OSDN Git Service

PR target/35713
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index aa4da10..0105239 100644 (file)
@@ -53,6 +53,7 @@
 #include "cfglayout.h"
 #include "sched-int.h"
 #include "tree-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 */
@@ -166,7 +171,7 @@ 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 ABI extensions.  */
@@ -257,12 +262,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 +358,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 +377,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 +396,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 +415,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 +434,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 +453,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 +472,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 +491,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 +510,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 +529,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 +548,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 +567,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 +586,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 +606,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 +625,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 +644,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 +663,31 @@ 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 POWER4 and POWER5 processors.  */
 static const
 struct processor_costs power4_cost = {
@@ -675,7 +701,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 +720,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,7 +732,6 @@ 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);
@@ -754,6 +779,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 *);
@@ -899,6 +926,7 @@ static void rs6000_darwin_file_start (void);
 #endif
 
 static tree rs6000_build_builtin_va_list (void);
+static void rs6000_va_start (tree, rtx);
 static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *);
 static bool rs6000_must_pass_in_stack (enum machine_mode, const_tree);
 static bool rs6000_scalar_mode_supported_p (enum machine_mode);
@@ -1159,6 +1187,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 +1248,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 +1273,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)
@@ -1402,6 +1438,8 @@ 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},
         {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
         {"970", PROCESSOR_POWER4,
          POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
@@ -1508,6 +1546,14 @@ rs6000_override_options (const char *default_cpu)
   if (TARGET_E500)
     rs6000_isel = 1;
 
+  if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3)
+    {
+      if (TARGET_ALTIVEC)
+       error ("AltiVec not supported in this target");
+      if (TARGET_SPE)
+       error ("Spe not supported in this target");
+    }
+
   /* If we are optimizing big endian systems for space, use the load/store
      multiple and string instructions.  */
   if (BYTES_BIG_ENDIAN && optimize_size)
@@ -1570,11 +1616,22 @@ rs6000_override_options (const char *default_cpu)
     rs6000_ieeequad = 1;
 #endif
 
-  /* Set Altivec ABI as default for powerpc64 linux.  */
-  if (TARGET_ELF && TARGET_64BIT)
+  /* Enable Altivec ABI for AIX -maltivec.  */
+  if (TARGET_XCOFF && TARGET_ALTIVEC)
+    rs6000_altivec_abi = 1;
+
+  /* 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.  */
@@ -1618,7 +1675,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;
@@ -1816,6 +1873,11 @@ 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_POWER4:
       case PROCESSOR_POWER5:
        rs6000_cost = &power4_cost;
@@ -1838,6 +1900,11 @@ 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;
 }
 
 /* Implement targetm.vectorize.builtin_mask_for_load.  */
@@ -2106,6 +2173,7 @@ 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;
 
@@ -2163,19 +2231,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)
@@ -2183,7 +2252,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;
        }
 
@@ -2447,13 +2516,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);
          }
 
@@ -2716,6 +2788,156 @@ output_vec_const_move (rtx *operands)
     return "li %0,%1\n\tevmergelo %0,%0,%0\n\tli %0,%2";
 }
 
+/* Initialize TARGET of vector PAIRED to VALS.  */
+
+void
+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;
+  int i;
+
+  for (i = 0; i < n_elts; ++i)
+    {
+      x = XVECEXP (vals, 0, i);
+      if (!CONSTANT_P (x))
+       ++n_var;
+    }
+  if (n_var == 0)
+    {
+      /* Load from constant pool.  */
+      emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
+      return;
+    }
+
+  if (n_var == 2)
+    {
+      /* The vector is initialized only with non-constants.  */
+      new = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
+                               XVECEXP (vals, 0, 1));
+
+      emit_move_insn (target, new);
+      return;
+    }
+  
+  /* One field is non-constant and the other one is a constant.  Load the
+     constant from the constant pool and use ps_merge instruction to
+     construct the whole vector.  */
+  op1 = XVECEXP (vals, 0, 0);
+  op2 = XVECEXP (vals, 0, 1);
+
+  constant_op = (CONSTANT_P (op1)) ? op1 : op2;
+
+  tmp = gen_reg_rtx (GET_MODE (constant_op));
+  emit_move_insn (tmp, constant_op);
+
+  if (CONSTANT_P (op1))
+    new = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
+  else
+    new = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
+
+  emit_move_insn (target, new);
+}
+
+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);
+
+  return 1;
+}
+
 /* Initialize vector TARGET to VALS.  */
 
 void
