Introduce the %I and %J flags for setting the .aqrl bits on LR/SC pairs
as needed.

Atomic compare and exchange ops provide 2 types of memory models. C++17
and later places no restrictions on the relative strength of each model,
so ensure we cover both by using a model that enforces the ordering of
both given models.

This change brings LR/SC ops in line with table A.6 of the ISA manual.

2022-03-31 Patrick O'Neill <patr...@rivosinc.com>
        
        * riscv.cc: Add functions to get the parent of two
        memmodels in sync.md.
        * riscv-protos.h: Likewise.
        * sync.md (atomic_cas_value_strong<mode>): Remove static
        .aqrl bits on SC op/.rl bits on LR op and replace with %I, %J
        flags.
        * inline-atomics-model-1.c: New test.
        * inline-atomics-model-2.c: Likewise.
        * inline-atomics-model-3.c: Likewise.
        * inline-atomics-model-4.c: Likewise.
        * inline-atomics-model-5.c: Likewise.
        * inline-atomics-model-6.c: Likewise.

Signed-off-by: Patrick O'Neill <patr...@rivosinc.com>
---
 gcc/config/riscv/riscv-protos.h               |  4 ++
 gcc/config/riscv/riscv.cc                     | 68 +++++++++++++++++++
 gcc/config/riscv/sync.md                      | 12 +++-
 .../gcc.target/riscv/inline-atomics-model-1.c | 12 ++++
 .../gcc.target/riscv/inline-atomics-model-2.c | 12 ++++
 .../gcc.target/riscv/inline-atomics-model-3.c | 12 ++++
 .../gcc.target/riscv/inline-atomics-model-4.c | 12 ++++
 .../gcc.target/riscv/inline-atomics-model-5.c | 12 ++++
 8 files changed, 143 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/inline-atomics-model-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/inline-atomics-model-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/inline-atomics-model-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/inline-atomics-model-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/inline-atomics-model-5.c

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 20c2381c21a..e32ea86a530 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_RISCV_PROTOS_H
 #define GCC_RISCV_PROTOS_H
 
+#include "memmodel.h"
+
 /* Symbol types we understand.  The order of this list must match that of
    the unspec enum in riscv.md, subsequent to UNSPEC_ADDRESS_FIRST.  */
 enum riscv_symbol_type {
@@ -74,6 +76,8 @@ extern bool riscv_expand_block_move (rtx, rtx, rtx);
 extern bool riscv_store_data_bypass_p (rtx_insn *, rtx_insn *);
 extern rtx riscv_gen_gpr_save_insn (struct riscv_frame_info *);
 extern bool riscv_gpr_save_operation_p (rtx);
+extern enum memmodel riscv_parent_memmodel (enum memmodel, enum memmodel);
+extern enum memmodel riscv_simplify_memmodel (enum memmodel);
 
 /* Routines implemented in riscv-c.cc.  */
 void riscv_cpu_cpp_builtins (cpp_reader *);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 6da602f1b59..6fbcd62fe73 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -3563,6 +3563,59 @@ riscv_print_operand_reloc (FILE *file, rtx op, bool 
hi_reloc)
   fputc (')', file);
 }
 
+/* Return the memory model that encapuslates both given models. This assumes
+   SYNC models output equivalent code to non-SYNC models.  */
+
+enum memmodel
+riscv_parent_memmodel (enum memmodel model1, enum memmodel model2)
+{
+  model1 = riscv_simplify_memmodel(model1);
+  model2 = riscv_simplify_memmodel(model2);
+
+  enum memmodel weaker = model1 <= model2 ? model1: model2;
+  enum memmodel stronger = model1 > model2 ? model1: model2;
+
+  switch (stronger)
+    {
+      case MEMMODEL_SEQ_CST:
+      case MEMMODEL_ACQ_REL:
+       return stronger;
+      case MEMMODEL_RELEASE:
+       if (weaker== MEMMODEL_ACQUIRE || weaker == MEMMODEL_CONSUME)
+         return MEMMODEL_ACQ_REL;
+       else
+         return stronger;
+      case MEMMODEL_ACQUIRE:
+      case MEMMODEL_CONSUME:
+      case MEMMODEL_RELAXED:
+       return stronger;
+      default:
+       gcc_unreachable ();
+    }
+}
+
+enum memmodel
+riscv_simplify_memmodel(enum memmodel model) {
+  switch (model)
+    {
+      case MEMMODEL_SYNC_SEQ_CST:
+       return MEMMODEL_SEQ_CST;
+      case MEMMODEL_SYNC_ACQUIRE:
+       return MEMMODEL_ACQUIRE;
+      case MEMMODEL_SYNC_RELEASE:
+       return MEMMODEL_RELEASE;
+      case MEMMODEL_SEQ_CST:
+      case MEMMODEL_ACQ_REL:
+      case MEMMODEL_RELEASE:
+      case MEMMODEL_ACQUIRE:
+      case MEMMODEL_CONSUME:
+      case MEMMODEL_RELAXED:
+       return model;
+      default:
+       gcc_unreachable();
+    }
+}
+
 /* Return true if the .AQ suffix should be added to an AMO to implement the
    acquire portion of memory model MODEL.  */
 
@@ -3622,6 +3675,8 @@ riscv_memmodel_needs_amo_release (enum memmodel model)
    'R' Print the low-part relocation associated with OP.
    'C' Print the integer branch condition for comparison OP.
    'A' Print the atomic operation suffix for memory model OP.
+   'I' Print the LR suffix for memory model OP.
+   'J' Print the SC suffix for memory model OP.
    'z' Print x0 if OP is zero, otherwise print OP normally.
    'i' Print i if the operand is not a register.
    'S' Print shift-index of single-bit mask OP.
