OSDN Git Service

PR target/52107
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index 46ad820..39c0714 100644 (file)
@@ -1,6 +1,7 @@
 /* Subroutines used for code generation on IBM RS/6000.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+   2012
    Free Software Foundation, Inc.
    Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
@@ -938,7 +939,7 @@ 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, bool);
-#if defined (HAVE_GAS_HIDDEN) && !defined (TARGET_MACHO)
+#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 static void rs6000_assemble_visibility (tree, int);
 #endif
 static int rs6000_ra_ever_killed (void);
@@ -1105,6 +1106,7 @@ static rtx rs6000_debug_legitimize_address (rtx, rtx, enum machine_mode);
 static rtx rs6000_legitimize_tls_address (rtx, enum tls_model);
 static void rs6000_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static rtx rs6000_delegitimize_address (rtx);
+static bool rs6000_const_not_ok_for_debug_p (rtx);
 static rtx rs6000_tls_get_addr (void);
 static rtx rs6000_got_sym (void);
 static int rs6000_tls_symbol_ref_1 (rtx *, void *);
@@ -1226,6 +1228,7 @@ static bool rs6000_cannot_force_const_mem (enum machine_mode, rtx);
 static bool rs6000_legitimate_constant_p (enum machine_mode, rtx);
 static bool rs6000_save_toc_in_prologue_p (void);
 static void rs6000_code_end (void) ATTRIBUTE_UNUSED;
+static void rs6000_set_up_by_prologue (struct hard_reg_set_container *);
 
 /* Hash table stuff for keeping track of TOC entries.  */
 
@@ -1386,11 +1389,14 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_ASM_INTEGER
 #define TARGET_ASM_INTEGER rs6000_assemble_integer
 
-#if defined (HAVE_GAS_HIDDEN) && !defined (TARGET_MACHO)
+#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 #undef TARGET_ASM_ASSEMBLE_VISIBILITY
 #define TARGET_ASM_ASSEMBLE_VISIBILITY rs6000_assemble_visibility
 #endif
 
+#undef TARGET_SET_UP_BY_PROLOGUE
+#define TARGET_SET_UP_BY_PROLOGUE rs6000_set_up_by_prologue
+
 #undef TARGET_HAVE_TLS
 #define TARGET_HAVE_TLS HAVE_AS_TLS
 
@@ -1400,6 +1406,9 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_DELEGITIMIZE_ADDRESS
 #define TARGET_DELEGITIMIZE_ADDRESS rs6000_delegitimize_address
 
+#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
+#define TARGET_CONST_NOT_OK_FOR_DEBUG_P rs6000_const_not_ok_for_debug_p
+
 #undef TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE rs6000_output_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
@@ -1575,7 +1584,7 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #define TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION \
   rs6000_builtin_vectorized_function
 
-#ifndef TARGET_MACHO
+#if !TARGET_MACHO
 #undef TARGET_STACK_PROTECT_FAIL
 #define TARGET_STACK_PROTECT_FAIL rs6000_stack_protect_fail
 #endif
@@ -1661,6 +1670,8 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_LEGITIMATE_CONSTANT_P
 #define TARGET_LEGITIMATE_CONSTANT_P rs6000_legitimate_constant_p
 
+#undef TARGET_VECTORIZE_VEC_PERM_CONST_OK
+#define TARGET_VECTORIZE_VEC_PERM_CONST_OK rs6000_vectorize_vec_perm_const_ok
 \f
 
 /* Simplifications for entries below.  */
@@ -2330,6 +2341,11 @@ rs6000_init_hard_regno_mode_ok (bool global_init_p)
          rs6000_vector_reload[V4SFmode][1]  = CODE_FOR_reload_v4sf_di_load;
          rs6000_vector_reload[V2DFmode][0]  = CODE_FOR_reload_v2df_di_store;
          rs6000_vector_reload[V2DFmode][1]  = CODE_FOR_reload_v2df_di_load;
