OSDN Git Service

Revert:
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index cbbb8a8..faeae52 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for code generation on IBM RS/6000.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
    Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
@@ -52,7 +52,8 @@
 #include "reload.h"
 #include "cfglayout.h"
 #include "sched-int.h"
-#include "tree-gimple.h"
+#include "gimple.h"
+#include "tree-flow.h"
 #include "intl.h"
 #include "params.h"
 #include "tm-constrs.h"
@@ -125,6 +126,10 @@ typedef struct machine_function GTY(())
   /* Offset from virtual_stack_vars_rtx to the start of the ABI_V4
      varargs save area.  */
   HOST_WIDE_INT varargs_save_offset;
+  /* Temporary stack slot to use for SDmode copies.  This slot is
+     64-bits wide and is allocated early enough so that the offset
+     does not overflow the 16-bit load/store offset field.  */
+  rtx sdmode_stack_slot;
 } machine_function;
 
 /* Target cpu type */
@@ -138,8 +143,6 @@ struct rs6000_cpu_select rs6000_select[3] =
   { (const char *)0,   "-mtune=",              1,      0 },
 };
 
-static GTY(()) bool rs6000_cell_dont_microcode;
-
 /* Always emit branch hint bits.  */
 static GTY(()) bool rs6000_always_hint;
 
@@ -166,12 +169,18 @@ int rs6000_long_double_type_size;
 /* IEEE quad extended precision long double. */
 int rs6000_ieeequad;
 
-/* Whether -mabi=altivec has appeared.  */
+/* Nonzero to use AltiVec ABI.  */
 int rs6000_altivec_abi;
 
+/* Nonzero if we want SPE SIMD instructions.  */
+int rs6000_spe;
+
 /* Nonzero if we want SPE ABI extensions.  */
 int rs6000_spe_abi;
 
+/* Nonzero to use isel instructions.  */
+int rs6000_isel;
+
 /* Nonzero if floating point operations are done in the GPRs.  */
 int rs6000_float_gprs = 0;
 
@@ -257,12 +266,14 @@ int rs6000_alignment_flags;
 struct {
   bool aix_struct_ret;         /* True if -maix-struct-ret was used.  */
   bool alignment;              /* True if -malign- was used.  */
-  bool abi;                    /* True if -mabi=spe/nospe was used.  */
+  bool spe_abi;                        /* True if -mabi=spe/no-spe was used.  */
+  bool altivec_abi;            /* True if -mabi=altivec/no-altivec used.  */
   bool spe;                    /* True if -mspe= was used.  */
   bool float_gprs;             /* True if -mfloat-gprs= was used.  */
   bool isel;                   /* True if -misel was used. */
   bool long_double;            /* True if -mlong-double- was used.  */
   bool ieee;                   /* True if -mabi=ieee/ibmlongdouble used.  */
+  bool vrsave;                 /* True if -mvrsave was used.  */
 } rs6000_explicit_options;
 
 struct builtin_description
@@ -351,7 +362,7 @@ struct processor_costs rios1_cost = {
   COSTS_N_INSNS (2),    /* dmul */
   COSTS_N_INSNS (19),   /* sdiv */
   COSTS_N_INSNS (19),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   64,                  /* l1 cache */
   512,                 /* l2 cache */
   0,                   /* streams */
@@ -370,7 +381,7 @@ struct processor_costs rios2_cost = {
   COSTS_N_INSNS (2),    /* dmul */
   COSTS_N_INSNS (17),   /* sdiv */
   COSTS_N_INSNS (17),   /* ddiv */
-  256,
+  256,                 /* cache line size */
   256,                 /* l1 cache */
   1024,                        /* l2 cache */
   0,                   /* streams */
@@ -389,7 +400,7 @@ struct processor_costs rs64a_cost = {
   COSTS_N_INSNS (4),    /* dmul */
   COSTS_N_INSNS (31),   /* sdiv */
   COSTS_N_INSNS (31),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   128,                 /* l1 cache */
   2048,                        /* l2 cache */
   1,                   /* streams */
@@ -408,7 +419,7 @@ struct processor_costs mpccore_cost = {
   COSTS_N_INSNS (5),    /* dmul */
   COSTS_N_INSNS (10),   /* sdiv */
   COSTS_N_INSNS (17),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   4,                   /* l1 cache */
   16,                  /* l2 cache */
   1,                   /* streams */
@@ -427,7 +438,7 @@ struct processor_costs ppc403_cost = {
   COSTS_N_INSNS (11),   /* dmul */
   COSTS_N_INSNS (11),   /* sdiv */
   COSTS_N_INSNS (11),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   4,                   /* l1 cache */
   16,                  /* l2 cache */
   1,                   /* streams */
@@ -446,7 +457,7 @@ struct processor_costs ppc405_cost = {
   COSTS_N_INSNS (11),   /* dmul */
   COSTS_N_INSNS (11),   /* sdiv */
   COSTS_N_INSNS (11),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   16,                  /* l1 cache */
   128,                 /* l2 cache */
   1,                   /* streams */
@@ -465,7 +476,7 @@ struct processor_costs ppc440_cost = {
   COSTS_N_INSNS (5),    /* dmul */
   COSTS_N_INSNS (19),   /* sdiv */
   COSTS_N_INSNS (33),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   256,                 /* l2 cache */
   1,                   /* streams */
@@ -484,7 +495,7 @@ struct processor_costs ppc601_cost = {
   COSTS_N_INSNS (5),    /* dmul */
   COSTS_N_INSNS (17),   /* sdiv */
   COSTS_N_INSNS (31),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   256,                 /* l2 cache */
   1,                   /* streams */
@@ -503,7 +514,7 @@ struct processor_costs ppc603_cost = {
   COSTS_N_INSNS (4),    /* dmul */
   COSTS_N_INSNS (18),   /* sdiv */
   COSTS_N_INSNS (33),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   8,                   /* l1 cache */
   64,                  /* l2 cache */
   1,                   /* streams */
@@ -522,7 +533,7 @@ struct processor_costs ppc604_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (18),   /* sdiv */
   COSTS_N_INSNS (32),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   16,                  /* l1 cache */
   512,                 /* l2 cache */
   1,                   /* streams */
@@ -541,7 +552,7 @@ struct processor_costs ppc604e_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (18),   /* sdiv */
   COSTS_N_INSNS (32),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   1024,                        /* l2 cache */
   1,                   /* streams */
@@ -560,7 +571,7 @@ struct processor_costs ppc620_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (18),   /* sdiv */
   COSTS_N_INSNS (32),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   32,                  /* l1 cache */
   1024,                        /* l2 cache */
   1,                   /* streams */
@@ -579,7 +590,7 @@ struct processor_costs ppc630_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (17),   /* sdiv */
   COSTS_N_INSNS (21),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   64,                  /* l1 cache */
   1024,                        /* l2 cache */
   1,                   /* streams */
@@ -599,7 +610,7 @@ struct processor_costs ppccell_cost = {
   COSTS_N_INSNS (10/2),   /* dmul */
   COSTS_N_INSNS (74/2),   /* sdiv */
   COSTS_N_INSNS (74/2),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   32,                  /* l1 cache */
   512,                 /* l2 cache */
   6,                   /* streams */
@@ -618,7 +629,7 @@ struct processor_costs ppc750_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (17),   /* sdiv */
   COSTS_N_INSNS (31),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   512,                 /* l2 cache */
   1,                   /* streams */
@@ -637,7 +648,7 @@ struct processor_costs ppc7450_cost = {
   COSTS_N_INSNS (5),    /* dmul */
   COSTS_N_INSNS (21),   /* sdiv */
   COSTS_N_INSNS (35),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   1024,                        /* l2 cache */
   1,                   /* streams */
@@ -656,12 +667,50 @@ struct processor_costs ppc8540_cost = {
   COSTS_N_INSNS (4),    /* dmul */
   COSTS_N_INSNS (29),   /* sdiv */
   COSTS_N_INSNS (29),   /* ddiv */
-  32,
+  32,                  /* cache line size */
   32,                  /* l1 cache */
   256,                 /* l2 cache */
   1,                   /* prefetch streams /*/
 };
 
+/* Instruction costs on E300C2 and E300C3 cores.  */
+static const
+struct processor_costs ppce300c2c3_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 (3),    /* fp */
+  COSTS_N_INSNS (4),    /* dmul */
+  COSTS_N_INSNS (18),   /* sdiv */
+  COSTS_N_INSNS (33),   /* ddiv */
+  32,
+  16,                  /* l1 cache */
+  16,                  /* l2 cache */
+  1,                   /* prefetch streams /*/
+};
+
+/* Instruction costs on PPCE500MC processors.  */
+static const
+struct processor_costs ppce500mc_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 (14),   /* divsi */
+  COSTS_N_INSNS (14),   /* divdi */
+  COSTS_N_INSNS (8),    /* fp */
+  COSTS_N_INSNS (10),   /* dmul */
+  COSTS_N_INSNS (36),   /* sdiv */
+  COSTS_N_INSNS (66),   /* ddiv */
+  64,                  /* cache line size */
+  32,                  /* l1 cache */
+  128,                 /* l2 cache */
+  1,                   /* prefetch streams /*/
+};
+
 /* Instruction costs on POWER4 and POWER5 processors.  */
 static const
 struct processor_costs power4_cost = {
@@ -675,7 +724,7 @@ struct processor_costs power4_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (17),   /* sdiv */
   COSTS_N_INSNS (17),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   32,                  /* l1 cache */
   1024,                        /* l2 cache */
   8,                   /* prefetch streams /*/
@@ -694,7 +743,7 @@ struct processor_costs power6_cost = {
   COSTS_N_INSNS (3),    /* dmul */
   COSTS_N_INSNS (13),   /* sdiv */
   COSTS_N_INSNS (16),   /* ddiv */
-  128,
+  128,                 /* cache line size */
   64,                  /* l1 cache */
   2048,                        /* l2 cache */
   16,                  /* prefetch streams */
@@ -706,22 +755,20 @@ static const char *rs6000_invalid_within_doloop (const_rtx);
 static rtx rs6000_generate_compare (enum rtx_code);
 static void rs6000_emit_stack_tie (void);
 static void rs6000_frame_related (rtx, rtx, HOST_WIDE_INT, rtx, rtx);
-static rtx spe_synthesize_frame_save (rtx);
 static bool spe_func_has_64bit_regs_p (void);
 static void emit_frame_save (rtx, rtx, enum machine_mode, unsigned int,
                             int, HOST_WIDE_INT);
 static rtx gen_frame_mem_offset (enum machine_mode, rtx, int);
-static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int);
+static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int, int);
 static unsigned rs6000_hash_constant (rtx);
 static unsigned toc_hash_function (const void *);
 static int toc_hash_eq (const void *, const void *);
-static int constant_pool_expr_1 (rtx, int *, int *);
 static bool constant_pool_expr_p (rtx);
 static bool legitimate_small_data_p (enum machine_mode, rtx);
 static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int);
 static struct machine_function * rs6000_init_machine_status (void);
 static bool rs6000_assemble_integer (rtx, unsigned int, int);
-static bool no_global_regs_above (int);
+static bool no_global_regs_above (int, bool);
 #ifdef HAVE_GAS_HIDDEN
 static void rs6000_assemble_visibility (tree, int);
 #endif
@@ -734,7 +781,13 @@ static void rs6000_eliminate_indexed_memrefs (rtx operands[2]);
 static const char *rs6000_mangle_type (const_tree);
 extern const struct attribute_spec rs6000_attribute_table[];
 static void rs6000_set_default_type_attributes (tree);
+static rtx rs6000_savres_routine_sym (rs6000_stack_t *, bool, bool, bool);
+static void rs6000_emit_stack_reset (rs6000_stack_t *, rtx, rtx, int, bool);
+static rtx rs6000_make_savres_rtx (rs6000_stack_t *, rtx, int,
+                                  enum machine_mode, bool, bool, bool);
 static bool rs6000_reg_live_or_pic_offset_p (int);
+static int rs6000_savres_strategy (rs6000_stack_t *, bool, int, int);
+static void rs6000_restore_saved_cr (rtx, int);
 static void rs6000_output_function_prologue (FILE *, HOST_WIDE_INT);
 static void rs6000_output_function_epilogue (FILE *, HOST_WIDE_INT);
 static void rs6000_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT,
@@ -754,6 +807,8 @@ static void rs6000_elf_encode_section_info (tree, rtx, int)
      ATTRIBUTE_UNUSED;
 #endif
 static bool rs6000_use_blocks_for_constant_p (enum machine_mode, const_rtx);
+static void rs6000_alloc_sdmode_stack_slot (void);
+static void rs6000_instantiate_decls (void);
 #if TARGET_XCOFF
 static void rs6000_xcoff_asm_output_anchor (rtx);
 static void rs6000_xcoff_asm_globalize_label (FILE *, const char *);
@@ -771,7 +826,7 @@ static void rs6000_xcoff_file_start (void);
 static void rs6000_xcoff_file_end (void);
 #endif
 static int rs6000_variable_issue (FILE *, int, rtx, int);
-static bool rs6000_rtx_costs (rtx, int, int, int *);
+static bool rs6000_rtx_costs (rtx, int, int, int *, bool);
 static int rs6000_adjust_cost (rtx, rtx, rtx, int);
 static void rs6000_sched_init (FILE *, int, int);
 static bool is_microcoded_insn (rtx);
@@ -799,11 +854,16 @@ static int rs6000_sched_reorder (FILE *, int, rtx *, int *, int);
 static int rs6000_sched_reorder2 (FILE *, int, rtx *, int *, int);
 static int rs6000_use_sched_lookahead (void);
 static int rs6000_use_sched_lookahead_guard (rtx);
+static void * rs6000_alloc_sched_context (void);
+static void rs6000_init_sched_context (void *, bool);
+static void rs6000_set_sched_context (void *);
+static void rs6000_free_sched_context (void *);
 static tree rs6000_builtin_reciprocal (unsigned int, bool, bool);
 static tree rs6000_builtin_mask_for_load (void);
 static tree rs6000_builtin_mul_widen_even (tree);
 static tree rs6000_builtin_mul_widen_odd (tree);
 static tree rs6000_builtin_conversion (enum tree_code, tree);
+static tree rs6000_builtin_vec_perm (tree, tree *);
 
 static void def_builtin (int, const char *, tree, int);
 static bool rs6000_vector_alignment_reachable (const_tree, bool);
@@ -842,7 +902,6 @@ static rtx altivec_expand_dst_builtin (tree, rtx, bool *);
 static rtx altivec_expand_abs_builtin (enum insn_code, tree, rtx);
 static rtx altivec_expand_predicate_builtin (enum insn_code,
                                             const char *, tree, rtx);
-static rtx altivec_expand_lv_builtin (enum insn_code, tree, rtx);
 static rtx altivec_expand_stv_builtin (enum insn_code, tree);
 static rtx altivec_expand_vec_init_builtin (tree, tree, rtx);
 static rtx altivec_expand_vec_set_builtin (tree);
@@ -899,7 +958,8 @@ static void rs6000_darwin_file_start (void);
 #endif
 
 static tree rs6000_build_builtin_va_list (void);
-static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *);
+static void rs6000_va_start (tree, rtx);
+static tree rs6000_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
 static bool rs6000_must_pass_in_stack (enum machine_mode, const_tree);
 static bool rs6000_scalar_mode_supported_p (enum machine_mode);
 static bool rs6000_vector_mode_supported_p (enum machine_mode);
@@ -1071,6 +1131,15 @@ static const char alt_reg_names[][8] =
 #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD rs6000_use_sched_lookahead_guard
 
+#undef TARGET_SCHED_ALLOC_SCHED_CONTEXT
+#define TARGET_SCHED_ALLOC_SCHED_CONTEXT rs6000_alloc_sched_context
+#undef TARGET_SCHED_INIT_SCHED_CONTEXT
+#define TARGET_SCHED_INIT_SCHED_CONTEXT rs6000_init_sched_context
+#undef TARGET_SCHED_SET_SCHED_CONTEXT
+#define TARGET_SCHED_SET_SCHED_CONTEXT rs6000_set_sched_context
+#undef TARGET_SCHED_FREE_SCHED_CONTEXT
+#define TARGET_SCHED_FREE_SCHED_CONTEXT rs6000_free_sched_context
+
 #undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
 #define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load
 #undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN
@@ -1079,6 +1148,8 @@ static const char alt_reg_names[][8] =
 #define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD rs6000_builtin_mul_widen_odd
 #undef TARGET_VECTORIZE_BUILTIN_CONVERSION
 #define TARGET_VECTORIZE_BUILTIN_CONVERSION rs6000_builtin_conversion
+#undef TARGET_VECTORIZE_BUILTIN_VEC_PERM
+#define TARGET_VECTORIZE_BUILTIN_VEC_PERM rs6000_builtin_vec_perm
 
 #undef TARGET_VECTOR_ALIGNMENT_REACHABLE
 #define TARGET_VECTOR_ALIGNMENT_REACHABLE rs6000_vector_alignment_reachable
@@ -1118,7 +1189,7 @@ static const char alt_reg_names[][8] =
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS rs6000_rtx_costs
 #undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_0
+#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
 
 #undef TARGET_VECTOR_OPAQUE_P
 #define TARGET_VECTOR_OPAQUE_P rs6000_is_opaque_type
@@ -1159,6 +1230,9 @@ static const char alt_reg_names[][8] =
 #undef TARGET_BUILD_BUILTIN_VA_LIST
 #define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
 
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START rs6000_va_start
+
 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
 #define TARGET_GIMPLIFY_VA_ARG_EXPR rs6000_gimplify_va_arg
 
@@ -1217,6 +1291,12 @@ static const char alt_reg_names[][8] =
 #undef TARGET_BUILTIN_RECIPROCAL
 #define TARGET_BUILTIN_RECIPROCAL rs6000_builtin_reciprocal
 
+#undef TARGET_EXPAND_TO_RTL_HOOK
+#define TARGET_EXPAND_TO_RTL_HOOK rs6000_alloc_sdmode_stack_slot
+
+#undef TARGET_INSTANTIATE_DECLS
+#define TARGET_INSTANTIATE_DECLS rs6000_instantiate_decls
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 
@@ -1236,7 +1316,6 @@ rs6000_hard_regno_mode_ok (int regno, enum machine_mode mode)
     return
       ((SCALAR_FLOAT_MODE_P (mode)
        && (mode != TDmode || (regno % 2) == 0)
-       && mode != SDmode
        && FP_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1))
       || (GET_MODE_CLASS (mode) == MODE_INT
          && GET_MODE_SIZE (mode) == UNITS_PER_FP_WORD)
@@ -1380,6 +1459,10 @@ rs6000_override_options (const char *default_cpu)
          POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
         {"440fp", PROCESSOR_PPC440,
          POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
+        {"464", PROCESSOR_PPC440,
+         POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_MULHW | MASK_DLMZB},
+        {"464fp", PROCESSOR_PPC440,
+         POWERPC_BASE_MASK | MASK_MULHW | MASK_DLMZB},
         {"505", PROCESSOR_MPCCORE, POWERPC_BASE_MASK},
         {"601", PROCESSOR_PPC601,
          MASK_POWER | POWERPC_BASE_MASK | MASK_MULTIPLE | MASK_STRING},
@@ -1402,6 +1485,9 @@ rs6000_override_options (const char *default_cpu)
         {"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN},
         /* 8548 has a dummy entry for now.  */
         {"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN},
+        {"e300c2", PROCESSOR_PPCE300C2, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"e300c3", PROCESSOR_PPCE300C3, POWERPC_BASE_MASK},
+        {"e500mc", PROCESSOR_PPCE500MC, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
         {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
         {"970", PROCESSOR_POWER4,
          POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
@@ -1419,19 +1505,24 @@ rs6000_override_options (const char *default_cpu)
         {"power3", PROCESSOR_PPC630,
          POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
         {"power4", PROCESSOR_POWER4,
-         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64},
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+         | MASK_MFCRF},
         {"power5", PROCESSOR_POWER5,
-         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
          | MASK_MFCRF | MASK_POPCNTB},
         {"power5+", PROCESSOR_POWER5,
-         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
          | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND},
         {"power6", PROCESSOR_POWER6,
-         POWERPC_7400_MASK | MASK_POWERPC64 | MASK_MFCRF | MASK_POPCNTB
-         | MASK_FPRND | MASK_CMPB | MASK_DFP },
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+         | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP},
         {"power6x", PROCESSOR_POWER6,
-         POWERPC_7400_MASK | MASK_POWERPC64 | MASK_MFCRF | MASK_POPCNTB
-         | MASK_FPRND | MASK_CMPB | MASK_MFPGPR | MASK_DFP },
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+         | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
+         | MASK_MFPGPR},
+        {"power7", PROCESSOR_POWER5,
+         POWERPC_7400_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_MFCRF
+         | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP},
         {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
         {"powerpc64", PROCESSOR_POWERPC64,
          POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
@@ -1461,8 +1552,6 @@ rs6000_override_options (const char *default_cpu)
                     | MASK_DLMZB | MASK_CMPB | MASK_MFPGPR | MASK_DFP)
   };
 
-  rs6000_init_hard_regno_mode_ok ();
-
   set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
 #ifdef OS_MISSING_POWERPC64
   if (OS_MISSING_POWERPC64)
@@ -1505,12 +1594,29 @@ rs6000_override_options (const char *default_cpu)
        }
     }
 
-  if (TARGET_E500)
+  if ((TARGET_E500 || rs6000_cpu == PROCESSOR_PPCE500MC)
+      && !rs6000_explicit_options.isel)
     rs6000_isel = 1;
 
+  if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3
+      || rs6000_cpu == PROCESSOR_PPCE500MC)
+    {
+      if (TARGET_ALTIVEC)
+       error ("AltiVec not supported in this target");
+      if (TARGET_SPE)
+       error ("Spe not supported in this target");
+    }
+
+  /* Disable cell micro code if we are optimizing for the cell
+     and not optimizing for size.  */
+  if (rs6000_gen_cell_microcode == -1)
+    rs6000_gen_cell_microcode = !(rs6000_cpu == PROCESSOR_CELL
+                                  && !optimize_size);
+
   /* If we are optimizing big endian systems for space, use the load/store
-     multiple and string instructions.  */
-  if (BYTES_BIG_ENDIAN && optimize_size)
+     multiple and string instructions unless we are not generating
+     Cell microcode.  */
+  if (BYTES_BIG_ENDIAN && optimize_size && !rs6000_gen_cell_microcode)
     target_flags |= ~target_flags_explicit & (MASK_MULTIPLE | MASK_STRING);
 
   /* Don't allow -mmultiple or -mstring on little endian systems
@@ -1574,11 +1680,18 @@ rs6000_override_options (const char *default_cpu)
   if (TARGET_XCOFF && TARGET_ALTIVEC)
     rs6000_altivec_abi = 1;
 
-  /* Set Altivec ABI as default for PowerPC64 Linux.  */
-  if (TARGET_ELF && TARGET_64BIT)
+  /* The AltiVec ABI is the default for PowerPC-64 GNU/Linux.  For
+     PowerPC-32 GNU/Linux, -maltivec implies the AltiVec ABI.  It can
+     be explicitly overridden in either case.  */
+  if (TARGET_ELF)
     {
-      rs6000_altivec_abi = 1;
-      TARGET_ALTIVEC_VRSAVE = 1;
+      if (!rs6000_explicit_options.altivec_abi
+         && (TARGET_64BIT || TARGET_ALTIVEC))
+       rs6000_altivec_abi = 1;
+
+      /* Enable VRSAVE for AltiVec ABI, unless explicitly overridden.  */
+      if (!rs6000_explicit_options.vrsave)
+       TARGET_ALTIVEC_VRSAVE = rs6000_altivec_abi;
     }
 
   /* Set the Darwin64 ABI as default for 64-bit Darwin.  */
@@ -1610,9 +1723,9 @@ rs6000_override_options (const char *default_cpu)
   SUB3TARGET_OVERRIDE_OPTIONS;
 #endif
 
-  if (TARGET_E500)
+  if (TARGET_E500 || rs6000_cpu == PROCESSOR_PPCE500MC)
     {
-      /* The e500 does not have string instructions, and we set
+      /* The e500 and e500mc do not have string instructions, and we set
         MASK_STRING above when optimizing for size.  */
       if ((target_flags & MASK_STRING) != 0)
        target_flags = target_flags & ~MASK_STRING;
@@ -1622,7 +1735,7 @@ rs6000_override_options (const char *default_cpu)
       /* For the powerpc-eabispe configuration, we set all these by
         default, so let's unset them if we manually set another
         CPU that is not the E500.  */
-      if (!rs6000_explicit_options.abi)
+      if (!rs6000_explicit_options.spe_abi)
        rs6000_spe_abi = 0;
       if (!rs6000_explicit_options.spe)
        rs6000_spe = 0;
@@ -1820,6 +1933,15 @@ rs6000_override_options (const char *default_cpu)
        rs6000_cost = &ppc8540_cost;
        break;
 
+      case PROCESSOR_PPCE300C2:
+      case PROCESSOR_PPCE300C3:
+       rs6000_cost = &ppce300c2c3_cost;
+       break;
+
+      case PROCESSOR_PPCE500MC:
+       rs6000_cost = &ppce500mc_cost;
+       break;
+
       case PROCESSOR_POWER4:
       case PROCESSOR_POWER5:
        rs6000_cost = &power4_cost;
@@ -1842,6 +1964,30 @@ rs6000_override_options (const char *default_cpu)
     set_param_value ("l1-cache-line-size", rs6000_cost->cache_line_size);
   if (!PARAM_SET_P (PARAM_L2_CACHE_SIZE))
     set_param_value ("l2-cache-size", rs6000_cost->l2_cache_size);
+
+  /* If using typedef char *va_list, signal that __builtin_va_start (&ap, 0)
+     can be optimized to ap = __builtin_next_arg (0).  */
+  if (DEFAULT_ABI != ABI_V4)
+    targetm.expand_builtin_va_start = NULL;
+
+  /* Set up single/double float flags.  
+     If TARGET_HARD_FLOAT is set, but neither single or double is set, 
+     then set both flags. */
+  if (TARGET_HARD_FLOAT && TARGET_FPRS 
+      && rs6000_single_float == 0 && rs6000_double_float == 0)
+    rs6000_single_float = rs6000_double_float = 1;
+
+  /* Reset single and double FP flags if target is E500. */
+  if (TARGET_E500) 
+  {
+    rs6000_single_float = rs6000_double_float = 0;
+    if (TARGET_E500_SINGLE)
+      rs6000_single_float = 1; 
+    if (TARGET_E500_DOUBLE)
+      rs6000_single_float = rs6000_double_float = 1;
+  }
+
+  rs6000_init_hard_regno_mode_ok ();
 }
 
 /* Implement targetm.vectorize.builtin_mask_for_load.  */
@@ -1854,7 +2000,11 @@ rs6000_builtin_mask_for_load (void)
     return 0;
 }
 
-/* Implement targetm.vectorize.builtin_conversion.  */
+/* Implement targetm.vectorize.builtin_conversion.
+   Returns a decl of a function that implements conversion of an integer vector
+   into a floating-point vector, or vice-versa. TYPE is the type of the integer
+   side of the conversion.
+   Return NULL_TREE if it is not available.  */
 static tree
 rs6000_builtin_conversion (enum tree_code code, tree type)
 {
@@ -1863,16 +2013,28 @@ rs6000_builtin_conversion (enum tree_code code, tree type)
 
   switch (code)
     {
+    case FIX_TRUNC_EXPR:
+      switch (TYPE_MODE (type))
+       {
+       case V4SImode:
+         return TYPE_UNSIGNED (type)
+           ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VCTUXS]
+           : rs6000_builtin_decls[ALTIVEC_BUILTIN_VCTSXS];
+       default:
+         return NULL_TREE;
+       }
+
     case FLOAT_EXPR:
       switch (TYPE_MODE (type))
        {
        case V4SImode:
-         return TYPE_UNSIGNED (type) ?
-           rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFUX] :
-           rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFSX];
+         return TYPE_UNSIGNED (type)
+           ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFUX]
+           rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFSX];
        default:
          return NULL_TREE;
        }
+
     default:
       return NULL_TREE;
     }
@@ -1888,14 +2050,14 @@ rs6000_builtin_mul_widen_even (tree type)
   switch (TYPE_MODE (type))
     {
     case V8HImode:
-      return TYPE_UNSIGNED (type) ?
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUH] :
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESH];
+      return TYPE_UNSIGNED (type)
+            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUH]
+            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESH];
 
     case V16QImode:
