Two of the patches have been posted here before; the libstdc++ patch was approved by Benjamin.
All of the patches tested on sparc64-linux, and sanity checked on x86_64-linux. I've cross-compiled for m68k-linux, but I've only been able to visually sanity check the code in libstdc++. Committed. Hopefully that wraps up the atomic patches... r~
* optabs.c (gen_atomic_test_and_set): Remove default. (maybe_emit_atomic_test_and_set): Use maybe_expand_insn. diff --git a/gcc/optabs.c b/gcc/optabs.c index fb01320..87cce8e 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -7311,39 +7311,34 @@ maybe_emit_compare_and_swap_exchange_loop (rtx target, rtx mem, rtx val) #ifndef HAVE_atomic_test_and_set #define HAVE_atomic_test_and_set 0 #define CODE_FOR_atomic_test_and_set CODE_FOR_nothing -#define gen_atomic_test_and_set(x,y,z) \ - (gcc_unreachable (), (void) (0 && (x) && (y) && (z)), NULL_RTX) #endif static rtx maybe_emit_atomic_test_and_set (rtx target, rtx mem, enum memmodel model) { enum machine_mode pat_bool_mode; - const struct insn_data_d *id; + struct expand_operand ops[3]; if (!HAVE_atomic_test_and_set) return NULL_RTX; - id = &insn_data[CODE_FOR_atomic_test_and_set]; - pat_bool_mode = id->operand[0].mode; - - /* ??? We only support test-and-set on single bytes at the moment. - We'd have to change the builtin to allow wider memories. */ - gcc_checking_assert (id->operand[1].mode == QImode); - /* While we always get QImode from __atomic_test_and_set, we get other memory modes from __sync_lock_test_and_set. Note that we use no endian adjustment here. This matches the 4.6 behavior in the Sparc backend. */ + gcc_checking_assert + (insn_data[CODE_FOR_atomic_test_and_set].operand[1].mode == QImode); if (GET_MODE (mem) != QImode) mem = adjust_address_nv (mem, QImode, 0); - if (target == NULL || GET_MODE (target) != pat_bool_mode) - target = gen_reg_rtx (pat_bool_mode); - - emit_insn (gen_atomic_test_and_set (target, mem, GEN_INT (model))); + pat_bool_mode = insn_data[CODE_FOR_atomic_test_and_set].operand[0].mode; + create_output_operand (&ops[0], target, pat_bool_mode); + create_fixed_operand (&ops[1], mem); + create_integer_operand (&ops[2], model); - return target; + if (maybe_expand_insn (CODE_FOR_atomic_test_and_set, 3, ops)) + return ops[0].value; + return NULL_RTX; } /* This function expands the legacy _sync_lock test_and_set operation which is
* target.def (TARGET_ATOMIC_TEST_AND_SET_TRUEVAL): New. * c-cppbuiltin.c (cpp_atomic_builtins): Define __GCC_ATOMIC_TEST_AND_SET_TRUEVAL. * doc/tm.texi.in (TARGET_ATOMIC_TEST_AND_SET_TRUEVAL): Add doc hook. * doc/tm.texi: Rebuild. diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 7e7b9c1..608dba6 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -670,6 +670,11 @@ cpp_atomic_builtins (cpp_reader *pfile) builtin_define_with_int_value ("__GCC_ATOMIC_LLONG_LOCK_FREE", (have_swap[SWAP_INDEX (long_long_integer_type_node)]? 2 : 1)); + /* If we're dealing with a "set" value that doesn't exactly correspond + to a boolean truth value, let the library work around that. */ + builtin_define_with_int_value ("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", + targetm.atomic_test_and_set_trueval); + /* ptr_type_node can't be used here since ptr_mode is only set when toplev calls backend_init which is not done with -E or pch. */ psize = POINTER_SIZE / BITS_PER_UNIT; diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index ceb0d1e..91e4b04 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -11359,3 +11359,7 @@ value of @code{TARGET_CONST_ANCHOR} is a power of 2. For example, on MIPS, where add-immediate takes a 16-bit signed value, @code{TARGET_CONST_ANCHOR} is set to @samp{0x8000}. The default value is zero, which disables this optimization. @end deftypevr + +@deftypevr {Target Hook} {unsigned char} TARGET_ATOMIC_TEST_AND_SET_TRUEVAL +This value should be set if the result written by @code{atomic_test_and_set} is not exactly 1, i.e. the @code{bool} @code{true}. +@end deftypevr diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 55c8432..0ebc15d 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -11237,3 +11237,5 @@ value of @code{TARGET_CONST_ANCHOR} is a power of 2. For example, on MIPS, where add-immediate takes a 16-bit signed value, @code{TARGET_CONST_ANCHOR} is set to @samp{0x8000}. The default value is zero, which disables this optimization. @end deftypevr + +@hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL diff --git a/gcc/target.def b/gcc/target.def index f86f782..6084b21 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2667,6 +2667,13 @@ DEFHOOK enum unwind_info_type, (void), default_debug_unwind_info) +DEFHOOKPOD +(atomic_test_and_set_trueval, + "This value should be set if the result written by\ + @code{atomic_test_and_set} is not exactly 1, i.e. the\ + @code{bool} @code{true}.", + unsigned char, 1) + /* Leave the boolean fields at the end. */ /* True if we can create zeroed data by switching to a BSS section
* include/bits/atomic_base.h (__atomic_flag_base): Define _M_i based on the value of __GCC_ATOMIC_TEST_AND_SET_TRUEVAL. (ATOMIC_FLAG_INIT): Initialize with 0, not false. (atomic_flag::atomic_flag): Use __GCC_ATOMIC_TEST_AND_SET_TRUEVAL. diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index ef17b7e..aa43bcc 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -227,12 +227,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __atomic_flag_base { + /* The target's "set" value for test-and-set may not be exactly 1. */ +#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1 bool _M_i; +#else + unsigned char _M_i; +#endif }; _GLIBCXX_END_EXTERN_C -#define ATOMIC_FLAG_INIT { false } +#define ATOMIC_FLAG_INIT { 0 } /// atomic_flag struct atomic_flag : public __atomic_flag_base @@ -244,7 +249,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION atomic_flag& operator=(const atomic_flag&) volatile = delete; // Conversion to ATOMIC_FLAG_INIT. - atomic_flag(bool __i) noexcept : __atomic_flag_base({ __i }) { } + constexpr atomic_flag(bool __i) noexcept + : __atomic_flag_base({ __i ? __GCC_ATOMIC_TEST_AND_SET_TRUEVAL : 0 }) + { } bool test_and_set(memory_order __m = memory_order_seq_cst) noexcept
* gcc.dg/atomic-flag.c: Adjust for __GCC_ATOMIC_TEST_AND_SET_TRUEVAL. diff --git a/gcc/testsuite/gcc.dg/atomic-flag.c b/gcc/testsuite/gcc.dg/atomic-flag.c index 771df2c..1b76832 100644 --- a/gcc/testsuite/gcc.dg/atomic-flag.c +++ b/gcc/testsuite/gcc.dg/atomic-flag.c @@ -1,27 +1,25 @@ /* Test __atomic routines for existence and execution. */ /* { dg-do run } */ -#include <stdbool.h> - /* Test that __atomic_test_and_set and __atomic_clear builtins execute. */ extern void abort(void); -bool a; +unsigned char a; main () { - bool b; + int b; __atomic_clear (&a, __ATOMIC_RELAXED); if (a != 0) abort (); b = __atomic_test_and_set (&a, __ATOMIC_SEQ_CST); - if (a != 1 || b != 0) + if (a != __GCC_ATOMIC_TEST_AND_SET_TRUEVAL || b != 0) abort (); b = __atomic_test_and_set (&a, __ATOMIC_ACQ_REL); - if (b != 1 || a != 1) + if (a != __GCC_ATOMIC_TEST_AND_SET_TRUEVAL || b != 1) abort (); __atomic_clear (&a, __ATOMIC_SEQ_CST);
* config/sparc/sparc.c (TARGET_ATOMIC_TEST_AND_SET_TRUEVAL): New. * config/sparc/sync.md (atomic_test_and_set): Only handle QImode. (ldstub): Rename from ldstubqi. (ldstub<I24MODE>): Remove. diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 19ab54a..1b3b4c8 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -779,6 +779,10 @@ char sparc_hard_reg_printed[8]; #undef TARGET_PRINT_OPERAND_ADDRESS #define TARGET_PRINT_OPERAND_ADDRESS sparc_print_operand_address +/* The value stored by LDSTUB. */ +#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL +#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 0xff + struct gcc_target targetm = TARGET_INITIALIZER; static void diff --git a/gcc/config/sparc/sync.md b/gcc/config/sparc/sync.md index be8c4c4..d07d572 100644 --- a/gcc/config/sparc/sync.md +++ b/gcc/config/sparc/sync.md @@ -242,25 +242,30 @@ "swap\t%1, %0" [(set_attr "type" "multi")]) -(define_expand "atomic_test_and_set<mode>" - [(match_operand:I124MODE 0 "register_operand" "") - (match_operand:I124MODE 1 "memory_operand" "") +(define_expand "atomic_test_and_set" + [(match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "memory_operand" "") (match_operand:SI 2 "const_int_operand" "")] "" { enum memmodel model = (enum memmodel) INTVAL (operands[2]); + rtx ret; sparc_emit_membar_for_model (model, 3, 1); + emit_insn (gen_ldstub (operands[0], operands[1])); + sparc_emit_membar_for_model (model, 3, 2); - if (<MODE>mode != QImode) - operands[1] = adjust_address (operands[1], QImode, 0); - emit_insn (gen_ldstub<mode> (operands[0], operands[1])); + /* Convert the 0/0xff result we would otherwise have to a boolean. + I.e. ignore all but bit 0. */ + ret = expand_simple_binop (QImode, AND, operands[0], const1_rtx, + operands[0], true, OPTAB_LIB_WIDEN); + if (ret != operands[0]) + emit_move_insn (operands[0], ret); - sparc_emit_membar_for_model (model, 3, 2); DONE; }) -(define_insn "ldstubqi" +(define_insn "ldstub" [(set (match_operand:QI 0 "register_operand" "=r") (unspec_volatile:QI [(match_operand:QI 1 "memory_operand" "+m")] UNSPECV_LDSTUB)) @@ -268,13 +273,3 @@ "" "ldstub\t%1, %0" [(set_attr "type" "multi")]) - -(define_insn "ldstub<mode>" - [(set (match_operand:I24MODE 0 "register_operand" "=r") - (zero_extend:I24MODE - (unspec_volatile:QI [(match_operand:QI 1 "memory_operand" "+m")] - UNSPECV_LDSTUB))) - (set (match_dup 1) (const_int -1))] - "" - "ldstub\t%1, %0" - [(set_attr "type" "multi")])
* config/m68k/m68k.c (TARGET_ATOMIC_TEST_AND_SET_TRUEVAL): New. * config/m68k/sync.md (atomic_test_and_set): Rename from sync_test_and_setqi and adjust the operands. (atomic_test_and_set_1): Rename from sync_test_and_setqi_1 and unconditionally enable. diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index e0edd5b..d3ed82b 100644 --- a/gcc/config/m68k/m68k.c +++ b/gcc/config/m68k/m68k.c @@ -303,6 +303,10 @@ static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED; #undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA #define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA m68k_output_addr_const_extra +/* The value stored by TAS. */ +#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL +#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 128 + static const struct attribute_spec m68k_attribute_table[] = { /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, diff --git a/gcc/config/m68k/sync.md b/gcc/config/m68k/sync.md index a40a5bf..5d5002a 100644 --- a/gcc/config/m68k/sync.md +++ b/gcc/config/m68k/sync.md @@ -56,25 +56,23 @@ ;; Elide the seq if operands[0] is dead. "cas<sz> %1,%4,%2\;seq %0") -(define_expand "sync_test_and_setqi" - [(match_operand:QI 0 "register_operand" "") - (match_operand:QI 1 "memory_operand" "") - (match_operand:QI 2 "general_operand" "")] - "!TARGET_CAS" +(define_expand "atomic_test_and_set" + [(match_operand:QI 0 "register_operand" "") ;; bool success output + (match_operand:QI 1 "memory_operand" "") ;; memory + (match_operand:SI 2 "const_int_operand" "")] ;; model + "" { - if (operands[2] != const1_rtx) - FAIL; - emit_insn (gen_sync_test_and_setqi_1 (operands[0], operands[1])); + emit_insn (gen_atomic_test_and_set_1 (operands[0], operands[1])); emit_insn (gen_negqi2 (operands[0], operands[0])); DONE; }) -(define_insn "sync_test_and_setqi_1" +(define_insn "atomic_test_and_set_1" [(set (match_operand:QI 0 "register_operand" "=d") (unspec_volatile:QI [(match_operand:QI 1 "memory_operand" "+m")] UNSPECV_TAS_1)) (set (match_dup 1) (unspec_volatile:QI [(match_dup 1)] UNSPECV_TAS_2))] - "!TARGET_CAS" + "" "tas %1\;sne %0")