OSDN Git Service

* config/rs6000/rs6000.c (output_toc): Make "offset" HOST_WIDE_INT.
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index a0d15fd..8d51003 100644 (file)
@@ -17,8 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with GCC; see the file COPYING.  If not, write to the
-   Free Software Foundation, 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.  */
+   Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -93,7 +93,6 @@ typedef struct rs6000_stack {
   int varargs_save_offset;     /* offset to save the varargs registers */
   int ehrd_offset;             /* offset to EH return data */
   int reg_size;                        /* register size (4 or 8) */
-  int varargs_size;            /* size to hold V.4 args passed in regs */
   HOST_WIDE_INT vars_size;     /* variable save area size */
   int parm_size;               /* outgoing parameter size */
   int save_size;               /* save area size */
@@ -113,6 +112,23 @@ typedef struct rs6000_stack {
   int spe_64bit_regs_used;
 } rs6000_stack_t;
 
+/* A C structure for machine-specific, per-function data.
+   This is added to the cfun structure.  */
+typedef struct machine_function GTY(())
+{
+  /* Flags if __builtin_return_address (n) with n >= 1 was used.  */
+  int ra_needs_full_frame;
+  /* Some local-dynamic symbol.  */
+  const char *some_ld_name;
+  /* Whether the instruction chain has been scanned already.  */
+  int insn_chain_scanned_p;
+  /* Flags if __builtin_return_address (0) was used.  */
+  int ra_need_lr;
+  /* Offset from virtual_stack_vars_rtx to the start of the ABI_V4
+     varargs save area.  */
+  HOST_WIDE_INT varargs_save_offset;
+} machine_function;
+
 /* Target cpu type */
 
 enum processor_type rs6000_cpu;
@@ -226,6 +242,7 @@ int rs6000_alignment_flags;
 
 /* True for any options that were explicitly set.  */
 struct {
+  bool aix_struct_ret;         /* True if -maix-struct-ret was used.  */
   bool alignment;              /* True if -malign- was used.  */
   bool abi;                    /* True if -mabi= was used.  */
   bool spe;                    /* True if -mspe= was used.  */
@@ -551,6 +568,7 @@ struct processor_costs power4_cost = {
 
 \f
 static bool rs6000_function_ok_for_sibcall (tree, tree);
+static const char *rs6000_invalid_within_doloop (rtx);
 static rtx rs6000_generate_compare (enum rtx_code);
 static void rs6000_maybe_dead (rtx);
 static void rs6000_emit_stack_tie (void);
@@ -566,11 +584,11 @@ 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_indexed_address_p (rtx, int);
 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);
 #ifdef HAVE_GAS_HIDDEN
 static void rs6000_assemble_visibility (tree, int);
 #endif
@@ -613,9 +631,6 @@ static unsigned int rs6000_xcoff_section_type_flags (tree, const char *, int);
 static void rs6000_xcoff_file_start (void);
 static void rs6000_xcoff_file_end (void);
 #endif
-#if TARGET_MACHO
-static bool rs6000_binds_local_p (tree);
-#endif
 static int rs6000_variable_issue (FILE *, int, rtx, int);
 static bool rs6000_rtx_costs (rtx, int, int, int *);
 static int rs6000_adjust_cost (rtx, rtx, rtx, int);
@@ -668,6 +683,10 @@ 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);
+static rtx altivec_expand_vec_ext_builtin (tree, rtx);
+static int get_element_number (tree, tree);
 static bool rs6000_handle_option (size_t, const char *, int);
 static void rs6000_parse_tls_size_option (void);
 static void rs6000_parse_yes_no_option (const char *, const char *, int *);
@@ -680,6 +699,7 @@ int easy_vector_constant (rtx, enum machine_mode);
 static bool rs6000_is_opaque_type (tree);
 static rtx rs6000_dwarf_register_span (rtx);
 static rtx rs6000_legitimize_tls_address (rtx, enum tls_model);
+static void rs6000_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static rtx rs6000_tls_get_addr (void);
 static rtx rs6000_got_sym (void);
 static int rs6000_tls_symbol_ref_1 (rtx *, void *);
@@ -727,7 +747,7 @@ static rtx rs6000_emit_vector_compare (enum rtx_code, rtx, rtx,
                                       enum machine_mode);
 static int get_vsel_insn (enum machine_mode);
 static void rs6000_emit_vector_select (rtx, rtx, rtx, rtx);
-
+static tree rs6000_stack_protect_fail (void);
 
 const int INSN_NOT_AVAILABLE = -1;
 static enum machine_mode rs6000_eh_return_filter_mode (void);
@@ -766,7 +786,9 @@ char rs6000_reg_names[][8] =
       "24", "25", "26", "27", "28", "29", "30", "31",
       "vrsave", "vscr",
       /* SPE registers.  */
-      "spe_acc", "spefscr"
+      "spe_acc", "spefscr",
+      /* Soft frame pointer.  */
+      "sfp"
 };
 
 #ifdef TARGET_REGNAMES
@@ -790,7 +812,9 @@ static const char alt_reg_names[][8] =
   "%v24", "%v25", "%v26", "%v27", "%v28", "%v29", "%v30", "%v31",
   "vrsave", "vscr",
   /* SPE registers.  */
-  "spe_acc", "spefscr"
+  "spe_acc", "spefscr",
+  /* Soft frame pointer.  */
+  "sfp"
 };
 #endif
 \f
@@ -893,7 +917,7 @@ static const char alt_reg_names[][8] =
 
 #if TARGET_MACHO
 #undef TARGET_BINDS_LOCAL_P
-#define TARGET_BINDS_LOCAL_P rs6000_binds_local_p
+#define TARGET_BINDS_LOCAL_P darwin_binds_local_p
 #endif
 
 #undef TARGET_ASM_OUTPUT_MI_THUNK
@@ -905,6 +929,9 @@ static const char alt_reg_names[][8] =
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL rs6000_function_ok_for_sibcall
 
+#undef TARGET_INVALID_WITHIN_DOLOOP
+#define TARGET_INVALID_WITHIN_DOLOOP rs6000_invalid_within_doloop
+
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS rs6000_rtx_costs
 #undef TARGET_ADDRESS_COST
@@ -965,6 +992,9 @@ static const char alt_reg_names[][8] =
 #define TARGET_DEFAULT_TARGET_FLAGS \
   (TARGET_DEFAULT | MASK_SCHED_PROLOG)
 
+#undef TARGET_STACK_PROTECT_FAIL
+#define TARGET_STACK_PROTECT_FAIL rs6000_stack_protect_fail
+
 /* MPC604EUM 3.5.2 Weak Consistency between Multiple Processors
    The PowerPC architecture requires only weak consistency among
    processors--that is, memory accesses between processors need not be
@@ -976,6 +1006,11 @@ static const char alt_reg_names[][8] =
 #undef TARGET_RELAXED_ORDERING
 #define TARGET_RELAXED_ORDERING true
 
+#ifdef HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL rs6000_output_dwarf_dtprel
+#endif
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 
@@ -1114,7 +1149,8 @@ rs6000_override_options (const char *default_cpu)
         {"power4", PROCESSOR_POWER4,
          POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64},
         {"power5", PROCESSOR_POWER5,
-         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64},
+         POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT
+         | MASK_MFCRF | MASK_POPCNTB},
         {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
         {"powerpc64", PROCESSOR_POWERPC64,
          POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
@@ -1251,9 +1287,7 @@ rs6000_override_options (const char *default_cpu)
   if (TARGET_ELF && TARGET_64BIT)
     {
       rs6000_altivec_abi = 1;
-#if TARGET_ALTIVEC_VRSAVE != 0
       TARGET_ALTIVEC_VRSAVE = 1;
-#endif
     }
 
   /* Set the Darwin64 ABI as default for 64-bit Darwin.  */
@@ -1312,18 +1346,13 @@ rs6000_override_options (const char *default_cpu)
   rs6000_sched_groups = (rs6000_cpu == PROCESSOR_POWER4
                         || rs6000_cpu == PROCESSOR_POWER5);
 
-  /* ?? I see no need for this.  This variable was always initialized to 0,
-     except when explicitly set.  It's not set in any .h file either.
   rs6000_sched_restricted_insns_priority
     = (rs6000_sched_groups ? 1 : 0);
-  */
 
   /* Handle -msched-costly-dep option.  */
-  /* ?? Same goes for this.  When would rs6000_sched_costly_dep ever have
-     a nonzero value upon entry to this function.  ??
   rs6000_sched_costly_dep
     = (rs6000_sched_groups ? store_to_load_dep_costly : no_dep_costly);
-  */
+
   if (rs6000_sched_costly_dep_str)
     {
       if (! strcmp (rs6000_sched_costly_dep_str, "no"))
@@ -1341,6 +1370,7 @@ rs6000_override_options (const char *default_cpu)
   /* Handle -minsert-sched-nops option.  */
   rs6000_sched_insert_nops
     = (rs6000_sched_groups ? sched_finish_regroup_exact : sched_finish_none);
+
   if (rs6000_sched_insert_nops_str)
     {
       if (! strcmp (rs6000_sched_insert_nops_str, "no"))
@@ -1360,16 +1390,11 @@ rs6000_override_options (const char *default_cpu)
     memcpy (rs6000_reg_names, alt_reg_names, sizeof (rs6000_reg_names));
 #endif
 
-  /* Set TARGET_AIX_STRUCT_RET last, after the ABI is determined.
+  /* Set aix_struct_return last, after the ABI is determined.
      If -maix-struct-return or -msvr4-struct-return was explicitly
      used, don't override with the ABI default.  */
-  if ((target_flags_explicit & MASK_AIX_STRUCT_RET) == 0)
-    {
-      if (DEFAULT_ABI == ABI_V4 && !DRAFT_V4_STRUCT_RET)
-       target_flags = (target_flags & ~MASK_AIX_STRUCT_RET);
-      else
-       target_flags |= MASK_AIX_STRUCT_RET;
-    }
+  if (!rs6000_explicit_options.aix_struct_ret)
+    aix_struct_return = (DEFAULT_ABI != ABI_V4 || DRAFT_V4_STRUCT_RET);
 
   if (TARGET_LONG_DOUBLE_128
       && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN))
@@ -1544,6 +1569,10 @@ rs6000_parse_tls_size_option (void)
 void
 optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
 {
+  if (DEFAULT_ABI == ABI_DARWIN)
+    /* The Darwin libraries never set errno, so we might as well
+       avoid calling them when that's the only reason we would.  */
+    flag_errno_math = 0;
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
@@ -1556,18 +1585,25 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_mno_power:
       target_flags &= ~(MASK_POWER | MASK_POWER2
                        | MASK_MULTIPLE | MASK_STRING);
+      target_flags_explicit |= (MASK_POWER | MASK_POWER2
+                               | MASK_MULTIPLE | MASK_STRING);
       break;
     case OPT_mno_powerpc:
       target_flags &= ~(MASK_POWERPC | MASK_PPC_GPOPT
                        | MASK_PPC_GFXOPT | MASK_POWERPC64);
+      target_flags_explicit |= (MASK_POWERPC | MASK_PPC_GPOPT
+                               | MASK_PPC_GFXOPT | MASK_POWERPC64);
       break;
     case OPT_mfull_toc:
       target_flags &= ~(MASK_MINIMAL_TOC | MASK_NO_FP_IN_TOC
                        | MASK_NO_SUM_IN_TOC);
+      target_flags_explicit |= (MASK_MINIMAL_TOC | MASK_NO_FP_IN_TOC
+                               | MASK_NO_SUM_IN_TOC);
 #ifdef TARGET_USES_SYSV4_OPT
       /* Note, V.4 no longer uses a normal TOC, so make -mfull-toc, be
         just the same as -mminimal-toc.  */
       target_flags |= MASK_MINIMAL_TOC;
+      target_flags_explicit |= MASK_MINIMAL_TOC;
 #endif
       break;
 
@@ -1575,6 +1611,7 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_mtoc:
       /* Make -mtoc behave like -mminimal-toc.  */
       target_flags |= MASK_MINIMAL_TOC;
+      target_flags_explicit |= MASK_MINIMAL_TOC;
       break;
 #endif
 
@@ -1584,6 +1621,8 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_m64:
 #endif
       target_flags |= MASK_POWERPC64 | MASK_POWERPC | MASK_PPC_GFXOPT;
+      target_flags_explicit |= MASK_POWERPC64 | MASK_POWERPC
+       | MASK_PPC_GFXOPT;
       break;
 
 #ifdef TARGET_USES_AIX64_OPT
@@ -1592,6 +1631,7 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_m32:
 #endif
       target_flags &= ~MASK_POWERPC64;
+      target_flags_explicit |= MASK_POWERPC64;
       break;
 
     case OPT_minsert_sched_nops_:
@@ -1601,32 +1641,44 @@ rs6000_handle_option (size_t code, const char *arg, int value)
     case OPT_mminimal_toc:
       if (value == 1)
        {
-         target_flags &= ~MASK_NO_FP_IN_TOC;
-         target_flags &= ~MASK_NO_SUM_IN_TOC;
+         target_flags &= ~(MASK_NO_FP_IN_TOC | MASK_NO_SUM_IN_TOC);
+         target_flags_explicit |= (MASK_NO_FP_IN_TOC | MASK_NO_SUM_IN_TOC);
        }
       break;
 
     case OPT_mpower:
       if (value == 1)
-       target_flags |= (MASK_MULTIPLE | MASK_STRING);
+       {
+         target_flags |= (MASK_MULTIPLE | MASK_STRING);
+         target_flags_explicit |= (MASK_MULTIPLE | MASK_STRING);
+       }
       break;
 
     case OPT_mpower2:
       if (value == 1)
-       target_flags |= (MASK_POWER | MASK_MULTIPLE | MASK_STRING);
+       {
+         target_flags |= (MASK_POWER | MASK_MULTIPLE | MASK_STRING);
+         target_flags_explicit |= (MASK_POWER | MASK_MULTIPLE | MASK_STRING);
+       }
       break;
 
     case OPT_mpowerpc_gpopt:
     case OPT_mpowerpc_gfxopt:
       if (value == 1)
-       target_flags |= MASK_POWERPC;
+       {
+         target_flags |= MASK_POWERPC;
+         target_flags_explicit |= MASK_POWERPC;
+       }
+      break;
+
+    case OPT_maix_struct_return:
+    case OPT_msvr4_struct_return:
+      rs6000_explicit_options.aix_struct_ret = true;
       break;
 
-#if TARGET_ALTIVEC_VRSAVE != 0
     case OPT_mvrsave_:
       rs6000_parse_yes_no_option ("vrsave", arg, &(TARGET_ALTIVEC_VRSAVE));
       break;
-#endif
 
     case OPT_misel_:
       rs6000_explicit_options.isel = true;
@@ -1660,15 +1712,25 @@ rs6000_handle_option (size_t code, const char *arg, int value)
 
     case OPT_mrelocatable:
       if (value == 1)
-       target_flags |= MASK_MINIMAL_TOC | MASK_NO_FP_IN_TOC;
+       {
+         target_flags |= MASK_MINIMAL_TOC | MASK_NO_FP_IN_TOC;
+         target_flags_explicit |= MASK_MINIMAL_TOC | MASK_NO_FP_IN_TOC;
+       }
       break;
 
     case OPT_mrelocatable_lib:
       if (value == 1)
-       target_flags |= MASK_RELOCATABLE | MASK_MINIMAL_TOC
-         | MASK_NO_FP_IN_TOC;
+       {
+         target_flags |= MASK_RELOCATABLE | MASK_MINIMAL_TOC
+           | MASK_NO_FP_IN_TOC;
+         target_flags_explicit |= MASK_RELOCATABLE | MASK_MINIMAL_TOC
+           | MASK_NO_FP_IN_TOC;
+       }
       else
-       target_flags &= ~MASK_RELOCATABLE;
+       {
+         target_flags &= ~MASK_RELOCATABLE;
+         target_flags_explicit |= MASK_RELOCATABLE;
+       }
       break;
 #endif
 
@@ -1814,6 +1876,12 @@ rs6000_file_start (void)
            }
        }
 
