Skip to content

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:

  1. 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.
  2. 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:

  1. Install-deps step (new): gcc-multilib g++-multilib on Ubuntu, added via a separate matrix.i686-guarded step. Existing Install dependencies (ubuntu) step is narrowed with && !matrix.i686.
  2. Cross-file: build-aux/i686-linux-gnu.ini (new). Uses gcc/g++ with c_args = ['-m32'] in [built-in options]; sets host_machine = { system = 'linux', cpu_family = 'x86', cpu = 'i686' }. No exe_wrapper — the host can run i686 binaries natively, but meson doesn't know that and marks cross-built tests as SKIP 77. That is acceptable here — the gate is build-only.
  3. Skip Run tests + Run tox tests (ubuntu) on the i686 leg. Both steps are already guarded by compound if: expressions; extend with && !matrix.i686.
  4. Keep the artifact upload step active (i686 leg is not --default-library static so 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=false flag 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++-multilib install 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_epi64 use sites on __x86_64__ so that the ASM path compiles on i686 too, then drop -Denable_asm=false from 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.md T4-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.