OSDN Git Service

* config/rs6000/darwin.h (SUBTARGET_OPTIONS): Move from here, to...
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index c9a5558..528d5e6 100644 (file)
@@ -52,6 +52,7 @@
 #include "reload.h"
 #include "cfglayout.h"
 #include "sched-int.h"
+#include "tree-gimple.h"
 #if TARGET_XCOFF
 #include "xcoffout.h"  /* get declarations of xcoff_*_section_name */
 #endif
@@ -78,6 +79,8 @@ typedef struct rs6000_stack {
   int toc_save_p;              /* true if the TOC needs to be saved */
   int push_p;                  /* true if we need to allocate stack space */
   int calls_p;                 /* true if the function makes any calls */
+  int world_save_p;             /* true if we're saving *everything*:
+                                  r13-r31, cr, f14-f31, vrsave, v20-v31  */
   enum rs6000_abi abi;         /* which ABI to use */
   int gp_save_offset;          /* offset to save GP regs from initial SP */
   int fp_save_offset;          /* offset to save FP regs from initial SP */
@@ -210,11 +213,17 @@ enum rs6000_abi rs6000_current_abi;
 /* ABI string from -mabi= option.  */
 const char *rs6000_abi_string;
 
+/* Whether to use variant of AIX ABI for PowerPC64 Linux.  */
+int dot_symbols;
+
 /* Debug flags */
 const char *rs6000_debug_name;
 int rs6000_debug_stack;                /* debug stack applications */
 int rs6000_debug_arg;          /* debug argument handling */
 
+/* Value is TRUE if register/mode pair is accepatable.  */
+bool rs6000_hard_regno_mode_ok_p[NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER];
+
 /* Opaque types.  */
 static GTY(()) tree opaque_V2SI_type_node;
 static GTY(()) tree opaque_V2SF_type_node;
@@ -278,7 +287,312 @@ struct builtin_description
   const char *const name;
   const enum rs6000_builtins code;
 };
+\f
+/* Target cpu costs.  */
+
+struct processor_costs {
+  const int mulsi;        /* cost of SImode multiplication.  */
+  const int mulsi_const;  /* cost of SImode multiplication by constant.  */
+  const int mulsi_const9; /* cost of SImode mult by short constant.  */
+  const int muldi;        /* cost of DImode multiplication.  */
+  const int divsi;        /* cost of SImode division.  */
+  const int divdi;        /* cost of DImode division.  */
+  const int fp;           /* cost of simple SFmode and DFmode insns.  */
+  const int dmul;         /* cost of DFmode multiplication (and fmadd).  */
+  const int sdiv;         /* cost of SFmode division (fdivs).  */
+  const int ddiv;         /* cost of DFmode division (fdiv).  */
+};
+
+const struct processor_costs *rs6000_cost;
+
+/* Processor costs (relative to an add) */
+
+/* Instruction size costs on 32bit processors.  */
+static const
+struct processor_costs size32_cost = {
+  COSTS_N_INSNS (1),    /* mulsi */
+  COSTS_N_INSNS (1),    /* mulsi_const */
+  COSTS_N_INSNS (1),    /* mulsi_const9 */
+  COSTS_N_INSNS (1),    /* muldi */
+  COSTS_N_INSNS (1),    /* divsi */
+  COSTS_N_INSNS (1),    /* divdi */
+  COSTS_N_INSNS (1),    /* fp */
+  COSTS_N_INSNS (1),    /* dmul */
+  COSTS_N_INSNS (1),    /* sdiv */
+  COSTS_N_INSNS (1),    /* ddiv */
+};
+
+/* Instruction size costs on 64bit processors.  */
+static const
+struct processor_costs size64_cost = {
+  COSTS_N_INSNS (1),    /* mulsi */
+  COSTS_N_INSNS (1),    /* mulsi_const */
+  COSTS_N_INSNS (1),    /* mulsi_const9 */
+  COSTS_N_INSNS (1),    /* muldi */
+  COSTS_N_INSNS (1),    /* divsi */
+  COSTS_N_INSNS (1),    /* divdi */
+  COSTS_N_INSNS (1),    /* fp */
+  COSTS_N_INSNS (1),    /* dmul */
+  COSTS_N_INSNS (1),    /* sdiv */
+  COSTS_N_INSNS (1),    /* ddiv */
+};
+
+/* Instruction costs on RIOS1 processors.  */
+static const
+struct processor_costs rios1_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (3),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (19),   /* divsi */
+  COSTS_N_INSNS (19),   /* divdi */
+  COSTS_N_INSNS (2),    /* fp */
+  COSTS_N_INSNS (2),    /* dmul */
+  COSTS_N_INSNS (19),   /* sdiv */
+  COSTS_N_INSNS (19),   /* ddiv */
+};
+
+/* Instruction costs on RIOS2 processors.  */
+static const
+struct processor_costs rios2_cost = {
+  COSTS_N_INSNS (2),    /* mulsi */
+  COSTS_N_INSNS (2),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (2),    /* muldi */
+  COSTS_N_INSNS (13),   /* divsi */
+  COSTS_N_INSNS (13),   /* divdi */
+  COSTS_N_INSNS (2),    /* fp */
+  COSTS_N_INSNS (2),    /* dmul */
+  COSTS_N_INSNS (17),   /* sdiv */
+  COSTS_N_INSNS (17),   /* ddiv */
+};
+
+/* Instruction costs on RS64A processors.  */
+static const
+struct processor_costs rs64a_cost = {
+  COSTS_N_INSNS (20),   /* mulsi */
+  COSTS_N_INSNS (12),   /* mulsi_const */
+  COSTS_N_INSNS (8),    /* mulsi_const9 */
+  COSTS_N_INSNS (34),   /* muldi */
+  COSTS_N_INSNS (65),   /* divsi */
+  COSTS_N_INSNS (67),   /* divdi */
+  COSTS_N_INSNS (4),    /* fp */
+  COSTS_N_INSNS (4),    /* dmul */
+  COSTS_N_INSNS (31),   /* sdiv */
+  COSTS_N_INSNS (31),   /* ddiv */
+};
+
+/* Instruction costs on MPCCORE processors.  */
+static const
+struct processor_costs mpccore_cost = {
+  COSTS_N_INSNS (2),    /* mulsi */
+  COSTS_N_INSNS (2),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (2),    /* muldi */
+  COSTS_N_INSNS (6),    /* divsi */
+  COSTS_N_INSNS (6),    /* divdi */
+  COSTS_N_INSNS (4),    /* fp */
+  COSTS_N_INSNS (5),    /* dmul */
+  COSTS_N_INSNS (10),   /* sdiv */
+  COSTS_N_INSNS (17),   /* ddiv */
+};
+
+/* Instruction costs on PPC403 processors.  */
+static const
+struct processor_costs ppc403_cost = {
+  COSTS_N_INSNS (4),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (4),    /* mulsi_const9 */
+  COSTS_N_INSNS (4),    /* muldi */
+  COSTS_N_INSNS (33),   /* divsi */
+  COSTS_N_INSNS (33),   /* divdi */
+  COSTS_N_INSNS (11),   /* fp */
+  COSTS_N_INSNS (11),   /* dmul */
+  COSTS_N_INSNS (11),   /* sdiv */
+  COSTS_N_INSNS (11),   /* ddiv */
+};
+
+/* Instruction costs on PPC405 processors.  */
+static const
+struct processor_costs ppc405_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (3),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (35),   /* divsi */
+  COSTS_N_INSNS (35),   /* divdi */
+  COSTS_N_INSNS (11),   /* fp */
+  COSTS_N_INSNS (11),   /* dmul */
+  COSTS_N_INSNS (11),   /* sdiv */
+  COSTS_N_INSNS (11),   /* ddiv */
+};
+
+/* Instruction costs on PPC440 processors.  */
+static const
+struct processor_costs ppc440_cost = {
+  COSTS_N_INSNS (3),    /* mulsi */
+  COSTS_N_INSNS (2),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (3),    /* muldi */
+  COSTS_N_INSNS (34),   /* divsi */
+  COSTS_N_INSNS (34),   /* divdi */
+  COSTS_N_INSNS (5),    /* fp */
+  COSTS_N_INSNS (5),    /* dmul */
+  COSTS_N_INSNS (19),   /* sdiv */
+  COSTS_N_INSNS (33),   /* ddiv */
+};
+
+/* Instruction costs on PPC601 processors.  */
+static const
+struct processor_costs ppc601_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (5),    /* mulsi_const */
+  COSTS_N_INSNS (5),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (36),   /* divsi */
+  COSTS_N_INSNS (36),   /* divdi */
+  COSTS_N_INSNS (4),    /* fp */
+  COSTS_N_INSNS (5),    /* dmul */
+  COSTS_N_INSNS (17),   /* sdiv */
+  COSTS_N_INSNS (31),   /* ddiv */
+};
+
+/* Instruction costs on PPC603 processors.  */
+static const
+struct processor_costs ppc603_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (3),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (37),   /* divsi */
+  COSTS_N_INSNS (37),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (4),    /* dmul */
+  COSTS_N_INSNS (18),   /* sdiv */
+  COSTS_N_INSNS (33),   /* ddiv */
+};
+
+/* Instruction costs on PPC604 processors.  */
+static const
+struct processor_costs ppc604_cost = {
+  COSTS_N_INSNS (4),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (4),    /* mulsi_const9 */
+  COSTS_N_INSNS (4),    /* muldi */
+  COSTS_N_INSNS (20),   /* divsi */
+  COSTS_N_INSNS (20),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (18),   /* sdiv */
+  COSTS_N_INSNS (32),   /* ddiv */
+};
+
+/* Instruction costs on PPC604e processors.  */
+static const
+struct processor_costs ppc604e_cost = {
+  COSTS_N_INSNS (2),    /* mulsi */
+  COSTS_N_INSNS (2),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (2),    /* muldi */
+  COSTS_N_INSNS (20),   /* divsi */
+  COSTS_N_INSNS (20),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (18),   /* sdiv */
+  COSTS_N_INSNS (32),   /* ddiv */
+};
+
+/* Instruction costs on PPC620 processors.  */
+static const
+struct processor_costs ppc620_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (3),    /* mulsi_const9 */
+  COSTS_N_INSNS (7),    /* muldi */
+  COSTS_N_INSNS (21),   /* divsi */
+  COSTS_N_INSNS (37),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (18),   /* sdiv */
+  COSTS_N_INSNS (32),   /* ddiv */
+};
 
+/* Instruction costs on PPC630 processors.  */
+static const
+struct processor_costs ppc630_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (3),    /* mulsi_const9 */
+  COSTS_N_INSNS (7),    /* muldi */
+  COSTS_N_INSNS (21),   /* divsi */
+  COSTS_N_INSNS (37),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (17),   /* sdiv */
+  COSTS_N_INSNS (21),   /* ddiv */
+};
+
+/* Instruction costs on PPC750 and PPC7400 processors.  */
+static const
+struct processor_costs ppc750_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (3),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (17),   /* divsi */
+  COSTS_N_INSNS (17),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (17),   /* sdiv */
+  COSTS_N_INSNS (31),   /* ddiv */
+};
+
+/* Instruction costs on PPC7450 processors.  */
+static const
+struct processor_costs ppc7450_cost = {
+  COSTS_N_INSNS (4),    /* mulsi */
+  COSTS_N_INSNS (3),    /* mulsi_const */
+  COSTS_N_INSNS (3),    /* mulsi_const9 */
+  COSTS_N_INSNS (4),    /* muldi */
+  COSTS_N_INSNS (23),   /* divsi */
+  COSTS_N_INSNS (23),   /* divdi */
+  COSTS_N_INSNS (5),    /* fp */
+  COSTS_N_INSNS (5),    /* dmul */
+  COSTS_N_INSNS (21),   /* sdiv */
+  COSTS_N_INSNS (35),   /* ddiv */
+};
+
+/* Instruction costs on PPC8540 processors.  */
+static const
+struct processor_costs ppc8540_cost = {
+  COSTS_N_INSNS (4),    /* mulsi */
+  COSTS_N_INSNS (4),    /* mulsi_const */
+  COSTS_N_INSNS (4),    /* mulsi_const9 */
+  COSTS_N_INSNS (4),    /* muldi */
+  COSTS_N_INSNS (19),   /* divsi */
+  COSTS_N_INSNS (19),   /* divdi */
+  COSTS_N_INSNS (4),    /* fp */
+  COSTS_N_INSNS (4),    /* dmul */
+  COSTS_N_INSNS (29),   /* sdiv */
+  COSTS_N_INSNS (29),   /* ddiv */
+};
+
+/* Instruction costs on POWER4 and POWER5 processors.  */
+static const
+struct processor_costs power4_cost = {
+  COSTS_N_INSNS (3),    /* mulsi */
+  COSTS_N_INSNS (2),    /* mulsi_const */
+  COSTS_N_INSNS (2),    /* mulsi_const9 */
+  COSTS_N_INSNS (4),    /* muldi */
+  COSTS_N_INSNS (18),   /* divsi */
+  COSTS_N_INSNS (34),   /* divdi */
+  COSTS_N_INSNS (3),    /* fp */
+  COSTS_N_INSNS (3),    /* dmul */
+  COSTS_N_INSNS (17),   /* sdiv */
+  COSTS_N_INSNS (17),   /* ddiv */
+};
+
+\f
 static bool rs6000_function_ok_for_sibcall (tree, tree);
 static int num_insns_constant_wide (HOST_WIDE_INT);
 static void validate_condition_mode (enum rtx_code, enum machine_mode);
@@ -299,7 +613,6 @@ static int constant_pool_expr_1 (rtx, int *, int *);
 static bool constant_pool_expr_p (rtx);
 static bool toc_relative_expr_p (rtx);
 static bool legitimate_small_data_p (enum machine_mode, rtx);
-static bool legitimate_offset_address_p (enum machine_mode, rtx, int);
 static bool legitimate_indexed_address_p (rtx, int);
 static bool legitimate_indirect_address_p (rtx, int);
 static bool macho_lo_sum_memory_operand (rtx x, enum machine_mode mode);
@@ -312,6 +625,7 @@ static void rs6000_assemble_visibility (tree, int);
 static int rs6000_ra_ever_killed (void);
 static tree rs6000_handle_longcall_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_handle_altivec_attribute (tree *, tree, tree, int, bool *);
+static void rs6000_eliminate_indexed_memrefs (rtx operands[2]);
 static const char *rs6000_mangle_fundamental_type (tree);
 extern const struct attribute_spec rs6000_attribute_table[];
 static void rs6000_set_default_type_attributes (tree);
@@ -349,7 +663,6 @@ static void rs6000_xcoff_file_end (void);
 #if TARGET_MACHO
 static bool rs6000_binds_local_p (tree);
 #endif
-static int rs6000_use_dfa_pipeline_interface (void);
 static int rs6000_variable_issue (FILE *, int, rtx, int);
 static bool rs6000_rtx_costs (rtx, int, int, int *);
 static int rs6000_adjust_cost (rtx, rtx, rtx, int);
@@ -381,8 +694,10 @@ static void rs6000_init_libfuncs (void);
 static void enable_mask_for_builtins (struct builtin_description *, int,
                                      enum rs6000_builtins,
                                      enum rs6000_builtins);
+static tree build_opaque_vector_type (tree, int);
 static void spe_init_builtins (void);
 static rtx spe_expand_builtin (tree, rtx, bool *);
+static rtx spe_expand_stv_builtin (enum insn_code, tree);
 static rtx spe_expand_predicate_builtin (enum insn_code, tree, rtx);
 static rtx spe_expand_evsel_builtin (enum insn_code, tree, rtx);
 static int rs6000_emit_int_cmove (rtx, rtx, rtx, rtx);
@@ -404,6 +719,7 @@ static void rs6000_parse_tls_size_option (void);
 static void rs6000_parse_yes_no_option (const char *, const char *, int *);
 static int first_altivec_reg_to_save (void);
 static unsigned int compute_vrsave_mask (void);
+static void compute_save_world_info(rs6000_stack_t *info_ptr);
 static void is_altivec_return_reg (rtx, void *);
 static rtx generate_set_vrsave (rtx, rs6000_stack_t *, int);
 int easy_vector_constant (rtx, enum machine_mode);
@@ -420,12 +736,13 @@ static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
 static rtx rs6000_complex_function_value (enum machine_mode);
 static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree);
-static rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *,
-                                     enum machine_mode, tree, int);
+static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int);
 static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
 static void setup_incoming_varargs (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree,
                                    int *, int);
+static bool rs6000_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
+                                     tree, bool);
 #if TARGET_MACHO
 static void macho_branch_islands (void);
 static void add_compiler_branch_island (tree, tree, int);
@@ -434,6 +751,10 @@ static tree get_prev_label (tree function_name);
 #endif
 
 static tree rs6000_build_builtin_va_list (void);
+static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *);
+static bool rs6000_must_pass_in_stack (enum machine_mode, tree);
+
+static enum machine_mode rs6000_eh_return_filter_mode (void);
 
 /* Hash table stuff for keeping track of TOC entries.  */
 
@@ -538,6 +859,10 @@ static const char alt_reg_names[][8] =
 #define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t"
 #undef TARGET_ASM_UNALIGNED_SI_OP
 #define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"
+#undef TARGET_ASM_UNALIGNED_DI_OP
+#define TARGET_ASM_UNALIGNED_DI_OP "\t.quad\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
 #endif
 #endif
 
@@ -562,8 +887,6 @@ static const char alt_reg_names[][8] =
 #undef TARGET_ASM_FUNCTION_EPILOGUE
 #define TARGET_ASM_FUNCTION_EPILOGUE rs6000_output_function_epilogue
 
-#undef  TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE 
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE rs6000_use_dfa_pipeline_interface
 #undef  TARGET_SCHED_VARIABLE_ISSUE
 #define TARGET_SCHED_VARIABLE_ISSUE rs6000_variable_issue
 
@@ -638,12 +961,81 @@ static const char alt_reg_names[][8] =
 #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED hook_bool_CUMULATIVE_ARGS_true
 #undef TARGET_SPLIT_COMPLEX_ARG
 #define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK rs6000_must_pass_in_stack
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE rs6000_pass_by_reference
 
 #undef TARGET_BUILD_BUILTIN_VA_LIST
 #define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
 
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR rs6000_gimplify_va_arg
+
+#undef TARGET_EH_RETURN_FILTER_MODE
+#define TARGET_EH_RETURN_FILTER_MODE rs6000_eh_return_filter_mode
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode
+   MODE.  */
+static int
+rs6000_hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+  /* The GPRs can hold any mode, but values bigger than one register
+     cannot go past R31.  */
+  if (INT_REGNO_P (regno))
+    return INT_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1);
+
+  /* The float registers can only hold floating modes and DImode.  */
+  if (FP_REGNO_P (regno))
+    return
+      (GET_MODE_CLASS (mode) == MODE_FLOAT
+       && FP_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1))
+      || (GET_MODE_CLASS (mode) == MODE_INT
+         && GET_MODE_SIZE (mode) == UNITS_PER_FP_WORD);
+
+  /* The CR register can only hold CC modes.  */
+  if (CR_REGNO_P (regno))
+    return GET_MODE_CLASS (mode) == MODE_CC;
+
+  if (XER_REGNO_P (regno))
+    return mode == PSImode;
+
+  /* AltiVec only in AldyVec registers.  */
+  if (ALTIVEC_REGNO_P (regno))
+    return ALTIVEC_VECTOR_MODE (mode);
+
+  /* ...but GPRs can hold SIMD data on the SPE in one register.  */
+  if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode))
+    return 1;
+
+  /* We cannot put TImode anywhere except general register and it must be
+     able to fit within the register set.  */
+
+  return GET_MODE_SIZE (mode) <= UNITS_PER_WORD;
+}
+
+/* Initialize rs6000_hard_regno_mode_ok_p table.  */
+static void
+rs6000_init_hard_regno_mode_ok (void)
+{
+  int r, m;
+
+  for (r = 0; r < FIRST_PSEUDO_REGISTER; ++r)
+    for (m = 0; m < NUM_MACHINE_MODES; ++m)
+      if (rs6000_hard_regno_mode_ok (r, m))
+       rs6000_hard_regno_mode_ok_p[m][r] = true;
+}
+
+/* If not otherwise specified by a target, make 'long double' equivalent to
+   'double'.  */
+
+#ifndef RS6000_DEFAULT_LONG_DOUBLE_SIZE
+#define RS6000_DEFAULT_LONG_DOUBLE_SIZE 64
+#endif
+
 /* Override command line options.  Mostly we process the processor
    type and sometimes adjust other TARGET_ options.  */
 
@@ -746,6 +1138,9 @@ rs6000_override_options (const char *default_cpu)
                     | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC
                     | MASK_MFCRF)
   };
+
+  rs6000_init_hard_regno_mode_ok ();
+
  set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
 #ifdef OS_MISSING_POWERPC64
   if (OS_MISSING_POWERPC64)
@@ -847,7 +1242,7 @@ rs6000_override_options (const char *default_cpu)
     }
 
   /* Set size of long double */
-  rs6000_long_double_type_size = 64;
+  rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
   if (rs6000_long_double_size_string)
     {
       char *tail;
@@ -893,6 +1288,9 @@ rs6000_override_options (const char *default_cpu)
 
   if (TARGET_E500)
     {
+      if (TARGET_ALTIVEC)
+       error ("AltiVec and E500 instructions cannot coexist");
+
       /* The e500 does not have string instructions, and we set
         MASK_STRING above when optimizing for size.  */
       if ((target_flags & MASK_STRING) != 0)
@@ -917,7 +1315,7 @@ rs6000_override_options (const char *default_cpu)
       if (rs6000_isel_string == 0)
        rs6000_isel = 0;
       if (rs6000_long_double_size_string == 0)
-       rs6000_long_double_type_size = 64;
+       rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
     }
 
   rs6000_always_hint = (rs6000_cpu != PROCESSOR_POWER4
@@ -1038,6 +1436,86 @@ rs6000_override_options (const char *default_cpu)
      Linux and Darwin ABIs at the moment.  For now, only AIX is fixed.  */
   if (DEFAULT_ABI != ABI_AIX)
     targetm.calls.split_complex_arg = NULL;
+
+  /* Initialize rs6000_cost with the appropriate target costs.  */
+  if (optimize_size)
+    rs6000_cost = TARGET_POWERPC64 ? &size64_cost : &size32_cost;
+  else
+    switch (rs6000_cpu)
+      {
+      case PROCESSOR_RIOS1:
+       rs6000_cost = &rios1_cost;
+       break;
+
+      case PROCESSOR_RIOS2:
+       rs6000_cost = &rios2_cost;
+       break;
+
+      case PROCESSOR_RS64A:
+       rs6000_cost = &rs64a_cost;
+       break;
+
+      case PROCESSOR_MPCCORE:
+       rs6000_cost = &mpccore_cost;
+       break;
+
+      case PROCESSOR_PPC403:
+       rs6000_cost = &ppc403_cost;
+       break;
+
+      case PROCESSOR_PPC405:
+       rs6000_cost = &ppc405_cost;
+       break;
+
+      case PROCESSOR_PPC440:
+       rs6000_cost = &ppc440_cost;
+       break;
+
+      case PROCESSOR_PPC601:
+       rs6000_cost = &ppc601_cost;
+       break;
+
+      case PROCESSOR_PPC603:
+       rs6000_cost = &ppc603_cost;
+       break;
+
+      case PROCESSOR_PPC604:
+       rs6000_cost = &ppc604_cost;
+       break;
+
+      case PROCESSOR_PPC604e:
+       rs6000_cost = &ppc604e_cost;
+       break;
+
+      case PROCESSOR_PPC620:
+       rs6000_cost = &ppc620_cost;
+       break;
+
+      case PROCESSOR_PPC630:
+       rs6000_cost = &ppc630_cost;
+       break;
+
+      case PROCESSOR_PPC750:
+      case PROCESSOR_PPC7400:
+       rs6000_cost = &ppc750_cost;
+       break;
+
+      case PROCESSOR_PPC7450:
+       rs6000_cost = &ppc7450_cost;
+       break;
+
+      case PROCESSOR_PPC8540:
+       rs6000_cost = &ppc8540_cost;
+       break;
+
+      case PROCESSOR_POWER4:
+      case PROCESSOR_POWER5:
+       rs6000_cost = &power4_cost;
+       break;
+
+      default:
+       abort ();
+      }
 }
 
 /* Handle generic options of the form -mfoo=yes/no.
@@ -1209,7 +1687,17 @@ any_operand (rtx op ATTRIBUTE_UNUSED,
   return 1;
 }
 
+/* Returns 1 always.  */
+
+int
+any_parallel_operand (rtx op ATTRIBUTE_UNUSED, 
+                     enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
+
 /* Returns 1 if op is the count register.  */
+
 int
 count_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
@@ -1226,6 +1714,7 @@ count_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 }
 
 /* Returns 1 if op is an altivec register.  */
+
 int
 altivec_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
@@ -1344,13 +1833,16 @@ reg_or_short_operand (rtx op, enum machine_mode mode)
 }
 
 /* Similar, except check if the negation of the constant would be
-   valid for a D-field.  */
+   valid for a D-field.  Don't allow a constant zero, since all the
+   patterns that call this predicate use "addic r1,r2,-constant" on
+   a constant value to set a carry when r2 is greater or equal to
+   "constant".  That doesn't work for zero.  */
 
 int
 reg_or_neg_short_operand (rtx op, enum machine_mode mode)
 {
   if (GET_CODE (op) == CONST_INT)
-    return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P');
+    return CONST_OK_FOR_LETTER_P (INTVAL (op), 'P') && INTVAL (op) != 0;
 
   return gpc_reg_operand (op, mode);
 }
@@ -1675,7 +2167,7 @@ easy_fp_constant (rtx op, enum machine_mode mode)
     abort ();
 }
 
-/* Returns the constant for the splat instrunction, if exists.  */
+/* Returns the constant for the splat instruction, if exists.  */
 
 static int
 easy_vector_splat_const (int cst, enum machine_mode mode)
@@ -2357,10 +2849,6 @@ input_operand (rtx op, enum machine_mode mode)
   if (memory_operand (op, mode))
     return 1;
 
-  /* Only a tiny bit of handling for CONSTANT_P_RTX is necessary.  */
-  if (GET_CODE (op) == CONSTANT_P_RTX)
-    return 1;
-
   /* For floating-point, easy constants are valid.  */
   if (GET_MODE_CLASS (mode) == MODE_FLOAT
       && CONSTANT_P (op)
@@ -2559,9 +3047,6 @@ toc_relative_expr_p (rtx op)
   return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_toc;
 }
 
-/* SPE offset addressing is limited to 5-bits worth of double words.  */
-#define SPE_CONST_OFFSET_OK(x) (((x) & ~0xf8) == 0)
-
 bool
 legitimate_constant_pool_address_p (rtx x)
 {
@@ -2581,8 +3066,11 @@ legitimate_small_data_p (enum machine_mode mode, rtx x)
          && small_data_operand (x, mode));
 }
 
-static bool
-legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
+/* SPE offset addressing is limited to 5-bits worth of double words.  */
+#define SPE_CONST_OFFSET_OK(x) (((x) & ~0xf8) == 0)
+
+bool
+rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
 {
   unsigned HOST_WIDE_INT offset, extra;
 
@@ -2592,6 +3080,8 @@ legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
     return false;
   if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
     return false;
+  if (legitimate_constant_pool_address_p (x))
+    return true;
   if (GET_CODE (XEXP (x, 1)) != CONST_INT)
     return false;
 
@@ -2704,8 +3194,7 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
        return false;
       if (GET_MODE_NUNITS (mode) != 1)
        return false;
-      if (GET_MODE_BITSIZE (mode) > 32
-         && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode))
+      if (GET_MODE_BITSIZE (mode) > 64)
        return false;
 
       return CONSTANT_P (x);
@@ -2974,8 +3463,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
                  tmp1 = gen_reg_rtx (Pmode);
                  tmp2 = gen_reg_rtx (Pmode);
                  tmp3 = gen_reg_rtx (Pmode);
-                 mem = gen_rtx_MEM (Pmode, tmp1);
-                 RTX_UNCHANGING_P (mem) = 1;
+                 mem = gen_const_mem (Pmode, tmp1);
 
                  first = emit_insn (gen_load_toc_v4_PIC_1b (tempLR, lab,
                                                             gsym));
@@ -3218,8 +3706,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
        {
          rtx offset = gen_rtx_CONST (Pmode,
                         gen_rtx_MINUS (Pmode, x,
-                          gen_rtx_SYMBOL_REF (Pmode,
-                            machopic_function_base_name ())));
+                                       machopic_function_base_sym ()));
          x = gen_rtx_LO_SUM (GET_MODE (x),
                gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
                  gen_rtx_HIGH (Pmode, offset)), offset);
@@ -3261,7 +3748,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
    word aligned.
 
    For modes spanning multiple registers (DFmode in 32-bit GPRs,
-   32-bit DImode, TImode), indexed addressing cannot be used because
+   32-bit DImode, TImode, TFmode), indexed addressing cannot be used because
    adjacent memory cells are accessed by adding word-sized offsets
    during assembly output.  */
 int
@@ -3289,9 +3776,10 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
          || XEXP (x, 0) == arg_pointer_rtx)
       && GET_CODE (XEXP (x, 1)) == CONST_INT)
     return 1;
-  if (legitimate_offset_address_p (mode, x, reg_ok_strict))
+  if (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict))
     return 1;
   if (mode != TImode
+      && mode != TFmode
       && ((TARGET_HARD_FLOAT && TARGET_FPRS)
          || TARGET_POWERPC64
          || (mode != DFmode && mode != TFmode))
@@ -3340,6 +3828,103 @@ rs6000_mode_dependent_address (rtx addr)
 
   return false;
 }
+
+/* Return number of consecutive hard regs needed starting at reg REGNO
+   to hold something of mode MODE.
+   This is ordinarily the length in words of a value of mode MODE
+   but can be less for certain modes in special long registers.
+
+   For the SPE, GPRs are 64 bits but only 32 bits are visible in
+   scalar instructions.  The upper 32 bits are only available to the
+   SIMD instructions.
+
+   POWER and PowerPC GPRs hold 32 bits worth;
+   PowerPC64 GPRs and FPRs point register holds 64 bits worth.  */
+
+int
+rs6000_hard_regno_nregs (int regno, enum machine_mode mode)
+{
+  if (FP_REGNO_P (regno))
+    return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
+
+  if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode))
+    return (GET_MODE_SIZE (mode) + UNITS_PER_SPE_WORD - 1) / UNITS_PER_SPE_WORD;
+
+  if (ALTIVEC_REGNO_P (regno))
+    return
+      (GET_MODE_SIZE (mode) + UNITS_PER_ALTIVEC_WORD - 1) / UNITS_PER_ALTIVEC_WORD;
+
+  return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Change register usage conditional on target flags.  */
+void
+rs6000_conditional_register_usage (void)
+{
+  int i;
+
+  /* Set MQ register fixed (already call_used) if not POWER
+     architecture (RIOS1, RIOS2, RSC, and PPC601) so that it will not
+     be allocated.  */
+  if (! TARGET_POWER)
+    fixed_regs[64] = 1;
+
+  /* 64-bit AIX reserves GPR13 for thread-private data.  */
+  if (TARGET_64BIT)
+    fixed_regs[13] = call_used_regs[13]
+      = call_really_used_regs[13] = 1;
+
+  /* Conditionally disable FPRs.  */
+  if (TARGET_SOFT_FLOAT || !TARGET_FPRS)
+    for (i = 32; i < 64; i++)
+      fixed_regs[i] = call_used_regs[i]
+        = call_really_used_regs[i] = 1;
+
+  if (DEFAULT_ABI == ABI_V4
+      && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
+      && flag_pic == 2)
+    fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
+
+  if (DEFAULT_ABI == ABI_V4
+      && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
+      && flag_pic == 1)
+    fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = call_really_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
+
+  if (DEFAULT_ABI == ABI_DARWIN
+      && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
+    global_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = call_really_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
+
+  if (TARGET_TOC && TARGET_MINIMAL_TOC)
+    fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
+
+  if (TARGET_ALTIVEC)
+    global_regs[VSCR_REGNO] = 1;
+
+  if (TARGET_SPE)
+    {
+      global_regs[SPEFSCR_REGNO] = 1;
+      fixed_regs[FIXED_SCRATCH]
+        = call_used_regs[FIXED_SCRATCH]
+       = call_really_used_regs[FIXED_SCRATCH] = 1;
+    }
+
+  if (! TARGET_ALTIVEC)
+    {
+      for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
+       fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1;
+      call_really_used_regs[VRSAVE_REGNO] = 1;
+    }
+
+  if (TARGET_ALTIVEC_ABI)
+    for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i)
+      call_used_regs[i] = call_really_used_regs[i] = 1;
+}
 \f
 /* Try to output insns to set TARGET equal to the constant C if it can
    be done in less than N insns.  Do all computations in MODE.
@@ -3493,6 +4078,27 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c1, HOST_WIDE_INT c2)
   return dest;
 }
 
+/* Helper for the following.  Get rid of [r+r] memory refs
+   in cases where it won't work (TImode, TFmode).  */
+
+static void
+rs6000_eliminate_indexed_memrefs (rtx operands[2])
+{
+  if (GET_CODE (operands[0]) == MEM
+      && GET_CODE (XEXP (operands[0], 0)) != REG
+      && ! reload_in_progress)
+    operands[0]
+      = replace_equiv_address (operands[0],
+                              copy_addr_to_reg (XEXP (operands[0], 0)));
+
+  if (GET_CODE (operands[1]) == MEM
+      && GET_CODE (XEXP (operands[1], 0)) != REG
+      && ! reload_in_progress)
+    operands[1]
+      = replace_equiv_address (operands[1],
+                              copy_addr_to_reg (XEXP (operands[1], 0)));
+}
+
 /* Emit a move from SOURCE to DEST in mode MODE.  */
 void
 rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
@@ -3598,10 +4204,6 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
          || ! nonimmediate_operand (operands[0], mode)))
     goto emit_set;
 
-  /* Handle the case of CONSTANT_P_RTX.  */
-  if (GET_CODE (operands[1]) == CONSTANT_P_RTX)
-    goto emit_set;
-
   /* 128-bit constant floating-point values on Darwin should really be
      loaded as two parts.  */
   if ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN)
@@ -3634,6 +4236,9 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       break;
 
     case TFmode:
+      rs6000_eliminate_indexed_memrefs (operands);
+      /* fall through */
+
     case DFmode:
     case SFmode:
       if (CONSTANT_P (operands[1]) 
@@ -3719,8 +4324,16 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                  return;
                }
 #endif
-             emit_insn (gen_macho_high (target, operands[1]));
-             emit_insn (gen_macho_low (operands[0], target, operands[1]));
+             if (mode == DImode)
+               {
+                 emit_insn (gen_macho_high_di (target, operands[1]));
+                 emit_insn (gen_macho_low_di (operands[0], target, operands[1]));
+               }
+             else
+               {
+                 emit_insn (gen_macho_high (target, operands[1]));
+                 emit_insn (gen_macho_low (operands[0], target, operands[1]));
+               }
              return;
            }
 
@@ -3807,28 +4420,16 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                        get_pool_mode (XEXP (operands[1], 0))))
            {
              operands[1]
-               = gen_rtx_MEM (mode,
-                              create_TOC_reference (XEXP (operands[1], 0)));
+               = gen_const_mem (mode,
+                                create_TOC_reference (XEXP (operands[1], 0)));
              set_mem_alias_set (operands[1], get_TOC_alias_set ());
-             RTX_UNCHANGING_P (operands[1]) = 1;
            }
        }
       break;
 
     case TImode:
