alpha.c (alpha_split_atomic_op): New.
authorRichard Henderson <rth@redhat.com>
Mon, 18 Apr 2005 16:13:00 +0000 (09:13 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Mon, 18 Apr 2005 16:13:00 +0000 (09:13 -0700)
        * config/alpha/alpha.c (alpha_split_atomic_op): New.
        (alphaev5_insn_pipe): Add LD_L, ST_C, MB types.
        (alphaev4_insn_pipe): Likewise.  Correct IST and LDSYM pipes.
        * config/alpha/alpha-protos.h: Update.
        * config/alpha/alpha.md (UNSPECV_MB, UNSPECV_LL, UNSPECV_SC): New.
        (UNSPECV_ATOMIC, UNSPECV_CMPXCHG, UNSPECV_XCHG): New.
        (attr type): Add ld_l, st_c, mb.
        (andsi_internal, andnotsi3, iorsi_internal, one_cmplsi_internal,
        iornotsi3, xorsi_internal, xornotsi3): New.
        * config/alpha/ev4.md (ev4_ld): Add ld_l.
        (ev4_ist_c, ev4_mb): New.
        * config/alpha/ev5.md (ev5_st): Add st_c, mb.
        (ev5_ld_l): New.
        * config/alpha/ev6.md (ev6_ild): Add ld_l.
        (ev6_ist): Add st_c.
        (ev6_mb): New.
        * config/alpha/sync.md: New file.

From-SVN: r98328

gcc/ChangeLog
gcc/config/alpha/alpha-protos.h
gcc/config/alpha/alpha.c
gcc/config/alpha/alpha.md
gcc/config/alpha/ev4.md
gcc/config/alpha/ev5.md
gcc/config/alpha/ev6.md
gcc/config/alpha/sync.md [new file with mode: 0644]

index eb81e48..335f5ff 100644 (file)
@@ -1,5 +1,25 @@
 2005-04-18  Richard Henderson  <rth@redhat.com>
 
+       * config/alpha/alpha.c (alpha_split_atomic_op): New.
+       (alphaev5_insn_pipe): Add LD_L, ST_C, MB types.
+       (alphaev4_insn_pipe): Likewise.  Correct IST and LDSYM pipes.
+       * config/alpha/alpha-protos.h: Update.
+       * config/alpha/alpha.md (UNSPECV_MB, UNSPECV_LL, UNSPECV_SC): New.
+       (UNSPECV_ATOMIC, UNSPECV_CMPXCHG, UNSPECV_XCHG): New.
+       (attr type): Add ld_l, st_c, mb.
+       (andsi_internal, andnotsi3, iorsi_internal, one_cmplsi_internal,
+       iornotsi3, xorsi_internal, xornotsi3): New.
+       * config/alpha/ev4.md (ev4_ld): Add ld_l.
+       (ev4_ist_c, ev4_mb): New.
+       * config/alpha/ev5.md (ev5_st): Add st_c, mb.
+       (ev5_ld_l): New.
+       * config/alpha/ev6.md (ev6_ild): Add ld_l.
+       (ev6_ist): Add st_c.
+       (ev6_mb): New.
+       * config/alpha/sync.md: New file.
+
+2005-04-18  Richard Henderson  <rth@redhat.com>
+
        * builtins.c (expand_builtin_sync_operation): Fold nand to and
        for constants.
 
index 6c2be06..dc2b027 100644 (file)
@@ -101,6 +101,7 @@ extern rtx alpha_emit_setcc (enum rtx_code);
 extern int alpha_split_conditional_move (enum rtx_code, rtx, rtx, rtx, rtx);
 extern void alpha_emit_xfloating_arith (enum rtx_code, rtx[]);
 extern void alpha_emit_xfloating_cvt (enum rtx_code, rtx[]);
+extern void alpha_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx);
 #endif
 
 extern rtx alpha_need_linkage (const char *, int);
index 71ba449..895189b 100644 (file)
@@ -4469,6 +4469,64 @@ alpha_expand_builtin_vector_binop (rtx (*gen) (rtx, rtx, rtx),
 
   emit_insn ((*gen) (op0, op1, op2));
 }
+
+/* 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
+alpha_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, cond, x;
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+
+  emit_insn (gen_memory_barrier ());
+
+  label = gen_label_rtx ();
+  emit_label (label);
+  label = gen_rtx_LABEL_REF (DImode, label);
+
+  if (before == NULL)
+    before = scratch;
+
+  if (mode == SImode)
+    emit_insn (gen_load_locked_si (before, mem));
+  else if (mode == DImode)
+    emit_insn (gen_load_locked_di (before, mem));
+  else
+    gcc_unreachable ();
+
+  if (code == NOT)
+    {
+      x = gen_rtx_NOT (mode, val);
+      x = gen_rtx_AND (mode, x, before);
+    }
+  else
+    x = gen_rtx_fmt_ee (code, mode, before, val);
+
+  emit_insn (gen_rtx_SET (VOIDmode, scratch, x));
+  if (after)
+    emit_insn (gen_rtx_SET (VOIDmode, after, copy_rtx (x)));
+
+  cond = gen_rtx_REG (DImode, REGNO (scratch));
+  if (mode == SImode)
+    emit_insn (gen_store_conditional_si (cond, mem, scratch));
+  else if (mode == DImode)
+    emit_insn (gen_store_conditional_di (cond, mem, scratch));
+  else
+    gcc_unreachable ();
+
+  x = gen_rtx_EQ (DImode, cond, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, 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);
+
+  emit_insn (gen_memory_barrier ());
+}
 \f
 /* Adjust the cost of a scheduling dependency.  Return the new cost of
    a dependency LINK or INSN on DEP_INSN.  COST is the current cost.  */
@@ -8479,21 +8537,22 @@ alphaev4_insn_pipe (rtx insn)
   switch (get_attr_type (insn))
     {
     case TYPE_ILD:
+    case TYPE_LDSYM:
     case TYPE_FLD:
+    case TYPE_LD_L:
       return EV4_IBX;
 
-    case TYPE_LDSYM:
     case TYPE_IADD:
     case TYPE_ILOG:
     case TYPE_ICMOV:
     case TYPE_ICMP:
-    case TYPE_IST:
     case TYPE_FST:
     case TYPE_SHIFT:
     case TYPE_IMUL:
     case TYPE_FBR:
       return EV4_IB0;
 
+    case TYPE_IST:
     case TYPE_MISC:
     case TYPE_IBR:
     case TYPE_JSR:
@@ -8503,6 +8562,8 @@ alphaev4_insn_pipe (rtx insn)
     case TYPE_FADD:
     case TYPE_FDIV:
     case TYPE_FMUL:
+    case TYPE_ST_C:
+    case TYPE_MB:
       return EV4_IB1;
 
     default:
@@ -8535,6 +8596,9 @@ alphaev5_insn_pipe (rtx insn)
     case TYPE_IMUL:
     case TYPE_MISC:
     case TYPE_MVI:
+    case TYPE_LD_L:
+    case TYPE_ST_C:
+    case TYPE_MB:
       return EV5_E0;
 
     case TYPE_IBR:
index 208161b..644b5b0 100644 (file)
    (UNSPECV_SET_TP     12)
    (UNSPECV_RPCC       13)
    (UNSPECV_SETJMPR_ER 14)     ; builtin_setjmp_receiver fragment
+   (UNSPECV_MB         15)
+   (UNSPECV_LL         16)     ; load-locked
+   (UNSPECV_SC         17)     ; store-conditional
+   (UNSPECV_ATOMIC     18)
+   (UNSPECV_CMPXCHG    19)
+   (UNSPECV_XCHG       20)
   ])
 
 ;; Where necessary, the suffixes _le and _be are used to distinguish between
 
 (define_attr "type"
   "ild,fld,ldsym,ist,fst,ibr,callpal,fbr,jsr,iadd,ilog,shift,icmov,fcmov,
-   icmp,imul,fadd,fmul,fcpys,fdiv,fsqrt,misc,mvi,ftoi,itof,multi,none"
+   icmp,imul,fadd,fmul,fcpys,fdiv,fsqrt,misc,mvi,ftoi,itof,mb,ld_l,st_c,
+   multi,none"
   (const_string "iadd"))
 
 ;; Describe a user's asm statement.
   [(set_attr "type" "jsr")
    (set_attr "length" "8")])
 \f
