xtensa-config.h (XCHAL_HAVE_THREADPTR): New.
authorBob Wilson <bob.wilson@acm.org>
Wed, 18 Jul 2007 18:51:21 +0000 (18:51 +0000)
committerBob Wilson <bwilson@gcc.gnu.org>
Wed, 18 Jul 2007 18:51:21 +0000 (18:51 +0000)
include/
* xtensa-config.h (XCHAL_HAVE_THREADPTR): New.
(XCHAL_HAVE_RELEASE_SYNC, XCHAL_HAVE_S32C1I): New.
gcc/
* config/xtensa/xtensa.c (xtensa_expand_mask_and_shift): New.
(struct alignment_context, init_alignment_context): New.
(xtensa_expand_compare_and_swap, xtensa_expand_atomic): New.
* config/xtensa/xtensa.h (XCHAL_HAVE_RELEASE_SYNC): Add default.
(XCHAL_HAVE_S32C1I): Likewise.
(TARGET_RELEASE_SYNC, TARGET_S32C1I): New.
* config/xtensa/xtensa.md (UNSPECV_MEMW): New constant.
(UNSPECV_S32RI, UNSPECV_S32C1I): Likewise.
(ATOMIC, HQI): New macros.
(memory_barrier, *memory_barrier): New.
(sync_lock_releasesi): New.
(sync_compare_and_swapsi, sync_compare_and_swap<mode>): New.
(sync_lock_test_and_set<mode>): New.
(sync_<atomic><mode>): New.
(sync_old_<atomic><mode>, sync_new_<atomic><mode>): New.
* config/xtensa/xtensa-protos.h (xtensa_expand_compare_and_swap): New.
(xtensa_expand_atomic): New.
gcc/testsuite/
* lib/target-supports.exp (check_effective_target_sync_int_long):
Enable for xtensa.
(check_effective_target_sync_char_short): Likewise.

From-SVN: r126728

gcc/ChangeLog
gcc/config/xtensa/xtensa-protos.h
gcc/config/xtensa/xtensa.c
gcc/config/xtensa/xtensa.h
gcc/config/xtensa/xtensa.md
gcc/testsuite/ChangeLog
gcc/testsuite/lib/target-supports.exp
include/ChangeLog
include/xtensa-config.h

index 24b7c2e..484d9b5 100644 (file)
@@ -1,3 +1,23 @@
+2007-07-18  Bob Wilson  <bob.wilson@acm.org>
+       
+       * config/xtensa/xtensa.c (xtensa_expand_mask_and_shift): New.
+       (struct alignment_context, init_alignment_context): New.
+       (xtensa_expand_compare_and_swap, xtensa_expand_atomic): New.
+       * config/xtensa/xtensa.h (XCHAL_HAVE_RELEASE_SYNC): Add default.
+       (XCHAL_HAVE_S32C1I): Likewise.
+       (TARGET_RELEASE_SYNC, TARGET_S32C1I): New.
+       * config/xtensa/xtensa.md (UNSPECV_MEMW): New constant.
+       (UNSPECV_S32RI, UNSPECV_S32C1I): Likewise.
+       (ATOMIC, HQI): New macros.
+       (memory_barrier, *memory_barrier): New.
+       (sync_lock_releasesi): New.
+       (sync_compare_and_swapsi, sync_compare_and_swap<mode>): New.
+       (sync_lock_test_and_set<mode>): New.
+       (sync_<atomic><mode>): New.
+       (sync_old_<atomic><mode>, sync_new_<atomic><mode>): New.
+       * config/xtensa/xtensa-protos.h (xtensa_expand_compare_and_swap): New.
+       (xtensa_expand_atomic): New.
+       
 2007-07-18  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
        PR target/30652
index f6cca7a..27b0a48 100644 (file)
@@ -47,6 +47,8 @@ extern void xtensa_split_operand_pair (rtx *, enum machine_mode);
 extern int xtensa_emit_move_sequence (rtx *, enum machine_mode);
 extern rtx xtensa_copy_incoming_a7 (rtx);
 extern void xtensa_expand_nonlocal_goto (rtx *);
+extern void xtensa_expand_compare_and_swap (rtx, rtx, rtx, rtx);
+extern void xtensa_expand_atomic (enum rtx_code, rtx, rtx, rtx, bool);
 extern void xtensa_emit_loop_end (rtx, rtx *);
 extern char *xtensa_emit_branch (bool, bool, rtx *);
 extern char *xtensa_emit_bit_branch (bool, bool, rtx *);