+      if (PPC405_ERRATUM77)
+       {
+         fprintf (file, "%s PPC405CR_ERRATUM77", start);
+         start = "";
+       }
+
 #ifdef USING_ELFOS_H
       switch (rs6000_sdata)
        {
@@ -1906,7 +1974,7 @@ int
 num_insns_constant (rtx op, enum machine_mode mode)
 {
   HOST_WIDE_INT low, high;
-  
+
   switch (GET_CODE (op))
     {
     case CONST_INT:
@@ -1923,7 +1991,7 @@ num_insns_constant (rtx op, enum machine_mode mode)
          {
            long l;
            REAL_VALUE_TYPE rv;
-           
+
            REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
            REAL_VALUE_TO_TARGET_SINGLE (rv, l);
            return num_insns_constant_wide ((HOST_WIDE_INT) l);
@@ -1938,7 +2006,7 @@ num_insns_constant (rtx op, enum machine_mode mode)
          {
            long l[2];
            REAL_VALUE_TYPE rv;
-           
+
            REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
            REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
            high = l[WORDS_BIG_ENDIAN == 0];
@@ -1953,18 +2021,18 @@ num_insns_constant (rtx op, enum machine_mode mode)
            if ((high == 0 && low >= 0)
                || (high == -1 && low < 0))
              return num_insns_constant_wide (low);
-           
+
            else if (mask64_operand (op, mode))
              return 2;
-           
+
            else if (low == 0)
              return num_insns_constant_wide (high) + 1;
-           
+
            else
              return (num_insns_constant_wide (high)
                      + num_insns_constant_wide (low) + 1);
          }
-       
+
     default:
       gcc_unreachable ();
     }
@@ -2059,7 +2127,7 @@ output_vec_const_move (rtx *operands)
        return "vxor %0,%0,%0";
 
       gcc_assert (easy_vector_constant (vec, mode));
-      
+
       operands[1] = GEN_INT (cst);
       switch (mode)
        {
@@ -2093,14 +2161,14 @@ output_vec_const_move (rtx *operands)
            }
          else if (EASY_VECTOR_15_ADD_SELF (cst))
            return "#";
-         
+
        default:
          gcc_unreachable ();
        }
     }
 
   gcc_assert (TARGET_SPE);
-  
+
   /* Vector constant 0 is handled as a splitter of V2SI, and in the
      pattern of V1DI, V4HI, and V2SF.
 
@@ -2114,59 +2182,168 @@ output_vec_const_move (rtx *operands)
     return "li %0,%1\n\tevmergelo %0,%0,%0\n\tli %0,%2";
 }
 
-int
-mask64_1or2_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED,
-                      bool allow_one)
-{
-  if (GET_CODE (op) == CONST_INT)
+/* Initialize vector TARGET to VALS.  */
+
+void
+rs6000_expand_vector_init (rtx target, rtx vals)
+{
+  enum machine_mode mode = GET_MODE (target);
+  enum machine_mode inner_mode = GET_MODE_INNER (mode);
+  int n_elts = GET_MODE_NUNITS (mode);
+  int n_var = 0, one_var = -1;
+  bool all_same = true, all_const_zero = true;
+  rtx x, mem;
+  int i;
+
+  for (i = 0; i < n_elts; ++i)
     {
-      HOST_WIDE_INT c, lsb;
-      bool one_ok;
-      
-      c = INTVAL (op);
+      x = XVECEXP (vals, 0, i);
+      if (!CONSTANT_P (x))
+       ++n_var, one_var = i;
+      else if (x != CONST0_RTX (inner_mode))
+       all_const_zero = false;
 
-      /* Disallow all zeros.  */
-      if (c == 0)
-       return 0;
+      if (i > 0 && !rtx_equal_p (x, XVECEXP (vals, 0, 0)))
+       all_same = false;
+    }
 
-      /* We can use a single rlwinm insn if no upper bits of C are set
-         AND there are zero, one or two transitions in the _whole_ of
-         C.  */
-      one_ok = !(c & ~(HOST_WIDE_INT)0xffffffff);
-      
-      /* We don't change the number of transitions by inverting,
-        so make sure we start with the LS bit zero.  */
-      if (c & 1)
-       c = ~c;
-
-      /* Find the first transition.  */
-      lsb = c & -c;
+  if (n_var == 0)
+    {
+      if (mode != V4SFmode && all_const_zero)
+       {
+         /* Zero register.  */
+         emit_insn (gen_rtx_SET (VOIDmode, target,
+                                 gen_rtx_XOR (mode, target, target)));
+         return;
+       }
+      else if (mode != V4SFmode && easy_vector_same (vals, mode))
+       {
+         /* Splat immediate.  */
+         x = gen_rtx_VEC_DUPLICATE (mode, CONST_VECTOR_ELT (vals, 0));
+         emit_insn (gen_rtx_SET (VOIDmode, target, x));
+         return;
+       }
+      else if (all_same)
+       ;       /* Splat vector element.  */
+      else
+       {
+         /* Load from constant pool.  */
+         emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
+         return;
+       }
+    }
 
-      /* Invert to look for a second transition.  */
-      c = ~c;
+  /* Store value to stack temp.  Load vector element.  Splat.  */
+  if (all_same)
+    {
+      mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0);
+      emit_move_insn (adjust_address_nv (mem, inner_mode, 0),
+                     XVECEXP (vals, 0, 0));
+      x = gen_rtx_UNSPEC (VOIDmode,
+                         gen_rtvec (1, const0_rtx), UNSPEC_LVE);
+      emit_insn (gen_rtx_PARALLEL (VOIDmode,
+                                  gen_rtvec (2,
+                                             gen_rtx_SET (VOIDmode,
+                                                          target, mem),
+                                             x)));
+      x = gen_rtx_VEC_SELECT (inner_mode, target,
+                             gen_rtx_PARALLEL (VOIDmode,
+                                               gen_rtvec (1, const0_rtx)));
+      emit_insn (gen_rtx_SET (VOIDmode, target,
+                             gen_rtx_VEC_DUPLICATE (mode, x)));
+      return;
+    }
 
-      /* Erase first transition.  */
-      c &= -lsb;
+  /* One field is non-constant.  Load constant then overwrite
+     varying field.  */
+  if (n_var == 1)
+    {
+      rtx copy = copy_rtx (vals);
 
-      /* Find the second transition.  */
-      lsb = c & -c;
+      /* Load constant part of vector, substitute neighboring value for
+        varying element.  */
+      XVECEXP (copy, 0, one_var) = XVECEXP (vals, 0, (one_var + 1) % n_elts);
+      rs6000_expand_vector_init (target, copy);
 
-      /* Invert to look for a third transition.  */
-      c = ~c;
+      /* Insert variable.  */
+      rs6000_expand_vector_set (target, XVECEXP (vals, 0, one_var), one_var);
+      return;
+    }
 
-      /* Erase second transition.  */
-      c &= -lsb;
+  /* Construct the vector in memory one field at a time
+     and load the whole vector.  */
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+  for (i = 0; i < n_elts; i++)
+    emit_move_insn (adjust_address_nv (mem, inner_mode,
+                                   i * GET_MODE_SIZE (inner_mode)),
+                   XVECEXP (vals, 0, i));
+  emit_move_insn (target, mem);
+}
 
-      if (one_ok && !(allow_one || c))
-       return 0;
+/* Set field ELT of TARGET to VAL.  */
 
-      /* Find the third transition (if any).  */
-      lsb = c & -c;
+void
+rs6000_expand_vector_set (rtx target, rtx val, int elt)
+{
+  enum machine_mode mode = GET_MODE (target);
+  enum machine_mode inner_mode = GET_MODE_INNER (mode);
+  rtx reg = gen_reg_rtx (mode);
+  rtx mask, mem, x;
+  int width = GET_MODE_SIZE (inner_mode);
+  int i;
 
-      /* Match if all the bits above are 1's (or c is zero).  */
-      return c == -lsb;
-    }
-  return 0;
+  /* Load single variable value.  */
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0);
+  emit_move_insn (adjust_address_nv (mem, inner_mode, 0), val);
+  x = gen_rtx_UNSPEC (VOIDmode,
+                     gen_rtvec (1, const0_rtx), UNSPEC_LVE);
+  emit_insn (gen_rtx_PARALLEL (VOIDmode,
+                              gen_rtvec (2,
+                                         gen_rtx_SET (VOIDmode,
+                                                      reg, mem),
+                                         x)));
+
+  /* Linear sequence.  */
+  mask = gen_rtx_PARALLEL (V16QImode, rtvec_alloc (16));
+  for (i = 0; i < 16; ++i)
+    XVECEXP (mask, 0, i) = GEN_INT (i);
+
+  /* Set permute mask to insert element into target.  */
+  for (i = 0; i < width; ++i)
+    XVECEXP (mask, 0, elt*width + i)
+      = GEN_INT (i + 0x10);
+  x = gen_rtx_CONST_VECTOR (V16QImode, XVEC (mask, 0));
+  x = gen_rtx_UNSPEC (mode,
+                     gen_rtvec (3, target, reg,
+                                force_reg (V16QImode, x)),
+                     UNSPEC_VPERM);
+  emit_insn (gen_rtx_SET (VOIDmode, target, x));
+}
+
+/* Extract field ELT from VEC into TARGET.  */
+
+void
+rs6000_expand_vector_extract (rtx target, rtx vec, int elt)
+{
+  enum machine_mode mode = GET_MODE (vec);
+  enum machine_mode inner_mode = GET_MODE_INNER (mode);
+  rtx mem, x;
+
+  /* Allocate mode-sized buffer.  */
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+
+  /* Add offset to field within buffer matching vector element.  */
+  mem = adjust_address_nv (mem, mode, elt * GET_MODE_SIZE (inner_mode));
+
+  /* Store single field into mode-sized buffer.  */
+  x = gen_rtx_UNSPEC (VOIDmode,
+                     gen_rtvec (1, const0_rtx), UNSPEC_STVE);
+  emit_insn (gen_rtx_PARALLEL (VOIDmode,
+                              gen_rtvec (2,
+                                         gen_rtx_SET (VOIDmode,
+                                                      mem, vec),
+                                         x)));
+  emit_move_insn (target, adjust_address_nv (mem, inner_mode, 0));
 }
 
 /* Generates shifts and masks for a pair of rldicl or rldicr insns to
@@ -2268,7 +2445,7 @@ rs6000_special_round_type_align (tree type, int computed, int specified)
 {
   tree field = TYPE_FIELDS (type);
 
-  /* Skip all non field decls */ 
+  /* Skip all non field decls */
   while (field != NULL && TREE_CODE (field) != FIELD_DECL)
     field = TREE_CHAIN (field);
 
@@ -2398,8 +2575,8 @@ legitimate_constant_pool_address_p (rtx x)
          && constant_pool_expr_p (XEXP (x, 1)));
 }
 
-static bool
-legitimate_small_data_p (enum machine_mode mode, rtx x)
+bool
+rs6000_legitimate_small_data_p (enum machine_mode mode, rtx x)
 {
   return (DEFAULT_ABI == ABI_V4
          && !flag_pic && !TARGET_TOC
@@ -2434,10 +2611,10 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
     case V8HImode:
     case V4SFmode:
     case V4SImode:
-      /* AltiVec vector modes.  Only reg+reg addressing is valid here,
-        which leaves the only valid constant offset of zero, which by
-        canonicalization rules is also invalid.  */
-      return false;
+      /* 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;
 
     case V4HImode:
     case V2SImode:
@@ -2701,10 +2878,10 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
     return NULL_RTX;
 }
 
-/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
    We need to emit DTP-relative relocations.  */
 
-void
+static void
 rs6000_output_dwarf_dtprel (FILE *file, int size, rtx x)
 {
   switch (size)
@@ -2801,8 +2978,13 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
     {
       rtx r3, got, tga, tmp1, tmp2, eqv;
 
+      /* We currently use relocations like @got@tlsgd for tls, which
+        means the linker will handle allocation of tls entries, placing
+        them in the .got section.  So use a pointer to the .got section,
+        not one to secondary TOC sections used by 64-bit -mminimal-toc,
+        or to secondary GOT sections used by 32-bit -fPIC.  */
       if (TARGET_64BIT)
-       got = gen_rtx_REG (Pmode, TOC_REGISTER);
+       got = gen_rtx_REG (Pmode, 2);
       else
        {
          if (flag_pic == 1)
@@ -3068,16 +3250,21 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       return x;
     }
 
-#if TARGET_MACHO
   if (GET_CODE (x) == SYMBOL_REF
-      && DEFAULT_ABI == ABI_DARWIN
       && !ALTIVEC_VECTOR_MODE (mode)
+#if TARGET_MACHO
+      && DEFAULT_ABI == ABI_DARWIN
       && (flag_pic || MACHO_DYNAMIC_NO_PIC_P)
+#else
+      && DEFAULT_ABI == ABI_V4
+      && !flag_pic
+#endif
       /* Don't do this for TFmode, since the result isn't offsettable.
         The same goes for DImode without 64-bit gprs.  */
       && mode != TFmode
       && (mode != DImode || TARGET_POWERPC64))
     {
+#if TARGET_MACHO
       if (flag_pic)
        {
          rtx offset = gen_rtx_CONST (Pmode,
@@ -3088,6 +3275,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
                  gen_rtx_HIGH (Pmode, offset)), offset);
        }
       else
+#endif
        x = gen_rtx_LO_SUM (GET_MODE (x),
              gen_rtx_HIGH (Pmode, x), x);
 
@@ -3097,7 +3285,23 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       *win = 1;
       return x;
     }
-#endif
+
+  /* Reload an offset address wrapped by an AND that represents the
+     masking of the lower bits.  Strip the outer AND and let reload
+     convert the offset address into an indirect address.  */
+  if (TARGET_ALTIVEC
+      && ALTIVEC_VECTOR_MODE (mode)
+      && GET_CODE (x) == AND
+      && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+      && GET_CODE (XEXP (x, 1)) == CONST_INT
+      && INTVAL (XEXP (x, 1)) == -16)
+    {
+      x = XEXP (x, 0);
+      *win = 1;
+      return x;
+    }
 
   if (TARGET_TOC
       && constant_pool_expr_p (x)
@@ -3151,7 +3355,7 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
       && TARGET_UPDATE
       && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict))
     return 1;
-  if (legitimate_small_data_p (mode, x))
+  if (rs6000_legitimate_small_data_p (mode, x))
     return 1;
   if (legitimate_constant_pool_address_p (x))
     return 1;
@@ -3259,7 +3463,7 @@ rs6000_conditional_register_usage (void)
   if (! TARGET_POWER)
     fixed_regs[64] = 1;
 
-  /* 64-bit AIX reserves GPR13 for thread-private data.  */
+  /* 64-bit AIX and Linux reserve GPR13 for thread-private data.  */
   if (TARGET_64BIT)
     fixed_regs[13] = call_used_regs[13]
       = call_really_used_regs[13] = 1;
@@ -3270,6 +3474,11 @@ rs6000_conditional_register_usage (void)
       fixed_regs[i] = call_used_regs[i]
        = call_really_used_regs[i] = 1;
 
+  /* The TOC register is not killed across calls in a way that is
+     visible to the compiler.  */
+  if (DEFAULT_ABI == ABI_AIX)
+    call_really_used_regs[2] = 0;
+
   if (DEFAULT_ABI == ABI_V4
       && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
       && flag_pic == 2)
@@ -3284,8 +3493,7 @@ rs6000_conditional_register_usage (void)
 
   if (DEFAULT_ABI == ABI_DARWIN
       && PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
-    global_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
-      = fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
+      fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
       = call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
       = call_really_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
 
@@ -3337,10 +3545,10 @@ rs6000_emit_set_const (rtx dest, enum machine_mode mode,
        dest = gen_reg_rtx (mode);
       emit_insn (gen_rtx_SET (VOIDmode, dest, source));
       return dest;
-      
+
     case SImode:
       result = no_new_pseudos ? dest : gen_reg_rtx (SImode);
-      
+
       emit_insn (gen_rtx_SET (VOIDmode, result,
                              GEN_INT (INTVAL (source)
                                       & (~ (HOST_WIDE_INT) 0xffff))));
@@ -3357,7 +3565,7 @@ rs6000_emit_set_const (rtx dest, enum machine_mode mode,
          c0 = INTVAL (source);
          c1 = -(c0 < 0);
          break;
-         
+
        case CONST_DOUBLE:
 #if HOST_BITS_PER_WIDE_INT >= 64
          c0 = CONST_DOUBLE_LOW (source);
@@ -3521,7 +3729,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                   || CONST_DOUBLE_LOW (operands[1]) < 0)
                  && (CONST_DOUBLE_HIGH (operands[1]) != -1
                      || CONST_DOUBLE_LOW (operands[1]) >= 0)));
-  
+
   /* Check if GCC is setting up a block move that will end up using FP
      registers as temporaries.  We must make sure this is acceptable.  */
   if (GET_CODE (operands[0]) == MEM
@@ -3575,11 +3783,29 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
 
   /* Recognize the case where operand[1] is a reference to thread-local
      data and load its address to a register.  */
-  if (GET_CODE (operands[1]) == SYMBOL_REF)
+  if (rs6000_tls_referenced_p (operands[1]))
     {
-      enum tls_model model = SYMBOL_REF_TLS_MODEL (operands[1]);
-      if (model != 0)
-       operands[1] = rs6000_legitimize_tls_address (operands[1], model);
+      enum tls_model model;
+      rtx tmp = operands[1];
+      rtx addend = NULL;
+
+      if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS)
+       {
+          addend = XEXP (XEXP (tmp, 0), 1);
+         tmp = XEXP (XEXP (tmp, 0), 0);
+       }
+
+      gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+      model = SYMBOL_REF_TLS_MODEL (tmp);
+      gcc_assert (model != 0);
+
+      tmp = rs6000_legitimize_tls_address (tmp, model);
+      if (addend)
+       {
+         tmp = gen_rtx_PLUS (mode, tmp, addend);
+         tmp = force_operand (tmp, operands[0]);
+       }
+      operands[1] = tmp;
     }
 
   /* Handle the case where reload calls us with an invalid address.  */
@@ -3854,7 +4080,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
    returned in memory.  The Darwin ABI does the same.  The SVR4 ABI
    specifies that structures <= 8 bytes are returned in r3/r4, but a
    draft put them in memory, and GCC used to implement the draft
-   instead of the final standard.  Therefore, TARGET_AIX_STRUCT_RET
+   instead of the final standard.  Therefore, aix_struct_return
    controls this instead of DEFAULT_ABI; V.4 targets needing backward
    compatibility can change DRAFT_V4_STRUCT_RET to override the
    default, and -m switches get the final word.  See
@@ -3890,7 +4116,7 @@ rs6000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
     }
 
   if (AGGREGATE_TYPE_P (type)
-      && (TARGET_AIX_STRUCT_RET
+      && (aix_struct_return
          || (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8))
     return true;
 
@@ -3980,9 +4206,9 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
       && TARGET_ALTIVEC_ABI
       && ALTIVEC_VECTOR_MODE (TYPE_MODE (TREE_TYPE (fntype))))
     {
-      error ("Cannot return value in vector register because"
+      error ("cannot return value in vector register because"
             " altivec instructions are disabled, use -maltivec"
-            " to enable them.");
+            " to enable them");
     }
 }
 \f
@@ -4226,9 +4452,9 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
        {
          cum->vregno++;
          if (!TARGET_ALTIVEC)
-           error ("Cannot pass argument in vector register because"
+           error ("cannot pass argument in vector register because"
                   " altivec instructions are disabled, use -maltivec"
-                  " to enable them.");
+                  " to enable them");
 
          /* PowerPC64 Linux and AIX allocate GPRs for a vector argument
             even if it is going to be passed in a vector register.
@@ -4294,7 +4520,7 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
             grovel through the fields for these too.  */
          cum->intoffset = 0;
          rs6000_darwin64_record_arg_advance_recurse (cum, type, 0);
-         rs6000_darwin64_record_arg_advance_flush (cum, 
+         rs6000_darwin64_record_arg_advance_flush (cum,
                                                    size * BITS_PER_UNIT);
        }
     }
@@ -4456,7 +4682,7 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
    structure between cum->intoffset and bitpos to integer registers.  */
 
 static void
-rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum, 
+rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum,
                                  HOST_WIDE_INT bitpos, rtx rvec[], int *k)
 {
   enum machine_mode mode;
@@ -4500,7 +4726,7 @@ rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum,
 
   if (intregs > 0 && intregs > GP_ARG_NUM_REG - this_regno)
     cum->use_stack = 1;
-    
+
   intregs = MIN (intregs, GP_ARG_NUM_REG - this_regno);
   if (intregs <= 0)
     return;
@@ -4524,7 +4750,7 @@ rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *cum,
 /* Recursive workhorse for the following.  */
 
 static void
-rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type, 
+rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
                                    HOST_WIDE_INT startbitpos, rtx rvec[],
                                    int *k)
 {
@@ -4558,7 +4784,7 @@ rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
 #endif
            rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
            rvec[(*k)++]
-             = gen_rtx_EXPR_LIST (VOIDmode, 
+             = gen_rtx_EXPR_LIST (VOIDmode,
                                   gen_rtx_REG (mode, cum->fregno++),
                                   GEN_INT (bitpos / BITS_PER_UNIT));
            if (mode == TFmode)
@@ -4568,8 +4794,8 @@ rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
          {
            rs6000_darwin64_record_arg_flush (cum, bitpos, rvec, k);
            rvec[(*k)++]
-             = gen_rtx_EXPR_LIST (VOIDmode, 
-                                  gen_rtx_REG (mode, cum->vregno++), 
+             = gen_rtx_EXPR_LIST (VOIDmode,
+                                  gen_rtx_REG (mode, cum->vregno++),
                                   GEN_INT (bitpos / BITS_PER_UNIT));
          }
        else if (cum->intoffset == -1)
@@ -4582,12 +4808,12 @@ rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
    being passed by value, along with the offset of where the
    register's value may be found in the block.  FP fields go in FP
    register, vector fields go in vector registers, and everything
-   else goes in int registers, packed as in memory.  
+   else goes in int registers, packed as in memory.
 
    This code is also used for function return values.  RETVAL indicates
    whether this is the case.
 
-   Much of this is taken from the Sparc V9 port, which has a similar
+   Much of this is taken from the SPARC V9 port, which has a similar
    calling convention.  */
 
 static rtx
@@ -4725,9 +4951,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   if (mode == VOIDmode)
     {
       if (abi == ABI_V4
-         && cum->nargs_prototype < 0
          && (cum->call_cookie & CALL_LIBCALL) == 0
-         && (cum->prototype || TARGET_NO_PROTOTYPE))
+         && (cum->stdarg
+             || (cum->nargs_prototype < 0
+                 && (cum->prototype || TARGET_NO_PROTOTYPE))))
        {
          /* For the SPE, we need to crxor CR6 always.  */
          if (TARGET_SPE_ABI)
@@ -4994,7 +5221,7 @@ rs6000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       /* If we are passing this arg in the fixed parameter save area
         (gprs or memory) as well as fprs, then this function should
         return the number of bytes passed in the parameter save area
-        rather than bytes passed in fprs.  */ 
+        rather than bytes passed in fprs.  */
       && !(type
           && (cum->nargs_prototype <= 0
               || (DEFAULT_ABI == ABI_AIX
@@ -5147,11 +5374,70 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   if (DEFAULT_ABI == ABI_V4)
     {
+      first_reg_offset = next_cum.sysv_gregno - GP_ARG_MIN_REG;
+
       if (! no_rtl)
-       save_area = plus_constant (virtual_stack_vars_rtx,
-                                  - RS6000_VARARGS_SIZE);
+       {
+         int gpr_reg_num = 0, gpr_size = 0, fpr_size = 0;
+         HOST_WIDE_INT offset = 0;
+
+         /* Try to optimize the size of the varargs save area.
+            The ABI requires that ap.reg_save_area is doubleword
+            aligned, but we don't need to allocate space for all
+            the bytes, only those to which we actually will save
+            anything.  */
+         if (cfun->va_list_gpr_size && first_reg_offset < GP_ARG_NUM_REG)
+           gpr_reg_num = GP_ARG_NUM_REG - first_reg_offset;
+         if (TARGET_HARD_FLOAT && TARGET_FPRS
+             && next_cum.fregno <= FP_ARG_V4_MAX_REG
+             && cfun->va_list_fpr_size)
+           {
+             if (gpr_reg_num)
+               fpr_size = (next_cum.fregno - FP_ARG_MIN_REG)
+                          * UNITS_PER_FP_WORD;
+             if (cfun->va_list_fpr_size
+                 < FP_ARG_V4_MAX_REG + 1 - next_cum.fregno)
+               fpr_size += cfun->va_list_fpr_size * UNITS_PER_FP_WORD;
+             else
+               fpr_size += (FP_ARG_V4_MAX_REG + 1 - next_cum.fregno)
+                           * UNITS_PER_FP_WORD;
+           }
+         if (gpr_reg_num)
+           {
+             offset = -((first_reg_offset * reg_size) & ~7);
+             if (!fpr_size && gpr_reg_num > cfun->va_list_gpr_size)
+               {
+                 gpr_reg_num = cfun->va_list_gpr_size;
+                 if (reg_size == 4 && (first_reg_offset & 1))
+                   gpr_reg_num++;
+               }
+             gpr_size = (gpr_reg_num * reg_size + 7) & ~7;
+           }
+         else if (fpr_size)
+           offset = - (int) (next_cum.fregno - FP_ARG_MIN_REG)
+                      * UNITS_PER_FP_WORD
+                    - (int) (GP_ARG_NUM_REG * reg_size);
 
-      first_reg_offset = next_cum.sysv_gregno - GP_ARG_MIN_REG;
+         if (gpr_size + fpr_size)
+           {
+             rtx reg_save_area
+               = assign_stack_local (BLKmode, gpr_size + fpr_size, 64);
+             gcc_assert (GET_CODE (reg_save_area) == MEM);
+             reg_save_area = XEXP (reg_save_area, 0);
+             if (GET_CODE (reg_save_area) == PLUS)
+               {
+                 gcc_assert (XEXP (reg_save_area, 0)
+                             == virtual_stack_vars_rtx);
+                 gcc_assert (GET_CODE (XEXP (reg_save_area, 1)) == CONST_INT);
+                 offset += INTVAL (XEXP (reg_save_area, 1));
+               }
+             else
+               gcc_assert (reg_save_area == virtual_stack_vars_rtx);
+           }
+
+         cfun->machine->varargs_save_offset = offset;
+         save_area = plus_constant (virtual_stack_vars_rtx, offset);
+       }
     }
   else
     {
@@ -5183,7 +5469,8 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
       mem = gen_rtx_MEM (BLKmode,
                         plus_constant (save_area,
-                                       first_reg_offset * reg_size)),
+                                       first_reg_offset * reg_size));
+      MEM_NOTRAP_P (mem) = 1;
       set_mem_alias_set (mem, set);
       set_mem_align (mem, BITS_PER_WORD);
 
@@ -5201,7 +5488,8 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       int fregno = next_cum.fregno, nregs;
       rtx cr1 = gen_rtx_REG (CCmode, CR1_REGNO);
       rtx lab = gen_label_rtx ();
-      int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
+      int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG)
+                                              * UNITS_PER_FP_WORD);
 
       emit_jump_insn
        (gen_rtx_SET (VOIDmode,
@@ -5214,9 +5502,10 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
       for (nregs = 0;
           fregno <= FP_ARG_V4_MAX_REG && nregs < cfun->va_list_fpr_size;
-          fregno++, off += 8, nregs++)
+          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));
@@ -5352,8 +5641,9 @@ rs6000_va_start (tree valist, rtx nextarg)
 
   /* Find the register save area.  */
   t = make_tree (TREE_TYPE (sav), virtual_stack_vars_rtx);
-  t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_cst (NULL_TREE, -RS6000_VARARGS_SIZE));
+  if (cfun->machine->varargs_save_offset)
+    t = build (PLUS_EXPR, TREE_TYPE (sav), t,
+              build_int_cst (NULL_TREE, cfun->machine->varargs_save_offset));
   t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -6183,8 +6473,8 @@ rs6000_expand_unop_builtin (enum insn_code icode, tree arglist, rtx target)
     {
       /* Only allow 5-bit *signed* literals.  */
       if (GET_CODE (op0) != CONST_INT
-         || INTVAL (op0) > 0x1f
-         || INTVAL (op0) < -0x1f)
+         || INTVAL (op0) > 15
+         || INTVAL (op0) < -16)
        {
          error ("argument 1 must be a 5-bit signed literal");
          return const0_rtx;
@@ -6735,6 +7025,111 @@ altivec_expand_dst_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
   return NULL_RTX;
 }
 
+/* Expand vec_init builtin.  */
+static rtx
+altivec_expand_vec_init_builtin (tree type, tree arglist, rtx target)
+{
+  enum machine_mode tmode = TYPE_MODE (type);
+  enum machine_mode inner_mode = GET_MODE_INNER (tmode);
+  int i, n_elt = GET_MODE_NUNITS (tmode);
+  rtvec v = rtvec_alloc (n_elt);
+
+  gcc_assert (VECTOR_MODE_P (tmode));
+
+  for (i = 0; i < n_elt; ++i, arglist = TREE_CHAIN (arglist))
+    {
+      rtx x = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+      RTVEC_ELT (v, i) = gen_lowpart (inner_mode, x);
+    }
+
+  gcc_assert (arglist == NULL);
+
+  if (!target || !register_operand (target, tmode))
+    target = gen_reg_rtx (tmode);
+
+  rs6000_expand_vector_init (target, gen_rtx_PARALLEL (tmode, v));
+  return target;
+}
+
+/* Return the integer constant in ARG.  Constrain it to be in the range
+   of the subparts of VEC_TYPE; issue an error if not.  */
+
+static int
+get_element_number (tree vec_type, tree arg)
+{
+  unsigned HOST_WIDE_INT elt, max = TYPE_VECTOR_SUBPARTS (vec_type) - 1;
+
+  if (!host_integerp (arg, 1)
+      || (elt = tree_low_cst (arg, 1), elt > max))
+    {
+      error ("selector must be an integer constant in the range 0..%wi", max);
+      return 0;
+    }
+
+  return elt;
+}
+
+/* Expand vec_set builtin.  */
+static rtx
+altivec_expand_vec_set_builtin (tree arglist)
+{
+  enum machine_mode tmode, mode1;
+  tree arg0, arg1, arg2;
+  int elt;
+  rtx op0, op1;
+
+  arg0 = TREE_VALUE (arglist);
+  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+  arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+  tmode = TYPE_MODE (TREE_TYPE (arg0));
+  mode1 = TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0)));
+  gcc_assert (VECTOR_MODE_P (tmode));
+
+  op0 = expand_expr (arg0, NULL_RTX, tmode, 0);
+  op1 = expand_expr (arg1, NULL_RTX, mode1, 0);
+  elt = get_element_number (TREE_TYPE (arg0), arg2);
+
+  if (GET_MODE (op1) != mode1 && GET_MODE (op1) != VOIDmode)
+    op1 = convert_modes (mode1, GET_MODE (op1), op1, true);
+
+  op0 = force_reg (tmode, op0);
+  op1 = force_reg (mode1, op1);
+
+  rs6000_expand_vector_set (op0, op1, elt);
+
+  return op0;
+}
+
+/* Expand vec_ext builtin.  */
+static rtx
+altivec_expand_vec_ext_builtin (tree arglist, rtx target)
+{
+  enum machine_mode tmode, mode0;
+  tree arg0, arg1;
+  int elt;
+  rtx op0;
+
+  arg0 = TREE_VALUE (arglist);
+  arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+  op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+  elt = get_element_number (TREE_TYPE (arg0), arg1);
+
+  tmode = TYPE_MODE (TREE_TYPE (TREE_TYPE (arg0)));
+  mode0 = TYPE_MODE (TREE_TYPE (arg0));
+  gcc_assert (VECTOR_MODE_P (mode0));
+
+  op0 = force_reg (mode0, op0);
+
+  if (optimize || !target || !register_operand (target, tmode))
+    target = gen_reg_rtx (tmode);
+
+  rs6000_expand_vector_extract (target, op0, elt);
+
+  return target;
+}
+
 /* Expand the builtin in EXP and store the result in TARGET.  Store
    true in *EXPANDEDP if we found a builtin to expand.  */
 static rtx
