Fold relaxed __atomic_fetch_*_N and __atomic_*_fetch_N calls with
identity operands to the matching __atomic_load_N builtin during
generic GIMPLE builtin folding.  This covers add/sub/or/xor by zero
and and by all-ones, avoiding unnecessary atomic RMW expansion when
the operation only returns the loaded value.

        PR tree-optimization/107462

gcc/ChangeLog:
        * gimple-fold.cc: Include memmodel.h.
        (atomic_load_fn_for_fetch_op): New function.
        (gimple_fold_builtin_atomic_fetch_op): New function.
        (gimple_fold_builtin): Handle no-op relaxed atomic fetch operations.

gcc/testsuite/ChangeLog:
        * gcc.dg/tree-ssa/pr107462.c: New test.
        * gcc.dg/tree-ssa/pr107462-1.c: New test.

Signed-off-by: Eikansh Gupta <[email protected]>
---
 gcc/gimple-fold.cc                         | 119 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr107462-1.c |  61 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr107462.c   |  57 ++++++++++
 3 files changed, 237 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr107462-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr107462.c

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 4ec6cc65e3a..982825863fa 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "internal-fn.h"
 #include "gimple-range.h"
+#include "memmodel.h"
 
 enum strlen_range_kind {
   /* Compute the exact constant string length.  */
@@ -5380,6 +5381,109 @@ gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, 
gcall *call)
     }
 }
 
+/* Return the __atomic_load_N builtin corresponding to atomic fetch-op
+   builtin FCODE.  */
+
+static enum built_in_function
+atomic_load_fn_for_fetch_op (enum built_in_function fcode)
+{
+#define CASE_ATOMIC_FETCH_SIZE(NAME, SIZE) \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_##SIZE: \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_##SIZE:
+
+#define CASE_ATOMIC_FETCH_LOAD(SIZE) \
+    CASE_ATOMIC_FETCH_SIZE (ADD, SIZE) \
+    CASE_ATOMIC_FETCH_SIZE (SUB, SIZE) \
+    CASE_ATOMIC_FETCH_SIZE (OR, SIZE) \
+    CASE_ATOMIC_FETCH_SIZE (XOR, SIZE) \
+    CASE_ATOMIC_FETCH_SIZE (AND, SIZE) \
+      return BUILT_IN_ATOMIC_LOAD_##SIZE
+
+  switch (fcode)
+    {
+    CASE_ATOMIC_FETCH_LOAD (1);
+    CASE_ATOMIC_FETCH_LOAD (2);
+    CASE_ATOMIC_FETCH_LOAD (4);
+    CASE_ATOMIC_FETCH_LOAD (8);
+    CASE_ATOMIC_FETCH_LOAD (16);
+
+    default:
+      gcc_unreachable ();
+    }
+#undef CASE_ATOMIC_FETCH_LOAD
+#undef CASE_ATOMIC_FETCH_SIZE
+}
+
+#define CASE_ATOMIC_FETCH_OP(NAME) \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_1: \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_2: \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_4: \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_8: \
+    case BUILT_IN_ATOMIC_FETCH_##NAME##_16
+
+#define CASE_ATOMIC_OP_FETCH(NAME) \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_1: \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_2: \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_4: \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_8: \
+    case BUILT_IN_ATOMIC_##NAME##_FETCH_16
+
+/* Fold relaxed atomic fetch-op calls whose operation is a no-op into
+   atomic loads.  */
+
+static bool
+gimple_fold_builtin_atomic_fetch_op (gimple_stmt_iterator *gsi,
+                                   enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+
+  if (gimple_call_num_args (stmt) != 3)
+    return false;
+
+  tree arg = gimple_call_arg (stmt, 1);
+  tree model = gimple_call_arg (stmt, 2);
+  if (!tree_fits_uhwi_p (model)
+      || !is_mm_relaxed (memmodel_from_int (tree_to_uhwi (model))))
+    return false;
+
+  enum built_in_function load_fn = END_BUILTINS;
+
+  switch (fcode)
+    {
+    CASE_ATOMIC_FETCH_OP (ADD):
+    CASE_ATOMIC_FETCH_OP (SUB):
+    CASE_ATOMIC_FETCH_OP (OR):
+    CASE_ATOMIC_FETCH_OP (XOR):
+    CASE_ATOMIC_OP_FETCH (ADD):
+    CASE_ATOMIC_OP_FETCH (SUB):
+    CASE_ATOMIC_OP_FETCH (OR):
+    CASE_ATOMIC_OP_FETCH (XOR):
+      if (!integer_zerop (arg))
+       return false;
+      load_fn = atomic_load_fn_for_fetch_op (fcode);
+      break;
+
+    CASE_ATOMIC_FETCH_OP (AND):
+    CASE_ATOMIC_OP_FETCH (AND):
+      if (!integer_all_onesp (arg))
+       return false;
+      load_fn = atomic_load_fn_for_fetch_op (fcode);
+      break;
+
+    default:
+      return false;
+    }
+
+  tree load_decl = builtin_decl_explicit (load_fn);
+  if (!load_decl)
+    return false;
+
+  gcall *repl = gimple_build_call (load_decl, 2, gimple_call_arg (stmt, 0),
+                                   model);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
 /* Fold the non-target builtin at *GSI and return whether any simplification
    was made.  */
 
@@ -5560,6 +5664,18 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_CONSTANT_P:
       return gimple_fold_builtin_constant_p (gsi);
 
+    CASE_ATOMIC_FETCH_OP (ADD):
+    CASE_ATOMIC_FETCH_OP (SUB):
+    CASE_ATOMIC_FETCH_OP (AND):
+    CASE_ATOMIC_FETCH_OP (XOR):
+    CASE_ATOMIC_FETCH_OP (OR):
+    CASE_ATOMIC_OP_FETCH (ADD):
+    CASE_ATOMIC_OP_FETCH (SUB):
+    CASE_ATOMIC_OP_FETCH (AND):
+    CASE_ATOMIC_OP_FETCH (XOR):
+    CASE_ATOMIC_OP_FETCH (OR):
+      return gimple_fold_builtin_atomic_fetch_op (gsi, fcode);
+
     default:;
     }
 
