Skip to content

About

Do OpenCL kernels give the same bits on different CPUs? A harness, every recorded hash, and the PoCL patches to reproduce them.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

opencl-bitid

One source, one compiler, unlike CPUs, same bits.

This harness asks whether OpenCL kernels compiled by PoCL, through one LLVM, give bit-identical results on different CPUs. It records a SHA-256 of every output slice, with NaNs made canonical (OpenCL does not specify NaN bits, and x86 and RISC-V produce different ones), and compares the hashes across builds, targets and settings.

It is published so that someone can refute it. Every hash we recorded is in results/. A different hash from yours, on the same cell, is a finding.

What it finds

x86-64 native; riscv64 under qemu.

  • Integer kernels (an int8 matmul, reductions, scans) are bit-identical across 84 configurations:
    • work-group handlers, core counts and local sizes;
    • compilation targets (x86-64 baseline and znver3 native), LLVM 22 and 23, and two vector math libraries;
    • our PoCL series and upstream's merge base;
    • x86 against riscv64, and PoCL against Mesa Rusticl on an AMD iGPU.
  • Float kernels calling 36 math builtins are bit-identical between x86 and riscv64 on all 76 outputs, when PoCL's builtins are CORE-MATH's correctly rounded functions. A correctly rounded result is unique, so any two such builds must agree.
    • With PoCL's own builtins, x86 and riscv64 also agree, because it is the same code, but the results are not correctly rounded.
    • With a vector math library (glibc's libmvec), the results differ between builds.
  • Three things break float bit-identity even on one machine, unless every path is correctly rounded:
    • The work-group size. Some sizes run a call scalar (the host's libm) and others vectorized (libmvec). With glibc's libmvec, sizes 1, 2 and 13 change about a quarter of all results. With CORE-MATH as the kernel library, no size changes anything.
    • Build flags such as -cl-fast-relaxed-math. On x86 they turn a division into a reciprocal estimate, and softmax and GELU then differ between x86 and riscv64.
    • Constant folding. LLVM folds a literal's math call with the compiling machine's libm, so a constant and the same value computed at run time can differ: in 333 of 2,048 cases with glibc's libmvec, and in none with CORE-MATH as the kernel library.

What bit-identity needs

The contract this harness tests:

  • correctly rounded math functions on every path (vector, scalar and compile time);
  • a fixed order for every reduction, set by the source;
  • no floating-point contraction (#pragma OPENCL FP_CONTRACT OFF);
  • no relaxed-math build flags;
  • no flushing of subnormals;
  • correctly rounded division and square root (-cl-fp32-correctly-rounded-divide-sqrt).

Limits, stated plainly

  • RISC-V has been checked under qemu only, not on silicon.
  • Everything but the one Rusticl cell is PoCL's CPU device.
  • The cells are not plain upstream PoCL. Every PoCL cell but up-base-none is built on our vector-math patch series, and the CORE-MATH route is one more patch on top (see patches/ and CELLS.md).
  • Integer kernels that call a subgroup reduction were not tested.

Layout

path what
harness/determinism/ the kernels (int_kernels.py, float_kernels.py), the probes (remainder_probe.py, constfold_probe.py), the runners (run.sh, run-float.sh) and the comparisons (compare.py, compare_float.py)
container/ the two build images: Dockerfile (x86-64, Ubuntu 24.04, LLVM 22) and Dockerfile.rv64 (riscv64, Debian trixie, LLVM 22)
patches/ the PoCL patches, on upstream aaaa2d67b (2026-09-11)
results/ every hash we recorded, one JSON per run
CELLS.md each cell: its source, its build options, and its result files

Reproduce

  1. Build an image from container/. For riscv64, use docker build --platform linux/riscv64 and run with -e QEMU_CPU=max,vlen=256.
  2. Check out PoCL at aaaa2d67b4f3cfe7b7d55a4984d5adb168d72d1a and apply the patches for your cell: git am patches/vecmath/*.patch, then patches/coremath.patch or patches/crmvec.patch if the cell needs one.
  3. Configure and build with the cell's options from CELLS.md, in the container, with the build at /work/build and this checkout at /h.
  4. Run harness/determinism/run.sh <build-dir> <tag> (integer) or run-float.sh <tag> (float), then compare with compare.py or compare_float.py against results/.

The patches reproduce our branches exactly: applied to aaaa2d67b, they give trees identical to the commits we built from (10a12a5fc, 8ba2be4a8, 56073e544).

License

MIT for this repository's own files (LICENSE). The patches carry PoCL's license, and patches/coremath.patch adds CORE-MATH's files with their own MIT notices.

Developed with AI assistance (Claude), disclosed throughout.

About

Do OpenCL kernels give the same bits on different CPUs? A harness, every recorded hash, and the PoCL patches to reproduce them.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages