OSDN Git Service

* config/sparc/sparc.md (UNSPEC_FRAME_BLOCKAGE): New constant.
[pf3gnuchains/gcc-fork.git] / gcc / config / sparc / sparc.c
index b57a158..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;
@@ -4956,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
@@ -4980,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
        {
@@ -9120,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];
 
@@ -9148,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
@@ -10846,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);
@@ -10875,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,
@@ -10921,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);
 
@@ -10929,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));
 
@@ -10947,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)
 {
@@ -11156,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,
@@ -11206,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];
 
@@ -11222,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";
        }
@@ -11230,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";
@@ -11241,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";
        }
@@ -11249,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";
@@ -11262,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";
     }
@@ -11272,46 +11435,50 @@ 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;
+  rtx t1, final_insn;
+  int bmask;
 
-      t1 = gen_reg_rtx (mode);
+  t1 = gen_reg_rtx (mode);
 
-      elt = convert_modes (SImode, inner_mode, elt, true);
-      emit_move_insn (gen_lowpart(SImode, t1), elt);
+  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 ();
-       }
+  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);
+  emit_insn (gen_bmasksi_vis (gen_reg_rtx (SImode), CONST0_RTX (SImode),
+                             force_reg (SImode, GEN_INT (bmask))));
+  emit_insn (final_insn);
 }
 
 static void
@@ -11359,6 +11526,8 @@ vector_init_faligndata (rtx target, rtx elt, enum machine_mode inner_mode)
   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)
 {
@@ -11433,11 +11602,13 @@ sparc_expand_vector_init (rtx target, rtx vals)
   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)
@@ -11501,6 +11672,9 @@ 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)
 {
@@ -11509,12 +11683,16 @@ sparc_expand_conditional_move (enum machine_mode mode, rtx *operands)
   rtx cc_reg, dst, cmp;
 
   cmp = operands[1];
-  cmp_mode = GET_MODE (XEXP (cmp, 0));
-  if (cmp_mode == DImode && !TARGET_ARCH64)
+  if (GET_MODE (XEXP (cmp, 0)) == DImode && !TARGET_ARCH64)
     return false;
 
-  dst = operands[0];
+  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))
     {
@@ -11533,9 +11711,6 @@ sparc_expand_conditional_move (enum machine_mode mode, rtx *operands)
         rc = reverse_condition (rc);
     }
 
-  if (cmp_mode == TFmode && !TARGET_HARD_QUAD)
-    cmp = sparc_emit_float_lib_cmp (XEXP (cmp, 0), XEXP (cmp, 1), rc);
-
   if (XEXP (cmp, 1) == const0_rtx
       && GET_CODE (XEXP (cmp, 0)) == REG
       && cmp_mode == DImode
@@ -11555,6 +11730,12 @@ sparc_expand_conditional_move (enum machine_mode mode, rtx *operands)
   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)
 {
@@ -11592,4 +11773,69 @@ sparc_expand_vcond (enum machine_mode mode, rtx *operands, int ccode, int fcode)
   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"