ADR-0151: i686 build-only CI job — reproduce Netflix #1481¶
- Status: Accepted
- Date: 2026-04-24
- Deciders: Lusoris, Claude (Anthropic)
- Tags: ci, build, x86, netflix-upstream
Context¶
Netflix upstream issue #1481 reports that libvmaf fails to compile on 32-bit x86 GNU/Linux with default build settings. The root cause is _mm256_extract_epi64 — an AVX2 intrinsic whose return type is int64_t — which gcc emits only on __x86_64__ targets. The intrinsic is used 24× in core/src/feature/x86/adm_avx2.c + several times in motion_avx2.c + adm_avx512.c. Building on 32-bit x86 with -mavx2 fails with implicit declaration of function '_mm256_extract_epi64'.
The upstream issue documents two viable workarounds:
meson setup build -Denable_asm=false— disables the entire ASM/SIMD path. Clean build at the cost of the AVX2 + AVX-512 speedups on i686 hosts.- Downgrade to the last commit before Netflix PR #1452 (upstream
7e16db0a, "ssim: add scale parameter"). Untenable for the fork.
The fork has no 32-bit CI dimension today. Every other platform axis (x86_64 Ubuntu gcc/clang, macOS clang, ARM clang, Windows MSVC + MinGW + CUDA/SYCL) is matrix-covered; only i686 is not. This means any future code change that inadvertently breaks the i686 -Denable_asm=false build path would ship undetected until a downstream user reports it.
Backlog entry T4-8 (Tier 4 upstream-port queue) flagged the gap: "i686 CI job (reproduce Netflix#1481). Fork has no 32-bit job today."
Decision¶
Add a single new matrix row to .github/workflows/libvmaf-build-matrix.yml:
- os: ubuntu-latest
CC: ccache gcc
CXX: ccache g++
i686: true
meson_extra: --cross-file=build-aux/i686-linux-gnu.ini -Denable_asm=false
name: Build — Ubuntu i686 gcc (CPU, no-asm)
Scope:
- Install-deps step (new):
gcc-multilib g++-multilibon Ubuntu, added via a separatematrix.i686-guarded step. ExistingInstall dependencies (ubuntu)step is narrowed with&& !matrix.i686. - Cross-file:
build-aux/i686-linux-gnu.ini(new). Usesgcc/g++withc_args = ['-m32']in[built-in options]; setshost_machine = { system = 'linux', cpu_family = 'x86', cpu = 'i686' }. Noexe_wrapper— the host can run i686 binaries natively, but meson doesn't know that and marks cross-built tests asSKIP 77. That is acceptable here — the gate is build-only. - Skip
Run tests+Run tox tests (ubuntu)on the i686 leg. Both steps are already guarded by compoundif:expressions; extend with&& !matrix.i686. - Keep the artifact upload step active (i686 leg is not
--default-library staticso it passes the existing filter).
The Netflix#1481 documented workaround -Denable_asm=false is pinned into the matrix row's meson_extra so the job runs the exact command the upstream issue recommends. If a future fork patch re-enables ASM on i686 (by gating _mm256_extract_epi64 call sites on __x86_64__ or by emulating the intrinsic with two _mm256_extract_epi32 calls), the matrix row can be updated to drop -Denable_asm=false and the job becomes a true "i686 with ASM" gate — that change is explicitly out of scope for this ADR.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Fix the _mm256_extract_epi64 issue in source + run i686 with ASM enabled | True 32-bit parity; doesn't require -Denable_asm=false | Requires gating every _mm256_extract_epi64 call on __x86_64__ (24 sites in adm_avx2.c alone) and emulating on i686 via two _mm256_extract_epi32 halves; real risk of introducing bit-exactness divergence that needs cross-backend-diff validation on i686; far larger scope than "add CI job" | Rejected for this PR — separable follow-up; tracked as future T4-8-followup if there's user demand |
Run i686 via qemu-i386 + exe_wrapper | Tests would actually run in the CI job | 5-10× slower than native; i686 can run natively on x86_64 anyway | Rejected — no perf benefit; wrapper pattern is for true cross (e.g. aarch64) |
| 32-bit Docker container | Full 32-bit userspace; tests could run | GitHub Actions Docker services add 1-2 min startup per job; gcc-multilib is the standard lightweight path | Rejected — gcc-multilib is simpler and matches what upstream's bug report uses |
| Skip T4-8 entirely, mark upstream#1481 as external | Zero code change | Leaves the fork vulnerable to future 32-bit regressions going undetected; widens the gap vs. Netflix upstream's implicit x86_64 assumption | Rejected — the CI job is small and exactly what T4-8 scoped |
Consequences¶
- Positive:
- Fork gains a 32-bit Linux build dimension. Any future PR that breaks i686-with-no-asm builds trips the matrix.
- Repro of Netflix#1481 is now self-contained: the cross-file e
-Denable_asm=falseflag reproduce the exact environment the upstream bug describes. - Zero code changes to source tree — this is purely CI + build infrastructure.
- Negative:
- The job pins
-Denable_asm=false, so any i686-specific regression in the ASM path is still invisible to CI. Only the C-path regressions are caught. gcc-multilib g++-multilibinstall adds ~15 seconds to job startup on cold runners. Negligible in the matrix total time.- Neutral / follow-ups:
- Future work (outside this ADR): gate the
_mm256_extract_epi64use sites on__x86_64__so that the ASM path compiles on i686 too, then drop-Denable_asm=falsefrom the matrix row. Needs bit-exactness verification. Not a CI task. - If upstream Netflix eventually fixes the issue (by either code change or by marking i686 unsupported in meson), sync the fork accordingly on
/sync-upstream.
Verification¶
Local reproduction on a Ubuntu host with gcc-multilib installed:
meson setup libvmaf core/build-i686 \
--cross-file=build-aux/i686-linux-gnu.ini \
-Denable_asm=false -Denable_cuda=false -Denable_sycl=false
ninja -C core/build-i686
file core/build-i686/tools/vmaf
# => ELF 32-bit LSB pie executable, Intel i386 ...
Build completes; produces a 32-bit i386 binary. meson test -C core/build-i686 reports 30 SKIP, 1 OK — tests skip (exit 77) because meson marks cross-compiled tests as not-runnable without an exe_wrapper. The single OK is the registry static check that doesn't actually invoke a binary. Accepted as the documented build-only gate.
References¶
- Upstream issue: Netflix/vmaf#1481.
- Netflix upstream PR that introduced the offending code: #1452 (merged).
- Backlog:
.workingdir2/BACKLOG.mdT4-8. - User direction 2026-04-24 popup: "T4-8 i686 CI job (Recommended)".
- ADR-0115 — precedent for folding new CPU-arch CI jobs into the unified
libvmaf-build-matrix.yml.