From: Soumya AR <soum...@nvidia.com> This patch extends various compiler components to handle the newly added atomic fetch min/max builtins:
1. Address Sanitizer (asan): Extended to recognize atomic min/max builtins and detect invalid memory accesses/overflows when these operations are used. 2. gimple-ssa-warn-access: Extended to validate atomic min/max operations for invalid memory accesses at compile time. 3. Static Analyzer: Extended kf_atomic_fetch_op and kf_atomic_op_fetch classes to model the behavior of atomic min/max builtins. Note: The analyzer currently doesn't recognize ternary operators (e.g., i < j ? i : j) as MIN_EXPR/MAX_EXPR, so analyzer tests use constants to verify correct min/max computation in memory. 4. Thread Sanitizer (tsan): Since TSAN runtime doesn't currently support atomic min/max operations, these builtins are explicitly marked as unsupported. When -fsanitize=thread is used, the compiler emits a warning but the code still compiles and runs correctly - just without thread sanitization for these specific operations. 5. Forward Propagation (forwprop): Optimizes sequences where __atomic_fetch_min/max is followed by a min/max operation using the same values. When the original value returned by fetch_op isn't otherwise used, this pattern is canonicalized to the more efficient __atomic_min/max_fetch variant (which returns the new value instead of the old one). Bootstrapped and regression tested on aarch64-linux-gnu and x86_64-linux-gnu. Cross-compiled and regression tested for arm-linux-gnueabihf-armv7-a and aarch64-linux-gnu without LSE. Signed-off-by: Soumya AR <soum...@nvidia.com> gcc/analyzer/ChangeLog: * kf.cc (register_atomic_builtins): Add support for atomic min/max builtins in static analyzer. gcc/ChangeLog: * asan.cc (get_mem_refs_of_builtin_call): Handle atomic min/max builtins for memory access tracking. * gimple-ssa-warn-access.cc (pass_waccess::check_atomic_builtin): Extend invalid access detection for min/max atomics. * tree-ssa-forwprop.cc (simplify_builtin_call): Add canonicalization for atomic min/max builtins. * tsan.cc (enum tsan_atomic_action): Add warn_missing. (UNHANDLED): Define macro for unsupported operations. (instrument_builtin_call): Emit warning for unsupported min/max atomics with tsan. gcc/testsuite/ChangeLog: * gcc.dg/Wstringop-overflow-78.c: Add tests for min/max atomic overflow detection. * gcc.dg/analyzer/atomic-builtins-1.c: Add analyzer tests for atomic min/max operations. * c-c++-common/asan/atomic-max-invalid.c: New test. * gcc.dg/tree-ssa/atomic-minmax-forwprop.c: New test. * gcc.dg/tsan/atomic-minmax.c: New test. --- gcc/analyzer/kf.cc | 85 +++++++ gcc/asan.cc | 40 ++++ gcc/gimple-ssa-warn-access.cc | 8 + .../c-c++-common/asan/atomic-max-invalid.c | 19 ++ gcc/testsuite/gcc.dg/Wstringop-overflow-78.c | 106 +++++++++ .../gcc.dg/analyzer/atomic-builtins-1.c | 207 ++++++++++++++++++ .../gcc.dg/tree-ssa/atomic-minmax-forwprop.c | 56 +++++ gcc/testsuite/gcc.dg/tsan/atomic-minmax.c | 48 ++++ gcc/tree-ssa-forwprop.cc | 7 + gcc/tsan.cc | 56 ++++- 10 files changed, 631 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/c-c++-common/asan/atomic-max-invalid.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/atomic-minmax-forwprop.c create mode 100644 gcc/testsuite/gcc.dg/tsan/atomic-minmax.c diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index 2a7c3570315..685b518d984 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -191,6 +191,8 @@ public: type __atomic_fetch_and (type *ptr, type val, int memorder); type __atomic_fetch_xor (type *ptr, type val, int memorder); type __atomic_fetch_or (type *ptr, type val, int memorder); + type __atomic_fetch_min (type *ptr, type val, int memorder); + type __atomic_fetch_max (type *ptr, type val, int memorder); */ class kf_atomic_fetch_op : public internal_known_function @@ -234,6 +236,8 @@ private: type __atomic_and_fetch (type *ptr, type val, int memorder); type __atomic_xor_fetch (type *ptr, type val, int memorder); type __atomic_or_fetch (type *ptr, type val, int memorder); + type __atomic_min_fetch (type *ptr, type val, int memorder); + type __atomic_max_fetch (type *ptr, type val, int memorder); */ class kf_atomic_op_fetch : public internal_known_function @@ -2208,6 +2212,87 @@ register_atomic_builtins (known_function_manager &kfm) std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); kfm.add (BUILT_IN_ATOMIC_FETCH_OR_16, std::make_unique<kf_atomic_fetch_op> (BIT_IOR_EXPR)); + /* Atomic min/max operations. */ + kfm.add (BUILT_IN_ATOMIC_FETCH_SMIN_1, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMIN_2, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMIN_4, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMIN_8, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMIN_16, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMAX_1, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMAX_2, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMAX_4, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMAX_8, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_SMAX_16, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMIN_1, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMIN_2, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMIN_4, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMIN_8, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMIN_16, + std::make_unique<kf_atomic_fetch_op> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMAX_1, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMAX_2, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMAX_4, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMAX_8, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_FETCH_UMAX_16, + std::make_unique<kf_atomic_fetch_op> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMIN_FETCH_1, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMIN_FETCH_2, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMIN_FETCH_4, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMIN_FETCH_8, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMIN_FETCH_16, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMAX_FETCH_1, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMAX_FETCH_2, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMAX_FETCH_4, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMAX_FETCH_8, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_SMAX_FETCH_16, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMIN_FETCH_1, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMIN_FETCH_2, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMIN_FETCH_4, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMIN_FETCH_8, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMIN_FETCH_16, + std::make_unique<kf_atomic_op_fetch> (MIN_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMAX_FETCH_1, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMAX_FETCH_2, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMAX_FETCH_4, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMAX_FETCH_8, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); + kfm.add (BUILT_IN_ATOMIC_UMAX_FETCH_16, + std::make_unique<kf_atomic_op_fetch> (MAX_EXPR)); } /* Handle calls to the various IFN_UBSAN_* with no return value. diff --git a/gcc/asan.cc b/gcc/asan.cc index 748b289d6f9..bd8c96932d6 100644 --- a/gcc/asan.cc +++ b/gcc/asan.cc @@ -1155,6 +1155,14 @@ get_mem_refs_of_builtin_call (gcall *call, case BUILT_IN_ATOMIC_FETCH_NAND_1: case BUILT_IN_ATOMIC_FETCH_XOR_1: case BUILT_IN_ATOMIC_FETCH_OR_1: + case BUILT_IN_ATOMIC_FETCH_SMIN_1: + case BUILT_IN_ATOMIC_FETCH_SMAX_1: + case BUILT_IN_ATOMIC_FETCH_UMIN_1: + case BUILT_IN_ATOMIC_FETCH_UMAX_1: + case BUILT_IN_ATOMIC_SMIN_FETCH_1: + case BUILT_IN_ATOMIC_SMAX_FETCH_1: + case BUILT_IN_ATOMIC_UMIN_FETCH_1: + case BUILT_IN_ATOMIC_UMAX_FETCH_1: access_size = 1; goto do_atomic; @@ -1192,6 +1200,14 @@ get_mem_refs_of_builtin_call (gcall *call, case BUILT_IN_ATOMIC_FETCH_NAND_2: case BUILT_IN_ATOMIC_FETCH_XOR_2: case BUILT_IN_ATOMIC_FETCH_OR_2: + case BUILT_IN_ATOMIC_FETCH_SMIN_2: + case BUILT_IN_ATOMIC_FETCH_SMAX_2: + case BUILT_IN_ATOMIC_FETCH_UMIN_2: + case BUILT_IN_ATOMIC_FETCH_UMAX_2: + case BUILT_IN_ATOMIC_SMIN_FETCH_2: + case BUILT_IN_ATOMIC_SMAX_FETCH_2: + case BUILT_IN_ATOMIC_UMIN_FETCH_2: + case BUILT_IN_ATOMIC_UMAX_FETCH_2: access_size = 2; goto do_atomic; @@ -1229,6 +1245,14 @@ get_mem_refs_of_builtin_call (gcall *call, case BUILT_IN_ATOMIC_FETCH_NAND_4: case BUILT_IN_ATOMIC_FETCH_XOR_4: case BUILT_IN_ATOMIC_FETCH_OR_4: + case BUILT_IN_ATOMIC_FETCH_SMIN_4: + case BUILT_IN_ATOMIC_FETCH_SMAX_4: + case BUILT_IN_ATOMIC_FETCH_UMIN_4: + case BUILT_IN_ATOMIC_FETCH_UMAX_4: + case BUILT_IN_ATOMIC_SMIN_FETCH_4: + case BUILT_IN_ATOMIC_SMAX_FETCH_4: + case BUILT_IN_ATOMIC_UMIN_FETCH_4: + case BUILT_IN_ATOMIC_UMAX_FETCH_4: access_size = 4; goto do_atomic; @@ -1266,6 +1290,14 @@ get_mem_refs_of_builtin_call (gcall *call, case BUILT_IN_ATOMIC_FETCH_NAND_8: case BUILT_IN_ATOMIC_FETCH_XOR_8: case BUILT_IN_ATOMIC_FETCH_OR_8: + case BUILT_IN_ATOMIC_FETCH_SMIN_8: + case BUILT_IN_ATOMIC_FETCH_SMAX_8: + case BUILT_IN_ATOMIC_FETCH_UMIN_8: + case BUILT_IN_ATOMIC_FETCH_UMAX_8: + case BUILT_IN_ATOMIC_SMIN_FETCH_8: + case BUILT_IN_ATOMIC_SMAX_FETCH_8: + case BUILT_IN_ATOMIC_UMIN_FETCH_8: + case BUILT_IN_ATOMIC_UMAX_FETCH_8: access_size = 8; goto do_atomic; @@ -1303,6 +1335,14 @@ get_mem_refs_of_builtin_call (gcall *call, case BUILT_IN_ATOMIC_FETCH_NAND_16: case BUILT_IN_ATOMIC_FETCH_XOR_16: case BUILT_IN_ATOMIC_FETCH_OR_16: + case BUILT_IN_ATOMIC_FETCH_SMIN_16: + case BUILT_IN_ATOMIC_FETCH_SMAX_16: + case BUILT_IN_ATOMIC_FETCH_UMIN_16: + case BUILT_IN_ATOMIC_FETCH_UMAX_16: + case BUILT_IN_ATOMIC_SMIN_FETCH_16: + case BUILT_IN_ATOMIC_SMAX_FETCH_16: + case BUILT_IN_ATOMIC_UMIN_FETCH_16: + case BUILT_IN_ATOMIC_UMAX_FETCH_16: access_size = 16; /* FALLTHRU */ do_atomic: diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 0f4aff6b59b..f64e9735925 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -3098,12 +3098,20 @@ pass_waccess::check_atomic_builtin (gcall *stmt) case BUILT_IN_ATOMIC_NAND_FETCH_ ## N: \ case BUILT_IN_ATOMIC_XOR_FETCH_ ## N: \ case BUILT_IN_ATOMIC_OR_FETCH_ ## N: \ + case BUILT_IN_ATOMIC_SMIN_FETCH_ ## N: \ + case BUILT_IN_ATOMIC_SMAX_FETCH_ ## N: \ + case BUILT_IN_ATOMIC_UMIN_FETCH_ ## N: \ + case BUILT_IN_ATOMIC_UMAX_FETCH_ ## N: \ case BUILT_IN_ATOMIC_FETCH_ADD_ ## N: \ case BUILT_IN_ATOMIC_FETCH_SUB_ ## N: \ case BUILT_IN_ATOMIC_FETCH_AND_ ## N: \ case BUILT_IN_ATOMIC_FETCH_NAND_ ## N: \ case BUILT_IN_ATOMIC_FETCH_OR_ ## N: \ case BUILT_IN_ATOMIC_FETCH_XOR_ ## N: \ + case BUILT_IN_ATOMIC_FETCH_SMIN_ ## N: \ + case BUILT_IN_ATOMIC_FETCH_SMAX_ ## N: \ + case BUILT_IN_ATOMIC_FETCH_UMIN_ ## N: \ + case BUILT_IN_ATOMIC_FETCH_UMAX_ ## N: \ bytes = N; \ if (sucs_arg == UINT_MAX) \ sucs_arg = 2; \ diff --git a/gcc/testsuite/c-c++-common/asan/atomic-max-invalid.c b/gcc/testsuite/c-c++-common/asan/atomic-max-invalid.c new file mode 100644 index 00000000000..20f290470e7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/atomic-max-invalid.c @@ -0,0 +1,19 @@ +/* Test that ASAN catches invalid memory access with atomic smax builtin. */ +/* { dg-do run } */ +/* { dg-shouldfail "asan" } */ + +#include <stdint.h> + +int32_t test_smax (int32_t *x) { + return __atomic_fetch_max (x, 100, 0); +} + +int main() { + int32_t *p = (int32_t *) __builtin_malloc (sizeof(int16_t)); + asm volatile ("" : "+r" (p) : : "memory"); + test_smax (p); + asm volatile ("" : "+r" (p) : : "memory"); + return 0; +} + +/* { dg-output "ERROR: AddressSanitizer: heap-buffer-overflow" } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c index a25a418ed76..93d3c767d7c 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c @@ -16,6 +16,8 @@ #define or_fetch(p, q) __atomic_or_fetch (p, q, 0) #define xor_fetch(p, q) __atomic_xor_fetch (p, q, 0) #define nand_fetch(p, q) __atomic_nand_fetch (p, q, 0) +#define min_fetch(p, q) __atomic_min_fetch (p, q, 0) +#define max_fetch(p, q) __atomic_max_fetch (p, q, 0) #define exchange(p, q, r) __atomic_exchange (p, q, r, 0) #define exchange_n(p, n) __atomic_exchange_n (p, n, 0) #define cmpxchg(p, q, r) __atomic_compare_exchange (p, q, r, __COUNTER__, 0, 0) @@ -485,6 +487,110 @@ NOIPA void warn_atomic_exchange_n (void) } +NOIPA void nowarn_atomic_min_fetch (void) +{ + _Atomic char *pc = &eac; + min_fetch (pc, ecc); + + _Atomic short *psi = &easi; + min_fetch (psi, esi); + + _Atomic int *pi = &eai; + min_fetch (pi, ei); + + _Atomic long *pli = &eali; + min_fetch (pli, eli); + + _Atomic long long *plli = &ealli; + min_fetch (plli, elli); +} + + +NOIPA void warn_atomic_min_fetch (void) +{ + _Atomic char *pc = &eac + 1; + min_fetch (pc, ecc); // { dg-warning "-Wstringop-overflow" } + + _Atomic short *psi = (_Atomic short*)((char*)&easi + 1); + min_fetch (psi, esi); // { dg-warning "-Wstringop-overflow" } + psi = (_Atomic short*)((char*)&easi + 2); + min_fetch (psi, esi); // { dg-warning "-Wstringop-overflow" } + + _Atomic int *pi = (_Atomic int*)((char*)&eai + 1); + min_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + pi = (_Atomic int*)((char*)&eai + 2); + min_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + pi = (_Atomic int*)((char*)&eai + sizeof eai); + min_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + + _Atomic long *pli = (_Atomic long*)((char*)&eali + 1); + min_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + pli = (_Atomic long*)((char*)&eali + 1); + min_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + pli = &eali + 1; + min_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + + _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1); + min_fetch (plli, elli); // { dg-warning "-Wstringop-overflow" } + plli = (_Atomic long long*)((char*)&ealli + 1); + min_fetch (plli, eali); // { dg-warning "-Wstringop-overflow" } + plli = &ealli + 1; + min_fetch (plli, elli); // { dg-warning "-Wstringop-overflow" } +} + + +NOIPA void nowarn_atomic_max_fetch (void) +{ + _Atomic char *pc = &eac; + max_fetch (pc, ecc); + + _Atomic short *psi = &easi; + max_fetch (psi, esi); + + _Atomic int *pi = &eai; + max_fetch (pi, ei); + + _Atomic long *pli = &eali; + max_fetch (pli, eli); + + _Atomic long long *plli = &ealli; + max_fetch (plli, elli); +} + + +NOIPA void warn_atomic_max_fetch (void) +{ + _Atomic char *pc = &eac + 1; + max_fetch (pc, ecc); // { dg-warning "-Wstringop-overflow" } + + _Atomic short *psi = (_Atomic short*)((char*)&easi + 1); + max_fetch (psi, esi); // { dg-warning "-Wstringop-overflow" } + psi = (_Atomic short*)((char*)&easi + 2); + max_fetch (psi, esi); // { dg-warning "-Wstringop-overflow" } + + _Atomic int *pi = (_Atomic int*)((char*)&eai + 1); + max_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + pi = (_Atomic int*)((char*)&eai + 2); + max_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + pi = (_Atomic int*)((char*)&eai + sizeof eai); + max_fetch (pi, ei); // { dg-warning "-Wstringop-overflow" } + + _Atomic long *pli = (_Atomic long*)((char*)&eali + 1); + max_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + pli = (_Atomic long*)((char*)&eali + 1); + max_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + pli = &eali + 1; + max_fetch (pli, eli); // { dg-warning "-Wstringop-overflow" } + + _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1); + max_fetch (plli, elli); // { dg-warning "-Wstringop-overflow" } + plli = (_Atomic long long*)((char*)&ealli + 1); + max_fetch (plli, eali); // { dg-warning "-Wstringop-overflow" } + plli = &ealli + 1; + max_fetch (plli, elli); // { dg-warning "-Wstringop-overflow" } +} + + NOIPA void warn_atomic_compare_exchange (void) { _Atomic char *pc = &eac + 1; diff --git a/gcc/testsuite/gcc.dg/analyzer/atomic-builtins-1.c b/gcc/testsuite/gcc.dg/analyzer/atomic-builtins-1.c index 69eac3f87fd..9b34c24e450 100644 --- a/gcc/testsuite/gcc.dg/analyzer/atomic-builtins-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/atomic-builtins-1.c @@ -542,3 +542,210 @@ void test__atomic_fetch_or_on_uint64_t (uint64_t i, uint64_t j) __analyzer_eval (i == (orig_i | j)); /* { dg-warning "TRUE" } */ __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ } + +/* __atomic_fetch_min. */ + +void test__atomic_fetch_min_on_uint32_t (uint32_t i, uint32_t j) +{ + uint32_t orig_i = i; + uint32_t ret; + ret = __atomic_fetch_min (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 10; + ret = __atomic_fetch_min (&i, 5, 0); + __analyzer_eval (ret == 10); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 5); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_min_on_uint64_t (uint64_t i, uint64_t j) +{ + uint64_t orig_i = i; + uint64_t ret; + ret = __atomic_fetch_min (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 20; + ret = __atomic_fetch_min (&i, 15, 0); + __analyzer_eval (ret == 20); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 15); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_min_on_int32_t (int32_t i, int32_t j) +{ + int32_t orig_i = i; + int32_t ret; + ret = __atomic_fetch_min (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 10; + ret = __atomic_fetch_min (&i, 5, 0); + __analyzer_eval (i == 5); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_min_on_int64_t (int64_t i, int64_t j) +{ + int64_t orig_i = i; + int64_t ret; + ret = __atomic_fetch_min (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = -5; + ret = __atomic_fetch_min (&i, -10, 0); + __analyzer_eval (ret == -5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == -10); /* { dg-warning "TRUE" } */ +} + +/* __atomic_fetch_max. */ + +void test__atomic_fetch_max_on_uint32_t (uint32_t i, uint32_t j) +{ + uint32_t orig_i = i; + uint32_t ret; + ret = __atomic_fetch_max (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 10; + ret = __atomic_fetch_max (&i, 15, 0); + __analyzer_eval (ret == 10); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 15); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_max_on_uint64_t (uint64_t i, uint64_t j) +{ + uint64_t orig_i = i; + uint64_t ret; + ret = __atomic_fetch_max (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 25; + ret = __atomic_fetch_max (&i, 30, 0); + __analyzer_eval (ret == 25); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 30); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_max_on_int32_t (int32_t i, int32_t j) +{ + int32_t orig_i = i; + int32_t ret; + ret = __atomic_fetch_max (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = -10; + ret = __atomic_fetch_max (&i, -5, 0); + __analyzer_eval (ret == -10); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == -5); /* { dg-warning "TRUE" } */ +} + +void test__atomic_fetch_max_on_int64_t (int64_t i, int64_t j) +{ + int64_t orig_i = i; + int64_t ret; + ret = __atomic_fetch_max (&i, j, 0); + __analyzer_eval (ret == orig_i); /* { dg-warning "TRUE" } */ + + i = 100; + ret = __atomic_fetch_max (&i, 200, 0); + __analyzer_eval (ret == 100); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 200); /* { dg-warning "TRUE" } */ +} + +/* __atomic_min_fetch. */ + +void test__atomic_min_fetch_on_uint32_t (uint32_t i, uint32_t j) +{ + uint32_t ret; + ret = __atomic_min_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 10; + ret = __atomic_min_fetch (&i, 5, 0); + __analyzer_eval (ret == 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 5); /* { dg-warning "TRUE" } */ +} + +void test__atomic_min_fetch_on_uint64_t (uint64_t i, uint64_t j) +{ + uint64_t ret; + ret = __atomic_min_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 50; + ret = __atomic_min_fetch (&i, 40, 0); + __analyzer_eval (ret == 40); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 40); /* { dg-warning "TRUE" } */ +} + +void test__atomic_min_fetch_on_int32_t (int32_t i, int32_t j) +{ + int32_t ret; + ret = __atomic_min_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 5; + ret = __atomic_min_fetch (&i, -3, 0); + __analyzer_eval (ret == -3); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == -3); /* { dg-warning "TRUE" } */ +} + +void test__atomic_min_fetch_on_int64_t (int64_t i, int64_t j) +{ + int64_t ret; + ret = __atomic_min_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = -20; + ret = __atomic_min_fetch (&i, -30, 0); + __analyzer_eval (ret == -30); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == -30); /* { dg-warning "TRUE" } */ +} + +/* __atomic_max_fetch. */ + +void test__atomic_max_fetch_on_uint32_t (uint32_t i, uint32_t j) +{ + uint32_t ret; + ret = __atomic_max_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 10; + ret = __atomic_max_fetch (&i, 15, 0); + __analyzer_eval (ret == 15); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 15); /* { dg-warning "TRUE" } */ +} + +void test__atomic_max_fetch_on_uint64_t (uint64_t i, uint64_t j) +{ + uint64_t ret; + ret = __atomic_max_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 75; + ret = __atomic_max_fetch (&i, 100, 0); + __analyzer_eval (ret == 100); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 100); /* { dg-warning "TRUE" } */ +} + +void test__atomic_max_fetch_on_int32_t (int32_t i, int32_t j) +{ + int32_t ret; + ret = __atomic_max_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = -15; + ret = __atomic_max_fetch (&i, -10, 0); + __analyzer_eval (ret == -10); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == -10); /* { dg-warning "TRUE" } */ +} + +void test__atomic_max_fetch_on_int64_t (int64_t i, int64_t j) +{ + int64_t ret; + ret = __atomic_max_fetch (&i, j, 0); + __analyzer_eval (ret == i); /* { dg-warning "TRUE" } */ + + i = 50; + ret = __atomic_max_fetch (&i, 60, 0); + __analyzer_eval (ret == 60); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 60); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/atomic-minmax-forwprop.c b/gcc/testsuite/gcc.dg/tree-ssa/atomic-minmax-forwprop.c new file mode 100644 index 00000000000..f6648a31ac7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/atomic-minmax-forwprop.c @@ -0,0 +1,56 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-forwprop1" } */ + +#include <stdint.h> + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +int32_t test_fetch_min_to_min_fetch_signed (int32_t *p, int32_t val) +{ + int32_t old = __atomic_fetch_min (p, val, __ATOMIC_SEQ_CST); + return MIN(old, val); +} + +int32_t test_fetch_max_to_max_fetch_signed (int32_t *p, int32_t val) +{ + int32_t old = __atomic_fetch_max (p, val, __ATOMIC_SEQ_CST); + return MAX(old, val); +} + +uint32_t test_fetch_min_to_min_fetch_unsigned (uint32_t *p, uint32_t val) +{ + uint32_t old = __atomic_fetch_min (p, val, __ATOMIC_SEQ_CST); + return MIN(old, val); +} + +uint32_t test_fetch_max_to_max_fetch_unsigned (uint32_t *p, uint32_t val) +{ + uint32_t old = __atomic_fetch_max (p, val, __ATOMIC_SEQ_CST); + return MAX(old, val); +} + +int16_t test_fetch_min_16bit (int16_t *p, int16_t val) +{ + int16_t old = __atomic_fetch_min (p, val, __ATOMIC_SEQ_CST); + return MIN(old, val); +} + +int64_t test_fetch_max_64bit (int64_t *p, int64_t val) +{ + int64_t old = __atomic_fetch_max (p, val, __ATOMIC_SEQ_CST); + return MAX(old, val); +} + +/* We expect the fetch_op calls to be converted to op_fetch calls. + Look for the op_fetch variants in the forwprop1 dump. */ +/* { dg-final { scan-tree-dump-times "__atomic_smin_fetch_4" 1 "forwprop1" } } */ +/* { dg-final { scan-tree-dump-times "__atomic_smax_fetch_4" 1 "forwprop1" } } */ +/* { dg-final { scan-tree-dump-times "__atomic_umin_fetch_4" 1 "forwprop1" } } */ +/* { dg-final { scan-tree-dump-times "__atomic_umax_fetch_4" 1 "forwprop1" } } */ +/* { dg-final { scan-tree-dump-times "__atomic_smin_fetch_2" 1 "forwprop1" } } */ +/* { dg-final { scan-tree-dump-times "__atomic_smax_fetch_8" 1 "forwprop1" } } */ + +/* And the original fetch_op calls should be gone. */ +/* { dg-final { scan-tree-dump-not "__atomic_fetch_smin" "forwprop1" } } */ +/* { dg-final { scan-tree-dump-not "__atomic_fetch_smax" "forwprop1" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/tsan/atomic-minmax.c b/gcc/testsuite/gcc.dg/tsan/atomic-minmax.c new file mode 100644 index 00000000000..0efbc52bd00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tsan/atomic-minmax.c @@ -0,0 +1,48 @@ +/* Test that TSAN warns about unsupported atomic min/max operations. */ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=thread" } */ + +#include <stdint.h> + +int8_t i8; +int16_t i16; +int32_t i32; +int64_t i64; + +uint8_t u8; +uint16_t u16; +uint32_t u32; +uint64_t u64; + +void test_atomic_minmax (void) +{ + /* Test fetch_min operations */ + __atomic_fetch_min (&i8, 5, 0); /* { dg-warning {.__atomic_fetch_smin_1. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&i16, 5, 0); /* { dg-warning {.__atomic_fetch_smin_2. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&i32, 5, 0); /* { dg-warning {.__atomic_fetch_smin_4. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&i64, 5, 0); /* { dg-warning {.__atomic_fetch_smin_8. is not supported with .-fsanitize=thread.} } */ + + /* Test fetch_max operations */ + __atomic_fetch_max (&i8, 5, 0); /* { dg-warning {.__atomic_fetch_smax_1. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&i16, 5, 0); /* { dg-warning {.__atomic_fetch_smax_2. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&i32, 5, 0); /* { dg-warning {.__atomic_fetch_smax_4. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&i64, 5, 0); /* { dg-warning {.__atomic_fetch_smax_8. is not supported with .-fsanitize=thread.} } */ + + /* Test unsigned fetch_min operations */ + __atomic_fetch_min (&u8, 5, 0); /* { dg-warning {.__atomic_fetch_umin_1. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&u16, 5, 0); /* { dg-warning {.__atomic_fetch_umin_2. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&u32, 5, 0); /* { dg-warning {.__atomic_fetch_umin_4. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_min (&u64, 5, 0); /* { dg-warning {.__atomic_fetch_umin_8. is not supported with .-fsanitize=thread.} } */ + + /* Test unsigned fetch_max operations */ + __atomic_fetch_max (&u8, 5, 0); /* { dg-warning {.__atomic_fetch_umax_1. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&u16, 5, 0); /* { dg-warning {.__atomic_fetch_umax_2. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&u32, 5, 0); /* { dg-warning {.__atomic_fetch_umax_4. is not supported with .-fsanitize=thread.} } */ + __atomic_fetch_max (&u64, 5, 0); /* { dg-warning {.__atomic_fetch_umax_8. is not supported with .-fsanitize=thread.} } */ + + /* Test op_fetch versions (return new value) */ + __atomic_min_fetch (&i32, 5, 0); /* { dg-warning {.__atomic_smin_fetch_4. is not supported with .-fsanitize=thread.} } */ + __atomic_max_fetch (&i32, 5, 0); /* { dg-warning {.__atomic_smax_fetch_4. is not supported with .-fsanitize=thread.} } */ + __atomic_min_fetch (&u32, 5, 0); /* { dg-warning {.__atomic_umin_fetch_4. is not supported with .-fsanitize=thread.} } */ + __atomic_max_fetch (&u32, 5, 0); /* { dg-warning {.__atomic_umax_fetch_4. is not supported with .-fsanitize=thread.} } */ +} \ No newline at end of file diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc index 43b1c9d696f..bec061b2cab 100644 --- a/gcc/tree-ssa-forwprop.cc +++ b/gcc/tree-ssa-forwprop.cc @@ -1767,6 +1767,11 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2) CASE_ATOMIC (SYNC_SUB_AND_FETCH, SYNC_FETCH_AND_SUB, PLUS_EXPR) CASE_ATOMIC (SYNC_XOR_AND_FETCH, SYNC_FETCH_AND_XOR, BIT_XOR_EXPR) + CASE_ATOMIC (ATOMIC_FETCH_SMIN, ATOMIC_SMIN_FETCH, MIN_EXPR) + CASE_ATOMIC (ATOMIC_FETCH_SMAX, ATOMIC_SMAX_FETCH, MAX_EXPR) + CASE_ATOMIC (ATOMIC_FETCH_UMIN, ATOMIC_UMIN_FETCH, MIN_EXPR) + CASE_ATOMIC (ATOMIC_FETCH_UMAX, ATOMIC_UMAX_FETCH, MAX_EXPR) + #undef CASE_ATOMIC handle_atomic_fetch_op: @@ -1967,6 +1972,8 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2) update_stmt (use_stmt); if (atomic_op != BIT_AND_EXPR && atomic_op != BIT_IOR_EXPR + && atomic_op != MIN_EXPR + && atomic_op != MAX_EXPR && !stmt_ends_bb_p (stmt2)) { /* For the benefit of debug stmts, emit stmt(s) to set diff --git a/gcc/tsan.cc b/gcc/tsan.cc index 70a7d09065e..b3ea6595d92 100644 --- a/gcc/tsan.cc +++ b/gcc/tsan.cc @@ -254,7 +254,7 @@ enum tsan_atomic_action { check_last, add_seq_cst, add_acquire, weak_cas, strong_cas, bool_cas, val_cas, lock_release, fetch_op, fetch_op_seq_cst, - bool_clear, bool_test_and_set + bool_clear, bool_test_and_set, warn_missing }; /* Table how to map sync/atomic builtins to their corresponding @@ -292,6 +292,8 @@ static const struct tsan_map_atomic TRANSFORM (fcode, tsan_fcode, bool_clear, ERROR_MARK) #define BOOL_TEST_AND_SET(fcode, tsan_fcode) \ TRANSFORM (fcode, tsan_fcode, bool_test_and_set, ERROR_MARK) +#define UNHANDLED(fcode) \ + TRANSFORM (fcode, TSAN_ATOMIC_THREAD_FENCE, warn_missing, ERROR_MARK) CHECK_LAST (ATOMIC_LOAD_1, TSAN_ATOMIC8_LOAD), CHECK_LAST (ATOMIC_LOAD_2, TSAN_ATOMIC16_LOAD), @@ -339,6 +341,28 @@ static const struct tsan_map_atomic CHECK_LAST (ATOMIC_FETCH_NAND_8, TSAN_ATOMIC64_FETCH_NAND), CHECK_LAST (ATOMIC_FETCH_NAND_16, TSAN_ATOMIC128_FETCH_NAND), + /* Atomic min/max operations are not supported by TSAN. */ + UNHANDLED (ATOMIC_FETCH_SMIN_1), + UNHANDLED (ATOMIC_FETCH_SMIN_2), + UNHANDLED (ATOMIC_FETCH_SMIN_4), + UNHANDLED (ATOMIC_FETCH_SMIN_8), + UNHANDLED (ATOMIC_FETCH_SMIN_16), + UNHANDLED (ATOMIC_FETCH_SMAX_1), + UNHANDLED (ATOMIC_FETCH_SMAX_2), + UNHANDLED (ATOMIC_FETCH_SMAX_4), + UNHANDLED (ATOMIC_FETCH_SMAX_8), + UNHANDLED (ATOMIC_FETCH_SMAX_16), + UNHANDLED (ATOMIC_FETCH_UMIN_1), + UNHANDLED (ATOMIC_FETCH_UMIN_2), + UNHANDLED (ATOMIC_FETCH_UMIN_4), + UNHANDLED (ATOMIC_FETCH_UMIN_8), + UNHANDLED (ATOMIC_FETCH_UMIN_16), + UNHANDLED (ATOMIC_FETCH_UMAX_1), + UNHANDLED (ATOMIC_FETCH_UMAX_2), + UNHANDLED (ATOMIC_FETCH_UMAX_4), + UNHANDLED (ATOMIC_FETCH_UMAX_8), + UNHANDLED (ATOMIC_FETCH_UMAX_16), + CHECK_LAST (ATOMIC_THREAD_FENCE, TSAN_ATOMIC_THREAD_FENCE), CHECK_LAST (ATOMIC_SIGNAL_FENCE, TSAN_ATOMIC_SIGNAL_FENCE), @@ -373,6 +397,28 @@ static const struct tsan_map_atomic FETCH_OP (ATOMIC_NAND_FETCH_8, TSAN_ATOMIC64_FETCH_NAND, BIT_NOT_EXPR), FETCH_OP (ATOMIC_NAND_FETCH_16, TSAN_ATOMIC128_FETCH_NAND, BIT_NOT_EXPR), + /* Atomic min/max op_fetch operations are not supported by TSAN. */ + UNHANDLED (ATOMIC_SMIN_FETCH_1), + UNHANDLED (ATOMIC_SMIN_FETCH_2), + UNHANDLED (ATOMIC_SMIN_FETCH_4), + UNHANDLED (ATOMIC_SMIN_FETCH_8), + UNHANDLED (ATOMIC_SMIN_FETCH_16), + UNHANDLED (ATOMIC_SMAX_FETCH_1), + UNHANDLED (ATOMIC_SMAX_FETCH_2), + UNHANDLED (ATOMIC_SMAX_FETCH_4), + UNHANDLED (ATOMIC_SMAX_FETCH_8), + UNHANDLED (ATOMIC_SMAX_FETCH_16), + UNHANDLED (ATOMIC_UMIN_FETCH_1), + UNHANDLED (ATOMIC_UMIN_FETCH_2), + UNHANDLED (ATOMIC_UMIN_FETCH_4), + UNHANDLED (ATOMIC_UMIN_FETCH_8), + UNHANDLED (ATOMIC_UMIN_FETCH_16), + UNHANDLED (ATOMIC_UMAX_FETCH_1), + UNHANDLED (ATOMIC_UMAX_FETCH_2), + UNHANDLED (ATOMIC_UMAX_FETCH_4), + UNHANDLED (ATOMIC_UMAX_FETCH_8), + UNHANDLED (ATOMIC_UMAX_FETCH_16), + ADD_ACQUIRE (SYNC_LOCK_TEST_AND_SET_1, TSAN_ATOMIC8_EXCHANGE), ADD_ACQUIRE (SYNC_LOCK_TEST_AND_SET_2, TSAN_ATOMIC16_EXCHANGE), ADD_ACQUIRE (SYNC_LOCK_TEST_AND_SET_4, TSAN_ATOMIC32_EXCHANGE), @@ -508,6 +554,14 @@ instrument_builtin_call (gimple_stmt_iterator *gsi) warning_at (gimple_location (stmt), OPT_Wtsan, "%qs is not supported with %qs", "atomic_thread_fence", "-fsanitize=thread"); + if (tsan_atomic_table[i].action == warn_missing) + { + warning_at (gimple_location (stmt), OPT_Wtsan, + "%qD is not supported with %qs", + builtin_decl_implicit (tsan_atomic_table[i].fcode), + "-fsanitize=thread"); + continue; + } tree decl = builtin_decl_implicit (tsan_atomic_table[i].tsan_fcode); if (decl == NULL_TREE) -- 2.44.0