-      if (GET_CODE (operands[0]) == MEM
-         && GET_CODE (XEXP (operands[0], 0)) != REG
-         && ! reload_in_progress)
-       operands[0]
-         = replace_equiv_address (operands[0],
-                                  copy_addr_to_reg (XEXP (operands[0], 0)));
-
-      if (GET_CODE (operands[1]) == MEM
-         && GET_CODE (XEXP (operands[1], 0)) != REG
-         && ! reload_in_progress)
-       operands[1]
-         = replace_equiv_address (operands[1],
-                                  copy_addr_to_reg (XEXP (operands[1], 0)));
+      rs6000_eliminate_indexed_memrefs (operands);
+
       if (TARGET_POWER)
        {
          emit_insn (gen_rtx_PARALLEL (VOIDmode,
@@ -3966,6 +4567,17 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
       }
 }
 \f
+/* Return true if TYPE must be passed on the stack and not in registers.  */
+
+static bool
+rs6000_must_pass_in_stack (enum machine_mode mode, tree type)
+{
+  if (DEFAULT_ABI == ABI_AIX || TARGET_64BIT)
+    return must_pass_in_stack_var_size (mode, type);
+  else
+    return must_pass_in_stack_var_size_or_pad (mode, type);
+}
+
 /* If defined, a C expression which determines whether, and in which
    direction, to pad out an argument with extra space.  The value
    should be of type `enum direction': either `upward' to pad above
@@ -4033,11 +4645,11 @@ function_arg_padding (enum machine_mode mode, tree type)
 int
 function_arg_boundary (enum machine_mode mode, tree type ATTRIBUTE_UNUSED)
 {
-  if (DEFAULT_ABI == ABI_V4 && (mode == DImode || mode == DFmode))
+  if (DEFAULT_ABI == ABI_V4 && GET_MODE_SIZE (mode) == 8)
     return 64;
-   else if (SPE_VECTOR_MODE (mode))
-     return 64;
-  else if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+  else if (SPE_VECTOR_MODE (mode))
+    return 64;
+  else if (ALTIVEC_VECTOR_MODE (mode))
     return 128;
   else
     return PARM_BOUNDARY;
@@ -4063,7 +4675,11 @@ rs6000_arg_size (enum machine_mode mode, tree type)
 \f
 /* Update the data in CUM to advance over an argument
    of mode MODE and data type TYPE.
-   (TYPE is null for libcalls where that information may not be available.)  */
+   (TYPE is null for libcalls where that information may not be available.)
+
+   Note that for args passed by reference, function_arg will be called
+   with MODE and TYPE set to that of the pointer to the arg, not the arg
+   itself.  */
 
 void
 function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
@@ -4073,6 +4689,8 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
     {
+      bool stack = false;
+
       if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
         {
          cum->vregno++;
@@ -4080,12 +4698,18 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
            error ("Cannot pass argument in vector register because"
                   " altivec instructions are disabled, use -maltivec"
                   " to enable them.");
+
+         /* PowerPC64 Linux and AIX allocate GPRs for a vector argument
+            even if it is going to be passed in a vector register.  
+            Darwin does the same for variable-argument functions.  */
+         if ((DEFAULT_ABI == ABI_AIX && TARGET_64BIT)
+             || (cum->stdarg && DEFAULT_ABI != ABI_V4))
+           stack = true;
        }
-      /* PowerPC64 Linux and AIX allocates GPRs for a vector argument
-        even if it is going to be passed in a vector register.  
-        Darwin does the same for variable-argument functions.  */
-      if ((DEFAULT_ABI == ABI_AIX && TARGET_64BIT)
-                  || (cum->stdarg && DEFAULT_ABI != ABI_V4))
+      else
+       stack = true;
+
+      if (stack)
         {
          int align;
          
@@ -4097,7 +4721,7 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
             aligned.  Space for GPRs is reserved even if the argument
             will be passed in memory.  */
          if (TARGET_32BIT)
-           align = ((6 - (cum->words & 3)) & 3);
+           align = (2 - cum->words) & 3;
          else
            align = cum->words & 1;
          cum->words += align + rs6000_arg_size (mode, type);
@@ -4132,25 +4756,21 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
        }
       else
        {
-         int n_words;
+         int n_words = rs6000_arg_size (mode, type);
          int gregno = cum->sysv_gregno;
 
-         /* Aggregates and IEEE quad get passed by reference.  */
-         if ((type && AGGREGATE_TYPE_P (type))
-             || mode == TFmode)
-           n_words = 1;
-         else 
-           n_words = rs6000_arg_size (mode, type);
+         /* Long long and SPE vectors are put in (r3,r4), (r5,r6),
+            (r7,r8) or (r9,r10).  As does any other 2 word item such
+            as complex int due to a historical mistake.  */
+         if (n_words == 2)
+           gregno += (1 - gregno) & 1;
 
-         /* Long long and SPE vectors are put in odd registers.  */
-         if (n_words == 2 && (gregno & 1) == 0)
-           gregno += 1;
-
-         /* Long long and SPE vectors are not split between registers
-            and stack.  */
+         /* Multi-reg args are not split between registers and stack.  */
          if (gregno + n_words - 1 > GP_ARG_MAX_REG)
            {
-             /* Long long is aligned on the stack.  */
+             /* Long long and SPE vectors are aligned on the stack.
+                So are other 2 word items such as complex int due to
+                a historical mistake.  */
              if (n_words == 2)
                cum->words += cum->words & 1;
              cum->words += n_words;
@@ -4174,10 +4794,16 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     }
   else
     {
-      int align = (TARGET_32BIT && (cum->words & 1) != 0
-                  && function_arg_boundary (mode, type) == 64) ? 1 : 0;
+      int n_words = rs6000_arg_size (mode, type);
+      int align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
 
-      cum->words += align + rs6000_arg_size (mode, type);
+      /* The simple alignment calculation here works because
+        function_arg_boundary / PARM_BOUNDARY will only be 1 or 2.
+        If we ever want to handle alignments larger than 8 bytes for
+        32-bit or 16 bytes for 64-bit, then we'll need to take into
+        account the offset to the start of the parm save area.  */
+      align &= cum->words;
+      cum->words += align + n_words;
 
       if (GET_MODE_CLASS (mode) == MODE_FLOAT
          && TARGET_HARD_FLOAT && TARGET_FPRS)
@@ -4235,105 +4861,49 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 /* Determine where to place an argument in 64-bit mode with 32-bit ABI.  */
 
 static rtx
-rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
-                          tree type, int align_words)
-{
-  if (mode == DFmode)
-    {
-      /* -mpowerpc64 with 32bit ABI splits up a DFmode argument
-        in vararg list into zero, one or two GPRs */
-      if (align_words >= GP_ARG_NUM_REG)
-       return gen_rtx_PARALLEL (DFmode,
-                gen_rtvec (2,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              NULL_RTX, const0_rtx), 
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (mode,
-                                                           cum->fregno),
-                                              const0_rtx)));
-      else if (align_words + rs6000_arg_size (mode, type)
-              > GP_ARG_NUM_REG)
-       /* If this is partially on the stack, then we only
-          include the portion actually in registers here.  */
-       return gen_rtx_PARALLEL (DFmode,
-                gen_rtvec (2,   
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words),
-                                              const0_rtx),
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (mode,
-                                                           cum->fregno),
-                                              const0_rtx)));
-
-      /* split a DFmode arg into two GPRs */
-      return gen_rtx_PARALLEL (DFmode,
-              gen_rtvec (3,
-                         gen_rtx_EXPR_LIST (VOIDmode,       
-                                            gen_rtx_REG (SImode,
-                                                         GP_ARG_MIN_REG
-                                                         + align_words),
-                                            const0_rtx),
-                         gen_rtx_EXPR_LIST (VOIDmode,
-                                            gen_rtx_REG (SImode,
-                                                         GP_ARG_MIN_REG
-                                                         + align_words + 1),
-                                            GEN_INT (4)),
-                         gen_rtx_EXPR_LIST (VOIDmode,
-                                            gen_rtx_REG (mode, cum->fregno),
-                                            const0_rtx)));
-    }
-  /* -mpowerpc64 with 32bit ABI splits up a DImode argument into one
-     or two GPRs */
-  else if (mode == DImode)
+rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
+{
+  int n_units;
+  int i, k;
+  rtx rvec[GP_ARG_NUM_REG + 1];
+
+  if (align_words >= GP_ARG_NUM_REG)
+    return NULL_RTX;
+
+  n_units = rs6000_arg_size (mode, type);
+
+  /* Optimize the simple case where the arg fits in one gpr, except in
+     the case of BLKmode due to assign_parms assuming that registers are
+     BITS_PER_WORD wide.  */
+  if (n_units == 0
+      || (n_units == 1 && mode != BLKmode))
+    return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+
+  k = 0;
+  if (align_words + n_units > GP_ARG_NUM_REG)
+    /* Not all of the arg fits in gprs.  Say that it goes in memory too,
+       using a magic NULL_RTX component.
+       FIXME: This is not strictly correct.  Only some of the arg
+       belongs in memory, not all of it.  However, there isn't any way
+       to do this currently, apart from building rtx descriptions for
+       the pieces of memory we want stored.  Due to bugs in the generic
+       code we can't use the normal function_arg_partial_nregs scheme
+       with the PARALLEL arg description we emit here.
+       In any case, the code to store the whole arg to memory is often
+       more efficient than code to store pieces, and we know that space
+       is available in the right place for the whole arg.  */
+    rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
+
+  i = 0;
+  do
     {
-      if (align_words < GP_ARG_NUM_REG - 1)
-       return gen_rtx_PARALLEL (DImode,
-                gen_rtvec (2,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words),
-                                              const0_rtx),
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (SImode,
-                                                           GP_ARG_MIN_REG
-                                                           + align_words + 1),
-                                              GEN_INT (4))));
-      else if (align_words == GP_ARG_NUM_REG - 1)
-         return gen_rtx_PARALLEL (DImode,
-                  gen_rtvec (2,
-                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                NULL_RTX, const0_rtx),
-                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                gen_rtx_REG (SImode,
-                                                             GP_ARG_MIN_REG
-                                                             + align_words),
-                                                const0_rtx)));
-    }
-  else if (mode == BLKmode && align_words <= (GP_ARG_NUM_REG - 1))
-    {
-      int k;
-      int size = int_size_in_bytes (type);
-      int no_units = ((size - 1) / 4) + 1;
-      int max_no_words = GP_ARG_NUM_REG - align_words;
-      int rtlvec_len = no_units < max_no_words ? no_units : max_no_words;
-      rtx *rtlvec = (rtx *) alloca (rtlvec_len * sizeof (rtx));
-
-      memset ((char *) rtlvec, 0, rtlvec_len * sizeof (rtx));
-
-      for (k=0; k < rtlvec_len; k++)
-       rtlvec[k] = gen_rtx_EXPR_LIST (VOIDmode,
-                                      gen_rtx_REG (SImode,
-                                                   GP_ARG_MIN_REG
-                                                   + align_words + k),
-                                      k == 0 ? const0_rtx : GEN_INT (k*4));
-
-      return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (k, rtlvec));
-  }
+      rtx r = gen_rtx_REG (SImode, GP_ARG_MIN_REG + align_words);
+      rtx off = GEN_INT (i++ * 4);
+      rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
+    }
+  while (++align_words < GP_ARG_NUM_REG && --n_units != 0);
 
-  return NULL_RTX;
+  return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
 }
 
 /* Determine where to put an argument to a function.
@@ -4357,7 +4927,11 @@ rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
    both an FP and integer register (or possibly FP reg and stack).  Library
    functions (when CALL_LIBCALL is set) always have the proper types for args,
    so we can pass the FP value just in one register.  emit_library_function
-   doesn't support PARALLEL anyway.  */
+   doesn't support PARALLEL anyway.
+
+   Note that for args passed by reference, function_arg will be called
+   with MODE and TYPE set to that of the pointer to the arg, not the arg
+   itself.  */
 
 struct rtx_def *
 function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
@@ -4424,8 +4998,8 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
        {
          /* Vector parameters to varargs functions under AIX or Darwin
             get passed in memory and possibly also in GPRs.  */
-         int align, align_words;
-         enum machine_mode part_mode = mode;
+         int align, align_words, n_words;
+         enum machine_mode part_mode;
 
          /* Vector parameters must be 16-byte aligned.  This places them at
             2 mod 4 in terms of words in 32-bit mode, since the parameter
@@ -4433,7 +5007,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
             they just have to start on an even word, since the parameter
             save area is 16-byte aligned.  */
          if (TARGET_32BIT)
-           align = ((6 - (cum->words & 3)) & 3);
+           align = (2 - cum->words) & 3;
          else
            align = cum->words & 1;
          align_words = cum->words + align;
@@ -4441,15 +5015,19 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          /* Out of registers?  Memory, then.  */
          if (align_words >= GP_ARG_NUM_REG)
            return NULL_RTX;
-         
+
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type, align_words);
+
          /* The vector value goes in GPRs.  Only the part of the
             value in GPRs is reported here.  */
-         if (align_words + CLASS_MAX_NREGS (mode, GENERAL_REGS)
-             > GP_ARG_NUM_REG)
+         part_mode = mode;
+         n_words = rs6000_arg_size (mode, type);
+         if (align_words + n_words > GP_ARG_NUM_REG)
            /* Fortunately, there are only two possibilities, the value
               is either wholly in GPRs or half in GPRs and half not.  */
            part_mode = DImode;
-         
+
          return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
        }
     }
@@ -4467,61 +5045,49 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
        }
       else
        {
-         int n_words;
+         int n_words = rs6000_arg_size (mode, type);
          int gregno = cum->sysv_gregno;
 
-         /* Aggregates and IEEE quad get passed by reference.  */
-         if ((type && AGGREGATE_TYPE_P (type))
-             || mode == TFmode)
-           n_words = 1;
-         else 
-           n_words = rs6000_arg_size (mode, type);
+         /* Long long and SPE vectors are put in (r3,r4), (r5,r6),
+            (r7,r8) or (r9,r10).  As does any other 2 word item such
+            as complex int due to a historical mistake.  */
+         if (n_words == 2)
+           gregno += (1 - gregno) & 1;
 
-         /* Long long and SPE vectors are put in odd registers.  */
-         if (n_words == 2 && (gregno & 1) == 0)
-           gregno += 1;
-
-         /* Long long does not split between registers and stack.  */
-         if (gregno + n_words - 1 <= GP_ARG_MAX_REG)
-           return gen_rtx_REG (mode, gregno);
-         else
+         /* Multi-reg args are not split between registers and stack.  */
+         if (gregno + n_words - 1 > GP_ARG_MAX_REG)
            return NULL_RTX;
+
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type,
+                                             gregno - GP_ARG_MIN_REG);
+         return gen_rtx_REG (mode, gregno);
        }
     }
   else
     {
-      int align = (TARGET_32BIT && (cum->words & 1) != 0
-                  && function_arg_boundary (mode, type) == 64) ? 1 : 0;
-      int align_words = cum->words + align;
-
-      if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
-        return NULL_RTX;
-
-      if (TARGET_32BIT && TARGET_POWERPC64
-         && (mode == DImode || mode == BLKmode))
-       return rs6000_mixed_function_arg (cum, mode, type, align_words);
+      int align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+      int align_words = cum->words + (cum->words & align);
 
       if (USE_FP_FOR_ARG_P (cum, mode, type))
        {
-         rtx fpr[2];
-         rtx *r;
+         rtx rvec[GP_ARG_NUM_REG + 1];
+         rtx r;
+         int k;
          bool needs_psave;
          enum machine_mode fmode = mode;
-         int n;
          unsigned long n_fpreg = (GET_MODE_SIZE (mode) + 7) >> 3;
 
          if (cum->fregno + n_fpreg > FP_ARG_MAX_REG + 1)
            {
-             /* Long double split over regs and memory.  */
-             if (fmode == TFmode)
-               fmode = DFmode;
-
              /* Currently, we only ever need one reg here because complex
                 doubles are split.  */
-             if (cum->fregno != FP_ARG_MAX_REG - 1)
+             if (cum->fregno != FP_ARG_MAX_REG || fmode != TFmode)
                abort ();
+
+             /* Long double split over regs and memory.  */
+             fmode = DFmode;
            }
-         fpr[1] = gen_rtx_REG (fmode, cum->fregno);
 
          /* Do we also need to pass this arg in the parameter save
             area?  */
@@ -4532,57 +5098,77 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                                 && align_words >= GP_ARG_NUM_REG)));
 
          if (!needs_psave && mode == fmode)
-           return fpr[1];
-
-          if (TARGET_32BIT && TARGET_POWERPC64
-              && mode == DFmode && cum->stdarg)
-            return rs6000_mixed_function_arg (cum, mode, type, align_words);
-
-         /* Describe where this piece goes.  */
-         r = fpr + 1;
-         *r = gen_rtx_EXPR_LIST (VOIDmode, *r, const0_rtx);
-         n = 1;
+           return gen_rtx_REG (fmode, cum->fregno);
 
+         k = 0;
          if (needs_psave)
            {
-             /* Now describe the part that goes in gprs or the stack.
+             /* Describe the part that goes in gprs or the stack.
                 This piece must come first, before the fprs.  */
-             rtx reg = NULL_RTX;
              if (align_words < GP_ARG_NUM_REG)
                {
                  unsigned long n_words = rs6000_arg_size (mode, type);
-                 enum machine_mode rmode = mode;
-
-                 if (align_words + n_words > GP_ARG_NUM_REG)
-                   /* If this is partially on the stack, then we only
-                      include the portion actually in registers here.
-                      We know this can only be one register because
-                      complex doubles are splt.  */
-                   rmode = Pmode;
-                 reg = gen_rtx_REG (rmode, GP_ARG_MIN_REG + align_words);
+
+                 if (align_words + n_words > GP_ARG_NUM_REG
+                     || (TARGET_32BIT && TARGET_POWERPC64))
+                   {
+                     /* If this is partially on the stack, then we only
+                        include the portion actually in registers here.  */
+                     enum machine_mode rmode = TARGET_32BIT ? SImode : DImode;
+                     rtx off;
+                     do
+                       {
+                         r = gen_rtx_REG (rmode,
+                                          GP_ARG_MIN_REG + align_words);
+                         off = GEN_INT (k * GET_MODE_SIZE (rmode));
+                         rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, off);
+                       }
+                     while (++align_words < GP_ARG_NUM_REG && --n_words != 0);
+                   }
+                 else
+                   {
+                     /* The whole arg fits in gprs.  */
+                     r = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+                     rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
+                   }
                }
-             *--r = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
-             ++n;
+             else
+               /* It's entirely in memory.  */
+               rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
            }
 
-         return gen_rtx_PARALLEL (mode, gen_rtvec_v (n, r));
+         /* Describe where this piece goes in the fprs.  */
+         r = gen_rtx_REG (fmode, cum->fregno);
+         rvec[k++] = gen_rtx_EXPR_LIST (VOIDmode, r, const0_rtx);
+
+         return gen_rtx_PARALLEL (mode, gen_rtvec_v (k, rvec));
        }
       else if (align_words < GP_ARG_NUM_REG)
-       return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+       {
+         if (TARGET_32BIT && TARGET_POWERPC64)
+           return rs6000_mixed_function_arg (mode, type, align_words);
+
+         return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+       }
       else
        return NULL_RTX;
     }
 }
 \f
-/* For an arg passed partly in registers and partly in memory,
-   this is the number of registers used.
-   For args passed entirely in registers or entirely in memory, zero.  */
+/* For an arg passed partly in registers and partly in memory, this is
+   the number of registers used.  For args passed entirely in registers
+   or entirely in memory, zero.  When an arg is described by a PARALLEL,
+   perhaps using more than one register type, this function returns the
+   number of registers used by the first element of the PARALLEL.  */
 
 int
 function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
                            tree type, int named)
 {
   int ret = 0;
+  int align;
+  int parm_offset;
+  int align_words;
 
   if (DEFAULT_ABI == ABI_V4)
     return 0;
@@ -4591,17 +5177,29 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       && cum->nargs_prototype >= 0)
     return 0;
 