index 1868727..4d21b65 100644 (file)
@@ -1198,6 +1198,263 @@ xtensa_init_machine_status (void)
 }
 
 
+/* Shift VAL of mode MODE left by COUNT bits.  */
+
+static inline rtx
+xtensa_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
+{
+  val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
+                            NULL_RTX, 1, OPTAB_DIRECT);
+  return expand_simple_binop (SImode, ASHIFT, val, count,
+                             NULL_RTX, 1, OPTAB_DIRECT);
+}
+
+
+/* Structure to hold the initial parameters for a compare_and_swap operation
+   in HImode and QImode.  */
+
+struct alignment_context
+{
+  rtx memsi;     /* SI aligned memory location.  */
+  rtx shift;     /* Bit offset with regard to lsb.  */
+  rtx modemask;          /* Mask of the HQImode shifted by SHIFT bits.  */
+  rtx modemaski;  /* ~modemask */
+};
+
+
+/* Initialize structure AC for word access to HI and QI mode memory.  */
+
+static void
+init_alignment_context (struct alignment_context *ac, rtx mem)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  rtx byteoffset = NULL_RTX;
+  bool aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
+
+  if (aligned)
+    ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned.  */
+  else
+    {
+      /* Alignment is unknown.  */
+      rtx addr, align;
+
+      /* Force the address into a register.  */
+      addr = force_reg (Pmode, XEXP (mem, 0));
+
+      /* Align it to SImode.  */
+      align = expand_simple_binop (Pmode, AND, addr,
+                                  GEN_INT (-GET_MODE_SIZE (SImode)),
+                                  NULL_RTX, 1, OPTAB_DIRECT);
+      /* Generate MEM.  */
+      ac->memsi = gen_rtx_MEM (SImode, align);
+      MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
+      set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
+      set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
+
+      byteoffset = expand_simple_binop (Pmode, AND, addr,
+                                       GEN_INT (GET_MODE_SIZE (SImode) - 1),
+                                       NULL_RTX, 1, OPTAB_DIRECT);
+    }
+
+  /* Calculate shiftcount.  */
+  if (TARGET_BIG_ENDIAN)
+    {
+      ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
+      if (!aligned)
+       ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
+                                        NULL_RTX, 1, OPTAB_DIRECT);
+    }
+  else
+    {
+      if (aligned)
+       ac->shift = NULL_RTX;
+      else
+       ac->shift = byteoffset;
+    }
+
+  if (ac->shift != NULL_RTX)
+    {
+      /* Shift is the byte count, but we need the bitcount.  */
+      ac->shift = expand_simple_binop (SImode, MULT, ac->shift,
+                                      GEN_INT (BITS_PER_UNIT),
+                                      NULL_RTX, 1, OPTAB_DIRECT);
+      ac->modemask = expand_simple_binop (SImode, ASHIFT,
+                                         GEN_INT (GET_MODE_MASK (mode)),
+                                         ac->shift,
+                                         NULL_RTX, 1, OPTAB_DIRECT);
+    }
+  else
+    ac->modemask = GEN_INT (GET_MODE_MASK (mode));
+
+  ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
+}
+
+
+/* Expand an atomic compare and swap operation for HImode and QImode.
+   MEM is the memory location, CMP the old value to compare MEM with
+   and NEW the value to set if CMP == MEM.  */
+
+void
+xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  struct alignment_context ac;
+  rtx tmp, cmpv, newv, val;
+  rtx oldval = gen_reg_rtx (SImode);
+  rtx res = gen_reg_rtx (SImode);
+  rtx csloop = gen_label_rtx ();
+  rtx csend = gen_label_rtx ();
+
+  init_alignment_context (&ac, mem);
+
+  if (ac.shift != NULL_RTX)
+    {
+      cmp = xtensa_expand_mask_and_shift (cmp, mode, ac.shift);
+      new = xtensa_expand_mask_and_shift (new, mode, ac.shift);
+    }
+
+  /* Load the surrounding word into VAL with the MEM value masked out.  */
+  val = force_reg (SImode, expand_simple_binop (SImode, AND, ac.memsi,
+                                               ac.modemaski, NULL_RTX, 1,
+                                               OPTAB_DIRECT));
+  emit_label (csloop);
+
+  /* Patch CMP and NEW into VAL at correct position.  */
+  cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
+                                                NULL_RTX, 1, OPTAB_DIRECT));
+  newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val,
+                                                NULL_RTX, 1, OPTAB_DIRECT));
+
+  /* Jump to end if we're done.  */
+  emit_insn (gen_sync_compare_and_swapsi (res, ac.memsi, cmpv, newv));
+  emit_cmp_and_jump_insns (res, cmpv, EQ, const0_rtx, SImode, true, csend);
+
+  /* Check for changes outside mode.  */
+  emit_move_insn (oldval, val);
+  tmp = expand_simple_binop (SImode, AND, res, ac.modemaski,
+                            val, 1, OPTAB_DIRECT);
+  if (tmp != val)
+    emit_move_insn (val, tmp);
+
+  /* Loop internal if so.  */
+  emit_cmp_and_jump_insns (oldval, val, NE, const0_rtx, SImode, true, csloop);
+
+  emit_label (csend);
+
+  /* Return the correct part of the bitfield.  */
+  convert_move (target,
+               (ac.shift == NULL_RTX ? res
+                : expand_simple_binop (SImode, LSHIFTRT, res, ac.shift,
+                                       NULL_RTX, 1, OPTAB_DIRECT)),
+               1);
+}
+
+
+/* Expand an atomic operation CODE of mode MODE (either HImode or QImode --
+   the default expansion works fine for SImode).  MEM is the memory location
+   and VAL the value to play with.  If AFTER is true then store the value
+   MEM holds after the operation, if AFTER is false then store the value MEM
+   holds before the operation.  If TARGET is zero then discard that value, else
+   store it to TARGET.  */
+
+void
+xtensa_expand_atomic (enum rtx_code code, rtx target, rtx mem, rtx val,
+                     bool after)
+{
+  enum machine_mode mode = GET_MODE (mem);
+  struct alignment_context ac;
+  rtx csloop = gen_label_rtx ();
+  rtx cmp, tmp;
+  rtx old = gen_reg_rtx (SImode);
+  rtx new = gen_reg_rtx (SImode);
+  rtx orig = NULL_RTX;
+
+  init_alignment_context (&ac, mem);
+
+  /* Prepare values before the compare-and-swap loop.  */
+  if (ac.shift != NULL_RTX)
+    val = xtensa_expand_mask_and_shift (val, mode, ac.shift);
+  switch (code)
+    {
+    case PLUS:
+    case MINUS:
+      orig = gen_reg_rtx (SImode);
+      convert_move (orig, val, 1);
+      break;
+
+    case SET:
+    case IOR:
+    case XOR:
+      break;
+
+    case MULT: /* NAND */
+    case AND:
+      /* val = "11..1<val>11..1" */
+      val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Load full word.  Subsequent loads are performed by S32C1I.  */
+  cmp = force_reg (SImode, ac.memsi);
+
+  emit_label (csloop);
+  emit_move_insn (old, cmp);
+
+  switch (code)
+    {
+    case PLUS:
+    case MINUS:
+      val = expand_simple_binop (SImode, code, old, orig,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      val = expand_simple_binop (SImode, AND, val, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      /* FALLTHRU */
+    case SET:
+      tmp = expand_simple_binop (SImode, AND, old, ac.modemaski,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      tmp = expand_simple_binop (SImode, IOR, tmp, val,
+                                new, 1, OPTAB_DIRECT);
+      break;
+
+    case AND:
+    case IOR:
+    case XOR:
+      tmp = expand_simple_binop (SImode, code, old, val,
+                                new, 1, OPTAB_DIRECT);
+      break;
+
+    case MULT: /* NAND */
+      tmp = expand_simple_binop (SImode, XOR, old, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      tmp = expand_simple_binop (SImode, AND, tmp, val,
+                                new, 1, OPTAB_DIRECT);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  if (tmp != new)
+    emit_move_insn (new, tmp);
+  emit_insn (gen_sync_compare_and_swapsi (cmp, ac.memsi, old, new));
+  emit_cmp_and_jump_insns (cmp, old, NE, const0_rtx, SImode, true, csloop);
+
+  if (target)
+    {
+      tmp = (after ? new : cmp);
+      convert_move (target,
+                   (ac.shift == NULL_RTX ? tmp
+                    : expand_simple_binop (SImode, LSHIFTRT, tmp, ac.shift,
+                                           NULL_RTX, 1, OPTAB_DIRECT)),
+                   1);
+    }
+}
+
+
 void
 xtensa_setup_frame_addresses (void)
 {
index 11c4184..e85dc26 100644 (file)
@@ -47,6 +47,12 @@ extern unsigned xtensa_current_frame_size;
 #ifndef XCHAL_HAVE_MUL32_HIGH
 #define XCHAL_HAVE_MUL32_HIGH 0
 #endif
+#ifndef XCHAL_HAVE_RELEASE_SYNC
+#define XCHAL_HAVE_RELEASE_SYNC 0
+#endif
+#ifndef XCHAL_HAVE_S32C1I
+#define XCHAL_HAVE_S32C1I 0
+#endif
 #define TARGET_BIG_ENDIAN      XCHAL_HAVE_BE
 #define TARGET_DENSITY         XCHAL_HAVE_DENSITY
 #define TARGET_MAC16           XCHAL_HAVE_MAC16
@@ -65,6 +71,8 @@ extern unsigned xtensa_current_frame_size;
 #define TARGET_HARD_FLOAT_RSQRT        XCHAL_HAVE_FP_RSQRT
 #define TARGET_ABS             XCHAL_HAVE_ABS
 #define TARGET_ADDX            XCHAL_HAVE_ADDX
+#define TARGET_RELEASE_SYNC    XCHAL_HAVE_RELEASE_SYNC
+#define TARGET_S32C1I          XCHAL_HAVE_S32C1I
 
 #define TARGET_DEFAULT (                                               \
   (XCHAL_HAVE_L32R     ? 0 : MASK_CONST16))
index 12dae4d..f2d218d 100644 (file)
   (UNSPEC_NOP          2)
   (UNSPEC_PLT          3)
   (UNSPEC_RET_ADDR     4)
+
   (UNSPECV_SET_FP      1)
   (UNSPECV_ENTRY       2)
+  (UNSPECV_MEMW                3)
+  (UNSPECV_S32RI       4)
+  (UNSPECV_S32C1I      5)
 ])
 
 ;; This code macro allows signed and unsigned widening multiplications
 ;; This code macro is for floating-point comparisons.
 (define_code_macro any_scc_sf [eq lt le])
 
+;; These macros allow to combine most atomic operations.
+(define_code_macro ATOMIC [and ior xor plus minus mult])
+(define_code_attr atomic [(and "and") (ior "ior") (xor "xor") 
+                         (plus "add") (minus "sub") (mult "nand")])
+
+;; These mode macros allow the HI and QI patterns to be defined from
+;; the same template.
+(define_mode_macro HQI [HI QI])
+
 \f
 ;; Attributes.
 
@@ -1687,3 +1700,113 @@ srli\t%3, %3, 30\;slli\t%0, %1, 2\;ssai\t2\;src\t%0, %3, %0"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "3")])
+
+\f
+;; Atomic operations
+
+(define_expand "memory_barrier"
+  [(set (mem:BLK (match_dup 0))
+       (unspec_volatile:BLK [(mem:BLK (match_dup 0))] UNSPECV_MEMW))]
+  ""
+{
+  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (SImode));
+  MEM_VOLATILE_P (operands[0]) = 1;
+})
+
+(define_insn "*memory_barrier"
+  [(set (match_operand:BLK 0 "" "")
+       (unspec_volatile:BLK [(match_operand:BLK 1 "" "")] UNSPECV_MEMW))]
+  ""
+  "memw"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "3")])
+
+;; sync_lock_release is only implemented for SImode.
+;; For other modes, just use the default of a store with a memory_barrier.
+(define_insn "sync_lock_releasesi"
+  [(set (match_operand:SI 0 "mem_operand" "=U")
+       (unspec_volatile:SI
+         [(match_operand:SI 1 "register_operand" "r")]
+         UNSPECV_S32RI))]
+  "TARGET_RELEASE_SYNC"
+  "s32ri\t%1, %0"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "3")])
+
+(define_insn "sync_compare_and_swapsi"
+  [(parallel
+    [(set (match_operand:SI 0 "register_operand" "=a")
+         (match_operand:SI 1 "mem_operand" "+U"))
+     (set (match_dup 1)
+         (unspec_volatile:SI
+           [(match_dup 1)
+            (match_operand:SI 2 "register_operand" "r")
+            (match_operand:SI 3 "register_operand" "0")]
+           UNSPECV_S32C1I))])]
+  "TARGET_S32C1I"
+  "wsr\t%2, SCOMPARE1\;s32c1i\t%3, %1"
+  [(set_attr "type"    "multi")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "6")])
+
+(define_expand "sync_compare_and_swap<mode>"
+  [(parallel
+    [(set (match_operand:HQI 0 "register_operand" "")
+         (match_operand:HQI 1 "mem_operand" ""))
+     (set (match_dup 1)
+         (unspec_volatile:HQI
+           [(match_dup 1)
+            (match_operand:HQI 2 "register_operand" "")
+            (match_operand:HQI 3 "register_operand" "")]
+           UNSPECV_S32C1I))])]
+  "TARGET_S32C1I"
+{
+  xtensa_expand_compare_and_swap (operands[0], operands[1],
+                                 operands[2], operands[3]);
+  DONE;
+})
+
+(define_expand "sync_lock_test_and_set<mode>"
+  [(match_operand:HQI 0 "register_operand")
+   (match_operand:HQI 1 "memory_operand")
+   (match_operand:HQI 2 "register_operand")]
+  "TARGET_S32C1I"
+{
+  xtensa_expand_atomic (SET, operands[0], operands[1], operands[2], false);
+  DONE;
+})
+
+(define_expand "sync_<atomic><mode>"
+  [(set (match_operand:HQI 0 "memory_operand")
+       (ATOMIC:HQI (match_dup 0)
+                   (match_operand:HQI 1 "register_operand")))]
+  "TARGET_S32C1I"
+{
+  xtensa_expand_atomic (<CODE>, NULL_RTX, operands[0], operands[1], false);
+  DONE;
+})
+
+(define_expand "sync_old_<atomic><mode>"
+  [(set (match_operand:HQI 0 "register_operand")
+       (match_operand:HQI 1 "memory_operand"))
+   (set (match_dup 1)
+       (ATOMIC:HQI (match_dup 1)
+                   (match_operand:HQI 2 "register_operand")))]
+  "TARGET_S32C1I"
+{
+  xtensa_expand_atomic (<CODE>, operands[0], operands[1], operands[2], false);
+  DONE;
+})
+
+(define_expand "sync_new_<atomic><mode>"
+  [(set (match_operand:HQI 0 "register_operand")
+       (ATOMIC:HQI (match_operand:HQI 1 "memory_operand")
+                   (match_operand:HQI 2 "register_operand"))) 
+   (set (match_dup 1) (ATOMIC:HQI (match_dup 1) (match_dup 2)))]
+  "TARGET_S32C1I"
+{
+  xtensa_expand_atomic (<CODE>, operands[0], operands[1], operands[2], true);
+  DONE;
+})
index ab18376..72564be 100644 (file)
@@ -1,3 +1,9 @@
+2007-07-18  Bob Wilson  <bob.wilson@acm.org>
+       
+       * lib/target-supports.exp (check_effective_target_sync_int_long):
+       Enable for xtensa.
+       (check_effective_target_sync_char_short): Likewise.
+       
 2007-07-18  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
        * gcc.dg/pr28796-2.c: Add more cases.
