https://gcc.gnu.org/g:ebe8e07eb8311b046e1e56e54ee7b5b21eccb9ab
commit r16-8113-gebe8e07eb8311b046e1e56e54ee7b5b21eccb9ab Author: Jonathan Wakely <[email protected]> Date: Wed Mar 11 11:41:56 2026 +0000 libstdc++: Rewrite test for compare_exchange padding Change the struct to ensure there is a padding byte between the members, which was previously not the case for byte-aligned targets such as cris-elf. Add a new helper function to verify that the padding bits in a struct S and in the std::atomic<S> are zero, without needing to disable SRA. Then rewrite the test in terms of that helper, so we check directly that the atomic object has the correct value and has no padding bits. Previously we loaded a value (with -fno-tree-sra to hopefully preserve padding) and then just using memcmp on that value to see if it was bitwise identical to another value, but didn't actually check the members were correct or that the padding was clear. Also add a loop around the weak compare-exchange, to tolerate spurious failures as described in https://gcc.gnu.org/pipermail/gcc-patches/2026-March/710289.html libstdc++-v3/ChangeLog: * testsuite/29_atomics/atomic/compare_exchange_padding.cc: Rewrite to be robust against spurious failures of weak compare exchange, and to check padding bits more directly. Diff: --- .../29_atomics/atomic/compare_exchange_padding.cc | 71 ++++++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/compare_exchange_padding.cc b/libstdc++-v3/testsuite/29_atomics/atomic/compare_exchange_padding.cc index d9c7b90c1a5c..41c77da823a4 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/compare_exchange_padding.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/compare_exchange_padding.cc @@ -1,22 +1,52 @@ // { dg-do run { target c++11 } } // { dg-require-atomic-cmpxchg-word "" } // { dg-add-options libatomic } -// { dg-additional-options "-fno-tree-sra" } #include <atomic> #include <cstring> +#include <cstdint> #include <testsuite_hooks.h> -struct S { char c; short s; }; +struct S +{ + char c; + alignas(2) std::int16_t s; + + bool operator==(const S& rhs) const { return c == rhs.c && s == rhs.s; } +}; void __attribute__((noinline,noipa)) fill_struct(S& s) { std::memset(&s, 0xff, sizeof(S)); } +bool __attribute__((noinline,noipa)) +padding_is_zero(const S& s) +{ + unsigned char bytes[sizeof(S)]; + std::memcpy(bytes, &s, sizeof(S)); + return bytes[1] == 0; +} + bool -compare_struct(const S& a, const S& b) -{ return std::memcmp(&a, &b, sizeof(S)) == 0; } +padding_is_zero(std::atomic<S>& a) +{ + const S dummy{}; + + // a.load() does not necessarily preserve padding bits, because the + // temporary returned from a.load() might get SRA'd so that the two + // data members are copied in separate registers and padding bits are + // not copied. + S s = a.load(); + + // So instead we use compare-exchange with an incorrect 'expected' value, + s.c = ~s.c; + // so that the compare will fail and return the value (including padding): + a.compare_exchange_weak(s, dummy); + + // Now we can inspect the padding bits to check they are zero: + return padding_is_zero(s); +} int main () @@ -27,18 +57,35 @@ main () s.s = 42; std::atomic<S> as{ s }; - auto ts = as.load(); // SRA might prevent copying of padding bits here. - VERIFY( !compare_struct(s, ts) ); // padding cleared on construction + VERIFY( as.load() == s ); // members are set correctly + VERIFY( padding_is_zero(as) ); // but padding was cleared on construction + + ++s.s; as.exchange(s); - auto es = as.load(); // SRA might prevent copying of padding bits here. - VERIFY( compare_struct(ts, es) ); // padding cleared on exchange + VERIFY( as.load() == s ); // members are set correctly + VERIFY( padding_is_zero(as) ); // but padding was cleared on construction S n; fill_struct(n); n.c = 'b'; n.s = 71; - // padding cleared on compexchg - VERIFY( as.compare_exchange_weak(s, n) ); - VERIFY( as.compare_exchange_strong(n, s) ); - return 0; + // padding in S ignored for compare, and padding in N not stored into AS: + VERIFY( as.compare_exchange_strong(s, n) ); + VERIFY( as.load() == n ); // members are set correctly + VERIFY( padding_is_zero(as) ); // but padding was cleared on compare exchange + + S w; + fill_struct(w); + w.c = 'c'; + w.s = 100; + // Weak compare exchange can fail spuriously, so loop a few times. + int count = 10; + do + { + VERIFY( --count ); // Should not keep failing indefinitely. + // padding in N ignored for compare, and padding in W not stored into AS: + } while ( ! as.compare_exchange_weak(n, w) ); + auto ws = as.load(); // SRA might prevent copying of padding bits here. + VERIFY( as.load() == w ); // members are set correctly + VERIFY( padding_is_zero(as) ); // but padding was cleared on compare exchange }