-      return TYPE_UNSIGNED (type) ?
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUB] :
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESB];
+      return TYPE_UNSIGNED (type)
+            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUB]
+            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESB];
     default:
       return NULL_TREE;
     }
@@ -1911,14 +2073,14 @@ rs6000_builtin_mul_widen_odd (tree type)
   switch (TYPE_MODE (type))
     {
     case V8HImode:
-      return TYPE_UNSIGNED (type) ?
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUH] :
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSH];
+      return TYPE_UNSIGNED (type)
+            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUH]
+            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSH];
 
     case V16QImode:
-      return TYPE_UNSIGNED (type) ?
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUB] :
-            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSB];
+      return TYPE_UNSIGNED (type)
+            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUB]
+            rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSB];
     default:
       return NULL_TREE;
     }
@@ -1955,6 +2117,40 @@ rs6000_vector_alignment_reachable (const_tree type ATTRIBUTE_UNUSED, bool is_pac
     }
 }
 
+/* Implement targetm.vectorize.builtin_vec_perm.  */
+tree
+rs6000_builtin_vec_perm (tree type, tree *mask_element_type)
+{
+  tree d;
+
+  *mask_element_type = unsigned_char_type_node;
+
+  switch (TYPE_MODE (type))
+    {
+    case V16QImode:
+      d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_16QI];
+      break;
+
+    case V8HImode:
+      d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_8HI];
+      break;
+
+    case V4SImode:
+      d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_4SI];
+      break;
+
+    case V4SFmode:
+      d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_4SF];
+      break;
+
+    default:
+      return NULL_TREE;
+    }
+
+  gcc_assert (d);
+  return d;
+}
+
 /* Handle generic options of the form -mfoo=yes/no.
    NAME is the option name.
    VALUE is the option value.
@@ -2005,7 +2201,19 @@ optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
      Skip section anchors for Objective C and Objective C++
      until front-ends fixed.  */
   if (!TARGET_MACHO && lang_hooks.name[4] != 'O')
-    flag_section_anchors = 1;
+    flag_section_anchors = 2;
+}
+
+static enum fpu_type_t
+rs6000_parse_fpu_option (const char *option)
+{
+  if (!strcmp("none", option)) return FPU_NONE;
+  if (!strcmp("sp_lite", option)) return FPU_SF_LITE;
+  if (!strcmp("dp_lite", option)) return FPU_DF_LITE;
+  if (!strcmp("sp_full", option)) return FPU_SF_FULL;
+  if (!strcmp("dp_full", option)) return FPU_DF_FULL;
+  error("unknown value %s for -mfpu", option);
+  return FPU_NONE;
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
@@ -2013,6 +2221,8 @@ optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
 static bool
 rs6000_handle_option (size_t code, const char *arg, int value)
 {
+  enum fpu_type_t fpu_type = FPU_NONE;
+
   switch (code)
     {
     case OPT_mno_power:
@@ -2110,14 +2320,25 @@ rs6000_handle_option (size_t code, const char *arg, int value)
       break;
 
     case OPT_mvrsave_:
+      rs6000_explicit_options.vrsave = true;
       rs6000_parse_yes_no_option ("vrsave", arg, &(TARGET_ALTIVEC_VRSAVE));
       break;
 
+    case OPT_misel:
+      rs6000_explicit_options.isel = true;
+      rs6000_isel = value;
+      break;
+
     case OPT_misel_:
       rs6000_explicit_options.isel = true;
       rs6000_parse_yes_no_option ("isel", arg, &(rs6000_isel));
       break;
 
+    case OPT_mspe:
+      rs6000_explicit_options.spe = true;
+      rs6000_spe = value;
+      break;
+
     case OPT_mspe_:
       rs6000_explicit_options.spe = true;
       rs6000_parse_yes_no_option ("spe", arg, &(rs6000_spe));
@@ -2167,19 +2388,20 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_mabi_:
       if (!strcmp (arg, "altivec"))
        {
-         rs6000_explicit_options.abi = true;
+         rs6000_explicit_options.altivec_abi = true;
          rs6000_altivec_abi = 1;
+
+         /* Enabling the AltiVec ABI turns off the SPE ABI.  */
          rs6000_spe_abi = 0;
        }
       else if (! strcmp (arg, "no-altivec"))
        {
-         /* ??? Don't set rs6000_explicit_options.abi here, to allow
-            the default for rs6000_spe_abi to be chosen later.  */
+         rs6000_explicit_options.altivec_abi = true;
          rs6000_altivec_abi = 0;
        }
       else if (! strcmp (arg, "spe"))
        {
-         rs6000_explicit_options.abi = true;
+         rs6000_explicit_options.spe_abi = true;
          rs6000_spe_abi = 1;
          rs6000_altivec_abi = 0;
          if (!TARGET_SPE_ABI)
@@ -2187,7 +2409,7 @@ rs6000_handle_option (size_t code, const char *arg, int value)
        }
       else if (! strcmp (arg, "no-spe"))
        {
-         rs6000_explicit_options.abi = true;
+         rs6000_explicit_options.spe_abi = true;
          rs6000_spe_abi = 0;
        }
 
@@ -2289,6 +2511,61 @@ rs6000_handle_option (size_t code, const char *arg, int value)
          return false;
        }
       break;
+
+    case OPT_msingle_float:
+      if (!TARGET_SINGLE_FPU) 
+       warning (0, "-msingle-float option equivalent to -mhard-float");
+      /* -msingle-float implies -mno-double-float and TARGET_HARD_FLOAT. */
+      rs6000_double_float = 0;
+      target_flags &= ~MASK_SOFT_FLOAT;
+      target_flags_explicit |= MASK_SOFT_FLOAT;
+      break;
+
+    case OPT_mdouble_float:
+      /* -mdouble-float implies -msingle-float and TARGET_HARD_FLOAT. */
+      rs6000_single_float = 1;
+      target_flags &= ~MASK_SOFT_FLOAT;
+      target_flags_explicit |= MASK_SOFT_FLOAT;
+      break;
+
+    case OPT_msimple_fpu:
+      if (!TARGET_SINGLE_FPU) 
+       warning (0, "-msimple-fpu option ignored");
+      break;
+
+    case OPT_mhard_float:
+      /* -mhard_float implies -msingle-float and -mdouble-float. */
+      rs6000_single_float = rs6000_double_float = 1;
+      break;
+
+    case OPT_msoft_float:
+      /* -msoft_float implies -mnosingle-float and -mnodouble-float. */
+      rs6000_single_float = rs6000_double_float = 0;
+      break;
+
+    case OPT_mfpu_:
+      fpu_type = rs6000_parse_fpu_option(arg);
+      if (fpu_type != FPU_NONE) 
+      /* If -mfpu is not none, then turn off SOFT_FLOAT, turn on HARD_FLOAT. */
+      {
+        target_flags &= ~MASK_SOFT_FLOAT;
+        target_flags_explicit |= MASK_SOFT_FLOAT;
+        rs6000_xilinx_fpu = 1;
+        if (fpu_type == FPU_SF_LITE || fpu_type == FPU_SF_FULL) 
+        rs6000_single_float = 1;
+        if (fpu_type == FPU_DF_LITE || fpu_type == FPU_DF_FULL) 
+          rs6000_single_float = rs6000_double_float = 1;
+        if (fpu_type == FPU_SF_LITE || fpu_type == FPU_DF_LITE) 
+          rs6000_simple_fpu = 1;
+      }
+      else
+      {
+        /* -mfpu=none is equivalent to -msoft-float */
+        target_flags |= MASK_SOFT_FLOAT;
+        target_flags_explicit |= MASK_SOFT_FLOAT;
+        rs6000_single_float = rs6000_double_float = 0;
+      }
+      break;
     }
   return true;
 }
@@ -2358,11 +2635,16 @@ rs6000_file_start (void)
   if (TARGET_32BIT && DEFAULT_ABI == ABI_V4)
     {
       fprintf (file, "\t.gnu_attribute 4, %d\n",
-              (TARGET_HARD_FLOAT && TARGET_FPRS) ? 1 : 2);
+              ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) ? 1 
+               : (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT) ? 3 
+               : 2));
       fprintf (file, "\t.gnu_attribute 8, %d\n",
               (TARGET_ALTIVEC_ABI ? 2
                : TARGET_SPE_ABI ? 3
                : 1));
+      fprintf (file, "\t.gnu_attribute 12, %d\n",
+              aix_struct_return ? 2 : 1);
+
     }
 #endif
 
@@ -2451,13 +2733,16 @@ num_insns_constant (rtx op, enum machine_mode mode)
        return num_insns_constant_wide (INTVAL (op));
 
       case CONST_DOUBLE:
-       if (mode == SFmode)
+       if (mode == SFmode || mode == SDmode)
          {
            long l;
            REAL_VALUE_TYPE rv;
 
            REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
-           REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+           if (DECIMAL_FLOAT_MODE_P (mode))
+             REAL_VALUE_TO_TARGET_DECIMAL32 (rv, l);
+           else
+             REAL_VALUE_TO_TARGET_SINGLE (rv, l);
            return num_insns_constant_wide ((HOST_WIDE_INT) l);
          }
 
@@ -2510,7 +2795,7 @@ num_insns_constant (rtx op, enum machine_mode mode)
    corresponding element of the vector, but for V4SFmode and V2SFmode,
    the corresponding "float" is interpreted as an SImode integer.  */
 
-static HOST_WIDE_INT
+HOST_WIDE_INT
 const_vector_elt_as_int (rtx op, unsigned int elt)
 {
   rtx tmp = CONST_VECTOR_ELT (op, elt);
@@ -2728,7 +3013,7 @@ paired_expand_vector_init (rtx target, rtx vals)
   enum machine_mode mode = GET_MODE (target);
   int n_elts = GET_MODE_NUNITS (mode);
   int n_var = 0;
-  rtx x, new, tmp, constant_op, op1, op2;
+  rtx x, new_rtx, tmp, constant_op, op1, op2;
   int i;
 
   for (i = 0; i < n_elts; ++i)
@@ -2747,10 +3032,10 @@ paired_expand_vector_init (rtx target, rtx vals)
   if (n_var == 2)
     {
       /* The vector is initialized only with non-constants.  */
-      new = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
+      new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
                                XVECEXP (vals, 0, 1));
 
-      emit_move_insn (target, new);
+      emit_move_insn (target, new_rtx);
       return;
     }
   
@@ -2766,11 +3051,108 @@ paired_expand_vector_init (rtx target, rtx vals)
   emit_move_insn (tmp, constant_op);
 
   if (CONSTANT_P (op1))
-    new = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
+    new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
   else
-    new = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
+    new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
+
+  emit_move_insn (target, new_rtx);
+}
+
+void
+paired_expand_vector_move (rtx operands[])
+{
+  rtx op0 = operands[0], op1 = operands[1];
+
+  emit_move_insn (op0, op1);
+}
+
+/* Emit vector compare for code RCODE.  DEST is destination, OP1 and
+   OP2 are two VEC_COND_EXPR operands, CC_OP0 and CC_OP1 are the two
+   operands for the relation operation COND.  This is a recursive
+   function.  */
+
+static void
+paired_emit_vector_compare (enum rtx_code rcode,
+                            rtx dest, rtx op0, rtx op1,
+                            rtx cc_op0, rtx cc_op1)
+{
+  rtx tmp = gen_reg_rtx (V2SFmode);
+  rtx tmp1, max, min, equal_zero;
+
+  gcc_assert (TARGET_PAIRED_FLOAT);
+  gcc_assert (GET_MODE (op0) == GET_MODE (op1));
+
+  switch (rcode)
+    {
+    case LT:
+    case LTU:
+      paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case GE:
+    case GEU:
+      emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1));
+      emit_insn (gen_selv2sf4 (dest, tmp, op0, op1, CONST0_RTX (SFmode)));
+      return;
+    case LE:
+    case LEU:
+      paired_emit_vector_compare (GE, dest, op0, op1, cc_op1, cc_op0);
+      return;
+    case GT:
+      paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case EQ:
+      tmp1 = gen_reg_rtx (V2SFmode);
+      max = gen_reg_rtx (V2SFmode);
+      min = gen_reg_rtx (V2SFmode);
+      equal_zero = gen_reg_rtx (V2SFmode);
+
+      emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1));
+      emit_insn (gen_selv2sf4
+                 (max, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode)));
+      emit_insn (gen_subv2sf3 (tmp, cc_op1, cc_op0));
+      emit_insn (gen_selv2sf4
+                 (min, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode)));
+      emit_insn (gen_subv2sf3 (tmp1, min, max));
+      emit_insn (gen_selv2sf4 (dest, tmp1, op0, op1, CONST0_RTX (SFmode)));
+      return;
+    case NE:
+      paired_emit_vector_compare (EQ, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case UNLE:
+      paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case UNLT:
+      paired_emit_vector_compare (LT, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case UNGE:
+      paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    case UNGT:
+      paired_emit_vector_compare (GT, dest, op1, op0, cc_op0, cc_op1);
+      return;
+    default:
+      gcc_unreachable ();
+    }
+
+  return;
+}
+
+/* Emit vector conditional expression.
+   DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands.
+   CC_OP0 and CC_OP1 are the two operands for the relation operation COND.  */
+
+int
+paired_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2,
+                             rtx cond, rtx cc_op0, rtx cc_op1)
+{
+  enum rtx_code rcode = GET_CODE (cond);
+
+  if (!TARGET_PAIRED_FLOAT)
+    return 0;
+
+  paired_emit_vector_compare (rcode, dest, op1, op2, cc_op0, cc_op1);
 
-  emit_move_insn (target, new);
+  return 1;
 }
 
 /* Initialize vector TARGET to VALS.  */
@@ -2800,6 +3182,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
 
   if (n_var == 0)
     {
+      rtx const_vec = gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0));
       if (mode != V4SFmode && all_const_zero)
        {
          /* Zero register.  */
@@ -2807,10 +3190,10 @@ rs6000_expand_vector_init (rtx target, rtx vals)
                                  gen_rtx_XOR (mode, target, target)));
          return;
        }
-      else if (mode != V4SFmode && easy_vector_constant (vals, mode))
+      else if (mode != V4SFmode && easy_vector_constant (const_vec, mode))
        {
          /* Splat immediate.  */
-         emit_insn (gen_rtx_SET (VOIDmode, target, vals));
+         emit_insn (gen_rtx_SET (VOIDmode, target, const_vec));
          return;
        }
       else if (all_same)
@@ -2818,7 +3201,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
       else
        {
          /* Load from constant pool.  */
-         emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
+         emit_move_insn (target, const_vec);
          return;
        }
     }
@@ -3013,9 +3396,12 @@ invalid_e500_subreg (rtx op, enum machine_mode mode)
   if (TARGET_E500_DOUBLE)
     {
       /* Reject (subreg:SI (reg:DF)); likewise with subreg:DI or
-        subreg:TI and reg:TF.  */
+        subreg:TI and reg:TF.  Decimal float modes are like integer
+        modes (only low part of each register used) for this
+        purpose.  */
       if (GET_CODE (op) == SUBREG
-         && (mode == SImode || mode == DImode || mode == TImode)
+         && (mode == SImode || mode == DImode || mode == TImode
+             || mode == DDmode || mode == TDmode)
          && REG_P (SUBREG_REG (op))
          && (GET_MODE (SUBREG_REG (op)) == DFmode
              || GET_MODE (SUBREG_REG (op)) == TFmode))
@@ -3027,7 +3413,9 @@ invalid_e500_subreg (rtx op, enum machine_mode mode)
          && (mode == DFmode || mode == TFmode)
          && REG_P (SUBREG_REG (op))
          && (GET_MODE (SUBREG_REG (op)) == DImode
-             || GET_MODE (SUBREG_REG (op)) == TImode))
+             || GET_MODE (SUBREG_REG (op)) == TImode
+             || GET_MODE (SUBREG_REG (op)) == DDmode
+             || GET_MODE (SUBREG_REG (op)) == TDmode))
        return true;
     }
 
@@ -3114,6 +3502,13 @@ small_data_operand (rtx op ATTRIBUTE_UNUSED,
   if (DEFAULT_ABI != ABI_V4)
     return 0;
 
+  /* Vector and float memory instructions have a limited offset on the
+     SPE, so using a vector or float variable directly as an operand is
+     not useful.  */
+  if (TARGET_SPE
+      && (SPE_VECTOR_MODE (mode) || FLOAT_MODE_P (mode)))
+    return 0;
+
   if (GET_CODE (op) == SYMBOL_REF)
     sym_ref = op;
 
@@ -3155,58 +3550,28 @@ gpr_or_gpr_p (rtx op0, rtx op1)
 \f
 /* Subroutines of rs6000_legitimize_address and rs6000_legitimate_address.  */
 
-static int
-constant_pool_expr_1 (rtx op, int *have_sym, int *have_toc)
-{
-  switch (GET_CODE (op))
-    {
-    case SYMBOL_REF:
-      if (RS6000_SYMBOL_REF_TLS_P (op))
-       return 0;
-      else if (CONSTANT_POOL_ADDRESS_P (op))
-       {
-         if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (op), Pmode))
-           {
-             *have_sym = 1;
-             return 1;
-           }
-         else
-           return 0;
-       }
-      else if (! strcmp (XSTR (op, 0), toc_label_name))
-       {
-         *have_toc = 1;
-         return 1;
-       }
-      else
-       return 0;
-    case PLUS:
-    case MINUS:
-      return (constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc)
-             && constant_pool_expr_1 (XEXP (op, 1), have_sym, have_toc));
-    case CONST:
-      return constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc);
-    case CONST_INT:
-      return 1;
-    default:
-      return 0;
-    }
-}
-
 static bool
 constant_pool_expr_p (rtx op)
 {
-  int have_sym = 0;
-  int have_toc = 0;
-  return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_sym;
+  rtx base, offset;
+
+  split_const (op, &base, &offset);
+  return (GET_CODE (base) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (base)
+         && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode));
 }
 
 bool
 toc_relative_expr_p (rtx op)
 {
-  int have_sym = 0;
-  int have_toc = 0;
-  return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_toc;
+  rtx base, offset;
+
+  if (GET_CODE (op) != CONST)
+    return false;
+
+  split_const (op, &base, &offset);
+  return (GET_CODE (base) == UNSPEC
+         && XINT (base, 1) == UNSPEC_TOCREL);
 }
 
 bool
