You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Regression: phpc build fails module verification for every user script — 4 lowering defects, 266/266 helper units degraded (lib/JIT/Builtin/StringOffsetRuntime.php) #22638
On pristine master (e1850c9) in php-compiler:22.04-dev, ./phpc build fails module verification for any user script — including the 5-line tier-1 fixture that check-aot-build-smoke.sh (#16010) uses. ./script/ci-fast.sh is therefore red at its first gate.
The same four defects also break the split-compilation helper-object cache: 266 of 266 helper units fail to emit, every one silently degraded to the "nested-lowering fallback (#15642)". Because every JIT context builds the htmlspecialchars helper, and that helper indexes a string, one bad zext poisoned every module in the tree.
Repro
Zend 8.2
AOT on master (2026-07-23)
$w = "AOT"; echo $w, "\n", strlen($w), "\n";
AOT / 3
RuntimeException: Module verification failed
php script/emit-helper-runtime-object.php
—
0 emitted, 266 failed
Four independent defects, all reported by one Module->verify():
ZExt only operates on integer — lib/JIT/Builtin/StringOffsetRuntime.php::normalizeOffset blind-zexts its operands. A box-backed offset arrives as a %__value__ struct, so it emits zext %__value__ ... to i64. 221 of 266 unit failures.
Call parameter type does not match function signature! — lib/JIT/JitNestedHelperCoerce.php::coerceArgForHelper always returns a pointer when boxing, but isValueBoxType() also matches a by-value __value__ parameter, so call ...(%__value__* %slot) hits a by-value param.
Both operands to ICmp instruction are not of the same type! — strcmp is declared i32 in lib/JIT/LibcExtern.php, but four sites compare its result against an i64 zero (ReflectionFunctionVariadicLookupRuntime.php, ParamSensitiveLookupRuntime.php ×3).
Why it went unobserved
Three layers of false green stacked up:
script/emit-helper-runtime-object.php sent every child's stderr to /dev/null, so 266 failures produced no diagnostics.
script/check-helper-runtime-prelink.php iterates glob(units/*/manifest.json) — with zero manifests the glob is empty and it reports clean, exit 0. It detects stale units, never zero units.
Category
Regression:· AOT/JIT lowering · release blockerProblem
On pristine
master(e1850c9) inphp-compiler:22.04-dev,./phpc buildfails module verification for any user script — including the 5-line tier-1 fixture thatcheck-aot-build-smoke.sh(#16010) uses../script/ci-fast.shis therefore red at its first gate.The same four defects also break the split-compilation helper-object cache: 266 of 266 helper units fail to emit, every one silently degraded to the "nested-lowering fallback (#15642)". Because every JIT context builds the
htmlspecialcharshelper, and that helper indexes a string, one badzextpoisoned every module in the tree.$w = "AOT"; echo $w, "\n", strlen($w), "\n";AOT/3RuntimeException: Module verification failedphp script/emit-helper-runtime-object.php0 emitted, 266 failedFour independent defects, all reported by one
Module->verify():ZExt only operates on integer—lib/JIT/Builtin/StringOffsetRuntime.php::normalizeOffsetblind-zexts its operands. A box-backed offset arrives as a%__value__struct, so it emitszext %__value__ ... to i64. 221 of 266 unit failures.Call parameter type does not match function signature!—lib/JIT/JitNestedHelperCoerce.php::coerceArgForHelperalways returns a pointer when boxing, butisValueBoxType()also matches a by-value__value__parameter, socall ...(%__value__* %slot)hits a by-value param.Incorrect number of arguments passed to called function!—JIT::instanceMethodUsesThis()falls back tocontext->scope->className(Epic: architecture rearrangement — TU-based pipeline, differential gates, generated maintenance (docs/architecture-review-2026-07.md) #16075). For a file whose class is compiled before its top level,{main}inherits the leftover class name and is given an%__object__*parameter; standalone main then emitscall void @internal_N()with no arguments.Both operands to ICmp instruction are not of the same type!—strcmpis declaredi32inlib/JIT/LibcExtern.php, but four sites compare its result against ani64zero (ReflectionFunctionVariadicLookupRuntime.php,ParamSensitiveLookupRuntime.php×3).Why it went unobserved
Three layers of false green stacked up:
script/emit-helper-runtime-object.phpsent every child's stderr to/dev/null, so 266 failures produced no diagnostics.script/check-helper-runtime-prelink.phpiteratesglob(units/*/manifest.json)— with zero manifests the glob is empty and it reports clean, exit 0. It detects stale units, never zero units.Repro
Fast single-unit repro (~10 s), with the operand asserts that locate each defect in one shot:
./script/docker-exec.sh -- bash -lc 'PHP_COMPILER_LLVM_ASSERT=1 php script/emit-helper-runtime-object.php --unit=/lib/VM/CompareJitHelperScalars.php'Done when
./phpc build+ execute green on the tier-1 fixture; output matches VM./script/ci-fast.shgreen at the AOT build smoke gatephp script/emit-helper-runtime-object.phpemits the large majority of units instead of 0PHP_COMPILER_LLVM_ASSERT=1also validatesiCmpoperand types