-  if (USE_FP_FOR_ARG_P (cum, mode, type))
+  align = function_arg_boundary (mode, type) / PARM_BOUNDARY - 1;
+  parm_offset = TARGET_32BIT ? 2 : 0;
+  align_words = cum->words + ((parm_offset - cum->words) & align);
+
+  if (USE_FP_FOR_ARG_P (cum, mode, type)
+      /* If we are passing this arg in gprs as well, then this function
+        should return the number of gprs (or memory) partially passed,
+        *not* the number of fprs.  */
+      && !(type
+          && (cum->nargs_prototype <= 0
+              || (DEFAULT_ABI == ABI_AIX
+                  && TARGET_XL_CALL
+                  && align_words >= GP_ARG_NUM_REG))))
     {
       if (cum->fregno + ((GET_MODE_SIZE (mode) + 7) >> 3) > FP_ARG_MAX_REG + 1)
-       ret = FP_ARG_MAX_REG - cum->fregno;
+       ret = FP_ARG_MAX_REG + 1 - cum->fregno;
       else if (cum->nargs_prototype >= 0)
        return 0;
     }
 
-  if (cum->words < GP_ARG_NUM_REG
-      && GP_ARG_NUM_REG < cum->words + rs6000_arg_size (mode, type))
-    ret = GP_ARG_NUM_REG - cum->words;
+  if (align_words < GP_ARG_NUM_REG
+      && GP_ARG_NUM_REG < align_words + rs6000_arg_size (mode, type))
+    ret = GP_ARG_NUM_REG - align_words;
 
   if (ret != 0 && TARGET_DEBUG_ARG)
     fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
@@ -4615,26 +5213,31 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
    the argument itself.  The pointer is passed in whatever way is
    appropriate for passing a pointer to that type.
 
-   Under V.4, structures and unions are passed by reference.
+   Under V.4, aggregates and long double are passed by reference.
+
+   As an extension to all 32-bit ABIs, AltiVec vectors are passed by
+   reference unless the AltiVec vector extension ABI is in force.
 
    As an extension to all ABIs, variable sized types are passed by
    reference.  */
 
-int
-function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, 
-                               enum machine_mode mode ATTRIBUTE_UNUSED, 
-                               tree type, int named ATTRIBUTE_UNUSED)
+static bool
+rs6000_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, 
+                         enum machine_mode mode ATTRIBUTE_UNUSED, 
+                         tree type, bool named ATTRIBUTE_UNUSED)
 {
-  if (DEFAULT_ABI == ABI_V4
-      && ((type && AGGREGATE_TYPE_P (type))
-         || mode == TFmode))
+  if ((DEFAULT_ABI == ABI_V4
+       && ((type && AGGREGATE_TYPE_P (type))
+          || mode == TFmode))
+      || (TARGET_32BIT && !TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+      || (type && int_size_in_bytes (type) < 0))
     {
       if (TARGET_DEBUG_ARG)
-       fprintf (stderr, "function_arg_pass_by_reference: aggregate\n");
+       fprintf (stderr, "function_arg_pass_by_reference\n");
 
       return 1;
     }
-  return type && int_size_in_bytes (type) < 0;
+  return 0;
 }
 
 static void
@@ -4711,7 +5314,7 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       save_area = virtual_incoming_args_rtx;
       cfun->machine->sysv_varargs_p = 0;
 
-      if (MUST_PASS_IN_STACK (mode, type))
+      if (targetm.calls.must_pass_in_stack (mode, type))
        first_reg_offset += rs6000_arg_size (TYPE_MODE (type), type);
     }
 
@@ -4831,10 +5434,10 @@ rs6000_va_start (tree valist, rtx nextarg)
   f_sav = TREE_CHAIN (f_ovf);
 
   valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
 
   /* Count number of gp and fp argument registers used.  */
   words = current_function_args_info.words;
@@ -4846,11 +5449,13 @@ rs6000_va_start (tree valist, rtx nextarg)
             HOST_WIDE_INT_PRINT_DEC", n_fpr = "HOST_WIDE_INT_PRINT_DEC"\n",
             words, n_gpr, n_fpr);
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0));
+  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
+            build_int_cst (NULL_TREE, n_gpr, 0));
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0));
+  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
+            build_int_cst (NULL_TREE, n_fpr, 0));
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -4858,7 +5463,7 @@ rs6000_va_start (tree valist, rtx nextarg)
   t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
   if (words != 0)
     t = build (PLUS_EXPR, TREE_TYPE (ovf), t,
-              build_int_2 (words * UNITS_PER_WORD, 0));
+              build_int_cst (NULL_TREE, words * UNITS_PER_WORD, 0));
   t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -4866,7 +5471,7 @@ rs6000_va_start (tree valist, rtx nextarg)
   /* Find the register save area.  */
   t = make_tree (TREE_TYPE (sav), virtual_stack_vars_rtx);
   t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_2 (-RS6000_VARARGS_SIZE, -1));
+            build_int_cst (NULL_TREE, -RS6000_VARARGS_SIZE, -1));
   t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -4874,36 +5479,25 @@ rs6000_va_start (tree valist, rtx nextarg)
 
 /* Implement va_arg.  */
 
-rtx
-rs6000_va_arg (tree valist, tree type)
+tree
+rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
 {
   tree f_gpr, f_fpr, f_res, f_ovf, f_sav;
   tree gpr, fpr, ovf, sav, reg, t, u;
-  int indirect_p, size, rsize, n_reg, sav_ofs, sav_scale;
-  rtx lab_false, lab_over, addr_rtx, r;
+  int size, rsize, n_reg, sav_ofs, sav_scale;
+  tree lab_false, lab_over, addr;
+  int align;
+  tree ptrtype = build_pointer_type (type);
+
+  if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
+    {
+      t = rs6000_gimplify_va_arg (valist, ptrtype, pre_p, post_p);
+      return build_fold_indirect_ref (t);
+    }
 
   if (DEFAULT_ABI != ABI_V4)
     {
-      /* Variable sized types are passed by reference.  */
-      if (int_size_in_bytes (type) < 0)
-       {
-         u = build_pointer_type (type);
-
-         /* Args grow upward.  */
-         t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist,
-                    build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         t = build1 (NOP_EXPR, build_pointer_type (u), t);
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         t = build1 (INDIRECT_REF, u, t);
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
-       }
-      if (targetm.calls.split_complex_arg
-         && TREE_CODE (type) == COMPLEX_TYPE)
+      if (targetm.calls.split_complex_arg && TREE_CODE (type) == COMPLEX_TYPE)
        {
          tree elem_type = TREE_TYPE (type);
          enum machine_mode elem_mode = TYPE_MODE (elem_type);
@@ -4911,33 +5505,24 @@ rs6000_va_arg (tree valist, tree type)
 
          if (elem_size < UNITS_PER_WORD)
            {
-             rtx real_part, imag_part, dest_real, rr;
-
-             real_part = rs6000_va_arg (valist, elem_type);
-             imag_part = rs6000_va_arg (valist, elem_type);
-
-             /* We're not returning the value here, but the address.
-                real_part and imag_part are not contiguous, and we know
-                there is space available to pack real_part next to
-                imag_part.  float _Complex is not promoted to
-                double _Complex by the default promotion rules that
-                promote float to double.  */
-             if (2 * elem_size > UNITS_PER_WORD)
-               abort ();
+             tree real_part, imag_part;
+             tree post = NULL_TREE;
 
-             real_part = gen_rtx_MEM (elem_mode, real_part);
-             imag_part = gen_rtx_MEM (elem_mode, imag_part);
+             real_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p,
+                                                 &post);
+             /* Copy the value into a temporary, lest the formal temporary
+                be reused out from under us.  */
+             real_part = get_initialized_tmp_var (real_part, pre_p, &post);
+             append_to_statement_list (post, pre_p);
 
-             dest_real = adjust_address (imag_part, elem_mode, -elem_size);
-             rr = gen_reg_rtx (elem_mode);
-             emit_move_insn (rr, real_part);
-             emit_move_insn (dest_real, rr);
+             imag_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p,
+                                                 post_p);
 
-             return XEXP (dest_real, 0);
+             return build (COMPLEX_EXPR, type, real_part, imag_part);
            }
        }
 
-      return std_expand_builtin_va_arg (valist, type);
+      return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
     }
 
   f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
@@ -4947,159 +5532,131 @@ rs6000_va_arg (tree valist, tree type)
   f_sav = TREE_CHAIN (f_ovf);
 
   valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
 
   size = int_size_in_bytes (type);
-  rsize = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  rsize = (size + 3) / 4;
+  align = 1;
 
-  if (AGGREGATE_TYPE_P (type) || TYPE_MODE (type) == TFmode)
-    {
-      /* Aggregates and long doubles are passed by reference.  */
-      indirect_p = 1;
-      reg = gpr;
-      n_reg = 1;
-      sav_ofs = 0;
-      sav_scale = 4;
-      size = UNITS_PER_WORD;
-      rsize = 1;
-    }
-  else if (FLOAT_TYPE_P (type) && TARGET_HARD_FLOAT && TARGET_FPRS)
+  if (TARGET_HARD_FLOAT && TARGET_FPRS
+      && (TYPE_MODE (type) == SFmode || TYPE_MODE (type) == DFmode))
     {
       /* FP args go in FP registers, if present.  */
-      indirect_p = 0;
       reg = fpr;
       n_reg = 1;
       sav_ofs = 8*4;
       sav_scale = 8;
+      if (TYPE_MODE (type) == DFmode)
+       align = 8;
     }
   else
     {
       /* Otherwise into GP registers.  */
-      indirect_p = 0;
       reg = gpr;
       n_reg = rsize;
       sav_ofs = 0;
       sav_scale = 4;
+      if (n_reg == 2)
+       align = 8;
     }
 
   /* Pull the value out of the saved registers....  */
 
-  lab_false = gen_label_rtx ();
-  lab_over = gen_label_rtx ();
-  addr_rtx = gen_reg_rtx (Pmode);
+  lab_over = NULL;
+  addr = create_tmp_var (ptr_type_node, "addr");
+  DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set ();
 
-  /*  AltiVec vectors never go in registers.  */
-  if (!TARGET_ALTIVEC || TREE_CODE (type) != VECTOR_TYPE)
+  /*  AltiVec vectors never go in registers when -mabi=altivec.  */
+  if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (TYPE_MODE (type)))
+    align = 16;
+  else
     {
-      TREE_THIS_VOLATILE (reg) = 1;
-      emit_cmp_and_jump_insns
-       (expand_expr (reg, NULL_RTX, QImode, EXPAND_NORMAL),
-        GEN_INT (8 - n_reg + 1), GE, const1_rtx, QImode, 1,
-        lab_false);
+      lab_false = create_artificial_label ();
+      lab_over = create_artificial_label ();
 
-      /* Long long is aligned in the registers.  */
-      if (n_reg > 1)
+      /* Long long and SPE vectors are aligned in the registers.
+        As are any other 2 gpr item such as complex int due to a
+        historical mistake.  */
+      u = reg;
+      if (n_reg == 2)
        {
-         u = build (BIT_AND_EXPR, TREE_TYPE (reg), reg,
-                    build_int_2 (n_reg - 1, 0));
-         u = build (PLUS_EXPR, TREE_TYPE (reg), reg, u);
-         u = build (MODIFY_EXPR, TREE_TYPE (reg), reg, u);
-         TREE_SIDE_EFFECTS (u) = 1;
-         expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), reg,
+                    size_int (n_reg - 1));
+         u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, u);
        }
 
-      if (sav_ofs)
-       t = build (PLUS_EXPR, ptr_type_node, sav, build_int_2 (sav_ofs, 0));
-      else
-       t = sav;
+      t = fold_convert (TREE_TYPE (reg), size_int (8 - n_reg + 1));
+      t = build2 (GE_EXPR, boolean_type_node, u, t);
+      u = build1 (GOTO_EXPR, void_type_node, lab_false);
+      t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
+      gimplify_and_add (t, pre_p);
 
-      u = build (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg,
-                build_int_2 (n_reg, 0));
-      TREE_SIDE_EFFECTS (u) = 1;
+      t = sav;
+      if (sav_ofs)
+       t = build2 (PLUS_EXPR, ptr_type_node, sav, size_int (sav_ofs));
 
+      u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, size_int (n_reg));
       u = build1 (CONVERT_EXPR, integer_type_node, u);
-      TREE_SIDE_EFFECTS (u) = 1;
+      u = build2 (MULT_EXPR, integer_type_node, u, size_int (sav_scale));
+      t = build2 (PLUS_EXPR, ptr_type_node, t, u);
 
-      u = build (MULT_EXPR, integer_type_node, u, build_int_2 (sav_scale, 0));
-      TREE_SIDE_EFFECTS (u) = 1;
+      t = build2 (MODIFY_EXPR, void_type_node, addr, t);
+      gimplify_and_add (t, pre_p);
 
-      t = build (PLUS_EXPR, ptr_type_node, t, u);
-      TREE_SIDE_EFFECTS (t) = 1;
+      t = build1 (GOTO_EXPR, void_type_node, lab_over);
+      gimplify_and_add (t, pre_p);
 
-      r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-      if (r != addr_rtx)
-       emit_move_insn (addr_rtx, r);
+      t = build1 (LABEL_EXPR, void_type_node, lab_false);
+      append_to_statement_list (t, pre_p);
 
-      emit_jump_insn (gen_jump (lab_over));
-      emit_barrier ();
+      if (n_reg > 2)
+       {
+         /* Ensure that we don't find any more args in regs.
+            Alignment has taken care of the n_reg == 2 case.  */
+         t = build (MODIFY_EXPR, TREE_TYPE (reg), reg, size_int (8));
+         gimplify_and_add (t, pre_p);
+       }
     }
 
-  emit_label (lab_false);
-
   /* ... otherwise out of the overflow area.  */
 
-  /* Make sure we don't find reg 7 for the next int arg.
-
-     All AltiVec vectors go in the overflow area.  So in the AltiVec
-     case we need to get the vectors from the overflow area, but
-     remember where the GPRs and FPRs are.  */
-  if (n_reg > 1 && (TREE_CODE (type) != VECTOR_TYPE
-                   || !TARGET_ALTIVEC))
-    {
-      t = build (MODIFY_EXPR, TREE_TYPE (reg), reg, build_int_2 (8, 0));
-      TREE_SIDE_EFFECTS (t) = 1;
-      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-    }
-
   /* Care for on-stack alignment if needed.  */
-  if (rsize <= 1)
-    t = ovf;
-  else
+  t = ovf;
+  if (align != 1)
     {
-      int align;
-
-      /* AltiVec vectors are 16 byte aligned.  */
-      if (TARGET_ALTIVEC && TREE_CODE (type) == VECTOR_TYPE)
-       align = 15;
-      else
-       align = 7;
-
-      t = build (PLUS_EXPR, TREE_TYPE (ovf), ovf, build_int_2 (align, 0));
-      t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align-1, -1));
+      t = build2 (PLUS_EXPR, TREE_TYPE (t), t, size_int (align - 1));
+      t = build2 (BIT_AND_EXPR, TREE_TYPE (t), t,
+                 build_int_cst (NULL_TREE, -align, -1));
     }
-  t = save_expr (t);
-
-  r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-  if (r != addr_rtx)
-    emit_move_insn (addr_rtx, r);
+  gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (size, 0));
-  t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  u = build2 (MODIFY_EXPR, void_type_node, addr, t);
+  gimplify_and_add (u, pre_p);
 
-  emit_label (lab_over);
+  t = build2 (PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
+  t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
+  gimplify_and_add (t, pre_p);
 
-  if (indirect_p)
+  if (lab_over)
     {
-      r = gen_rtx_MEM (Pmode, addr_rtx);
-      set_mem_alias_set (r, get_varargs_alias_set ());
-      emit_move_insn (addr_rtx, r);
+      t = build1 (LABEL_EXPR, void_type_node, lab_over);
+      append_to_statement_list (t, pre_p);
     }
 
-  return addr_rtx;
+  addr = fold_convert (ptrtype, addr);
+  return build_fold_indirect_ref (addr);
 }
 
 /* Builtins.  */
 
-#define def_builtin(MASK, NAME, TYPE, CODE)                    \
-do {                                                           \
-  if ((MASK) & target_flags)                                   \
-    builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,     \
-                     NULL, NULL_TREE);                         \
+#define def_builtin(MASK, NAME, TYPE, CODE)                            \
+do {                                                                   \
+  if ((MASK) & target_flags)                                           \
+    lang_hooks.builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,  \
+                                NULL, NULL_TREE);                      \
 } while (0)
 
 /* Simple ternary operations: VECd = foo (VECa, VECb, VECc).  */
@@ -5529,7 +6086,7 @@ static struct builtin_description bdesc_1arg[] =
   { 0, CODE_FOR_spe_evfsnabs, "__builtin_spe_evfsnabs", SPE_BUILTIN_EVFSNABS },
   { 0, CODE_FOR_spe_evfsneg, "__builtin_spe_evfsneg", SPE_BUILTIN_EVFSNEG },
   { 0, CODE_FOR_spe_evmra, "__builtin_spe_evmra", SPE_BUILTIN_EVMRA },
-  { 0, CODE_FOR_spe_evneg, "__builtin_spe_evneg", SPE_BUILTIN_EVNEG },
+  { 0, CODE_FOR_negv2si2, "__builtin_spe_evneg", SPE_BUILTIN_EVNEG },
   { 0, CODE_FOR_spe_evrndw, "__builtin_spe_evrndw", SPE_BUILTIN_EVRNDW },
   { 0, CODE_FOR_spe_evsubfsmiaaw, "__builtin_spe_evsubfsmiaaw", SPE_BUILTIN_EVSUBFSMIAAW },
   { 0, CODE_FOR_spe_evsubfssiaaw, "__builtin_spe_evsubfssiaaw", SPE_BUILTIN_EVSUBFSSIAAW },
@@ -5818,6 +6375,39 @@ altivec_expand_lv_builtin (enum insn_code icode, tree arglist, rtx target)
 }
 
 static rtx