@@ -3216,7 +3581,7 @@ legitimate_constant_pool_address_p (rtx x)
          && GET_CODE (x) == PLUS
          && GET_CODE (XEXP (x, 0)) == REG
          && (TARGET_MINIMAL_TOC || REGNO (XEXP (x, 0)) == TOC_REGISTER)
-         && constant_pool_expr_p (XEXP (x, 1)));
+         && toc_relative_expr_p (XEXP (x, 1)));
 }
 
 static bool
@@ -3256,27 +3621,25 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
     case V4SFmode:
     case V4SImode:
       /* AltiVec vector modes.  Only reg+reg addressing is valid and
-        constant offset zero should not occur due to canonicalization.
-        Allow any offset when not strict before reload.  */
-      return !strict;
+        constant offset zero should not occur due to canonicalization.  */
+      return false;
 
     case V4HImode:
     case V2SImode:
     case V1DImode:
     case V2SFmode:
        /* Paired vector modes.  Only reg+reg addressing is valid and
-         constant offset zero should not occur due to canonicalization.
-         Allow any offset when not strict before reload.  */
+         constant offset zero should not occur due to canonicalization.  */
       if (TARGET_PAIRED_FLOAT)
-        return !strict;
+        return false;
       /* SPE vector modes.  */
       return SPE_CONST_OFFSET_OK (offset);
 
     case DFmode:
-    case DDmode:
       if (TARGET_E500_DOUBLE)
        return SPE_CONST_OFFSET_OK (offset);
 
+    case DDmode:
     case DImode:
       /* On e500v2, we may have:
 
@@ -3297,8 +3660,8 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
        return (SPE_CONST_OFFSET_OK (offset)
                && SPE_CONST_OFFSET_OK (offset + 8));
 
-    case TImode:
     case TDmode:
+    case TImode:
       if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
        extra = 12;
       else if (offset & 3)
@@ -3377,6 +3740,7 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
     return false;
   /* Restrict addressing for DI because of our SUBREG hackery.  */
   if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+                            || mode == DDmode || mode == TDmode
                             || mode == DImode))
     return false;
   x = XEXP (x, 1);
@@ -3391,7 +3755,8 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
        return false;
       if (GET_MODE_BITSIZE (mode) > 64
          || (GET_MODE_BITSIZE (mode) > 32 && !TARGET_POWERPC64
-             && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode)))
+             && !(TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+                  && (mode == DFmode || mode == DDmode))))
        return false;
 
       return CONSTANT_P (x);
@@ -3440,8 +3805,10 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       && GET_CODE (XEXP (x, 1)) == CONST_INT
       && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000
       && !(SPE_VECTOR_MODE (mode)
+          || ALTIVEC_VECTOR_MODE (mode)
           || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
-                                     || mode == DImode))))
+                                     || mode == DImode || mode == DDmode
+                                     || mode == TDmode))))
     {
       HOST_WIDE_INT high_int, low_int;
       rtx sum;
@@ -3455,13 +3822,14 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_CODE (XEXP (x, 0)) == REG
           && GET_CODE (XEXP (x, 1)) != CONST_INT
           && GET_MODE_NUNITS (mode) == 1
-          && ((TARGET_HARD_FLOAT && TARGET_FPRS)
+          && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
               || TARGET_POWERPC64
-              || (((mode != DImode && mode != DFmode && mode != DDmode)
-                   || TARGET_E500_DOUBLE)
-                  && mode != TFmode && mode != TDmode))
+              || ((mode != DImode && mode != DFmode && mode != DDmode)
+                  || (TARGET_E500_DOUBLE && mode != DDmode)))
           && (TARGET_POWERPC64 || mode != DImode)
-          && mode != TImode)
+          && mode != TImode
+          && mode != TFmode
+          && mode != TDmode)
     {
       return gen_rtx_PLUS (Pmode, XEXP (x, 0),
                           force_reg (Pmode, force_operand (XEXP (x, 1), 0)));
@@ -3488,19 +3856,29 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       /* We accept [reg + reg] and [reg + OFFSET].  */
 
       if (GET_CODE (x) == PLUS)
-       {
-         rtx op1 = XEXP (x, 0);
-         rtx op2 = XEXP (x, 1);
+       {
+         rtx op1 = XEXP (x, 0);
+         rtx op2 = XEXP (x, 1);
+         rtx y;
 
-         op1 = force_reg (Pmode, op1);
+         op1 = force_reg (Pmode, op1);
 
-         if (GET_CODE (op2) != REG
-             && (GET_CODE (op2) != CONST_INT
-                 || !SPE_CONST_OFFSET_OK (INTVAL (op2))))
-           op2 = force_reg (Pmode, op2);
+         if (GET_CODE (op2) != REG
+             && (GET_CODE (op2) != CONST_INT
+                 || !SPE_CONST_OFFSET_OK (INTVAL (op2))
+                 || (GET_MODE_SIZE (mode) > 8
+                     && !SPE_CONST_OFFSET_OK (INTVAL (op2) + 8))))
+           op2 = force_reg (Pmode, op2);
 
-         return gen_rtx_PLUS (Pmode, op1, op2);
-       }
+         /* We can't always do [reg + reg] for these, because [reg +
+            reg + offset] is not a legitimate addressing mode.  */
+         y = gen_rtx_PLUS (Pmode, op1, op2);
+
+         if ((GET_MODE_SIZE (mode) > 8 || mode == DDmode) && REG_P (op2))
+           return force_reg (Pmode, y);
+         else
+           return y;
+       }
 
       return force_reg (Pmode, x);
     }
@@ -3513,7 +3891,8 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && CONSTANT_P (x)
           && GET_MODE_NUNITS (mode) == 1
           && (GET_MODE_BITSIZE (mode) <= 32
-              || ((TARGET_HARD_FLOAT && TARGET_FPRS) && mode == DFmode)))
+              || ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
+                  && (mode == DFmode || mode == DDmode))))
     {
       rtx reg = gen_reg_rtx (Pmode);
       emit_insn (gen_elf_high (reg, x));
@@ -3527,7 +3906,9 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_CODE (x) != CONST_INT
           && GET_CODE (x) != CONST_DOUBLE
           && CONSTANT_P (x)
-          && ((TARGET_HARD_FLOAT && TARGET_FPRS) || mode != DFmode)
+          && GET_MODE_NUNITS (mode) == 1
+          && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
+              || (mode != DFmode && mode != DDmode))
           && mode != DImode
           && mode != TImode)
     {
@@ -3536,6 +3917,7 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       return gen_rtx_LO_SUM (Pmode, reg, x);
     }
   else if (TARGET_TOC
+          && GET_CODE (x) == SYMBOL_REF
           && constant_pool_expr_p (x)
           && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode))
     {
@@ -3679,7 +4061,6 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
                  emit_insn (gen_addsi3 (tmp3, tmp1, tmp2));
                  last = emit_move_insn (got, tmp3);
                  set_unique_reg_note (last, REG_EQUAL, gsym);
-                 maybe_encapsulate_block (first, last, gsym);
                }
            }
        }
@@ -3687,17 +4068,23 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
       if (model == TLS_MODEL_GLOBAL_DYNAMIC)
        {
          r3 = gen_rtx_REG (Pmode, 3);
-         if (TARGET_64BIT)
-           insn = gen_tls_gd_64 (r3, got, addr);
+         tga = rs6000_tls_get_addr ();
+
+         if (DEFAULT_ABI == ABI_AIX && TARGET_64BIT)
+           insn = gen_tls_gd_aix64 (r3, got, addr, tga, const0_rtx);
+         else if (DEFAULT_ABI == ABI_AIX && !TARGET_64BIT)
+           insn = gen_tls_gd_aix32 (r3, got, addr, tga, const0_rtx);
+         else if (DEFAULT_ABI == ABI_V4)
+           insn = gen_tls_gd_sysvsi (r3, got, addr, tga, const0_rtx);
          else
-           insn = gen_tls_gd_32 (r3, got, addr);
+           gcc_unreachable ();
+
          start_sequence ();
-         emit_insn (insn);
-         tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ());
-         insn = gen_call_value (r3, tga, const0_rtx, const0_rtx);
          insn = emit_call_insn (insn);
-         CONST_OR_PURE_CALL_P (insn) = 1;
+         RTL_CONST_CALL_P (insn) = 1;
          use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3);
+         if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+           use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
          insn = get_insns ();
          end_sequence ();
          emit_libcall_block (insn, dest, r3, addr);
@@ -3705,17 +4092,23 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
       else if (model == TLS_MODEL_LOCAL_DYNAMIC)
        {
          r3 = gen_rtx_REG (Pmode, 3);
-         if (TARGET_64BIT)
-           insn = gen_tls_ld_64 (r3, got);
+         tga = rs6000_tls_get_addr ();
+
+         if (DEFAULT_ABI == ABI_AIX && TARGET_64BIT)
+           insn = gen_tls_ld_aix64 (r3, got, tga, const0_rtx);
+         else if (DEFAULT_ABI == ABI_AIX && !TARGET_64BIT)
+           insn = gen_tls_ld_aix32 (r3, got, tga, const0_rtx);
+         else if (DEFAULT_ABI == ABI_V4)
+           insn = gen_tls_ld_sysvsi (r3, got, tga, const0_rtx);
          else
-           insn = gen_tls_ld_32 (r3, got);
+           gcc_unreachable ();
+
          start_sequence ();
-         emit_insn (insn);
-         tga = gen_rtx_MEM (Pmode, rs6000_tls_get_addr ());
-         insn = gen_call_value (r3, tga, const0_rtx, const0_rtx);
          insn = emit_call_insn (insn);
-         CONST_OR_PURE_CALL_P (insn) = 1;
+         RTL_CONST_CALL_P (insn) = 1;
          use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3);
+         if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+           use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
          insn = get_insns ();
          end_sequence ();
          tmp1 = gen_reg_rtx (Pmode);
@@ -3813,8 +4206,8 @@ rs6000_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
 
    On Darwin, we use this to generate code for floating point constants.
    A movsf_low is generated so we wind up with 2 instructions rather than 3.
-   The Darwin code is inside #if TARGET_MACHO because only then is
-   machopic_function_base_name() defined.  */
+   The Darwin code is inside #if TARGET_MACHO because only then are the
+   machopic_* functions defined.  */
 rtx
 rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
                                  int opnum, int type,
@@ -3840,11 +4233,8 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       && GET_CODE (XEXP (x, 0)) == PLUS
       && XEXP (XEXP (x, 0), 0) == pic_offset_table_rtx
       && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH
-      && GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 0)) == CONST
       && XEXP (XEXP (XEXP (x, 0), 1), 0) == XEXP (x, 1)
-      && GET_CODE (XEXP (XEXP (x, 1), 0)) == MINUS
-      && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 0)) == SYMBOL_REF
-      && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 1)) == SYMBOL_REF)
+      && machopic_operand_p (XEXP (x, 1)))
     {
       /* Result of previous invocation of this function on Darwin
         floating point constant.  */
@@ -3883,6 +4273,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       && GET_CODE (XEXP (x, 1)) == CONST_INT
       && !SPE_VECTOR_MODE (mode)
       && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+                                 || mode == DDmode || mode == TDmode
                                  || mode == DImode))
       && !ALTIVEC_VECTOR_MODE (mode))
     {
@@ -3924,20 +4315,18 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       && !flag_pic
 #endif
       /* Don't do this for TFmode or TDmode, since the result isn't offsettable.
-        The same goes for DImode without 64-bit gprs and DFmode
+        The same goes for DImode without 64-bit gprs and DFmode and DDmode
         without fprs.  */
       && mode != TFmode
       && mode != TDmode
       && (mode != DImode || TARGET_POWERPC64)
-      && (mode != DFmode || TARGET_POWERPC64
-         || (TARGET_FPRS && TARGET_HARD_FLOAT)))
+      && ((mode != DFmode && mode != DDmode) || TARGET_POWERPC64
+         || (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)))
     {
 #if TARGET_MACHO
       if (flag_pic)
        {
-         rtx offset = gen_rtx_CONST (Pmode,
-                        gen_rtx_MINUS (Pmode, x,
-                                       machopic_function_base_sym ()));
+         rtx offset = machopic_gen_offset (x);
          x = gen_rtx_LO_SUM (GET_MODE (x),
                gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
                  gen_rtx_HIGH (Pmode, offset)), offset);
@@ -3972,6 +4361,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
     }
 
   if (TARGET_TOC
+      && GET_CODE (x) == SYMBOL_REF
       && constant_pool_expr_p (x)
       && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode))
     {
@@ -3992,11 +4382,11 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
    refers to a constant pool entry of an address (or the sum of it
    plus a constant), a short (16-bit signed) constant plus a register,
    the sum of two registers, or a register indirect, possibly with an
-   auto-increment.  For DFmode and DImode with a constant plus register,
-   we must ensure that both words are addressable or PowerPC64 with offset
-   word aligned.
+   auto-increment.  For DFmode, DDmode and DImode with a constant plus
+   register, we must ensure that both words are addressable or PowerPC64
+   with offset word aligned.
 
-   For modes spanning multiple registers (DFmode in 32-bit GPRs,
+   For modes spanning multiple registers (DFmode and DDmode in 32-bit GPRs,
    32-bit DImode, TImode, TFmode, TDmode), indexed addressing cannot be used
    because adjacent memory cells are accessed by adding word-sized offsets
    during assembly output.  */
@@ -4021,8 +4411,8 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
       && mode != TFmode
       && mode != TDmode
       /* Restrict addressing for DI because of our SUBREG hackery.  */
-      && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
-                                 || mode == DImode))
+      && !(TARGET_E500_DOUBLE
+          && (mode == DFmode || mode == DDmode || mode == DImode))
       && TARGET_UPDATE
       && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict))
     return 1;
@@ -4045,7 +4435,8 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
       && mode != TDmode
       && ((TARGET_HARD_FLOAT && TARGET_FPRS)
          || TARGET_POWERPC64
-         || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
+         || (mode != DFmode && mode != DDmode)
+         || (TARGET_E500_DOUBLE && mode != DDmode))
       && (TARGET_POWERPC64 || mode != DImode)
       && legitimate_indexed_address_p (x, reg_ok_strict))
     return 1;
@@ -4053,14 +4444,15 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
       && mode != TImode
       && mode != TFmode
       && mode != TDmode
-      && ((TARGET_HARD_FLOAT && TARGET_FPRS)
+      && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
          || TARGET_POWERPC64
-         || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
+         || ((mode != DFmode && mode != DDmode) || TARGET_E500_DOUBLE))
       && (TARGET_POWERPC64 || mode != DImode)
       && !ALTIVEC_VECTOR_MODE (mode)
       && !SPE_VECTOR_MODE (mode)
       /* Restrict addressing for DI because of our SUBREG hackery.  */
-      && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
+      && !(TARGET_E500_DOUBLE
+          && (mode == DFmode || mode == DDmode || mode == DImode))
       && TARGET_UPDATE
       && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)
       && (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1), reg_ok_strict)
@@ -4099,8 +4491,7 @@ rs6000_mode_dependent_address (rtx addr)
     case LO_SUM:
       return true;
 
-    case PRE_INC:
-    case PRE_DEC:
+    /* Auto-increment cases are now treated generically in recog.c.  */
     case PRE_MODIFY:
       return TARGET_UPDATE;
 
@@ -4111,6 +4502,27 @@ rs6000_mode_dependent_address (rtx addr)
   return false;
 }
 
+/* Implement FIND_BASE_TERM.  */
+
+rtx
+rs6000_find_base_term (rtx op)
+{
+  rtx base, offset;
+
+  split_const (op, &base, &offset);
+  if (GET_CODE (base) == UNSPEC)
+    switch (XINT (base, 1))
+      {
+      case UNSPEC_TOCREL:
+      case UNSPEC_MACHOPIC_OFFSET:
+       /* OP represents SYM [+ OFFSET] - ANCHOR.  SYM is the base term
+          for aliasing purposes.  */
+       return XVECEXP (base, 0, 0);
+      }
+
+  return op;
+}
+
 /* More elaborate version of recog's offsettable_memref_p predicate
    that works around the ??? note of rs6000_mode_dependent_address.
    In particular it accepts
@@ -4168,7 +4580,8 @@ rs6000_hard_regno_nregs (int regno, enum machine_mode mode)
      would require function_arg and rs6000_spe_function_arg to handle
      SCmode so as to pass the value correctly in a pair of
      registers.  */
-  if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode)
+  if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode
+      && !DECIMAL_FLOAT_MODE_P (mode))
     return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
 
   return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
@@ -4507,7 +4920,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
     operands[1] = force_reg (mode, operands[1]);
 
   if (mode == SFmode && ! TARGET_POWERPC
-      && TARGET_HARD_FLOAT && TARGET_FPRS
+      && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT 
       && GET_CODE (operands[0]) == MEM)
     {
       int regnum;
@@ -4584,6 +4997,55 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       return;
     }
 
+  if (reload_in_progress && cfun->machine->sdmode_stack_slot != NULL_RTX)
+    cfun->machine->sdmode_stack_slot =
+      eliminate_regs (cfun->machine->sdmode_stack_slot, VOIDmode, NULL_RTX);
+
+  if (reload_in_progress
+      && mode == SDmode
+      && MEM_P (operands[0])
+      && rtx_equal_p (operands[0], cfun->machine->sdmode_stack_slot)
+      && REG_P (operands[1]))
+    {
+      if (FP_REGNO_P (REGNO (operands[1])))
+       {
+         rtx mem = adjust_address_nv (operands[0], DDmode, 0);
+         mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+         emit_insn (gen_movsd_store (mem, operands[1]));
+       }
+      else if (INT_REGNO_P (REGNO (operands[1])))
+       {
+         rtx mem = adjust_address_nv (operands[0], mode, 4);
+         mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+         emit_insn (gen_movsd_hardfloat (mem, operands[1]));
+       }
+      else
+       gcc_unreachable();
+      return;
+    }
+  if (reload_in_progress
+      && mode == SDmode
+      && REG_P (operands[0])
+      && MEM_P (operands[1])
+      && rtx_equal_p (operands[1], cfun->machine->sdmode_stack_slot))
+    {
+      if (FP_REGNO_P (REGNO (operands[0])))
+       {
+         rtx mem = adjust_address_nv (operands[1], DDmode, 0);
+         mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+         emit_insn (gen_movsd_load (operands[0], mem));
+       }
+      else if (INT_REGNO_P (REGNO (operands[0])))
+       {
+         rtx mem = adjust_address_nv (operands[1], mode, 4);
+         mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+         emit_insn (gen_movsd_hardfloat (operands[0], mem));
+       }
+      else
+       gcc_unreachable();
+      return;
+    }
+
   /* FIXME:  In the long term, this switch statement should go away
      and be replaced by a sequence of tests based on things like
      mode == Pmode.  */
@@ -4604,6 +5066,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
     case DFmode:
     case DDmode:
     case SFmode:
+    case SDmode:
       if (CONSTANT_P (operands[1])
          && ! easy_fp_constant (operands[1], mode))
        operands[1] = force_const_mem (mode, operands[1]);
@@ -4729,7 +5192,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
             This should not be done for operands that contain LABEL_REFs.
             For now, we just handle the obvious case.  */
          if (GET_CODE (operands[1]) != LABEL_REF)
-           emit_insn (gen_rtx_USE (VOIDmode, operands[1]));
+           emit_use (operands[1]);
 
 #if TARGET_MACHO
          /* Darwin uses a special PIC legitimizer.  */
@@ -4771,6 +5234,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
          operands[1] = force_const_mem (mode, operands[1]);
 
          if (TARGET_TOC
+             && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
              && constant_pool_expr_p (XEXP (operands[1], 0))
              && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (
                        get_pool_constant (XEXP (operands[1], 0)),
@@ -4816,7 +5280,6 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
 /* Nonzero if we can use a floating-point register to pass this arg.  */
 #define USE_FP_FOR_ARG_P(CUM,MODE,TYPE)                \
   (SCALAR_FLOAT_MODE_P (MODE)                  \
-   && (MODE) != SDmode                         \
    && (CUM)->fregno <= FP_ARG_MAX_REG          \
    && TARGET_HARD_FLOAT && TARGET_FPRS)
 
@@ -5295,9 +5758,10 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   else if (DEFAULT_ABI == ABI_V4)
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
-         && (mode == SFmode || mode == DFmode
-             || mode == DDmode || mode == TDmode
-             || (mode == TFmode && !TARGET_IEEEQUAD)))
+         && ((TARGET_SINGLE_FLOAT && mode == SFmode)
+             || (TARGET_DOUBLE_FLOAT && mode == DFmode)
+             || (mode == TFmode && !TARGET_IEEEQUAD)
+             || mode == SDmode || mode == DDmode || mode == TDmode))
        {
          /* _Decimal128 must use an even/odd register pair.  This assumes
             that the register number is odd when fregno is odd.  */
@@ -5310,7 +5774,8 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          else
            {
              cum->fregno = FP_ARG_V4_MAX_REG + 1;
-             if (mode == DFmode || mode == TFmode || mode == DDmode || mode == TDmode)
+             if (mode == DFmode || mode == TFmode
+                 || mode == DDmode || mode == TDmode)
                cum->words += cum->words & 1;
              cum->words += rs6000_arg_size (mode, type);
            }
@@ -5362,7 +5827,6 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       cum->words = align_words + n_words;
 
       if (SCALAR_FLOAT_MODE_P (mode)
-         && mode != SDmode
          && TARGET_HARD_FLOAT && TARGET_FPRS)
        {
          /* _Decimal128 must be passed in an even/odd float register pair.
@@ -5430,8 +5894,8 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   /* On E500 v2, double arithmetic is done on the full 64-bit GPR, but
      are passed and returned in a pair of GPRs for ABI compatibility.  */
-  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode
-                            || mode == TFmode || mode == TCmode))
+  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+                            || mode == DCmode || mode == TCmode))
     {
       int n_words = rs6000_arg_size (mode, type);
 
@@ -5849,19 +6313,18 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   else if (TARGET_SPE_ABI && TARGET_SPE
           && (SPE_VECTOR_MODE (mode)
               || (TARGET_E500_DOUBLE && (mode == DFmode
-                                         || mode == DDmode
                                          || mode == DCmode
                                          || mode == TFmode
-                                         || mode == TDmode
                                          || mode == TCmode))))
     return rs6000_spe_function_arg (cum, mode, type);
 
   else if (abi == ABI_V4)
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
-         && (mode == SFmode || mode == DFmode
+         && ((TARGET_SINGLE_FLOAT && mode == SFmode)
+             || (TARGET_DOUBLE_FLOAT && mode == DFmode)
              || (mode == TFmode && !TARGET_IEEEQUAD)
-             || mode == DDmode || mode == TDmode))
+             || mode == SDmode || mode == DDmode || mode == TDmode))
        {
          /* _Decimal128 must use an even/odd register pair.  This assumes
             that the register number is odd when fregno is odd.  */
@@ -6321,11 +6784,17 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
           fregno <= FP_ARG_V4_MAX_REG && nregs < cfun->va_list_fpr_size;
           fregno++, off += UNITS_PER_FP_WORD, nregs++)
        {
-         mem = gen_rtx_MEM (DFmode, plus_constant (save_area, off));
-         MEM_NOTRAP_P (mem) = 1;
-         set_mem_alias_set (mem, set);
-         set_mem_align (mem, GET_MODE_ALIGNMENT (DFmode));
-         emit_move_insn (mem, gen_rtx_REG (DFmode, fregno));
+         mem = gen_rtx_MEM ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                             ? DFmode : SFmode, 
+                             plus_constant (save_area, off));
+         MEM_NOTRAP_P (mem) = 1;
+         set_mem_alias_set (mem, set);
+         set_mem_align (mem, GET_MODE_ALIGNMENT (
+                        (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                         ? DFmode : SFmode));
+         emit_move_insn (mem, gen_rtx_REG (
+                          (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                          ? DFmode : SFmode, fregno));
        }
 
       emit_label (lab);
@@ -6385,7 +6854,7 @@ rs6000_build_builtin_va_list (void)
 
 /* Implement va_start.  */
 
-void
+static void
 rs6000_va_start (tree valist, rtx nextarg)
 {
   HOST_WIDE_INT words, n_gpr, n_fpr;
@@ -6407,15 +6876,18 @@ rs6000_va_start (tree valist, rtx nextarg)
 
   valist = build_va_arg_indirect_ref (valist);
   gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
-  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
-  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
-  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist),
+               f_fpr, NULL_TREE);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist),
+               f_ovf, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
+               f_sav, NULL_TREE);
 
   /* Count number of gp and fp argument registers used.  */
-  words = current_function_args_info.words;
-  n_gpr = MIN (current_function_args_info.sysv_gregno - GP_ARG_MIN_REG,
+  words = crtl->args.info.words;
+  n_gpr = MIN (crtl->args.info.sysv_gregno - GP_ARG_MIN_REG,
               GP_ARG_NUM_REG);
-  n_fpr = MIN (current_function_args_info.fregno - FP_ARG_MIN_REG,
+  n_fpr = MIN (crtl->args.info.fregno - FP_ARG_MIN_REG,
               FP_ARG_NUM_REG);
 
   if (TARGET_DEBUG_ARG)
@@ -6425,7 +6897,7 @@ rs6000_va_start (tree valist, rtx nextarg)
 
   if (cfun->va_list_gpr_size)
     {
-      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (gpr), gpr,
+      t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
                  build_int_cst (NULL_TREE, n_gpr));
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -6433,7 +6905,7 @@ rs6000_va_start (tree valist, rtx nextarg)
 
   if (cfun->va_list_fpr_size)
     {
-      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (fpr), fpr,
+      t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
                  build_int_cst (NULL_TREE, n_fpr));
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -6444,7 +6916,7 @@ rs6000_va_start (tree valist, rtx nextarg)
   if (words != 0)
     t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t,
                size_int (words * UNITS_PER_WORD));
-  t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t);
+  t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -6461,7 +6933,7 @@ rs6000_va_start (tree valist, rtx nextarg)
   if (cfun->machine->varargs_save_offset)
     t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t,
                size_int (cfun->machine->varargs_save_offset));
-  t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (sav), sav, t);
+  t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 }
@@ -6469,7 +6941,8 @@ rs6000_va_start (tree valist, rtx nextarg)
 /* Implement va_arg.  */
 
 tree
-rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
+rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
+                       gimple_seq *post_p)
 {
   tree f_gpr, f_fpr, f_res, f_ovf, f_sav;
   tree gpr, fpr, ovf, sav, reg, t, u;
@@ -6478,6 +6951,7 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
   int align;
   tree ptrtype = build_pointer_type (type);
   int regalign = 0;
+  gimple stmt;
 
   if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
     {
@@ -6496,14 +6970,14 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
          if (elem_size < UNITS_PER_WORD)
            {
              tree real_part, imag_part;
-             tree post = NULL_TREE;
+             gimple_seq post = NULL;
 
              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);
+             gimple_seq_add_seq (pre_p, post);
 
              imag_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p,
                                                  post_p);
@@ -6523,27 +6997,32 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
 
   valist = build_va_arg_indirect_ref (valist);
   gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
-  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
-  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
-  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist),
+               f_fpr, NULL_TREE);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist),
+               f_ovf, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
+               f_sav, NULL_TREE);
 
   size = int_size_in_bytes (type);
   rsize = (size + 3) / 4;
   align = 1;
 
   if (TARGET_HARD_FLOAT && TARGET_FPRS
-      && (TYPE_MODE (type) == SFmode
-         || TYPE_MODE (type) == DFmode
-         || TYPE_MODE (type) == TFmode
-         || TYPE_MODE (type) == DDmode
-         || TYPE_MODE (type) == TDmode))
+      && ((TARGET_SINGLE_FLOAT && TYPE_MODE (type) == SFmode)
+          || (TARGET_DOUBLE_FLOAT 
+              && (TYPE_MODE (type) == DFmode 
+                 || TYPE_MODE (type) == TFmode
+                 || TYPE_MODE (type) == SDmode
+                 || TYPE_MODE (type) == DDmode
+                 || TYPE_MODE (type) == TDmode))))
     {
       /* FP args go in FP registers, if present.  */
       reg = fpr;
       n_reg = (size + 7) / 8;
-      sav_ofs = 8*4;
-      sav_scale = 8;
-      if (TYPE_MODE (type) != SFmode)
+      sav_ofs = ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) ? 8 : 4) * 4;
+      sav_scale = ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) ? 8 : 4);
+      if (TYPE_MODE (type) != SFmode && TYPE_MODE (type) != SDmode)
        align = 8;
     }
   else
@@ -6578,17 +7057,19 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       if (n_reg == 2 && reg == gpr)
        {
          regalign = 1;
-         u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), reg,
+         u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), unshare_expr (reg),
                     build_int_cst (TREE_TYPE (reg), n_reg - 1));
-         u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, u);
+         u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg),
+                     unshare_expr (reg), u);
        }
       /* _Decimal128 is passed in even/odd fpr pairs; the stored
         reg number is 0 for f1, so we want to make it odd.  */
       else if (reg == fpr && TYPE_MODE (type) == TDmode)
        {
          regalign = 1;
-         t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg, size_int (1));
-         u = build2 (MODIFY_EXPR, void_type_node, reg, t);
+         t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), unshare_expr (reg),
+                     build_int_cst (TREE_TYPE (reg), 1));
+         u = build2 (MODIFY_EXPR, void_type_node, unshare_expr (reg), t);
        }
 
       t = fold_convert (TREE_TYPE (reg), size_int (8 - n_reg + 1));
@@ -6601,27 +7082,31 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       if (sav_ofs)
        t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, size_int (sav_ofs));
 
-      u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg,
+      u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), unshare_expr (reg),
                  build_int_cst (TREE_TYPE (reg), n_reg));
       u = fold_convert (sizetype, u);
       u = build2 (MULT_EXPR, sizetype, u, size_int (sav_scale));
       t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, u);
 
-      t = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
-      gimplify_and_add (t, pre_p);
+      /* _Decimal32 varargs are located in the second word of the 64-bit
+        FP register for 32-bit binaries.  */
+      if (!TARGET_POWERPC64
+         && TARGET_HARD_FLOAT && TARGET_FPRS
+         && TYPE_MODE (type) == SDmode)
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
 
-      t = build1 (GOTO_EXPR, void_type_node, lab_over);
-      gimplify_and_add (t, pre_p);
+      gimplify_assign (addr, t, pre_p);
 
-      t = build1 (LABEL_EXPR, void_type_node, lab_false);
-      append_to_statement_list (t, pre_p);
+      gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over));
+
+      stmt = gimple_build_label (lab_false);
+      gimple_seq_add_stmt (pre_p, stmt);
 
       if ((n_reg == 2 && !regalign) || n_reg > 2)
        {
          /* Ensure that we don't find any more args in regs.
             Alignment has taken care of for special cases.  */
-         t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (reg), reg, size_int (8));
-         gimplify_and_add (t, pre_p);
+         gimplify_assign (reg, build_int_cst (TREE_TYPE (reg), 8), pre_p);
        }
     }
 
@@ -6639,17 +7124,15 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
     }
   gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  u = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
-  gimplify_and_add (u, pre_p);
+  gimplify_assign (unshare_expr (addr), t, pre_p);
 
   t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
-  t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t);
-  gimplify_and_add (t, pre_p);
+  gimplify_assign (unshare_expr (ovf), t, pre_p);
 
   if (lab_over)
     {
-      t = build1 (LABEL_EXPR, void_type_node, lab_over);
-      append_to_statement_list (t, pre_p);
+      stmt = gimple_build_label (lab_over);
+      gimple_seq_add_stmt (pre_p, stmt);
     }
 
   if (STRICT_ALIGNMENT
@@ -6841,20 +7324,20 @@ static struct builtin_description bdesc_2arg[] =
   { MASK_ALTIVEC, CODE_FOR_altivec_vrlb, "__builtin_altivec_vrlb", ALTIVEC_BUILTIN_VRLB },
   { MASK_ALTIVEC, CODE_FOR_altivec_vrlh, "__builtin_altivec_vrlh", ALTIVEC_BUILTIN_VRLH },
   { MASK_ALTIVEC, CODE_FOR_altivec_vrlw, "__builtin_altivec_vrlw", ALTIVEC_BUILTIN_VRLW },
-  { MASK_ALTIVEC, CODE_FOR_altivec_vslb, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB },
-  { MASK_ALTIVEC, CODE_FOR_altivec_vslh, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH },
-  { MASK_ALTIVEC, CODE_FOR_altivec_vslw, "__builtin_altivec_vslw", ALTIVEC_BUILTIN_VSLW },
+  { MASK_ALTIVEC, CODE_FOR_vashlv16qi3, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB },
+  { MASK_ALTIVEC, CODE_FOR_vashlv8hi3, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH },
+  { MASK_ALTIVEC, CODE_FOR_vashlv4si3, "__builtin_altivec_vslw", ALTIVEC_BUILTIN_VSLW },
   { MASK_ALTIVEC, CODE_FOR_altivec_vsl, "__builtin_altivec_vsl", ALTIVEC_BUILTIN_VSL },
   { MASK_ALTIVEC, CODE_FOR_altivec_vslo, "__builtin_altivec_vslo", ALTIVEC_BUILTIN_VSLO },
   { MASK_ALTIVEC, CODE_FOR_altivec_vspltb, "__builtin_altivec_vspltb", ALTIVEC_BUILTIN_VSPLTB },
   { MASK_ALTIVEC, CODE_FOR_altivec_vsplth, "__builtin_altivec_vsplth", ALTIVEC_BUILTIN_VSPLTH },
   { MASK_ALTIVEC, CODE_FOR_altivec_vspltw, "__builtin_altivec_vspltw", ALTIVEC_BUILTIN_VSPLTW },
-  { MASK_ALTIVEC, CODE_FOR_lshrv16qi3, "__builtin_altivec_vsrb", ALTIVEC_BUILTIN_VSRB },
-  { MASK_ALTIVEC, CODE_FOR_lshrv8hi3, "__builtin_altivec_vsrh", ALTIVEC_BUILTIN_VSRH },
-  { MASK_ALTIVEC, CODE_FOR_lshrv4si3, "__builtin_altivec_vsrw", ALTIVEC_BUILTIN_VSRW },
-  { MASK_ALTIVEC, CODE_FOR_ashrv16qi3, "__builtin_altivec_vsrab", ALTIVEC_BUILTIN_VSRAB },
-  { MASK_ALTIVEC, CODE_FOR_ashrv8hi3, "__builtin_altivec_vsrah", ALTIVEC_BUILTIN_VSRAH },
-  { MASK_ALTIVEC, CODE_FOR_ashrv4si3, "__builtin_altivec_vsraw", ALTIVEC_BUILTIN_VSRAW },
+  { MASK_ALTIVEC, CODE_FOR_vlshrv16qi3, "__builtin_altivec_vsrb", ALTIVEC_BUILTIN_VSRB },
+  { MASK_ALTIVEC, CODE_FOR_vlshrv8hi3, "__builtin_altivec_vsrh", ALTIVEC_BUILTIN_VSRH },
+  { MASK_ALTIVEC, CODE_FOR_vlshrv4si3, "__builtin_altivec_vsrw", ALTIVEC_BUILTIN_VSRW },
+  { MASK_ALTIVEC, CODE_FOR_vashrv16qi3, "__builtin_altivec_vsrab", ALTIVEC_BUILTIN_VSRAB },
+  { MASK_ALTIVEC, CODE_FOR_vashrv8hi3, "__builtin_altivec_vsrah", ALTIVEC_BUILTIN_VSRAH },
+  { MASK_ALTIVEC, CODE_FOR_vashrv4si3, "__builtin_altivec_vsraw", ALTIVEC_BUILTIN_VSRAW },
   { MASK_ALTIVEC, CODE_FOR_altivec_vsr, "__builtin_altivec_vsr", ALTIVEC_BUILTIN_VSR },
   { MASK_ALTIVEC, CODE_FOR_altivec_vsro, "__builtin_altivec_vsro", ALTIVEC_BUILTIN_VSRO },
   { MASK_ALTIVEC, CODE_FOR_subv16qi3, "__builtin_altivec_vsububm", ALTIVEC_BUILTIN_VSUBUBM },
@@ -7611,7 +8094,7 @@ paired_expand_lv_builtin (enum insn_code icode, tree exp, rtx target)
 }
 
 static rtx
-altivec_expand_lv_builtin (enum insn_code icode, tree exp, rtx target)
+altivec_expand_lv_builtin (enum insn_code icode, tree exp, rtx target, bool blk)
 {
   rtx pat, addr;
   tree arg0 = CALL_EXPR_ARG (exp, 0);
@@ -7639,12 +8122,12 @@ altivec_expand_lv_builtin (enum insn_code icode, tree exp, rtx target)
 
   if (op0 == const0_rtx)
     {
-      addr = gen_rtx_MEM (tmode, op1);
+      addr = gen_rtx_MEM (blk ? BLKmode : tmode, op1);
     }
   else
     {
       op0 = copy_to_mode_reg (mode0, op0);
-      addr = gen_rtx_MEM (tmode, gen_rtx_PLUS (Pmode, op0, op1));
+      addr = gen_rtx_MEM (blk ? BLKmode : tmode, gen_rtx_PLUS (Pmode, op0, op1));
     }
 
   pat = GEN_FCN (icode) (target, addr);
@@ -8151,6 +8634,15 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
     case ALTIVEC_BUILTIN_STVXL:
       return altivec_expand_stv_builtin (CODE_FOR_altivec_stvxl, exp);
 
+    case ALTIVEC_BUILTIN_STVLX:
+      return altivec_expand_stv_builtin (CODE_FOR_altivec_stvlx, exp);
+    case ALTIVEC_BUILTIN_STVLXL:
+      return altivec_expand_stv_builtin (CODE_FOR_altivec_stvlxl, exp);
+    case ALTIVEC_BUILTIN_STVRX:
+      return altivec_expand_stv_builtin (CODE_FOR_altivec_stvrx, exp);
+    case ALTIVEC_BUILTIN_STVRXL:
+      return altivec_expand_stv_builtin (CODE_FOR_altivec_stvrxl, exp);
+
     case ALTIVEC_BUILTIN_MFVSCR:
       icode = CODE_FOR_altivec_mfvscr;
       tmode = insn_data[icode].operand[0].mode;
@@ -8253,25 +8745,37 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
     {
     case ALTIVEC_BUILTIN_LVSL:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsl,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVSR:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsr,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVEBX:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvebx,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVEHX:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvehx,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVEWX:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvewx,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVXL:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl,
-                                       exp, target);
+                                       exp, target, false);
     case ALTIVEC_BUILTIN_LVX:
       return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx,
-                                       exp, target);
+                                       exp, target, false);
+    case ALTIVEC_BUILTIN_LVLX:
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvlx,
+                                       exp, target, true);
+    case ALTIVEC_BUILTIN_LVLXL:
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvlxl,
+                                       exp, target, true);
+    case ALTIVEC_BUILTIN_LVRX:
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvrx,
+                                       exp, target, true);
+    case ALTIVEC_BUILTIN_LVRXL:
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvrxl,
+                                       exp, target, true);
     default:
       break;
       /* Fall through.  */