-;; Next are the basic logical operations.  These only exist in DImode.
+;; Next are the basic logical operations.  We only expose the DImode operations
+;; to the rtl expanders, but SImode versions exist for combine as well as for
+;; the atomic operation splitters.
+
+(define_insn "*andsi_internal"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
+       (and:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ,rJ,rJ")
+               (match_operand:SI 2 "and_operand" "rI,N,MH")))]
+  ""
+  "@
+   and %r1,%2,%0
+   bic %r1,%N2,%0
+   zapnot %r1,%m2,%0"
+  [(set_attr "type" "ilog,ilog,shift")])
 
 (define_insn "anddi3"
   [(set (match_operand:DI 0 "register_operand" "=r,r,r")
   "zapnot %1,15,%0"
   [(set_attr "type" "shift")])
 
+(define_insn "*andnotsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (and:SI (not:SI (match_operand:SI 1 "reg_or_8bit_operand" "rI"))
+               (match_operand:SI 2 "reg_or_0_operand" "rJ")))]
+  ""
+  "bic %r2,%1,%0"
+  [(set_attr "type" "ilog")])
+
 (define_insn "andnotdi3"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (and:DI (not:DI (match_operand:DI 1 "reg_or_8bit_operand" "rI"))
   "bic %r2,%1,%0"
   [(set_attr "type" "ilog")])
 
+(define_insn "*iorsi_internal"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+       (ior:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ,rJ")
+               (match_operand:SI 2 "or_operand" "rI,N")))]
+  ""
+  "@
+   bis %r1,%2,%0
+   ornot %r1,%N2,%0"
+  [(set_attr "type" "ilog")])
+
 (define_insn "iordi3"
   [(set (match_operand:DI 0 "register_operand" "=r,r")
        (ior:DI (match_operand:DI 1 "reg_or_0_operand" "%rJ,rJ")
    ornot %r1,%N2,%0"
   [(set_attr "type" "ilog")])
 
+(define_insn "*one_cmplsi_internal"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (match_operand:SI 1 "reg_or_8bit_operand" "rI")))]
+  ""
+  "ornot $31,%1,%0"
+  [(set_attr "type" "ilog")])
+
 (define_insn "one_cmpldi2"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (not:DI (match_operand:DI 1 "reg_or_8bit_operand" "rI")))]
   "ornot $31,%1,%0"
   [(set_attr "type" "ilog")])
 
-(define_insn "*iornot"
+(define_insn "*iornotsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ior:SI (not:SI (match_operand:SI 1 "reg_or_8bit_operand" "rI"))
+               (match_operand:SI 2 "reg_or_0_operand" "rJ")))]
+  ""
+  "ornot %r2,%1,%0"
+  [(set_attr "type" "ilog")])
+
+(define_insn "*iornotdi3"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (ior:DI (not:DI (match_operand:DI 1 "reg_or_8bit_operand" "rI"))
                (match_operand:DI 2 "reg_or_0_operand" "rJ")))]
   "ornot %r2,%1,%0"
   [(set_attr "type" "ilog")])
 
+(define_insn "*xorsi_internal"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+       (xor:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ,rJ")
+               (match_operand:SI 2 "or_operand" "rI,N")))]
+  ""
+  "@
+   xor %r1,%2,%0
+   eqv %r1,%N2,%0"
+  [(set_attr "type" "ilog")])
+
 (define_insn "xordi3"
   [(set (match_operand:DI 0 "register_operand" "=r,r")
        (xor:DI (match_operand:DI 1 "reg_or_0_operand" "%rJ,rJ")
    eqv %r1,%N2,%0"
   [(set_attr "type" "ilog")])
 
-(define_insn "*xornot"
+(define_insn "*xornotsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (xor:SI (match_operand:SI 1 "register_operand" "%rJ")
+                       (match_operand:SI 2 "register_operand" "rI"))))]
+  ""
+  "eqv %r1,%2,%0"
+  [(set_attr "type" "ilog")])
+
+(define_insn "*xornotdi3"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (not:DI (xor:DI (match_operand:DI 1 "register_operand" "%rJ")
                        (match_operand:DI 2 "register_operand" "rI"))))]
   "unpkbw %r1,%0"
   [(set_attr "type" "mvi")])
 \f
+(include "sync.md")
+\f
 ;; The call patterns are at the end of the file because their
 ;; wildcard operand0 interferes with nice recognition.
 
