Skip to content

ci: cover i386 and ClangCL for the AVX2-dispatch guard; drop dead _M_X64 - #144

Merged
fcostaoliveira merged 12 commits into
mainfrom
ci/cover-i386-and-clangcl
Sep 30, 2026
Merged

fcostaoliveira merged 12 commits into
mainfrom
ci/cover-i386-and-clangcl

Conversation

@filipecosta90

Copy link
Copy Markdown
Contributor

Follow-up to #142 (ClangCL) and #143 (i386), which both edited the AVX2 runtime-dispatch guard in src/hdr_histogram.c but were validated manually rather than in CI.

Why

The CI matrix excludes 32-bit Linux and never selects the ClangCL toolset, so neither configuration the guard specifically handles was exercised. A future refactor of the guard could silently reintroduce either break.

What

Two regression jobs, in both of which HDR_HAS_AVX2_DISPATCH must be compiled out:

  • build-linux-i386 — 32-bit GCC (-m32, gcc-multilib), HDR_LOG_REQUIRED=DISABLED so no 32-bit zlib is needed. The core suites still run hdr_value_at_percentile (the dispatched path). Guards against the Don't attempt AVX2 on i386 #143 break (_mm_extract_epi64 is unavailable in 32-bit codegen).
  • build-windows-clangcl — the ClangCL VS toolset (-T ClangCL) on x64. Guards against the Fix ClangCL build on Windows #142 break (clang-cl defines __clang__+_MSC_VER and previously failed to link __cpu_model).

Also simplifies the guard: _M_X64 is a dead term once _MSC_VER is excluded (only the MSVC family defines _M_X64, and clang-cl uses _M_X64 rather than __x86_64__), so it now gates on __x86_64__ alone, with a comment documenting why 32-bit / MSVC-ABI / ICC are excluded.

No functional change on the platforms that ship the AVX2 path (64-bit GCC/Clang on Linux/macOS): the fast path stays enabled there.

🤖 Generated with Claude Code

The AVX2 runtime-dispatch guard in src/hdr_histogram.c is the subject of two
recently merged fixes (#142 clang-cl, #143 i386), yet CI exercised neither
configuration: the matrix excludes 32-bit Linux and never selects the ClangCL
toolset. Add two regression jobs that build exactly those configurations, in
both of which HDR_HAS_AVX2_DISPATCH must be compiled out:

  - build-linux-i386: 32-bit GCC (-m32, gcc-multilib), HDR_LOG_REQUIRED=DISABLED
    so no 32-bit zlib is needed; the core suites still run hdr_value_at_percentile.
  - build-windows-clangcl: the ClangCL VS toolset (-T ClangCL) on x64.

Also simplify the guard: _M_X64 is dead once _MSC_VER is excluded (only the
MSVC family defines _M_X64, and clang-cl uses _M_X64 rather than __x86_64__),
so gate on __x86_64__ alone and document why 32-bit / MSVC-ABI / ICC are out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@filipecosta90
filipecosta90 requested a review from mikeb01 July 23, 2026 14:41
fcostaoliveira and others added 2 commits July 23, 2026 15:41
…index type)

The ClangCL CI job added in this PR surfaced a pre-existing clang-cl build
break that PR #142 did not cover: _BitScanReverse{,64} take an
'unsigned long *' out-parameter, but the index was declared uint32_t. MSVC
cl.exe tolerates this (same width), but clang-cl treats the pointer-type
mismatch as a hard error (-Wincompatible-pointer-types is a default error in
Clang 16+). Declare the index as 'unsigned long' to match the intrinsic; on
Windows it is 32-bit, so behavior is unchanged. Fixes both the _WIN64 and
32-bit branches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second clang-cl-only break surfaced by the new ClangCL CI job: on the MSVC
atomic path, hdr_atomic_{load,store}_pointer are typed 'void**' functions, so
passing 'struct hdr_histogram**' is a hard error under clang-cl
(-Wincompatible-pointer-types, default error in Clang 16+). GCC/Clang use a
type-generic __atomic_* macro, so this never showed on Linux/macOS. Cast the
call-site arguments to (void**), matching the existing idiom in
test/hdr_atomic_test.c. void* and object pointers share representation
(C11 6.2.5p28), so behavior is unchanged on all platforms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-bit