@@ -8779,7 +9283,9 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   /* FIXME: There's got to be a nicer way to handle this case than
      constructing a new CALL_EXPR.  */
   if (fcode == ALTIVEC_BUILTIN_VCFUX
-      || fcode == ALTIVEC_BUILTIN_VCFSX)
+      || fcode == ALTIVEC_BUILTIN_VCFSX
+      || fcode == ALTIVEC_BUILTIN_VCTUXS
+      || fcode == ALTIVEC_BUILTIN_VCTSXS)
     {
       if (call_expr_nargs (exp) == 1)
        exp = build_call_nary (TREE_TYPE (exp), CALL_EXPR_FN (exp),
@@ -8836,6 +9342,7 @@ build_opaque_vector_type (tree node, int nunits)
 {
   node = copy_node (node);
   TYPE_MAIN_VARIANT (node) = node;
+  TYPE_CANONICAL (node) = node;
   return build_vector_type (node, nunits);
 }
 
@@ -8973,6 +9480,10 @@ rs6000_init_builtins (void)
   if (built_in_decls [BUILT_IN_CLOG])
     set_user_assembler_name (built_in_decls [BUILT_IN_CLOG], "__clog");
 #endif
+
+#ifdef SUBTARGET_INIT_BUILTINS
+  SUBTARGET_INIT_BUILTINS;
+#endif
 }
 
 /* Search through a set of builtins and enable the mask bits.
@@ -9303,7 +9814,9 @@ altivec_init_builtins (void)
   tree int_ftype_opaque
     = build_function_type_list (integer_type_node,
                                opaque_V4SI_type_node, NULL_TREE);
-
+  tree opaque_ftype_opaque
+    = build_function_type (integer_type_node,
+                               NULL_TREE);
   tree opaque_ftype_opaque_int
     = build_function_type_list (opaque_V4SI_type_node,
                                opaque_V4SI_type_node, integer_type_node, NULL_TREE);
@@ -9449,10 +9962,36 @@ altivec_init_builtins (void)
   def_builtin (MASK_ALTIVEC, "__builtin_vec_stvebx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEBX);
   def_builtin (MASK_ALTIVEC, "__builtin_vec_stvehx", void_ftype_opaque_long_pvoid, ALTIVEC_BUILTIN_VEC_STVEHX);
 
+  if (rs6000_cpu == PROCESSOR_CELL)
+    {
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvlx",  v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVLX);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvlxl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVLXL);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvrx",  v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVRX);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvrxl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVRXL);
+
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_lvlx",  v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVLX);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_lvlxl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVLXL);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_lvrx",  v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVRX);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_lvrxl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_VEC_LVRXL);
+
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvlx",  void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVLX);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvlxl", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVLXL);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvrx",  void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVRX);
+      def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvrxl", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVRXL);
+
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_stvlx",  void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_VEC_STVLX);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_stvlxl", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_VEC_STVLXL);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_stvrx",  void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_VEC_STVRX);
+      def_builtin (MASK_ALTIVEC, "__builtin_vec_stvrxl", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_VEC_STVRXL);
+    }
   def_builtin (MASK_ALTIVEC, "__builtin_vec_step", int_ftype_opaque, ALTIVEC_BUILTIN_VEC_STEP);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_splats", opaque_ftype_opaque, ALTIVEC_BUILTIN_VEC_SPLATS);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_promote", opaque_ftype_opaque, ALTIVEC_BUILTIN_VEC_PROMOTE);
 
   def_builtin (MASK_ALTIVEC, "__builtin_vec_sld", opaque_ftype_opaque_opaque_int, ALTIVEC_BUILTIN_VEC_SLD);
   def_builtin (MASK_ALTIVEC, "__builtin_vec_splat", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_SPLAT);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_extract", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_EXTRACT);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_insert", opaque_ftype_opaque_opaque_int, ALTIVEC_BUILTIN_VEC_INSERT);
   def_builtin (MASK_ALTIVEC, "__builtin_vec_vspltw", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTW);
   def_builtin (MASK_ALTIVEC, "__builtin_vec_vsplth", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTH);
   def_builtin (MASK_ALTIVEC, "__builtin_vec_vspltb", opaque_ftype_opaque_int, ALTIVEC_BUILTIN_VEC_VSPLTB);
@@ -10901,49 +11440,152 @@ mems_ok_for_quad_peep (rtx mem1, rtx mem2)
       offset1 = 0;
     }
 
-  /* And now for the second addr.  */
-  if (GET_CODE (addr2) == PLUS)
+  /* And now for the second addr.  */
+  if (GET_CODE (addr2) == PLUS)
+    {
+      /* If not a REG, return zero.  */
+      if (GET_CODE (XEXP (addr2, 0)) != REG)
+       return 0;
+      else
+       {
+         reg2 = REGNO (XEXP (addr2, 0));
+         /* The offset must be constant. */
+         if (GET_CODE (XEXP (addr2, 1)) != CONST_INT)
+           return 0;
+         offset2 = INTVAL (XEXP (addr2, 1));
+       }
+    }
+  else if (GET_CODE (addr2) != REG)
+    return 0;
+  else
+    {
+      reg2 = REGNO (addr2);
+      /* This was a simple (mem (reg)) expression.  Offset is 0.  */
+      offset2 = 0;
+    }
+
+  /* Both of these must have the same base register.  */
+  if (reg1 != reg2)
+    return 0;
+
+  /* The offset for the second addr must be 8 more than the first addr.  */
+  if (offset2 != offset1 + 8)
+    return 0;
+
+  /* All the tests passed.  addr1 and addr2 are valid for lfq or stfq
+     instructions.  */
+  return 1;
+}
+\f
+
+rtx
+rs6000_secondary_memory_needed_rtx (enum machine_mode mode)
+{
+  static bool eliminated = false;
+  if (mode != SDmode)
+    return assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+  else
+    {
+      rtx mem = cfun->machine->sdmode_stack_slot;
+      gcc_assert (mem != NULL_RTX);
+
+      if (!eliminated)
+       {
+         mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+         cfun->machine->sdmode_stack_slot = mem;
+         eliminated = true;
+       }
+      return mem;
+    }
+}
+
+static tree
+rs6000_check_sdmode (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+{
+  /* Don't walk into types.  */
+  if (*tp == NULL_TREE || *tp == error_mark_node || TYPE_P (*tp))
+    {
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
+  switch (TREE_CODE (*tp))
+    {
+    case VAR_DECL:
+    case PARM_DECL:
+    case FIELD_DECL:
+    case RESULT_DECL:
+    case REAL_CST:
+    case INDIRECT_REF:
+    case ALIGN_INDIRECT_REF:
+    case MISALIGNED_INDIRECT_REF:
+    case VIEW_CONVERT_EXPR:
+      if (TYPE_MODE (TREE_TYPE (*tp)) == SDmode)
+       return *tp;
+      break;
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+
+/* Allocate a 64-bit stack slot to be used for copying SDmode
+   values through if this function has any SDmode references.  */
+
+static void
+rs6000_alloc_sdmode_stack_slot (void)
+{
+  tree t;
+  basic_block bb;
+  gimple_stmt_iterator gsi;
+
+  gcc_assert (cfun->machine->sdmode_stack_slot == NULL_RTX);
+
+  FOR_EACH_BB (bb)
+    for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+       tree ret = walk_gimple_op (gsi_stmt (gsi), rs6000_check_sdmode, NULL);
+       if (ret)
+         {
+           rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
+           cfun->machine->sdmode_stack_slot = adjust_address_nv (stack,
+                                                                 SDmode, 0);
+           return;
+         }
+      }
+
+  /* Check for any SDmode parameters of the function.  */
+  for (t = DECL_ARGUMENTS (cfun->decl); t; t = TREE_CHAIN (t))
     {
-      /* If not a REG, return zero.  */
-      if (GET_CODE (XEXP (addr2, 0)) != REG)
-       return 0;
-      else
+      if (TREE_TYPE (t) == error_mark_node)
+       continue;
+
+      if (TYPE_MODE (TREE_TYPE (t)) == SDmode
+         || TYPE_MODE (DECL_ARG_TYPE (t)) == SDmode)
        {
-         reg2 = REGNO (XEXP (addr2, 0));
-         /* The offset must be constant. */
-         if (GET_CODE (XEXP (addr2, 1)) != CONST_INT)
-           return 0;
-         offset2 = INTVAL (XEXP (addr2, 1));
+         rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
+         cfun->machine->sdmode_stack_slot = adjust_address_nv (stack,
+                                                               SDmode, 0);
+         return;
        }
     }
-  else if (GET_CODE (addr2) != REG)
-    return 0;
-  else
-    {
-      reg2 = REGNO (addr2);
-      /* This was a simple (mem (reg)) expression.  Offset is 0.  */
-      offset2 = 0;
-    }
-
-  /* Both of these must have the same base register.  */
-  if (reg1 != reg2)
-    return 0;
-
-  /* The offset for the second addr must be 8 more than the first addr.  */
-  if (offset2 != offset1 + 8)
-    return 0;
+}
 
-  /* All the tests passed.  addr1 and addr2 are valid for lfq or stfq
-     instructions.  */
-  return 1;
+static void
+rs6000_instantiate_decls (void)
+{
+  if (cfun->machine->sdmode_stack_slot != NULL_RTX)
+    instantiate_decl_rtl (cfun->machine->sdmode_stack_slot);
 }
-\f
+
 /* Return the register class of a scratch register needed to copy IN into
-   or out of a register in CLASS in MODE.  If it can be done directly,
+   or out of a register in RCLASS in MODE.  If it can be done directly,
    NO_REGS is returned.  */
 
 enum reg_class
-rs6000_secondary_reload_class (enum reg_class class,
+rs6000_secondary_reload_class (enum reg_class rclass,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               rtx in)
 {
@@ -10962,7 +11604,7 @@ rs6000_secondary_reload_class (enum reg_class class,
 
         On Darwin, pic addresses require a load from memory, which
         needs a base register.  */
-      if (class != BASE_REGS
+      if (rclass != BASE_REGS
          && (GET_CODE (in) == SYMBOL_REF
              || GET_CODE (in) == HIGH
              || GET_CODE (in) == LABEL_REF
@@ -10991,22 +11633,22 @@ rs6000_secondary_reload_class (enum reg_class class,
 
   /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
      into anything.  */
-  if (class == GENERAL_REGS || class == BASE_REGS
+  if (rclass == GENERAL_REGS || rclass == BASE_REGS
       || (regno >= 0 && INT_REGNO_P (regno)))
     return NO_REGS;
 
   /* Constants, memory, and FP registers can go into FP registers.  */
   if ((regno == -1 || FP_REGNO_P (regno))
-      && (class == FLOAT_REGS || class == NON_SPECIAL_REGS))
-    return NO_REGS;
+      && (rclass == FLOAT_REGS || rclass == NON_SPECIAL_REGS))
+    return (mode != SDmode) ? NO_REGS : GENERAL_REGS;
 
   /* Memory, and AltiVec registers can go into AltiVec registers.  */
   if ((regno == -1 || ALTIVEC_REGNO_P (regno))
-      && class == ALTIVEC_REGS)
+      && rclass == ALTIVEC_REGS)
     return NO_REGS;
 
   /* We can copy among the CR registers.  */
-  if ((class == CR_REGS || class == CR0_REGS)
+  if ((rclass == CR_REGS || rclass == CR0_REGS)
       && regno >= 0 && CR_REGNO_P (regno))
     return NO_REGS;
 
@@ -11089,7 +11731,7 @@ rs6000_got_register (rtx value ATTRIBUTE_UNUSED)
       && !df_regs_ever_live_p (RS6000_PIC_OFFSET_TABLE_REGNUM))
     df_set_regs_ever_live (RS6000_PIC_OFFSET_TABLE_REGNUM, true);
 
-  current_function_uses_pic_offset_table = 1;
+  crtl->uses_pic_offset_table = 1;
 
   return pic_offset_table_rtx;
 }
@@ -11101,7 +11743,7 @@ rs6000_got_register (rtx value ATTRIBUTE_UNUSED)
 static struct machine_function *
 rs6000_init_machine_status (void)
 {
-  return ggc_alloc_cleared (sizeof (machine_function));
+  return GGC_CNEW (machine_function);
 }
 \f
 /* These macros test for integers and extract the low-order bits.  */
@@ -11864,9 +12506,13 @@ print_operand (FILE *file, rtx x, int code)
          fprintf (file, "0,%s", reg_names[REGNO (tmp)]);
        else
          {
-           gcc_assert (GET_CODE (tmp) == PLUS
-                       && REG_P (XEXP (tmp, 0))
-                       && REG_P (XEXP (tmp, 1)));
+           if (!GET_CODE (tmp) == PLUS
+               || !REG_P (XEXP (tmp, 0))
+               || !REG_P (XEXP (tmp, 1)))
+             {
+               output_operand_lossage ("invalid %%y value, try using the 'Z' constraint");
+               break;
+             }
 
            if (REGNO (XEXP (tmp, 0)) == 0)
              fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (tmp, 1)) ],
@@ -11958,44 +12604,45 @@ print_operand_address (FILE *file, rtx x)
 #endif
   else if (legitimate_constant_pool_address_p (x))
     {
-      if (TARGET_AIX && (!TARGET_ELF || !TARGET_MINIMAL_TOC))
-       {
-         rtx contains_minus = XEXP (x, 1);
-         rtx minus, symref;
-         const char *name;
-
-         /* Find the (minus (sym) (toc)) buried in X, and temporarily
-            turn it into (sym) for output_addr_const.  */
-         while (GET_CODE (XEXP (contains_minus, 0)) != MINUS)
-           contains_minus = XEXP (contains_minus, 0);
-
-         minus = XEXP (contains_minus, 0);
-         symref = XEXP (minus, 0);
-         XEXP (contains_minus, 0) = symref;
-         if (TARGET_ELF)
-           {
-             char *newname;
-
-             name = XSTR (symref, 0);
-             newname = alloca (strlen (name) + sizeof ("@toc"));
-             strcpy (newname, name);
-             strcat (newname, "@toc");
-             XSTR (symref, 0) = newname;
-           }
-         output_addr_const (file, XEXP (x, 1));
-         if (TARGET_ELF)
-           XSTR (symref, 0) = name;
-         XEXP (contains_minus, 0) = minus;
-       }
-      else
-       output_addr_const (file, XEXP (x, 1));
-
+      output_addr_const (file, XEXP (x, 1));
       fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]);
     }
   else
     gcc_unreachable ();
 }
 \f
+/* Implement OUTPUT_ADDR_CONST_EXTRA for address X.  */
+
+bool
+rs6000_output_addr_const_extra (FILE *file, rtx x)
+{
+  if (GET_CODE (x) == UNSPEC)
+    switch (XINT (x, 1))
+      {
+      case UNSPEC_TOCREL:
+       x = XVECEXP (x, 0, 0);
+       gcc_assert (GET_CODE (x) == SYMBOL_REF);
+       output_addr_const (file, x);
+       if (!TARGET_AIX || (TARGET_ELF && TARGET_MINIMAL_TOC))
+         {
+           putc ('-', file);
+           assemble_name (file, toc_label_name);
+         }
+       else if (TARGET_ELF)
+         fputs ("@toc", file);
+       return true;
+
+#if TARGET_MACHO
+      case UNSPEC_MACHOPIC_OFFSET:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       putc ('-', file);
+       machopic_output_function_base_name (file);
+       return true;
+#endif
+      }
+  return false;
+}
+\f
 /* Target hook for assembling integer objects.  The PowerPC version has
    to handle fixup entries for relocatable code if RELOCATABLE_NEEDS_FIXUP
    is defined.  It also needs to handle DI-mode objects on 64-bit
@@ -13177,7 +13824,7 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
   rtx shift = NULL_RTX;
 
   if (sync_p)
-    emit_insn (gen_memory_barrier ());
+    emit_insn (gen_lwsync ());
 
   if (GET_CODE (m) == NOT)
     used_m = XEXP (m, 0);
@@ -13417,7 +14064,7 @@ rs6000_split_atomic_op (enum rtx_code code, rtx mem, rtx val,
   enum machine_mode mode = GET_MODE (mem);
   rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
 
-  emit_insn (gen_memory_barrier ());
+  emit_insn (gen_lwsync ());
 
   label = gen_label_rtx ();
   emit_label (label);
@@ -13457,7 +14104,7 @@ rs6000_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval,
   enum machine_mode mode = GET_MODE (mem);
   rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
 
-  emit_insn (gen_memory_barrier ());
+  emit_insn (gen_lwsync ());
 
   label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
   label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
@@ -13490,8 +14137,6 @@ rs6000_split_lock_test_and_set (rtx retval, rtx mem, rtx val, rtx scratch)
   enum machine_mode mode = GET_MODE (mem);
   rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
 
-  emit_insn (gen_memory_barrier ());
-
   label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
   emit_label (XEXP (label, 0));
 
@@ -13514,7 +14159,8 @@ rs6000_expand_compare_and_swapqhi (rtx dst, rtx mem, rtx oldval, rtx newval)
   HOST_WIDE_INT imask = GET_MODE_MASK (mode);
 
   /* Shift amount for subword relative to aligned word.  */
-  addrSI = force_reg (SImode, gen_lowpart_common (SImode, XEXP (mem, 0)));
+  addrSI = force_reg (GET_MODE (XEXP (mem, 0)), XEXP (mem, 0));
+  addrSI = force_reg (SImode, gen_lowpart_common (SImode, addrSI));
   shift = gen_reg_rtx (SImode);
   emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3),
                         GEN_INT (shift_mask)));
@@ -13550,6 +14196,9 @@ rs6000_expand_compare_and_swapqhi (rtx dst, rtx mem, rtx oldval, rtx newval)
   emit_insn (gen_sync_compare_and_swapqhi_internal (wdst, mask,
                                                    oldval, newval, mem));
 
+  /* Shift the result back.  */
+  emit_insn (gen_lshrsi3 (wdst, wdst, shift));
+
   emit_move_insn (dst, gen_lowpart (mode, wdst));
 }
 
@@ -13560,7 +14209,7 @@ rs6000_split_compare_and_swapqhi (rtx dest, rtx mask,
 {
   rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
 
-  emit_insn (gen_memory_barrier ());
+  emit_insn (gen_lwsync ());
   label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
   label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
   emit_label (XEXP (label1, 0));
@@ -13618,7 +14267,8 @@ rs6000_split_multireg_move (rtx dst, rtx src)
   mode = GET_MODE (dst);
   nregs = hard_regno_nregs[reg][mode];
   if (FP_REGNO_P (reg))
-    reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
+    reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : 
+       ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT) ? DFmode : SFmode);
   else if (ALTIVEC_REGNO_P (reg))
     reg_mode = V16QImode;
   else if (TARGET_E500_DOUBLE && mode == TFmode)
@@ -13765,7 +14415,7 @@ first_reg_to_save (void)
 
 #if TARGET_MACHO
   if (flag_pic
-      && current_function_uses_pic_offset_table
+      && crtl->uses_pic_offset_table
       && first_reg > RS6000_PIC_OFFSET_TABLE_REGNUM)
     return RS6000_PIC_OFFSET_TABLE_REGNUM;
 #endif
@@ -13802,7 +14452,7 @@ first_altivec_reg_to_save (void)
   /* On Darwin, the unwind routines are compiled without
      TARGET_ALTIVEC, and use save_world to save/restore the
      altivec registers when necessary.  */
-  if (DEFAULT_ABI == ABI_DARWIN && current_function_calls_eh_return
+  if (DEFAULT_ABI == ABI_DARWIN && crtl->calls_eh_return
       && ! TARGET_ALTIVEC)
     return FIRST_ALTIVEC_REGNO + 20;
 
@@ -13826,7 +14476,7 @@ compute_vrsave_mask (void)
   /* On Darwin, the unwind routines are compiled without
      TARGET_ALTIVEC, and use save_world to save/restore the
      call-saved altivec registers when necessary.  */
-  if (DEFAULT_ABI == ABI_DARWIN && current_function_calls_eh_return
+  if (DEFAULT_ABI == ABI_DARWIN && crtl->calls_eh_return
       && ! TARGET_ALTIVEC)
     mask |= 0xFFF;
 
@@ -13843,7 +14493,7 @@ compute_vrsave_mask (void)
      them in again.  More importantly, the mask we compute here is
      used to generate CLOBBERs in the set_vrsave insn, and we do not
      wish the argument registers to die.  */
-  for (i = cfun->args_info.vregno - 1; i >= ALTIVEC_ARG_MIN_REG; --i)
+  for (i = crtl->args.info.vregno - 1; i >= ALTIVEC_ARG_MIN_REG; --i)
     mask &= ~ALTIVEC_REG_BIT (i);
 
   /* Similarly, remove the return value from the set.  */
@@ -13868,7 +14518,7 @@ compute_save_world_info (rs6000_stack_t *info_ptr)
   info_ptr->world_save_p
     = (WORLD_SAVE_P (info_ptr)
        && DEFAULT_ABI == ABI_DARWIN
-       && ! (current_function_calls_setjmp && flag_exceptions)
+       && ! (cfun->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
@@ -13895,6 +14545,9 @@ compute_save_world_info (rs6000_stack_t *info_ptr)
         will attempt to save it. */
       info_ptr->vrsave_size  = 4;
 
+      /* If we are going to save the world, we need to save the link register too.  */
+      info_ptr->lr_save_p = 1;
+
       /* "Save" the VRsave register too if we're saving the world.  */
       if (info_ptr->vrsave_mask == 0)
        info_ptr->vrsave_mask = compute_vrsave_mask ();
@@ -14050,7 +14703,7 @@ rs6000_stack_info (void)
   if (((TARGET_TOC && TARGET_MINIMAL_TOC)
        || (flag_pic == 1 && DEFAULT_ABI == ABI_V4)
        || (flag_pic && DEFAULT_ABI == ABI_DARWIN))
-      && current_function_uses_const_pool
+      && crtl->uses_const_pool
       && info_ptr->first_gp_reg_save > RS6000_PIC_OFFSET_TABLE_REGNUM)
     first_gp = RS6000_PIC_OFFSET_TABLE_REGNUM;
   else
@@ -14086,15 +14739,14 @@ rs6000_stack_info (void)
 
   /* Determine if we need to save the link register.  */
   if ((DEFAULT_ABI == ABI_AIX
-       && current_function_profile
+       && crtl->profile
        && !TARGET_PROFILE_KERNEL)
 #ifdef TARGET_RELOCATABLE
       || (TARGET_RELOCATABLE && (get_pool_size () != 0))
 #endif
       || (info_ptr->first_fp_reg_save != 64
          && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
-      || info_ptr->first_altivec_reg_save <= LAST_ALTIVEC_REGNO
-      || (DEFAULT_ABI == ABI_V4 && current_function_calls_alloca)
+      || (DEFAULT_ABI == ABI_V4 && cfun->calls_alloca)
       || info_ptr->calls_p
       || rs6000_ra_ever_killed ())
     {
@@ -14115,7 +14767,7 @@ rs6000_stack_info (void)
   /* If the current function calls __builtin_eh_return, then we need
      to allocate stack space for registers that will hold data for
      the exception handler.  */
-  if (current_function_calls_eh_return)
+  if (crtl->calls_eh_return)
     {
       unsigned int i;
       for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; ++i)
@@ -14133,7 +14785,7 @@ rs6000_stack_info (void)
   info_ptr->reg_size     = reg_size;
   info_ptr->fixed_size   = RS6000_SAVE_AREA;
   info_ptr->vars_size    = RS6000_ALIGN (get_frame_size (), 8);
-  info_ptr->parm_size    = RS6000_ALIGN (current_function_outgoing_args_size,
+  info_ptr->parm_size    = RS6000_ALIGN (crtl->outgoing_args_size,
                                         TARGET_ALTIVEC ? 16 : 8);
   if (FRAME_GROWS_DOWNWARD)
     info_ptr->vars_size
@@ -14210,7 +14862,7 @@ rs6000_stack_info (void)
        {
          /* Align stack so SPE GPR save area is aligned on a
             double-word boundary.  */
-         if (info_ptr->spe_gp_size != 0)
+         if (info_ptr->spe_gp_size != 0 && info_ptr->cr_save_offset != 0)
            info_ptr->spe_padding_size
              = 8 - (-info_ptr->cr_save_offset % 8);
          else
@@ -14334,9 +14986,9 @@ spe_func_has_64bit_regs_p (void)
 
   /* Functions that save and restore all the call-saved registers will
      need to save/restore the registers in 64-bits.  */
-  if (current_function_calls_eh_return
-      || current_function_calls_setjmp
-      || current_function_has_nonlocal_goto)
+  if (crtl->calls_eh_return
+      || cfun->calls_setjmp
+      || crtl->has_nonlocal_goto)
     return true;
 
   insns = get_insns ();
@@ -14587,7 +15239,7 @@ rs6000_ra_ever_killed (void)
   rtx reg;
   rtx insn;
 
-  if (current_function_is_thunk)
+  if (crtl->is_thunk)
     return 0;
 
   /* regs_ever_live has LR marked as used if any sibcalls are present,
@@ -14743,7 +15395,7 @@ rs6000_emit_eh_reg_restore (rtx source, rtx scratch)
       rtx tmp;
 
       if (frame_pointer_needed
-         || current_function_calls_alloca
+         || cfun->calls_alloca
          || info->total_size > 32767)
        {
          tmp = gen_frame_mem (Pmode, frame_rtx);
@@ -14811,8 +15463,7 @@ create_TOC_reference (rtx symbol)
   return gen_rtx_PLUS (Pmode,
           gen_rtx_REG (Pmode, TOC_REGISTER),
             gen_rtx_CONST (Pmode,
-              gen_rtx_MINUS (Pmode, symbol,
-                gen_rtx_SYMBOL_REF (Pmode, toc_label_name))));
+              gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol), UNSPEC_TOCREL)));
 }
 
 /* If _Unwind_* has been called from within the same module,
@@ -14865,10 +15516,12 @@ rs6000_emit_stack_tie (void)
 
 /* Emit the correct code for allocating stack space, as insns.
    If COPY_R12, make sure a copy of the old frame is left in r12.
+   If COPY_R11, make sure a copy of the old frame is left in r11,
+   in preference to r12 if COPY_R12.
    The generated code may use hard register 0 as a temporary.  */
 
 static void
-rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12)
+rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12, int copy_r11)
 {
   rtx insn;
   rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
@@ -14882,7 +15535,7 @@ rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12)
       return;
     }
 
-  if (current_function_limit_stack)
+  if (crtl->limit_stack)
     {
       if (REG_P (stack_limit_rtx)
          && REGNO (stack_limit_rtx) > 1
@@ -14917,11 +15570,16 @@ rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12)
        warning (0, "stack limit expression is not supported");
     }
 
-  if (copy_r12 || ! TARGET_UPDATE)
-    emit_move_insn (gen_rtx_REG (Pmode, 12), stack_reg);
+  if (copy_r12 || copy_r11 || ! TARGET_UPDATE)
+    emit_move_insn (copy_r11
+                    ? gen_rtx_REG (Pmode, 11)
+                    : gen_rtx_REG (Pmode, 12),
+                    stack_reg);
 
   if (TARGET_UPDATE)
     {
+      rtx par, set, mem;
+
       if (size > 32767)
        {
          /* Need a note here so that try_split doesn't get confused.  */
@@ -14937,14 +15595,28 @@ rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12)
                                            todec, stack_reg)
                        : gen_movdi_di_update (stack_reg, stack_reg,
                                            todec, stack_reg));
+      /* Since we didn't use gen_frame_mem to generate the MEM, grab
+        it now and set the alias set/attributes. The above gen_*_update
+        calls will generate a PARALLEL with the MEM set being the first
+        operation. */
+      par = PATTERN (insn);
+      gcc_assert (GET_CODE (par) == PARALLEL);
+      set = XVECEXP (par, 0, 0);
+      gcc_assert (GET_CODE (set) == SET);
+      mem = SET_DEST (set);
+      gcc_assert (MEM_P (mem));
+      MEM_NOTRAP_P (mem) = 1;
+      set_mem_alias_set (mem, get_frame_alias_set ());
     }
   else
     {
       insn = emit_insn (TARGET_32BIT
                        ? gen_addsi3 (stack_reg, stack_reg, todec)
                        : gen_adddi3 (stack_reg, stack_reg, todec));
-      emit_move_insn (gen_rtx_MEM (Pmode, stack_reg),
-                     gen_rtx_REG (Pmode, 12));
+      emit_move_insn (gen_frame_mem (Pmode, stack_reg),
+                     copy_r11
+                      ? gen_rtx_REG (Pmode, 11)
+                      : gen_rtx_REG (Pmode, 12));
     }
 
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -15033,77 +15705,12 @@ rs6000_frame_related (rtx insn, rtx reg, HOST_WIDE_INT val,
          }
     }
 
-  if (TARGET_SPE)
-    real = spe_synthesize_frame_save (real);
-
   RTX_FRAME_RELATED_P (insn) = 1;
   REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
                                        real,
                                        REG_NOTES (insn));
 }
 
-/* Given an SPE frame note, return a PARALLEL of SETs with the
-   original note, plus a synthetic register save.  */
-
-static rtx
-spe_synthesize_frame_save (rtx real)
-{
-  rtx synth, offset, reg, real2;
-
-  if (GET_CODE (real) != SET
-      || GET_MODE (SET_SRC (real)) != V2SImode)
-    return real;
-
-  /* For the SPE, registers saved in 64-bits, get a PARALLEL for their
-     frame related note.  The parallel contains a set of the register
-     being saved, and another set to a synthetic register (n+1200).
-     This is so we can differentiate between 64-bit and 32-bit saves.
-     Words cannot describe this nastiness.  */
-
-  gcc_assert (GET_CODE (SET_DEST (real)) == MEM
-             && GET_CODE (XEXP (SET_DEST (real), 0)) == PLUS
-             && GET_CODE (SET_SRC (real)) == REG);
-
-  /* Transform:
-       (set (mem (plus (reg x) (const y)))
-            (reg z))
-     into:
-       (set (mem (plus (reg x) (const y+4)))
-            (reg z+1200))
-  */
-
-  real2 = copy_rtx (real);
-  PUT_MODE (SET_DEST (real2), SImode);
-  reg = SET_SRC (real2);
-  real2 = replace_rtx (real2, reg, gen_rtx_REG (SImode, REGNO (reg)));
-  synth = copy_rtx (real2);
-
-  if (BYTES_BIG_ENDIAN)
-    {
-      offset = XEXP (XEXP (SET_DEST (real2), 0), 1);
-      real2 = replace_rtx (real2, offset, GEN_INT (INTVAL (offset) + 4));
-    }
-
-  reg = SET_SRC (synth);
-
-  synth = replace_rtx (synth, reg,
-                      gen_rtx_REG (SImode, REGNO (reg) + 1200));
-
-  offset = XEXP (XEXP (SET_DEST (synth), 0), 1);
-  synth = replace_rtx (synth, offset,
-                      GEN_INT (INTVAL (offset)
-                               + (BYTES_BIG_ENDIAN ? 0 : 4)));
-
-  RTX_FRAME_RELATED_P (synth) = 1;
-  RTX_FRAME_RELATED_P (real2) = 1;
-  if (BYTES_BIG_ENDIAN)
-    real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, synth, real2));
-  else
-    real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, real2, synth));
-
-  return real;
-}
-
 /* Returns an insn that has a vrsave set operation with the
    appropriate CLOBBERs.  */
 
