Skip to content

Fix concat into slot-less destination (#21886) + self-host bundle compile OOM + measured iteration costs - #21908

Merged
PurHur merged 3 commits into
masterfrom
docs/state-sync-20260719
Jul 21, 2026
Merged

PurHur merged 3 commits into
masterfrom
docs/state-sync-20260719

Conversation

@PurHur

@PurHur PurHur commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Three independent changes, each verified.

1. #21886 — concat into a slot-less destination (release blocker)

test/selfhost/compiler_helloworld_smoke/main.php could not compile at all:

Module verification failed due to Store operand must be a pointer.
  store %__string__* %293, i64 258

The native concat path promotes a KIND_VALUE destination to a real alloca before storing — but only when $result->type === TYPE_STRING. A destination whose inferred type never settled on string skipped the promotion and hit the raw store, writing a __string__* into an immediate (the i64 constant is the value; there is no slot). The lowering already called helper->loadValue() on both inputs; nothing checked the output had anywhere to go.

Now promotes when the destination is KIND_VALUE, when its LLVM type is not __string__*, or when ->value is null — covering the mis-inferred case rather than only the known-good shape.

Verified

  • bundle now compiles and links: exit 0, 0 store errors, 841712-byte binary (previously hard-failed)
  • bin/compile.php still compiles clean (exit 0) — general path not regressed
  • phpunit --filter Concat and --filter "StringConcat|EchoConcat|FloatConcat|EnumConcat" are byte-identical to master baseline (83/148/3E/2F/1S and 6/7/3E) — I stashed the fix and re-ran to confirm those failures pre-exist

Explicitly not fixed here: the linked binary still aborts at runtime with SuperglobalRefreshJitHelper::buildGetTable(): must return HashTable, int returned. That reproduces in PHPUnit on clean master, is unrelated to concat, and is filed as #21907. This commit unblocks the compile only.

2. Self-host bundle compiles OOM'd at 1536M

bin/compile.php raises memory_limit to 6G for test/selfhost/ bundles, but the bump sat inside the isset($options['-l']) branch — lint only. Compiling the same bundle with -o ran at 1536M and died as:

Allowed memory size of 1610612736 bytes exhausted in vendor/.../PHPCfg/Traverser.php:117

with nothing pointing at the cap. Hoisted so both paths get it. Verified on the exact command that previously OOM'd (0 Allowed memory size lines).

3. Measured AOT iteration costs documented

bootstrap-dev-workflow.md's only timing figure implied lowering work iterates in minutes. Measured: a three-line echo $s script takes ~43 min to AOT-compile, because the path emits and links the whole runtime regardless of source size — so "shrink the repro" buys nothing. Records the table and what actually helps: the helper-runtime cache (and why #21855's guard correctly makes lowering edits cold anyway), parallelising hypotheses, and one PHP_COMPILER_DUMP_IR=1 dump instead of repeated compiles — which is how #21886 was located in a single run.

Related

#21886 · #21907 · #21855 · #21860 · #16508

🤖 Generated with Claude Code

anmichels and others added 3 commits July 21, 2026 18:49
…ust lints (#16508)

bin/compile.php raises memory_limit to 6G for test/selfhost/ bundles because the
1536M default OOMs — but the bump sat INSIDE the `isset($options['-l'])` branch,
so it only applied to lint. Compiling the same bundle:

  php bin/compile.php -o OUT test/selfhost/compiler_helloworld_smoke/main.php

still ran at 1536M and died as:

  PHP Fatal error: Allowed memory size of 1610612736 bytes exhausted
    in vendor/ircmaxell/php-cfg/lib/PHPCfg/Traverser.php:117

with nothing indicating the cap was the cause — the traceback points at vendor
parser internals, not at a memory setting. Hoisted the bump so lint and compile
paths both get it.

Verified: the exact command above, with no explicit PHP_COMPILER_MEMORY_LIMIT,
previously OOMd and now runs past it (0 'Allowed memory size' lines).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The doc's only timing figure was 'north-star5-verify-fast ~1-2 min', which
implies lowering work iterates in minutes. Measured, it does not: a THREE-LINE
'echo $s' script takes ~43 min to AOT-compile, because the path emits and links
the whole runtime regardless of source size. Anyone planning a 'shrink the repro'
inner loop will lose hours discovering this.

Records the measured table and the three things that actually help: the
helper-runtime cache (and why #21855's guard correctly makes lowering edits cold
anyway), parallelising hypotheses instead of chasing a faster cycle, and using a
single PHP_COMPILER_DUMP_IR=1 dump instead of repeated compiles — which is how
#21886 was located in one run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…inter' (#21886)

Compiling test/selfhost/compiler_helloworld_smoke/main.php failed module
verification:

  Module verification failed due to Store operand must be a pointer.
    store %__string__* %293, i64 258

The native concat path in compileBlockInternal promotes a KIND_VALUE destination
to a real alloca slot before storing — but only when $result->type is
TYPE_STRING. A destination whose inferred type never settled on string skipped
the promotion and reached the raw store, writing a __string__* into an immediate
(the i64 constant IS the value, there is no slot). The lowering already called
helper->loadValue() on both INPUTS; nothing checked the OUTPUT had somewhere to
go.

Promote when the destination is KIND_VALUE, or when its LLVM type is not
__string__*, or when ->value is null — covering the mis-inferred case rather than
only the one known-good shape.

Located by dumping IR once (PHP_COMPILER_DUMP_IR=1) and reading the single
malformed store plus its enclosing function, rather than by repeated compiles —
each attempt on this path costs ~26 min.

Verified:
- test/selfhost/compiler_helloworld_smoke/main.php now compiles and links
  (exit 0, 0 store errors, 841712-byte binary; previously hard-failed)
- bin/compile.php still compiles clean (exit 0) — general path not regressed
- phpunit --filter Concat and StringConcat|EchoConcat|FloatConcat|EnumConcat are
  BYTE-IDENTICAL to master baseline (83/148/3E/2F/1S and 6/7/3E), i.e. this
  change introduces no regressions; those failures pre-exist.

NOT fixed by this: the linked binary still aborts at runtime with
  TypeError: SuperglobalRefreshJitHelper::buildGetTable(): must return HashTable,
  int returned
which reproduces in phpunit on clean master and is unrelated to concat. Filed
separately — this commit only unblocks the compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants