Building the library with USE_64_BIT=1 on aarch64/arm64 using gcc fails to
compile. The 64-bit NEON path of conflictMask16 passes a uint64x2_t (the
return type of vcgtq_s64) to vmovn_s64, which requires an int64x2_t. clang
accepts this via an implicit vector conversion, but gcc does not, so the build
aborts.
This code was added by the fix for #69 ("ARM NEON scan helpers truncate 64-bit
versions when USE_64_BIT=1"). That issue and its fix were validated with clang
on aarch64 (see the reproduction in #69), so the gcc breakage went unnoticed.
CI never compiles this path: the 64-bit-versions matrix runs on amd64/clang
(uses the AVX compare16/compare16_avx512, not conflictMask16, which is
guarded by #ifdef HAS_ARM_NEON), and the aarch64 matrices all use USE_64_BIT=0 (the #else 32-bit path). So the aarch64 USE_64_BIT=1 branch
is entirely untested in CI.
Location
ConflictSet.cpp, conflictMask16, the #if USE_64_BIT arm, line 2138:
inlineuint8x16_tconflictMask16(constInternalVersionT*vs,InternalVersionTreadVersion){#if USE_64_BIT
int64_trv;memcpy(&rv,&readVersion,sizeof(rv));constautorvVec=vdupq_n_s64(rv);int32x2_tr32[8];constauto*vsp=reinterpret_cast<constint64_t*>(vs);for(intj=0;j<8;++j){r32[j]=vmovn_s64(vcgtq_s64(vld1q_s64(vsp+2*j),rvVec));// <-- line 2138
}...
So the failure is specific to the gcc + aarch64 + USE_64_BIT=1 combination,
isolated to the conflictMask16#if USE_64_BIT branch.
Expected vs actual
Expected: USE_64_BIT=1 (a flag the project's own CI uses, just on amd64)
builds on aarch64 with gcc, the platform's default system compiler on many
Linux distributions (e.g. Fedora/RHEL aarch64 ship gcc as the default
compiler, and the default gcc/aarch64 build of this repo already works).
Actual: the build fails with a hard type error.
Impact
Anyone on an aarch64 system who wants 64-bit versions (e.g. to support version
histories beyond 2^32, or to avoid the 2-billion version window) and builds
with gcc — the default compiler on that platform — cannot compile the library
at all when passing -DUSE_64_BIT=1. Because CI does not exercise the
aarch64 USE_64_BIT=1 path, this regression is not caught.
Suggested fix
Make the vector types explicit so the code is not dependent on lax vector
conversions. For example, reinterpret the comparison result before narrowing:
(Or narrow with vmovn_u64, which takes uint64x2_t, and reinterpret to int32x2_t.) This keeps the generated code identical on clang while allowing
gcc to compile.
## Defect
Building the library with `USE_64_BIT=1` on aarch64/arm64 using **gcc** fails to
compile. The 64-bit NEON path of `conflictMask16` passes a `uint64x2_t` (the
return type of `vcgtq_s64`) to `vmovn_s64`, which requires an `int64x2_t`. clang
accepts this via an implicit vector conversion, but gcc does not, so the build
aborts.
This code was added by the fix for #69 ("ARM NEON scan helpers truncate 64-bit
versions when USE_64_BIT=1"). That issue and its fix were validated with clang
on aarch64 (see the reproduction in #69), so the gcc breakage went unnoticed.
CI never compiles this path: the `64-bit-versions` matrix runs on **amd64/clang**
(uses the AVX `compare16`/`compare16_avx512`, not `conflictMask16`, which is
guarded by `#ifdef HAS_ARM_NEON`), and the aarch64 matrices all use
`USE_64_BIT=0` (the `#else` 32-bit path). So the aarch64 `USE_64_BIT=1` branch
is entirely untested in CI.
## Location
`ConflictSet.cpp`, `conflictMask16`, the `#if USE_64_BIT` arm, line 2138:
```cpp
inline uint8x16_t conflictMask16(const InternalVersionT *vs,
InternalVersionT readVersion) {
#if USE_64_BIT
int64_t rv;
memcpy(&rv, &readVersion, sizeof(rv));
const auto rvVec = vdupq_n_s64(rv);
int32x2_t r32[8];
const auto *vsp = reinterpret_cast<const int64_t *>(vs);
for (int j = 0; j < 8; ++j) {
r32[j] = vmovn_s64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec)); // <-- line 2138
}
...
```
`vcgtq_s64` returns `uint64x2_t`; `vmovn_s64` takes `int64x2_t`.
## Reproduction
```sh
g++ --version # g++ (GCC) 16.1.1, target aarch64-redhat-linux-gnu
g++ -std=c++20 -O2 -DUSE_64_BIT=1 -I include -I third_party/valgrind -I . \
ConflictSet.cpp -shared -fPIC -o /tmp/libcs64.so -fsyntax-only
```
Result (exit code 1):
```
ConflictSet.cpp: In function 'uint8x16_t conflictMask16(const InternalVersionT*, InternalVersionT)':
ConflictSet.cpp:2138:23: note: use '-flax-vector-conversions' to permit conversions between vectors with differing element types or numbers of subparts
2138 | r32[j] = vmovn_s64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec));
ConflictSet.cpp:2138:33: error: cannot convert 'uint64x2_t' to 'int64x2_t'
2138 | r32[j] = vmovn_s64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| uint64x2_t
arm_neon.h:4493:22: note: initializing argument 1 of 'int32x2_t vmovn_s64(int64x2_t)'
```
For comparison, the same file compiles cleanly on this aarch64 host in three
nearby configurations:
- gcc, default (`USE_64_BIT=0`): `g++ -std=c++20 -O2 -I include -I third_party/valgrind -I . ConflictSet.cpp -shared -fPIC -o /tmp/libcs0.so -fsyntax-only` -> exit 0
- clang, `USE_64_BIT=1`: `clang++ -std=c++20 -O2 -DUSE_64_BIT=1 ...` -> exit 0
So the failure is specific to the gcc + aarch64 + `USE_64_BIT=1` combination,
isolated to the `conflictMask16` `#if USE_64_BIT` branch.
## Expected vs actual
- Expected: `USE_64_BIT=1` (a flag the project's own CI uses, just on amd64)
builds on aarch64 with gcc, the platform's default system compiler on many
Linux distributions (e.g. Fedora/RHEL aarch64 ship gcc as the default
compiler, and the default gcc/aarch64 build of this repo already works).
- Actual: the build fails with a hard type error.
## Impact
Anyone on an aarch64 system who wants 64-bit versions (e.g. to support version
histories beyond 2^32, or to avoid the 2-billion version window) and builds
with gcc — the default compiler on that platform — cannot compile the library
at all when passing `-DUSE_64_BIT=1`. Because CI does not exercise the
aarch64 `USE_64_BIT=1` path, this regression is not caught.
## Suggested fix
Make the vector types explicit so the code is not dependent on lax vector
conversions. For example, reinterpret the comparison result before narrowing:
```cpp
r32[j] = vmovn_s64(vreinterpretq_s64_u64(vcgtq_s64(vld1q_s64(vsp + 2 * j), rvVec)));
```
(Or narrow with `vmovn_u64`, which takes `uint64x2_t`, and reinterpret to
`int32x2_t`.) This keeps the generated code identical on clang while allowing
gcc to compile.
weaselbot
was assigned by andrew2026-08-17 00:23:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Defect
Building the library with
USE_64_BIT=1on aarch64/arm64 using gcc fails tocompile. The 64-bit NEON path of
conflictMask16passes auint64x2_t(thereturn type of
vcgtq_s64) tovmovn_s64, which requires anint64x2_t. clangaccepts this via an implicit vector conversion, but gcc does not, so the build
aborts.
This code was added by the fix for #69 ("ARM NEON scan helpers truncate 64-bit
versions when USE_64_BIT=1"). That issue and its fix were validated with clang
on aarch64 (see the reproduction in #69), so the gcc breakage went unnoticed.
CI never compiles this path: the
64-bit-versionsmatrix runs on amd64/clang(uses the AVX
compare16/compare16_avx512, notconflictMask16, which isguarded by
#ifdef HAS_ARM_NEON), and the aarch64 matrices all useUSE_64_BIT=0(the#else32-bit path). So the aarch64USE_64_BIT=1branchis entirely untested in CI.
Location
ConflictSet.cpp,conflictMask16, the#if USE_64_BITarm, line 2138:vcgtq_s64returnsuint64x2_t;vmovn_s64takesint64x2_t.Reproduction
Result (exit code 1):
For comparison, the same file compiles cleanly on this aarch64 host in three
nearby configurations:
USE_64_BIT=0):g++ -std=c++20 -O2 -I include -I third_party/valgrind -I . ConflictSet.cpp -shared -fPIC -o /tmp/libcs0.so -fsyntax-only-> exit 0USE_64_BIT=1:clang++ -std=c++20 -O2 -DUSE_64_BIT=1 ...-> exit 0So the failure is specific to the gcc + aarch64 +
USE_64_BIT=1combination,isolated to the
conflictMask16#if USE_64_BITbranch.Expected vs actual
USE_64_BIT=1(a flag the project's own CI uses, just on amd64)builds on aarch64 with gcc, the platform's default system compiler on many
Linux distributions (e.g. Fedora/RHEL aarch64 ship gcc as the default
compiler, and the default gcc/aarch64 build of this repo already works).
Impact
Anyone on an aarch64 system who wants 64-bit versions (e.g. to support version
histories beyond 2^32, or to avoid the 2-billion version window) and builds
with gcc — the default compiler on that platform — cannot compile the library
at all when passing
-DUSE_64_BIT=1. Because CI does not exercise theaarch64
USE_64_BIT=1path, this regression is not caught.Suggested fix
Make the vector types explicit so the code is not dependent on lax vector
conversions. For example, reinterpret the comparison result before narrowing:
(Or narrow with
vmovn_u64, which takesuint64x2_t, and reinterpret toint32x2_t.) This keeps the generated code identical on clang while allowinggcc to compile.