@@ -15232,11 +15839,11 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
    and cannot use stmw/lmw if there are any in its range.  */
 
 static bool
-no_global_regs_above (int first_greg)
+no_global_regs_above (int first, bool gpr)
 {
   int i;
-  for (i = 0; i < 32 - first_greg; i++)
-    if (global_regs[first_greg + i])
+  for (i = first; i < gpr ? 32 : 64 ; i++)
+    if (global_regs[i])
       return false;
   return true;
 }
@@ -15245,6 +15852,163 @@ no_global_regs_above (int first_greg)
 #define TARGET_FIX_AND_CONTINUE 0
 #endif
 
+/* It's really GPR 13 and FPR 14, but we need the smaller of the two.  */
+#define FIRST_SAVRES_REGISTER FIRST_SAVED_GP_REGNO
+#define LAST_SAVRES_REGISTER 31
+#define N_SAVRES_REGISTERS (LAST_SAVRES_REGISTER - FIRST_SAVRES_REGISTER + 1)
+
+static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][8];
+
+/* Return the symbol for an out-of-line register save/restore routine.
+   We are saving/restoring GPRs if GPR is true.  */
+
+static rtx
+rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep, bool gpr, bool exitp)
+{
+  int regno = gpr ? info->first_gp_reg_save : (info->first_fp_reg_save - 32);
+  rtx sym;
+  int select = ((savep ? 1 : 0) << 2
+               | (gpr
+                  /* On the SPE, we never have any FPRs, but we do have
+                     32/64-bit versions of the routines.  */
+                  ? (TARGET_SPE_ABI && info->spe_64bit_regs_used ? 1 : 0)
+                  : 0) << 1
+               | (exitp ? 1: 0));
+
+  /* Don't generate bogus routine names.  */
+  gcc_assert (FIRST_SAVRES_REGISTER <= regno && regno <= LAST_SAVRES_REGISTER);
+
+  sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select];
+
+  if (sym == NULL)
+    {
+      char name[30];
+      const char *action;
+      const char *regkind;
+      const char *exit_suffix;
+
+      action = savep ? "save" : "rest";
+
+      /* SPE has slightly different names for its routines depending on
+        whether we are saving 32-bit or 64-bit registers.  */
+      if (TARGET_SPE_ABI)
+       {
+         /* No floating point saves on the SPE.  */
+         gcc_assert (gpr);
+
+         regkind = info->spe_64bit_regs_used ? "64gpr" : "32gpr";
+       }
+      else
+       regkind = gpr ? "gpr" : "fpr";
+
+      exit_suffix = exitp ? "_x" : "";
+
+      sprintf (name, "_%s%s_%d%s", action, regkind, regno, exit_suffix);
+
+      sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select]
+       = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+    }
+
+  return sym;
+}
+
+/* Emit a sequence of insns, including a stack tie if needed, for
+   resetting the stack pointer.  If SAVRES is true, then don't reset the
+   stack pointer, but move the base of the frame into r11 for use by
+   out-of-line register restore routines.  */
+
+static void
+rs6000_emit_stack_reset (rs6000_stack_t *info,
+                        rtx sp_reg_rtx, rtx frame_reg_rtx,
+                        int sp_offset, bool savres)
+{
+  /* This blockage is needed so that sched doesn't decide to move
+     the sp change before the register restores.  */
+  if (frame_reg_rtx != sp_reg_rtx
+      || (TARGET_SPE_ABI
+         && info->spe_64bit_regs_used != 0
+         && info->first_gp_reg_save != 32))
+    rs6000_emit_stack_tie ();
+  
+  if (frame_reg_rtx != sp_reg_rtx)
+    {
+      rs6000_emit_stack_tie ();
+      if (sp_offset != 0)
+       emit_insn (gen_addsi3 (sp_reg_rtx, frame_reg_rtx,
+                              GEN_INT (sp_offset)));
+      else if (!savres)
+       emit_move_insn (sp_reg_rtx, frame_reg_rtx);
+    }
+  else if (sp_offset != 0)
+    {
+      /* If we are restoring registers out-of-line, we will be using the
+        "exit" variants of the restore routines, which will reset the
+        stack for us.  But we do need to point r11 into the right place
+        for those routines.  */
+      rtx dest_reg = (savres
+                     ? gen_rtx_REG (Pmode, 11)
+                     : sp_reg_rtx);
+
+      emit_insn (TARGET_32BIT
+                ? gen_addsi3 (dest_reg, sp_reg_rtx,
+                              GEN_INT (sp_offset))
+                : gen_adddi3 (dest_reg, sp_reg_rtx,
+                              GEN_INT (sp_offset)));
+    }
+}
+
+/* Construct a parallel rtx describing the effect of a call to an
+   out-of-line register save/restore routine.  */
+
+static rtx
+rs6000_make_savres_rtx (rs6000_stack_t *info,
+                       rtx frame_reg_rtx, int save_area_offset,
+                       enum machine_mode reg_mode,
+                       bool savep, bool gpr, bool exitp)
+{
+  int i;
+  int offset, start_reg, end_reg, n_regs;
+  int reg_size = GET_MODE_SIZE (reg_mode);
+  rtx sym;
+  rtvec p;
+
+  offset = 0;
+  start_reg = (gpr
+              ? info->first_gp_reg_save
+              : info->first_fp_reg_save);
+  end_reg = gpr ? 32 : 64;
+  n_regs = end_reg - start_reg;
+  p = rtvec_alloc ((exitp ? 4 : 3) + n_regs);
+
+  /* If we're saving registers, then we should never say we're exiting.         */
+  gcc_assert ((savep && !exitp) || !savep);
+
+  if (exitp)
+    RTVEC_ELT (p, offset++) = gen_rtx_RETURN (VOIDmode);
+
+  RTVEC_ELT (p, offset++)
+    = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 65));
+
+  sym = rs6000_savres_routine_sym (info, savep, gpr, exitp);
+  RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, sym);
+  RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 11));
+
+  for (i = 0; i < end_reg - start_reg; i++)
+    {
+      rtx addr, reg, mem;
+      reg = gen_rtx_REG (reg_mode, start_reg + i);
+      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                          GEN_INT (save_area_offset + reg_size*i));
+      mem = gen_frame_mem (reg_mode, addr);
+
+      RTVEC_ELT (p, i + offset) = gen_rtx_SET (VOIDmode,
+                                              savep ? mem : reg,
+                                              savep ? reg : mem);
+    }
+
+  return gen_rtx_PARALLEL (VOIDmode, p);
+}
+
 /* Determine whether the gp REG is really used.  */
 
 static bool
@@ -15259,6 +16023,85 @@ rs6000_reg_live_or_pic_offset_p (int reg)
                   || (DEFAULT_ABI == ABI_DARWIN && flag_pic))));
 }
 
+enum {
+  SAVRES_MULTIPLE = 0x1,
+  SAVRES_INLINE_FPRS = 0x2,
+  SAVRES_INLINE_GPRS = 0x4
+};
+
+/* Determine the strategy for savings/restoring registers.  */
+
+static int
+rs6000_savres_strategy (rs6000_stack_t *info, bool savep,
+                       int using_static_chain_p, int sibcall)
+{
+  bool using_multiple_p;
+  bool common;
+  bool savres_fprs_inline;
+  bool savres_gprs_inline;
+  bool noclobber_global_gprs
+    = no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true);
+
+  using_multiple_p = (TARGET_MULTIPLE && ! TARGET_POWERPC64
+                     && (!TARGET_SPE_ABI
+                         || info->spe_64bit_regs_used == 0)
+                     && info->first_gp_reg_save < 31
+                     && noclobber_global_gprs);
+  /* Don't bother to try to save things out-of-line if r11 is occupied
+     by the static chain.  It would require too much fiddling and the
+     static chain is rarely used anyway.  */
+  common = (using_static_chain_p
+           || sibcall
+           || crtl->calls_eh_return
+           || !info->lr_save_p
+           || cfun->machine->ra_need_lr
+           || info->total_size > 32767);
+  savres_fprs_inline = (common
+                       || info->first_fp_reg_save == 64
+                       || !no_global_regs_above (info->first_fp_reg_save,
+                                                 /*gpr=*/false)
+                       || FP_SAVE_INLINE (info->first_fp_reg_save));
+  savres_gprs_inline = (common
+                       /* Saving CR interferes with the exit routines
+                          used on the SPE, so just punt here.  */
+                       || (!savep
+                           && TARGET_SPE_ABI
+                           && info->spe_64bit_regs_used != 0
+                           && info->cr_save_p != 0)
+                       || info->first_gp_reg_save == 32
+                       || !noclobber_global_gprs
+                       || GP_SAVE_INLINE (info->first_gp_reg_save));
+
+  if (savep)
+    /* If we are going to use store multiple, then don't even bother
+     with the out-of-line routines, since the store-multiple instruction
+     will always be smaller.  */
+    savres_gprs_inline = savres_gprs_inline || using_multiple_p;
+  else
+    {
+      /* The situation is more complicated with load multiple.  We'd
+         prefer to use the out-of-line routines for restores, since the
+         "exit" out-of-line routines can handle the restore of LR and
+         the frame teardown.  But we can only use the out-of-line
+         routines if we know that we've used store multiple or
+         out-of-line routines in the prologue, i.e. if we've saved all
+         the registers from first_gp_reg_save.  Otherwise, we risk
+         loading garbage from the stack.  Furthermore, we can only use
+         the "exit" out-of-line gpr restore if we haven't saved any
+         fprs.  */
+      bool saved_all = !savres_gprs_inline || using_multiple_p;
+
+      if (saved_all && info->first_fp_reg_save != 64)
+       /* We can't use the exit routine; use load multiple if it's
+          available.  */
+       savres_gprs_inline = savres_gprs_inline || using_multiple_p;
+    }
+
+  return (using_multiple_p
+         | (savres_fprs_inline << 1)
+         | (savres_gprs_inline << 2));
+}
+
 /* Emit function prologue as insns.  */
 
 void
@@ -15272,8 +16115,13 @@ rs6000_emit_prologue (void)
   rtx frame_reg_rtx = sp_reg_rtx;
   rtx cr_save_rtx = NULL_RTX;
   rtx insn;
+  int strategy;
   int saving_FPRs_inline;
+  int saving_GPRs_inline;
   int using_store_multiple;
+  int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
+                              && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
+                              && !call_used_regs[STATIC_CHAIN_REGNUM]);
   HOST_WIDE_INT sp_offset = 0;
 
   if (TARGET_FIX_AND_CONTINUE)
@@ -15296,33 +16144,37 @@ rs6000_emit_prologue (void)
       reg_size = 8;
     }
 
-  using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
-                         && (!TARGET_SPE_ABI
-                             || info->spe_64bit_regs_used == 0)
-                         && info->first_gp_reg_save < 31
-                         && no_global_regs_above (info->first_gp_reg_save));
-  saving_FPRs_inline = (info->first_fp_reg_save == 64
-                       || FP_SAVE_INLINE (info->first_fp_reg_save)
-                       || current_function_calls_eh_return
-                       || cfun->machine->ra_need_lr);
+  strategy = rs6000_savres_strategy (info, /*savep=*/true,
+                                    /*static_chain_p=*/using_static_chain_p,
+                                    /*sibcall=*/0);
+  using_store_multiple = strategy & SAVRES_MULTIPLE;
+  saving_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
+  saving_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
 
   /* For V.4, update stack before we do any saving and set back pointer.  */
   if (! WORLD_SAVE_P (info)
       && info->push_p
       && (DEFAULT_ABI == ABI_V4
-         || current_function_calls_eh_return))
+         || crtl->calls_eh_return))
     {
+      bool need_r11 = (TARGET_SPE
+                      ? (!saving_GPRs_inline
+                         && info->spe_64bit_regs_used == 0)
+                      : (!saving_FPRs_inline || !saving_GPRs_inline));
       if (info->total_size < 32767)
        sp_offset = info->total_size;
       else
-       frame_reg_rtx = frame_ptr_rtx;
+       frame_reg_rtx = (need_r11
+                        ? gen_rtx_REG (Pmode, 11)
+                        : frame_ptr_rtx);
       rs6000_emit_allocate_stack (info->total_size,
                                  (frame_reg_rtx != sp_reg_rtx
                                   && (info->cr_save_p
                                       || info->lr_save_p
                                       || info->first_fp_reg_save < 64
                                       || info->first_gp_reg_save < 32
-                                      )));
+                                      )),
+                                 need_r11);
       if (frame_reg_rtx != sp_reg_rtx)
        rs6000_emit_stack_tie ();
     }
@@ -15353,7 +16205,7 @@ rs6000_emit_prologue (void)
                  && info->cr_save_offset == 4
                  && info->push_p
                  && info->lr_save_p
-                 && (!current_function_calls_eh_return
+                 && (!crtl->calls_eh_return
                       || info->ehrd_offset == -432)
                  && info->vrsave_save_offset == -224
                  && info->altivec_save_offset == -416);
@@ -15381,11 +16233,14 @@ rs6000_emit_prologue (void)
         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 reg = gen_rtx_REG (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                                  ? DFmode : SFmode), 
+                                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_frame_mem (DFmode, addr);
+         rtx mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                                    ? DFmode : SFmode), addr);
 
          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
        }
@@ -15492,47 +16347,156 @@ rs6000_emit_prologue (void)
       for (i = 0; i < 64 - info->first_fp_reg_save; i++)
        if ((df_regs_ever_live_p (info->first_fp_reg_save+i)
             && ! call_used_regs[info->first_fp_reg_save+i]))
-         emit_frame_save (frame_reg_rtx, frame_ptr_rtx, DFmode,
+         emit_frame_save (frame_reg_rtx, frame_ptr_rtx, 
+                          (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                           ? DFmode : SFmode,
                           info->first_fp_reg_save + i,
                           info->fp_save_offset + sp_offset + 8 * i,
                           info->total_size);
     }
   else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64)
     {
+      rtx par;
+
+      par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+                                   info->fp_save_offset + sp_offset,
+                                   DFmode,
+                                   /*savep=*/true, /*gpr=*/false,
+                                   /*exitp=*/false);
+      insn = emit_insn (par);
+      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+                           NULL_RTX, NULL_RTX);
+    }
+
+  /* Save GPRs.  This is done as a PARALLEL if we are using
+     the store-multiple instructions.  */
+  if (!WORLD_SAVE_P (info)
+      && TARGET_SPE_ABI
+      && info->spe_64bit_regs_used != 0
+      && info->first_gp_reg_save != 32)
+    {
       int i;
-      char rname[30];
-      const char *alloc_rname;
-      rtvec p;
-      p = rtvec_alloc (2 + 64 - info->first_fp_reg_save);
+      rtx spe_save_area_ptr;
+      /* Determine whether we can address all of the registers that need
+        to be saved with an offset from the stack pointer that fits in
+        the small const field for SPE memory instructions.  */
+      int spe_regs_addressable_via_sp
+       = (SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
+                              + (32 - info->first_gp_reg_save - 1) * reg_size)
+          && saving_GPRs_inline);
+      int spe_offset;
+      if (spe_regs_addressable_via_sp)
+       {
+         spe_save_area_ptr = frame_reg_rtx;
+         spe_offset = info->spe_gp_save_offset + sp_offset;
+       }
+      else
+       {
+         /* Make r11 point to the start of the SPE save area.  We need
+            to be careful here if r11 is holding the static chain.  If
+            it is, then temporarily save it in r0.  We would use r0 as
+            our base register here, but using r0 as a base register in
+            loads and stores means something different from what we
+            would like.  */
+         int ool_adjust = (saving_GPRs_inline
+                           ? 0
+                           : (info->first_gp_reg_save
+                              - (FIRST_SAVRES_REGISTER+1))*8);
+         HOST_WIDE_INT offset = (info->spe_gp_save_offset
+                                 + sp_offset - ool_adjust);
+
+         if (using_static_chain_p)
+           {
+             rtx r0 = gen_rtx_REG (Pmode, 0);
+             gcc_assert (info->first_gp_reg_save > 11);
+             emit_move_insn (r0, gen_rtx_REG (Pmode, 11));
+           }
+         spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
+         insn = emit_insn (gen_addsi3 (spe_save_area_ptr,
+                                       frame_reg_rtx,
+                                       GEN_INT (offset)));
+         /* We need to make sure the move to r11 gets noted for
+            properly outputting unwind information.  */
+         if (!saving_GPRs_inline)
+           rs6000_frame_related (insn, frame_reg_rtx, offset,
+                                 NULL_RTX, NULL_RTX);
+         spe_offset = 0;
+       }
+      if (saving_GPRs_inline)
+       {
+         for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+           if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+             {
+               rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+               rtx offset, addr, mem;
 
-      RTVEC_ELT (p, 0) = gen_rtx_CLOBBER (VOIDmode,
-                                         gen_rtx_REG (Pmode,
-                                                      LR_REGNO));
-      sprintf (rname, "%s%d%s", SAVE_FP_PREFIX,
-              info->first_fp_reg_save - 32, SAVE_FP_SUFFIX);
-      alloc_rname = ggc_strdup (rname);
-      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode,
-                                     gen_rtx_SYMBOL_REF (Pmode,
-                                                         alloc_rname));
-      for (i = 0; i < 64 - info->first_fp_reg_save; i++)
+               /* We're doing all this to ensure that the offset fits into
+                  the immediate offset of 'evstdd'.  */
+               gcc_assert (SPE_CONST_OFFSET_OK (reg_size * i + spe_offset));
+               offset = GEN_INT (reg_size * i + spe_offset);
+               addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
+               mem = gen_rtx_MEM (V2SImode, addr);
+  
+               insn = emit_move_insn (mem, reg);
+          
+               rs6000_frame_related (insn, spe_save_area_ptr,
+                                     info->spe_gp_save_offset
+                                     + sp_offset + reg_size * i,
+                                     offset, const0_rtx);
+             }
+       }
+      else
        {
-         rtx addr, reg, mem;
-         reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
-         addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->fp_save_offset
-                                       + sp_offset + 8*i));
-         mem = gen_frame_mem (DFmode, addr);
+         rtx par;
 
-         RTVEC_ELT (p, i + 2) = gen_rtx_SET (VOIDmode, mem, reg);
+         par = rs6000_make_savres_rtx (info, gen_rtx_REG (Pmode, 11),
+                                       0, reg_mode,
+                                       /*savep=*/true, /*gpr=*/true,
+                                       /*exitp=*/false);
+         insn = emit_insn (par);
+         rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+                               NULL_RTX, NULL_RTX);
        }
-      insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+                                       
+      /* Move the static chain pointer back.  */
+      if (using_static_chain_p && !spe_regs_addressable_via_sp)
+       emit_move_insn (gen_rtx_REG (Pmode, 11), gen_rtx_REG (Pmode, 0));
+    }
+  else if (!WORLD_SAVE_P (info) && !saving_GPRs_inline)
+    {
+      rtx par;
+
+      /* Need to adjust r11 if we saved any FPRs.  */
+      if (info->first_fp_reg_save != 64)
+        {
+          rtx r11 = gen_rtx_REG (reg_mode, 11);
+          rtx offset = GEN_INT (info->total_size
+                                + (-8 * (64-info->first_fp_reg_save)));
+          rtx ptr_reg = (sp_reg_rtx == frame_reg_rtx
+                         ? sp_reg_rtx : r11);
+
+          emit_insn (TARGET_32BIT
+                     ? gen_addsi3 (r11, ptr_reg, offset)
+                     : gen_adddi3 (r11, ptr_reg, offset));
+        }
+
+      par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+                                   info->gp_save_offset + sp_offset,
+                                   reg_mode,
+                                   /*savep=*/true, /*gpr=*/true,
+                                   /*exitp=*/false);
+      insn = emit_insn (par);
       rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
                            NULL_RTX, NULL_RTX);
     }
-
-  /* Save GPRs.  This is done as a PARALLEL if we are using
-     the store-multiple instructions.  */
-  if (!WORLD_SAVE_P (info) && using_store_multiple)
+  else if (!WORLD_SAVE_P (info) && using_store_multiple)
     {
       rtvec p;
       int i;
@@ -15553,80 +16517,6 @@ rs6000_emit_prologue (void)
       rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
                            NULL_RTX, NULL_RTX);
     }
-   else if (!WORLD_SAVE_P (info)
-            && TARGET_SPE_ABI
-            && info->spe_64bit_regs_used != 0
-            && info->first_gp_reg_save != 32)
-     {
-       int i;
-       rtx spe_save_area_ptr;
-       int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
-                                   && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
-                                   && !call_used_regs[STATIC_CHAIN_REGNUM]);
-       /* Determine whether we can address all of the registers that need
-          to be saved with an offset from the stack pointer that fits in
-          the small const field for SPE memory instructions.  */
-       int spe_regs_addressable_via_sp
-         = SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
-                               + (32 - info->first_gp_reg_save - 1) * reg_size);
-       int spe_offset;
-       if (spe_regs_addressable_via_sp)
-         {
-           spe_save_area_ptr = sp_reg_rtx;
-           spe_offset = info->spe_gp_save_offset + sp_offset;
-         }
-       else
-         {
-           /* Make r11 point to the start of the SPE save area.  We need
-              to be careful here if r11 is holding the static chain.  If
-              it is, then temporarily save it in r0.  We would use r0 as
-              our base register here, but using r0 as a base register in
-              loads and stores means something different from what we
-              would like.  */
-           if (using_static_chain_p)
-             {
-               rtx r0 = gen_rtx_REG (Pmode, 0);
-               gcc_assert (info->first_gp_reg_save > 11);
-               emit_move_insn (r0, gen_rtx_REG (Pmode, 11));
-             }
-           spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
-           emit_insn (gen_addsi3 (spe_save_area_ptr, sp_reg_rtx,
-                                  GEN_INT (info->spe_gp_save_offset + sp_offset)));
-           spe_offset = 0;
-         }
-       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-         if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
-           {
-             rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-             rtx offset, addr, mem;
-             /* We're doing all this to ensure that the offset fits into
-                the immediate offset of 'evstdd'.  */
-             gcc_assert (SPE_CONST_OFFSET_OK (reg_size * i + spe_offset));
-             offset = GEN_INT (reg_size * i + spe_offset);
-             addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
-             mem = gen_rtx_MEM (V2SImode, addr);
-             insn = emit_move_insn (mem, reg);
-           
-             rs6000_frame_related (insn, spe_save_area_ptr,
-                                   info->spe_gp_save_offset
-                                   + sp_offset + reg_size * i,
-                                   offset, const0_rtx);
-           }
-       /* Move the static chain pointer back.  */
-       if (using_static_chain_p && !spe_regs_addressable_via_sp)
-         emit_move_insn (gen_rtx_REG (Pmode, 11), gen_rtx_REG (Pmode, 0));
-     }
   else if (!WORLD_SAVE_P (info))
     {
       int i;
@@ -15650,7 +16540,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 (crtl->calls_eh_return)
     {
       unsigned int i, regno;
 
@@ -15716,7 +16606,7 @@ rs6000_emit_prologue (void)
   /* Update stack and set back pointer unless this is V.4,
      for which it was done previously.  */
   if (!WORLD_SAVE_P (info) && info->push_p
-      && !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return))
+      && !(DEFAULT_ABI == ABI_V4 || crtl->calls_eh_return))
     {
       if (info->total_size < 32767)
       sp_offset = info->total_size;
@@ -15726,7 +16616,8 @@ rs6000_emit_prologue (void)
                                  (frame_reg_rtx != sp_reg_rtx
                                   && ((info->altivec_size != 0)
                                       || (info->vrsave_mask != 0)
-                                      )));
+                                      )),
+                                 FALSE);
       if (frame_reg_rtx != sp_reg_rtx)
        rs6000_emit_stack_tie ();
     }