+spe_expand_stv_builtin (enum insn_code icode, tree arglist)
+{
+  tree arg0 = TREE_VALUE (arglist);
+  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+  tree arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+  rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+  rtx op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0);
+  rtx pat;
+  enum machine_mode mode0 = insn_data[icode].operand[0].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode2 = insn_data[icode].operand[2].mode;
+
+  /* Invalid arguments.  Bail before doing anything stoopid!  */
+  if (arg0 == error_mark_node
+      || arg1 == error_mark_node
+      || arg2 == error_mark_node)
+    return const0_rtx;
+
+  if (! (*insn_data[icode].operand[2].predicate) (op0, mode2))
+    op0 = copy_to_mode_reg (mode2, op0);
+  if (! (*insn_data[icode].operand[0].predicate) (op1, mode0))
+    op1 = copy_to_mode_reg (mode0, op1);
+  if (! (*insn_data[icode].operand[1].predicate) (op2, mode1))
+    op2 = copy_to_mode_reg (mode1, op2);
+
+  pat = GEN_FCN (icode) (op1, op2, op0);
+  if (pat)
+    emit_insn (pat);
+  return NULL_RTX;
+}
+
+static rtx
 altivec_expand_stv_builtin (enum insn_code icode, tree arglist)
 {
   tree arg0 = TREE_VALUE (arglist);
@@ -6344,33 +6934,33 @@ spe_expand_builtin (tree exp, rtx target, bool *expandedp)
   switch (fcode)
     {
     case SPE_BUILTIN_EVSTDDX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstddx, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstddx, arglist);
     case SPE_BUILTIN_EVSTDHX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstdhx, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstdhx, arglist);
     case SPE_BUILTIN_EVSTDWX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstdwx, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstdwx, arglist);
     case SPE_BUILTIN_EVSTWHEX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwhex, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwhex, arglist);
     case SPE_BUILTIN_EVSTWHOX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwhox, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwhox, arglist);
     case SPE_BUILTIN_EVSTWWEX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwwex, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwwex, arglist);
     case SPE_BUILTIN_EVSTWWOX:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwwox, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwwox, arglist);
     case SPE_BUILTIN_EVSTDD:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstdd, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstdd, arglist);
     case SPE_BUILTIN_EVSTDH:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstdh, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstdh, arglist);
     case SPE_BUILTIN_EVSTDW:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstdw, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstdw, arglist);
     case SPE_BUILTIN_EVSTWHE:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwhe, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwhe, arglist);
     case SPE_BUILTIN_EVSTWHO:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwho, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwho, arglist);
     case SPE_BUILTIN_EVSTWWE:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwwe, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwwe, arglist);
     case SPE_BUILTIN_EVSTWWO:
-      return altivec_expand_stv_builtin (CODE_FOR_spe_evstwwo, arglist);
+      return spe_expand_stv_builtin (CODE_FOR_spe_evstwwo, arglist);
     case SPE_BUILTIN_MFSPEFSCR:
       icode = CODE_FOR_spe_mfspefscr;
       tmode = insn_data[icode].operand[0].mode;
@@ -6628,6 +7218,14 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   return NULL_RTX;
 }
 
+static tree
+build_opaque_vector_type (tree node, int nunits)
+{
+  node = copy_node (node);
+  TYPE_MAIN_VARIANT (node) = node;
+  return build_vector_type (node, nunits);
+}
+
 static void
 rs6000_init_builtins (void)
 {
@@ -6643,8 +7241,8 @@ rs6000_init_builtins (void)
   unsigned_V8HI_type_node = build_vector_type (unsigned_intHI_type_node, 8);
   unsigned_V4SI_type_node = build_vector_type (unsigned_intSI_type_node, 4);
 
-  opaque_V2SI_type_node = copy_node (V2SI_type_node);
-  opaque_V2SF_type_node = copy_node (V2SF_type_node);
+  opaque_V2SF_type_node = build_opaque_vector_type (float_type_node, 2);
+  opaque_V2SI_type_node = build_opaque_vector_type (intSI_type_node, 2);
   opaque_p_V2SI_type_node = build_pointer_type (opaque_V2SI_type_node);
 
   /* The 'vector bool ...' types must be kept distinct from 'vector unsigned ...'
@@ -7020,8 +7618,8 @@ altivec_init_builtins (void)
     = build_function_type (V8HI_type_node, void_list_node);
   tree void_ftype_void
     = build_function_type (void_type_node, void_list_node);
-  tree void_ftype_qi
-    = build_function_type_list (void_type_node, char_type_node, NULL_TREE);
+  tree void_ftype_int
+    = build_function_type_list (void_type_node, integer_type_node, NULL_TREE);
 
   tree v16qi_ftype_long_pcvoid
     = build_function_type_list (V16QI_type_node,
@@ -7092,7 +7690,7 @@ altivec_init_builtins (void)
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_mtvscr", void_ftype_v4si, ALTIVEC_BUILTIN_MTVSCR);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_mfvscr", v8hi_ftype_void, ALTIVEC_BUILTIN_MFVSCR);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_dssall", void_ftype_void, ALTIVEC_BUILTIN_DSSALL);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_dss", void_ftype_qi, ALTIVEC_BUILTIN_DSS);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_dss", void_ftype_int, ALTIVEC_BUILTIN_DSS);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSL);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSR);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEBX);
@@ -7686,6 +8284,89 @@ rs6000_init_libfuncs (void)
       set_conv_libfunc (sfloat_optab, TFmode, SImode, "_q_itoq");
     }
 }
+
+\f
+/* Expand a block clear operation, and return 1 if successful.  Return 0
+   if we should let the compiler generate normal code.
+
+   operands[0] is the destination
+   operands[1] is the length
+   operands[2] is the alignment */
+
+int
+expand_block_clear (rtx operands[])
+{
+  rtx orig_dest = operands[0];
+  rtx bytes_rtx        = operands[1];
+  rtx align_rtx = operands[2];
+  int constp   = (GET_CODE (bytes_rtx) == CONST_INT);
+  int align;
+  int bytes;
+  int offset;
+  int clear_bytes;
+
+  /* If this is not a fixed size move, just call memcpy */
+  if (! constp)
+    return 0;
+
+  /* If this is not a fixed size alignment, abort */
+  if (GET_CODE (align_rtx) != CONST_INT)
+    abort ();
+  align = INTVAL (align_rtx) * BITS_PER_UNIT;
+
+  /* Anything to clear? */
+  bytes = INTVAL (bytes_rtx);
+  if (bytes <= 0)
+    return 1;
+
+  if (bytes > (TARGET_POWERPC64 && align >= 32 ? 64 : 32))
+    return 0;
+
+  if (optimize_size && bytes > 16)
+    return 0;
+
+  for (offset = 0; bytes > 0; offset += clear_bytes, bytes -= clear_bytes)
+    {
+      rtx (*mov) (rtx, rtx);
+      enum machine_mode mode = BLKmode;
+      rtx dest;
+      
+      if (bytes >= 8 && TARGET_POWERPC64
+              /* 64-bit loads and stores require word-aligned
+                 displacements.  */
+              && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32)))
+       {
+         clear_bytes = 8;
+         mode = DImode;
+         mov = gen_movdi;
+       }
+      else if (bytes >= 4 && !STRICT_ALIGNMENT)
+       {                       /* move 4 bytes */
+         clear_bytes = 4;
+         mode = SImode;
+         mov = gen_movsi;
+       }
+      else if (bytes == 2 && !STRICT_ALIGNMENT)
+       {                       /* move 2 bytes */
+         clear_bytes = 2;
+         mode = HImode;
+         mov = gen_movhi;
+       }
+      else /* move 1 byte at a time */
+       {
+         clear_bytes = 1;
+         mode = QImode;
+         mov = gen_movqi;
+       }
+      
+      dest = adjust_address (orig_dest, mode, offset);
+      
+      emit_insn ((*mov) (dest, const0_rtx));
+    }
+
+  return 1;
+}
+
 \f
 /* Expand a block move operation, and return 1 if successful.  Return 0
    if we should let the compiler generate normal code.
@@ -7719,7 +8400,7 @@ expand_block_move (rtx operands[])
   /* If this is not a fixed size alignment, abort */
   if (GET_CODE (align_rtx) != CONST_INT)
     abort ();
-  align = INTVAL (align_rtx);
+  align = INTVAL (align_rtx) * BITS_PER_UNIT;
 
   /* Anything to move? */
   bytes = INTVAL (bytes_rtx);
@@ -7734,7 +8415,7 @@ expand_block_move (rtx operands[])
   for (offset = 0; bytes > 0; offset += move_bytes, bytes -= move_bytes)
     {
       union {
-       rtx (*movstrsi) (rtx, rtx, rtx, rtx);
+       rtx (*movmemsi) (rtx, rtx, rtx, rtx);
        rtx (*mov) (rtx, rtx);
       } gen_func;
       enum machine_mode mode = BLKmode;
@@ -7752,7 +8433,7 @@ expand_block_move (rtx operands[])
          && ! fixed_regs[12])
        {
          move_bytes = (bytes > 32) ? 32 : bytes;
-         gen_func.movstrsi = gen_movstrsi_8reg;
+         gen_func.movmemsi = gen_movmemsi_8reg;
        }
       else if (TARGET_STRING
               && bytes > 16    /* move up to 24 bytes at a time */
@@ -7764,7 +8445,7 @@ expand_block_move (rtx operands[])
               && ! fixed_regs[10])
        {
          move_bytes = (bytes > 24) ? 24 : bytes;
-         gen_func.movstrsi = gen_movstrsi_6reg;
+         gen_func.movmemsi = gen_movmemsi_6reg;
        }
       else if (TARGET_STRING
               && bytes > 8     /* move up to 16 bytes at a time */
@@ -7774,12 +8455,12 @@ expand_block_move (rtx operands[])
               && ! fixed_regs[8])
        {
          move_bytes = (bytes > 16) ? 16 : bytes;
-         gen_func.movstrsi = gen_movstrsi_4reg;
+         gen_func.movmemsi = gen_movmemsi_4reg;
        }
       else if (bytes >= 8 && TARGET_POWERPC64
               /* 64-bit loads and stores require word-aligned
                  displacements.  */
-              && (align >= 8 || (! STRICT_ALIGNMENT && align >= 4)))
+              && (align >= 64 || (!STRICT_ALIGNMENT && align >= 32)))
        {
          move_bytes = 8;
          mode = DImode;
@@ -7788,15 +8469,15 @@ expand_block_move (rtx operands[])
       else if (TARGET_STRING && bytes > 4 && !TARGET_POWERPC64)
        {                       /* move up to 8 bytes at a time */
          move_bytes = (bytes > 8) ? 8 : bytes;
-         gen_func.movstrsi = gen_movstrsi_2reg;
+         gen_func.movmemsi = gen_movmemsi_2reg;
        }
-      else if (bytes >= 4 && (align >= 4 || ! STRICT_ALIGNMENT))
+      else if (bytes >= 4 && !STRICT_ALIGNMENT)
        {                       /* move 4 bytes */
          move_bytes = 4;
          mode = SImode;
          gen_func.mov = gen_movsi;
        }
-      else if (bytes == 2 && (align >= 2 || ! STRICT_ALIGNMENT))
+      else if (bytes == 2 && !STRICT_ALIGNMENT)
        {                       /* move 2 bytes */
          move_bytes = 2;
          mode = HImode;
@@ -7805,7 +8486,7 @@ expand_block_move (rtx operands[])
       else if (TARGET_STRING && bytes > 1)
        {                       /* move up to 4 bytes at a time */
          move_bytes = (bytes > 4) ? 4 : bytes;
-         gen_func.movstrsi = gen_movstrsi_1reg;
+         gen_func.movmemsi = gen_movmemsi_1reg;
        }
       else /* move 1 byte at a time */
        {
@@ -7835,7 +8516,7 @@ expand_block_move (rtx operands[])
 
       if (mode == BLKmode)
        {
-         /* Move the address into scratch registers.  The movstrsi
+         /* Move the address into scratch registers.  The movmemsi
             patterns require zero offset.  */
          if (!REG_P (XEXP (src, 0)))
            {
@@ -7851,7 +8532,7 @@ expand_block_move (rtx operands[])
            }
          set_mem_size (dest, GEN_INT (move_bytes));
          
-         emit_insn ((*gen_func.movstrsi) (dest, src,
+         emit_insn ((*gen_func.movmemsi) (dest, src,
                                           GEN_INT (move_bytes & 31),
                                           align_rtx));
        }
@@ -7861,6 +8542,143 @@ expand_block_move (rtx operands[])
 }
 
 \f
+/* Return 1 if OP is suitable for a save_world call in prologue. It is
+   known to be a PARALLEL. */
+int
+save_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  int index;
+  int i;
+  rtx elt;
+  int count = XVECLEN (op, 0);
+
+  if (count != 55)
+    return 0;
+
+  index = 0;
+  if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
+      || GET_CODE (XVECEXP (op, 0, index++)) != USE)
+    return 0;
+
+  for (i=1; i <= 18; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || ! memory_operand (SET_DEST (elt), DFmode)
+         || GET_CODE (SET_SRC (elt)) != REG
+         || GET_MODE (SET_SRC (elt)) != DFmode)
+       return 0;
+    }
+
+  for (i=1; i <= 12; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || GET_CODE (SET_SRC (elt)) != REG
+         || GET_MODE (SET_SRC (elt)) != V4SImode)
+       return 0;
+    }
+
+  for (i=1; i <= 19; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || ! memory_operand (SET_DEST (elt), Pmode)
+         || GET_CODE (SET_SRC (elt)) != REG
+         || GET_MODE (SET_SRC (elt)) != Pmode)
+       return 0;
+    }
+
+  elt = XVECEXP (op, 0, index++);
+  if (GET_CODE (elt) != SET
+      || GET_CODE (SET_DEST (elt)) != MEM
+      || ! memory_operand (SET_DEST (elt), Pmode)
+      || GET_CODE (SET_SRC (elt)) != REG
+      || REGNO (SET_SRC (elt)) != CR2_REGNO
+      || GET_MODE (SET_SRC (elt)) != Pmode)
+    return 0;
+
+  if (GET_CODE (XVECEXP (op, 0, index++)) != USE
+      || GET_CODE (XVECEXP (op, 0, index++)) != USE
+      || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
+    return 0;
+  return 1;
+}
+
+/* Return 1 if OP is suitable for a save_world call in prologue. It is
+   known to be a PARALLEL. */
+int
+restore_world_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  int index;
+  int i;
+  rtx elt;
+  int count = XVECLEN (op, 0);
+
+  if (count != 59)
+    return 0;
+
+  index = 0;
+  if (GET_CODE (XVECEXP (op, 0, index++)) != RETURN
+      || GET_CODE (XVECEXP (op, 0, index++)) != USE
+      || GET_CODE (XVECEXP (op, 0, index++)) != USE
+      || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER)
+    return 0;
+
+  elt = XVECEXP (op, 0, index++);
+  if (GET_CODE (elt) != SET
+      || GET_CODE (SET_SRC (elt)) != MEM
+      || ! memory_operand (SET_SRC (elt), Pmode)
+      || GET_CODE (SET_DEST (elt)) != REG
+      || REGNO (SET_DEST (elt)) != CR2_REGNO
+      || GET_MODE (SET_DEST (elt)) != Pmode)
+    return 0;
+
+  for (i=1; i <= 19; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_SRC (elt)) != MEM
+         || ! memory_operand (SET_SRC (elt), Pmode)
+         || GET_CODE (SET_DEST (elt)) != REG
+         || GET_MODE (SET_DEST (elt)) != Pmode)
+       return 0;
+    }
+
+  for (i=1; i <= 12; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_SRC (elt)) != MEM
+         || GET_CODE (SET_DEST (elt)) != REG
+         || GET_MODE (SET_DEST (elt)) != V4SImode)
+       return 0;
+    }
+
+  for (i=1; i <= 18; i++)
+    {
+      elt = XVECEXP (op, 0, index++);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_SRC (elt)) != MEM
+         || ! memory_operand (SET_SRC (elt), DFmode)
+         || GET_CODE (SET_DEST (elt)) != REG
+         || GET_MODE (SET_DEST (elt)) != DFmode)
+       return 0;
+    }
+
+  if (GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
+      || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
+      || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
+      || GET_CODE (XVECEXP (op, 0, index++)) != CLOBBER
+      || GET_CODE (XVECEXP (op, 0, index++)) != USE)
+    return 0;
+  return 1;
+}
+
+\f
 /* Return 1 if OP is a load multiple operation.  It is known to be a
    PARALLEL and the first section will be tested.  */
 
@@ -8162,7 +8980,7 @@ lmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
       if (base_regno == 0)
        return 0;
     }
-  else if (legitimate_offset_address_p (SImode, src_addr, 0))
+  else if (rs6000_legitimate_offset_address_p (SImode, src_addr, 0))
     {
       offset = INTVAL (XEXP (src_addr, 1));
       base_regno = REGNO (XEXP (src_addr, 0));
@@ -8190,7 +9008,7 @@ lmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
          newoffset = 0;
          addr_reg = newaddr;
        }
-      else if (legitimate_offset_address_p (SImode, newaddr, 0))
+      else if (rs6000_legitimate_offset_address_p (SImode, newaddr, 0))
        {
          addr_reg = XEXP (newaddr, 0);
          newoffset = INTVAL (XEXP (newaddr, 1));
@@ -8238,7 +9056,7 @@ stmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
       if (base_regno == 0)
        return 0;
     }
-  else if (legitimate_offset_address_p (SImode, dest_addr, 0))
+  else if (rs6000_legitimate_offset_address_p (SImode, dest_addr, 0))
     {
       offset = INTVAL (XEXP (dest_addr, 1));
       base_regno = REGNO (XEXP (dest_addr, 0));
@@ -8266,7 +9084,7 @@ stmw_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
          newoffset = 0;
          addr_reg = newaddr;
        }
-      else if (legitimate_offset_address_p (SImode, newaddr, 0))
+      else if (rs6000_legitimate_offset_address_p (SImode, newaddr, 0))
        {
          addr_reg = XEXP (newaddr, 0);
          newoffset = INTVAL (XEXP (newaddr, 1));
@@ -8590,11 +9408,28 @@ includes_rldicr_lshift_p (rtx shiftop, rtx andop)
     return 0;
 }
 
-/* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates
-   for lfq and stfq insns.
+/* Return 1 if operands will generate a valid arguments to rlwimi
+instruction for insert with right shift in 64-bit mode.  The mask may
+not start on the first bit or stop on the last bit because wrap-around
+effects of instruction do not correspond to semantics of RTL insn.  */
+
+int
+insvdi_rshift_rlwimi_p (rtx sizeop, rtx startop, rtx shiftop)
+{
+  if (INTVAL (startop) < 64
+      && INTVAL (startop) > 32
+      && (INTVAL (sizeop) + INTVAL (startop) < 64)
+      && (INTVAL (sizeop) + INTVAL (startop) > 33)
+      && (INTVAL (sizeop) + INTVAL (startop) + INTVAL (shiftop) < 96)
+      && (INTVAL (sizeop) + INTVAL (startop) + INTVAL (shiftop) >= 64)
+      && (64 - (INTVAL (shiftop) & 63)) >= INTVAL (sizeop))
+    return 1;
+
+  return 0;
+}
 