index 4a5ae03..c52a050 100644 (file)
@@ -2188,7 +2188,8 @@ proc check_effective_target_sync_int_long { } {
             || [istarget s390*-*-*] 
             || [istarget powerpc*-*-*]
             || [istarget sparc64-*-*]
-            || [istarget sparcv9-*-*] } {
+            || [istarget sparcv9-*-*]
+            || [istarget xtensa-*-*] } {
            set et_sync_int_long_saved 1
         }
     }
@@ -2215,7 +2216,8 @@ proc check_effective_target_sync_char_short { } {
             || [istarget s390*-*-*] 
             || [istarget powerpc*-*-*]
             || [istarget sparc64-*-*]
-            || [istarget sparcv9-*-*] } {
+            || [istarget sparcv9-*-*]
+            || [istarget xtensa-*-*] } {
            set et_sync_char_short_saved 1
         }
     }
index 6d54a48..fe03cb2 100644 (file)
@@ -1,3 +1,8 @@
+2007-07-18  Bob Wilson  <bob.wilson@acm.org>
+       
+       * xtensa-config.h (XCHAL_HAVE_THREADPTR): New.
+       (XCHAL_HAVE_RELEASE_SYNC, XCHAL_HAVE_S32C1I): New.
+       
 2007-07-17  Nick Clifton  <nickc@redhat.com>
 
        * COPYING3: New file.  Contains version 3 of the GNU General
index 36f9719..2aca5fe 100644 (file)
@@ -1,5 +1,5 @@
 /* Xtensa configuration settings.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
    Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
 
 #undef XCHAL_HAVE_LOOPS
 #define XCHAL_HAVE_LOOPS               1
 
+#undef XCHAL_HAVE_THREADPTR
+#define XCHAL_HAVE_THREADPTR           1
+
+#undef XCHAL_HAVE_RELEASE_SYNC
+#define XCHAL_HAVE_RELEASE_SYNC                0
+
+#undef XCHAL_HAVE_S32C1I
+#define XCHAL_HAVE_S32C1I              0
+
 #undef XCHAL_HAVE_BOOLEANS
 #define XCHAL_HAVE_BOOLEANS            0