diff options
author | Andrew MacLeod <amacleod@redhat.com> | 2011-11-24 23:14:31 +0000 |
---|---|---|
committer | Andrew Macleod <amacleod@gcc.gnu.org> | 2011-11-24 23:14:31 +0000 |
commit | 744accb21bd7b45ad5d0bfd63dda028d86e2a09b (patch) | |
tree | bf092826976e956804e16b109cecf1f0d3830700 /gcc/optabs.c | |
parent | bee51209f65f0d80bc8cfd7a494d1d6c4f730c2b (diff) | |
download | gcc-744accb21bd7b45ad5d0bfd63dda028d86e2a09b.zip gcc-744accb21bd7b45ad5d0bfd63dda028d86e2a09b.tar.gz gcc-744accb21bd7b45ad5d0bfd63dda028d86e2a09b.tar.bz2 |
optab.c (maybe_emit_atomic_exchange): New.
2011-11-24 Andrew MacLeod <amacleod@redhat.com>
* optab.c (maybe_emit_atomic_exchange): New. Try to emit an
atomic_exchange pattern.
(maybe_emit_sync_lock_test_and_set): New. Try to emit an exchange
using __sync_lock_test_and_set.
(maybe_emit_compare_and_swap_exchange_loop): New. Try to emit an
exchange using a compare_and_swap loop.
(expand_sync_lock_test_and_set): New. Expand sync_lock_test_and_set.
(expand_atomic_test_and_set): New. Expand test_and_set operation.
(expand_atomic_exchange): Use new maybe_emit_* functions.
(expand_atomic_store): Use new maybe_emit_* functions.
* builtins.c (expand_builtin_sync_lock_test_and_set): Call
expand_sync_lock_test_and_set routine.
(expand_builtin_atomic_exchange): Remove parameter from call.
(expand_builtin_atomic_clear): Use atomic_clear pattern if present.
(expand_builtin_atomic_test_and_set): Add target and simply call
expand_atomic_test_and_set.
(expand_builtin): Add target to expand_builtin_atomic_test_and_set.
* expr.h (expand_atomic_exchange): Add parameter.
(expand_sync_lock_test_and_set): New prototype.
(expand_atomic_test_and_set, expand_atomic_clear): New prototypes.
From-SVN: r181702
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r-- | gcc/optabs.c | 231 |
1 files changed, 170 insertions, 61 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c index 647805b..1aafd28 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -7325,17 +7325,12 @@ expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq) } -/* This function expands the atomic exchange operation: - atomically store VAL in MEM and return the previous value in MEM. - - MEMMODEL is the memory model variant to use. - TARGET is an optional place to stick the return value. - USE_TEST_AND_SET indicates whether __sync_lock_test_and_set should be used - as a fall back if the atomic_exchange pattern does not exist. */ - -rtx -expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model, - bool use_test_and_set) +/* This function tries to emit an atomic_exchange intruction. VAL is written + to *MEM using memory model MODEL. The previous contents of *MEM are returned, + using TARGET if possible. */ + +static rtx +maybe_emit_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model) { enum machine_mode mode = GET_MODE (mem); enum insn_code icode; @@ -7355,65 +7350,78 @@ expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model, return ops[0].value; } - /* Legacy sync_lock_test_and_set works the same, but is only defined as an - acquire barrier. If the pattern exists, and the memory model is stronger - than acquire, add a release barrier before the instruction. - The barrier is not needed if sync_lock_test_and_set doesn't exist since - it will expand into a compare-and-swap loop. + return NULL_RTX; +} - Some targets have non-compliant test_and_sets, so it would be incorrect - to emit a test_and_set in place of an __atomic_exchange. The test_and_set - builtin shares this expander since exchange can always replace the - test_and_set. */ +/* This function tries to implement an atomic exchange operation using + __sync_lock_test_and_set. VAL is written to *MEM using memory model MODEL. + The previous contents of *MEM are returned, using TARGET if possible. + Since this instructionn is an acquire barrier only, stronger memory + models may require additional barriers to be emitted. */ - if (use_test_and_set) +static rtx +maybe_emit_sync_lock_test_and_set (rtx target, rtx mem, rtx val, + enum memmodel model) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx last_insn = get_last_insn (); + + icode = optab_handler (sync_lock_test_and_set_optab, mode); + + /* Legacy sync_lock_test_and_set is an acquire barrier. If the pattern + exists, and the memory model is stronger than acquire, add a release + barrier before the instruction. */ + + if (model == MEMMODEL_SEQ_CST + || model == MEMMODEL_RELEASE + || model == MEMMODEL_ACQ_REL) + expand_mem_thread_fence (model); + + if (icode != CODE_FOR_nothing) { - icode = optab_handler (sync_lock_test_and_set_optab, mode); + struct expand_operand ops[3]; + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], mem); + /* VAL may have been promoted to a wider mode. Shrink it if so. */ + create_convert_operand_to (&ops[2], val, mode, true); + if (maybe_expand_insn (icode, 3, ops)) + return ops[0].value; + } - if (icode != CODE_FOR_nothing) + /* If an external test-and-set libcall is provided, use that instead of + any external compare-and-swap that we might get from the compare-and- + swap-loop expansion later. */ + if (!can_compare_and_swap_p (mode, false)) + { + rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, mode); + if (libfunc != NULL) { - struct expand_operand ops[3]; - rtx last_insn = get_last_insn (); - - if (model == MEMMODEL_SEQ_CST - || model == MEMMODEL_RELEASE - || model == MEMMODEL_ACQ_REL) - expand_mem_thread_fence (model); - - create_output_operand (&ops[0], target, mode); - create_fixed_operand (&ops[1], mem); - /* VAL may have been promoted to a wider mode. Shrink it if so. */ - create_convert_operand_to (&ops[2], val, mode, true); - if (maybe_expand_insn (icode, 3, ops)) - return ops[0].value; - - delete_insns_since (last_insn); + rtx addr; + + addr = convert_memory_address (ptr_mode, XEXP (mem, 0)); + return emit_library_call_value (libfunc, target, LCT_NORMAL, + mode, 2, addr, ptr_mode, + val, mode); } + } - /* If an external test-and-set libcall is provided, use that instead of - any external compare-and-swap that we might get from the compare-and- - swap-loop expansion below. */ - if (!can_compare_and_swap_p (mode, false)) - { - rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, mode); - if (libfunc != NULL) - { - rtx addr; + /* If the test_and_set can't be emitted, eliminate any barrier that might + have been emitted. */ + delete_insns_since (last_insn); + return NULL_RTX; +} - if (model == MEMMODEL_SEQ_CST - || model == MEMMODEL_RELEASE - || model == MEMMODEL_ACQ_REL) - expand_mem_thread_fence (model); +/* This function tries to implement an atomic exchange operation using a + compare_and_swap loop. VAL is written to *MEM. The previous contents of + *MEM are returned, using TARGET if possible. No memory model is required + since a compare_and_swap loop is seq-cst. */ - addr = convert_memory_address (ptr_mode, XEXP (mem, 0)); - return emit_library_call_value (libfunc, target, LCT_NORMAL, - mode, 2, addr, ptr_mode, - val, mode); - } - } - } +static rtx +maybe_emit_compare_and_swap_exchange_loop (rtx target, rtx mem, rtx val) +{ + enum machine_mode mode = GET_MODE (mem); - /* Otherwise, use a compare-and-swap loop for the exchange. */ if (can_compare_and_swap_p (mode, true)) { if (!target || !register_operand (target, mode)) @@ -7427,6 +7435,105 @@ expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model, return NULL_RTX; } +#ifndef HAVE_atomic_test_and_set +#define HAVE_atomic_test_and_set 0 +#define gen_atomic_test_and_set(x,y,z) (gcc_unreachable (), NULL_RTX) +#endif + +/* This function expands the legacy _sync_lock test_and_set operation which is + generally an atomic exchange. Some limited targets only allow the + constant 1 to be stored. This is an ACQUIRE operation. + + TARGET is an optional place to stick the return value. + MEM is where VAL is stored. */ + +rtx +expand_sync_lock_test_and_set (rtx target, rtx mem, rtx val) +{ + rtx ret; + + /* Try an atomic_exchange first. */ + ret = maybe_emit_atomic_exchange (target, mem, val, MEMMODEL_ACQUIRE); + + if (!ret) + ret = maybe_emit_sync_lock_test_and_set (target, mem, val, + MEMMODEL_ACQUIRE); + if (!ret) + ret = maybe_emit_compare_and_swap_exchange_loop (target, mem, val); + + /* If there are no other options, try atomic_test_and_set if the value + being stored is 1. */ + if (!ret && val == const1_rtx && HAVE_atomic_test_and_set) + { + ret = gen_atomic_test_and_set (target, mem, GEN_INT (MEMMODEL_ACQUIRE)); + emit_insn (ret); + } + + return ret; +} + +/* This function expands the atomic test_and_set operation: + atomically store a boolean TRUE into MEM and return the previous value. + + MEMMODEL is the memory model variant to use. + TARGET is an optional place to stick the return value. */ + +rtx +expand_atomic_test_and_set (rtx target, rtx mem, enum memmodel model) +{ + enum machine_mode mode = GET_MODE (mem); + rtx ret = NULL_RTX; + + if (target == NULL_RTX) + target = gen_reg_rtx (mode); + + if (HAVE_atomic_test_and_set) + { + ret = gen_atomic_test_and_set (target, mem, GEN_INT (MEMMODEL_ACQUIRE)); + emit_insn (ret); + return ret; + } + + /* If there is no test and set, try exchange, then a compare_and_swap loop, + then __sync_test_and_set. */ + ret = maybe_emit_atomic_exchange (target, mem, const1_rtx, model); + + if (!ret) + ret = maybe_emit_compare_and_swap_exchange_loop (target, mem, const1_rtx); + + if (!ret) + ret = maybe_emit_sync_lock_test_and_set (target, mem, const1_rtx, model); + + if (ret) + return ret; + + /* Failing all else, assume a single threaded environment and simply perform + the operation. */ + emit_move_insn (target, mem); + emit_move_insn (mem, const1_rtx); + return target; +} + +/* This function expands the atomic exchange operation: + atomically store VAL in MEM and return the previous value in MEM. + + MEMMODEL is the memory model variant to use. + TARGET is an optional place to stick the return value. */ + +rtx +expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model) +{ + rtx ret; + + ret = maybe_emit_atomic_exchange (target, mem, val, model); + + /* Next try a compare-and-swap loop for the exchange. */ + if (!ret) + ret = maybe_emit_compare_and_swap_exchange_loop (target, mem, val); + + return ret; +} + /* This function expands the atomic compare exchange operation: *PTARGET_BOOL is an optional place to store the boolean success/failure. @@ -7726,7 +7833,9 @@ expand_atomic_store (rtx mem, rtx val, enum memmodel model, bool use_release) the result. If that doesn't work, don't do anything. */ if (GET_MODE_PRECISION(mode) > BITS_PER_WORD) { - rtx target = expand_atomic_exchange (NULL_RTX, mem, val, model, false); + rtx target = maybe_emit_atomic_exchange (NULL_RTX, mem, val, model); + if (!target) + target = maybe_emit_compare_and_swap_exchange_loop (NULL_RTX, mem, val); if (target) return const0_rtx; else |