Skip to content

Regression: strlen() on non-constant operand silently terminates AOT binaries — exit 0, output truncated; VM/JIT fine (root cause of #15624) #15632

Description

@PurHur

Category

regression · AOT runtime / builtin lowering

Problem

Any non-constant-folded strlen() call terminates an AOT binary at the call site: process exits 0, all output after the call is lost, no error. VM and MCJIT execute the same script correctly, so the LLVM lowering is fine in-process — the failure is specific to the AOT link (suspect: a runtime helper symbol binding to a no-op/exit stub in the prelinked objects instead of the real implementation; MCJIT instead resolves against the host process, masking it).

Repro ($x = "abc"; echo strlen($x), "\n"; echo "after\n";) Output
php bin/vm.php 3 · after
php bin/jit.php 3 · after
phpc build binary (nothing, exit 0)

echo strlen("abc") works (constant-folded). substr, is_string, count on variables work.

Blast radius: every AOT web example that touches request parsing (lib/Web/MultipartParser.php is strlen-heavy) — direct root cause of #15624 (006-FileUploadWeb empty state). Regression window: 2026-06-02 (6ca795e72, honest AOT benchmark timings) → master.

Repro

printf "<?php\n\$x = \"abc\";\necho strlen(\$x), \"\\n\";\necho \"after\\n\";\n" > /tmp/r.php
./script/docker-exec.sh -- bash -lc "./phpc build -o /tmp/r /tmp/r.php && /tmp/r; echo RC=\$?"

Done when

Activity

  1. added
    bugSomething isn't working
    phase-3:aotPhase 3 – AOT deployment
    MOST IMPORTANTThis are the most important targets
    on Jul 3, 2026
  2. PurHur commented on Jul 3, 2026

    @PurHur
    OwnerAuthor

    claim: lane-c-worker — starting this run

  3. PurHur commented on Jul 3, 2026

    @PurHur
    OwnerAuthor

    Merged via #15641.

    Root cause: JitStrlen::lowerLengthFromValueBox() returned from PHP after emitting the non-strict null-deprecation branch, leaving strlen_value_ok as an empty ret void. Any non-null boxed operand (including {main} script globals like $x in the repro) took that branch and silently ended the function — exit 0, no output after strlen($var).

    Fix: Continue LLVM emission into the string-length path after the null branch; merge null-coercion (deprecation + length 0) via phi.

    Verified: VM + AOT repro test/repro/issue_15632_strlen_aot.php prints 3 / after; AotRegressionFixtureCatalogTest + strlen_script_global.phpt; ci-fast.sh green.

  4. PurHur commented on Jul 3, 2026

    @PurHur
    OwnerAuthor

    Master is still broken after #15641 (verified at 8250d4076, 2026-07-03):

    $x = "abc"; echo strlen($x), "\n"; echo "after\n";
    # phpc build + run → no output, exit 0 (unchanged symptom)
    

    #15641 merged the null path via phi but kept the early return in the non-strict objectBlock branch of lowerLengthFromValueBox. For any non-strict compile the PHP method returns during objectBlock emission, so coerceBlock — the plain-string runtime path — and mergeBlock are never populated: same dangling-block failure, one block later. Two further defects: mergeBlock is appended unconditionally but never terminated in strict mode (new dangling block on strict scripts), and the phi incomings use the block labels ($nullLenBlock, $coerceBlock) rather than the builder's actual end blocks — wrong if emitNullStringDeprecation/JitStringArg::lower emit internal branches.

    PR #15640 (opened before #15641 landed) fixes all paths — null/object/plain merge via phi with getInsertBlock() incomings, merge block only in non-strict mode; verified plain/null/strict/JIT/examples-000 + Strlen-filter regression-neutral. Rebasing it onto master now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    MOST IMPORTANTThis are the most important targetsarea:compilerCompiler / CFG / JITbugSomething isn't workingphase-3:aotPhase 3 – AOT deployment

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions