Work around arm64 clang codegen issues breaking CI
CI / build-image (arm64, ubuntu-latest-arm64) (push) Successful in 22s
CI / build-image (amd64, ubuntu-latest-amd64) (push) Successful in 42s
CI / pre-commit (push) Successful in 34s
CI / release (arm64, ubuntu-latest-arm64) (push) Failing after 1m7s
CI / test (-DCMAKE_BUILD_TYPE=Debug, debug) (push) Successful in 2m35s
CI / test (-DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 64-bit-versions) (push) Successful in 2m27s
CI / test (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc) (push) Successful in 2m35s
CI / test (-DUSE_SIMD_FALLBACK=ON, simd-fallback) (push) Successful in 2m27s
CI / release (amd64, ubuntu-latest-amd64) (push) Failing after 2m32s
CI / coverage (push) Failing after 2m20s

Disable preserve_none under ASan on aarch64: every clang tested (20,
21, trunk 22) miscompiles the continuation chains with that
combination, crashing ~97% of fuzz corpus tests. The chains still work
with the default calling convention. See #38.

Mark the masked Node3 scan results defined for valgrind on aarch64:
clang 21+ lowers the in-bounds tests through flags+csel, which
memcheck models imprecisely, tainting bits the mask provably clears.
See #39.

Skip valgrind tests in the arm64 release job, since -DNVALGRIND
compiles out the client requests the workaround relies on.

Both issues track filing upstream bugs.
This commit is contained in:
2026-06-12 16:35:57 -04:00
parent 0921d8fedf
commit 3a82d90914
2 changed files with 33 additions and 3 deletions
+5 -1
View File
@@ -149,7 +149,11 @@ jobs:
- name: Test - name: Test
run: | run: |
cd build cd build
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 -T Test -j "$(nproc)" --timeout 90 > /dev/null # On arm64, valgrind needs the MAKE_MEM_DEFINED client requests for
# https://git.weaselab.dev/weaselab/conflict-set/issues/39, but this
# build has -DNVALGRIND, which compiles them out. Skip valgrind
# tests here; they run annotated in the test job.
ctest --no-compress-output --test-output-size-passed 100000 --test-output-size-failed 100000 ${{ matrix.arch == 'arm64' && '-E valgrind' || '' }} -T Test -j "$(nproc)" --timeout 90 > /dev/null
- name: Package - name: Package
run: | run: |
+28 -2
View File
@@ -52,6 +52,14 @@ limitations under the License.
#endif #endif
#endif #endif
#ifndef __SANITIZE_ADDRESS__
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define __SANITIZE_ADDRESS__
#endif
#endif
#endif
#include <memcheck.h> #include <memcheck.h>
using namespace weaselab; using namespace weaselab;
@@ -2246,6 +2254,13 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
mask |= inBounds(self->index[i]) << i; mask |= inBounds(self->index[i]) << i;
} }
mask &= (1 << self->numChildren) - 1; mask &= (1 << self->numChildren) - 1;
#ifdef __aarch64__
// The bits surviving the mask above don't derive from uninitialized slots,
// but clang 21+ on aarch64 lowers inBounds through flags+csel, which
// memcheck models imprecisely, tainting bits the mask provably clears.
// https://git.weaselab.dev/weaselab/conflict-set/issues/39
VALGRIND_MAKE_MEM_DEFINED(&mask, sizeof(mask));
#endif
if (!mask) { if (!mask) {
return true; return true;
} }
@@ -2257,7 +2272,13 @@ bool checkMaxBetweenExclusiveImpl(Node3 *n, int begin, int end,
compared |= (self->childMaxVersion[i] > readVersion) << i; compared |= (self->childMaxVersion[i] > readVersion) << i;
} }
return !(compared & mask) && firstRangeOk; uint32_t compared_masked = compared & mask;
#ifdef __aarch64__
// Same imprecise csel modeling as above.
// https://git.weaselab.dev/weaselab/conflict-set/issues/39
VALGRIND_MAKE_MEM_DEFINED(&compared_masked, sizeof(compared_masked));
#endif
return !compared_masked && firstRangeOk;
} }
template <bool kAVX512> template <bool kAVX512>
@@ -3048,7 +3069,12 @@ Node *firstGeqPhysical(Node *n, const TrivialSpan key) {
#define MUSTTAIL #define MUSTTAIL
#endif #endif
#if __has_attribute(preserve_none) // ASan + preserve_none miscompiles the continuation chains on aarch64 with
// every clang tested (20, 21, trunk 22). The chains work with the default
// calling convention, so use that under ASan on aarch64.
// https://git.weaselab.dev/weaselab/conflict-set/issues/38
#if __has_attribute(preserve_none) && \
!(defined(__aarch64__) && defined(__SANITIZE_ADDRESS__))
#define PRESERVE_NONE __attribute__((preserve_none)) #define PRESERVE_NONE __attribute__((preserve_none))
#else #else
#define PRESERVE_NONE #define PRESERVE_NONE