-   Note reg1 and reg2 *must* be hard registers.  To be sure we will
-   abort if we are passed pseudo registers.  */
+/* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates
+   for lfq and stfq insns iff the registers are hard registers.   */
 
 int
 registers_ok_for_quad_peep (rtx reg1, rtx reg2)
@@ -8602,6 +9437,11 @@ registers_ok_for_quad_peep (rtx reg1, rtx reg2)
   /* We might have been passed a SUBREG.  */
   if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) 
     return 0;
+    
+  /* We might have been passed non floating point registers.  */
+  if (!FP_REGNO_P (REGNO (reg1))
+      || !FP_REGNO_P (REGNO (reg2)))
+    return 0;
 
   return (REGNO (reg1) == REGNO (reg2) - 1);
 }
@@ -8611,11 +9451,19 @@ registers_ok_for_quad_peep (rtx reg1, rtx reg2)
    (addr2 == addr1 + 8).  */
 
 int
-addrs_ok_for_quad_peep (rtx addr1, rtx addr2)
+mems_ok_for_quad_peep (rtx mem1, rtx mem2)
 {
+  rtx addr1, addr2;
   unsigned int reg1;
   int offset1;
 
+  /* The mems cannot be volatile.  */
+  if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
+    return 0;
+  
+  addr1 = XEXP (mem1, 0);
+  addr2 = XEXP (mem2, 0);
+
   /* Extract an offset (if used) from the first addr.  */
   if (GET_CODE (addr1) == PLUS)
     {
@@ -8946,6 +9794,36 @@ rs6000_get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
   return 0;
 }
 
+/* Write out a function code label.  */
+
+void
+rs6000_output_function_entry (FILE *file, const char *fname)
+{
+  if (fname[0] != '.')
+    {
+      switch (DEFAULT_ABI)
+       {
+       default:
+         abort ();
+
+       case ABI_AIX:
+         if (DOT_SYMBOLS)
+           putc ('.', file);
+         else
+           ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "L.");
+         break;
+
+       case ABI_V4:
+       case ABI_DARWIN:
+         break;
+       }
+    }
+  if (TARGET_AIX)
+    RS6000_OUTPUT_BASENAME (file, fname);
+  else
+    assemble_name (file, fname);
+}
+
 /* Print an operand.  Recognize special options, documented below.  */
 
 #if TARGET_ELF
@@ -9011,12 +9889,12 @@ print_operand (FILE *file, rtx x, int code)
       return;
 
     case 'D':
-      /* Like 'J' but get to the GT bit.  */
+      /* Like 'J' but get to the EQ bit.  */
       if (GET_CODE (x) != REG)
        abort ();
 
-      /* Bit 1 is GT bit.  */
-      i = 4 * (REGNO (x) - CR0_REGNO) + 1;
+      /* Bit 1 is EQ bit.  */
+      i = 4 * (REGNO (x) - CR0_REGNO) + 2;
 
       /* If we want bit 31, write a shift count of zero, not 32.  */
       fprintf (file, "%d", i == 31 ? 0 : i + 1);
@@ -9474,36 +10352,25 @@ print_operand (FILE *file, rtx x, int code)
       if (GET_CODE (x) != SYMBOL_REF)
        abort ();
 
-      if (XSTR (x, 0)[0] != '.')
-       {
-         switch (DEFAULT_ABI)
-           {
-           default:
-             abort ();
-
-           case ABI_AIX:
-             putc ('.', file);
-             break;
+      /* Mark the decl as referenced so that cgraph will output the function.  */
+      if (SYMBOL_REF_DECL (x))
+        mark_decl_referenced (SYMBOL_REF_DECL (x));
 
-           case ABI_V4:
-           case ABI_DARWIN:
-             break;
-           }
-       }
-      /* For macho, we need to check it see if we need a stub.  */
+      /* For macho, check to see if we need a stub.  */
       if (TARGET_MACHO)
        {
          const char *name = XSTR (x, 0);
-#ifdef TARGET_MACHO
-         if (machopic_classify_name (name) == MACHOPIC_UNDEFINED_FUNCTION)
-           name = machopic_stub_name (name);
+#if TARGET_MACHO
+         if (MACHOPIC_INDIRECT
+             && machopic_classify_symbol (x) == MACHOPIC_UNDEFINED_FUNCTION)
+           name = machopic_indirection_name (x, /*stub_p=*/true);
 #endif
          assemble_name (file, name);
        }
-     else if (TARGET_AIX)
-       RS6000_OUTPUT_BASENAME (file, XSTR (x, 0));
-      else
+      else if (!DOT_SYMBOLS)
        assemble_name (file, XSTR (x, 0));
+      else
+       rs6000_output_function_entry (file, XSTR (x, 0));
       return;
 
     case 'Z':
@@ -9759,7 +10626,9 @@ rs6000_assemble_visibility (tree decl, int vis)
 {
   /* Functions need to have their entry point symbol visibility set as
      well as their descriptor symbol visibility.  */
-  if (DEFAULT_ABI == ABI_AIX && TREE_CODE (decl) == FUNCTION_DECL)
+  if (DEFAULT_ABI == ABI_AIX
+      && DOT_SYMBOLS
+      && TREE_CODE (decl) == FUNCTION_DECL)
     {
       static const char * const visibility_types[] = {
         NULL, "internal", "hidden", "protected"
@@ -9969,9 +10838,9 @@ rs6000_emit_sCOND (enum rtx_code code, rtx result)
        abort ();
 
       if (cond_code == NE)
-       emit_insn (gen_e500_flip_gt_bit (t, t));
+       emit_insn (gen_e500_flip_eq_bit (t, t));
 
-      emit_insn (gen_move_from_CR_gt_bit (result, t));
+      emit_insn (gen_move_from_CR_eq_bit (result, t));
       return;
     }
 
@@ -10152,9 +11021,9 @@ output_cbranch (rtx op, const char *label, int reversed, rtx insn)
   return string;
 }
 
-/* Return the string to flip the GT bit on a CR.  */
+/* Return the string to flip the EQ bit on a CR.  */
 char *
-output_e500_flip_gt_bit (rtx dst, rtx src)
+output_e500_flip_eq_bit (rtx dst, rtx src)
 {
   static char string[64];
   int a, b;
@@ -10163,9 +11032,9 @@ output_e500_flip_gt_bit (rtx dst, rtx src)
       || GET_CODE (src) != REG || ! CR_REGNO_P (REGNO (src)))
     abort ();
 
-  /* GT bit.  */
-  a = 4 * (REGNO (dst) - CR0_REGNO) + 1;
-  b = 4 * (REGNO (src) - CR0_REGNO) + 1;
+  /* EQ bit.  */
+  a = 4 * (REGNO (dst) - CR0_REGNO) + 2;
+  b = 4 * (REGNO (src) - CR0_REGNO) + 2;
 
   sprintf (string, "crnot %d,%d", a, b);
   return string;
@@ -10458,7 +11327,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
       int j = -1;
       bool used_update = false;
 
-      if (GET_CODE (src) == MEM && INT_REGNO_P (reg))
+      if (MEM_P (src) && INT_REGNO_P (reg))
         {
           rtx breg;
 
@@ -10475,6 +11344,15 @@ rs6000_split_multireg_move (rtx dst, rtx src)
                         : gen_adddi3 (breg, breg, delta_rtx));
              src = gen_rtx_MEM (mode, breg);
            }
+         else if (! offsettable_memref_p (src))
+           {
+             rtx newsrc, basereg;
+             basereg = gen_rtx_REG (Pmode, reg);
+             emit_insn (gen_rtx_SET (VOIDmode, basereg, XEXP (src, 0)));
+             newsrc = gen_rtx_MEM (GET_MODE (src), basereg);
+             MEM_COPY_ATTRIBUTES (newsrc, src);
+             src = newsrc;
+           }
 
          /* We have now address involving an base register only.
             If we use one of the registers to address memory, 
@@ -10522,6 +11400,8 @@ rs6000_split_multireg_move (rtx dst, rtx src)
                           : gen_adddi3 (breg, breg, delta_rtx));
              dst = gen_rtx_MEM (mode, breg);
            }
+         else if (! offsettable_memref_p (dst))
+           abort ();
        }
 
       for (i = 0; i < nregs; i++)
@@ -10563,7 +11443,8 @@ first_reg_to_save (void)
        && (! call_used_regs[first_reg]
            || (first_reg == RS6000_PIC_OFFSET_TABLE_REGNUM
                && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
-                   || (DEFAULT_ABI == ABI_DARWIN && flag_pic)))))
+                   || (DEFAULT_ABI == ABI_DARWIN && flag_pic)
+                   || (TARGET_TOC && TARGET_MINIMAL_TOC)))))
       break;
 
 #if TARGET_MACHO
@@ -10646,6 +11527,57 @@ compute_vrsave_mask (void)
   return mask;
 }
 
+/* For a very restricted set of circumstances, we can cut down the
+   size of prologs/epilogs by calling our own save/restore-the-world
+   routines. */
+
+static void
+compute_save_world_info(rs6000_stack_t *info_ptr)
+{
+  info_ptr->world_save_p =
+    (DEFAULT_ABI == ABI_DARWIN)
+    && ! (current_function_calls_setjmp && flag_exceptions)
+    && info_ptr->first_fp_reg_save == FIRST_SAVED_FP_REGNO
+    && info_ptr->first_gp_reg_save == FIRST_SAVED_GP_REGNO
+    && info_ptr->first_altivec_reg_save == FIRST_SAVED_ALTIVEC_REGNO
+    && info_ptr->cr_save_p;
+  
+  /* This will not work in conjunction with sibcalls.  Make sure there
+     are none.  (This check is expensive, but seldom executed.) */
+  if ( info_ptr->world_save_p )
+    { 
+      rtx insn;
+      for ( insn = get_last_insn_anywhere (); insn; insn = PREV_INSN (insn))
+        if ( GET_CODE (insn) == CALL_INSN
+             && SIBLING_CALL_P (insn))
+          { 
+            info_ptr->world_save_p = 0;
+            break;
+          }
+    }
+  
+  if (info_ptr->world_save_p)
+    {
+      /* Even if we're not touching VRsave, make sure there's room on the
+        stack for it, if it looks like we're calling SAVE_WORLD, which
+         will attempt to save it. */
+      info_ptr->vrsave_size  = 4;
+
+      /* "Save" the VRsave register too if we're saving the world.  */
+      if (info_ptr->vrsave_mask == 0)
+        info_ptr->vrsave_mask = compute_vrsave_mask ();
+
+      /* Because the Darwin register save/restore routines only handle
+         F14 .. F31 and V20 .. V31 as per the ABI, perform a consistancy
+         check and abort if there's something worng.  */
+      if (info_ptr->first_fp_reg_save < FIRST_SAVED_FP_REGNO 
+          || info_ptr->first_altivec_reg_save < FIRST_SAVED_ALTIVEC_REGNO)
+        abort ();
+    }
+  return; 
+}
+
+
 static void
 is_altivec_return_reg (rtx reg, void *xyes)
 {
@@ -10759,7 +11691,7 @@ rs6000_stack_info (void)
   rs6000_stack_t *info_ptr = &info;
   int reg_size = TARGET_32BIT ? 4 : 8;
   int ehrd_size;
-  HOST_WIDE_INT total_raw_size;
+  HOST_WIDE_INT non_fixed_size;
 
   /* Zero all fields portably.  */
   info = zero_info;
@@ -10886,6 +11818,8 @@ rs6000_stack_info (void)
   else
     info_ptr->vrsave_size  = 0;
 
+  compute_save_world_info (info_ptr);
+
   /* Calculate the offsets.  */
   switch (DEFAULT_ABI)
     {
@@ -10990,14 +11924,13 @@ rs6000_stack_info (void)
                                         (TARGET_ALTIVEC_ABI || ABI_DARWIN)
                                         ? 16 : 8);
 
-  total_raw_size        = (info_ptr->vars_size
+  non_fixed_size        = (info_ptr->vars_size
                            + info_ptr->parm_size
                            + info_ptr->save_size
-                           + info_ptr->varargs_size
-                           + info_ptr->fixed_size);
+                           + info_ptr->varargs_size);
 
-  info_ptr->total_size =
-    RS6000_ALIGN (total_raw_size, ABI_STACK_BOUNDARY / BITS_PER_UNIT);
+  info_ptr->total_size = RS6000_ALIGN (non_fixed_size + info_ptr->fixed_size,
+                                      ABI_STACK_BOUNDARY / BITS_PER_UNIT);
 
   /* Determine if we need to allocate any stack frame:
 
@@ -11015,7 +11948,7 @@ rs6000_stack_info (void)
     info_ptr->push_p = 1;
 
   else if (DEFAULT_ABI == ABI_V4)
-    info_ptr->push_p = total_raw_size > info_ptr->fixed_size;
+    info_ptr->push_p = non_fixed_size != 0;
 
   else if (frame_pointer_needed)
     info_ptr->push_p = 1;
@@ -11024,8 +11957,7 @@ rs6000_stack_info (void)
     info_ptr->push_p = 1;
 
   else
-    info_ptr->push_p
-      = total_raw_size - info_ptr->fixed_size > (TARGET_32BIT ? 220 : 288);
+    info_ptr->push_p = non_fixed_size > (TARGET_32BIT ? 220 : 288);
 
   /* Zero offsets if we're not saving those registers.  */
   if (info_ptr->fp_size == 0)
@@ -11861,7 +12793,7 @@ generate_set_vrsave (rtx reg, rs6000_stack_t *info, int epiloguep)
      need an unspec use/set of the register.  */
 
   for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
-    if (info->vrsave_mask != 0 && ALTIVEC_REG_BIT (i) != 0)
+    if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
       {
        if (!epiloguep || call_used_regs [i])
          clobs[nclobs++] = gen_rtx_CLOBBER (VOIDmode,
@@ -11950,6 +12882,10 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
   return gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode, reg, offset_rtx));
 }
 
+#ifndef TARGET_FIX_AND_CONTINUE
+#define TARGET_FIX_AND_CONTINUE 0
+#endif
+
 /* Emit function prologue as insns.  */
 
 void
@@ -11967,11 +12903,24 @@ rs6000_emit_prologue (void)
   int using_store_multiple;
   HOST_WIDE_INT sp_offset = 0;
   
-   if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0)
-     {
-       reg_mode = V2SImode;
-       reg_size = 8;
-     }
+  if (TARGET_FIX_AND_CONTINUE)
+    {
+      /* gdb on darwin arranges to forward a function from the old
+        address by modifying the first 4 instructions of the function
+        to branch to the overriding function.  This is necessary to
+        permit function pointers that point to the old function to
+        actually forward to the new function.  */
+      emit_insn (gen_nop ());
+      emit_insn (gen_nop ());
+      emit_insn (gen_nop ());
+      emit_insn (gen_nop ());
+    }
+
+  if (TARGET_SPE_ABI && info->spe_64bit_regs_used != 0)
+    {
+      reg_mode = V2SImode;
+      reg_size = 8;
+    }
 
   using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
                          && (!TARGET_SPE_ABI
@@ -12002,8 +12951,128 @@ rs6000_emit_prologue (void)
        rs6000_emit_stack_tie ();
     }
 
+  /* Handle world saves specially here.  */
+  if (info->world_save_p)
+    {
+      int i, j, sz;
+      rtx treg;
+      rtvec p;
+
+      /* save_world expects lr in r0. */
+      if (info->lr_save_p)
+        {
+          insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
+                                gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
+          RTX_FRAME_RELATED_P (insn) = 1;
+        }
+
+      /* The SAVE_WORLD and RESTORE_WORLD routines make a number of
+         assumptions about the offsets of various bits of the stack
+         frame.  Abort if things aren't what they should be.  */
+      if (info->gp_save_offset != -220
+          || info->fp_save_offset != -144
+          || info->lr_save_offset != 8
+          || info->cr_save_offset != 4
+          || !info->push_p
+          || !info->lr_save_p
+          || (current_function_calls_eh_return && info->ehrd_offset != -432)
+          || (info->vrsave_save_offset != -224
+              || info->altivec_save_offset != (-224 -16 -192)))
+        abort ();
+
+      treg = gen_rtx_REG (SImode, 11);
+      emit_move_insn (treg, GEN_INT (-info->total_size));
+
+      /* SAVE_WORLD takes the caller's LR in R0 and the frame size
+         in R11.  It also clobbers R12, so beware!  */
+
+      /* Preserve CR2 for save_world prologues */
+      sz = 6;
+      sz += 32 - info->first_gp_reg_save;
+      sz += 64 - info->first_fp_reg_save;
+      sz += LAST_ALTIVEC_REGNO - info->first_altivec_reg_save + 1;
+      p = rtvec_alloc (sz);
+      j = 0;
+      RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode,
+                                            gen_rtx_REG (Pmode,
+                                                         LINK_REGISTER_REGNUM));
+      RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
+                                        gen_rtx_SYMBOL_REF (Pmode,
+                                                            "*save_world"));
+      /* We do floats first so that the instruction pattern matches
+         properly.  */
+     for (i = 0; i < 64 - info->first_fp_reg_save; i++)
+        {
+          rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->fp_save_offset
+                                            + sp_offset + 8 * i));
+          rtx mem = gen_rtx_MEM (DFmode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+        }
+      for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
+        {
+          rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->altivec_save_offset
+                                            + sp_offset + 16 * i));
+          rtx mem = gen_rtx_MEM (V4SImode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+        }
+      for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+        {
+          rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->gp_save_offset
+                                            + sp_offset + reg_size * i));
+          rtx mem = gen_rtx_MEM (reg_mode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+        }
+
+        {
+          /* CR register traditionally saved as CR2.  */
+          rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->cr_save_offset
+                                            + sp_offset));
+          rtx mem = gen_rtx_MEM (reg_mode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
+        }
+      /* Prevent any attempt to delete the setting of r0 and treg!  */
+      RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
+      RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode, treg);
+      RTVEC_ELT (p, j++) = gen_rtx_CLOBBER (VOIDmode, sp_reg_rtx);
+
+      insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+                            NULL_RTX, NULL_RTX);
+
+      if (current_function_calls_eh_return)
+        {
+          unsigned int i;
+          for (i = 0; ; ++i)
+            {
+              unsigned int regno = EH_RETURN_DATA_REGNO (i);
+              if (regno == INVALID_REGNUM)
+                break;
+              emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno,
+                               info->ehrd_offset + sp_offset
+                               + reg_size * (int) i,
+                               info->total_size);
+            }
+        }
+    }
+
   /* Save AltiVec registers if needed.  */
-  if (TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+  if (! info->world_save_p && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
     {
       int i;
 
@@ -12044,7 +13113,7 @@ rs6000_emit_prologue (void)
      epilogue.  */
 
   if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
-      && info->vrsave_mask != 0)
+      && ! info->world_save_p && info->vrsave_mask != 0)
     {
       rtx reg, mem, vrsave;
       int offset;
@@ -12072,20 +13141,37 @@ rs6000_emit_prologue (void)
     }
 
   /* If we use the link register, get it into r0.  */
-  if (info->lr_save_p)
-    emit_move_insn (gen_rtx_REG (Pmode, 0),
-                   gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
+  if (! info->world_save_p && info->lr_save_p)
+    {
+      insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
+                            gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
   /* If we need to save CR, put it into r12.  */
-  if (info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
+  if (! info->world_save_p && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
     {
+      rtx set;
+      
       cr_save_rtx = gen_rtx_REG (SImode, 12);
-      emit_insn (gen_movesi_from_cr (cr_save_rtx));
+      insn = emit_insn (gen_movesi_from_cr (cr_save_rtx));
+      RTX_FRAME_RELATED_P (insn) = 1;
+      /* Now, there's no way that dwarf2out_frame_debug_expr is going
+        to understand '(unspec:SI [(reg:CC 68) ...] UNSPEC_MOVESI_FROM_CR)'.
+        But that's OK.  All we have to do is specify that _one_ condition
+        code register is saved in this stack slot.  The thrower's epilogue
+        will then restore all the call-saved registers.
+        We use CR2_REGNO (70) to be compatible with gcc-2.95 on Linux.  */
+      set = gen_rtx_SET (VOIDmode, cr_save_rtx,
+                        gen_rtx_REG (SImode, CR2_REGNO));
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                           set,
+                                           REG_NOTES (insn));
     }
 
   /* Do any required saving of fpr's.  If only one or two to save, do
      it ourselves.  Otherwise, call function.  */
-  if (saving_FPRs_inline)
+  if (! info->world_save_p && saving_FPRs_inline)
     {
       int i;
       for (i = 0; i < 64 - info->first_fp_reg_save; i++)
@@ -12096,7 +13182,7 @@ rs6000_emit_prologue (void)
                           info->fp_save_offset + sp_offset + 8 * i,
                           info->total_size);
     }
-  else if (info->first_fp_reg_save != 64)
+  else if (! info->world_save_p && info->first_fp_reg_save != 64)
     {
       int i;
       char rname[30];
@@ -12132,7 +13218,7 @@ rs6000_emit_prologue (void)
 
   /* Save GPRs.  This is done as a PARALLEL if we are using
      the store-multiple instructions.  */
-  if (using_store_multiple)
+  if (! info->world_save_p && using_store_multiple)
     {
       rtvec p;
       int i;
@@ -12154,12 +13240,15 @@ rs6000_emit_prologue (void)
       rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, 
                            NULL_RTX, NULL_RTX);
     }
-  else
+  else if (! info->world_save_p)
     {
       int i;
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
        if ((regs_ever_live[info->first_gp_reg_save+i] 
-            && ! call_used_regs[info->first_gp_reg_save+i])
+            && (! call_used_regs[info->first_gp_reg_save+i]
+                || (i+info->first_gp_reg_save
+                    == RS6000_PIC_OFFSET_TABLE_REGNUM
+                    && TARGET_TOC && TARGET_MINIMAL_TOC)))
            || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
                && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
                    || (DEFAULT_ABI == ABI_DARWIN && flag_pic))))
@@ -12210,7 +13299,7 @@ rs6000_emit_prologue (void)
 
   /* ??? There's no need to emit actual instructions here, but it's the
      easiest way to get the frame unwind information emitted.  */
-  if (current_function_calls_eh_return)
+  if (! info->world_save_p && current_function_calls_eh_return)
     {
       unsigned int i, regno;
 
@@ -12245,7 +13334,7 @@ rs6000_emit_prologue (void)
     }
 
   /* Save lr if we used it.  */
-  if (info->lr_save_p)
+  if (! info->world_save_p && info->lr_save_p)
     {
       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
                               GEN_INT (info->lr_save_offset + sp_offset));
@@ -12256,15 +13345,17 @@ rs6000_emit_prologue (void)
       
       insn = emit_move_insn (mem, reg);
       rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, 
-                           reg, gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM));
+                           NULL_RTX, NULL_RTX);
     }
 
   /* Save CR if we use any that must be preserved.  */
-  if (info->cr_save_p)
+  if (! info->world_save_p && info->cr_save_p)
     {
       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
                               GEN_INT (info->cr_save_offset + sp_offset));
       rtx mem = gen_rtx_MEM (SImode, addr);
+      /* See the large comment above about why CR2_REGNO is used.  */
+      rtx magic_eh_cr_reg = gen_rtx_REG (SImode, CR2_REGNO);
 
       set_mem_alias_set (mem, rs6000_sr_alias_set);
 
@@ -12272,24 +13363,26 @@ rs6000_emit_prologue (void)
         that it's free.  */
       if (REGNO (frame_reg_rtx) == 12)
        {
+         rtx set;
+
          cr_save_rtx = gen_rtx_REG (SImode, 0);
-         emit_insn (gen_movesi_from_cr (cr_save_rtx));
+         insn = emit_insn (gen_movesi_from_cr (cr_save_rtx));
+         RTX_FRAME_RELATED_P (insn) = 1;
+         set = gen_rtx_SET (VOIDmode, cr_save_rtx, magic_eh_cr_reg);
+         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                               set,
+                                               REG_NOTES (insn));
+         
        }
       insn = emit_move_insn (mem, cr_save_rtx);
 
-      /* Now, there's no way that dwarf2out_frame_debug_expr is going
-        to understand '(unspec:SI [(reg:CC 68) ...] UNSPEC_MOVESI_FROM_CR)'.
-        But that's OK.  All we have to do is specify that _one_ condition
-        code register is saved in this stack slot.  The thrower's epilogue
-        will then restore all the call-saved registers.
-        We use CR2_REGNO (70) to be compatible with gcc-2.95 on Linux.  */
       rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, 
-                           cr_save_rtx, gen_rtx_REG (SImode, CR2_REGNO));
+                           NULL_RTX, NULL_RTX);
     }
 
   /* Update stack and set back pointer unless this is V.4, 
      for which it was done previously.  */
-  if (info->push_p
+  if (! info->world_save_p && info->push_p
       && !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return))
     rs6000_emit_allocate_stack (info->total_size, FALSE);
 
@@ -12318,9 +13411,16 @@ rs6000_emit_prologue (void)
     if (save_LR_around_toc_setup)
       {
        rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
-       rs6000_maybe_dead (emit_move_insn (frame_ptr_rtx, lr));
+
+       insn = emit_move_insn (frame_ptr_rtx, lr);
+       rs6000_maybe_dead (insn);
+       RTX_FRAME_RELATED_P (insn) = 1;
+
        rs6000_emit_load_toc_table (TRUE);
-       rs6000_maybe_dead (emit_move_insn (lr, frame_ptr_rtx));
+
+       insn = emit_move_insn (lr, frame_ptr_rtx);
+       rs6000_maybe_dead (insn);
+       RTX_FRAME_RELATED_P (insn) = 1;
       }
     else
       rs6000_emit_load_toc_table (TRUE);
@@ -12330,15 +13430,15 @@ rs6000_emit_prologue (void)
   if (DEFAULT_ABI == ABI_DARWIN
       && flag_pic && current_function_uses_pic_offset_table)
     {
-      rtx dest = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
-      const char *picbase = machopic_function_base_name ();
-      rtx src = gen_rtx_SYMBOL_REF (Pmode, picbase);
+      rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
+      rtx src = machopic_function_base_sym ();
 
-      rs6000_maybe_dead (emit_insn (gen_load_macho_picbase (dest, src)));
+      rs6000_maybe_dead (emit_insn (gen_load_macho_picbase (lr, src)));
 
-      rs6000_maybe_dead (
-       emit_move_insn (gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM),
-                       gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)));
+      insn = emit_move_insn (gen_rtx_REG (Pmode, 
+                                         RS6000_PIC_OFFSET_TABLE_REGNUM),
+                            lr);
+      rs6000_maybe_dead (insn);
     }
 #endif
 }
@@ -12451,6 +13551,101 @@ rs6000_emit_epilogue (int sibcall)
                         || rs6000_cpu == PROCESSOR_PPC750
                         || optimize_size);
 
+  if (info->world_save_p)
+    {
+      int i, j;
+      char rname[30];
+      const char *alloc_rname;
+      rtvec p;
+
+      /* eh_rest_world_r10 will return to the location saved in the LR
+         stack slot (which is not likely to be our caller.)
+         Input: R10 -- stack adjustment.  Clobbers R0, R11, R12, R7, R8.
+         rest_world is similar, except any R10 parameter is ignored.
+         The exception-handling stuff that was here in 2.95 is no
+         longer necessary.  */
+
+      p = rtvec_alloc (9
+                      + 1
+                      + 32 - info->first_gp_reg_save 
+                       + LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
+                       + 63 + 1 - info->first_fp_reg_save);
+
+      strcpy (rname, (current_function_calls_eh_return) ?
+                        "*eh_rest_world_r10" : "*rest_world");
+      alloc_rname = ggc_strdup (rname);
+
+      j = 0;
+      RTVEC_ELT (p, j++) = gen_rtx_RETURN (VOIDmode);
+      RTVEC_ELT (p, j++) = gen_rtx_USE (VOIDmode,
+                                        gen_rtx_REG (Pmode,
+                                                     LINK_REGISTER_REGNUM));
+      RTVEC_ELT (p, j++)
+        = gen_rtx_USE (VOIDmode, gen_rtx_SYMBOL_REF (Pmode, alloc_rname));
+      /* The instruction pattern requires a clobber here;
+         it is shared with the restVEC helper. */
+      RTVEC_ELT (p, j++)
+        = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
+
+      {
+        /* CR register traditionally saved as CR2.  */
+        rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
+        rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                 GEN_INT (info->cr_save_offset));
+        rtx mem = gen_rtx_MEM (reg_mode, addr);
+        set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+        RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+      }
+
+      for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+        {
+          rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->gp_save_offset
+                                            + reg_size * i));
+          rtx mem = gen_rtx_MEM (reg_mode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+      }
+      for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
+        {
+          rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->altivec_save_offset
+                                            + 16 * i));
+          rtx mem = gen_rtx_MEM (V4SImode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+        }
+      for (i = 0; info->first_fp_reg_save + i <= 63; i++)
+        {
+          rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
+          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                                   GEN_INT (info->fp_save_offset
+                                            + 8 * i));
+          rtx mem = gen_rtx_MEM (DFmode, addr);
+          set_mem_alias_set (mem, rs6000_sr_alias_set);
+
+          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+        }
+      RTVEC_ELT (p, j++)
+        = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 0));
+      RTVEC_ELT (p, j++)
+        = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 12));
+      RTVEC_ELT (p, j++)
+        = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 7));
+      RTVEC_ELT (p, j++)
+        = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (SImode, 8));
+      RTVEC_ELT (p, j++)
+        = gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, 10));
+      emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
+
+      return;
+    }
+
   /* If we have a frame pointer, a call to alloca,  or a large stack
      frame, restore the old stack pointer using the backchain.  Otherwise,
      we know what size to update it with.  */
@@ -12506,7 +13701,7 @@ rs6000_emit_epilogue (int sibcall)
     }
 
   /* Restore VRSAVE if needed.  */
-  if (TARGET_ALTIVEC_ABI && TARGET_ALTIVEC_VRSAVE 
+  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
       && info->vrsave_mask != 0)
     {
       rtx addr, mem, reg;
@@ -12608,7 +13803,9 @@ rs6000_emit_epilogue (int sibcall)
   else
     for (i = 0; i < 32 - info->first_gp_reg_save; i++)
       if ((regs_ever_live[info->first_gp_reg_save+i] 
-          && ! call_used_regs[info->first_gp_reg_save+i])
+          && (! call_used_regs[info->first_gp_reg_save+i]
+              || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
+                  && TARGET_TOC && TARGET_MINIMAL_TOC)))
          || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
              && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
                  || (DEFAULT_ABI == ABI_DARWIN && flag_pic))))
@@ -12919,7 +14116,8 @@ rs6000_output_function_epilogue (FILE *file,
         Java is 13.  Objective-C is 14.  */
       if (! strcmp (language_string, "GNU C"))
        i = 0;
-      else if (! strcmp (language_string, "GNU F77"))
+      else if (! strcmp (language_string, "GNU F77")
+              || ! strcmp (language_string, "GNU F95"))
        i = 1;
       else if (! strcmp (language_string, "GNU Pascal"))
        i = 2;
@@ -13040,17 +14238,12 @@ rs6000_output_function_epilogue (FILE *file,
       /* Offset from start of code to tb table.  */
       fputs ("\t.long ", file);
       ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT");
-#if TARGET_AIX
-      RS6000_OUTPUT_BASENAME (file, fname);
-#else
-      assemble_name (file, fname);
-#endif
-      fputs ("-.", file);
-#if TARGET_AIX
-      RS6000_OUTPUT_BASENAME (file, fname);
-#else
-      assemble_name (file, fname);
-#endif
+      if (TARGET_AIX)
+       RS6000_OUTPUT_BASENAME (file, fname);
+      else
+       assemble_name (file, fname);
+      putc ('-', file);
+      rs6000_output_function_entry (file, fname);
       putc ('\n', file);
 
       /* Interrupt handler mask.  */
@@ -13122,6 +14315,7 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   reload_completed = 1;
   epilogue_completed = 1;
   no_new_pseudos = 1;
+  reset_block_changes ();
 
   /* Mark the end of the (empty) prologue.  */
   emit_note (NOTE_INSN_PROLOGUE_END);
@@ -13824,12 +15018,9 @@ output_profile_hook (int labelno ATTRIBUTE_UNUSED)
 #if TARGET_MACHO
       /* For PIC code, set up a stub and collect the caller's address
         from r0, which is where the prologue puts it.  */
-      if (MACHOPIC_INDIRECT)
-       {
-         mcount_name = machopic_stub_name (mcount_name);
-         if (current_function_uses_pic_offset_table)
-           caller_addr_regno = 0;
-       }
+      if (MACHOPIC_INDIRECT
+         && current_function_uses_pic_offset_table)
+       caller_addr_regno = 0;
 #endif
       emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mcount_name),
                         0, VOIDmode, 1,
@@ -13914,7 +15105,7 @@ output_function_profiler (FILE *file, int labelno)
          asm_fprintf (file, "\tmflr %s\n", reg_names[0]);
          asm_fprintf (file, "\tstd %s,16(%s)\n", reg_names[0], reg_names[1]);
 
-         if (current_function_needs_context)
+         if (cfun->static_chain_decl != NULL)
            {
              asm_fprintf (file, "\tstd %s,24(%s)\n",
                           reg_names[STATIC_CHAIN_REGNUM], reg_names[1]);
@@ -13930,12 +15121,6 @@ output_function_profiler (FILE *file, int labelno)
 }
 
 \f
-static int
-rs6000_use_dfa_pipeline_interface (void)
-{
-  return 1;
-}
-
 /* Power4 load update and store update instructions are cracked into a
    load or store and an integer insn which are executed in the same cycle.
    Branches have their own dispatch slot which does not count against the
@@ -14969,6 +16154,9 @@ rs6000_handle_altivec_attribute (tree *node, tree name, tree args,
     default: break;
     }
 
+  if (result && result != type && TYPE_READONLY (type))
+    result = build_qualified_type (result, TYPE_QUAL_CONST);
+
   *no_add_attrs = true;  /* No need to hang on to the attribute.  */
 
   if (!result)
@@ -15244,7 +16432,7 @@ static void
 add_compiler_branch_island (tree label_name, tree function_name, int line_number)
 {
   tree branch_island = build_tree_list (function_name, label_name);
-  TREE_TYPE (branch_island) = build_int_2 (line_number, 0);
+  TREE_TYPE (branch_island) = build_int_cst (NULL_TREE, line_number, 0);
   TREE_CHAIN (branch_island) = branch_island_list;
   branch_island_list = branch_island;
 }
@@ -15273,8 +16461,7 @@ macho_branch_islands (void)
       const char *label =
        IDENTIFIER_POINTER (BRANCH_ISLAND_LABEL_NAME (branch_island));
       const char *name  =
-       darwin_strip_name_encoding (
-         IDENTIFIER_POINTER (BRANCH_ISLAND_FUNCTION_NAME (branch_island)));
+       IDENTIFIER_POINTER (BRANCH_ISLAND_FUNCTION_NAME (branch_island));
       char name_buf[512];
       /* Cheap copy of the details from the Darwin ASM_OUTPUT_LABELREF().  */
       if (name[0] == '*' || name[0] == '&')
@@ -15436,13 +16623,14 @@ machopic_output_stub (FILE *file, const char *symb, const char *stub)
     machopic_picsymbol_stub1_section ();
   else
     machopic_symbol_stub1_section ();
-  fprintf (file, "\t.align 2\n");
-
-  fprintf (file, "%s:\n", stub);
-  fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
 
   if (flag_pic == 2)
     {
+      fprintf (file, "\t.align 5\n");
+
+      fprintf (file, "%s:\n", stub);
+      fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
+
       label++;
       local_label_0 = alloca (sizeof("\"L0000000000$spb\""));
       sprintf (local_label_0, "\"L%011d$spb\"", label);
@@ -15459,17 +16647,23 @@ machopic_output_stub (FILE *file, const char *symb, const char *stub)
       fprintf (file, "\tbctr\n");
     }
   else
-   {
-     fprintf (file, "\tlis r11,ha16(%s)\n", lazy_ptr_name);
-     fprintf (file, "\tlwzu r12,lo16(%s)(r11)\n", lazy_ptr_name);
-     fprintf (file, "\tmtctr r12\n");
-     fprintf (file, "\tbctr\n");
-   }
+    {
+      fprintf (file, "\t.align 4\n");
+
+      fprintf (file, "%s:\n", stub);
+      fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
+
+      fprintf (file, "\tlis r11,ha16(%s)\n", lazy_ptr_name);
+      fprintf (file, "\tlwzu r12,lo16(%s)(r11)\n", lazy_ptr_name);
+      fprintf (file, "\tmtctr r12\n");
+      fprintf (file, "\tbctr\n");
+    }
   
   machopic_lazy_symbol_ptr_section ();
   fprintf (file, "%s:\n", lazy_ptr_name);
   fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
-  fprintf (file, "\t.long dyld_stub_binding_helper\n");
+  fprintf (file, "%sdyld_stub_binding_helper\n",
+          (TARGET_64BIT ? DOUBLE_INT_ASM_OP : "\t.long\t"));
 }
 
 /* Legitimize PIC addresses.  If the address is already
@@ -15622,22 +16816,27 @@ rs6000_elf_declare_function_name (FILE *file, const char *name, tree decl)
       fputs ("\t.section\t\".opd\",\"aw\"\n\t.align 3\n", file);
       ASM_OUTPUT_LABEL (file, name);
       fputs (DOUBLE_INT_ASM_OP, file);
-      putc ('.', file);
-      assemble_name (file, name);
-      fputs (",.TOC.@tocbase,0\n\t.previous\n\t.size\t", file);
-      assemble_name (file, name);
-      fputs (",24\n\t.type\t.", file);
-      assemble_name (file, name);
-      fputs (",@function\n", file);
-      if (TREE_PUBLIC (decl) && ! DECL_WEAK (decl))
-       {
-         fputs ("\t.globl\t.", file);
+      rs6000_output_function_entry (file, name);
+      fputs (",.TOC.@tocbase,0\n\t.previous\n", file);
+      if (DOT_SYMBOLS)
+       {
+         fputs ("\t.size\t", file);
+         assemble_name (file, name);
+         fputs (",24\n\t.type\t.", file);
          assemble_name (file, name);
-         putc ('\n', file);
+         fputs (",@function\n", file);
+         if (TREE_PUBLIC (decl) && ! DECL_WEAK (decl))
+           {
+             fputs ("\t.globl\t.", file);
+             assemble_name (file, name);
+             putc ('\n', file);
+           }
        }
+      else
+       ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function");
       ASM_DECLARE_RESULT (file, DECL_RESULT (decl));
-      putc ('.', file);
-      ASM_OUTPUT_LABEL (file, name);
+      rs6000_output_function_entry (file, name);
+      fputs (":\n", file);
       return;
     }
 
@@ -15870,221 +17069,317 @@ static bool
 rs6000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, 
                  int *total)
 {
+  enum machine_mode mode = GET_MODE (x);
+
   switch (code)
     {
-      /* On the RS/6000, if it is valid in the insn, it is free.
-        So this always returns 0.  */
+      /* On the RS/6000, if it is valid in the insn, it is free.  */
     case CONST_INT:
-    case CONST:
-    case LABEL_REF:
-    case SYMBOL_REF:
-    case CONST_DOUBLE:
-    case HIGH:
-      *total = 0;
-      return true;
-
-    case PLUS:
-      *total = ((GET_CODE (XEXP (x, 1)) == CONST_INT
-                && ((unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1))
-                                              + 0x8000) >= 0x10000)
-                && ((INTVAL (XEXP (x, 1)) & 0xffff) != 0))
-               ? COSTS_N_INSNS (2)
-               : COSTS_N_INSNS (1));
-      return true;
-
-    case AND:
-    case IOR:
-    case XOR:
-      *total = ((GET_CODE (XEXP (x, 1)) == CONST_INT
-                && (INTVAL (XEXP (x, 1)) & (~ (HOST_WIDE_INT) 0xffff)) != 0
-                && ((INTVAL (XEXP (x, 1)) & 0xffff) != 0))
-               ? COSTS_N_INSNS (2)
-               : COSTS_N_INSNS (1));
-      return true;
-
-    case MULT:
-      if (optimize_size)
+      if (((outer_code == SET
+           || outer_code == PLUS
+           || outer_code == MINUS)
+          && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
+              || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')))
+         || ((outer_code == IOR || outer_code == XOR)
+             && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
+                 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')))
+         || ((outer_code == DIV || outer_code == UDIV
+              || outer_code == MOD || outer_code == UMOD)
+             && exact_log2 (INTVAL (x)) >= 0)
+         || (outer_code == AND
+             && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
+                 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')
+                 || mask_operand (x, VOIDmode)))
+         || outer_code == ASHIFT
+         || outer_code == ASHIFTRT
+         || outer_code == LSHIFTRT
+         || outer_code == ROTATE
+         || outer_code == ROTATERT
+         || outer_code == ZERO_EXTRACT
+         || (outer_code == MULT
+             && CONST_OK_FOR_LETTER_P (INTVAL (x), 'I'))
+         || (outer_code == COMPARE
+             && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
+                 || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))))
        {
-         *total = COSTS_N_INSNS (2);
+         *total = 0;
          return true;
        }
-      switch (rs6000_cpu)
+      else if ((outer_code == PLUS
+               && reg_or_add_cint64_operand (x, VOIDmode))
+              || (outer_code == MINUS
+                  && reg_or_sub_cint64_operand (x, VOIDmode))
+              || ((outer_code == SET
+                   || outer_code == IOR
+                   || outer_code == XOR)
+                  && (INTVAL (x)
+                      & ~ (unsigned HOST_WIDE_INT) 0xffffffff) == 0))
        {
-       case PROCESSOR_RIOS1:
-       case PROCESSOR_PPC405:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? COSTS_N_INSNS (5)
-                   : (INTVAL (XEXP (x, 1)) >= -256
-                      && INTVAL (XEXP (x, 1)) <= 255)
-                   ? COSTS_N_INSNS (3) : COSTS_N_INSNS (4));
-         return true;
-
-       case PROCESSOR_PPC440:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? COSTS_N_INSNS (3)
-                   : COSTS_N_INSNS (2));
-         return true;
-
-       case PROCESSOR_RS64A:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (20) : COSTS_N_INSNS (34)
-                   : (INTVAL (XEXP (x, 1)) >= -256
-                      && INTVAL (XEXP (x, 1)) <= 255)
-                   ? COSTS_N_INSNS (8) : COSTS_N_INSNS (12));
-         return true;
-
-       case PROCESSOR_RIOS2:
-       case PROCESSOR_MPCCORE:
-       case PROCESSOR_PPC604e:
-         *total = COSTS_N_INSNS (2);
+         *total = COSTS_N_INSNS (1);
          return true;
+       }
+      /* FALLTHRU */
 
-       case PROCESSOR_PPC601:
-         *total = COSTS_N_INSNS (5);
+    case CONST_DOUBLE:
+      if (mode == DImode
+         && ((outer_code == AND
+              && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
+                  || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')
+                  || mask64_operand (x, DImode)))
+             || ((outer_code == IOR || outer_code == XOR)
+                 && CONST_DOUBLE_HIGH (x) == 0
+                 && (CONST_DOUBLE_LOW (x)
+                     & ~ (unsigned HOST_WIDE_INT) 0xffff) == 0)))
+       {
+         *total = 0;
          return true;
-
-       case PROCESSOR_PPC603:
-       case PROCESSOR_PPC7400:
-       case PROCESSOR_PPC750:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? COSTS_N_INSNS (5)
-                   : (INTVAL (XEXP (x, 1)) >= -256
-                      && INTVAL (XEXP (x, 1)) <= 255)
-                   ? COSTS_N_INSNS (2) : COSTS_N_INSNS (3));
+       }
+      else if (mode == DImode
+              && (outer_code == SET
+                  || outer_code == IOR
+                  || outer_code == XOR)
+              && CONST_DOUBLE_HIGH (x) == 0)
+       {
+         *total = COSTS_N_INSNS (1);
          return true;
+       }
+      /* FALLTHRU */
 
-       case PROCESSOR_PPC7450:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? COSTS_N_INSNS (4)
-                   : COSTS_N_INSNS (3));
-         return true;
+    case CONST:
+    case HIGH:
+    case SYMBOL_REF:
+    case MEM:
+      /* When optimizing for size, MEM should be slightly more expensive
+        than generating address, e.g., (plus (reg) (const)).
+        L1 cache latecy is about two instructions.  */
+      *total = optimize_size ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
+      return true;
 
-       case PROCESSOR_PPC403:
-       case PROCESSOR_PPC604:
-       case PROCESSOR_PPC8540:
-         *total = COSTS_N_INSNS (4);
-         return true;
+    case LABEL_REF:
+      *total = 0;
+      return true;
 
-       case PROCESSOR_PPC620:
-       case PROCESSOR_PPC630:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (5) : COSTS_N_INSNS (7)
-                   : (INTVAL (XEXP (x, 1)) >= -256
-                      && INTVAL (XEXP (x, 1)) <= 255)
-                   ? COSTS_N_INSNS (3) : COSTS_N_INSNS (4));
-         return true;
+    case PLUS:
+      if (mode == DFmode)
+       {
+         if (GET_CODE (XEXP (x, 0)) == MULT)
+           {
+             /* FNMA accounted in outer NEG.  */
+             if (outer_code == NEG)
+               *total = rs6000_cost->dmul - rs6000_cost->fp;
+             else
+               *total = rs6000_cost->dmul;
+           }
+         else
+           *total = rs6000_cost->fp;
+       }
+      else if (mode == SFmode)
+       {
+         /* FNMA accounted in outer NEG.  */
+         if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT)
+           *total = 0;
+         else
+           *total = rs6000_cost->fp;
+       }
+      else if (GET_CODE (XEXP (x, 0)) == MULT)
+       {
+         /* The rs6000 doesn't have shift-and-add instructions.  */
+         rs6000_rtx_costs (XEXP (x, 0), MULT, PLUS, total);
+         *total += COSTS_N_INSNS (1);
+       }
+      else
+       *total = COSTS_N_INSNS (1);
+      return false;
 
-       case PROCESSOR_POWER4:
-       case PROCESSOR_POWER5:
-         *total = (GET_CODE (XEXP (x, 1)) != CONST_INT
-                   ? GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (3) : COSTS_N_INSNS (4)
-                   : COSTS_N_INSNS (2));
-         return true;
+    case MINUS:
+      if (mode == DFmode)
+       {
+         if (GET_CODE (XEXP (x, 0)) == MULT)
+           {
+             /* FNMA accounted in outer NEG.  */
+             if (outer_code == NEG)
+               *total = 0;
+             else
+               *total = rs6000_cost->dmul;
+           }
+         else
+           *total = rs6000_cost->fp;
+       }
+      else if (mode == SFmode)
+       {
+         /* FNMA accounted in outer NEG.  */
+         if (outer_code == NEG && GET_CODE (XEXP (x, 0)) == MULT)
+           *total = 0;
+         else
+           *total = rs6000_cost->fp;
+       }
+      else if (GET_CODE (XEXP (x, 0)) == MULT)
+       {
+         /* The rs6000 doesn't have shift-and-sub instructions.  */
+         rs6000_rtx_costs (XEXP (x, 0), MULT, MINUS, total);
+         *total += COSTS_N_INSNS (1);
+       }
+      else
+        *total = COSTS_N_INSNS (1);
+      return false;
 
-       default:
-         abort ();
+    case MULT:
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         if (INTVAL (XEXP (x, 1)) >= -256
+             && INTVAL (XEXP (x, 1)) <= 255)
+           *total = rs6000_cost->mulsi_const9;
+         else
+           *total = rs6000_cost->mulsi_const;
        }
+      /* FMA accounted in outer PLUS/MINUS.  */
+      else if ((mode == DFmode || mode == SFmode)
+              && (outer_code == PLUS || outer_code == MINUS))
+       *total = 0;
+      else if (mode == DFmode)
+       *total = rs6000_cost->dmul;
+      else if (mode == SFmode)
+       *total = rs6000_cost->fp;
+      else if (mode == DImode)
+       *total = rs6000_cost->muldi;
+      else
+       *total = rs6000_cost->mulsi;
+      return false;
 
     case DIV:
     case MOD:
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+      if (FLOAT_MODE_P (mode))
        {
-         *total = COSTS_N_INSNS (2);
-         return true;
+         *total = mode == DFmode ? rs6000_cost->ddiv
+                                 : rs6000_cost->sdiv;
+         return false;
        }
       /* FALLTHRU */
 
     case UDIV:
     case UMOD:
-      switch (rs6000_cpu)
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
        {
-       case PROCESSOR_RIOS1:
-         *total = COSTS_N_INSNS (19);
-         return true;
-
-       case PROCESSOR_RIOS2:
-         *total = COSTS_N_INSNS (13);
-         return true;
-
-       case PROCESSOR_RS64A:
-         *total = (GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (65)
-                   : COSTS_N_INSNS (67));
-         return true;
-
-       case PROCESSOR_MPCCORE:
-         *total = COSTS_N_INSNS (6);
-         return true;
-
-       case PROCESSOR_PPC403:
-         *total = COSTS_N_INSNS (33);
-         return true;
-
-       case PROCESSOR_PPC405:
-         *total = COSTS_N_INSNS (35);
-         return true;
+         if (code == DIV || code == MOD)
+           /* Shift, addze */
+           *total = COSTS_N_INSNS (2);
+         else
+           /* Shift */
+           *total = COSTS_N_INSNS (1);
+       }
+      else 
+       {
+         if (GET_MODE (XEXP (x, 1)) == DImode)
+           *total = rs6000_cost->divdi;
+         else
+           *total = rs6000_cost->divsi;
+       }
+      /* Add in shift and subtract for MOD. */
+      if (code == MOD || code == UMOD)
+       *total += COSTS_N_INSNS (2);
+      return false;
 
-       case PROCESSOR_PPC440:
-         *total = COSTS_N_INSNS (34);
-         return true;
+    case FFS:
+      *total = COSTS_N_INSNS (4);
+      return false;
 
-       case PROCESSOR_PPC601:
-         *total = COSTS_N_INSNS (36);
-         return true;
+    case NOT:
+      if (outer_code == AND || outer_code == IOR || outer_code == XOR)
+       {
+         *total = 0;
+         return false;
+       }
+      /* FALLTHRU */
 
-       case PROCESSOR_PPC603:
-         *total = COSTS_N_INSNS (37);
-         return true;
+    case AND:
+    case IOR:
+    case XOR:
+    case ZERO_EXTRACT:
+      *total = COSTS_N_INSNS (1);
+      return false;
 
-       case PROCESSOR_PPC604:
-       case PROCESSOR_PPC604e:
-         *total = COSTS_N_INSNS (20);
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ROTATE:
+    case ROTATERT:
+      /* Handle mul_highpart.  */
+      if (outer_code == TRUNCATE
+         && GET_CODE (XEXP (x, 0)) == MULT)
+       {
+         if (mode == DImode)
+           *total = rs6000_cost->muldi;
+         else
+           *total = rs6000_cost->mulsi;
          return true;
+       }
+      else if (outer_code == AND)
+       *total = 0;
+      else
+       *total = COSTS_N_INSNS (1);
+      return false;
 
-       case PROCESSOR_PPC620:
-       case PROCESSOR_PPC630:
-         *total = (GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (21)
-                   : COSTS_N_INSNS (37));
-         return true;
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      if (GET_CODE (XEXP (x, 0)) == MEM)
+       *total = 0;
+      else
+       *total = COSTS_N_INSNS (1);
+      return false;
 
-       case PROCESSOR_PPC750:
-       case PROCESSOR_PPC8540:
-       case PROCESSOR_PPC7400:
-         *total = COSTS_N_INSNS (19);
-         return true;
+    case COMPARE:
+    case NEG:
+    case ABS:
+      if (!FLOAT_MODE_P (mode))
+       {
+         *total = COSTS_N_INSNS (1);
+         return false;
+       }
+      /* FALLTHRU */
 
-       case PROCESSOR_PPC7450:
-         *total = COSTS_N_INSNS (23);
-         return true;
+    case FLOAT:
+    case UNSIGNED_FLOAT:
+    case FIX:
+    case UNSIGNED_FIX:
+    case FLOAT_EXTEND:
+    case FLOAT_TRUNCATE:
+      *total = rs6000_cost->fp;
+      return false;
 
-       case PROCESSOR_POWER4:
-       case PROCESSOR_POWER5:
-         *total = (GET_MODE (XEXP (x, 1)) != DImode
-                   ? COSTS_N_INSNS (18)
-                   : COSTS_N_INSNS (34));
+    case UNSPEC:
+      switch (XINT (x, 1))
+       {
+       case UNSPEC_FRSP:
+         *total = rs6000_cost->fp;
          return true;
 
        default:
-         abort ();
+         break;
        }
+      break;
 
-    case FFS:
-      *total = COSTS_N_INSNS (4);
-      return true;
+    case CALL:
+    case IF_THEN_ELSE:
+      if (optimize_size)
+       {
+         *total = COSTS_N_INSNS (1);
+         return true;
+       }
+      else if (FLOAT_MODE_P (mode)
+              && TARGET_PPC_GFXOPT && TARGET_HARD_FLOAT && TARGET_FPRS)
+       {
+         *total = rs6000_cost->fp;
+         return false;
+       }
 
-    case MEM:
-      /* MEM should be slightly more expensive than (plus (reg) (const)).  */
-      *total = 5;
-      return true;
+      break;
 
     default:
-      return false;
+      break;
     }
+
+  return false;
 }
 
 /* A C expression returning the cost of moving data from a register of class
@@ -16151,7 +17446,7 @@ rs6000_complex_function_value (enum machine_mode mode)
   enum machine_mode inner = GET_MODE_INNER (mode);
   unsigned int inner_bytes = GET_MODE_SIZE (inner);
 
-  if (FLOAT_MODE_P (mode))
+  if (FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
   else
     {
@@ -16209,13 +17504,14 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
   else
     mode = TYPE_MODE (valtype);
 
-  if (TREE_CODE (valtype) == REAL_TYPE && TARGET_HARD_FLOAT && TARGET_FPRS)
+  if (SCALAR_FLOAT_TYPE_P (valtype) && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
   else if (TREE_CODE (valtype) == COMPLEX_TYPE
-          && TARGET_HARD_FLOAT
           && targetm.calls.split_complex_arg)
     return rs6000_complex_function_value (mode);
-  else if (TREE_CODE (valtype) == VECTOR_TYPE && TARGET_ALTIVEC)
+  else if (TREE_CODE (valtype) == VECTOR_TYPE
+          && TARGET_ALTIVEC && TARGET_ALTIVEC_ABI
+          && ALTIVEC_VECTOR_MODE(mode))
     regno = ALTIVEC_ARG_RETURN;
   else
     regno = GP_ARG_RETURN;
@@ -16233,7 +17529,8 @@ rs6000_libcall_value (enum machine_mode mode)
   if (GET_MODE_CLASS (mode) == MODE_FLOAT
           && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
-  else if (ALTIVEC_VECTOR_MODE (mode))
+  else if (ALTIVEC_VECTOR_MODE (mode)
+          && TARGET_ALTIVEC && TARGET_ALTIVEC_ABI)
     regno = ALTIVEC_ARG_RETURN;
   else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg)
     return rs6000_complex_function_value (mode);
@@ -16335,4 +17632,11 @@ rs6000_dbx_register_number (unsigned int regno)
   abort ();
 }
 
+/* target hook eh_return_filter_mode */
+static enum machine_mode 
+rs6000_eh_return_filter_mode (void)
+{
+  return TARGET_32BIT ? SImode : word_mode;
+}
+
 #include "gt-rs6000.h"