Adversarial review of the guard comment found a factual error: clang-cl
defines __x86_64__ AND __clang__ (alongside _MSC_VER), so it is the
!defined(_MSC_VER) clause -- not the arch/compiler check -- that keeps clang-cl
out of the AVX2 dispatch. Correct the comment so a future editor does not drop
_MSC_VER and reintroduce the #142 break. Verified with
clang --target=x86_64-pc-windows-msvc -dM -E.

Also harden the i386 job: assert the built test binary is 'ELF 32-bit' so the
job fails loudly if -m32 is ever silently ignored (would otherwise be a
false-positive regression test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fcostaoliveira
fcostaoliveira force-pushed the ci/cover-i386-and-clangcl branch from 149bddd to e2d1506 Compare August 28, 2026 10:31
@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

🤖 Automated first-pass review. A human maintainer still needs to review this before merge.

Looks good overall. hdr_atomic.h is a private header under src/ and the old functions were static, so turning them into macros has no ABI impact. The unsigned long change for _BitScanReverse{,64} matches the intrinsic's signature, and dropping _M_X64 from the AVX2 guard doesn't change which platforms get the fast path. The new ClangCL job also compiles the MSVC-branch macros, which is useful.

One thing CI can't check: the __x86_64__ inline-asm fallback in hdr_atomic.h only runs on compilers that don't define __ATOMIC_SEQ_CST (roughly pre-4.7 GCC). No runner builds that branch, so the new __typeof__/statement-expression version is untested. Did you build it locally, for example by temporarily moving that branch above the __ATOMIC_SEQ_CST one? If not, it's worth a quick check before merge.

fcostaoliveira and others added 3 commits September 2, 2026 12:29
The negative assert used 'cc -m32 ... | grep -q ... && { exit 1; } || true'.
The trailing '|| true' greens the step even if 'cc -m32' itself breaks (e.g.
missing multilib): grep sees empty input, doesn't match, and the step passes
— the same false-positive class the 'ELF 32-bit' check was added to close.

Write the preprocessor macros to a file first so a broken 'cc -m32' fails the
step under 'set -e', then assert with an explicit if-guard. (A '! grep'
one-liner would not work: a pipeline preceded by '!' is exempted from 'set -e',
so a match would not fail the step.) Now fails on both a broken command and a
regressed guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@fcostaoliveira

Copy link
Copy Markdown
Contributor

Review round: 2026-09-29. Reviewed commit: 212fa77cf02006ba6ee601583c7ed2e62860d1b6.

Verdict: MERGE-READY for this portability/CI change.

The AVX2 guard still limits dispatch to 64-bit x86 GCC/Clang, excludes clang-cl/MSVC and ICC classic, and retains the scalar fallback. The MSVC bit-scan output now has the API-required unsigned long type. The interval-recorder edits supply the pointer type expected by the existing atomic wrappers; their synchronization operations are unchanged.

Fresh local arm64 release CTest 5/5, ASan+UBSan+float-cast-overflow 5/5 and logging-disabled 4/4 pass. The reviewed head's i386 and Windows ClangCL jobs, existing platform matrix, sanitizers and PR fuzz checks are green. Those platform jobs, rather than the arm64 build, support the i386/ClangCL conclusion.

This PR adds coverage; it does not claim to fix main's separate timestamp UBSan batch failure (#154), nor does it establish a performance gain. Keep the new jobs and existing sanitizer job when resolving later merges.

Review evidence.

@fcostaoliveira

Copy link
Copy Markdown
Contributor

Review round: 2026-09-29 (adversarial re-verification, no code change). Reviewed commit: 212fa77.

7-lens adversarial pass on the current head — 7/7 GREEN.

  • CI-only change: adds i386 + ClangCL legs that exercise the AVX2 runtime-dispatch guard, and drops the dead _M_X64 branch. No library source touched, so all correctness lenses (A1–A6) are N/A.
  • The added matrix legs are the right coverage for the __builtin_cpu_supports / MSVC-intrinsic dispatch path, which is exactly where portability regressions on 32-bit / clang-cl would surface.

Verdict: MERGE-READY confirmed.

Comment thread src/hdr_histogram.c Outdated
Comment on lines +194 to +195
/* _BitScanReverse* out-param is 'unsigned long*' (32-bit on Windows);
uint32_t* is a hard error under clang-cl */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should remove the comment (won't make sense after merging)

Suggested change
/* _BitScanReverse* out-param is 'unsigned long*' (32-bit on Windows);
uint32_t* is a hard error under clang-cl */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed in 48ef6b6 to a single durable line — /* _BitScanReverse writes an unsigned long */ — dropping the clang-cl/PR-context framing. Kept a one-liner rather than removing it entirely so the unsigned long (not uint32_t) choice doesn't get "cleaned up" back into a clang-cl hard error later; matches @mikeb01's terse-why style. Shout if you'd rather it go completely.

Comment thread src/hdr_interval_recorder.c Outdated

/* volatile read */
old_active = hdr_atomic_load_pointer(&r->active);
old_active = hdr_atomic_load_pointer((void**) &r->active);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about changing the atomic helpers' types instead of casting here (and line 74)?
void** isn't interchangeable with struct hdr_histogram**; the cast seems to hide the mismatch rather than resolve it

Both hdr_atomic_store_pointer and hdr_atomic_load_pointer have 2~3 callers each, so updating them should be a small change (??)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — done in 48ef6b6. You're right that the cast hides a real mismatch: void** and struct hdr_histogram** aren't compatible, and on the __atomic branch the helper is already a type-generic macro, so the cast there actually introduced a void** aliasing access that wasn't there before.

Rather than casting at the call sites, I made the two remaining branches (MSVC and the x86_64 asm fallback) type-generic macros to match the __atomic branch — so hdr_atomic_{load,store}_pointer now accept a typed T** everywhere and all three call sites in hdr_interval_recorder.c (plus the two in hdr_atomic_test.c) drop the cast. MSVC: (_ReadBarrier(), *(x)) / (_WriteBarrier(), (void)(*(f)=(v))); asm: __typeof__-based statement expressions. gcc ctest 5/5 + ASan/UBSan 5/5 (incl. the atomic concurrency test that drives these via the recorder). The clang-cl + i386 legs this PR adds will validate the MSVC-branch macros. Happy to switch to typed helper functions instead if you'd prefer that shape.

…**) casts

Addresses @paulorsousa review: rather than casting typed T** to void** at each
call site (which papers over an incompatible-pointer mismatch and, on the __atomic
branch, would introduce a void** aliasing access where none existed), make the MSVC
and x86_64-asm branches type-generic macros like the __atomic branch already is.
Callers in hdr_interval_recorder.c and the atomic test now pass &r->active with no
cast. Also trim the count_leading_zeros comment to the durable why.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@fcostaoliveira

Copy link
Copy Markdown
Contributor

Review round: 2026-09-29 (follow-up). Reviewed commit: 48ef6b6.

Correction to my earlier note: this PR is not CI-only — besides the i386/ClangCL matrix legs it also hardens the AVX2 runtime-dispatch guard in hdr_histogram.c (dropping the dead _M_X64, excluding _MSC_VER/__INTEL_COMPILER, fixing the count_leading_zeros_64 out-param type) and touched hdr_interval_recorder.c. My prior "no library source touched" line was wrong; re-reviewed the actual diff.

@paulorsousa's two points are addressed in 48ef6b6:

  • (void**) casts → made hdr_atomic_{load,store}_pointer type-generic in the MSVC and x86_64-asm branches (the __atomic branch was already a type-generic macro), so all three call sites in hdr_interval_recorder.c and the two in hdr_atomic_test.c pass a typed T** with no cast. This removes the incompatible-pointer mismatch rather than hiding it (and avoids the void** aliasing access the cast introduced on the __atomic path).
  • stale comment → trimmed to the one-line durable why.

gcc ctest 5/5 + ASan/UBSan 5/5 (incl. the atomic concurrency test that drives the recorder's pointer atomics). The MSVC-branch macros are exactly what the new clang-cl leg this PR adds will validate. Glad to switch to typed helper functions instead if that's the preferred shape.

@paulorsousa paulorsousa left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!! Thank you!! 🙏

@fcostaoliveira
fcostaoliveira merged commit 1dfc67e into main Sep 30, 2026
36 checks passed
fcostaoliveira added a commit to fcostaoliveira/HdrHistogram_c that referenced this pull request Sep 30, 2026
fcostaoliveira added a commit to fcostaoliveira/HdrHistogram_c that referenced this pull request Sep 30, 2026
fcostaoliveira added a commit to fcostaoliveira/HdrHistogram_c that referenced this pull request Sep 30, 2026
fcostaoliveira added a commit to fcostaoliveira/HdrHistogram_c that referenced this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants