Regression: fix four AOT module-verification defects — phpc build works again (#22638)#22640
Regression: fix four AOT module-verification defects — phpc build works again (#22638)#22640PurHur wants to merge 2 commits into
Conversation
|
Follow-up blocker filed as #22642 (gen-0 seed cannot be rebuilt). The two gates listed under "Not fixed here" are tracked there. |
`./phpc build` failed module verification for every user script on master, including the 5-line tier-1 fixture in check-aot-build-smoke.sh (#16010), so ci-fast was red at its first gate. The same four defects broke the split helper-object cache: 266/266 units failed to emit, each silently degraded to the nested-lowering fallback (#15642). Every JIT context builds the htmlspecialchars helper, and that helper indexes a string, so the string-offset defect alone poisoned every module in the tree. - StringOffsetRuntime::normalizeOffset blind-zext'd its operands, emitting `zext %__value__ ... to i64` for a box-backed offset. Widen width-aware and read the long out of a box; dimFetch now coerces through JitLongArg, which applies the int/bool/null/float/numeric-string offset rules. - JitNestedHelperCoerce::coerceArgForHelper always returned a pointer when boxing, but isValueBoxType() also matches a by-value `__value__` parameter, so the call passed `%__value__*` to a by-value param. - JIT::instanceMethodUsesThis fell back to scope->className (#16075), giving `{main}` an `%__object__*` parameter whenever a class was compiled first; standalone main then emitted `call void @internal_N()` with no arguments. - strcmp is declared i32 in LibcExtern, but four sites compared its result against an i64 zero. Also extends the PHP_COMPILER_LLVM_ASSERT hook to validate iCmp operand types. That assert located the strcmp defect from a backtrace in one shot, the same way the existing zExt assert located the string-offset one. Verified in php-compiler:22.04-dev (LLVM 9): - ./phpc build + execute green on the tier-1 fixture, output matches VM - ci-fast AOT build smoke: "OK tier 1 (build + execute + VM parity)" - helper-unit emit: 219/266 units, was 1/266 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The helper-unit sweep ran one `--unit=` child at a time with its stderr sent to /dev/null, so a full sweep cost ~45 min and 266 failures produced no diagnostics at all. Three layers of false green stacked up: no stderr, a prelink check that iterates glob(units/*/manifest.json) and so reports clean when *zero* units emit, and emission failure tolerated as the nested-lowering fallback (#15642). - Fan the child lowerings out over a proc_open worker pool. Each unit already runs in its own child writing only to its own cache directory, so there is no shared mutable state. Default nproc-2 mirrors bin/lint.php's PHP_COMPILER_LINT_JOBS; PHP_COMPILER_EMIT_JOBS=1 restores the serial sweep. - Send child stderr to <unit dir>/emit.log and surface the first error line in the sweep output, so a failing unit names its cause. Measured on a 16-core box, 266 units: 4m27s wall / 55m28s CPU — ~12.5x. The stderr capture is what turned an opaque "266 failed" into a ranked defect list, which is how #22638 was found. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d30dee5 to
fb310c3
Compare
Honest scope note — this PR makes the compiler build, it does not make it correctFollow-up verification after opening this PR, on a class + method + string-offset + class Greeter { /* ... */ public function initial(): string { return $this->who[0]; } }
$g = new Greeter("AOT");
echo $g->greet(), "\n", $g->initial(), "\n", htmlspecialchars("Tom & Jerry"), "\n";
So master cannot build it; this PR builds it and then produces silently wrong output for two of the three lines. That is a real improvement in the gate, and it is not a correct compiler. Both remaining defects are pre-existing, not introduced here:
A/B against a master baseline (master + this PR's emit-parallel commit only, so the sweep is comparable)Failing data-set identities Not verifiedA full |
Cleanup hygieneDeleting remote branch Reason: PR closed unmerged after landing via #22643 + follow-up #22647. Issue #22638 closed; branch tip is obsolete vs master's icmp-assert / emit-jobs evolution. Open PR #22717 ( |
Closes #22638.
Problem
On pristine
master(e1850c9) inphp-compiler:22.04-dev,./phpc buildfailed module verification for any user script — including the 5-line tier-1 fixture thatcheck-aot-build-smoke.sh(#16010) uses — so./script/ci-fast.shwas red at its first gate. The same four defects broke the split helper-object cache: 266 of 266 helper units failed to emit, each silently degraded to the "nested-lowering fallback (#15642)".One
Module->verify()reported all four at once:Because every JIT context builds the
htmlspecialcharshelper, and that helper indexes a string, the string-offset defect alone poisoned every module in the tree.Root cause and fix
zext %__value__ ... to i64StringOffsetRuntime::normalizeOffsetblind-zexts its operandsdimFetchcoerces throughJitLongArg, which applies the int/bool/null/float/numeric-string offset rulesJitNestedHelperCoerce::coerceArgForHelperalways returns a pointer when boxing, butisValueBoxType()also matches a by-value__value__call void @internal_N()arityJIT::instanceMethodUsesThisfalls back toscope->className(#16075), so{main}inherits a leftover class name and gets an%__object__*param$thisicmp i32 vs i64strcmpis declaredi32inLibcExtern, four sites compare against ani64zeroWhy it went unobserved
Three layers of false green stacked up, all addressed here:
emit-helper-runtime-object.phpsent every child's stderr to/dev/null— 266 failures, zero diagnostics. Now captured per unit and the first error line surfaced.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. Not fixed here — left in Regression: phpc build fails module verification for every user script — 4 lowering defects, 266/266 helper units degraded (lib/JIT/Builtin/StringOffsetRuntime.php) #22638's Done-when list.Extending
PHP_COMPILER_LLVM_ASSERTto validateiCmpoperand types is what located defect 4 from a backtrace in one shot — the same way the existingzExtassert located defect 1. That assert is the highest-leverage part of this PR.Verification (php-compiler:22.04-dev, LLVM 9)
./phpc build+ execute, tier-1 fixtureRuntimeException: Module verification failedHello AOT!/3— matches VMFAILED — phpc build exited non-zero (tier 1)OK tier 1 (build + execute + VM parity)php script/emit-helper-runtime-object.phpThe 47 remaining unit failures are a different, pre-existing class (
Call to undefined method HashTable::iteratekeyed()and friends) and are not touched here.Not fixed here — still blocking a release
Two gates remain red on
master, both for the same structural reason and neither caused by this PR:north-star5-verify --faststep 4f-m — committed gen-0lowering_source_fingerprintis stale.ci-fastcheck-selfhost-spine-sidecar-sync— committed.m3_compiler_lib_aot_blobstamp is stale (the spine entry file is untouched by this PR).Both are repaired by
make bootstrap-gen0-refresh-sidecar, which cannot complete: the Zend gen-0 compile ofcompile_driver.phpexhausts 6 GiB, 24 GiB and 48 GiB PHP memory caps in turn, growing monotonically with no completion (~90 min to 48 GiB on a 62 GB box).prelinked/bootstrap-gen0/bin-compile-aotlast actually changed on 2026-06-15; the 44 "Trust: restamp gen-0 fingerprint" commits since then re-assert freshness without rebuilding the blob. Filed separately.Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
🤖 Generated with Claude Code