index cba847e..52f2556 100644 (file)
@@ -39,7 +39,7 @@
 ; in user-specified memory latency, so return 1 here.
 (define_insn_reservation "ev4_ld" 1
   (and (eq_attr "tune" "ev4")
-       (eq_attr "type" "ild,fld,ldsym"))
+       (eq_attr "type" "ild,fld,ldsym,ld_l"))
   "ev4_ib01+ev4_abox")
 
 ; Stores can issue before the data (but not address) is ready.
        (eq_attr "type" "ist"))
   "ev4_ib1+ev4_abox")
 
+; ??? Separate from ev4_ist because store_data_bypass_p can't handle
+; the patterns with multiple sets, like store-conditional.
+(define_insn_reservation "ev4_ist_c" 1
+  (and (eq_attr "tune" "ev4")
+       (eq_attr "type" "st_c"))
+  "ev4_ib1+ev4_abox")
+
 (define_insn_reservation "ev4_fst" 1
   (and (eq_attr "tune" "ev4")
        (eq_attr "type" "fst"))
   "ev4_ib0+ev4_abox")
 
+; Memory barrier blocks ABOX insns until it's acknowledged by the external
+; memory bus.  This may be *quite* slow.  Setting this to 4 cycles gets
+; about all the benefit without making the DFA too large.
+(define_insn_reservation "ev4_mb" 4
+  (and (eq_attr "tune" "ev4")
+       (eq_attr "type" "mb"))
+  "ev4_ib1+ev4_abox,ev4_abox*3")
+
 ; Branches have no delay cost, but do tie up the unit for two cycles.
 (define_insn_reservation "ev4_ibr" 2
   (and (eq_attr "tune" "ev4")
index d4b0b95..b61cfe8 100644 (file)
@@ -43,7 +43,7 @@
 
 (define_insn_reservation "ev5_st" 1
   (and (eq_attr "tune" "ev5")
-       (eq_attr "type" "ist,fst"))
+       (eq_attr "type" "ist,fst,st_c,mb"))
   "ev5_e0+ev5_st")
 
 ; Loads from L0 complete in two cycles.  adjust_cost still factors
        (eq_attr "type" "ild,fld,ldsym"))
   "ev5_e01+ev5_ld")
 
+(define_insn_reservation "ev5_ld_l" 1
+  (and (eq_attr "tune" "ev5")
+       (eq_attr "type" "ld_l"))
+  "ev5_e0+ev5_ld")
+
 ; Integer branches slot only to E1.
 (define_insn_reservation "ev5_ibr" 1
   (and (eq_attr "tune" "ev5")
 ; Model this instead with increased latency on the input instruction.
 
 (define_bypass 3
-  "ev5_ld,ev5_shift,ev5_mvi,ev5_cmov,ev5_iadd,ev5_ilogcmp"
+  "ev5_ld,ev5_ld_l,ev5_shift,ev5_mvi,ev5_cmov,ev5_iadd,ev5_ilogcmp"
   "ev5_imull,ev5_imulq,ev5_imulh")
 
 (define_bypass  9 "ev5_imull" "ev5_imull,ev5_imulq,ev5_imulh")
index 0f39fa2..6ddadca 100644 (file)
 ; adjust_cost still factors in user-specified memory latency, so return 1 here.
 (define_insn_reservation "ev6_ild" 1
   (and (eq_attr "tune" "ev6")
-       (eq_attr "type" "ild,ldsym"))
+       (eq_attr "type" "ild,ldsym,ld_l"))
   "ev6_l")
 
 (define_insn_reservation "ev6_ist" 1
   (and (eq_attr "tune" "ev6")
-       (eq_attr "type" "ist"))
+       (eq_attr "type" "ist,st_c"))
   "ev6_l")
 
