Skip to content

bootstrap-aot-link: VALUE-box concat + ordered float compare lowering#8563

Closed
PurHur wants to merge 10 commits into
masterfrom
agent/issue-8555-bootstrap-aot-link-lane-c
Closed

bootstrap-aot-link: VALUE-box concat + ordered float compare lowering#8563
PurHur wants to merge 10 commits into
masterfrom
agent/issue-8555-bootstrap-aot-link-lane-c

Conversation

@PurHur

@PurHur PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

Continues #8555 (bootstrap-aot-link): fixes AOT segfault on ?string ternary returns when the non-null value is assigned on the if-entry CFG arm.

This commit (cbf095b61):

  • Detect if-entry non-null ?: return merges (jumpIfTargetsReturnMerge)
  • For pure string-typed assign sources: emit RETURN from the assign operand on the if arm (skip phi VALUE-box read that was use-after-free under merge-block dead operands)
  • For nullable/union if-arms: emit RETURN from each arm tail via shared phi slot (else-string merge path unchanged)

Verification

./script/docker-exec.sh -- bash -lc 'php vendor/bin/phpunit --filter TernaryReturnMergeSlotTest'
# OK (2 tests)

./script/docker-exec.sh -- bash -lc 'PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/t test/repro/ns_nullable_ternary_return.php && /tmp/t'
# /compiler/test/repro/ns_nullable_ternary_return.php (exit 0; was segfault 139)

./script/docker-exec.sh -- bash -lc 'PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/b test/repro/aot_ternary_bool.php && /tmp/b'
# prints path (exit 0)

./script/docker-exec.sh -- bash -lc 'PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/g test/repro/getenv_identical_false.php && /tmp/g'
# yes (exit 0)

./script/docker-exec.sh -- bash -lc 'PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/vbs test/bootstrap-aot/value_bitwise_shift.php && /tmp/vbs'
# 65556

./script/docker-exec.sh -- bash -lc 'make bootstrap-aot-link 2>&1 | grep -c test/bootstrap-aot'
# 16 failures (unchanged vs branch base; gate not green)

Blockers (next)

  • test/repro/aot_ternary_ne_null.php?string param on if-entry still segfaults (phi return)
  • test/bootstrap-aot/ns_func.php — nested ternary / concat candidate path
  • Full make bootstrap-aot-link still red

Closes #8555

PurHur and others added 4 commits June 14, 2026 13:59
Route TYPE_CONCAT into __value__ slots instead of native string concat,
and lower boxed __value__ vs native double relational compares so deferred
stdlib smoke and string concat AOT targets compile again (21→13 gate failures).

Co-authored-by: Cursor <cursoragent@cursor.com>
Load script-global VALUE arrays via valuePtrFromVariable before
readHashtable (foreach_by_ref), guard string dimAssign to real char
lvalues, and rebind && short-circuit false-branch phi slots that were
mis-typed from string offset fetches (ns_func compile).

Co-authored-by: Cursor <cursoragent@cursor.com>
When logical-and if conditions compare a string offset (e.g. $path[1] === ':'),
the CFG reuses the dim-fetch operand for the bool === result; box the bool
into __value__ instead of throwing assign type mismatch (ns_func AOT compile).

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove broken Helper fast paths for VALUE===bool (used wrong pointers).
Branch identicalToNative bool/long checks so LLVM select does not
eagerly call __value__readLong on non-matching tags. Add
JitValueBox::readStringOrNull for ?string ternary returns; copy
getenv __value__* rvalues into stack slots on first bind.

Gate still 13 failures — getenv===false and ternary string return
segfault at runtime (repro: test/repro/getenv_identical_false.php).

Co-authored-by: Cursor <cursoragent@cursor.com>
PurHur and others added 4 commits June 14, 2026 15:21
Patch php-types getenv to string|false so CFG inference uses VALUE-box
IDENTICAL lowering; reorder Helper IDENTICAL before JitLongArg; narrow
type_pair self-host stub skip; use __value__readString for nullable string
returns; split valueSlot/valueRef in VALUE dest assigns.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track function scope slots via jitCurrentBlock (not only entry block) so
branch assigns and merge RETURN share one VALUE box. Skip freeing unnamed
return phi temps marked dead by php-cfg, and resolve RETURN operands
through functionScopeSlotBindings.

AOT ?string ternary return still segfaults — next step is dominator-safe
entry alloca or CoalesceHelper-style merge for string|null phi (#8555).

Co-authored-by: Cursor <cursoragent@cursor.com>
Stabilize ?: phi scope bindings across CFG blocks, compile non-string
arms before string arms for shared RETURN merges, and add readOwnedStringOrNull
for nullable returns. AOT still segfaults when the string arm is php-cfg
block1 (if-entry); VM/JIT pass. Adds repro scripts and debug-phi-root probe.

Co-authored-by: Cursor <cursoragent@cursor.com>
Return typed string operands directly from the if-entry arm instead of
reading the shared phi VALUE box after merge-block dead-operand frees.
Nullable/union if-arms keep per-arm RETURN from the phi slot; pure string
params skip the box on the string arm only.

Verification:
- test/repro/ns_nullable_ternary_return.php AOT exit 0 (was segfault)
- test/repro/aot_ternary_bool.php AOT exit 0
- test/repro/getenv_identical_false.php AOT prints yes
- make bootstrap-aot-link still 16 failures (ns_func, ne_null, …)

Co-authored-by: Cursor <cursoragent@cursor.com>
@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

claim: worker-lane-b-automation — starting this run

…hape

When a ?: return merge has a boxed ?string on the if-entry arm, swap CFG
arms and invert the branch condition so codegen matches the working
null === $x ? null : $x shape (issue #8555).

AOT standalone still segfaults on test/repro/aot_ternary_ne_null.php f('hello')
(JIT/VM pass); ns_func blocked on return-inside-if after dim-fetch condition.
bootstrap-aot-link still 16 failures — not merge-ready.

Co-authored-by: Cursor <cursoragent@cursor.com>
@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

Update (lane C)

Added commit defda1834: boxed ?string ?: if-entry CFG rewrite (swap arms + invert branch) to match working null === $x ? null : $x codegen shape.

Still not merge-readymake bootstrap-aot-link remains at 16 failures.

Key blockers documented on #8555:

  • aot_ternary_ne_null.php AOT segfault (JIT/VM pass)
  • ns_func.php — return inside if after string dim-fetch condition

@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

Lane B update (WIP — not merging)

Continued #8555/#8563 bootstrap-aot-link work on branch agent/issue-8555-bootstrap-aot-link-lane-c (commit c74f923dc).

Root cause isolated

test/repro/aot_ternary_ne_null.php AOT segfault is not the ?: merge optimization alone:

Pattern AOT
return null !== $name ? 'fixed' : null OK
return null === $name ? null : $name (else-entry $name) OK
return null !== $name ? $name : null (if-entry $name) segfault
if (null !== $name) { return $name; } return null; segfault
$r = … ? … : …; return $r; OK

JIT (bin/jit.php) passes for all shapes; failure is AOT native link/runtime when a ?string param is returned from the if-entry CFG arm.

Changes this run

  • Restrict pure-string ?: arm-tail RETURN fast-path; add nullable if-entry branch-target swap hooks
  • VM unit test: testNullableStringTernaryIfArmValue
  • docs/bootstrap-inventory.md sync (2669 files, bootstrap-inventory-check green)

Verification

./script/docker-exec.sh -- bash -lc './vendor/bin/phpunit test/unit/TernaryReturnMergeSlotTest.php'
# OK (3 tests)

./script/docker-exec.sh -- bash -lc 'php script/bootstrap-inventory.php --check'
# OK 2669/2669

# Still blocked
./script/docker-exec.sh -- bash -lc 'export PHP_COMPILER_BOOTSTRAP_AOT_LINK=1; php bin/compile.php -o /tmp/t test/repro/aot_ternary_ne_null.php && /tmp/t'
# segfault 139

# No regression
./script/docker-exec.sh -- bash -lc 'export PHP_COMPILER_BOOTSTRAP_AOT_LINK=1; php bin/compile.php -o /tmp/t test/repro/ns_nullable_ternary_return.php && /tmp/t'
# OK

Next step

Per d87ae06: dominator-safe entry alloca or CoalesceHelper-style merge for string|null phi, or Compiler rewrite to assign-then-return (proven sound by aot_ternary_return_via_var shape).

- Narrow ?: arm-tail RETURN opt to pure-string if arms; add branch-target
  swap hooks for nullable ?string if-entry patterns (#8563)
- Add VM unit test for null !== $param ? $param : null return shape
- Sync docs/bootstrap-inventory.md (2669 files, 0 blockers)

Blocker: AOT still segfaults on test/repro/aot_ternary_ne_null.php when
?string param is returned from the if-entry CFG arm (literal if-arm OK;
else-entry param return OK). JIT path passes; next: dominator-safe entry
alloca per d87ae06 or Compiler assign-then-return rewrite.

Co-authored-by: Cursor <cursoragent@cursor.com>
@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #8584 (clean branch from master with compile-time CFG rewrite). Old branch had merge conflicts with master JIT landing.

PurHur added a commit that referenced this pull request Jun 14, 2026
…8563) (#8586)

* Fix AOT ?: return for null !== $param ? $param : null pattern (#8563).

Compile-time rewrite maps the if-entry ?string arm to null === $param ? null : $param
(php-src equivalent) and tracks the JumpIf so branch targets stay aligned with IDENTICAL.

Verification: php vendor/bin/phpunit test/unit/NullableNeNullTernaryRewriteTest.php test/unit/TernaryReturnMergeSlotTest.php
  php bin/vm.php test/repro/aot_ternary_ne_null.php
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/n test/repro/aot_ternary_ne_null.php && /tmp/n
Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix AOT ?: return when non-null arm is if-entry (#8555)

Return typed string operands directly from the if-entry arm instead of
reading the shared phi VALUE box after merge-block dead-operand frees.
Nullable/union if-arms keep per-arm RETURN from the phi slot; pure string
params skip the box on the string arm only.

Verification:
- test/repro/ns_nullable_ternary_return.php AOT exit 0 (was segfault)
- test/repro/aot_ternary_bool.php AOT exit 0
- test/repro/getenv_identical_false.php AOT prints yes
- make bootstrap-aot-link still 16 failures (ns_func, ne_null, …)

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix AOT nullable ?string param returns via compile-time ?? rewrite (#8563).

Rewrite direct and ternary returns of explicit ?T params to $param ?? null
(php-src equivalent) so native AOT uses the proven coalesce VALUE-box path.
Register nullable scalar returns as __value__* in JIT ABI; cherry-pick ?: merge
lowering from #8555.

Verification: php vendor/bin/phpunit test/unit/NullableNeNullTernaryRewriteTest.php test/unit/NullableStringReturnAbiTest.php
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/t test/repro/aot_ternary_ne_null.php && /tmp/t
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/t test/repro/aot_nullable_param_direct_return.php && /tmp/t
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: PurHur <PurHur@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@PurHur PurHur closed this in #8586 Jun 14, 2026
@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

Merged #8586

Fix: compile-time rewrite of nullable param returns to $param ?? null + __value__* return ABI for nullable scalars.

Verification (all exit 0):

PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/ne test/repro/aot_ternary_ne_null.php && /tmp/ne
PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/v test/repro/aot_ternary_var_nullable.php && /tmp/v
PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/d test/repro/aot_nullable_param_direct_return.php && /tmp/d
php vendor/bin/phpunit test/unit/NullableNeNullTernaryRewriteTest.php test/unit/NullableStringReturnAbiTest.php

@PurHur

PurHur commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

Merged #8586 — nullable param AOT returns green (see PR body for verification commands).

PurHur added a commit that referenced this pull request Jun 14, 2026
* Fix AOT ?: return for null !== $param ? $param : null pattern (#8563).

Compile-time rewrite maps the if-entry ?string arm to null === $param ? null : $param
(php-src equivalent) and tracks the JumpIf so branch targets stay aligned with IDENTICAL.

Verification: php vendor/bin/phpunit test/unit/NullableNeNullTernaryRewriteTest.php test/unit/TernaryReturnMergeSlotTest.php
  php bin/vm.php test/repro/aot_ternary_ne_null.php
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/n test/repro/aot_ternary_ne_null.php && /tmp/n
Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix AOT ?: return when non-null arm is if-entry (#8555)

Return typed string operands directly from the if-entry arm instead of
reading the shared phi VALUE box after merge-block dead-operand frees.
Nullable/union if-arms keep per-arm RETURN from the phi slot; pure string
params skip the box on the string arm only.

Verification:
- test/repro/ns_nullable_ternary_return.php AOT exit 0 (was segfault)
- test/repro/aot_ternary_bool.php AOT exit 0
- test/repro/getenv_identical_false.php AOT prints yes
- make bootstrap-aot-link still 16 failures (ns_func, ne_null, …)

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix AOT nullable ?string param returns via compile-time ?? rewrite (#8563).

Rewrite direct and ternary returns of explicit ?T params to $param ?? null
(php-src equivalent) so native AOT uses the proven coalesce VALUE-box path.
Register nullable scalar returns as __value__* in JIT ABI; cherry-pick ?: merge
lowering from #8555.

Verification: php vendor/bin/phpunit test/unit/NullableNeNullTernaryRewriteTest.php test/unit/NullableStringReturnAbiTest.php
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/t test/repro/aot_ternary_ne_null.php && /tmp/t
  PHP_COMPILER_BOOTSTRAP_AOT_LINK=1 php bin/compile.php -o /tmp/t test/repro/aot_nullable_param_direct_return.php && /tmp/t
Co-authored-by: Cursor <cursoragent@cursor.com>

* Extend inline expr call-arg bridging to new expressions (#8561).

php-cfg also splits Expr_New results from FuncCall/New ctor args; route
TYPE_ARG_SEND through the producer slot directly to avoid AOT assign type
mismatches. Unblocks const_string_folder bootstrap smokes.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: PurHur <PurHur@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.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.

1 participant