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")