+(define_insn_reservation "ev6_mb" 1
+  (and (eq_attr "tune" "ev6")
+       (eq_attr "type" "mb"))
+  "ev6_l1")
+
 ; FP loads take at least 4 clocks.  adjust_cost still factors
 ; in user-specified memory latency, so return 2 here.
 (define_insn_reservation "ev6_fld" 2
diff --git a/gcc/config/alpha/sync.md b/gcc/config/alpha/sync.md
new file mode 100644 (file)
index 0000000..755a3f3
--- /dev/null
@@ -0,0 +1,319 @@
+;; GCC machine description for Alpha synchronization instructions.
+;; Copyright (C) 2005
+;; Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; 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.
+
+(define_mode_macro I48MODE [SI DI])
+(define_mode_attr modesuffix [(SI "l") (DI "q")])
+
+(define_code_macro FETCHOP [plus minus ior xor and])
+(define_code_attr fetchop_name
+  [(plus "add") (minus "sub") (ior "ior") (xor "xor") (and "and")])
+(define_code_attr fetchop_pred
+  [(plus "add_operand") (minus "reg_or_8bit_operand")
+   (ior "or_operand") (xor "or_operand") (and "and_operand")])
+(define_code_attr fetchop_constr
+  [(plus "rKL") (minus "rI") (ior "rIN") (xor "rIN") (and "riNHM")])
+
+
+(define_expand "memory_barrier"
+  [(set (mem:BLK (match_dup 0))
+       (unspec_volatile:BLK [(mem:BLK (match_dup 0))] UNSPECV_MB))]
+  ""
+{
+  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (DImode));
+  MEM_VOLATILE_P (operands[0]) = 1;
+})
+
+(define_insn "*mb_internal"
+  [(set (match_operand:BLK 0 "" "")
+       (unspec_volatile:BLK [(match_operand:BLK 1 "" "")] UNSPECV_MB))]
+  ""
+  "mb"
+  [(set_attr "type" "mb")])
+
+(define_insn "load_locked_<mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=r")
+       (unspec_volatile:I48MODE
+         [(match_operand:I48MODE 1 "memory_operand" "m")]
+         UNSPECV_LL))]
+  ""
+  "ld<modesuffix>_l %0,%1"
+  [(set_attr "type" "ld_l")])
+
+(define_insn "store_conditional_<mode>"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+        (unspec_volatile:DI [(const_int 0)] UNSPECV_SC))
+   (set (match_operand:I48MODE 1 "memory_operand" "=m")
+       (match_operand:I48MODE 2 "reg_or_0_operand" "0"))]
+  ""
+  "st<modesuffix>_c %0,%1"
+  [(set_attr "type" "st_c")])
+
+;; The Alpha Architecture Handbook says that it is UNPREDICTABLE whether
+;; the lock is cleared by a TAKEN branch.  If we were to honor that, it
+;; would mean that we could not expand a ll/sc sequence until after the
+;; final basic-block reordering pass.  Fortunately, it appears that no
+;; Alpha implementation ever built actually clears the lock on branches,
+;; taken or not.
+
+(define_insn_and_split "sync_<fetchop_name><mode>"
+  [(set (match_operand:I48MODE 0 "memory_operand" "+m")
+       (unspec_volatile:I48MODE
+         [(FETCHOP:I48MODE (match_dup 0)
+            (match_operand:I48MODE 1 "<fetchop_pred>" "<fetchop_constr>"))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 2 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (<CODE>, operands[0], operands[1],
+                        NULL, NULL, operands[2]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_nand<mode>"
+  [(set (match_operand:I48MODE 0 "memory_operand" "+m")
+       (unspec_volatile:I48MODE
+         [(and:I48MODE
+            (not:I48MODE
+              (match_operand:I48MODE 1 "reg_or_8bit_operand" "rI"))
+            (match_dup 0))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 2 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (NOT, operands[0], operands[1],
+                        NULL, NULL, operands[2]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_old_<fetchop_name><mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (match_operand:I48MODE 1 "memory_operand" "+m"))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(FETCHOP:I48MODE (match_dup 1)
+            (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>"))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 3 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (<CODE>, operands[1], operands[2],
+                        operands[0], NULL, operands[3]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_old_nand<mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (match_operand:I48MODE 1 "memory_operand" "+m"))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(and:I48MODE
+            (not:I48MODE
+              (match_operand:I48MODE 2 "reg_or_8bit_operand" "rI"))
+            (match_dup 1))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 3 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (NOT, operands[1], operands[2],
+                        operands[0], NULL, operands[3]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_new_<fetchop_name><mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (FETCHOP:I48MODE 
+         (match_operand:I48MODE 1 "memory_operand" "+m")
+         (match_operand:I48MODE 2 "<fetchop_pred>" "<fetchop_constr>")))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(FETCHOP:I48MODE (match_dup 1) (match_dup 2))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 3 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (<CODE>, operands[1], operands[2],
+                        NULL, operands[0], operands[3]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_new_nand<mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (and:I48MODE 
+         (not:I48MODE
+           (match_operand:I48MODE 2 "reg_or_8bit_operand" "rI"))
+         (match_operand:I48MODE 1 "memory_operand" "+m")))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(and:I48MODE (not:I48MODE (match_dup 2)) (match_dup 1))]
+         UNSPECV_ATOMIC))
+   (clobber (match_scratch:I48MODE 3 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  alpha_split_atomic_op (NOT, operands[1], operands[2],
+                        NULL, operands[0], operands[3]);
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_expand "sync_compare_and_swap<mode>"
+  [(parallel
+     [(set (match_operand:I48MODE 0 "register_operand" "")
+          (match_operand:I48MODE 1 "memory_operand" ""))
+      (set (match_dup 1)
+          (unspec_volatile:I48MODE
+            [(match_operand:I48MODE 2 "reg_or_8bit_operand" "")
+             (match_operand:I48MODE 3 "add_operand" "rKL")]
+            UNSPECV_CMPXCHG))
+      (clobber (match_scratch:I48MODE 4 "=&r"))])]
+  ""
+{
+  if (<MODE>mode == SImode)
+    operands[2] = convert_modes (DImode, SImode, operands[2], 0);
+})
+
+(define_insn_and_split "*sync_compare_and_swap<mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (match_operand:I48MODE 1 "memory_operand" "+m"))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(match_operand:DI 2 "reg_or_8bit_operand" "rI")
+          (match_operand:I48MODE 3 "add_operand" "rKL")]
+         UNSPECV_CMPXCHG))
+   (clobber (match_scratch:I48MODE 4 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  rtx retval, mem, oldval, newval, scratch;
+  rtx cond, label1, label2, x;
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+
+  retval = operands[0];
+  mem = operands[1];
+  oldval = operands[2];
+  newval = operands[3];
+  scratch = operands[4];
+  cond = gen_lowpart (DImode, scratch);
+
+  emit_insn (gen_memory_barrier ());
+
+  label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ());
+  label2 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ());
+  emit_label (XEXP (label1, 0));
+
+  emit_insn (gen_load_locked_<mode> (retval, mem));
+
+  x = gen_lowpart (DImode, retval);
+  x = gen_rtx_EQ (DImode, x, oldval);
+  if (oldval != const0_rtx)
+    {
+      emit_insn (gen_rtx_SET (VOIDmode, cond, x));
+      x = gen_rtx_EQ (DImode, cond, const0_rtx);
+    }
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label2, 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);
+    
+  emit_move_insn (scratch, newval);
+  emit_move_insn (retval, newval);
+  
+  emit_insn (gen_store_conditional_<mode> (cond, mem, scratch));
+
+  x = gen_rtx_EQ (DImode, cond, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label1, 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);
+
+  emit_label (XEXP (label2, 0));
+  emit_insn (gen_memory_barrier ());
+  DONE;
+}
+  [(set_attr "type" "multi")])
+
+(define_insn_and_split "sync_lock_test_and_set<mode>"
+  [(set (match_operand:I48MODE 0 "register_operand" "=&r")
+       (match_operand:I48MODE 1 "memory_operand" "+m"))
+   (set (match_dup 1)
+       (unspec_volatile:I48MODE
+         [(match_operand:I48MODE 2 "add_operand" "rKL")]
+         UNSPECV_XCHG))
+   (clobber (match_scratch:I48MODE 3 "=&r"))]
+  ""
+  "#"
+  "reload_completed"
+  [(const_int 0)]
+{
+  rtx retval, mem, val, scratch;
+  rtx cond, label1, label2, x;
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+
+  retval = operands[0];
+  mem = operands[1];
+  val = operands[2];
+  scratch = operands[3];
+  cond = gen_lowpart (DImode, scratch);
+
+  emit_insn (gen_memory_barrier ());
+
+  label1 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ());
+  label2 = gen_rtx_LABEL_REF (DImode, gen_label_rtx ());
+  emit_label (XEXP (label1, 0));
+
+  emit_insn (gen_load_locked_<mode> (retval, mem));
+
+  emit_move_insn (scratch, val);
+  
+  emit_insn (gen_store_conditional_<mode> (cond, mem, scratch));
+
+  x = gen_rtx_EQ (DImode, cond, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label1, 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);
+
+  emit_label (XEXP (label2, 0));
+  emit_insn (gen_memory_barrier ());
+  DONE;
+}
+  [(set_attr "type" "multi")])