The issue that I'm trying to solve is that we will have at least two targets whose test-and-set insn does not define "set" as 1, as a variable of type "bool" would expect. In the case of Sparc we *could* make the test-and-set implementation test for any non-zero value, but in the case of m68k the hw will only test-and-set the msb of the byte.
This compiles on x86_64, and is still undergoing testing, but I wanted to get this out before Ben quits for the day. ;-) Notes: * I try to leave _M_i as bool if at all possible, as this gives the best information to the debugger. * The suggestion on IRC to derive private from __atomic_flag_base falls afoul of src/c++11/compatibility-atomic-c++0x.cc. I'll leave any possible cleanup here to libstdc++ maintainers. ;-) * The changes to the target files actually depend on outstanding patches in my tree. But at least show where the problems would lie. Did I miss anything? r~
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/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/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/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 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