@@ -6755,7 +7150,7 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
       && fcode <= ALTIVEC_BUILTIN_OVERLOADED_LAST)
     {
       *expandedp = true;
-      error ("unresolved overload for Altivec builtin %qE", fndecl);
+      error ("unresolved overload for Altivec builtin %qF", fndecl);
       return const0_rtx;
     }
 
@@ -6846,6 +7241,28 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
 
       emit_insn (gen_altivec_dss (op0));
       return NULL_RTX;
+
+    case ALTIVEC_BUILTIN_VEC_INIT_V4SI:
+    case ALTIVEC_BUILTIN_VEC_INIT_V8HI:
+    case ALTIVEC_BUILTIN_VEC_INIT_V16QI:
+    case ALTIVEC_BUILTIN_VEC_INIT_V4SF:
+      return altivec_expand_vec_init_builtin (TREE_TYPE (exp), arglist, target);
+
+    case ALTIVEC_BUILTIN_VEC_SET_V4SI:
+    case ALTIVEC_BUILTIN_VEC_SET_V8HI:
+    case ALTIVEC_BUILTIN_VEC_SET_V16QI:
+    case ALTIVEC_BUILTIN_VEC_SET_V4SF:
+      return altivec_expand_vec_set_builtin (arglist);
+
+    case ALTIVEC_BUILTIN_VEC_EXT_V4SI:
+    case ALTIVEC_BUILTIN_VEC_EXT_V8HI:
+    case ALTIVEC_BUILTIN_VEC_EXT_V16QI:
+    case ALTIVEC_BUILTIN_VEC_EXT_V4SF:
+      return altivec_expand_vec_ext_builtin (arglist, target);
+
+    default:
+      break;
+      /* Fall through.  */
     }
 
   /* Expand abs* operations.  */
@@ -7293,13 +7710,13 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
     }
 
   gcc_assert (TARGET_ALTIVEC || TARGET_SPE);
-  
+
   /* Handle simple unary operations.  */
   d = (struct builtin_description *) bdesc_1arg;
   for (i = 0; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
     if (d->code == fcode)
       return rs6000_expand_unop_builtin (d->icode, arglist, target);
-  
+
   /* Handle simple binary operations.  */
   d = (struct builtin_description *) bdesc_2arg;
   for (i = 0; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
@@ -7311,7 +7728,7 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   for (i = 0; i < ARRAY_SIZE  (bdesc_3arg); i++, d++)
     if (d->code == fcode)
       return rs6000_expand_ternop_builtin (d->icode, arglist, target);
-  
+
   gcc_unreachable ();
 }
 
@@ -7679,6 +8096,8 @@ altivec_init_builtins (void)
   struct builtin_description *d;
   struct builtin_description_predicates *dp;
   size_t i;
+  tree ftype;
+
   tree pfloat_type_node = build_pointer_type (float_type_node);
   tree pint_type_node = build_pointer_type (integer_type_node);
   tree pshort_type_node = build_pointer_type (short_integer_type_node);
@@ -7944,6 +8363,88 @@ altivec_init_builtins (void)
       /* Record the decl. Will be used by rs6000_builtin_mask_for_load.  */
       altivec_builtin_mask_for_load = decl;
     }
+
+  /* Access to the vec_init patterns.  */
+  ftype = build_function_type_list (V4SI_type_node, integer_type_node,
+                                   integer_type_node, integer_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v4si", ftype,
+              ALTIVEC_BUILTIN_VEC_INIT_V4SI);
+
+  ftype = build_function_type_list (V8HI_type_node, short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node,
+                                   short_integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v8hi", ftype,
+              ALTIVEC_BUILTIN_VEC_INIT_V8HI);
+
+  ftype = build_function_type_list (V16QI_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, char_type_node,
+                                   char_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v16qi", ftype,
+              ALTIVEC_BUILTIN_VEC_INIT_V16QI);
+
+  ftype = build_function_type_list (V4SF_type_node, float_type_node,
+                                   float_type_node, float_type_node,
+                                   float_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_init_v4sf", ftype,
+              ALTIVEC_BUILTIN_VEC_INIT_V4SF);
+
+  /* Access to the vec_set patterns.  */
+  ftype = build_function_type_list (V4SI_type_node, V4SI_type_node,
+                                   intSI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v4si", ftype,
+              ALTIVEC_BUILTIN_VEC_SET_V4SI);
+
+  ftype = build_function_type_list (V8HI_type_node, V8HI_type_node,
+                                   intHI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v8hi", ftype,
+              ALTIVEC_BUILTIN_VEC_SET_V8HI);
+
+  ftype = build_function_type_list (V8HI_type_node, V16QI_type_node,
+                                   intQI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v16qi", ftype,
+              ALTIVEC_BUILTIN_VEC_SET_V16QI);
+
+  ftype = build_function_type_list (V4SF_type_node, V4SF_type_node,
+                                   float_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_set_v4sf", ftype,
+              ALTIVEC_BUILTIN_VEC_SET_V4SF);
+
+  /* Access to the vec_extract patterns.  */
+  ftype = build_function_type_list (intSI_type_node, V4SI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v4si", ftype,
+              ALTIVEC_BUILTIN_VEC_EXT_V4SI);
+
+  ftype = build_function_type_list (intHI_type_node, V8HI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v8hi", ftype,
+              ALTIVEC_BUILTIN_VEC_EXT_V8HI);
+
+  ftype = build_function_type_list (intQI_type_node, V16QI_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v16qi", ftype,
+              ALTIVEC_BUILTIN_VEC_EXT_V16QI);
+
+  ftype = build_function_type_list (float_type_node, V4SF_type_node,
+                                   integer_type_node, NULL_TREE);
+  def_builtin (MASK_ALTIVEC, "__builtin_vec_ext_v4sf", ftype,
+              ALTIVEC_BUILTIN_VEC_EXT_V4SF);
 }
 
 static void
@@ -8171,7 +8672,7 @@ rs6000_common_init_builtins (void)
           mode2 = insn_data[d->icode].operand[2].mode;
           mode3 = insn_data[d->icode].operand[3].mode;
        }
-      
+
       /* When all four are of the same mode.  */
       if (mode0 == mode1 && mode1 == mode2 && mode2 == mode3)
        {
@@ -8268,7 +8769,7 @@ rs6000_common_init_builtins (void)
          mode2 = VOIDmode;
        }
       else
-       { 
+       {
           if (d->name == 0 || d->icode == CODE_FOR_nothing)
            continue;
 
@@ -8418,7 +8919,7 @@ rs6000_common_init_builtins (void)
         {
           if (d->name == 0 || d->icode == CODE_FOR_nothing)
            continue;
-      
+
           mode0 = insn_data[d->icode].operand[0].mode;
           mode1 = insn_data[d->icode].operand[1].mode;
         }
@@ -8522,14 +9023,14 @@ rs6000_init_libfuncs (void)
 
    operands[0] is the destination
    operands[1] is the length
-   operands[2] is the alignment */
+   operands[3] is the alignment */
 
 int
 expand_block_clear (rtx operands[])
 {
   rtx orig_dest = operands[0];
   rtx bytes_rtx        = operands[1];
-  rtx align_rtx = operands[2];
+  rtx align_rtx = operands[3];
   bool constp  = (GET_CODE (bytes_rtx) == CONST_INT);
   HOST_WIDE_INT align;
   HOST_WIDE_INT bytes;
@@ -8589,7 +9090,7 @@ expand_block_clear (rtx operands[])
          clear_bytes = 4;
          mode = SImode;
        }
-      else if (bytes == 2 && (align >= 16 || !STRICT_ALIGNMENT))
+      else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT))
        {                       /* move 2 bytes */
          clear_bytes = 2;
          mode = HImode;
@@ -8725,7 +9226,7 @@ expand_block_move (rtx operands[])
          mode = SImode;
          gen_func.mov = gen_movsi;
        }
-      else if (bytes == 2 && (align >= 16 || !STRICT_ALIGNMENT))
+      else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT))
        {                       /* move 2 bytes */
          move_bytes = 2;
          mode = HImode;
@@ -9120,8 +9621,8 @@ int
 mems_ok_for_quad_peep (rtx mem1, rtx mem2)
 {
   rtx addr1, addr2;
-  unsigned int reg1;
-  int offset1;
+  unsigned int reg1, reg2;
+  int offset1, offset2;
 
   /* The mems cannot be volatile.  */
   if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
@@ -9154,23 +9655,36 @@ mems_ok_for_quad_peep (rtx mem1, rtx mem2)
       offset1 = 0;
     }
 
-  /* Make sure the second address is a (mem (plus (reg) (const_int)))
-     or if it is (mem (reg)) then make sure that offset1 is -8 and the same
-     register as addr1.  */
-  if (offset1 == -8 && GET_CODE (addr2) == REG && reg1 == REGNO (addr2))
-    return 1;
-  if (GET_CODE (addr2) != PLUS)
-    return 0;
-
-  if (GET_CODE (XEXP (addr2, 0)) != REG
-      || GET_CODE (XEXP (addr2, 1)) != CONST_INT)
+  /* 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;
+    }
 
-  if (reg1 != REGNO (XEXP (addr2, 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 (INTVAL (XEXP (addr2, 1)) != offset1 + 8)
+  if (offset2 != offset1 + 8)
     return 0;
 
   /* All the tests passed.  addr1 and addr2 are valid for lfq or stfq
@@ -10090,7 +10604,7 @@ print_operand (FILE *file, rtx x, int code)
          {
            gcc_assert (GET_CODE (tmp) == PLUS
                        && GET_CODE (XEXP (tmp, 1)) == REG);
-           
+
            if (REGNO (XEXP (tmp, 0)) == 0)
              fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (tmp, 1)) ],
                       reg_names[ REGNO (XEXP (tmp, 0)) ]);
@@ -10391,7 +10905,7 @@ rs6000_generate_compare (enum rtx_code code)
              gcc_unreachable ();
            }
          break;
-         
+
        case GT: case GTU: case UNGT: case UNGE: case GE: case GEU:
          switch (op_mode)
            {
@@ -10402,7 +10916,7 @@ rs6000_generate_compare (enum rtx_code code)
                : gen_cmpsfgt_gpr (compare_result, rs6000_compare_op0,
                                   rs6000_compare_op1);
              break;
-             
+
            case DFmode:
              cmp = flag_unsafe_math_optimizations
                ? gen_tstdfgt_gpr (compare_result, rs6000_compare_op0,
@@ -10415,7 +10929,7 @@ rs6000_generate_compare (enum rtx_code code)
              gcc_unreachable ();
            }
          break;
-         
+
        case LT: case LTU: case UNLT: case UNLE: case LE: case LEU:
          switch (op_mode)
            {
@@ -10426,7 +10940,7 @@ rs6000_generate_compare (enum rtx_code code)
                : gen_cmpsflt_gpr (compare_result, rs6000_compare_op0,
                                   rs6000_compare_op1);
              break;
-             
+
            case DFmode:
              cmp = flag_unsafe_math_optimizations
                ? gen_tstdflt_gpr (compare_result, rs6000_compare_op0,
@@ -10523,6 +11037,19 @@ rs6000_generate_compare (enum rtx_code code)
                     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
                     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)),
                     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (DFmode)))));
+      else if (GET_CODE (rs6000_compare_op1) == UNSPEC
+              && XINT (rs6000_compare_op1, 1) == UNSPEC_SP_TEST)
+       {
+         rtx op1 = XVECEXP (rs6000_compare_op1, 0, 0);
+         comp_mode = CCEQmode;
+         compare_result = gen_reg_rtx (CCEQmode);
+         if (TARGET_64BIT)
+           emit_insn (gen_stack_protect_testdi (compare_result,
+                                                rs6000_compare_op0, op1));
+         else
+           emit_insn (gen_stack_protect_testsi (compare_result,
+                                                rs6000_compare_op0, op1));
+       }
       else
        emit_insn (gen_rtx_SET (VOIDmode, compare_result,
                                gen_rtx_COMPARE (comp_mode,
@@ -11037,7 +11564,7 @@ rs6000_emit_vector_select (rtx dest, rtx op1, rtx op2, rtx mask)
 
   temp = gen_reg_rtx (dest_mode);
 
-  /* For each vector element, select op1 when mask is 1 otherwise 
+  /* For each vector element, select op1 when mask is 1 otherwise
      select op2.  */
   t = gen_rtx_SET (VOIDmode, temp,
                   gen_rtx_UNSPEC (dest_mode,
@@ -11327,7 +11854,7 @@ rs6000_emit_minmax (rtx dest, enum rtx_code code, rtx op0, rtx op1)
    (set M (CODE:MODE M OP))
    If not NULL, BEFORE is atomically set to M before the operation, and
    AFTER is set to M after the operation (that is, (CODE:MODE M OP)).
-   If SYNC_P then a memory barrier is emitted before the operation.  
+   If SYNC_P then a memory barrier is emitted before the operation.
    Either OP or M may be wrapped in a NOT operation.  */
 
 void
@@ -11341,10 +11868,10 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
   rtvec vec;
   HOST_WIDE_INT imask = GET_MODE_MASK (mode);
   rtx shift = NULL_RTX;
-  
+
   if (sync_p)
     emit_insn (gen_memory_barrier ());
-  
+
   if (GET_CODE (m) == NOT)
     used_m = XEXP (m, 0);
   else
@@ -11361,19 +11888,21 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
          int ishift = 0;
          if (BYTES_BIG_ENDIAN)
            ishift = GET_MODE_BITSIZE (SImode) - GET_MODE_BITSIZE (mode);
-         
+
          shift = GEN_INT (ishift);
        }
       else
        {
          rtx addrSI, aligned_addr;
-         
+         int shift_mask = mode == QImode ? 0x18 : 0x10;
+
          addrSI = force_reg (SImode, gen_lowpart_common (SImode,
                                                          XEXP (used_m, 0)));
          shift = gen_reg_rtx (SImode);
 
          emit_insn (gen_rlwinm (shift, addrSI, GEN_INT (3),
-                                GEN_INT (0x18)));
+                                GEN_INT (shift_mask)));
+         emit_insn (gen_xorsi3 (shift, shift, GEN_INT (shift_mask)));
 
          aligned_addr = expand_binop (Pmode, and_optab,
                                       XEXP (used_m, 0),
@@ -11397,6 +11926,7 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
        }
       else
        oldop = lowpart_subreg (SImode, op, mode);
+
       switch (code)
        {
        case IOR:
@@ -11411,13 +11941,14 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
          newop = expand_binop (SImode, ior_optab,
                                oldop, GEN_INT (~imask), NULL_RTX,
                                1, OPTAB_LIB_WIDEN);
-         emit_insn (gen_ashlsi3 (newop, newop, shift));
+         emit_insn (gen_rotlsi3 (newop, newop, shift));
          break;
 
        case PLUS:
+       case MINUS:
          {
            rtx mask;
-           
+
            newop = expand_binop (SImode, and_optab,
                                  oldop, GEN_INT (imask), NULL_RTX,
                                  1, OPTAB_LIB_WIDEN);
@@ -11427,8 +11958,11 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
            emit_move_insn (mask, GEN_INT (imask));
            emit_insn (gen_ashlsi3 (mask, mask, shift));
 
-           newop = gen_rtx_AND (SImode, gen_rtx_PLUS (SImode, m, newop),
-                                mask);
+           if (code == PLUS)
+             newop = gen_rtx_PLUS (SImode, m, newop);
+           else
+             newop = gen_rtx_MINUS (SImode, m, newop);
+           newop = gen_rtx_AND (SImode, newop, mask);
            newop = gen_rtx_IOR (SImode, newop,
                                 gen_rtx_AND (SImode,
                                              gen_rtx_NOT (SImode, mask),
@@ -11440,6 +11974,19 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
          gcc_unreachable ();
        }
 
+      if (GET_CODE (m) == NOT)
+       {
+         rtx mask, xorm;
+
+         mask = gen_reg_rtx (SImode);
+         emit_move_insn (mask, GEN_INT (imask));
+         emit_insn (gen_ashlsi3 (mask, mask, shift));
+
+         xorm = gen_rtx_XOR (SImode, used_m, mask);
+         /* Depending on the value of 'op', the XOR or the operation might
+            be able to be simplified away.  */
+         newop = simplify_gen_binary (code, SImode, xorm, newop);
+       }
       op = newop;
       used_mode = SImode;
       before = gen_reg_rtx (used_mode);
@@ -11456,8 +12003,9 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
       if (after == NULL_RTX)
        after = gen_reg_rtx (used_mode);
     }
-  
-  if (code == PLUS && used_mode != mode)
+
+  if ((code == PLUS || code == MINUS || GET_CODE (m) == NOT)
+      && used_mode != mode)
     the_op = op;  /* Computed above.  */
   else if (GET_CODE (op) == NOT && GET_CODE (m) != NOT)
     the_op = gen_rtx_fmt_ee (code, used_mode, op, m);
@@ -11467,11 +12015,12 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
   set_after = gen_rtx_SET (VOIDmode, after, the_op);
   set_before = gen_rtx_SET (VOIDmode, before, used_m);
   set_atomic = gen_rtx_SET (VOIDmode, used_m,
-                           gen_rtx_UNSPEC (used_mode, gen_rtvec (1, the_op),
+                           gen_rtx_UNSPEC (used_mode,
+                                           gen_rtvec (1, the_op),
                                            UNSPEC_SYNC_OP));
   cc_scratch = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (CCmode));
 
-  if (code == PLUS && used_mode != mode)
+  if ((code == PLUS || code == MINUS) && used_mode != mode)
     vec = gen_rtvec (5, set_after, set_before, set_atomic, cc_scratch,
                     gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode)));
   else
@@ -11499,7 +12048,155 @@ rs6000_emit_sync (enum rtx_code code, enum machine_mode mode,
     emit_insn (gen_isync ());
 }
 
-/* Emit instructions to move SRC to DST.  Called by splitters for
+/* A subroutine of the atomic operation splitters.  Jump to LABEL if
+   COND is true.  Mark the jump as unlikely to be taken.  */
+
+static void
+emit_unlikely_jump (rtx cond, rtx label)
+{
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+  rtx x;
+
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, label, pc_rtx);
+  x = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, x));
+  REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_BR_PROB, very_unlikely, NULL_RTX);
+}
+
+/* A subroutine of the atomic operation splitters.  Emit a load-locked
+   instruction in MODE.  */
+
+static void
+emit_load_locked (enum machine_mode mode, rtx reg, rtx mem)
+{
+  rtx (*fn) (rtx, rtx) = NULL;
+  if (mode == SImode)
+    fn = gen_load_locked_si;
+  else if (mode == DImode)
+    fn = gen_load_locked_di;
+  emit_insn (fn (reg, mem));
+}
+
+/* A subroutine of the atomic operation splitters.  Emit a store-conditional
+   instruction in MODE.  */
+
+static void
+emit_store_conditional (enum machine_mode mode, rtx res, rtx mem, rtx val)
+{
+  rtx (*fn) (rtx, rtx, rtx) = NULL;
+  if (mode == SImode)
+    fn = gen_store_conditional_si;
+  else if (mode == DImode)
+    fn = gen_store_conditional_di;
+
+  /* Emit sync before stwcx. to address PPC405 Erratum.  */
+  if (PPC405_ERRATUM77)
+    emit_insn (gen_memory_barrier ());
+
+  emit_insn (fn (res, mem, val));
+}
+
+/* Expand an an atomic fetch-and-operate pattern.  CODE is the binary operation
+   to perform.  MEM is the memory on which to operate.  VAL is the second
+   operand of the binary operator.  BEFORE and AFTER are optional locations to
+   return the value of MEM either before of after the operation.  SCRATCH is
+   a scratch register.  */
+
+void
+rs6000_split_atomic_op (enum rtx_code code, rtx mem, rtx val,
+                       rtx before, rtx after, 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_label_rtx ();
+  emit_label (label);
+  label = gen_rtx_LABEL_REF (VOIDmode, label);
+
+  if (before == NULL_RTX)
+    before = scratch;
+  emit_load_locked (mode, before, mem);
+
+  if (code == NOT)
+    x = gen_rtx_AND (mode, gen_rtx_NOT (mode, before), val);
+  else if (code == AND)
+    x = gen_rtx_UNSPEC (mode, gen_rtvec (2, before, val), UNSPEC_AND);
+  else
+    x = gen_rtx_fmt_ee (code, mode, before, val);
+
+  if (after != NULL_RTX)
+    emit_insn (gen_rtx_SET (VOIDmode, after, copy_rtx (x)));
+  emit_insn (gen_rtx_SET (VOIDmode, scratch, x));
+
+  emit_store_conditional (mode, cond, mem, scratch);
+
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  emit_unlikely_jump (x, label);
+
+  emit_insn (gen_isync ());
+}
+
+/* Expand an atomic compare and swap operation.  MEM is the memory on which
+   to operate.  OLDVAL is the old value to be compared.  NEWVAL is the new
+   value to be stored.  SCRATCH is a scratch GPR.  */
+
+void
+rs6000_split_compare_and_swap (rtx retval, rtx mem, rtx oldval, rtx newval,
+                              rtx scratch)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  rtx label1, label2, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
+
+  emit_insn (gen_memory_barrier ());
+
+  label1 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+  label2 = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+  emit_label (XEXP (label1, 0));
+
+  emit_load_locked (mode, retval, mem);
+
+  x = gen_rtx_COMPARE (CCmode, retval, oldval);
+  emit_insn (gen_rtx_SET (VOIDmode, cond, x));
+
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  emit_unlikely_jump (x, label2);
+
+  emit_move_insn (scratch, newval);
+  emit_store_conditional (mode, cond, mem, scratch);
+
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  emit_unlikely_jump (x, label1);
+
+  emit_insn (gen_isync ());
+  emit_label (XEXP (label2, 0));
+}
+
+/* Expand an atomic test and set operation.  MEM is the memory on which
+   to operate.  VAL is the value set.  SCRATCH is a scratch GPR.  */
+
+void
+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));
+
+  emit_load_locked (mode, retval, mem);
+  emit_move_insn (scratch, val);
+  emit_store_conditional (mode, cond, mem, scratch);
+
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  emit_unlikely_jump (x, label);
+
+  emit_insn (gen_isync ());
+}
+
+  /* Emit instructions to move SRC to DST.  Called by splitters for
    multi-register moves.  It will emit at most one instruction for
    each register that is accessed; that is, it won't emit li/lis pairs
    (or equivalent for 64-bit code).  One of SRC or DST must be a hard
@@ -11520,7 +12217,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
 
   reg = REG_P (dst) ? REGNO (dst) : REGNO (src);
   mode = GET_MODE (dst);
-  nregs = HARD_REGNO_NREGS (reg, mode);
+  nregs = hard_regno_nregs[reg][mode];
   if (FP_REGNO_P (reg))
     reg_mode = DFmode;
   else if (ALTIVEC_REGNO_P (reg))
@@ -11564,16 +12261,14 @@ rs6000_split_multireg_move (rtx dst, rtx src)
              emit_insn (TARGET_32BIT
                         ? gen_addsi3 (breg, breg, delta_rtx)
                         : gen_adddi3 (breg, breg, delta_rtx));
-             src = gen_rtx_MEM (mode, breg);
+             src = replace_equiv_address (src, breg);
            }
          else if (! offsettable_memref_p (src))
            {
-             rtx newsrc, basereg;
+             rtx basereg;
              basereg = gen_rtx_REG (Pmode, reg);
              emit_insn (gen_rtx_SET (VOIDmode, basereg, XEXP (src, 0)));
-             newsrc = gen_rtx_MEM (GET_MODE (src), basereg);
-             MEM_COPY_ATTRIBUTES (newsrc, src);
-             src = newsrc;
+             src = replace_equiv_address (src, basereg);
            }
 
          breg = XEXP (src, 0);
@@ -11618,7 +12313,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
                emit_insn (TARGET_32BIT
                           ? gen_addsi3 (breg, breg, delta_rtx)
                           : gen_adddi3 (breg, breg, delta_rtx));
-             dst = gen_rtx_MEM (mode, breg);
+             dst = replace_equiv_address (dst, breg);
            }
          else
            gcc_assert (offsettable_memref_p (dst));
@@ -11981,9 +12676,6 @@ rs6000_stack_info (void)
          && !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_DARWIN
-         && flag_pic
-         && current_function_uses_pic_offset_table)
       || info_ptr->calls_p)
     {
       info_ptr->lr_save_p = 1;
@@ -12020,10 +12712,16 @@ rs6000_stack_info (void)
   /* Determine various sizes.  */
   info_ptr->reg_size     = reg_size;
   info_ptr->fixed_size   = RS6000_SAVE_AREA;
-  info_ptr->varargs_size = RS6000_VARARGS_AREA;
   info_ptr->vars_size    = RS6000_ALIGN (get_frame_size (), 8);
   info_ptr->parm_size    = RS6000_ALIGN (current_function_outgoing_args_size,
                                         TARGET_ALTIVEC ? 16 : 8);
+  if (FRAME_GROWS_DOWNWARD)
+    info_ptr->vars_size
+      += RS6000_ALIGN (info_ptr->fixed_size + info_ptr->vars_size
+                      + info_ptr->parm_size,
+                      ABI_STACK_BOUNDARY / BITS_PER_UNIT)
+        - (info_ptr->fixed_size + info_ptr->vars_size
+           + info_ptr->parm_size);
 
   if (TARGET_SPE_ABI && info_ptr->spe_64bit_regs_used != 0)
     info_ptr->spe_gp_size = 8 * (32 - info_ptr->first_gp_reg_save);
@@ -12148,8 +12846,7 @@ rs6000_stack_info (void)
 
   non_fixed_size        = (info_ptr->vars_size
                            + info_ptr->parm_size
-                           + info_ptr->save_size
-                           + info_ptr->varargs_size);
+                           + info_ptr->save_size);
 
   info_ptr->total_size = RS6000_ALIGN (non_fixed_size + info_ptr->fixed_size,
                                       ABI_STACK_BOUNDARY / BITS_PER_UNIT);
@@ -12349,9 +13046,6 @@ debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\ttotal_size          = "HOST_WIDE_INT_PRINT_DEC"\n",
             info->total_size);
 
-  if (info->varargs_size)
-    fprintf (stderr, "\tvarargs_size        = %5d\n", info->varargs_size);
-
   if (info->vars_size)
     fprintf (stderr, "\tvars_size           = "HOST_WIDE_INT_PRINT_DEC"\n",
             info->vars_size);
@@ -12463,6 +13157,24 @@ rs6000_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
   return false;
 }
 
+/* NULL if INSN insn is valid within a low-overhead loop.
+   Otherwise return why doloop cannot be applied.
+   PowerPC uses the COUNT register for branch on table instructions.  */
+
+static const char *
+rs6000_invalid_within_doloop (rtx insn)
+{
+  if (CALL_P (insn))
+    return "Function call in the loop.";
+
+  if (JUMP_P (insn)
+      && (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+         || GET_CODE (PATTERN (insn)) == ADDR_VEC))
+    return "Computed branch in the loop.";
+
+  return NULL;
+}
+
 static int
 rs6000_ra_ever_killed (void)
 {
@@ -12530,15 +13242,49 @@ rs6000_emit_load_toc_table (int fromprolog)
   rtx dest, insn;
   dest = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM);
 
-  if (TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 1)
+  if (TARGET_ELF && TARGET_SECURE_PLT && DEFAULT_ABI != ABI_AIX && flag_pic)
     {
-      rtx temp = (fromprolog
-                 ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)
-                 : gen_reg_rtx (Pmode));
-      insn = emit_insn (gen_load_toc_v4_pic_si (temp));
+      char buf[30];
+      rtx lab, tmp1, tmp2, got, tempLR;
+
+      ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+      lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
+      if (flag_pic == 2)
+       got = gen_rtx_SYMBOL_REF (Pmode, toc_label_name);
+      else
+       got = rs6000_got_sym ();
+      tmp1 = tmp2 = dest;
+      if (!fromprolog)
+       {
+         tmp1 = gen_reg_rtx (Pmode);
+         tmp2 = gen_reg_rtx (Pmode);
+       }
+      tempLR = (fromprolog
+               ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)
+               : gen_reg_rtx (Pmode));
+      insn = emit_insn (gen_load_toc_v4_PIC_1 (tempLR, lab));
       if (fromprolog)
        rs6000_maybe_dead (insn);
-      insn = emit_move_insn (dest, temp);
+      insn = emit_move_insn (tmp1, tempLR);
+      if (fromprolog)
+       rs6000_maybe_dead (insn);
+      insn = emit_insn (gen_load_toc_v4_PIC_3b (tmp2, tmp1, got, lab));
+      if (fromprolog)
+       rs6000_maybe_dead (insn);
+      insn = emit_insn (gen_load_toc_v4_PIC_3c (dest, tmp2, got, lab));
+      if (fromprolog)
+       rs6000_maybe_dead (insn);
+    }
+  else if (TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 1)
+    {
+      rtx tempLR = (fromprolog
+                   ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)
+                   : gen_reg_rtx (Pmode));
+
+      insn = emit_insn (gen_load_toc_v4_pic_si (tempLR));
+      if (fromprolog)
+       rs6000_maybe_dead (insn);
+      insn = emit_move_insn (dest, tempLR);
       if (fromprolog)
        rs6000_maybe_dead (insn);
     }
@@ -12600,7 +13346,7 @@ rs6000_emit_load_toc_table (int fromprolog)
   else
     {
       gcc_assert (DEFAULT_ABI == ABI_AIX);
-      
+
       if (TARGET_32BIT)
        insn = emit_insn (gen_load_toc_aix_si (dest));
       else
@@ -12632,7 +13378,10 @@ rs6000_emit_eh_reg_restore (rtx source, rtx scratch)
          || current_function_calls_alloca
          || info->total_size > 32767)
        {
-         emit_move_insn (operands[1], gen_rtx_MEM (Pmode, frame_rtx));
+         tmp = gen_rtx_MEM (Pmode, frame_rtx);
+         MEM_NOTRAP_P (tmp) = 1;
+         set_mem_alias_set (tmp, rs6000_sr_alias_set);
+         emit_move_insn (operands[1], tmp);
          frame_rtx = operands[1];
        }
       else if (info->push_p)
@@ -12640,6 +13389,8 @@ rs6000_emit_eh_reg_restore (rtx source, rtx scratch)
 
       tmp = plus_constant (frame_rtx, info->lr_save_offset + sp_offset);
       tmp = gen_rtx_MEM (Pmode, tmp);
+      MEM_NOTRAP_P (tmp) = 1;
+      set_mem_alias_set (tmp, rs6000_sr_alias_set);
       emit_move_insn (tmp, operands[0]);
     }
   else
@@ -12712,12 +13463,12 @@ rs6000_aix_emit_builtin_unwind_init (void)
   rtx tocompare = gen_reg_rtx (SImode);
   rtx no_toc_save_needed = gen_label_rtx ();
 
-  mem = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx);
+  mem = gen_frame_mem (Pmode, hard_frame_pointer_rtx);
   emit_move_insn (stack_top, mem);
 