@@ -2743,6 +2965,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.  */
@@ -2750,10 +2973,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)
@@ -2761,7 +2984,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;
        }
     }
@@ -2961,13 +3184,16 @@ invalid_e500_subreg (rtx op, enum machine_mode mode)
          && (mode == SImode || mode == DImode || mode == TImode)
          && REG_P (SUBREG_REG (op))
          && (GET_MODE (SUBREG_REG (op)) == DFmode
-             || GET_MODE (SUBREG_REG (op)) == TFmode))
+             || GET_MODE (SUBREG_REG (op)) == TFmode
+             || GET_MODE (SUBREG_REG (op)) == DDmode
+             || GET_MODE (SUBREG_REG (op)) == TDmode))
        return true;
 
       /* Reject (subreg:DF (reg:DI)); likewise with subreg:TF and
         reg:TI.  */
       if (GET_CODE (op) == SUBREG
-         && (mode == DFmode || mode == TFmode)
+         && (mode == DFmode || mode == TFmode
+             || mode == DDmode || mode == TDmode)
          && REG_P (SUBREG_REG (op))
          && (GET_MODE (SUBREG_REG (op)) == DImode
              || GET_MODE (SUBREG_REG (op)) == TImode))
@@ -3057,6 +3283,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;
 
@@ -3199,19 +3432,17 @@ 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);
 
@@ -3236,12 +3467,12 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
       break;
 
     case TFmode:
+    case TDmode:
       if (TARGET_E500_DOUBLE)
        return (SPE_CONST_OFFSET_OK (offset)
                && SPE_CONST_OFFSET_OK (offset + 8));
 
     case TImode:
-    case TDmode:
       if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
        extra = 12;
       else if (offset & 3)
@@ -3320,6 +3551,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);
@@ -3334,7 +3566,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
+                  && (mode == DFmode || mode == DDmode))))
        return false;
 
       return CONSTANT_P (x);
@@ -3381,7 +3614,11 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
   if (GET_CODE (x) == PLUS
       && GET_CODE (XEXP (x, 0)) == REG
       && GET_CODE (XEXP (x, 1)) == CONST_INT
-      && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000)
+      && (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))))
     {
       HOST_WIDE_INT high_int, low_int;
       rtx sum;
@@ -3397,11 +3634,12 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_MODE_NUNITS (mode) == 1
           && ((TARGET_HARD_FLOAT && TARGET_FPRS)
               || TARGET_POWERPC64
-              || (((mode != DImode && mode != DFmode && mode != DDmode)
-                   || TARGET_E500_DOUBLE)
-                  && mode != TFmode && mode != TDmode))
+              || ((mode != DImode && mode != DFmode && mode != DDmode)
+                  || TARGET_E500_DOUBLE))
           && (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)));
@@ -3428,19 +3666,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 && REG_P (op2))
+           return force_reg (Pmode, y);
+         else
+           return y;
+       }
 
       return force_reg (Pmode, x);
     }
@@ -3453,7 +3701,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)
+                  && (mode == DFmode || mode == DDmode))))
     {
       rtx reg = gen_reg_rtx (Pmode);
       emit_insn (gen_elf_high (reg, x));
@@ -3467,7 +3716,8 @@ 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)
+          && ((TARGET_HARD_FLOAT && TARGET_FPRS)
+              || (mode != DFmode && mode != DDmode))
           && mode != DImode
           && mode != TImode)
     {
@@ -3823,6 +4073,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))
     {
@@ -3864,12 +4115,12 @@ 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
+      && ((mode != DFmode && mode != DDmode) || TARGET_POWERPC64
          || (TARGET_FPRS && TARGET_HARD_FLOAT)))
     {
 #if TARGET_MACHO
@@ -3932,11 +4183,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.  */
@@ -3961,8 +4212,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;
@@ -3985,7 +4236,7 @@ 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))
       && (TARGET_POWERPC64 || mode != DImode)
       && legitimate_indexed_address_p (x, reg_ok_strict))
     return 1;
@@ -3995,12 +4246,13 @@ 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))
       && (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)
@@ -4164,9 +4416,6 @@ rs6000_conditional_register_usage (void)
     fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
       = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
 
-  if (TARGET_ALTIVEC)
-    global_regs[VSCR_REGNO] = 1;
-
   if (TARGET_SPE)
     {
       global_regs[SPEFSCR_REGNO] = 1;
@@ -4181,16 +4430,26 @@ rs6000_conditional_register_usage (void)
        = call_really_used_regs[14] = 1;
     }
 
-  if (! TARGET_ALTIVEC)
+  if (!TARGET_ALTIVEC)
     {
       for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
        fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1;
       call_really_used_regs[VRSAVE_REGNO] = 1;
     }
 
+  if (TARGET_ALTIVEC)
+    global_regs[VSCR_REGNO] = 1;
+
   if (TARGET_ALTIVEC_ABI)
-    for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i)
-      call_used_regs[i] = call_really_used_regs[i] = 1;
+    {
+      for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i)
+       call_used_regs[i] = call_really_used_regs[i] = 1;
+
+      /* AIX reserves VR20:31 in non-extended ABI mode.  */
+      if (TARGET_XCOFF)
+       for (i = FIRST_ALTIVEC_REGNO + 20; i < FIRST_ALTIVEC_REGNO + 32; ++i)
+         fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1;
+    }
 }
 \f
 /* Try to output insns to set TARGET equal to the constant C if it can
@@ -4517,6 +4776,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.  */
@@ -4537,6 +4845,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]);
@@ -4749,7 +5058,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)
 
@@ -5229,7 +5537,7 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
          && (mode == SFmode || mode == DFmode
-             || mode == DDmode || mode == TDmode
+             || mode == SDmode || mode == DDmode || mode == TDmode
              || (mode == TFmode && !TARGET_IEEEQUAD)))
        {
          /* _Decimal128 must use an even/odd register pair.  This assumes
@@ -5243,7 +5551,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);
            }
@@ -5295,7 +5604,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.
@@ -5326,12 +5634,14 @@ spe_build_register_parallel (enum machine_mode mode, int gregno)
   switch (mode)
     {
     case DFmode:
+    case DDmode:
       r1 = gen_rtx_REG (DImode, gregno);
       r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
       return gen_rtx_PARALLEL (mode, gen_rtvec (1, r1));
 
     case DCmode:
     case TFmode:
+    case TDmode:
       r1 = gen_rtx_REG (DImode, gregno);
       r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
       r3 = gen_rtx_REG (DImode, gregno + 2);
@@ -5363,13 +5673,14 @@ 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 == DDmode || mode == TDmode
+                            || mode == DCmode || mode == TCmode))
     {
       int n_words = rs6000_arg_size (mode, type);
 
       /* Doubles go in an odd/even register pair (r5/r6, etc).  */
-      if (mode == DFmode)
+      if (mode == DFmode || mode == DDmode)
        gregno += (1 - gregno) & 1;
 
       /* Multi-reg args are not split between registers and stack.  */
@@ -5794,7 +6105,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       if (TARGET_HARD_FLOAT && TARGET_FPRS
          && (mode == SFmode || 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.  */
@@ -6318,7 +6629,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;
@@ -6468,6 +6779,7 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       && (TYPE_MODE (type) == SFmode
          || TYPE_MODE (type) == DFmode
          || TYPE_MODE (type) == TFmode
+         || TYPE_MODE (type) == SDmode
          || TYPE_MODE (type) == DDmode
          || TYPE_MODE (type) == TDmode))
     {
@@ -6476,7 +6788,7 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       n_reg = (size + 7) / 8;
       sav_ofs = 8*4;
       sav_scale = 8;
-      if (TYPE_MODE (type) != SFmode)
+      if (TYPE_MODE (type) != SFmode && TYPE_MODE (type) != SDmode)
        align = 8;
     }
   else
@@ -6520,7 +6832,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       else if (reg == fpr && TYPE_MODE (type) == TDmode)
        {
          regalign = 1;
-         t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg, size_int (1));
+         t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg,
+                     build_int_cst (TREE_TYPE (reg), 1));
          u = build2 (MODIFY_EXPR, void_type_node, reg, t);
        }
 
