The bug observes that atomic and/or operations can occasionally be
replaced with a exchange operation.
fetch_and (&x, 0) -> exchange (&x, 0)
fetch_or (&x, -1) -> exchange (&x, -1)
This is now trivial in the general expansion code. Others could easily
be added later.
This patch works for both __atomic and __sync variations, but will only
replace the operation if a native atomic_exchange pattern exists.
bootstrapped and no new regressions on x86_64-unknown-linux-gnu.
I created a testcase which will compile on x86 and verify in the
assembly that an exchange operation is performed rather than a
compare_swap loop... hopefully thats sufficient for testing.
Andrew
PR target/50123
* optabs.c (maybe_optimize_fetch_op): New. Look for more optimal
instructions for a FECTH_OP or OP_FECTH sequence.
(expand_atomic_fetch_op): Call maybe_optimize_fetch_op.
* testsuite/gcc.dg/atomic-op-optimize.c: New. Test for optimizations.
Index: optabs.c
===================================================================
*** optabs.c (revision 181702)
--- optabs.c (working copy)
*************** get_atomic_op_for_code (struct atomic_op
*** 7943,7948 ****
--- 7943,7983 ----
}
}
+ /* See if there is a more optimal way to implement the operation "*MEM CODE
VAL"
+ using memory order MODEL. If AFTER is true the operation needs to return
+ the value of *MEM after the operation, otherwise the previous value.
+ TARGET is an optional place to place the result. The result is unused if
+ it is const0_rtx.
+ Return the result if there is a better sequence, otherwise NULL_RTX. */
+
+ static rtx
+ maybe_optimize_fetch_op (rtx target, rtx mem, rtx val, enum rtx_code code,
+ enum memmodel model, bool after)
+ {
+ /* If the value is prefetched, or not used, it may be possible to replace
+ the sequence with a native exchange operation. */
+ if (!after || target == const0_rtx)
+ {
+ /* fetch_and (&x, 0, m) can be replaced with exchange (&x, 0, m). */
+ if (code == AND && val == const0_rtx)
+ {
+ if (target == const0_rtx)
+ target = gen_reg_rtx (GET_MODE (mem));
+ return maybe_emit_atomic_exchange (target, mem, val, model);
+ }
+
+ /* fetch_or (&x, -1, m) can be replaced with exchange (&x, -1, m). */
+ if (code == IOR && val == constm1_rtx)
+ {
+ if (target == const0_rtx)
+ target = gen_reg_rtx (GET_MODE (mem));
+ return maybe_emit_atomic_exchange (target, mem, val, model);
+ }
+ }
+
+ return NULL_RTX;
+ }
+
/* Try to emit an instruction for a specific operation varaition.
OPTAB contains the OP functions.
TARGET is an optional place to return the result. const0_rtx means unused.
*************** expand_atomic_fetch_op (rtx target, rtx
*** 8028,8033 ****
--- 8063,8073 ----
get_atomic_op_for_code (&optab, code);
+ /* Check to see if there are any better instructions. */
+ result = maybe_optimize_fetch_op (target, mem, val, code, model, after);
+ if (result)
+ return result;
+
/* Check for the case where the result isn't used and try those patterns.
*/
if (unused_result)
{
Index: testsuite/gcc.dg/atomic-op-optimize.c
===================================================================
*** testsuite/gcc.dg/atomic-op-optimize.c (revision 0)
--- testsuite/gcc.dg/atomic-op-optimize.c (revision 0)
***************
*** 0 ****
--- 1,20 ----
+ /* Both these atomic operations should be optimized to an exchange operation.
+ Test that it at happens on x86 by making sure there are 2 xchg's and no
+ compare_exchange loop. */
+
+ /* { dg-require-effective-target sync_int_long } */
+ /* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+ /* { dg-final { scan-assembler-times "cmpxchg" 0 } } */
+ /* { dg-final { scan-assembler-times "xchg" 2 } } */
+
+ int x;
+
+ int f()
+ {
+ return __atomic_fetch_and (&x, 0, __ATOMIC_RELAXED);
+ }
+
+ int g()
+ {
+ return __atomic_fetch_or (&x, -1, __ATOMIC_RELAXED);
+ }