-  mem = gen_rtx_MEM (Pmode,
-                    gen_rtx_PLUS (Pmode, stack_top,
-                                  GEN_INT (2 * GET_MODE_SIZE (Pmode))));
+  mem = gen_frame_mem (Pmode,
+                      gen_rtx_PLUS (Pmode, stack_top,
+                                    GEN_INT (2 * GET_MODE_SIZE (Pmode))));
   emit_move_insn (opcode_addr, mem);
   emit_move_insn (opcode, gen_rtx_MEM (SImode, opcode_addr));
   emit_move_insn (tocompare, gen_int_mode (TARGET_32BIT ? 0x80410014
@@ -12727,9 +13478,9 @@ rs6000_aix_emit_builtin_unwind_init (void)
                           SImode, NULL_RTX, NULL_RTX,
                           no_toc_save_needed);
 
-  mem = gen_rtx_MEM (Pmode,
-                    gen_rtx_PLUS (Pmode, stack_top,
-                                  GEN_INT (5 * GET_MODE_SIZE (Pmode))));
+  mem = gen_frame_mem (Pmode,
+                      gen_rtx_PLUS (Pmode, stack_top,
+                                    GEN_INT (5 * GET_MODE_SIZE (Pmode))));
   emit_move_insn (mem, gen_rtx_REG (Pmode, 2));
   emit_label (no_toc_save_needed);
 }
@@ -13002,7 +13753,7 @@ generate_set_vrsave (rtx reg, rs6000_stack_t *info, int epiloguep)
                   vrsave,
                   gen_rtx_UNSPEC_VOLATILE (SImode,
                                            gen_rtvec (2, reg, vrsave),
-                                           30));
+                                           UNSPECV_SET_VRSAVE));
 
   nclobs = 1;
 
@@ -13112,6 +13863,19 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
   return gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode, reg, offset_rtx));
 }
 
+/* Look for user-defined global regs.  We should not save and restore these,
+   and cannot use stmw/lmw if there are any in its range.  */
+
+static bool
+no_global_regs_above (int first_greg)
+{
+  int i;
+  for (i = 0; i < 32 - first_greg; i++)
+    if (global_regs[first_greg + i])
+      return false;
+  return true;
+}
+
 #ifndef TARGET_FIX_AND_CONTINUE
 #define TARGET_FIX_AND_CONTINUE 0
 #endif
@@ -13156,7 +13920,8 @@ rs6000_emit_prologue (void)
   using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
                          && (!TARGET_SPE_ABI
                              || info->spe_64bit_regs_used == 0)
-                         && info->first_gp_reg_save < 31);
+                         && 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
@@ -13480,12 +14245,12 @@ rs6000_emit_prologue (void)
     {
       int i;
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-       if ((regs_ever_live[info->first_gp_reg_save+i]
-            && (! call_used_regs[info->first_gp_reg_save+i]
-                || (i+info->first_gp_reg_save
+       if ((regs_ever_live[info->first_gp_reg_save + i]
+            && (!call_used_regs[info->first_gp_reg_save + i]
+                || (i + info->first_gp_reg_save
                     == RS6000_PIC_OFFSET_TABLE_REGNUM
                     && TARGET_TOC && TARGET_MINIMAL_TOC)))
-           || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
+           || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
                && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
                    || (DEFAULT_ABI == ABI_DARWIN && flag_pic))))
          {
@@ -13625,14 +14390,15 @@ rs6000_emit_prologue (void)
   /* Set frame pointer, if needed.  */
   if (frame_pointer_needed)
     {
-      insn = emit_move_insn (gen_rtx_REG (Pmode, FRAME_POINTER_REGNUM),
+      insn = emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM),
                             sp_reg_rtx);
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
   /* If we are using RS6000_PIC_OFFSET_TABLE_REGNUM, we need to set it up.  */
   if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
-      || (DEFAULT_ABI == ABI_V4 && flag_pic == 1
+      || (DEFAULT_ABI == ABI_V4
+         && (flag_pic == 1 || (flag_pic && TARGET_SECURE_PLT))
          && regs_ever_live[RS6000_PIC_OFFSET_TABLE_REGNUM]))
     {
       /* If emit_load_toc_table will use the link register, we need to save
@@ -13669,12 +14435,19 @@ rs6000_emit_prologue (void)
       rtx lr = gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM);
       rtx src = machopic_function_base_sym ();
 
+      /* Save and restore LR locally around this call (in R0).  */
+      if (!info->lr_save_p)
+       rs6000_maybe_dead (emit_move_insn (gen_rtx_REG (Pmode, 0), lr));
+
       rs6000_maybe_dead (emit_insn (gen_load_macho_picbase (lr, src)));
 
       insn = emit_move_insn (gen_rtx_REG (Pmode,
                                          RS6000_PIC_OFFSET_TABLE_REGNUM),
                             lr);
       rs6000_maybe_dead (insn);
+
+      if (!info->lr_save_p)
+       rs6000_maybe_dead (emit_move_insn (lr, gen_rtx_REG (Pmode, 0)));
     }
 #endif
 }
@@ -13774,7 +14547,8 @@ rs6000_emit_epilogue (int sibcall)
   using_load_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
                         && (!TARGET_SPE_ABI
                             || info->spe_64bit_regs_used == 0)
-                        && info->first_gp_reg_save < 31);
+                        && 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
@@ -14038,11 +14812,11 @@ rs6000_emit_epilogue (int sibcall)
     }
   else
     for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-      if ((regs_ever_live[info->first_gp_reg_save+i]
-          && (! call_used_regs[info->first_gp_reg_save+i]
-              || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
+      if ((regs_ever_live[info->first_gp_reg_save + i]
+          && (!call_used_regs[info->first_gp_reg_save + i]
+              || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
                   && TARGET_TOC && TARGET_MINIMAL_TOC)))
-         || (i+info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
+         || (i + info->first_gp_reg_save == RS6000_PIC_OFFSET_TABLE_REGNUM
              && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
                  || (DEFAULT_ABI == ABI_DARWIN && flag_pic))))
        {
@@ -14814,7 +15588,7 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
   const char *name = buf;
   const char *real_name;
   rtx base = x;
-  int offset = 0;
+  HOST_WIDE_INT offset = 0;
 
   gcc_assert (!TARGET_NO_TOC);
 
@@ -15081,9 +15855,9 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
       fprintf (file, "\t.tc %s", real_name);
 
       if (offset < 0)
-       fprintf (file, ".N%d", - offset);
+       fprintf (file, ".N" HOST_WIDE_INT_PRINT_UNSIGNED, - offset);
       else if (offset)
-       fprintf (file, ".P%d", offset);
+       fprintf (file, ".P" HOST_WIDE_INT_PRINT_UNSIGNED, offset);
 
       fputs ("[TC],", file);
     }
@@ -15098,9 +15872,9 @@ output_toc (FILE *file, rtx x, int labelno, enum machine_mode mode)
     {
       RS6000_OUTPUT_BASENAME (file, name);
       if (offset < 0)
-       fprintf (file, "%d", offset);
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
       else if (offset > 0)
-       fprintf (file, "+%d", offset);
+       fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset);
     }
   else
     output_addr_const (file, x);
@@ -15233,6 +16007,10 @@ rs6000_gen_section_name (char **buf, const char *filename,
 void
 output_profile_hook (int labelno ATTRIBUTE_UNUSED)
 {
+  /* Non-standard profiling for kernels, which just saves LR then calls
+     _mcount without worrying about arg saves.  The idea is to change
+     the function prologue as little as possible as it isn't easy to
+     account for arg save/restore code added just for _mcount.  */
   if (TARGET_PROFILE_KERNEL)
     return;
 
@@ -15284,7 +16062,6 @@ void
 output_function_profiler (FILE *file, int labelno)
 {
   char buf[100];
-  int save_lr = 8;
 
   switch (DEFAULT_ABI)
     {
@@ -15292,7 +16069,6 @@ output_function_profiler (FILE *file, int labelno)
       gcc_unreachable ();
 
     case ABI_V4:
-      save_lr = 4;
       if (!TARGET_32BIT)
        {
          warning (0, "no profiling of 64-bit code for this ABI");
@@ -15300,11 +16076,28 @@ output_function_profiler (FILE *file, int labelno)
        }
       ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
       fprintf (file, "\tmflr %s\n", reg_names[0]);
-      if (flag_pic == 1)
+      if (NO_PROFILE_COUNTERS)
+       {
+         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+                      reg_names[0], reg_names[1]);
+       }
+      else if (TARGET_SECURE_PLT && flag_pic)
+       {
+         asm_fprintf (file, "\tbcl 20,31,1f\n1:\n\t{st|stw} %s,4(%s)\n",
+                      reg_names[0], reg_names[1]);
+         asm_fprintf (file, "\tmflr %s\n", reg_names[12]);
+         asm_fprintf (file, "\t{cau|addis} %s,%s,",
+                      reg_names[12], reg_names[12]);
+         assemble_name (file, buf);
+         asm_fprintf (file, "-1b@ha\n\t{cal|la} %s,", reg_names[0]);
+         assemble_name (file, buf);
+         asm_fprintf (file, "-1b@l(%s)\n", reg_names[12]);
+       }
+      else if (flag_pic == 1)
        {
          fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file);
-         asm_fprintf (file, "\t{st|stw} %s,%d(%s)\n",
-                      reg_names[0], save_lr, reg_names[1]);
+         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+                      reg_names[0], reg_names[1]);
          asm_fprintf (file, "\tmflr %s\n", reg_names[12]);
          asm_fprintf (file, "\t{l|lwz} %s,", reg_names[0]);
          assemble_name (file, buf);
@@ -15312,10 +16105,10 @@ output_function_profiler (FILE *file, int labelno)
        }
       else if (flag_pic > 1)
        {
-         asm_fprintf (file, "\t{st|stw} %s,%d(%s)\n",
-                      reg_names[0], save_lr, reg_names[1]);
+         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+                      reg_names[0], reg_names[1]);
          /* Now, we need to get the address of the label.  */
-         fputs ("\tb1f\n\t.long ", file);
+         fputs ("\tbcl 20,31,1f\n\t.long ", file);
          assemble_name (file, buf);
          fputs ("-.\n1:", file);
          asm_fprintf (file, "\tmflr %s\n", reg_names[11]);
@@ -15329,8 +16122,8 @@ output_function_profiler (FILE *file, int labelno)
          asm_fprintf (file, "\t{liu|lis} %s,", reg_names[12]);
          assemble_name (file, buf);
          fputs ("@ha\n", file);
-         asm_fprintf (file, "\t{st|stw} %s,%d(%s)\n",
-                      reg_names[0], save_lr, reg_names[1]);
+         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+                      reg_names[0], reg_names[1]);
          asm_fprintf (file, "\t{cal|la} %s,", reg_names[0]);
          assemble_name (file, buf);
          asm_fprintf (file, "@l(%s)\n", reg_names[12]);
@@ -15526,6 +16319,11 @@ is_dispatch_slot_restricted (rtx insn)
     case TYPE_IDIV:
     case TYPE_LDIV:
       return 2;
+    case TYPE_LOAD_L:
+    case TYPE_STORE_C:
+    case TYPE_ISYNC:
+    case TYPE_SYNC:
+      return 4;
     default:
       if (rs6000_cpu == PROCESSOR_POWER5
          && is_cracked_insn (insn))
@@ -15839,26 +16637,24 @@ rs6000_is_costly_dependence (rtx insn, rtx next, rtx link, int cost,
 static rtx
 get_next_active_insn (rtx insn, rtx tail)
 {
-  rtx next_insn;
-
-  if (!insn || insn == tail)
+  if (insn == NULL_RTX || insn == tail)
     return NULL_RTX;
 
-  next_insn = NEXT_INSN (insn);
-
-  while (next_insn
-        && next_insn != tail
-        && (GET_CODE (next_insn) == NOTE
-            || GET_CODE (PATTERN (next_insn)) == USE
-            || GET_CODE (PATTERN (next_insn)) == CLOBBER))
+  while (1)
     {
-      next_insn = NEXT_INSN (next_insn);
-    }
-
-  if (!next_insn || next_insn == tail)
-    return NULL_RTX;
+      insn = NEXT_INSN (insn);
+      if (insn == NULL_RTX || insn == tail)
+       return NULL_RTX;
 
-  return next_insn;
+      if (CALL_P (insn)
+         || JUMP_P (insn)
+         || (NONJUMP_INSN_P (insn)
+             && GET_CODE (PATTERN (insn)) != USE
+             && GET_CODE (PATTERN (insn)) != CLOBBER
+             && INSN_CODE (insn) != CODE_FOR_stack_tie))
+       break;
+    }
+  return insn;
 }
 
 /* Return whether the presence of INSN causes a dispatch group termination
@@ -16074,7 +16870,7 @@ force_new_group (int sched_verbose, FILE *dump, rtx *group_insns,
    between the insns.
 
    The function estimates the group boundaries that the processor will form as
-   folllows:  It keeps track of how many vacant issue slots are available after
+   follows:  It keeps track of how many vacant issue slots are available after
    each insn.  A subsequent insn will start a new group if one of the following
    4 cases applies:
    - no more vacant issue slots remain in the current dispatch group.
@@ -16481,7 +17277,7 @@ rs6000_handle_longcall_attribute (tree *node, tree name,
       && TREE_CODE (*node) != FIELD_DECL
       && TREE_CODE (*node) != TYPE_DECL)
     {
-      warning (0, "%qs attribute only applies to functions",
+      warning (OPT_Wattributes, "%qs attribute only applies to functions",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
@@ -16959,7 +17755,7 @@ rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode,
        return orig;
 
       gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS);
-      
+
       /* Use a different reg for the intermediate value, as
         it will be marked UNCHANGING.  */
       reg_temp = no_new_pseudos ? reg : gen_reg_rtx (Pmode);
@@ -16968,7 +17764,7 @@ rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode,
       offset =
        rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 1),
                                                Pmode, reg);
