We can perform a single-copy atomic load with an ldrexd insn.
If the load is all we care about, we need not pair this with
a strexd.

Ok?


r~
        * config/arm/arm.md (UNSPEC_LL): New.
        * config/arm/sync.md (atomic_loaddi, atomic_loaddi_1): New.

diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 79eff0e..75d2565 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -117,6 +117,7 @@
                        ; that.
   UNSPEC_UNALIGNED_STORE ; Same for str/strh.
   UNSPEC_PIC_UNIFIED    ; Create a common pic addressing form.
+  UNSPEC_LL            ; Represent an unpaired load-register-exclusive.
 ])
 
 ;; UNSPEC_VOLATILE Usage:
diff --git a/gcc/config/arm/sync.md b/gcc/config/arm/sync.md
index 03838f5..de2da3b 100644
--- a/gcc/config/arm/sync.md
+++ b/gcc/config/arm/sync.md
@@ -65,6 +65,40 @@
    (set_attr "conds" "unconditional")
    (set_attr "predicable" "no")])
 
+;; Note that ldrd and vldr are *not* guaranteed to be single-copy atomic,
+;; even for a 64-bit aligned address.  Instead we use a ldrexd unparied
+;; with a store.
+(define_expand "atomic_loaddi"
+  [(match_operand:DI 0 "s_register_operand")           ;; val out
+   (match_operand:DI 1 "mem_noofs_operand")            ;; memory
+   (match_operand:SI 2 "const_int_operand")]           ;; model
+  "TARGET_HAVE_LDREXD && ARM_DOUBLEWORD_ALIGN"
+{
+  enum memmodel model = (enum memmodel) INTVAL (operands[2]);
+  expand_mem_thread_fence (model);
+  emit_insn (gen_atomic_loaddi_1 (operands[0], operands[1]));
+  if (model == MEMMODEL_SEQ_CST)
+    expand_mem_thread_fence (model);
+  DONE;
+})
+
+(define_insn "atomic_loaddi_1"
+  [(set (match_operand:DI 0 "s_register_operand" "=r")
+       (unspec:DI [(match_operand:DI 1 "mem_noofs_operand" "Ua")]
+                  UNSPEC_LL))]
+  "TARGET_HAVE_LDREXD && ARM_DOUBLEWORD_ALIGN"
+  {
+    rtx target = operands[0];
+    /* The restrictions on target registers in ARM mode are that the two
+       registers are consecutive and the first one is even; Thumb is
+       actually more flexible, but DI should give us this anyway.
+       Note that the 1st register always gets the lowest word in memory.  */
+    gcc_assert ((REGNO (target) & 1) == 0);
+    operands[2] = gen_rtx_REG (SImode, REGNO (target) + 1);
+    return "ldrexd%?\t%0, %2, %C1";
+  }
+  [(set_attr "predicable" "yes")])
+
 (define_expand "atomic_compare_and_swap<mode>"
   [(match_operand:SI 0 "s_register_operand" "")                ;; bool out
    (match_operand:QHSD 1 "s_register_operand" "")      ;; val out

Reply via email to