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