@@ -5579,6 +5695,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   return false;
 }
 
+#undef CASE_ATOMIC_FETCH_OP
+#undef CASE_ATOMIC_OP_FETCH
+
 /* Transform IFN_GOACC_DIM_SIZE and IFN_GOACC_DIM_POS internal
    function calls to constants, where possible.  */
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr107462-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr107462-1.c
new file mode 100644
index 00000000000..1ee00b1cae0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr107462-1.c
@@ -0,0 +1,61 @@
+/* PR tree-optimization/107462 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+unsigned char
+load_add_fetch_1 (unsigned char *a)
+{
+  return __atomic_add_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned short
+load_add_fetch_2 (unsigned short *a)
+{
+  return __atomic_add_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_add_fetch_4 (unsigned int *a)
+{
+  return __atomic_add_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned long long
+load_add_fetch_8 (unsigned long long *a)
+{
+  return __atomic_add_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_sub_fetch_4 (unsigned int *a)
+{
+  return __atomic_sub_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_or_fetch_4 (unsigned int *a)
+{
+  return __atomic_or_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_xor_fetch_4 (unsigned int *a)
+{
+  return __atomic_xor_fetch (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_and_fetch_4 (unsigned int *a)
+{
+  return __atomic_and_fetch (a, ~0u, __ATOMIC_RELAXED);
+}
+
+/* { dg-final { scan-tree-dump-times "__atomic_load_1" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_2" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_4" 5 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_8" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_add_fetch" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_sub_fetch" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_or_fetch" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_xor_fetch" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_and_fetch" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr107462.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr107462.c
new file mode 100644
index 00000000000..4e59b1dd82d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr107462.c
@@ -0,0 +1,57 @@
+/* PR tree-optimization/107462 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+unsigned char
+load_add_1 (unsigned char *a)
+{
+  return __atomic_fetch_add (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned short
+load_add_2 (unsigned short *a)
+{
+  return __atomic_fetch_add (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_add_4 (unsigned int *a)
+{
+  return __atomic_fetch_add (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned long long
+load_add_8 (unsigned long long *a)
+{
+  return __atomic_fetch_add (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_sub_4 (unsigned int *a)
+{
+  return __atomic_fetch_sub (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_or_4 (unsigned int *a)
+{
+  return __atomic_fetch_or (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_xor_4 (unsigned int *a)
+{
+  return __atomic_fetch_xor (a, 0, __ATOMIC_RELAXED);
+}
+
+unsigned int
+load_and_4 (unsigned int *a)
+{
+  return __atomic_fetch_and (a, ~0u, __ATOMIC_RELAXED);
+}
+
+/* { dg-final { scan-tree-dump-times "__atomic_load_1" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_2" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_4" 5 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__atomic_load_8" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__atomic_fetch" "optimized" } } */
-- 
2.34.1

Reply via email to