+         if (TARGET_VSX && TARGET_VSX_SCALAR_MEMORY)
+           {
+             rs6000_vector_reload[DFmode][0]  = CODE_FOR_reload_df_di_store;
+             rs6000_vector_reload[DFmode][1]  = CODE_FOR_reload_df_di_load;
+           }
        }
       else
        {
@@ -2345,6 +2361,11 @@ rs6000_init_hard_regno_mode_ok (bool global_init_p)
          rs6000_vector_reload[V4SFmode][1]  = CODE_FOR_reload_v4sf_si_load;
          rs6000_vector_reload[V2DFmode][0]  = CODE_FOR_reload_v2df_si_store;
          rs6000_vector_reload[V2DFmode][1]  = CODE_FOR_reload_v2df_si_load;
+         if (TARGET_VSX && TARGET_VSX_SCALAR_MEMORY)
+           {
+             rs6000_vector_reload[DFmode][0]  = CODE_FOR_reload_df_si_store;
+             rs6000_vector_reload[DFmode][1]  = CODE_FOR_reload_df_si_load;
+           }
        }
     }
 
@@ -2943,7 +2964,8 @@ rs6000_option_override_internal (bool global_init_p)
                        && rs6000_cpu != PROCESSOR_POWER6
                        && rs6000_cpu != PROCESSOR_POWER7
                        && rs6000_cpu != PROCESSOR_PPCA2
-                       && rs6000_cpu != PROCESSOR_CELL);
+                       && rs6000_cpu != PROCESSOR_CELL
+                       && rs6000_cpu != PROCESSOR_PPC476);
   rs6000_sched_groups = (rs6000_cpu == PROCESSOR_POWER4
                         || rs6000_cpu == PROCESSOR_POWER5
                         || rs6000_cpu == PROCESSOR_POWER7);
@@ -3518,9 +3540,13 @@ rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
       case vec_to_scalar:
       case scalar_to_vec:
       case cond_branch_not_taken:
-      case vec_perm:
         return 1;
 
+      case vec_perm:
+       if (!TARGET_VSX)
+         return 1;
+       return 2;
+
       case cond_branch_taken:
         return 3;
 
@@ -4723,7 +4749,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
                                          copy_to_reg (XVECEXP (vals, 0, 3))));
          emit_insn (gen_vsx_xvcvdpsp (flt_even, dbl_even));
          emit_insn (gen_vsx_xvcvdpsp (flt_odd, dbl_odd));
-         emit_insn (gen_vec_extract_evenv4sf (target, flt_even, flt_odd));
+         rs6000_expand_extract_even (target, flt_even, flt_odd);
        }
       return;
     }
@@ -5798,6 +5824,25 @@ rs6000_delegitimize_address (rtx orig_x)
   return orig_x;
 }
 
+/* Return true if X shouldn't be emitted into the debug info.
+   The linker doesn't like .toc section references from
+   .debug_* sections, so reject .toc section symbols.  */
+
+static bool
+rs6000_const_not_ok_for_debug_p (rtx x)
+{
+  if (GET_CODE (x) == SYMBOL_REF
+      && CONSTANT_POOL_ADDRESS_P (x))
+    {
+      rtx c = get_pool_constant (x);
+      enum machine_mode cmode = get_pool_mode (x);
+      if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (c, cmode))
+       return true;
+    }
+
+  return false;
+}
+
 /* Construct the SYMBOL_REF for the tls_get_addr function.  */
 
 static GTY(()) rtx rs6000_tls_symbol;
@@ -6965,17 +7010,14 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
   if (!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
       && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE)
     {
-      /* DImode is used, not DFmode, because simplify_gen_subreg doesn't
-        know how to get a DFmode SUBREG of a TFmode.  */
-      enum machine_mode imode = (TARGET_E500_DOUBLE ? DFmode : DImode);
-      rs6000_emit_move (simplify_gen_subreg (imode, operands[0], mode, 0),
-                       simplify_gen_subreg (imode, operands[1], mode, 0),
-                       imode);
-      rs6000_emit_move (simplify_gen_subreg (imode, operands[0], mode,
-                                            GET_MODE_SIZE (imode)),
-                       simplify_gen_subreg (imode, operands[1], mode,
-                                            GET_MODE_SIZE (imode)),
-                       imode);
+      rs6000_emit_move (simplify_gen_subreg (DFmode, operands[0], mode, 0),
+                       simplify_gen_subreg (DFmode, operands[1], mode, 0),
+                       DFmode);
+      rs6000_emit_move (simplify_gen_subreg (DFmode, operands[0], mode,
+                                            GET_MODE_SIZE (DFmode)),
+                       simplify_gen_subreg (DFmode, operands[1], mode,
+                                            GET_MODE_SIZE (DFmode)),
+                       DFmode);
       return;
     }
 
@@ -10578,7 +10620,9 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
     {
       *expandedp = true;
       error ("unresolved overload for Altivec builtin %qF", fndecl);
-      return const0_rtx;
+
+      /* Given it is invalid, just generate a normal call.  */
+      return expand_call (exp, target, false);
     }
 
   target = altivec_expand_ld_builtin (exp, target, expandedp);
@@ -11306,7 +11350,9 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   if (!func_valid_p)
     {
       rs6000_invalid_builtin (fcode);
-      return NULL_RTX;
+
+      /* Given it is invalid, just generate a normal call.  */
+      return expand_call (exp, target, ignore);
     }
 
   switch (fcode)
@@ -15450,12 +15496,13 @@ rs6000_assemble_integer (rtx x, unsigned int size, int aligned_p)
     {
       static int recurse = 0;
 
-      /* For -mrelocatable, we mark all addresses that need to be fixed up
-        in the .fixup section.  */
+      /* For -mrelocatable, we mark all addresses that need to be fixed up in
+        the .fixup section.  Since the TOC section is already relocated, we
+        don't need to mark it here.  We used to skip the text section, but it
+        should never be valid for relocated addresses to be placed in the text
+        section.  */
       if (TARGET_RELOCATABLE
          && in_section != toc_section
-         && in_section != text_section
-         && !unlikely_text_section_p (in_section)
          && !recurse
          && GET_CODE (x) != CONST_INT
          && GET_CODE (x) != CONST_DOUBLE
@@ -15497,7 +15544,7 @@ rs6000_assemble_integer (rtx x, unsigned int size, int aligned_p)
   return default_assemble_integer (x, size, aligned_p);
 }
 
-#if defined (HAVE_GAS_HIDDEN) && !defined (TARGET_MACHO)
+#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to
    VISIBILITY_TYPE.  */
 
@@ -19903,7 +19950,9 @@ rs6000_emit_prologue (void)
      used in this function, and do the corresponding magic in the
      epilogue.  */
 
-  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
+  if (!WORLD_SAVE_P (info)
+      && TARGET_ALTIVEC
+      && TARGET_ALTIVEC_VRSAVE
       && info->vrsave_mask != 0)
     {
       rtx reg, mem, vrsave;
@@ -19919,15 +19968,12 @@ rs6000_emit_prologue (void)
       else
         emit_insn (gen_rtx_SET (VOIDmode, reg, vrsave));
 
-      if (!WORLD_SAVE_P (info))
-        {
-          /* Save VRSAVE.  */
-          offset = info->vrsave_save_offset + sp_offset;
-          mem = gen_frame_mem (SImode,
-                               gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                             GEN_INT (offset)));
-          insn = emit_move_insn (mem, reg);
-        }
+      /* Save VRSAVE.  */
+      offset = info->vrsave_save_offset + sp_offset;
+      mem = gen_frame_mem (SImode,
+                          gen_rtx_PLUS (Pmode, frame_reg_rtx, 
+                                        GEN_INT (offset)));
+      insn = emit_move_insn (mem, reg);
 
       /* Include the registers in the mask.  */
       emit_insn (gen_iorsi3 (reg, reg, GEN_INT ((int) info->vrsave_mask)));
@@ -26202,6 +26248,327 @@ rs6000_emit_parity (rtx dst, rtx src)
     }
 }
 