@@ -6540,6 +6853,11 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       u = build2 (MULT_EXPR, sizetype, u, size_int (sav_scale));
       t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, u);
 
+      /* _Decimal32 varargs are located in the second word of the 64-bit
+        FP register for 32-bit binaries.  */
+      if (!TARGET_POWERPC64 && TYPE_MODE (type) == SDmode)
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
+
       t = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
       gimplify_and_add (t, pre_p);
 
@@ -6553,7 +6871,8 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
        {
          /* 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));
+         t = build_gimple_modify_stmt (reg,
+                                       build_int_cst (TREE_TYPE (reg), 8));
          gimplify_and_add (t, pre_p);
        }
     }
@@ -6774,9 +7093,9 @@ 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_ashlv16qi3, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB },
+  { MASK_ALTIVEC, CODE_FOR_ashlv8hi3, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH },
+  { MASK_ALTIVEC, CODE_FOR_ashlv4si3, "__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 },
@@ -8906,6 +9225,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.
@@ -10871,6 +11194,106 @@ mems_ok_for_quad_peep (rtx mem1, rtx mem2)
   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:
+      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;
+  block_stmt_iterator bsi;
+
+  gcc_assert (cfun->machine->sdmode_stack_slot == NULL_RTX);
+
+  FOR_EACH_BB (bb)
+    for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      {
+       tree ret = walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
+                                                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 (TREE_TYPE (t) == error_mark_node)
+       continue;
+
+      if (TYPE_MODE (TREE_TYPE (t)) == SDmode
+         || TYPE_MODE (DECL_ARG_TYPE (t)) == SDmode)
+       {
+         rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
+         cfun->machine->sdmode_stack_slot = adjust_address_nv (stack,
+                                                               SDmode, 0);
+         return;
+       }
+    }
+}
+
+static void
+rs6000_instantiate_decls (void)
+{
+  if (cfun->machine->sdmode_stack_slot != NULL_RTX)
+    instantiate_decl_rtl (cfun->machine->sdmode_stack_slot);
+}
+
 /* 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,
    NO_REGS is returned.  */
@@ -10931,7 +11354,7 @@ rs6000_secondary_reload_class (enum reg_class class,
   /* 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;
+    return (mode != SDmode) ? NO_REGS : GENERAL_REGS;
 
   /* Memory, and AltiVec registers can go into AltiVec registers.  */
   if ((regno == -1 || ALTIVEC_REGNO_P (regno))
@@ -13483,6 +13906,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));
 }
 
@@ -13554,8 +13980,8 @@ rs6000_split_multireg_move (rtx dst, rtx src)
     reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
   else if (ALTIVEC_REGNO_P (reg))
     reg_mode = V16QImode;
-  else if (TARGET_E500_DOUBLE && mode == TFmode)
-    reg_mode = DFmode;
+  else if (TARGET_E500_DOUBLE && (mode == TFmode || mode == TDmode))
+    reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
   else
     reg_mode = word_mode;
   reg_mode_size = GET_MODE_SIZE (reg_mode);
