benchmarks/mandelbrot.php compiled with phpc build prints every cell as _. Zend and the VM both render the set correctly, so this is an AOT-only lowering defect — silent wrong output, exit 0, no diagnostic.
AOT : _______________________________________________________________________________
Zend: ################################_______________________________________#########
VM : ################################__…
Same source, same machine, pinned container. The binary builds cleanly (rc=0, helper-runtime cache hit) and runs to completion — it just decides every point escapes.
That the VM agrees with Zend narrows this to the native path: the escape test ($zr*$zr + $zi*$zi > LIMIT style float compare, or the iteration-count branch) is lowered wrongly. Prime suspects are a float comparison predicate (ordered vs unordered), an i1/i8 truncation on the loop-exit flag, or the accumulator being read before the update lands.
Reproduce:
phpc build -o /tmp/m benchmarks/mandelbrot.php && /tmp/m | head -3
php benchmarks/mandelbrot.php | head -3
Found while refreshing benchmarks/README.md: this is one of the compute benchmarks whose numbers the project quotes for AOT speedup, so the published "native run" column has been measured on a binary that produces wrong output. A fast wrong binary is not a benchmark.
Done when: the compiled binary's output is byte-identical to Zend's, and script/differential-sweep.sh --aot covers a mandelbrot-shaped float-loop case so this cannot regress silently.
benchmarks/mandelbrot.phpcompiled withphpc buildprints every cell as_. Zend and the VM both render the set correctly, so this is an AOT-only lowering defect — silent wrong output, exit 0, no diagnostic.Same source, same machine, pinned container. The binary builds cleanly (
rc=0, helper-runtime cache hit) and runs to completion — it just decides every point escapes.That the VM agrees with Zend narrows this to the native path: the escape test (
$zr*$zr + $zi*$zi > LIMITstyle float compare, or the iteration-count branch) is lowered wrongly. Prime suspects are a float comparison predicate (ordered vs unordered), ani1/i8truncation on the loop-exit flag, or the accumulator being read before the update lands.Reproduce:
Found while refreshing benchmarks/README.md: this is one of the compute benchmarks whose numbers the project quotes for AOT speedup, so the published "native run" column has been measured on a binary that produces wrong output. A fast wrong binary is not a benchmark.
Done when: the compiled binary's output is byte-identical to Zend's, and
script/differential-sweep.sh --aotcovers a mandelbrot-shaped float-loop case so this cannot regress silently.