+/* Expand an Altivec constant permutation.  Return true if we match
+   an efficient implementation; false to fall back to VPERM.  */
+
+bool
+altivec_expand_vec_perm_const (rtx operands[4])
+{
+  struct altivec_perm_insn {
+    enum insn_code impl;
+    unsigned char perm[16];
+  };
+  static const struct altivec_perm_insn patterns[] = {
+    { CODE_FOR_altivec_vpkuhum,
+      {  1,  3,  5,  7,  9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 } },
+    { CODE_FOR_altivec_vpkuwum,
+      {  2,  3,  6,  7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31 } },
+    { CODE_FOR_altivec_vmrghb,
+      {  0, 16,  1, 17,  2, 18,  3, 19,  4, 20,  5, 21,  6, 22,  7, 23 } },
+    { CODE_FOR_altivec_vmrghh,
+      {  0,  1, 16, 17,  2,  3, 18, 19,  4,  5, 20, 21,  6,  7, 22, 23 } },
+    { CODE_FOR_altivec_vmrghw,
+      {  0,  1,  2,  3, 16, 17, 18, 19,  4,  5,  6,  7, 20, 21, 22, 23 } },
+    { CODE_FOR_altivec_vmrglb,
+      {  8, 24,  9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31 } },
+    { CODE_FOR_altivec_vmrglh,
+      {  8,  9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31 } },
+    { CODE_FOR_altivec_vmrglw,
+      {  8,  9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31 } }
+  };
+
+  unsigned int i, j, elt, which;
+  unsigned char perm[16];
+  rtx target, op0, op1, sel, x;
+  bool one_vec;
+
+  target = operands[0];
+  op0 = operands[1];
+  op1 = operands[2];
+  sel = operands[3];
+
+  /* Unpack the constant selector.  */
+  for (i = which = 0; i < 16; ++i)
+    {
+      rtx e = XVECEXP (sel, 0, i);
+      elt = INTVAL (e) & 31;
+      which |= (elt < 16 ? 1 : 2);
+      perm[i] = elt;
+    }
+
+  /* Simplify the constant selector based on operands.  */
+  switch (which)
+    {
+    default:
+      gcc_unreachable ();
+
+    case 3:
+      one_vec = false;
+      if (!rtx_equal_p (op0, op1))
+       break;
+      /* FALLTHRU */
+
+    case 2:
+      for (i = 0; i < 16; ++i)
+       perm[i] &= 15;
+      op0 = op1;
+      one_vec = true;
+      break;
+
+    case 1:
+      op1 = op0;
+      one_vec = true;
+      break;
+    }
+  /* Look for splat patterns.  */
+  if (one_vec)
+    {
+      elt = perm[0];
+
+      for (i = 0; i < 16; ++i)
+       if (perm[i] != elt)
+         break;
+      if (i == 16)
+       {
+         emit_insn (gen_altivec_vspltb (target, op0, GEN_INT (elt)));
+         return true;
+       }
+
+      if (elt % 2 == 0)
+       {
+         for (i = 0; i < 16; i += 2)
+           if (perm[i] != elt || perm[i + 1] != elt + 1)
+             break;
+         if (i == 16)
+           {
+             x = gen_reg_rtx (V8HImode);
+             emit_insn (gen_altivec_vsplth (x, gen_lowpart (V8HImode, op0),
+                                            GEN_INT (elt / 2)));
+             emit_move_insn (target, gen_lowpart (V16QImode, x));
+             return true;
+           }
+       }
+
+      if (elt % 4 == 0)
+       {
+         for (i = 0; i < 16; i += 4)
+           if (perm[i] != elt
+               || perm[i + 1] != elt + 1
+               || perm[i + 2] != elt + 2
+               || perm[i + 3] != elt + 3)
+             break;
+         if (i == 16)
+           {
+             x = gen_reg_rtx (V4SImode);
+             emit_insn (gen_altivec_vspltw (x, gen_lowpart (V4SImode, op0),
+                                            GEN_INT (elt / 4)));
+             emit_move_insn (target, gen_lowpart (V16QImode, x));
+             return true;
+           }
+       }
+    }
+
+  /* Look for merge and pack patterns.  */
+  for (j = 0; j < ARRAY_SIZE (patterns); ++j)
+    {
+      bool swapped;
+
+      elt = patterns[j].perm[0];
+      if (perm[0] == elt)
+       swapped = false;
+      else if (perm[0] == elt + 16)
+       swapped = true;
+      else
+       continue;
+      for (i = 1; i < 16; ++i)
+       {
+         elt = patterns[j].perm[i];
+         if (swapped)
+           elt = (elt >= 16 ? elt - 16 : elt + 16);
+         else if (one_vec && elt >= 16)
+           elt -= 16;
+         if (perm[i] != elt)
+           break;
+       }
+      if (i == 16)
+       {
+         enum insn_code icode = patterns[j].impl;
+         enum machine_mode omode = insn_data[icode].operand[0].mode;
+         enum machine_mode imode = insn_data[icode].operand[1].mode;
+
+         if (swapped)
+           x = op0, op0 = op1, op1 = x;
+         if (imode != V16QImode)
+           {
+             op0 = gen_lowpart (imode, op0);
+             op1 = gen_lowpart (imode, op1);
+           }
+         if (omode == V16QImode)
+           x = target;
+         else
+           x = gen_reg_rtx (omode);
+         emit_insn (GEN_FCN (icode) (x, op0, op1));
+         if (omode != V16QImode)
+           emit_move_insn (target, gen_lowpart (V16QImode, x));
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* Expand a Paired Single, VSX Permute Doubleword, or SPE constant permutation.
+   Return true if we match an efficient implementation.  */
+
+static bool
+rs6000_expand_vec_perm_const_1 (rtx target, rtx op0, rtx op1,
+                               unsigned char perm0, unsigned char perm1)
+{
+  rtx x;
+
+  /* If both selectors come from the same operand, fold to single op.  */
+  if ((perm0 & 2) == (perm1 & 2))
+    {
+      if (perm0 & 2)
+       op0 = op1;
+      else
+       op1 = op0;
+    }
+  /* If both operands are equal, fold to simpler permutation.  */
+  if (rtx_equal_p (op0, op1))
+    {
+      perm0 = perm0 & 1;
+      perm1 = (perm1 & 1) + 2;
+    }
+  /* If the first selector comes from the second operand, swap.  */
+  else if (perm0 & 2)
+    {
+      if (perm1 & 2)
+       return false;
+      perm0 -= 2;
+      perm1 += 2;
+      x = op0, op0 = op1, op1 = x;
+    }
+  /* If the second selector does not come from the second operand, fail.  */
+  else if ((perm1 & 2) == 0)
+    return false;
+
+  /* Success! */
+  if (target != NULL)
+    {
+      enum machine_mode vmode, dmode;
+      rtvec v;
+
+      vmode = GET_MODE (target);
+      gcc_assert (GET_MODE_NUNITS (vmode) == 2);
+      dmode = mode_for_vector (GET_MODE_INNER (vmode), 4);
+
+      x = gen_rtx_VEC_CONCAT (dmode, op0, op1);
+      v = gen_rtvec (2, GEN_INT (perm0), GEN_INT (perm1));
+      x = gen_rtx_VEC_SELECT (vmode, x, gen_rtx_PARALLEL (VOIDmode, v));
+      emit_insn (gen_rtx_SET (VOIDmode, target, x));
+    }
+  return true;
+}
+
+bool
+rs6000_expand_vec_perm_const (rtx operands[4])
+{
+  rtx target, op0, op1, sel;
+  unsigned char perm0, perm1;
+
+  target = operands[0];
+  op0 = operands[1];
+  op1 = operands[2];
+  sel = operands[3];
+
+  /* Unpack the constant selector.  */
+  perm0 = INTVAL (XVECEXP (sel, 0, 0)) & 3;
+  perm1 = INTVAL (XVECEXP (sel, 0, 1)) & 3;
+
+  return rs6000_expand_vec_perm_const_1 (target, op0, op1, perm0, perm1);
+}
+
+/* Test whether a constant permutation is supported.  */
+
+static bool
+rs6000_vectorize_vec_perm_const_ok (enum machine_mode vmode,
+                                   const unsigned char *sel)
+{
+  /* AltiVec (and thus VSX) can handle arbitrary permutations.  */
+  if (TARGET_ALTIVEC)
+    return true;
+
+  /* Check for ps_merge* or evmerge* insns.  */
+  if ((TARGET_PAIRED_FLOAT && vmode == V2SFmode)
+      || (TARGET_SPE && vmode == V2SImode))
+    {
+      rtx op0 = gen_raw_REG (vmode, LAST_VIRTUAL_REGISTER + 1);
+      rtx op1 = gen_raw_REG (vmode, LAST_VIRTUAL_REGISTER + 2);
+      return rs6000_expand_vec_perm_const_1 (NULL, op0, op1, sel[0], sel[1]);
+    }
+
+  return false;
+}
+
+/* A subroutine for rs6000_expand_extract_even & rs6000_expand_interleave.  */
+
+static void
+rs6000_do_expand_vec_perm (rtx target, rtx op0, rtx op1,
+                          enum machine_mode vmode, unsigned nelt, rtx perm[])
+{
+  enum machine_mode imode;
+  rtx x;
+
+  imode = vmode;
+  if (GET_MODE_CLASS (vmode) != MODE_VECTOR_INT)
+    {
+      imode = GET_MODE_INNER (vmode);
+      imode = mode_for_size (GET_MODE_BITSIZE (imode), MODE_INT, 0);
+      imode = mode_for_vector (imode, nelt);
+    }
+
+  x = gen_rtx_CONST_VECTOR (imode, gen_rtvec_v (nelt, perm));
+  x = expand_vec_perm (vmode, op0, op1, x, target);
+  if (x != target)
+    emit_move_insn (target, x);
+}
+
+/* Expand an extract even operation.  */
+
+void
+rs6000_expand_extract_even (rtx target, rtx op0, rtx op1)
+{
+  enum machine_mode vmode = GET_MODE (target);
+  unsigned i, nelt = GET_MODE_NUNITS (vmode);
+  rtx perm[16];
+
+  for (i = 0; i < nelt; i++)
+    perm[i] = GEN_INT (i * 2);
+
+  rs6000_do_expand_vec_perm (target, op0, op1, vmode, nelt, perm);
+}
+
+/* Expand a vector interleave operation.  */
+
+void
+rs6000_expand_interleave (rtx target, rtx op0, rtx op1, bool highp)
+{
+  enum machine_mode vmode = GET_MODE (target);
+  unsigned i, high, nelt = GET_MODE_NUNITS (vmode);
+  rtx perm[16];
+
+  high = (highp == BYTES_BIG_ENDIAN ? 0 : nelt / 2);
+  for (i = 0; i < nelt / 2; i++)
+    {
+      perm[i * 2] = GEN_INT (i + high);
+      perm[i * 2 + 1] = GEN_INT (i + nelt + high);
+    }
+
+  rs6000_do_expand_vec_perm (target, op0, op1, vmode, nelt, perm);
+}
+
 /* Return an RTX representing where to find the function value of a
    function returning MODE.  */
 static rtx
@@ -26789,6 +27156,11 @@ rs6000_inner_target_options (tree args, bool attr_p)
                        error_p = false;
                        target_flags_explicit |= mask;
 
+                       /* VSX needs altivec, so -mvsx automagically sets
+                          altivec.  */
+                       if (mask == MASK_VSX && !invert)
+                         mask |= MASK_ALTIVEC;
+
                        if (rs6000_opt_masks[i].invert)
                          invert = !invert;
 
@@ -26806,7 +27178,7 @@ rs6000_inner_target_options (tree args, bool attr_p)
                    if (strcmp (r, rs6000_opt_vars[i].name) == 0)
                      {
                        size_t j = rs6000_opt_vars[i].global_offset;
-                       ((int *) &global_options)[j] = !invert;
+                       *((int *) ((char *)&global_options + j)) = !invert;
                        error_p = false;
                        break;
                      }
@@ -27001,7 +27373,6 @@ rs6000_pragma_target_parse (tree args, tree pop_target)
   struct cl_target_option *prev_opt, *cur_opt;
   unsigned prev_bumask, cur_bumask, diff_bumask;
   int prev_flags, cur_flags, diff_flags;
-  bool ret;
 
   if (TARGET_DEBUG_TARGET)
     {
@@ -27023,7 +27394,6 @@ rs6000_pragma_target_parse (tree args, tree pop_target)
 
   if (! args)
     {
-      ret = true;
       cur_tree = ((pop_target)
                  ? pop_target
                  : target_option_default_node);
@@ -27033,13 +27403,13 @@ rs6000_pragma_target_parse (tree args, tree pop_target)
   else
     {
       rs6000_cpu_index = rs6000_tune_index = -1;
-      ret = rs6000_inner_target_options (args, false);
-      cur_tree = build_target_option_node ();
-
-      if (!cur_tree)
+      if (!rs6000_inner_target_options (args, false)
+         || !rs6000_option_override_internal (false)
+         || (cur_tree = build_target_option_node ()) == NULL_TREE)
        {
          if (TARGET_DEBUG_BUILTIN || TARGET_DEBUG_TARGET)
-           fprintf (stderr, "build_target_option_node returned NULL\n");
+           fprintf (stderr, "invalid pragma\n");
+
          return false;
        }
     }
@@ -27075,7 +27445,7 @@ rs6000_pragma_target_parse (tree args, tree pop_target)
        }
     }
 
-  return ret;
+  return true;
 }
 
 \f
@@ -27562,6 +27932,19 @@ rs6000_code_end (void)
   current_function_decl = NULL;
 }
 
+/* Add r30 to hard reg set if the prologue sets it up and it is not
+   pic_offset_table_rtx.  */
+
+static void
+rs6000_set_up_by_prologue (struct hard_reg_set_container *set)
+{
+  if (!TARGET_SINGLE_PIC_BASE
+      && TARGET_TOC
+      && TARGET_MINIMAL_TOC
+      && get_pool_size () != 0)
+    add_to_hard_reg_set (&set->set, Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM);
+}
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-rs6000.h"