@@ -14026,7 +14452,6 @@ rs6000_stack_info (void)
 #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)
       || info_ptr->calls_p
       || rs6000_ra_ever_killed ())
@@ -14294,7 +14719,8 @@ spe_func_has_64bit_regs_p (void)
 
              if (SPE_VECTOR_MODE (mode))
                return true;
-             if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode))
+             if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+                                        || mode == DDmode || mode == TDmode))
                return true;
            }
        }
@@ -14966,77 +15392,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.  */
 
@@ -15110,7 +15471,7 @@ emit_frame_save (rtx frame_reg, rtx frame_ptr, enum machine_mode mode,
 
   /* Some cases that need register indexed addressing.  */
   if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
-      || (TARGET_E500_DOUBLE && mode == DFmode)
+      || (TARGET_E500_DOUBLE && (mode == DFmode || mode == DDmode))
       || (TARGET_SPE_ABI
          && SPE_VECTOR_MODE (mode)
          && !SPE_CONST_OFFSET_OK (offset)))
@@ -15150,7 +15511,7 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
   int_rtx = GEN_INT (offset);
 
   if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode))
-      || (TARGET_E500_DOUBLE && mode == DFmode))
+      || (TARGET_E500_DOUBLE && (mode == DFmode || mode == DDmode)))
     {
       offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH);
       emit_move_insn (offset_rtx, int_rtx);
@@ -15507,7 +15868,7 @@ rs6000_emit_prologue (void)
  
        if (spe_regs_addressable_via_sp)
          {
-           spe_save_area_ptr = sp_reg_rtx;
+           spe_save_area_ptr = frame_reg_rtx;
            spe_offset = info->spe_gp_save_offset + sp_offset;
          }
        else
@@ -15528,7 +15889,7 @@ rs6000_emit_prologue (void)
              }
  
            spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
-           emit_insn (gen_addsi3 (spe_save_area_ptr, sp_reg_rtx,
+           emit_insn (gen_addsi3 (spe_save_area_ptr, frame_reg_rtx,
                                   GEN_INT (info->spe_gp_save_offset + sp_offset)));
  
            spe_offset = 0;
@@ -15998,8 +16359,8 @@ rs6000_emit_epilogue (int sibcall)
       return;
     }
 
-  /* Set sp_offset based on the stack push from the prologue.  */
-  if (info->total_size < 32767)
+  /* frame_reg_rtx + sp_offset points to the top of this stack frame.  */
+  if (info->push_p)
     sp_offset = info->total_size;
 
   /* Restore AltiVec registers if needed.  */
@@ -16026,23 +16387,6 @@ rs6000_emit_epilogue (int sibcall)
          }
     }
 
-  /* Restore VRSAVE if needed.  */
-  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
-      && info->vrsave_mask != 0)
-    {
-      rtx addr, mem, reg;
-
-      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));
-    }
-
-  sp_offset = 0;
-
   /* 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.  */
@@ -16055,20 +16399,33 @@ rs6000_emit_epilogue (int sibcall)
 
       emit_move_insn (frame_reg_rtx,
                      gen_rtx_MEM (Pmode, sp_reg_rtx));
+      sp_offset = 0;
     }
-  else if (info->push_p)
+  else if (info->push_p
+          && DEFAULT_ABI != ABI_V4
+          && !current_function_calls_eh_return)
     {
-      if (DEFAULT_ABI == ABI_V4
-         || current_function_calls_eh_return)
-       sp_offset = info->total_size;
-      else
-       {
-         emit_insn (TARGET_32BIT
-                    ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
-                                  GEN_INT (info->total_size))
-                    : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
-                                  GEN_INT (info->total_size)));
-       }
+      emit_insn (TARGET_32BIT
+                ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
+                              GEN_INT (info->total_size))
+                : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
+                              GEN_INT (info->total_size)));
+      sp_offset = 0;
+    }
+
+  /* Restore VRSAVE if needed.  */
+  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
+      && info->vrsave_mask != 0)
+    {
+      rtx addr, mem, reg;
+
+      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));
     }
 
   /* Get the old lr if we saved it.  */
@@ -16150,7 +16507,6 @@ rs6000_emit_epilogue (int sibcall)
            && info->spe_64bit_regs_used != 0
            && info->first_gp_reg_save != 32)
     {
-      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.  */
@@ -16160,20 +16516,21 @@ rs6000_emit_epilogue (int sibcall)
       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;
-        }
+       spe_offset = info->spe_gp_save_offset + sp_offset;
       else
         {
+         rtx old_frame_reg_rtx = frame_reg_rtx;
           /* Make r11 point to the start of the SPE save area.  We worried about
              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.  */
-          spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
-
-          emit_insn (gen_addsi3 (spe_save_area_ptr, frame_reg_rtx,
+         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)));
+         /* Keep the invariant that frame_reg_rtx + sp_offset points
+            at the top of the stack frame.  */
+         sp_offset = -info->spe_gp_save_offset;
 
           spe_offset = 0;
         }
@@ -16188,7 +16545,7 @@ rs6000_emit_epilogue (int sibcall)
             gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
 
             offset = GEN_INT (spe_offset + reg_size * i);
-            addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
+            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),
@@ -16280,11 +16637,9 @@ rs6000_emit_epilogue (int sibcall)
       /* This blockage is needed so that sched doesn't decide to move
         the sp change before the register restores.  */
       rs6000_emit_stack_tie ();
-      if (TARGET_SPE_ABI
-          && info->spe_64bit_regs_used != 0
-          && info->first_gp_reg_save != 32)
-        emit_insn (gen_addsi3 (sp_reg_rtx, gen_rtx_REG (Pmode, 11),
-                               GEN_INT (-(info->spe_gp_save_offset + sp_offset))));
+      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);
     }
@@ -16548,6 +16903,7 @@ rs6000_output_function_epilogue (FILE *file,
                      switch (mode)
                        {
                        case SFmode:
+                       case SDmode:
                          bits = 0x2;
                          break;
 
@@ -16770,6 +17126,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;
@@ -17994,9 +18351,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)
         {
@@ -18017,8 +18373,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;
@@ -18147,6 +18503,8 @@ rs6000_issue_rate (void)
   case CPU_PPC7400:
   case CPU_PPC8540:
   case CPU_CELL:
+  case CPU_PPCE300C2:
+  case CPU_PPCE300C3:
     return 2;
   case CPU_RIOS2:
   case CPU_PPC604:
@@ -18203,6 +18561,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;
 
@@ -21330,29 +21693,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
@@ -21363,8 +21706,8 @@ rs6000_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
           && ALTIVEC_VECTOR_MODE (mode))
     regno = ALTIVEC_ARG_RETURN;
   else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
-          && (mode == DFmode || mode == DCmode
-              || mode == TFmode || mode == TCmode))
+          && (mode == DFmode || mode == DDmode || mode == DCmode
+              || mode == TFmode || mode == TDmode || mode == TCmode))
     return spe_build_register_parallel (mode, GP_ARG_RETURN);
   else
     regno = GP_ARG_RETURN;
@@ -21393,29 +21736,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;
@@ -21425,8 +21748,8 @@ rs6000_libcall_value (enum machine_mode mode)
   else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg)
     return rs6000_complex_function_value (mode);
   else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
-          && (mode == DFmode || mode == DCmode
-              || mode == TFmode || mode == TCmode))
+          && (mode == DFmode || mode == DDmode || mode == DCmode
+              || mode == TFmode || mode == TDmode || mode == TCmode))
     return spe_build_register_parallel (mode, GP_ARG_RETURN);
   else
     regno = GP_ARG_RETURN;
@@ -21484,7 +21807,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;