Repository navigation
fix(cpu): name xmm0 in the AVX-512 warm-up's clobber list so a clang LTO build keeps its caller's value - #1886
Merged
Merged
Conversation
| vmaf_x86_avx512_warm_up(); | ||
| const double after = scaled + offset; | ||
|
|
||
| mu_assert("the AVX-512 warm-up changed a value its caller holds in xmm0", after == expected); |
15 of 26 tasks
lusoris
force-pushed
the
fix/cpu-avx512-warmup-clobber
branch
2 times, most recently
from
October 2, 2026 22:51
6957376 to
7995621
Compare
…LTO build keeps its caller's value (#1886) * fix(cpu): name xmm0 in the AVX-512 warm-up's clobber list so a clang LTO build keeps its caller's value On a host with AVX-512, vmaf_init_cpu() runs one 512-bit instruction as inline assembly so the first frame does not wait for the 512-bit units. The statement declared "zmm0" clobbered. clang drops the clobber of a register the enclosing function's target does not have, and nothing that contains the statement is compiled for AVX-512. Out of line that is harmless (xmm0 is caller-saved); the default build is link-time optimised, and clang inlined vmaf_init() and vmaf_init_cpu() into test_ciede_device_math, kept -20 * log10(x) in xmm0 across the statement and added 45 to the zero it left. That is the hosted `Ubuntu clang` failure (cpu=35.418753523280415 replay=45), seen only on runners with AVX-512. GCC maps zmm0 to the same hard register as xmm0 and was never affected. The statement moves to x86/avx512_warm_up.h as vmaf_x86_avx512_warm_up() with the list "xmm0", "zmm0". test_cpu holds a product in xmm0 across it (fails with the old list under clang 22.1.8, with and without LTO); test_inline_asm_clobber_contract.py requires xmmN next to every ymmN / zmmN clobber under core/src. Measured with the hosted compiler (clang 22.1.8, -O3 -flto, glibc 2.43, dev container) on a Ryzen 9 9950X3D: all suites 256 passed, 0 failed (before: test_ciede_device_math failed). The vmaf tool of that build returns the same 2256 per-frame values before and after. GCC 16.2.1 fast suite: 247 passed.
lusoris
force-pushed
the
fix/cpu-avx512-warmup-clobber
branch
from
October 2, 2026 23:02
7995621 to
6bb4cc7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
vmaf_init_cpu()zeroed a value its caller held inxmm0when a clang build with link-time optimisation ran on a host with AVX-512. This PR namesxmm0in the clobber list of the AVX-512 warm-up, which fixes the hostedUbuntu clangfailure oftest_ciede_device_math(item 4 ofT-CI-MASTER-FIRST-FULL-RUN-2026-10-02).Opens and closes
T-CPU-AVX512-WARMUP-CLOBBER-2026-10-02.What was wrong
On a host with AVX-512,
vmaf_init_cpu()runs one 512-bit instruction as inline assembly, so the first frame does not wait for the 512-bit units to power up. The statement declared"zmm0"clobbered. clang drops the clobber of a register the enclosing function's target does not have, and nothing that contains the statement is compiled for AVX-512. GCC mapszmm0andxmm0to one hard register and is not affected.Out of line the statement is harmless, because
xmm0is caller-saved. The default build is link-time optimised (b_lto=true). clang inlinedvmaf_init()andvmaf_init_cpu()into the test'scheck_case(), kept-20 * log10(x)inxmm0across the statement and added 45 to the zero it left. Assembly of the linked test:The hosted line is
96x64 8-bit fmt 1: cpu=35.418753523280415 replay=45 delta=9.581e+00. It appears on runners with AVX-512 and not on the others, which is why the same job passed ona27ebd114.ciedeand the test are correct;T-CIEDE-CLANG-POWF-BUILTIN-2026-10-02(ADR-1467) was a different difference of 2e-11.A short program shows the compiler behaviour alone (
log10(x) * -20, the statement,+ 45):-mavx512f"zmm0""xmm0""xmm0", "zmm0"What changes
core/src/x86/avx512_warm_up.hasvmaf_x86_avx512_warm_up(), with the clobber list"xmm0", "zmm0".vmaf_init_cpu()calls it.test_cpugainstest_avx512_warm_up_keeps_xmm0: a product held inxmm0across the statement. It returns early on a host without AVX-512.test_inline_asm_clobber_contract.py(device-free) requiresxmmNnext to everyymmN/zmmNclobber of an inline-assembly statement undercore/src, with four planted regressions.Reach
Any clang build with link-time optimisation that inlines
vmaf_init_cpu()next to a live value inxmm0, on an AVX-512 host. Thevmaftool of the clang 22.1.8 LTO build returns the same 2256 per-frame values and pooled scores before and after the fix (Netflix 576x324 pair, 15 extractors, two models,--precision max), so no score of that build was wrong. Another compiler version or another caller can inline differently. GCC builds were never affected.Verification
Ryzen 9 9950X3D (AVX-512). clang 22.1.8 is the hosted job's build from apt.llvm.org (
++20260714...ca7933e47d3a), installed in a throwaway container of the dev image (glibc 2.43); the hosted runner is Ubuntu 24.04.10d6a0505)-O3 -flto(the hostedUbuntu clangconfiguration), all suitestest_ciede_device_mathfails with the hosted line-O3 -flto, hosttest_ciede_device_mathandtest_cpupasstest_cpucase with the old list, clang 22.1.8cpulane in the dev container: exit 0 (330 translation units, 70 warnings, the baseline). The new header has no finding.scripts/dev/preflight.sh --stage msvcism: pass. Under MSVC the function is empty, as the statement was skipped there before.Type
feat— new featurefix— bug fixperf— performance improvementrefactor— no behavior changedocs— documentation onlytest— test-onlybuild/ci— tooling / infraport— cherry-pick from upstream Netflix/vmafsycl/cuda/simd— backend-specificChecklist
make format && make lintis green locally. Not run as one target:clang-formatis clean on the touched files and thecpuclang-tidy lane was measured in the dev container (exit 0).python3 scripts/ci/run_meson_test.py -- -C build./cross-backend-diffand the worst ULP is ≤ 2. Not applicable: no SIMD or GPU path..c/.cpp/.cu/.h/.hpp, it has the appropriate license header (seeCONTRIBUTING.md).!orBREAKING CHANGE:and the migration path is documented below. Not a breaking change.docs/adr/_index_fragments/<NNNN-slug>.md. No ADR.Bug-status hygiene (ADR-0165)
docs/state.mdupdated in this PR with a row in the appropriate section (Open / Recently closed / Confirmed not-affected / Deferred), ORno state delta: REASON.T-CPU-AVX512-WARMUP-CLOBBER-2026-10-02under "Recently closed"; item 4 ofT-CI-MASTER-FIRST-FULL-RUN-2026-10-02updated (six items fixed, one open).Netflix golden-data gate (ADR-0024)
assertAlmostEqual(...)score in the Netflix golden Python tests.Cross-backend numerical results
Deep-dive deliverables (ADR-0108)
docs/state.mdrow.AGENTS.mdinvariant note —core/src/AGENTS.d/inline-asm-clobbers.md.changelog.d/fixed/cpu-avx512-warmup-clobber.md.docs/rebase-notes.md.Reproducer
Known follow-ups
no ffmpeg-patches update needed: no public surface changes.
The
Ubuntu clangjobs also fail their tox step on a Cython compile error (adm_dwt2_cy.c, incompatible pointer types under clang 22). That is a separate failure and not part of this PR.Breaking changes / migration
None.