@@ -15844,10 +16735,10 @@ rs6000_emit_prologue (void)
 
 #if TARGET_MACHO
   if (DEFAULT_ABI == ABI_DARWIN
-      && flag_pic && current_function_uses_pic_offset_table)
+      && flag_pic && crtl->uses_pic_offset_table)
     {
       rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
-      rtx src = machopic_function_base_sym ();
+      rtx src = gen_rtx_SYMBOL_REF (Pmode, MACHOPIC_FUNCTION_BASE_NAME);
 
       /* Save and restore LR locally around this call (in R0).  */
       if (!info->lr_save_p)
@@ -15882,8 +16773,7 @@ rs6000_output_function_prologue (FILE *file,
       && !FP_SAVE_INLINE (info->first_fp_reg_save))
     fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n",
             SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX,
-            RESTORE_FP_PREFIX, info->first_fp_reg_save - 32,
-            RESTORE_FP_SUFFIX);
+            RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
 
   /* Write .extern for AIX common mode routines, if needed.  */
   if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
@@ -15913,18 +16803,70 @@ rs6000_output_function_prologue (FILE *file,
        unsigned addr = 0;
        for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
          {
-           INSN_ADDRESSES_NEW (insn, addr);
-           addr += 4;
+           INSN_ADDRESSES_NEW (insn, addr);
+           addr += 4;
+         }
+      }
+
+      if (TARGET_DEBUG_STACK)
+       debug_rtx_list (get_insns (), 100);
+      final (get_insns (), file, FALSE);
+      end_sequence ();
+    }
+
+  rs6000_pic_labelno++;
+}
+
+/* Non-zero if vmx regs are restored before the frame pop, zero if
+   we restore after the pop when possible.  */
+#define ALWAYS_RESTORE_ALTIVEC_BEFORE_POP 0
+
+/* Reload CR from REG.  */
+
+static void
+rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
+{
+  int count = 0;
+  int i;
+
+  if (using_mfcr_multiple)
+    {
+      for (i = 0; i < 8; i++)
+       if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+         count++;
+      gcc_assert (count);
+    }
+
+  if (using_mfcr_multiple && count > 1)
+    {
+      rtvec p;
+      int ndx;
+
+      p = rtvec_alloc (count);
+
+      ndx = 0;
+      for (i = 0; i < 8; i++)
+       if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+         {
+           rtvec r = rtvec_alloc (2);
+           RTVEC_ELT (r, 0) = reg;
+           RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i));
+           RTVEC_ELT (p, ndx) =
+             gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i),
+                          gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR));
+           ndx++;
          }
-      }
-
-      if (TARGET_DEBUG_STACK)
-       debug_rtx_list (get_insns (), 100);
-      final (get_insns (), file, FALSE);
-      end_sequence ();
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+      gcc_assert (ndx == count);
     }
-
-  rs6000_pic_labelno++;
+  else
+    for (i = 0; i < 8; i++)
+      if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+       {
+         emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode,
+                                                      CR0_REGNO+i),
+                                         reg));
+       }
 }
 
 /* Emit function epilogue as insns.
@@ -15938,10 +16880,13 @@ void
 rs6000_emit_epilogue (int sibcall)
 {
   rs6000_stack_t *info;
+  int restoring_GPRs_inline;
   int restoring_FPRs_inline;
   int using_load_multiple;
   int using_mtcr_multiple;
   int use_backchain_to_restore_sp;
+  int restore_lr;
+  int strategy;
   int sp_offset = 0;
   rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1);
   rtx frame_reg_rtx = sp_reg_rtx;
@@ -15957,22 +16902,29 @@ rs6000_emit_epilogue (int sibcall)
       reg_size = 8;
     }
 
-  using_load_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
-                        && (!TARGET_SPE_ABI
-                            || info->spe_64bit_regs_used == 0)
-                        && info->first_gp_reg_save < 31
-                        && no_global_regs_above (info->first_gp_reg_save));
-  restoring_FPRs_inline = (sibcall
-                          || current_function_calls_eh_return
-                          || info->first_fp_reg_save == 64
-                          || FP_SAVE_INLINE (info->first_fp_reg_save));
-  use_backchain_to_restore_sp = (frame_pointer_needed
-                                || current_function_calls_alloca
-                                || info->total_size > 32767);
+  strategy = rs6000_savres_strategy (info, /*savep=*/false,
+                                    /*static_chain_p=*/0, sibcall);
+  using_load_multiple = strategy & SAVRES_MULTIPLE;
+  restoring_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
+  restoring_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
   using_mtcr_multiple = (rs6000_cpu == PROCESSOR_PPC601
                         || rs6000_cpu == PROCESSOR_PPC603
                         || rs6000_cpu == PROCESSOR_PPC750
                         || optimize_size);
+  /* Restore via the backchain when we have a large frame, since this
+     is more efficient than an addis, addi pair.  The second condition
+     here will not trigger at the moment;  We don't actually need a
+     frame pointer for alloca, but the generic parts of the compiler
+     give us one anyway.  */
+  use_backchain_to_restore_sp = (info->total_size > 32767
+                                || info->total_size
+                                    + (info->lr_save_p ? info->lr_save_offset : 0)
+                                      > 32767
+                                || (cfun->calls_alloca
+                                    && !frame_pointer_needed));
+  restore_lr = (info->lr_save_p
+               && restoring_GPRs_inline
+               && restoring_FPRs_inline);
 
   if (WORLD_SAVE_P (info))
     {
@@ -15994,7 +16946,7 @@ rs6000_emit_epilogue (int sibcall)
                       + LAST_ALTIVEC_REGNO + 1 - info->first_altivec_reg_save
                       + 63 + 1 - info->first_fp_reg_save);
 
-      strcpy (rname, ((current_function_calls_eh_return) ?
+      strcpy (rname, ((crtl->calls_eh_return) ?
                      "*eh_rest_world_r10" : "*rest_world"));
       alloc_rname = ggc_strdup (rname);
 
@@ -16042,11 +16994,14 @@ rs6000_emit_epilogue (int sibcall)
        }
       for (i = 0; info->first_fp_reg_save + i <= 63; i++)
        {
-         rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
+         rtx reg = gen_rtx_REG (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                                  ? DFmode : SFmode), 
+                                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_frame_mem (DFmode, addr);
+         rtx mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                                    ? DFmode : SFmode), addr);
 
          RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
        }
@@ -16069,11 +17024,26 @@ rs6000_emit_epilogue (int sibcall)
   if (info->push_p)
     sp_offset = info->total_size;
 
-  /* Restore AltiVec registers if needed.  */
-  if (TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+  /* Restore AltiVec registers if we must do so before adjusting the
+     stack.  */
+  if (TARGET_ALTIVEC_ABI
+      && info->altivec_size != 0
+      && (ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
+         || (DEFAULT_ABI != ABI_V4
+             && info->altivec_save_offset < (TARGET_32BIT ? -220 : -288))))
     {
       int i;
 
+      if (use_backchain_to_restore_sp)
+       {
+         frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+         emit_move_insn (frame_reg_rtx,
+                         gen_rtx_MEM (Pmode, sp_reg_rtx));
+         sp_offset = 0;
+       }
+      else if (frame_pointer_needed)
+       frame_reg_rtx = hard_frame_pointer_rtx;
+
       for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
        if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
          {
@@ -16093,23 +17063,81 @@ rs6000_emit_epilogue (int sibcall)
          }
     }
 
-  /* 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.  */
+  /* Restore VRSAVE if we must do so before adjusting the stack.  */
+  if (TARGET_ALTIVEC
+      && TARGET_ALTIVEC_VRSAVE
+      && info->vrsave_mask != 0
+      && (ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
+         || (DEFAULT_ABI != ABI_V4
+             && info->vrsave_save_offset < (TARGET_32BIT ? -220 : -288))))
+    {
+      rtx addr, mem, reg;
+
+      if (frame_reg_rtx == sp_reg_rtx)
+       {
+         if (use_backchain_to_restore_sp)
+           {
+             frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+             emit_move_insn (frame_reg_rtx,
+                             gen_rtx_MEM (Pmode, sp_reg_rtx));
+             sp_offset = 0;
+           }
+         else if (frame_pointer_needed)
+           frame_reg_rtx = hard_frame_pointer_rtx;
+       }
+
+      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+                          GEN_INT (info->vrsave_save_offset + sp_offset));
+      mem = gen_frame_mem (SImode, addr);
+      reg = gen_rtx_REG (SImode, 12);
+      emit_move_insn (reg, mem);
+
+      emit_insn (generate_set_vrsave (reg, info, 1));
+    }
+
+  /* If we have a large stack frame, restore the old stack pointer
+     using the backchain.  */
   if (use_backchain_to_restore_sp)
     {
-      /* Under V.4, don't reset the stack pointer until after we're done
-        loading the saved registers.  */
+      if (frame_reg_rtx == sp_reg_rtx)
+       {
+         /* Under V.4, don't reset the stack pointer until after we're done
+            loading the saved registers.  */
+         if (DEFAULT_ABI == ABI_V4)
+           frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+
+         emit_move_insn (frame_reg_rtx,
+                         gen_rtx_MEM (Pmode, sp_reg_rtx));
+         sp_offset = 0;
+       }
+      else if (ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
+              && DEFAULT_ABI == ABI_V4)
+       /* frame_reg_rtx has been set up by the altivec restore.  */
+       ;
+      else
+       {
+         emit_move_insn (sp_reg_rtx, frame_reg_rtx);
+         frame_reg_rtx = sp_reg_rtx;
+       }
+    }
+  /* If we have a frame pointer, we can restore the old stack pointer
+     from it.  */
+  else if (frame_pointer_needed)
+    {
+      frame_reg_rtx = sp_reg_rtx;
       if (DEFAULT_ABI == ABI_V4)
        frame_reg_rtx = gen_rtx_REG (Pmode, 11);
 
-      emit_move_insn (frame_reg_rtx,
-                     gen_rtx_MEM (Pmode, sp_reg_rtx));
+      emit_insn (TARGET_32BIT
+                ? gen_addsi3 (frame_reg_rtx, hard_frame_pointer_rtx,
+                              GEN_INT (info->total_size))
+                : gen_adddi3 (frame_reg_rtx, hard_frame_pointer_rtx,
+                              GEN_INT (info->total_size)));
       sp_offset = 0;
     }
   else if (info->push_p
           && DEFAULT_ABI != ABI_V4
-          && !current_function_calls_eh_return)
+          && !crtl->calls_eh_return)
     {
       emit_insn (TARGET_32BIT
                 ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
@@ -16119,9 +17147,41 @@ rs6000_emit_epilogue (int sibcall)
       sp_offset = 0;
     }
 
-  /* Restore VRSAVE if needed.  */
-  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
-      && info->vrsave_mask != 0)
+  /* Restore AltiVec registers if we have not done so already.  */
+  if (!ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
+      && TARGET_ALTIVEC_ABI
+      && info->altivec_size != 0
+      && (DEFAULT_ABI == ABI_V4
+         || info->altivec_save_offset >= (TARGET_32BIT ? -220 : -288)))
+    {
+      int i;
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+       if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+         {
+           rtx addr, areg, mem;
+
+           areg = gen_rtx_REG (Pmode, 0);
+           emit_move_insn
+             (areg, GEN_INT (info->altivec_save_offset
+                             + sp_offset
+                             + 16 * (i - info->first_altivec_reg_save)));
+
+           /* AltiVec addressing mode is [reg+reg].  */
+           addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+           mem = gen_frame_mem (V4SImode, addr);
+
+           emit_move_insn (gen_rtx_REG (V4SImode, i), mem);
+         }
+    }
+
+  /* Restore VRSAVE if we have not done so already.  */
+  if (!ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
+      && TARGET_ALTIVEC
+      && TARGET_ALTIVEC_VRSAVE
+      && info->vrsave_mask != 0
+      && (DEFAULT_ABI == ABI_V4
+         || info->vrsave_save_offset >= (TARGET_32BIT ? -220 : -288)))
     {
       rtx addr, mem, reg;
 
@@ -16134,8 +17194,9 @@ rs6000_emit_epilogue (int sibcall)
       emit_insn (generate_set_vrsave (reg, info, 1));
     }
 
-  /* Get the old lr if we saved it.  */
-  if (info->lr_save_p)
+  /* Get the old lr if we saved it.  If we are restoring registers
+     out-of-line, then the out-of-line routines can do this for us.  */
+  if (restore_lr)
     {
       rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx,
                                      info->lr_save_offset + sp_offset);
@@ -16154,12 +17215,12 @@ rs6000_emit_epilogue (int sibcall)
     }
 
   /* Set LR here to try to overlap restores below.  */
-  if (info->lr_save_p)
+  if (restore_lr)
     emit_move_insn (gen_rtx_REG (Pmode, LR_REGNO),
                    gen_rtx_REG (Pmode, 0));
 
   /* Load exception handler data registers, if needed.  */
-  if (current_function_calls_eh_return)
+  if (crtl->calls_eh_return)
     {
       unsigned int i, regno;
 
@@ -16190,35 +17251,17 @@ rs6000_emit_epilogue (int sibcall)
 
   /* Restore GPRs.  This is done as a PARALLEL if we are using
      the load-multiple instructions.  */
-  if (using_load_multiple)
-    {
-      rtvec p;
-      p = rtvec_alloc (32 - info->first_gp_reg_save);
-      for (i = 0; i < 32 - 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_frame_mem (reg_mode, addr);
-
-         RTVEC_ELT (p, i) =
-           gen_rtx_SET (VOIDmode,
-                        gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
-                        mem);
-       }
-      emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
-    }
-  else if (TARGET_SPE_ABI
-           && info->spe_64bit_regs_used != 0
-           && info->first_gp_reg_save != 32)
+  if (TARGET_SPE_ABI
+      && info->spe_64bit_regs_used != 0
+      && info->first_gp_reg_save != 32)
     {
       /* Determine whether we can address all of the registers that need
          to be saved with an offset from the stack pointer that fits in
          the small const field for SPE memory instructions.  */
       int spe_regs_addressable_via_sp
-        = SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
-                              + (32 - info->first_gp_reg_save - 1) * reg_size);
+       = (SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
+                              + (32 - info->first_gp_reg_save - 1) * reg_size)
+          && restoring_GPRs_inline);
       int spe_offset;
 
       if (spe_regs_addressable_via_sp)
@@ -16230,10 +17273,17 @@ rs6000_emit_epilogue (int sibcall)
              not clobbering it when we were saving registers in the prologue.
              There's no need to worry here because the static chain is passed
              anew to every function.  */
+         int ool_adjust = (restoring_GPRs_inline
+                           ? 0
+                           : (info->first_gp_reg_save
+                              - (FIRST_SAVRES_REGISTER+1))*8);
+
          if (frame_reg_rtx == sp_reg_rtx)
            frame_reg_rtx = gen_rtx_REG (Pmode, 11);
           emit_insn (gen_addsi3 (frame_reg_rtx, old_frame_reg_rtx,
-                                 GEN_INT (info->spe_gp_save_offset + sp_offset)));
+                                GEN_INT (info->spe_gp_save_offset
+                                         + sp_offset
+                                         - ool_adjust)));
          /* Keep the invariant that frame_reg_rtx + sp_offset points
             at the top of the stack frame.  */
          sp_offset = -info->spe_gp_save_offset;
@@ -16241,26 +17291,80 @@ rs6000_emit_epilogue (int sibcall)
           spe_offset = 0;
         }
 
-      for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-        if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
-          {
-            rtx offset, addr, mem;
+      if (restoring_GPRs_inline)
+       {
+         for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+           if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+             {
+               rtx offset, addr, mem;
 
-            /* We're doing all this to ensure that the immediate offset
-               fits into the immediate field of 'evldd'.  */
-            gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
+               /* We're doing all this to ensure that the immediate offset
+                  fits into the immediate field of 'evldd'.  */
+               gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
 
-            offset = GEN_INT (spe_offset + reg_size * i);
-            addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, offset);
-            mem = gen_rtx_MEM (V2SImode, addr);
+               offset = GEN_INT (spe_offset + reg_size * i);
+               addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, offset);
+               mem = gen_rtx_MEM (V2SImode, addr);
 
-            emit_move_insn (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
-                            mem);
-          }
+               emit_move_insn (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                               mem);
+             }
+       }
+      else
+       {
+         rtx par;
+
+         par = rs6000_make_savres_rtx (info, gen_rtx_REG (Pmode, 11),
+                                       0, reg_mode,
+                                       /*savep=*/false, /*gpr=*/true,
+                                       /*exitp=*/true);
+         emit_jump_insn (par);
+
+         /* We don't want anybody else emitting things after we jumped
+            back.  */
+         return;
+       }
     }
-  else
-    for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-      if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+  else if (!restoring_GPRs_inline)
+    {
+      /* We are jumping to an out-of-line function.  */
+      bool can_use_exit = info->first_fp_reg_save == 64;
+      rtx par;
+
+      /* Emit stack reset code if we need it.  */
+      if (can_use_exit)
+       rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
+                                sp_offset, can_use_exit);
+      else
+       emit_insn (gen_addsi3 (gen_rtx_REG (Pmode, 11),
+                              sp_reg_rtx,
+                              GEN_INT (sp_offset - info->fp_size)));
+
+      par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+                                   info->gp_save_offset, reg_mode,
+                                   /*savep=*/false, /*gpr=*/true,
+                                   /*exitp=*/can_use_exit);
+
+      if (can_use_exit)
+       {
+         if (info->cr_save_p)
+           rs6000_restore_saved_cr (gen_rtx_REG (SImode, 12),
+                                    using_mtcr_multiple);
+
+         emit_jump_insn (par);
+
+         /* We don't want anybody else emitting things after we jumped
+            back.  */
+         return;
+       }
+      else
+       emit_insn (par);
+    }
+  else if (using_load_multiple)
+    {
+      rtvec p;
+      p = rtvec_alloc (32 - info->first_gp_reg_save);
+      for (i = 0; i < 32 - info->first_gp_reg_save; i++)
        {
          rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
                                   GEN_INT (info->gp_save_offset
@@ -16268,9 +17372,28 @@ rs6000_emit_epilogue (int sibcall)
                                            + reg_size * i));
          rtx mem = gen_frame_mem (reg_mode, addr);
 
-         emit_move_insn (gen_rtx_REG (reg_mode,
-                                      info->first_gp_reg_save + i), mem);
+         RTVEC_ELT (p, i) =
+           gen_rtx_SET (VOIDmode,
+                        gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                        mem);
        }
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+    }
+  else
+    {
+      for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+        if (rs6000_reg_live_or_pic_offset_p (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_frame_mem (reg_mode, addr);
+
+            emit_move_insn (gen_rtx_REG (reg_mode,
+                                         info->first_gp_reg_save + i), mem);
+          }
+    }
 
   /* Restore fpr's if we need to do it without calling a function.  */
   if (restoring_FPRs_inline)
@@ -16283,80 +17406,26 @@ rs6000_emit_epilogue (int sibcall)
                               GEN_INT (info->fp_save_offset
                                        + sp_offset
                                        + 8 * i));
-         mem = gen_frame_mem (DFmode, addr);
+         mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
+                                ? DFmode : SFmode), addr);
 
-         emit_move_insn (gen_rtx_REG (DFmode,
+         emit_move_insn (gen_rtx_REG (((TARGET_HARD_FLOAT 
+                                        && TARGET_DOUBLE_FLOAT)
+                                       ? DFmode : SFmode),
                                       info->first_fp_reg_save + i),
                          mem);
        }
 
   /* If we saved cr, restore it here.  Just those that were used.  */
   if (info->cr_save_p)
-    {
-      rtx r12_rtx = gen_rtx_REG (SImode, 12);
-      int count = 0;
-
-      if (using_mtcr_multiple)
-       {
-         for (i = 0; i < 8; i++)
-           if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
-             count++;
-         gcc_assert (count);
-       }
-
-      if (using_mtcr_multiple && count > 1)
-       {
-         rtvec p;
-         int ndx;
-
-         p = rtvec_alloc (count);
-
-         ndx = 0;
-         for (i = 0; i < 8; i++)
-           if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
-             {
-               rtvec r = rtvec_alloc (2);
-               RTVEC_ELT (r, 0) = r12_rtx;
-               RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i));
-               RTVEC_ELT (p, ndx) =
-                 gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i),
-                              gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR));
-               ndx++;
-             }
-         emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
-         gcc_assert (ndx == count);
-       }
-      else
-       for (i = 0; i < 8; i++)
-         if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
-           {
-             emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode,
-                                                          CR0_REGNO+i),
-                                             r12_rtx));
-           }
-    }
+    rs6000_restore_saved_cr (gen_rtx_REG (SImode, 12), using_mtcr_multiple);
 
   /* If this is V.4, unwind the stack pointer after all of the loads
      have been done.  */
-  if (frame_reg_rtx != sp_reg_rtx)
-    {
-      /* This blockage is needed so that sched doesn't decide to move
-        the sp change before the register restores.  */
-      rs6000_emit_stack_tie ();
-      if (sp_offset != 0)
-        emit_insn (gen_addsi3 (sp_reg_rtx, frame_reg_rtx,
-                              GEN_INT (sp_offset)));
-      else
-        emit_move_insn (sp_reg_rtx, frame_reg_rtx);
-    }
-  else if (sp_offset != 0)
-    emit_insn (TARGET_32BIT
-              ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
-                            GEN_INT (sp_offset))
-              : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
-                            GEN_INT (sp_offset)));
+  rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
+                          sp_offset, !restoring_FPRs_inline);
 
-  if (current_function_calls_eh_return)
+  if (crtl->calls_eh_return)
     {
       rtx sa = EH_RETURN_STACKADJ_RTX;
       emit_insn (TARGET_32BIT
@@ -16368,30 +17437,30 @@ rs6000_emit_epilogue (int sibcall)
     {
       rtvec p;
       if (! restoring_FPRs_inline)
-       p = rtvec_alloc (3 + 64 - info->first_fp_reg_save);
+       p = rtvec_alloc (4 + 64 - info->first_fp_reg_save);
       else
        p = rtvec_alloc (2);
 
       RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
-      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode,
-                                     gen_rtx_REG (Pmode,
-                                                  LR_REGNO));
+      RTVEC_ELT (p, 1) = (restoring_FPRs_inline
+                         ? gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 65))
+                         : gen_rtx_CLOBBER (VOIDmode,
+                                            gen_rtx_REG (Pmode, 65)));
 
       /* If we have to restore more than two FP registers, branch to the
         restore function.  It will return to our caller.  */
       if (! restoring_FPRs_inline)
        {
          int i;
-         char rname[30];
-         const char *alloc_rname;
-
-         sprintf (rname, "%s%d%s", RESTORE_FP_PREFIX,
-                  info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
-         alloc_rname = ggc_strdup (rname);
-         RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode,
-                                         gen_rtx_SYMBOL_REF (Pmode,
-                                                             alloc_rname));
-
+         rtx sym;
+
+         sym = rs6000_savres_routine_sym (info,
+                                          /*savep=*/false,
+                                          /*gpr=*/false,
+                                          /*exitp=*/true);
+         RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, sym);
+         RTVEC_ELT (p, 3) = gen_rtx_USE (VOIDmode,
+                                         gen_rtx_REG (Pmode, 11));
          for (i = 0; i < 64 - info->first_fp_reg_save; i++)
            {
              rtx addr, mem;
@@ -16399,7 +17468,7 @@ rs6000_emit_epilogue (int sibcall)
                                   GEN_INT (info->fp_save_offset + 8*i));
              mem = gen_frame_mem (DFmode, addr);
 