-      
+
       if (GET_CODE (offset) == CONST_INT)
        {
          if (SMALL_INT (offset))
@@ -17158,6 +17954,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)
       && uses_TOC ())
     {
@@ -17374,17 +18171,6 @@ rs6000_xcoff_file_end (void)
 }
 #endif /* TARGET_XCOFF */
 
-#if TARGET_MACHO
-/* Cross-module name binding.  Darwin does not support overriding
-   functions at dynamic-link time.  */
-
-static bool
-rs6000_binds_local_p (tree decl)
-{
-  return default_binds_local_p_1 (decl, 0);
-}
-#endif
-
 /* Compute a (partial) cost for rtx X.  Return true if the complete
    cost has been computed, and false if subexpressions should be
    scanned.  In either case, *TOTAL contains the cost result.  */
@@ -17407,7 +18193,9 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
              && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
                  || (CONST_OK_FOR_LETTER_P (INTVAL (x),
                                             mode == SImode ? 'L' : 'J'))
-                 || mask_operand (x, VOIDmode)))
+                 || mask_operand (x, mode)
+                 || (mode == DImode
+                     && mask64_operand (x, DImode))))
          || ((outer_code == IOR || outer_code == XOR)
              && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
                  || (CONST_OK_FOR_LETTER_P (INTVAL (x),
@@ -17440,9 +18228,9 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
          return true;
        }
       else if ((outer_code == PLUS
-               && reg_or_add_cint64_operand (x, VOIDmode))
+               && reg_or_add_cint_operand (x, VOIDmode))
               || (outer_code == MINUS
-                  && reg_or_sub_cint64_operand (x, VOIDmode))
+                  && reg_or_sub_cint_operand (x, VOIDmode))
               || ((outer_code == SET
                    || outer_code == IOR
                    || outer_code == XOR)
@@ -17459,6 +18247,7 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
          && ((outer_code == AND
               && (CONST_OK_FOR_LETTER_P (INTVAL (x), 'K')
                   || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L')
+                  || mask_operand (x, DImode)
                   || mask64_operand (x, DImode)))
              || ((outer_code == IOR || outer_code == XOR)
                  && CONST_DOUBLE_HIGH (x) == 0
@@ -17515,12 +18304,6 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
          else
            *total = rs6000_cost->fp;
        }
-      else if (GET_CODE (XEXP (x, 0)) == MULT)
-       {
-         /* The rs6000 doesn't have shift-and-add instructions.  */
-         rs6000_rtx_costs (XEXP (x, 0), MULT, PLUS, total);
-         *total += COSTS_N_INSNS (1);
-       }
       else
        *total = COSTS_N_INSNS (1);
       return false;
@@ -17547,12 +18330,6 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
          else
            *total = rs6000_cost->fp;
        }
-      else if (GET_CODE (XEXP (x, 0)) == MULT)
-       {
-         /* The rs6000 doesn't have shift-and-sub instructions.  */
-         rs6000_rtx_costs (XEXP (x, 0), MULT, MINUS, total);
-         *total += COSTS_N_INSNS (1);
-       }
       else
        *total = COSTS_N_INSNS (1);
       return false;
@@ -17677,11 +18454,17 @@ rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
     case UNSIGNED_FLOAT:
     case FIX:
     case UNSIGNED_FIX:
-    case FLOAT_EXTEND:
     case FLOAT_TRUNCATE:
       *total = rs6000_cost->fp;
       return false;
 
+    case FLOAT_EXTEND:
+      if (mode == DFmode)
+       *total = 0;
+      else
+       *total = rs6000_cost->fp;
+      return false;
+
     case UNSPEC:
       switch (XINT (x, 1))
        {
@@ -17783,7 +18566,7 @@ rs6000_register_move_cost (enum machine_mode mode,
 
       else
        /* A move will cost one instruction per GPR moved.  */
-       return 2 * HARD_REGNO_NREGS (0, mode);
+       return 2 * hard_regno_nregs[0][mode];
     }
 
   /* Moving between two similar registers is just one instruction.  */
@@ -17804,15 +18587,118 @@ rs6000_memory_move_cost (enum machine_mode mode, enum reg_class class,
                         int in ATTRIBUTE_UNUSED)
 {
   if (reg_classes_intersect_p (class, GENERAL_REGS))
-    return 4 * HARD_REGNO_NREGS (0, mode);
+    return 4 * hard_regno_nregs[0][mode];
   else if (reg_classes_intersect_p (class, FLOAT_REGS))
-    return 4 * HARD_REGNO_NREGS (32, mode);
+    return 4 * hard_regno_nregs[32][mode];
   else if (reg_classes_intersect_p (class, ALTIVEC_REGS))
-    return 4 * HARD_REGNO_NREGS (FIRST_ALTIVEC_REGNO, mode);
+    return 4 * hard_regno_nregs[FIRST_ALTIVEC_REGNO][mode];
   else
     return 4 + rs6000_register_move_cost (mode, class, GENERAL_REGS);
 }
 
+/* Newton-Raphson approximation of single-precision floating point divide n/d.
+   Assumes no trapping math and finite arguments.  */
+
+void
+rs6000_emit_swdivsf (rtx res, rtx n, rtx d)
+{
+  rtx x0, e0, e1, y1, u0, v0, one;
+
+  x0 = gen_reg_rtx (SFmode);
+  e0 = gen_reg_rtx (SFmode);
+  e1 = gen_reg_rtx (SFmode);
+  y1 = gen_reg_rtx (SFmode);
+  u0 = gen_reg_rtx (SFmode);
+  v0 = gen_reg_rtx (SFmode);
+  one = force_reg (SFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconst1, SFmode));
+
+  /* x0 = 1./d estimate */
+  emit_insn (gen_rtx_SET (VOIDmode, x0,
+                         gen_rtx_UNSPEC (SFmode, gen_rtvec (1, d),
+                                         UNSPEC_FRES)));
+  /* e0 = 1. - d * x0 */
+  emit_insn (gen_rtx_SET (VOIDmode, e0,
+                         gen_rtx_MINUS (SFmode, one,
+                                        gen_rtx_MULT (SFmode, d, x0))));
+  /* e1 = e0 + e0 * e0 */
+  emit_insn (gen_rtx_SET (VOIDmode, e1,
+                         gen_rtx_PLUS (SFmode,
+                                       gen_rtx_MULT (SFmode, e0, e0), e0)));
+  /* y1 = x0 + e1 * x0 */
+  emit_insn (gen_rtx_SET (VOIDmode, y1,
+                         gen_rtx_PLUS (SFmode,
+                                       gen_rtx_MULT (SFmode, e1, x0), x0)));
+  /* u0 = n * y1 */
+  emit_insn (gen_rtx_SET (VOIDmode, u0,
+                         gen_rtx_MULT (SFmode, n, y1)));
+  /* v0 = n - d * u0 */
+  emit_insn (gen_rtx_SET (VOIDmode, v0,
+                         gen_rtx_MINUS (SFmode, n,
+                                        gen_rtx_MULT (SFmode, d, u0))));
+  /* res = u0 + v0 * y1 */
+  emit_insn (gen_rtx_SET (VOIDmode, res,
+                         gen_rtx_PLUS (SFmode,
+                                       gen_rtx_MULT (SFmode, v0, y1), u0)));
+}
+
+/* Newton-Raphson approximation of double-precision floating point divide n/d.
+   Assumes no trapping math and finite arguments.  */
+
+void
+rs6000_emit_swdivdf (rtx res, rtx n, rtx d)
+{
+  rtx x0, e0, e1, e2, y1, y2, y3, u0, v0, one;
+
+  x0 = gen_reg_rtx (DFmode);
+  e0 = gen_reg_rtx (DFmode);
+  e1 = gen_reg_rtx (DFmode);
+  e2 = gen_reg_rtx (DFmode);
+  y1 = gen_reg_rtx (DFmode);
+  y2 = gen_reg_rtx (DFmode);
+  y3 = gen_reg_rtx (DFmode);
+  u0 = gen_reg_rtx (DFmode);
+  v0 = gen_reg_rtx (DFmode);
+  one = force_reg (DFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconst1, DFmode));
+
+  /* x0 = 1./d estimate */
+  emit_insn (gen_rtx_SET (VOIDmode, x0,
+                         gen_rtx_UNSPEC (DFmode, gen_rtvec (1, d),
+                                         UNSPEC_FRES)));
+  /* e0 = 1. - d * x0 */
+  emit_insn (gen_rtx_SET (VOIDmode, e0,
+                         gen_rtx_MINUS (DFmode, one,
+                                        gen_rtx_MULT (SFmode, d, x0))));
+  /* y1 = x0 + e0 * x0 */
+  emit_insn (gen_rtx_SET (VOIDmode, y1,
+                         gen_rtx_PLUS (DFmode,
+                                       gen_rtx_MULT (DFmode, e0, x0), x0)));
+  /* e1 = e0 * e0 */
+  emit_insn (gen_rtx_SET (VOIDmode, e1,
+                         gen_rtx_MULT (DFmode, e0, e0)));
+  /* y2 = y1 + e1 * y1 */
+  emit_insn (gen_rtx_SET (VOIDmode, y2,
+                         gen_rtx_PLUS (DFmode,
+                                       gen_rtx_MULT (DFmode, e1, y1), y1)));
+  /* e2 = e1 * e1 */
+  emit_insn (gen_rtx_SET (VOIDmode, e2,
+                         gen_rtx_MULT (DFmode, e1, e1)));
+  /* y3 = y2 + e2 * y2 */
+  emit_insn (gen_rtx_SET (VOIDmode, y3,
+                         gen_rtx_PLUS (DFmode,
+                                       gen_rtx_MULT (DFmode, e2, y2), y2)));
+  /* u0 = n * y3 */
+  emit_insn (gen_rtx_SET (VOIDmode, u0,
+                         gen_rtx_MULT (DFmode, n, y3)));
+  /* v0 = n - d * u0 */
+  emit_insn (gen_rtx_SET (VOIDmode, v0,
+                         gen_rtx_MINUS (DFmode, n,
+                                        gen_rtx_MULT (DFmode, d, u0))));
+  /* res = u0 + v0 * y3 */
+  emit_insn (gen_rtx_SET (VOIDmode, res,
+                         gen_rtx_PLUS (DFmode,
+                                       gen_rtx_MULT (DFmode, v0, y3), u0)));
+}
+
 /* Return an RTX representing where to find the function value of a
    function returning MODE.  */
 static rtx
@@ -17893,7 +18779,26 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
                                                   GP_ARG_RETURN + 1),
                                      GEN_INT (4))));
     }
-
+  if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DCmode)
+    {
+      return gen_rtx_PARALLEL (DCmode,
+       gen_rtvec (4,
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode, GP_ARG_RETURN),
+                                     const0_rtx),
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode,
+                                                  GP_ARG_RETURN + 1),
+                                     GEN_INT (4)),
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode,
+                                                  GP_ARG_RETURN + 2),
+                                     GEN_INT (8)),
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode,
+                                                  GP_ARG_RETURN + 3),
+                                     GEN_INT (12))));
+    }
   if ((INTEGRAL_TYPE_P (valtype)
        && TYPE_PRECISION (valtype) < BITS_PER_WORD)
       || POINTER_TYPE_P (valtype))
@@ -17965,9 +18870,19 @@ rs6000_initial_elimination_offset (int from, int to)
   rs6000_stack_t *info = rs6000_stack_info ();
   HOST_WIDE_INT offset;
 
-  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+  if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
     offset = info->push_p ? 0 : -info->total_size;
-  else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+  else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    {
+      offset = info->push_p ? 0 : -info->total_size;
+      if (FRAME_GROWS_DOWNWARD)
+       offset += info->fixed_size + info->vars_size + info->parm_size;
+    }
+  else if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+    offset = FRAME_GROWS_DOWNWARD
+            ? info->fixed_size + info->vars_size + info->parm_size
+            : 0;
+  else if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
     offset = info->total_size;
   else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
     offset = info->push_p ? info->total_size : 0;
@@ -18073,8 +18988,8 @@ rs6000_vector_mode_supported_p (enum machine_mode mode)
     return false;
 }
 
-/* Target hook for invalid_arg_for_unprototyped_fn. */ 
-static const char * 
+/* Target hook for invalid_arg_for_unprototyped_fn. */
+static const char *
 invalid_arg_for_unprototyped_fn (tree typelist, tree funcdecl, tree val)
 {
   return (!rs6000_darwin64_abi
@@ -18087,4 +19002,17 @@ invalid_arg_for_unprototyped_fn (tree typelist, tree funcdecl, tree val)
          : NULL;
 }
 
+/* For TARGET_SECURE_PLT 32-bit PIC code we can save PIC register
+   setup by using __stack_chk_fail_local hidden function instead of
+   calling __stack_chk_fail directly.  Otherwise it is better to call
+   __stack_chk_fail directly.  */
+
+static tree
+rs6000_stack_protect_fail (void)
+{
+  return (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+        ? default_hidden_stack_protect_fail ()
+        : default_external_stack_protect_fail ();
+}
+
 #include "gt-rs6000.h"