@@ -3660,6 +3715,19 @@ riscv_print_operand (FILE *file, rtx op, int letter)
        fputs (".rl", file);
       break;
 
+    case 'I':
+      if ((enum memmodel) INTVAL (op) == MEMMODEL_SEQ_CST ||
+         (enum memmodel) INTVAL (op) == MEMMODEL_SYNC_SEQ_CST)
+       fputs (".aqrl", file);
+      else if (riscv_memmodel_needs_amo_acquire ((enum memmodel) INTVAL (op)))
+       fputs (".aq", file);
+      break;
+
+    case 'J':
+      if (riscv_memmodel_needs_amo_release ((enum memmodel) INTVAL (op)))
+       fputs (".rl", file);
+      break;
+
     case 'i':
       if (code != REG)
         fputs ("i", file);
diff --git a/gcc/config/riscv/sync.md b/gcc/config/riscv/sync.md
index f8a79465ee3..b54338d8eb2 100644
--- a/gcc/config/riscv/sync.md
+++ b/gcc/config/riscv/sync.md
@@ -115,7 +115,17 @@
         UNSPEC_COMPARE_AND_SWAP))
    (clobber (match_scratch:GPR 6 "=&r"))]
   "TARGET_ATOMIC"
-  
"1:\;lr.<amo>.aqrl\t%0,%1\;bne\t%0,%z2,1f\;sc.<amo>.rl\t%6,%z3,%1\;bnez\t%6,1b\;1:"
+  {
+    enum memmodel model_success = (enum memmodel) INTVAL(operands[4]);
+    enum memmodel model_failure = (enum memmodel) INTVAL(operands[5]);
+    operands[5] = GEN_INT(riscv_parent_memmodel(model_success, model_failure));
+    return "1:\;"
+          "lr.<amo>%I5\t%0,%1\;"
+          "bne\t%0,%z2,1f\;"
+          "sc.<amo>%J5\t%6,%z3,%1\;"
+          "bnez\t%6,1b\;"
+          "1:";
+  }
   [(set (attr "length") (const_int 16))])
 
 (define_expand "atomic_compare_and_swap<mode>"
diff --git a/gcc/testsuite/gcc.target/riscv/inline-atomics-model-1.c 
b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-1.c
new file mode 100644
index 00000000000..a2c3fc7a1b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-1.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* Verify that appropriate bits are placed per memory model.  */
+/* { dg-final { scan-assembler-not "lr.w.aq" } } */
+/* { dg-final { scan-assembler-not "lr.w.rl" } } */
+/* { dg-final { scan-assembler-not "sc.w.aq" } } */
+/* { dg-final { scan-assembler-not "sc.w.rl" } } */
+
+void
+foo (int bar, int baz, int qux)
+{
+  __atomic_compare_exchange_n(&bar, &baz, qux, 1, __ATOMIC_RELAXED, 
__ATOMIC_RELAXED);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/inline-atomics-model-2.c 
b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-2.c
new file mode 100644
index 00000000000..d23d4db945f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* Verify that appropriate bits are placed per memory model.  */
+/* { dg-final { scan-assembler "lr.w.aq" } } */
+/* { dg-final { scan-assembler-not "lr.w.rl" } } */
+/* { dg-final { scan-assembler-not "sc.w.aq" } } */
+/* { dg-final { scan-assembler-not "sc.w.rl" } } */
+
+void
+foo (int bar, int baz, int qux)
+{
+  __atomic_compare_exchange_n(&bar, &baz, qux, 1, __ATOMIC_CONSUME, 
__ATOMIC_CONSUME);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/inline-atomics-model-3.c 
b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-3.c
new file mode 100644
index 00000000000..7379825c6f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* Verify that appropriate bits are placed per memory model.  */
+/* { dg-final { scan-assembler "lr.w.aq" } } */
+/* { dg-final { scan-assembler-not "lr.w.rl" } } */
+/* { dg-final { scan-assembler-not "sc.w.aq" } } */
+/* { dg-final { scan-assembler-not "sc.w.rl" } } */
+
+void
+foo (int bar, int baz, int qux)
+{
+  __atomic_compare_exchange_n(&bar, &baz, qux, 1, __ATOMIC_ACQUIRE, 
__ATOMIC_ACQUIRE);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/inline-atomics-model-4.c 
b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-4.c
new file mode 100644
index 00000000000..80ab9889288
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* Verify that appropriate bits are placed per memory model.  */
+/* { dg-final { scan-assembler "lr.w.aqrl" } } */
+/* { dg-final { scan-assembler "sc.w.rl" } } */
+/* { dg-final { scan-assembler-not "lr.w.rl" } } */
+/* { dg-final { scan-assembler-not "sc.w.aq" } } */
+
+void
+foo (int bar, int baz, int qux)
+{
+  __atomic_compare_exchange_n(&bar, &baz, qux, 1, __ATOMIC_SEQ_CST, 
__ATOMIC_SEQ_CST);
+}
diff --git a/gcc/testsuite/gcc.target/riscv/inline-atomics-model-5.c 
b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-5.c
new file mode 100644
index 00000000000..da905242317
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/inline-atomics-model-5.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* Verify that appropriate bits are placed per memory model.  */
+/* { dg-final { scan-assembler "lr.w.aq" } } */
+/* { dg-final { scan-assembler "sc.w.rl" } } */
+/* { dg-final { scan-assembler-not "lr.w.rl" } } */
+/* { dg-final { scan-assembler-not "sc.w.aq" } } */
+
+void
+foo (int bar, int baz, int qux)
+{
+  __atomic_compare_exchange_n(&bar, &baz, qux, 1, __ATOMIC_RELEASE, 
__ATOMIC_ACQUIRE);
+}
-- 
2.25.1

Reply via email to