OSDN Git Service

* config/sparc/sparc.md (UNSPEC_FRAME_BLOCKAGE): New constant.
[pf3gnuchains/gcc-fork.git] / gcc / config / sparc / sparc.c
index f806c6c..19ab54a 100644 (file)
@@ -576,7 +576,8 @@ static bool sparc_print_operand_punct_valid_p (unsigned char);
 static void sparc_print_operand (FILE *, rtx, int);
 static void sparc_print_operand_address (FILE *, rtx);
 static reg_class_t sparc_secondary_reload (bool, rtx, reg_class_t,
-                                          enum machine_mode, secondary_reload_info *);
+                                          enum machine_mode,
+                                          secondary_reload_info *);
 \f
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
 /* Table of valid machine attributes.  */
@@ -1159,6 +1160,21 @@ sparc_option_override (void)
       gcc_unreachable ();
     };
 
+  if (sparc_memory_model == SMM_DEFAULT)
+    {
+      /* Choose the memory model for the operating system.  */
+      enum sparc_memory_model_type os_default = SUBTARGET_DEFAULT_MEMORY_MODEL;
+      if (os_default != SMM_DEFAULT)
+       sparc_memory_model = os_default;
+      /* Choose the most relaxed model for the processor.  */
+      else if (TARGET_V9)
+       sparc_memory_model = SMM_RMO;
+      else if (TARGET_V8)
+       sparc_memory_model = SMM_PSO;
+      else
+       sparc_memory_model = SMM_SC;
+    }
+
 #ifdef TARGET_DEFAULT_LONG_DOUBLE_128
   if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
     target_flags |= MASK_LONG_DOUBLE_128;
@@ -2519,7 +2535,7 @@ emit_scc_insn (rtx operands[])
         }
       else if (GET_MODE (x) == DImode)
         {
-          rtx pat = gen_seqdi_special (operands[0], x, y);
+         rtx pat = gen_seqdi_special (operands[0], x, y);
           emit_insn (pat);
           return true;
         }
@@ -2535,19 +2551,23 @@ emit_scc_insn (rtx operands[])
         }
       else if (GET_MODE (x) == DImode)
         {
-          rtx pat = gen_snedi_special (operands[0], x, y);
+         rtx pat;
+         if (TARGET_VIS3)
+           pat = gen_snedi_special_vis3 (operands[0], x, y);
+         else
+           pat = gen_snedi_special (operands[0], x, y);
           emit_insn (pat);
           return true;
         }
     }
 
-  /* For the rest, on v9 we can use conditional moves.  */
-
-  if (TARGET_V9)
-    {
-      if (gen_v9_scc (operands[0], code, x, y))
-        return true;
-    }
+  if (TARGET_V9
+      && TARGET_ARCH64
+      && GET_MODE (x) == DImode
+      && !(TARGET_VIS3
+          && (code == GTU || code == LTU))
+      && gen_v9_scc (operands[0], code, x, y))
+    return true;
 
   /* We can do LTU and GEU using the addx/subx instructions too.  And
      for GTU/LEU, if both operands are registers swap them and fall
@@ -2564,7 +2584,8 @@ emit_scc_insn (rtx operands[])
         }
     }
 
-  if (code == LTU || code == GEU)
+  if (code == LTU
+      || (!TARGET_VIS3 && code == GEU))
     {
       emit_insn (gen_rtx_SET (VOIDmode, operands[0],
                              gen_rtx_fmt_ee (code, SImode,
@@ -2573,6 +2594,12 @@ emit_scc_insn (rtx operands[])
       return true;
     }
 
+  /* All the posibilities to use addx/subx based sequences has been
+     exhausted, try for a 3 instruction sequence using v9 conditional
+     moves.  */
+  if (TARGET_V9 && gen_v9_scc (operands[0], code, x, y))
+    return true;
+
   /* Nope, do branches.  */
   return false;
 }
@@ -3429,7 +3456,7 @@ sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
             REG+REG address, then only one of them gets converted to an
             offsettable address.  */
          if (mode == TFmode
-             && ! (TARGET_FPU && TARGET_ARCH64 && TARGET_HARD_QUAD))
+             && ! (TARGET_ARCH64 && TARGET_HARD_QUAD))
            return 0;
 
          /* We prohibit REG + REG on ARCH32 if not optimizing for
@@ -4945,8 +4972,9 @@ sparc_expand_prologue (void)
       else if (size <= 8192)
        {
          insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096)));
-         /* %sp is still the CFA register.  */
          RTX_FRAME_RELATED_P (insn) = 1;
+
+         /* %sp is still the CFA register.  */
          insn = emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size)));
        }
       else
@@ -4969,8 +4997,18 @@ sparc_expand_prologue (void)
       else if (size <= 8192)
        {
          emit_window_save (GEN_INT (-4096));
+
          /* %sp is not the CFA register anymore.  */
          emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size)));
+
+         /* Make sure no %fp-based store is issued until after the frame is
+            established.  The offset between the frame pointer and the stack
+            pointer is calculated relative to the value of the stack pointer
+            at the end of the function prologue, and moving instructions that
+            access the stack via the frame pointer between the instructions
+            that decrement the stack pointer could result in accessing the
+            register window save area, which is volatile.  */
+         emit_insn (gen_frame_blockage ());
        }
       else
        {
@@ -8162,20 +8200,11 @@ sparc_print_operand (FILE *file, rtx x, int code)
        }
       return;
 
-      /* These are used by the conditional move instructions.  */
-    case 'c' :
+      /* This is used by the conditional move instructions.  */
     case 'C':
       {
        enum rtx_code rc = GET_CODE (x);
        
-       if (code == 'c')
-         {
-           enum machine_mode mode = GET_MODE (XEXP (x, 0));
-           if (mode == CCFPmode || mode == CCFPEmode)
-             rc = reverse_condition_maybe_unordered (GET_CODE (x));
-           else
-             rc = reverse_condition (GET_CODE (x));
-         }
        switch (rc)
          {
          case NE: fputs ("ne", file); break;
@@ -8196,20 +8225,15 @@ sparc_print_operand (FILE *file, rtx x, int code)
          case UNGT: fputs ("ug", file); break;
          case UNGE: fputs ("uge", file); break;
          case UNEQ: fputs ("ue", file); break;
-         default: output_operand_lossage (code == 'c'
-                                          ? "invalid %%c operand"
-                                          : "invalid %%C operand");
+         default: output_operand_lossage ("invalid %%C operand");
          }
        return;
       }
 
-      /* These are used by the movr instruction pattern.  */
-    case 'd':
+      /* This are used by the movr instruction pattern.  */
     case 'D':
       {
-       enum rtx_code rc = (code == 'd'
-                           ? reverse_condition (GET_CODE (x))
-                           : GET_CODE (x));
+       enum rtx_code rc = GET_CODE (x);
        switch (rc)
          {
          case NE: fputs ("ne", file); break;
@@ -8218,9 +8242,7 @@ sparc_print_operand (FILE *file, rtx x, int code)
          case LT: fputs ("lz", file); break;
          case LE: fputs ("lez", file); break;
          case GT: fputs ("gz", file); break;
-         default: output_operand_lossage (code == 'd'
-                                          ? "invalid %%d operand"
-                                          : "invalid %%D operand");
+         default: output_operand_lossage ("invalid %%D operand");
          }
        return;
       }
@@ -9125,10 +9147,11 @@ sparc_check_64 (rtx x, rtx insn)
   return 0;
 }
 
-/* Returns assembly code to perform a DImode shift using
-   a 64-bit global or out register on SPARC-V8+.  */
+/* Output a wide shift instruction in V8+ mode.  INSN is the instruction,
+   OPERANDS are its operands and OPCODE is the mnemonic to be used.  */
+
 const char *