-             RTVEC_ELT (p, i+3) =
+             RTVEC_ELT (p, i+4) =
                gen_rtx_SET (VOIDmode,
                             gen_rtx_REG (DFmode, info->first_fp_reg_save + i),
                             mem);
@@ -16486,7 +17555,7 @@ rs6000_output_function_epilogue (FILE *file,
      System V.4 Powerpc's (and the embedded ABI derived from it) use a
      different traceback table.  */
   if (DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive
-      && rs6000_traceback != traceback_none && !current_function_is_thunk)
+      && rs6000_traceback != traceback_none && !crtl->is_thunk)
     {
       const char *fname = NULL;
       const char *language_string = lang_hooks.name;
@@ -16539,7 +17608,7 @@ rs6000_output_function_epilogue (FILE *file,
       if (! strcmp (language_string, "GNU C"))
        i = 0;
       else if (! strcmp (language_string, "GNU F77")
-              || ! strcmp (language_string, "GNU F95"))
+              || ! strcmp (language_string, "GNU Fortran"))
        i = 1;
       else if (! strcmp (language_string, "GNU Pascal"))
        i = 2;
@@ -16609,6 +17678,7 @@ rs6000_output_function_epilogue (FILE *file,
                      switch (mode)
                        {
                        case SFmode:
+                       case SDmode:
                          bits = 0x2;
                          break;
 
@@ -16743,7 +17813,7 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                        HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                        tree function)
 {
-  rtx this, insn, funexp;
+  rtx this_rtx, insn, funexp;
 
   reload_completed = 1;
   epilogue_completed = 1;
@@ -16754,17 +17824,17 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   /* Find the "this" pointer.  If the function returns a structure,
      the structure return pointer is in r3.  */
   if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
-    this = gen_rtx_REG (Pmode, 4);
+    this_rtx = gen_rtx_REG (Pmode, 4);
   else
-    this = gen_rtx_REG (Pmode, 3);
+    this_rtx = gen_rtx_REG (Pmode, 3);
 
   /* Apply the constant offset, if required.  */
   if (delta)
     {
       rtx delta_rtx = GEN_INT (delta);
       emit_insn (TARGET_32BIT
-                ? gen_addsi3 (this, this, delta_rtx)
-                : gen_adddi3 (this, this, delta_rtx));
+                ? gen_addsi3 (this_rtx, this_rtx, delta_rtx)
+                : gen_adddi3 (this_rtx, this_rtx, delta_rtx));
     }
 
   /* Apply the offset from the vtable, if required.  */
@@ -16773,7 +17843,7 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       rtx vcall_offset_rtx = GEN_INT (vcall_offset);
       rtx tmp = gen_rtx_REG (Pmode, 12);
 
-      emit_move_insn (tmp, gen_rtx_MEM (Pmode, this));
+      emit_move_insn (tmp, gen_rtx_MEM (Pmode, this_rtx));
       if (((unsigned HOST_WIDE_INT) vcall_offset) + 0x8000 >= 0x10000)
        {
          emit_insn (TARGET_32BIT
@@ -16788,8 +17858,8 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
          emit_move_insn (tmp, gen_rtx_MEM (Pmode, loc));
        }
       emit_insn (TARGET_32BIT
-                ? gen_addsi3 (this, this, tmp)
-                : gen_adddi3 (this, this, tmp));
+                ? gen_addsi3 (this_rtx, this_rtx, tmp)
+                : gen_adddi3 (this_rtx, this_rtx, tmp));
     }
 
   /* Generate a tail call to the target function.  */
@@ -16831,6 +17901,7 @@ rs6000_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   final_start_function (insn, file, 1);
   final (insn, file, 1);
   final_end_function ();
+  free_after_compilation (cfun);
 
   reload_completed = 0;
   epilogue_completed = 0;
@@ -16973,6 +18044,35 @@ toc_hash_eq (const void *h1, const void *h2)
   || strncmp ("_ZTI", name, strlen ("_ZTI")) == 0      \
   || strncmp ("_ZTC", name, strlen ("_ZTC")) == 0)
 
+#ifdef NO_DOLLAR_IN_LABEL
+/* Return a GGC-allocated character string translating dollar signs in
+   input NAME to underscores.  Used by XCOFF ASM_OUTPUT_LABELREF.  */
+
+const char *
+rs6000_xcoff_strip_dollar (const char *name)
+{
+  char *strip, *p;
+  int len;
+
+  p = strchr (name, '$');
+
+  if (p == 0 || p == name)
+    return name;
+
+  len = strlen (name);
+  strip = (char *) alloca (len + 1);
+  strcpy (strip, name);
+  p = strchr (strip, '$');
+  while (p)
+    {
+      *p = '_';
+      p = strchr (p + 1, '$');
+    }
+
+  return ggc_alloc_string (strip, len);
+}
+#endif
+
 void
 rs6000_output_symbol_ref (FILE *file, rtx x)
 {
@@ -17000,7 +18100,6 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
 {
   char buf[256];
   const char *name = buf;
-  const char *real_name;
   rtx base = x;
   HOST_WIDE_INT offset = 0;
 
@@ -17021,7 +18120,7 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
        toc_hash_table = htab_create_ggc (1021, toc_hash_function,
                                          toc_hash_eq, NULL);
 
-      h = ggc_alloc (sizeof (*h));
+      h = GGC_NEW (struct toc_hash_struct);
       h->key = x;
       h->key_mode = mode;
       h->labelno = labelno;
@@ -17274,12 +18373,12 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
       gcc_unreachable ();
     }
 
-  real_name = (*targetm.strip_name_encoding) (name);
   if (TARGET_MINIMAL_TOC)
     fputs (TARGET_32BIT ? "\t.long " : DOUBLE_INT_ASM_OP, file);
   else
     {
-      fprintf (file, "\t.tc %s", real_name);
+      fputs ("\t.tc ", file);
+      RS6000_OUTPUT_BASENAME (file, name);
 
       if (offset < 0)
        fprintf (file, ".N" HOST_WIDE_INT_PRINT_UNSIGNED, - offset);
@@ -17468,13 +18567,13 @@ output_profile_hook (int labelno ATTRIBUTE_UNUSED)
       int caller_addr_regno = LR_REGNO;
 
       /* Be conservative and always set this, at least for now.  */
-      current_function_uses_pic_offset_table = 1;
+      crtl->uses_pic_offset_table = 1;
 
 #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
-         && current_function_uses_pic_offset_table)
+         && crtl->uses_pic_offset_table)
        caller_addr_regno = 0;
 #endif
       emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mcount_name),
@@ -18055,9 +19154,8 @@ adjacent_mem_locations (rtx insn1, rtx insn2)
          || (GET_CODE (XEXP (b, 0)) == PLUS
              && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
     {
-      HOST_WIDE_INT val0 = 0, val1 = 0;
+      HOST_WIDE_INT val0 = 0, val1 = 0, val_diff;
       rtx reg0, reg1;
-      int val_diff;
 
       if (GET_CODE (XEXP (a, 0)) == PLUS)
         {
@@ -18078,8 +19176,8 @@ adjacent_mem_locations (rtx insn1, rtx insn2)
       val_diff = val1 - val0;
 
       return ((REGNO (reg0) == REGNO (reg1))
-             && (val_diff == INTVAL (MEM_SIZE (a))
-                  || val_diff == -INTVAL (MEM_SIZE (b))));
+             && ((MEM_SIZE (a) && val_diff == INTVAL (MEM_SIZE (a)))
+                 || (MEM_SIZE (b) && val_diff == -INTVAL (MEM_SIZE (b)))));
     }
 
   return false;
@@ -18208,6 +19306,9 @@ rs6000_issue_rate (void)
   case CPU_PPC7400:
   case CPU_PPC8540:
   case CPU_CELL:
+  case CPU_PPCE300C2:
+  case CPU_PPCE300C3:
+  case CPU_PPCE500MC:
     return 2;
   case CPU_RIOS2:
   case CPU_PPC604:
@@ -18264,6 +19365,11 @@ is_mem_ref (rtx pat)
   int i, j;
   bool ret = false;
 
+  /* stack_tie does not produce any real memory traffic.  */
+  if (GET_CODE (pat) == UNSPEC
+      && XINT (pat, 1) == UNSPEC_TIE)
+    return false;
+
   if (GET_CODE (pat) == MEM)
     return true;
 
@@ -18572,7 +19678,8 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
                   for (i=pos; i<*pn_ready-1; i++)
                     ready[i] = ready[i + 1];
                   ready[*pn_ready-1] = tmp;
-                  if INSN_PRIORITY_KNOWN (tmp)
+
+                  if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
                     INSN_PRIORITY (tmp)++;
                   break;
                 }
@@ -18589,7 +19696,8 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
           while (pos >= 0)
             {
               if (is_load_insn (ready[pos])
-                  && INSN_PRIORITY_KNOWN (ready[pos]))
+                  && !sel_sched_p ()
+                 && INSN_PRIORITY_KNOWN (ready[pos]))
                 {
                   INSN_PRIORITY (ready[pos])++;
 
@@ -18631,8 +19739,10 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
                       for (i=pos; i<*pn_ready-1; i++)
                         ready[i] = ready[i + 1];
                       ready[*pn_ready-1] = tmp;
-                      if INSN_PRIORITY_KNOWN (tmp)
+
+                      if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
                         INSN_PRIORITY (tmp)++;
+
                       first_store_pos = -1;
 
                       break;
@@ -18651,7 +19761,7 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
               for (i=first_store_pos; i<*pn_ready-1; i++)
                 ready[i] = ready[i + 1];
               ready[*pn_ready-1] = tmp;
-              if INSN_PRIORITY_KNOWN (tmp)
+              if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
                 INSN_PRIORITY (tmp)++;
             }
         }
@@ -18665,7 +19775,8 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
           while (pos >= 0)
             {
               if (is_store_insn (ready[pos])
-                  && INSN_PRIORITY_KNOWN (ready[pos]))
+                  && !sel_sched_p ()
+                 && INSN_PRIORITY_KNOWN (ready[pos]))
                 {
                   INSN_PRIORITY (ready[pos])++;
 
@@ -19073,7 +20184,7 @@ redefine_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail)
 
   /* Initialize.  */
   issue_rate = rs6000_issue_rate ();
-  group_insns = alloca (issue_rate * sizeof (rtx));
+  group_insns = XALLOCAVEC (rtx, issue_rate);
   for (i = 0; i < issue_rate; i++)
     {
       group_insns[i] = 0;
@@ -19167,7 +20278,7 @@ pad_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail)
       if (group_end)
        {
          /* If the scheduler had marked group termination at this location
-            (between insn and next_indn), and neither insn nor next_insn will
+            (between insn and next_insn), and neither insn nor next_insn will
             force group termination, pad the group with nops to force group
             termination.  */
          if (can_issue_more
@@ -19221,6 +20332,10 @@ rs6000_sched_finish (FILE *dump, int sched_verbose)
 
   if (reload_completed && rs6000_sched_groups)
     {
+      /* Do not run sched_finish hook when selective scheduling enabled.  */
+      if (sel_sched_p ())
+       return;
+
       if (rs6000_sched_insert_nops == sched_finish_none)
        return;
 
@@ -19241,6 +20356,67 @@ rs6000_sched_finish (FILE *dump, int sched_verbose)
        }
     }
 }
+
+struct _rs6000_sched_context
+{
+  short cached_can_issue_more;
+  rtx last_scheduled_insn;
+  int load_store_pendulum;
+};
+
+typedef struct _rs6000_sched_context rs6000_sched_context_def;
+typedef rs6000_sched_context_def *rs6000_sched_context_t;
+
+/* Allocate store for new scheduling context.  */
+static void *
+rs6000_alloc_sched_context (void)
+{
+  return xmalloc (sizeof (rs6000_sched_context_def));
+}
+
+/* If CLEAN_P is true then initializes _SC with clean data,
+   and from the global context otherwise.  */
+static void
+rs6000_init_sched_context (void *_sc, bool clean_p)
+{
+  rs6000_sched_context_t sc = (rs6000_sched_context_t) _sc;
+
+  if (clean_p)
+    {
+      sc->cached_can_issue_more = 0;
+      sc->last_scheduled_insn = NULL_RTX;
+      sc->load_store_pendulum = 0;
+    }
+  else
+    {
+      sc->cached_can_issue_more = cached_can_issue_more;
+      sc->last_scheduled_insn = last_scheduled_insn;
+      sc->load_store_pendulum = load_store_pendulum;
+    }
+}
+
+/* Sets the global scheduling context to the one pointed to by _SC.  */
+static void
+rs6000_set_sched_context (void *_sc)
+{
+  rs6000_sched_context_t sc = (rs6000_sched_context_t) _sc;
+
+  gcc_assert (sc != NULL);
+
+  cached_can_issue_more = sc->cached_can_issue_more;
+  last_scheduled_insn = sc->last_scheduled_insn;
+  load_store_pendulum = sc->load_store_pendulum;
+}
+
+/* Free _SC.  */
+static void
+rs6000_free_sched_context (void *_sc)
+{
+  gcc_assert (_sc != NULL);
+
+  free (_sc);
+}
+
 \f
 /* Length in units of the trampoline for entering a nested function.  */
 
@@ -19430,13 +20606,15 @@ rs6000_handle_altivec_attribute (tree *node,
     default: break;
     }
 
-  if (result && result != type && TYPE_READONLY (type))
-    result = build_qualified_type (result, TYPE_QUAL_CONST);
+  /* Propagate qualifiers attached to the element type
+     onto the vector type.  */
+  if (result && result != type && TYPE_QUALS (type))
+    result = build_qualified_type (result, TYPE_QUALS (type));
 
   *no_add_attrs = true;  /* No need to hang on to the attribute.  */
 
   if (result)
-    *node = reconstruct_complex_type (*node, result);
+    *node = lang_hooks.types.reconstruct_complex_type (*node, result);
 
   return NULL_TREE;
 }
@@ -19669,7 +20847,7 @@ rs6000_elf_encode_section_info (tree decl, rtx rtl, int first)
     {
       rtx sym_ref = XEXP (rtl, 0);
       size_t len = strlen (XSTR (sym_ref, 0));
-      char *str = alloca (len + 2);
+      char *str = XALLOCAVEC (char, len + 2);
       str[0] = '.';
       memcpy (str + 1, XSTR (sym_ref, 0), len + 1);
       XSTR (sym_ref, 0) = ggc_alloc_string (str, len + 1);
@@ -19677,12 +20855,12 @@ rs6000_elf_encode_section_info (tree decl, rtx rtl, int first)
 }
 
 static inline bool
-compare_section_name (const char *section, const char *template)
+compare_section_name (const char *section, const char *templ)
 {
   int len;
 
-  len = strlen (template);
-  return (strncmp (section, template, len) == 0
+  len = strlen (templ);
+  return (strncmp (section, templ, len) == 0
          && (section[len] == 0 || section[len] == '.'));
 }
 
@@ -19968,10 +21146,10 @@ machopic_output_stub (FILE *file, const char *symb, const char *stub)
 
 
   length = strlen (symb);
-  symbol_name = alloca (length + 32);
+  symbol_name = XALLOCAVEC (char, length + 32);
   GEN_SYMBOL_NAME_FOR_SYMBOL (symbol_name, symb, length);
 
-  lazy_ptr_name = alloca (length + 32);
+  lazy_ptr_name = XALLOCAVEC (char, length + 32);
   GEN_LAZY_PTR_NAME_FOR_SYMBOL (lazy_ptr_name, symb, length);
 
   if (flag_pic == 2)
@@ -19987,7 +21165,7 @@ machopic_output_stub (FILE *file, const char *symb, const char *stub)
       fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
 
       label++;
-      local_label_0 = alloca (sizeof ("\"L00000000000$spb\""));
+      local_label_0 = XALLOCAVEC (char, sizeof ("\"L00000000000$spb\""));
       sprintf (local_label_0, "\"L%011d$spb\"", label);
 
       fprintf (file, "\tmflr r0\n");
@@ -20244,7 +21422,7 @@ rs6000_elf_declare_function_name (FILE *file, const char *name, tree decl)
 
   if (TARGET_RELOCATABLE
       && !TARGET_SECURE_PLT
-      && (get_pool_size () != 0 || current_function_profile)
+      && (get_pool_size () != 0 || crtl->profile)
       && uses_TOC ())
     {
       char buf[256];
@@ -20550,7 +21728,8 @@ rs6000_xcoff_file_end (void)
    scanned.  In either case, *TOTAL contains the cost result.  */
 
 static bool
-rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
+rs6000_rtx_costs (rtx x, int code, int outer_code, int *total,
+                 bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
 
@@ -20649,7 +21828,7 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
       /* When optimizing for size, MEM should be slightly more expensive
         than generating address, e.g., (plus (reg) (const)).
         L1 cache latency is about two instructions.  */
-      *total = optimize_size ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
+      *total = !speed ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
       return true;
 
     case LABEL_REF:
@@ -20860,7 +22039,7 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
 
     case CALL:
     case IF_THEN_ELSE:
-      if (optimize_size)
+      if (!speed)
        {
          *total = COSTS_N_INSNS (1);
          return true;
@@ -20945,6 +22124,12 @@ rs6000_register_move_cost (enum machine_mode mode,
       else if (from == CR_REGS)
        return 4;
 
+      /* Power6 has slower LR/CTR moves so make them more expensive than
+        memory in order to bias spills to memory .*/
+      else if (rs6000_cpu == PROCESSOR_POWER6
+              && reg_classes_intersect_p (from, LINK_OR_CTR_REGS))
+        return 6 * hard_regno_nregs[0][mode];
+
       else
        /* A move will cost one instruction per GPR moved.  */
        return 2 * hard_regno_nregs[0][mode];
@@ -20964,17 +22149,17 @@ rs6000_register_move_cost (enum machine_mode mode,
    or from memory.  */
 
 int
-rs6000_memory_move_cost (enum machine_mode mode, enum reg_class class,
+rs6000_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
                         int in ATTRIBUTE_UNUSED)
 {
-  if (reg_classes_intersect_p (class, GENERAL_REGS))
+  if (reg_classes_intersect_p (rclass, GENERAL_REGS))
     return 4 * hard_regno_nregs[0][mode];
-  else if (reg_classes_intersect_p (class, FLOAT_REGS))
+  else if (reg_classes_intersect_p (rclass, FLOAT_REGS))
     return 4 * hard_regno_nregs[32][mode];
-  else if (reg_classes_intersect_p (class, ALTIVEC_REGS))
+  else if (reg_classes_intersect_p (rclass, ALTIVEC_REGS))
     return 4 * hard_regno_nregs[FIRST_ALTIVEC_REGNO][mode];
   else
-    return 4 + rs6000_register_move_cost (mode, class, GENERAL_REGS);
+    return 4 + rs6000_register_move_cost (mode, rclass, GENERAL_REGS);
 }
 
 /* Returns a code for a target-specific builtin that implements
@@ -21391,29 +22576,9 @@ rs6000_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
       || POINTER_TYPE_P (valtype))
     mode = TARGET_32BIT ? SImode : DImode;
 
-  if (DECIMAL_FLOAT_MODE_P (mode))
-    {
-      if (TARGET_HARD_FLOAT && TARGET_FPRS)
-       {
-         switch (mode)
-           {
-           default:
-             gcc_unreachable ();
-           case SDmode:
-             regno = GP_ARG_RETURN;
-             break;
-           case DDmode:
-             regno = FP_ARG_RETURN;
-             break;
-           case TDmode:
-             /* Use f2:f3 specified by the ABI.  */
-             regno = FP_ARG_RETURN + 1;
-             break;
-           }
-       }
-      else
-       regno = GP_ARG_RETURN;
-    }
+  if (DECIMAL_FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS)
+    /* _Decimal128 must use an even/odd register pair.  */
+    regno = (mode == TDmode) ? FP_ARG_RETURN + 1 : FP_ARG_RETURN;
   else if (SCALAR_FLOAT_TYPE_P (valtype) && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
   else if (TREE_CODE (valtype) == COMPLEX_TYPE
@@ -21454,29 +22619,9 @@ rs6000_libcall_value (enum machine_mode mode)
                                      GEN_INT (4))));
     }
 
-  if (DECIMAL_FLOAT_MODE_P (mode))
-    {
-      if (TARGET_HARD_FLOAT && TARGET_FPRS)
-       {
-         switch (mode)
-           {
-           default:
-             gcc_unreachable ();
-           case SDmode:
-             regno = GP_ARG_RETURN;
-             break;
-           case DDmode:
-             regno = FP_ARG_RETURN;
-             break;
-           case TDmode:
-             /* Use f2:f3 specified by the ABI.  */
-             regno = FP_ARG_RETURN + 1;
-             break;
-           }
-       }
-      else
-       regno = GP_ARG_RETURN;
-    }
+  if (DECIMAL_FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS)
+    /* _Decimal128 must use an even/odd register pair.  */
+    regno = (mode == TDmode) ? FP_ARG_RETURN + 1 : FP_ARG_RETURN;
   else if (SCALAR_FLOAT_MODE_P (mode)
           && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
@@ -21534,7 +22679,6 @@ rs6000_is_opaque_type (const_tree type)
 {
   return (type == opaque_V2SI_type_node
              || type == opaque_V2SF_type_node
-             || type == opaque_p_V2SI_type_node
              || type == opaque_V4SI_type_node);
 }
 
@@ -21545,7 +22689,8 @@ rs6000_dwarf_register_span (rtx reg)
 
   if (TARGET_SPE
       && (SPE_VECTOR_MODE (GET_MODE (reg))
-         || (TARGET_E500_DOUBLE && GET_MODE (reg) == DFmode)))
+         || (TARGET_E500_DOUBLE
+             && (GET_MODE (reg) == DFmode || GET_MODE (reg) == DDmode))))
     ;
   else
     return NULL_RTX;
@@ -21685,4 +22830,31 @@ rs6000_stack_protect_fail (void)
         : default_external_stack_protect_fail ();
 }
 
+void
+rs6000_final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
+                          int num_operands ATTRIBUTE_UNUSED)
+{
+  if (rs6000_warn_cell_microcode)
+    {
+      const char *temp;
+      int insn_code_number = recog_memoized (insn);
+      location_t location = locator_location (INSN_LOCATOR (insn));
+
+      /* Punt on insns we cannot recognize.  */
+      if (insn_code_number < 0)
+       return;
+
+      temp = get_insn_template (insn_code_number, insn);
+
+      if (get_attr_cell_micro (insn) == CELL_MICRO_ALWAYS)
+       warning_at (location, OPT_mwarn_cell_microcode,
+                   "emitting microcode insn %s\t[%s] #%d",
+                   temp, insn_data[INSN_CODE (insn)].name, INSN_UID (insn)); 
+      else if (get_attr_cell_micro (insn) == CELL_MICRO_CONDITIONAL)
+       warning_at (location, OPT_mwarn_cell_microcode,
+                   "emitting conditional microcode insn %s\t[%s] #%d",
+                   temp, insn_data[INSN_CODE (insn)].name, INSN_UID (insn));
+    }
+}
+
 #include "gt-rs6000.h"