-output_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
+output_v8plus_shift (rtx insn, rtx *operands, const char *opcode)
 {
   static char asm_code[60];
 
@@ -9153,12 +9176,13 @@ output_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
       output_asm_insn ("or\t%L1, %3, %3", operands);
     }
 
-  strcpy(asm_code, opcode);
+  strcpy (asm_code, opcode);
 
   if (which_alternative != 2)
     return strcat (asm_code, "\t%0, %2, %L0\n\tsrlx\t%L0, 32, %H0");
   else
-    return strcat (asm_code, "\t%3, %2, %3\n\tsrlx\t%3, 32, %H0\n\tmov\t%3, %L0");
+    return
+      strcat (asm_code, "\t%3, %2, %3\n\tsrlx\t%3, 32, %H0\n\tmov\t%3, %L0");
 }
 \f
 /* Output rtl to increment the profiler label LABELNO
@@ -10851,11 +10875,95 @@ sparc_mangle_type (const_tree type)
 }
 #endif
 
+/* Expand a membar instruction for various use cases.  Both the LOAD_STORE
+   and BEFORE_AFTER arguments of the form X_Y.  They are two-bit masks where
+   bit 0 indicates that X is true, and bit 1 indicates Y is true.  */
+
+void
+sparc_emit_membar_for_model (enum memmodel model,
+                            int load_store, int before_after)
+{
+  /* Bits for the MEMBAR mmask field.  */
+  const int LoadLoad = 1;
+  const int StoreLoad = 2;
+  const int LoadStore = 4;
+  const int StoreStore = 8;
+
+  int mm = 0, implied = 0;
+
+  switch (sparc_memory_model)
+    {
+    case SMM_SC:
+      /* Sequential Consistency.  All memory transactions are immediately
+        visible in sequential execution order.  No barriers needed.  */
+      implied = LoadLoad | StoreLoad | LoadStore | StoreStore;
+      break;
+
+    case SMM_TSO:
+      /* Total Store Ordering: all memory transactions with store semantics
+        are followed by an implied StoreStore.  */
+      implied |= StoreStore;
+      /* FALLTHRU */
+
+    case SMM_PSO:
+      /* Partial Store Ordering: all memory transactions with load semantics
+        are followed by an implied LoadLoad | LoadStore.  */
+      implied |= LoadLoad | LoadStore;
+
+      /* If we're not looking for a raw barrer (before+after), then atomic
+        operations get the benefit of being both load and store.  */
+      if (load_store == 3 && before_after == 2)
+       implied |= StoreLoad | StoreStore;
+      /* FALLTHRU */
+
+    case SMM_RMO:
+      /* Relaxed Memory Ordering: no implicit bits.  */
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  if (before_after & 1)
+    {
+      if (model == MEMMODEL_ACQUIRE
+          || model == MEMMODEL_ACQ_REL
+          || model == MEMMODEL_SEQ_CST)
+       {
+         if (load_store & 1)
+           mm |= LoadLoad | LoadStore;
+         if (load_store & 2)
+           mm |= StoreLoad | StoreStore;
+       }
+    }
+  if (before_after & 2)
+    {
+      if (model == MEMMODEL_RELEASE
+         || model == MEMMODEL_ACQ_REL
+         || model == MEMMODEL_SEQ_CST)
+       {
+         if (load_store & 1)
+           mm |= LoadLoad | StoreLoad;
+         if (load_store & 2)
+           mm |= LoadStore | StoreStore;
+       }
+    }
+
+  /* Remove the bits implied by the system memory model.  */
+  mm &= ~implied;
+
+  /* For raw barriers (before+after), always emit a barrier.
+     This will become a compile-time barrier if needed.  */
+  if (mm || before_after == 3)
+    emit_insn (gen_membar (GEN_INT (mm)));
+}
+
 /* Expand code to perform a 8 or 16-bit compare and swap by doing 32-bit
    compare and swap on the word containing the byte or half-word.  */
 
-void
-sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
+static void
+sparc_expand_compare_and_swap_12 (rtx bool_result, rtx result, rtx mem,
+                                 rtx oldval, rtx newval)
 {
   rtx addr1 = force_reg (Pmode, XEXP (mem, 0));
   rtx addr = gen_reg_rtx (Pmode);
@@ -10880,7 +10988,7 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
   set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
   MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
 
-  val = force_reg (SImode, memsi);
+  val = copy_to_reg (memsi);
 
   emit_insn (gen_rtx_SET (VOIDmode, off,
                          gen_rtx_XOR (SImode, off,
@@ -10926,7 +11034,9 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
   emit_insn (gen_rtx_SET (VOIDmode, newvalue,
                          gen_rtx_IOR (SImode, newv, val)));
 
-  emit_insn (gen_sync_compare_and_swapsi (res, memsi, oldvalue, newvalue));
+  emit_move_insn (bool_result, const1_rtx);
+
+  emit_insn (gen_atomic_compare_and_swapsi_1 (res, memsi, oldvalue, newvalue));
 
   emit_cmp_and_jump_insns (res, oldvalue, EQ, NULL, SImode, 0, end_label);
 
@@ -10934,6 +11044,8 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
                          gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
                                       res)));
 
+  emit_move_insn (bool_result, const0_rtx);
+
   cc = gen_compare_reg_1 (NE, resv, val);
   emit_insn (gen_rtx_SET (VOIDmode, val, resv));
 
@@ -10952,6 +11064,49 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
   emit_move_insn (result, gen_lowpart (GET_MODE (result), res));
 }
 
+/* Expand code to perform a compare-and-swap.  */
+
+void
+sparc_expand_compare_and_swap (rtx operands[])
+{
+  rtx bval, retval, mem, oldval, newval;
+  enum machine_mode mode;
+  enum memmodel model;
+
+  bval = operands[0];
+  retval = operands[1];
+  mem = operands[2];
+  oldval = operands[3];
+  newval = operands[4];
+  model = (enum memmodel) INTVAL (operands[6]);
+  mode = GET_MODE (mem);
+
+  sparc_emit_membar_for_model (model, 3, 1);
+
+  if (reg_overlap_mentioned_p (retval, oldval))
+    oldval = copy_to_reg (oldval);
+
+  if (mode == QImode || mode == HImode)
+    sparc_expand_compare_and_swap_12 (bval, retval, mem, oldval, newval);
+  else
+    {
+      rtx (*gen) (rtx, rtx, rtx, rtx);
+      rtx x;
+
+      if (mode == SImode)
+       gen = gen_atomic_compare_and_swapsi_1;
+      else
+       gen = gen_atomic_compare_and_swapdi_1;
+      emit_insn (gen (retval, mem, oldval, newval));
+
+      x = emit_store_flag (bval, EQ, retval, oldval, mode, 1, 1);
+      if (x != bval)
+       convert_move (bval, x, 1);
+    }
+
+  sparc_emit_membar_for_model (model, 3, 2);
+}
+
 void
 sparc_expand_vec_perm_bmask (enum machine_mode vmode, rtx sel)
 {
@@ -11161,7 +11316,7 @@ sparc_conditional_register_usage (void)
     global_regs[SPARC_GSR_REG] = 1;
 }
 
-/* Implement TARGET_PREFERRED_RELOAD_CLASS
+/* Implement TARGET_PREFERRED_RELOAD_CLASS:
 
    - We can't load constants into FP registers.
    - We can't load FP constants into integer registers when soft-float,
@@ -11211,8 +11366,11 @@ sparc_preferred_reload_class (rtx x, reg_class_t rclass)
   return rclass;
 }
 
+/* Output a wide multiply instruction in V8+ mode.  INSN is the instruction,
+   OPERANDS are its operands and OPCODE is the mnemonic to be used.  */
+
 const char *
-output_v8plus_mult (rtx insn, rtx *operands, const char *name)
+output_v8plus_mult (rtx insn, rtx *operands, const char *opcode)
 {
   char mulstr[32];
 
@@ -11227,7 +11385,7 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
       if (which_alternative == 1)
        {
          output_asm_insn ("or\t%L1, %H1, %H1", operands);
-         sprintf (mulstr, "%s\t%%H1, %%2, %%L0", name);
+         sprintf (mulstr, "%s\t%%H1, %%2, %%L0", opcode);
          output_asm_insn (mulstr, operands);
          return "srlx\t%L0, 32, %H0";
        }
@@ -11235,7 +11393,7 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
        {
          output_asm_insn ("sllx\t%H1, 32, %3", operands);
           output_asm_insn ("or\t%L1, %3, %3", operands);
-          sprintf (mulstr, "%s\t%%3, %%2, %%3", name);
+          sprintf (mulstr, "%s\t%%3, %%2, %%3", opcode);
          output_asm_insn (mulstr, operands);
          output_asm_insn ("srlx\t%3, 32, %H0", operands);
           return "mov\t%3, %L0";
@@ -11246,7 +11404,7 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
       if (which_alternative == 1)
        {
          output_asm_insn ("or\t%L1, %H1, %H1", operands);
-          sprintf (mulstr, "%s\t%%H1, %%H1, %%L0", name);
+          sprintf (mulstr, "%s\t%%H1, %%H1, %%L0", opcode);
          output_asm_insn (mulstr, operands);
          return "srlx\t%L0, 32, %H0";
        }
@@ -11254,7 +11412,7 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
        {
          output_asm_insn ("sllx\t%H1, 32, %3", operands);
           output_asm_insn ("or\t%L1, %3, %3", operands);
-         sprintf (mulstr, "%s\t%%3, %%3, %%3", name);
+         sprintf (mulstr, "%s\t%%3, %%3, %%3", opcode);
          output_asm_insn (mulstr, operands);
          output_asm_insn ("srlx\t%3, 32, %H0", operands);
           return "mov\t%3, %L0";
@@ -11267,7 +11425,7 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
       output_asm_insn ("or\t%L1, %H1, %H1", operands);
       output_asm_insn ("sllx\t%H2, 32, %L1", operands);
       output_asm_insn ("or\t%L2, %L1, %L1", operands);
-      sprintf (mulstr, "%s\t%%H1, %%L1, %%L0", name);
+      sprintf (mulstr, "%s\t%%H1, %%L1, %%L0", opcode);
       output_asm_insn (mulstr, operands);
       return "srlx\t%L0, 32, %H0";
     }
@@ -11277,13 +11435,99 @@ output_v8plus_mult (rtx insn, rtx *operands, const char *name)
       output_asm_insn ("sllx\t%H2, 32, %4", operands);
       output_asm_insn ("or\t%L1, %3, %3", operands);
       output_asm_insn ("or\t%L2, %4, %4", operands);
-      sprintf (mulstr, "%s\t%%3, %%4, %%3", name);
+      sprintf (mulstr, "%s\t%%3, %%4, %%3", opcode);
       output_asm_insn (mulstr, operands);
       output_asm_insn ("srlx\t%3, 32, %H0", operands);
       return "mov\t%3, %L0";
     }
 }
 
+/* Subroutine of sparc_expand_vector_init.  Emit code to initialize
+   all fields of TARGET to ELT by means of VIS2 BSHUFFLE insn.  MODE
+   and INNER_MODE are the modes describing TARGET.  */
+
+static void
+vector_init_bshuffle (rtx target, rtx elt, enum machine_mode mode,
+                     enum machine_mode inner_mode)
+{
+  rtx t1, final_insn;
+  int bmask;
+
+  t1 = gen_reg_rtx (mode);
+
+  elt = convert_modes (SImode, inner_mode, elt, true);
+  emit_move_insn (gen_lowpart(SImode, t1), elt);
+
+  switch (mode)
+    {
+    case V2SImode:
+      final_insn = gen_bshufflev2si_vis (target, t1, t1);
+      bmask = 0x45674567;
+      break;
+    case V4HImode:
+      final_insn = gen_bshufflev4hi_vis (target, t1, t1);
+      bmask = 0x67676767;
+      break;
+    case V8QImode:
+      final_insn = gen_bshufflev8qi_vis (target, t1, t1);
+      bmask = 0x77777777;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  emit_insn (gen_bmasksi_vis (gen_reg_rtx (SImode), CONST0_RTX (SImode),
+                             force_reg (SImode, GEN_INT (bmask))));
+  emit_insn (final_insn);
+}
+
+static void
+vector_init_fpmerge (rtx target, rtx elt, enum machine_mode inner_mode)
+{
+  rtx t1, t2, t3, t3_low;
+
+  t1 = gen_reg_rtx (V4QImode);
+  elt = convert_modes (SImode, inner_mode, elt, true);
+  emit_move_insn (gen_lowpart (SImode, t1), elt);
+
+  t2 = gen_reg_rtx (V4QImode);
+  emit_move_insn (t2, t1);
+
+  t3 = gen_reg_rtx (V8QImode);
+  t3_low = gen_lowpart (V4QImode, t3);
+
+  emit_insn (gen_fpmerge_vis (t3, t1, t2));
+  emit_move_insn (t1, t3_low);
+  emit_move_insn (t2, t3_low);
+
+  emit_insn (gen_fpmerge_vis (t3, t1, t2));
+  emit_move_insn (t1, t3_low);
+  emit_move_insn (t2, t3_low);
+
+  emit_insn (gen_fpmerge_vis (gen_lowpart (V8QImode, target), t1, t2));
+}
+
+static void
+vector_init_faligndata (rtx target, rtx elt, enum machine_mode inner_mode)
+{
+  rtx t1 = gen_reg_rtx (V4HImode);
+
+  elt = convert_modes (SImode, inner_mode, elt, true);
+
+  emit_move_insn (gen_lowpart (SImode, t1), elt);
+
+  emit_insn (gen_alignaddrsi_vis (gen_reg_rtx (SImode),
+                                 force_reg (SImode, GEN_INT (6)),
+                                 CONST0_RTX (SImode)));
+
+  emit_insn (gen_faligndatav4hi_vis (target, t1, target));
+  emit_insn (gen_faligndatav4hi_vis (target, t1, target));
+  emit_insn (gen_faligndatav4hi_vis (target, t1, target));
+  emit_insn (gen_faligndatav4hi_vis (target, t1, target));
+}
+
+/* Emit code to initialize TARGET to values for individual fields VALS.  */
+
 void
 sparc_expand_vector_init (rtx target, rtx vals)
 {
@@ -11291,13 +11535,18 @@ sparc_expand_vector_init (rtx target, rtx vals)
   enum machine_mode inner_mode = GET_MODE_INNER (mode);
   int n_elts = GET_MODE_NUNITS (mode);
   int i, n_var = 0;
+  bool all_same;
   rtx mem;
 
+  all_same = true;
   for (i = 0; i < n_elts; i++)
     {
       rtx x = XVECEXP (vals, 0, i);
       if (!CONSTANT_P (x))
        n_var++;
+
+      if (i > 0 && !rtx_equal_p (x, XVECEXP (vals, 0, 0)))
+       all_same = false;
     }
 
   if (n_var == 0)
@@ -11306,14 +11555,60 @@ sparc_expand_vector_init (rtx target, rtx vals)
       return;
     }
 
+  if (GET_MODE_SIZE (inner_mode) == GET_MODE_SIZE (mode))
+    {
+      if (GET_MODE_SIZE (inner_mode) == 4)
+       {
+         emit_move_insn (gen_lowpart (SImode, target),
+                         gen_lowpart (SImode, XVECEXP (vals, 0, 0)));
+         return;
+       }
+      else if (GET_MODE_SIZE (inner_mode) == 8)
+       {
+         emit_move_insn (gen_lowpart (DImode, target),
+                         gen_lowpart (DImode, XVECEXP (vals, 0, 0)));
+         return;
+       }
+    }
+  else if (GET_MODE_SIZE (inner_mode) == GET_MODE_SIZE (word_mode)
+          && GET_MODE_SIZE (mode) == 2 * GET_MODE_SIZE (word_mode))
+    {
+      emit_move_insn (gen_highpart (word_mode, target),
+                     gen_lowpart (word_mode, XVECEXP (vals, 0, 0)));
+      emit_move_insn (gen_lowpart (word_mode, target),
+                     gen_lowpart (word_mode, XVECEXP (vals, 0, 1)));
+      return;
+    }
+
+  if (all_same && GET_MODE_SIZE (mode) == 8)
+    {
+      if (TARGET_VIS2)
+       {
+         vector_init_bshuffle (target, XVECEXP (vals, 0, 0), mode, inner_mode);
+         return;
+       }
+      if (mode == V8QImode)
+       {
+         vector_init_fpmerge (target, XVECEXP (vals, 0, 0), inner_mode);
+         return;
+       }
+      if (mode == V4HImode)
+       {
+         vector_init_faligndata (target, XVECEXP (vals, 0, 0), inner_mode);
+         return;
+       }
+    }
+
   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)),
+                                      i * GET_MODE_SIZE (inner_mode)),
                    XVECEXP (vals, 0, i));
   emit_move_insn (target, mem);
 }
 
+/* Implement TARGET_SECONDARY_RELOAD.  */
+
 static reg_class_t
 sparc_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
                        enum machine_mode mode, secondary_reload_info *sri)
@@ -11377,4 +11672,170 @@ sparc_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
   return NO_REGS;
 }
 
+/* Emit code to conditionally move either OPERANDS[2] or OPERANDS[3] into
+   OPERANDS[0] in MODE.  OPERANDS[1] is the operator of the condition.  */
+
+bool
+sparc_expand_conditional_move (enum machine_mode mode, rtx *operands)
+{
+  enum rtx_code rc = GET_CODE (operands[1]);
+  enum machine_mode cmp_mode;
+  rtx cc_reg, dst, cmp;
+
+  cmp = operands[1];
+  if (GET_MODE (XEXP (cmp, 0)) == DImode && !TARGET_ARCH64)
+    return false;
+
+  if (GET_MODE (XEXP (cmp, 0)) == TFmode && !TARGET_HARD_QUAD)
+    cmp = sparc_emit_float_lib_cmp (XEXP (cmp, 0), XEXP (cmp, 1), rc);
+
+  cmp_mode = GET_MODE (XEXP (cmp, 0));
+  rc = GET_CODE (cmp);
+
+  dst = operands[0];
+  if (! rtx_equal_p (operands[2], dst)
+      && ! rtx_equal_p (operands[3], dst))
+    {
+      if (reg_overlap_mentioned_p (dst, cmp))
+       dst = gen_reg_rtx (mode);
+
+      emit_move_insn (dst, operands[3]);
+    }
+  else if (operands[2] == dst)
+    {
+      operands[2] = operands[3];
+
+      if (GET_MODE_CLASS (cmp_mode) == MODE_FLOAT)
+        rc = reverse_condition_maybe_unordered (rc);
+      else
+        rc = reverse_condition (rc);
+    }
+
+  if (XEXP (cmp, 1) == const0_rtx
+      && GET_CODE (XEXP (cmp, 0)) == REG
+      && cmp_mode == DImode
+      && v9_regcmp_p (rc))
+    cc_reg = XEXP (cmp, 0);
+  else
+    cc_reg = gen_compare_reg_1 (rc, XEXP (cmp, 0), XEXP (cmp, 1));
+
+  cmp = gen_rtx_fmt_ee (rc, GET_MODE (cc_reg), cc_reg, const0_rtx);
+
+  emit_insn (gen_rtx_SET (VOIDmode, dst,
+                         gen_rtx_IF_THEN_ELSE (mode, cmp, operands[2], dst)));
+
+  if (dst != operands[0])
+    emit_move_insn (operands[0], dst);
+
+  return true;
+}
+
+/* Emit code to conditionally move a combination of OPERANDS[1] and OPERANDS[2]
+   into OPERANDS[0] in MODE, depending on the outcome of the comparison of
+   OPERANDS[4] and OPERANDS[5].  OPERANDS[3] is the operator of the condition.
+   FCODE is the machine code to be used for OPERANDS[3] and CCODE the machine
+   code to be used for the condition mask.  */
+
+void
+sparc_expand_vcond (enum machine_mode mode, rtx *operands, int ccode, int fcode)
+{
+  rtx mask, cop0, cop1, fcmp, cmask, bshuf, gsr;
+  enum rtx_code code = GET_CODE (operands[3]);
+
+  mask = gen_reg_rtx (Pmode);
+  cop0 = operands[4];
+  cop1 = operands[5];
+  if (code == LT || code == GE)
+    {
+      rtx t;
+
+      code = swap_condition (code);
+      t = cop0; cop0 = cop1; cop1 = t;
+    }
+
+  gsr = gen_rtx_REG (DImode, SPARC_GSR_REG);
+
+  fcmp = gen_rtx_UNSPEC (Pmode,
+                        gen_rtvec (1, gen_rtx_fmt_ee (code, mode, cop0, cop1)),
+                        fcode);
+
+  cmask = gen_rtx_UNSPEC (DImode,
+                         gen_rtvec (2, mask, gsr),
+                         ccode);
+
+  bshuf = gen_rtx_UNSPEC (mode,
+                         gen_rtvec (3, operands[1], operands[2], gsr),
+                         UNSPEC_BSHUFFLE);
+
+  emit_insn (gen_rtx_SET (VOIDmode, mask, fcmp));
+  emit_insn (gen_rtx_SET (VOIDmode, gsr, cmask));
+
+  emit_insn (gen_rtx_SET (VOIDmode, operands[0], bshuf));
+}
+
+/* On sparc, any mode which naturally allocates into the float
+   registers should return 4 here.  */
+
+unsigned int
+sparc_regmode_natural_size (enum machine_mode mode)
+{
+  int size = UNITS_PER_WORD;
+
+  if (TARGET_ARCH64)
+    {
+      enum mode_class mclass = GET_MODE_CLASS (mode);
+
+      if (mclass == MODE_FLOAT || mclass == MODE_VECTOR_INT)
+       size = 4;
+    }
+
+  return size;
+}
+
+/* Return TRUE if it is a good idea to tie two pseudo registers
+   when one has mode MODE1 and one has mode MODE2.
+   If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+   for any hard reg, then this must be FALSE for correct output.
+
+   For V9 we have to deal with the fact that only the lower 32 floating
+   point registers are 32-bit addressable.  */
+
+bool
+sparc_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+  enum mode_class mclass1, mclass2;
+  unsigned short size1, size2;
+
+  if (mode1 == mode2)
+    return true;
+
+  mclass1 = GET_MODE_CLASS (mode1);
+  mclass2 = GET_MODE_CLASS (mode2);
+  if (mclass1 != mclass2)
+    return false;
+
+  if (! TARGET_V9)
+    return true;
+
+  /* Classes are the same and we are V9 so we have to deal with upper
+     vs. lower floating point registers.  If one of the modes is a
+     4-byte mode, and the other is not, we have to mark them as not
+     tieable because only the lower 32 floating point register are
+     addressable 32-bits at a time.
+
+     We can't just test explicitly for SFmode, otherwise we won't
+     cover the vector mode cases properly.  */
+
+  if (mclass1 != MODE_FLOAT && mclass1 != MODE_VECTOR_INT)
+    return true;
+
+  size1 = GET_MODE_SIZE (mode1);
+  size2 = GET_MODE_SIZE (mode2);
+  if ((size1 > 4 && size2 == 4)
+      || (size2 > 4 && size1 == 4))
+    return false;
+
+  return true;
+}
+
 #include "gt-sparc.h"