From c8920abee73ca9c938f0f8dcaf9962e45df96c05 Mon Sep 17 00:00:00 2001 From: PurHur Date: Mon, 25 May 2026 15:32:10 +0000 Subject: [PATCH] North Star 2: phpc doctor --selfhost + HelloWorld M3 emit TU (#1056) Add phpc doctor --selfhost for M2/M3/M4 bootstrap gate discovery (#2053). Wire helloworld_m3_emit_native_entry.php and align helloworld-probe with the compile-smoke emit-helper pattern (#1768, #1493). Document emit-TU ladder in bootstrap-selfhost.md and bootstrap-m5-fast-path.md. Closes #2053 Contributes to #1492, #1056, #1768 Co-authored-by: Cursor --- bin/compile.php | 3 +- bin/phpc.php | 13 +- docs/bootstrap-m5-fast-path.md | 3 +- docs/bootstrap-selfhost.md | 3 +- lib/Doctor.php | 72 ++++++++++++ script/bootstrap-selfhost-helloworld-probe.sh | 111 +++++++++--------- .../helloworld_m3_emit_native_entry.php | 109 +++++++++++++++++ test/unit/BootstrapSelfhostHelloWorldTest.php | 12 +- test/unit/PhpcDoctorTest.php | 24 ++++ 9 files changed, 289 insertions(+), 61 deletions(-) create mode 100644 test/bootstrap-aot/helloworld_m3_emit_native_entry.php diff --git a/bin/compile.php b/bin/compile.php index 8832e91d623..1b9cafaddbc 100755 --- a/bin/compile.php +++ b/bin/compile.php @@ -21,7 +21,8 @@ function run(string $filename, string $code, array $options): void if ('' !== $normalized && str_contains($normalized, '/test/bootstrap-aot/')) { // M3 native emit TU: self-host M3 allowlist (not full bootstrap JIT) (#1937, #1983). $m3Driver = getenv('PHP_COMPILER_M3_COMPILE_DRIVER'); - $m3EmitNative = str_contains($normalized, 'compile_smoke_m3_emit_native_entry.php') + $m3EmitNative = (str_contains($normalized, 'compile_smoke_m3_emit_native_entry.php') + || str_contains($normalized, 'helloworld_m3_emit_native_entry.php')) && ('1' === $m3Driver || 'true' === strtolower((string) $m3Driver)); if ($m3EmitNative) { putenv('PHP_COMPILER_SELFHOST_AOT=1'); diff --git a/bin/phpc.php b/bin/phpc.php index 82a126c5902..a1da4126ae5 100755 --- a/bin/phpc.php +++ b/bin/phpc.php @@ -23,7 +23,7 @@ * phpc lint --all [--json] * phpc init [--profile default|miniwebapp|sessionsweb|apijson|fileupload] [--force] [target-dir] * phpc test [--fast] [--bootstrap] [--bootstrap-strict] [-- phpunit/ci-local args...] - * phpc doctor [--gates] [--no-lint] [--jit-probe] [--aot-project-probe [dir]] Env probes; --gates adds 005-SessionsWeb ladder (#1903, #1969) + * phpc doctor [--gates] [--selfhost] [--no-lint] [--jit-probe] [--aot-project-probe [dir]] Env probes; --gates MiniWebApp; --selfhost NS2 (#2053) * phpc validate-manifest [dir] Validate phpc.json schema and paths (issue #263) */ @@ -69,9 +69,10 @@ Scaffold web project (default, miniwebapp, sessionsweb, apijson, or fileupload) phpc test [--fast] [args...] Run ci-local.sh (full) or ci-fast.sh (no LLVM) phpc test --bootstrap [--strict] Bootstrap subset (inventory + spine sync; #1961) - phpc doctor [--gates] [--no-lint] [--jit-probe] [--aot-project-probe [dir]] + phpc doctor [--gates] [--selfhost] [--no-lint] [--jit-probe] [--aot-project-probe [dir]] Probe environment; LLVM/JIT readiness (#717, #746) --gates MiniWebApp ladder + NS1/NS2 + 005-SessionsWeb (#1969) + --selfhost North Star 2 bootstrap gates only (#2053, #1492) --jit-probe Run MCJIT smoke (script/jit-runtime-probe.php) --aot-project-probe [dir] AOT build + execute on 003-MiniWebApp (or dir) phpc validate-manifest [dir] Validate phpc.json (default: cwd) @@ -264,6 +265,7 @@ } require $repoRoot.'/vendor/autoload.php'; $gates = false; + $selfhost = false; $noLint = false; $jitProbe = false; $aotProjectProbe = false; @@ -273,6 +275,10 @@ $gates = true; continue; } + if ('--selfhost' === $arg) { + $selfhost = true; + continue; + } if ('--no-lint' === $arg) { $noLint = true; continue; @@ -292,6 +298,9 @@ fwrite(STDERR, "phpc doctor: unknown option: {$arg}\n"); exit(1); } + if ($selfhost) { + exit(\PHPCompiler\Doctor::runSelfhost($repoRoot)); + } if ($gates) { exit(\PHPCompiler\Doctor::runGates($repoRoot, $noLint)); } diff --git a/docs/bootstrap-m5-fast-path.md b/docs/bootstrap-m5-fast-path.md index 96fbad05c3f..a57ac7c9122 100644 --- a/docs/bootstrap-m5-fast-path.md +++ b/docs/bootstrap-m5-fast-path.md @@ -39,7 +39,8 @@ Supporting fixes from #1402: | `Runtime::initVmContext` | **Native** via `RuntimeInitVmContext::emit` (allocate `VM\Context`, set `runtime` + `vmContext`); wired in `compileBlock()`; off deny list (#1494). PHP CFG `new VMContext` still LLVM 9 link crash when combined with ctor spine. | | `Runtime::loadJit` | `compileRuntimeLoadJitM3Native` + nested `createJit` helpers (#1495) | | `Runtime::standalone` | Compile-driver link OK (#1402, #1056) | -| `helloworld_compile_smoke` | Deny-listed for link (LLVM 9); smoke uses int+echo (#1514); compile_driver calls stub until link fixed | +| `helloworld_compile_smoke` | Deny-listed for link (LLVM 9); compile_driver bundle keeps stub; runtime emit uses `helloworld_m3_emit_native_entry.php` → `compile_smoke_m3_emit` (#1768) | +| `helloworld_m3_emit_native_entry.php` | Native emit TU (same spine as `compile_smoke_m3_emit_native_entry.php`); probe: `bootstrap-selfhost-helloworld-probe.sh` with `PHP_COMPILER_EMIT_HELPER_LINK=1` | | `compile_smoke_m3_emit` | Deny-listed for self-host `compile_driver` link (LLVM 9); native emit via `compile_smoke_m3_emit_native_entry.php` + `PHP_COMPILER_SELFHOST_AOT=1` `PHP_COMPILER_M3_COMPILE_DRIVER=1` `PHP_COMPILER_EMIT_HELPER_LINK=1` (#1983, #1937) | | `Compiler::compile` / `compileFunc` | PHP CFG lowering in emit TU when `PHP_COMPILER_EMIT_HELPER_LINK=1` (link OK; runtime emit still blocked by emit-binary global init segfault — #1937) | | Native emit runtime | `BOOTSTRAP_M3_RUNTIME_COMPILE=1` + `PHP_COMPILER_M3_EMIT_MINIMAL=1` skips eager `loadJitCompileModuleFuncs` during smoke emit | diff --git a/docs/bootstrap-selfhost.md b/docs/bootstrap-selfhost.md index 830d49ef9a4..df3e108a226 100644 --- a/docs/bootstrap-selfhost.md +++ b/docs/bootstrap-selfhost.md @@ -26,13 +26,14 @@ | M2 lib spine VM `-r` smoke | `./script/bootstrap-selfhost-lib-spine-vm-smoke.sh` or `BOOTSTRAP_LIB_SPINE_VM_SMOKE=1 make bootstrap-selfhost-lib-spine-vm-smoke` | ✅ same binary + `PHP_COMPILER_VM_SPINE_SMOKE=1` prints `vm-spine-ok` ([#1846](https://github.com/PurHur/php-compiler/issues/1846); `ci-local.sh` default `BOOTSTRAP_LIB_SPINE_VM_SMOKE_GATE=1` ([#1867](https://github.com/PurHur/php-compiler/issues/1867)); set `0` to skip) | | M3 HelloWorld self-host probe lint | `php bin/compile.php -l test/selfhost/compiler_helloworld_smoke/main.php` | ✅ `compiler_compile_smoke` spine + `helloworld_compile_smoke` driver (linkable) | | M3 HelloWorld compile driver lint | `php bin/compile.php -l test/selfhost/compiler_helloworld_smoke/compile_driver.php` | ✅ `Runtime::parseAndCompile` + mode-file dispatch (native link opt-in) | -| M3 HelloWorld self-host probe | `./script/bootstrap-selfhost-helloworld-probe.sh` or `make bootstrap-selfhost-helloworld` | 🚧 **partial** — bundle link ✅; HelloWorld AOT **run** native ✅; **emit uses Zend fallback** until native runtime compile passes ([#1492](https://github.com/PurHur/php-compiler/issues/1492); probe sets `M3_NATIVE_COMPILE=1` / `emit_path=native` only on selfhost compile-driver success; `BOOTSTRAP_M3_HELLOWORLD_STRICT=1` fails without fallback — see [bootstrap-m5-fast-path.md](bootstrap-m5-fast-path.md)) | +| M3 HelloWorld self-host probe | `./script/bootstrap-selfhost-helloworld-probe.sh` or `make bootstrap-selfhost-helloworld` | 🚧 **partial** — bundle link ✅; HelloWorld AOT **run** native ✅; native emit via `helloworld_m3_emit_native_entry.php` blocked at emit-TU runtime init ([#1768](https://github.com/PurHur/php-compiler/issues/1768), [#1937](https://github.com/PurHur/php-compiler/issues/1937)); `emit_path=native` when `compile_smoke_m3_emit: compile OK`; Zend fallback otherwise — see [bootstrap-m5-fast-path.md](bootstrap-m5-fast-path.md) | | M3 HelloWorld wave gate | `./script/bootstrap-wave-check.sh --with-helloworld` | ✅ opt-in `BOOTSTRAP_M3_HELLOWORLD=1` | | M3 HelloWorld strict CI gate | `BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE=1 ./script/ci-local.sh` | opt-in ([#1526](https://github.com/PurHur/php-compiler/issues/1526)); runs `bootstrap-selfhost-helloworld-probe.sh` with `BOOTSTRAP_M3_HELLOWORLD_STRICT=1` + compile-driver link (default **off** until `emit_path=native` stable) | | M4 bootstrap-loop gen-1 link | `./script/bootstrap-loop-gen1-link.sh` or `make bootstrap-loop-gen1-link` | 🚧 **partial** — gen-1 link ✅; gen-2 **Zend** emit until M3 native compile ([#1498](https://github.com/PurHur/php-compiler/issues/1498)) | | M4 bootstrap-loop probe | `./script/bootstrap-loop-probe.sh` or `make bootstrap-loop-probe` | 🚧 **ladder** — M2 + M3 partial + gen-1/gen-2 slice, then M3 strict; `--dry-run` skips strict ([#1498](https://github.com/PurHur/php-compiler/issues/1498)) | | M4 bootstrap-loop probe in CI | `BOOTSTRAP_LOOP_PROBE_GATE=1 ./script/ci-fast.sh` or `./script/ci-local.sh` (LLVM tail) | opt-in runs `--dry-run` ladder; default off until M3 strict stable ([#1777](https://github.com/PurHur/php-compiler/issues/1777), [#1929](https://github.com/PurHur/php-compiler/issues/1929)) | | North Star 2 presenter in fast CI | `./script/ci-fast.sh` (default) or `make north-star2-verify` | ✅ default-on in ci-fast ([#1928](https://github.com/PurHur/php-compiler/issues/1928), [#2051](https://github.com/PurHur/php-compiler/issues/2051)); opt-out `NORTH_STAR2_VERIFY_GATE=0` | +| Self-host gate ladder CLI | `./phpc doctor --selfhost` | ✅ M2/M3/M4 env vars + probe commands only ([#2053](https://github.com/PurHur/php-compiler/issues/2053)) | | Bootstrap iteration CLI | `./phpc test --bootstrap` or `./script/bootstrap-test-subset.sh` | ✅ inventory `--check` + spine count sync (no LLVM link by default); `BOOTSTRAP_TEST_SUBSET_VM_SMOKE=1` for M2 VM spine smoke; `--strict` / `--bootstrap-strict` runs M3 HelloWorld strict probe ([#1961](https://github.com/PurHur/php-compiler/issues/1961)) | Regenerate: `make bootstrap-profile` (inventory + profile + optional `bootstrap-aot-lint`). Phase C: `make bootstrap-aot-link` (or `php script/bootstrap-aot-lint.php --link`). Phase D: `make bootstrap-aot-link-lib`. Bundled compiler lint: `./script/bootstrap-selfhost-lint.sh`. Live lowering target: `make bootstrap-selfhost-probe` (or `./script/bootstrap-selfhost-compile-probe.sh`; optional `--update-inventory`). diff --git a/lib/Doctor.php b/lib/Doctor.php index 469f24d9a6d..506f316b4bb 100644 --- a/lib/Doctor.php +++ b/lib/Doctor.php @@ -141,6 +141,78 @@ public static function runGates(string $repoRoot, bool $noLint = false): int return is_int($exit) ? $exit : 1; } + /** + * North Star 2 self-host gate ladder only (issues #1492, #2053). + */ + public static function runSelfhost(string $repoRoot): int + { + require_once $repoRoot.'/script/bootstrap-spine-count.php'; + $counts = bootstrap_spine_counts($repoRoot); + $spine = $counts['spine']; + $inventory = $counts['inventory']; + + $llvmInfo = self::resolveLlvmInfo($repoRoot); + $llvmReady = null !== $llvmInfo['dir']; + $llvmDetail = $llvmReady + ? 'ready at '.$llvmInfo['dir'].' ('.$llvmInfo['source'].')' + : 'missing — link/probe steps need LLVM 9 (script/install-llvm9.sh)'; + + $defaults = self::readCiDefaultsEnv($repoRoot); + $ns2Default = $defaults['NORTH_STAR2_VERIFY_GATE'] ?? '1'; + $m3HelloStrictDefault = $defaults['BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE'] ?? '0'; + $m3SmokeStrictDefault = $defaults['BOOTSTRAP_M3_COMPILE_SMOKE_STRICT_GATE'] ?? '0'; + $m3SmokeProbeDefault = $defaults['BOOTSTRAP_M3_COMPILE_SMOKE_PROBE_GATE'] ?? '1'; + $spineCountSyncDefault = $defaults['SELFHOST_SPINE_COUNT_SYNC_GATE'] ?? '1'; + $spineCoverageDefault = $defaults['SELFHOST_SPINE_COVERAGE_SYNC_GATE'] ?? '1'; + $loopProbeDefault = $defaults['BOOTSTRAP_LOOP_PROBE_GATE'] ?? '0'; + + $inventoryScript = $repoRoot.'/script/bootstrap-inventory.php'; + $inventoryOk = is_file($inventoryScript); + $inventoryDetail = $inventoryOk + ? 'php script/bootstrap-inventory.php --check' + : 'script/bootstrap-inventory.php missing'; + + fwrite(STDOUT, "North Star 2 self-host gates (#1492, #1056):\n\n"); + + fwrite(STDOUT, "1. Inventory\n"); + fwrite(STDOUT, " {$inventoryDetail}\n"); + fwrite(STDOUT, " php script/bootstrap-spine-count.php → {$spine}/{$inventory}\n\n"); + + fwrite(STDOUT, "2. M2 spine\n"); + fwrite(STDOUT, " SELFHOST_SPINE_COUNT_SYNC_GATE=".(self::gateEnabled('SELFHOST_SPINE_COUNT_SYNC_GATE', $spineCountSyncDefault) ? '1' : '0')." (default {$spineCountSyncDefault})\n"); + fwrite(STDOUT, " SELFHOST_SPINE_COVERAGE_SYNC_GATE=".(self::gateEnabled('SELFHOST_SPINE_COVERAGE_SYNC_GATE', $spineCoverageDefault) ? '1' : '0')." (default {$spineCoverageDefault})\n"); + fwrite(STDOUT, " BOOTSTRAP_LIB_SPINE_SMOKE=1 make bootstrap-selfhost-lib-spine-smoke\n"); + fwrite(STDOUT, " BOOTSTRAP_LIB_SPINE_VM_SMOKE=1 make bootstrap-selfhost-lib-spine-vm-smoke\n\n"); + + fwrite(STDOUT, "3. M3 emit (partial vs strict)\n"); + fwrite(STDOUT, " BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE=".(self::gateEnabled('BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE', $m3HelloStrictDefault) ? '1' : '0')." (default {$m3HelloStrictDefault}) — ci-local LLVM tail\n"); + fwrite(STDOUT, " BOOTSTRAP_M3_COMPILE_SMOKE_PROBE_GATE=".(self::gateEnabled('BOOTSTRAP_M3_COMPILE_SMOKE_PROBE_GATE', $m3SmokeProbeDefault) ? '1' : '0')." (default {$m3SmokeProbeDefault})\n"); + fwrite(STDOUT, " BOOTSTRAP_M3_COMPILE_SMOKE_STRICT_GATE=".(self::gateEnabled('BOOTSTRAP_M3_COMPILE_SMOKE_STRICT_GATE', $m3SmokeStrictDefault) ? '1' : '0')." (default {$m3SmokeStrictDefault})\n"); + fwrite(STDOUT, " BOOTSTRAP_M3_LINK_COMPILE_DRIVER=1 BOOTSTRAP_M3_COMPILE_DRIVER_REAL_LOWERING=1 BOOTSTRAP_M3_RUNTIME_COMPILE=1 ./script/bootstrap-selfhost-helloworld-probe.sh\n"); + fwrite(STDOUT, " BOOTSTRAP_M3_HELLOWORLD_STRICT=1 … helloworld-probe.sh (no Zend fallback; #1493)\n"); + fwrite(STDOUT, " Emit TU: test/bootstrap-aot/helloworld_m3_emit_native_entry.php (#1768)\n\n"); + + fwrite(STDOUT, "4. Presenter / fast CI\n"); + fwrite(STDOUT, " NORTH_STAR2_VERIFY_GATE=".(self::gateEnabled('NORTH_STAR2_VERIFY_GATE', $ns2Default) ? '1' : '0')." (default {$ns2Default}) — ci-fast\n"); + if (is_executable($repoRoot.'/script/north-star2-verify.sh')) { + fwrite(STDOUT, " make north-star2-verify or ./script/north-star2-verify.sh\n"); + } + fwrite(STDOUT, " phpc test --bootstrap [--strict]\n"); + fwrite(STDOUT, " make bootstrap-wave-check (opt-in --with-helloworld)\n\n"); + + fwrite(STDOUT, "5. M4 loop\n"); + fwrite(STDOUT, " BOOTSTRAP_LOOP_PROBE_GATE=".(self::gateEnabled('BOOTSTRAP_LOOP_PROBE_GATE', $loopProbeDefault) ? '1' : '0')." (default {$loopProbeDefault}) — ./script/bootstrap-loop-probe.sh --dry-run\n\n"); + + fwrite(STDOUT, "6. LLVM\n"); + fwrite(STDOUT, " {$llvmDetail}\n"); + if ($llvmReady) { + fwrite(STDOUT, " phpc doctor --jit-probe (MCJIT smoke)\n"); + } + fwrite(STDOUT, "\nDocs: docs/bootstrap-selfhost.md · docs/bootstrap-m5-fast-path.md · docs/self-host-target.md\n"); + + return 0; + } + /** * North Star 1 presenter commands after the gate ladder (issues #1845, #1857). */ diff --git a/script/bootstrap-selfhost-helloworld-probe.sh b/script/bootstrap-selfhost-helloworld-probe.sh index df7ace0b874..95cf1b8d422 100755 --- a/script/bootstrap-selfhost-helloworld-probe.sh +++ b/script/bootstrap-selfhost-helloworld-probe.sh @@ -1,16 +1,16 @@ #!/usr/bin/env bash -# M3 HelloWorld self-host probe (issue #1056): link selfhost bundle, compile HelloWorld AOT, run natively. +# M3 HelloWorld self-host probe (issue #1056): link selfhost bundle, native or Zend emit, run natively. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" ENTRY="${ROOT}/test/selfhost/compiler_helloworld_smoke/main.php" PROBE="${ROOT}/build/selfhost-helloworld" SOURCE="${ROOT}/examples/000-HelloWorld/example.php" AOT_OUT="${ROOT}/build/helloworld-aot" -COMPILE_DRIVER="${ROOT}/build/selfhost-helloworld-compile" -MODE_FILE="${ROOT}/build/.m3-helloworld-mode" +EMIT_HELPER="${ROOT}/build/selfhost-helloworld-emit" +EMIT_ENTRY="${ROOT}/test/bootstrap-aot/helloworld_m3_emit_native_entry.php" M3_NATIVE_COMPILE=0 M3_EMIT_PATH="none" -M3_BLOCK_REASON="native compile driver not linked (set BOOTSTRAP_M3_LINK_COMPILE_DRIVER=1)" +M3_BLOCK_REASON="native emit helper not linked (set BOOTSTRAP_M3_LINK_COMPILE_DRIVER=1)" # shellcheck source=php-env.sh source "$(dirname "$0")/php-env.sh" ci_apply_llvm_memory_env @@ -18,7 +18,7 @@ ci_apply_llvm_memory_env m3_exit_label() { local code=$1 if [[ "${code}" -eq 139 ]]; then - echo "segfault (LLVM 9 link/lowering; see docs/bootstrap-m5-fast-path.md deny list)" + echo "segfault (LLVM 9 emit TU global init; see docs/bootstrap-m5-fast-path.md)" elif [[ "${code}" -eq 137 ]]; then echo "SIGKILL (likely OOM during link)" elif [[ "${code}" -ne 0 ]]; then @@ -38,6 +38,11 @@ if [[ ! -f "${SOURCE}" ]]; then exit 1 fi +if [[ ! -f "${EMIT_ENTRY}" ]]; then + echo "bootstrap-selfhost-helloworld-probe: missing ${EMIT_ENTRY} (#1768)" >&2 + exit 1 +fi + if [[ -z "${PHP_COMPILER_LLVM_PATH:-}" || ! -f "${PHP_COMPILER_LLVM_PATH}/libLLVM-9.so.1" ]]; then echo "bootstrap-selfhost-helloworld-probe: LLVM 9 not found (skip)" >&2 exit 2 @@ -46,84 +51,80 @@ fi mkdir -p "${ROOT}/build" export PHP_COMPILER_SELFHOST_AOT=1 export PHP_COMPILER_JIT_PROGRESS_FILE="${ROOT}/build/.last-jit-func-helloworld-probe" -rm -f "${PROBE}" "${COMPILE_DRIVER}" "${AOT_OUT}" "${MODE_FILE}" "${PHP_COMPILER_JIT_PROGRESS_FILE}" - -if ! php "${ROOT}/bin/compile.php" -o "${PROBE}" "${ENTRY}" 2>&1; then - echo "bootstrap-selfhost-helloworld-probe: link bundle failed (see stderr above)" >&2 - exit 1 -fi -test -x "${PROBE}" - -bundle_out="$("${PROBE}")" -if ! grep -q 'compiler_helloworld_smoke bundle OK' <<< "${bundle_out}"; then - echo "bootstrap-selfhost-helloworld-probe: unexpected bundle stdout (want compiler_helloworld_smoke bundle OK)" >&2 - printf '%s\n' "${bundle_out}" >&2 - exit 1 -fi +rm -f "${PROBE}" "${EMIT_HELPER}" "${AOT_OUT}" "${PHP_COMPILER_JIT_PROGRESS_FILE}" -# Native compile driver link is opt-in; runtime dispatch uses mode file (#1056). if [[ "${BOOTSTRAP_M3_LINK_COMPILE_DRIVER:-0}" == "1" ]]; then m3_link_env=() m3_link_mode="stub" if [[ "${BOOTSTRAP_M3_COMPILE_DRIVER_REAL_LOWERING:-0}" == "1" ]]; then - m3_link_env=(env PHP_COMPILER_SELFHOST_AOT=1 PHP_COMPILER_M3_COMPILE_DRIVER=1) - m3_link_mode="real-lowering (helloworld_compile_smoke + Runtime::parseAndCompile allowlist)" + m3_link_env=(env PHP_COMPILER_SELFHOST_AOT=1 PHP_COMPILER_M3_COMPILE_DRIVER=1 PHP_COMPILER_EMIT_HELPER_LINK=1) + m3_link_mode="selfhost M3 emit TU (helloworld_m3_emit_native_entry.php)" else m3_link_env=(env PHP_COMPILER_SELFHOST_AOT=1) m3_link_mode="selfhost stubs (no PHP_COMPILER_M3_COMPILE_DRIVER)" fi set +e - "${m3_link_env[@]}" php "${ROOT}/bin/compile.php" -o "${COMPILE_DRIVER}" "${ROOT}/test/selfhost/compiler_helloworld_smoke/compile_driver.php" 2>&1 + "${m3_link_env[@]}" php "${ROOT}/bin/compile.php" -o "${EMIT_HELPER}" "${EMIT_ENTRY}" >/dev/null 2>&1 m3_link_code=$? set -e - if [[ "${m3_link_code}" -eq 0 ]]; then - echo "bootstrap-selfhost-helloworld-probe: compile driver link OK (${COMPILE_DRIVER}, ${m3_link_mode})" - set +e - runtime_env=( - env PHP_COMPILER_M3_COMPILE_MODE=compile - PHP_COMPILER_M3_SOURCE="${SOURCE}" - PHP_COMPILER_M3_OUT="${AOT_OUT}" - ) + if [[ -x "${EMIT_HELPER}" ]]; then + echo "bootstrap-selfhost-helloworld-probe: native emit helper link OK (${EMIT_HELPER}, ${m3_link_mode})" if [[ "${BOOTSTRAP_M3_RUNTIME_COMPILE:-0}" == "1" ]]; then - runtime_env+=(PHP_COMPILER_M3_RUNTIME_COMPILE=1) - fi - compile_out="$({ "${runtime_env[@]}" env -u PHP_COMPILER_SELFHOST_AOT "${COMPILE_DRIVER}"; } 2>&1)" - native_compile_code=$? - set -e - if [[ "${native_compile_code}" -eq 0 ]] && grep -q 'helloworld_compile_smoke: compile OK' <<< "${compile_out}"; then - M3_NATIVE_COMPILE=1 - M3_EMIT_PATH="native" - M3_BLOCK_REASON="" - echo "bootstrap-selfhost-helloworld-probe: native emit via selfhost compile driver OK" - else - if [[ "${BOOTSTRAP_M3_RUNTIME_COMPILE:-0}" != "1" ]]; then - M3_BLOCK_REASON="runtime gate: BOOTSTRAP_M3_RUNTIME_COMPILE=1 not set (spine still stubbed in bundle)" - elif grep -q 'native emit failed at phase=' <<< "${compile_out}"; then - M3_BLOCK_REASON="$(grep -m1 'native emit failed at phase=' <<< "${compile_out}" | sed 's/^helloworld_compile_smoke: //')" - elif grep -q 'emit path blocked' <<< "${compile_out}"; then - M3_BLOCK_REASON="$(grep -m1 'emit path blocked' <<< "${compile_out}" | sed 's/^helloworld_compile_smoke: //')" + set +e + compile_out="$( + env PHP_COMPILER_M3_EMIT_MINIMAL=1 \ + PHP_COMPILER_M3_SOURCE="${SOURCE}" \ + PHP_COMPILER_M3_OUT="${AOT_OUT}" \ + "${EMIT_HELPER}" 2>&1 + )" + native_compile_code=$? + set -e + if [[ "${native_compile_code}" -eq 0 ]] && grep -q 'compile_smoke_m3_emit: compile OK' <<< "${compile_out}"; then + M3_NATIVE_COMPILE=1 + M3_EMIT_PATH="native" + M3_BLOCK_REASON="" + echo "bootstrap-selfhost-helloworld-probe: native emit via selfhost emit helper OK" else - M3_BLOCK_REASON="native compile runtime failed ($(m3_exit_label "${native_compile_code}"))" + if grep -q 'native emit failed at phase=' <<< "${compile_out}"; then + M3_BLOCK_REASON="$(grep -m1 'native emit failed at phase=' <<< "${compile_out}" | sed 's/^compile_smoke_m3_emit: //')" + else + M3_BLOCK_REASON="native emit runtime failed ($(m3_exit_label "${native_compile_code}"))" + fi + echo "bootstrap-selfhost-helloworld-probe: native emit blocked — ${M3_BLOCK_REASON}" >&2 + printf '%s\n' "${compile_out}" >&2 fi - echo "bootstrap-selfhost-helloworld-probe: native emit blocked — ${M3_BLOCK_REASON}" >&2 - printf '%s\n' "${compile_out}" >&2 + else + M3_BLOCK_REASON="runtime gate: BOOTSTRAP_M3_RUNTIME_COMPILE=1 not set" fi else - M3_BLOCK_REASON="compile driver link failed ($(m3_exit_label "${m3_link_code}"), mode=${m3_link_mode})" + M3_BLOCK_REASON="emit helper link failed ($(m3_exit_label "${m3_link_code}"), mode=${m3_link_mode})" echo "bootstrap-selfhost-helloworld-probe: ${M3_BLOCK_REASON}" >&2 fi fi +if ! php "${ROOT}/bin/compile.php" -o "${PROBE}" "${ENTRY}" 2>&1; then + echo "bootstrap-selfhost-helloworld-probe: link bundle failed (see stderr above)" >&2 + exit 1 +fi +test -x "${PROBE}" + +bundle_out="$("${PROBE}")" +if ! grep -q 'compiler_helloworld_smoke bundle OK' <<< "${bundle_out}"; then + echo "bootstrap-selfhost-helloworld-probe: unexpected bundle stdout (want compiler_helloworld_smoke bundle OK)" >&2 + printf '%s\n' "${bundle_out}" >&2 + exit 1 +fi + if [[ "${M3_NATIVE_COMPILE}" -eq 0 ]]; then echo "bootstrap-selfhost-helloworld-probe: native emit unavailable (M3 partial) — ${M3_BLOCK_REASON}" >&2 - echo "bootstrap-selfhost-helloworld-probe: NEXT_LOWER: Runtime ctor / loadJit / standalone off M3 deny list (#1402)" >&2 + echo "bootstrap-selfhost-helloworld-probe: NEXT_LOWER: M3 emit TU runtime init (global ctor / type reconstructor; #1937)" >&2 if [[ "${BOOTSTRAP_M3_HELLOWORLD_STRICT:-0}" == "1" ]]; then echo "bootstrap-selfhost-helloworld-probe: BOOTSTRAP_M3_HELLOWORLD_STRICT=1 — require native emit; refusing Zend compile.php fallback" >&2 echo "bootstrap-selfhost-helloworld-probe: emit_path=zend_fallback_would_be_used block_reason=${M3_BLOCK_REASON}" >&2 exit 1 fi M3_EMIT_PATH="zend" - echo "bootstrap-selfhost-helloworld-probe: emit_path=zend (bin/compile.php) — HelloWorld AOT until native spine is ready" >&2 + echo "bootstrap-selfhost-helloworld-probe: emit_path=zend (bin/compile.php) — HelloWorld AOT until native emit TU is stable" >&2 rm -f "${AOT_OUT}" if ! php "${ROOT}/bin/compile.php" -o "${AOT_OUT}" "${SOURCE}" 2>&1; then echo "bootstrap-selfhost-helloworld-probe: Zend HelloWorld emit failed (emit_path=zend)" >&2 @@ -144,7 +145,7 @@ if ! grep -q 'Hello World' <<< "${run_out}"; then fi if [[ "${M3_NATIVE_COMPILE}" -eq 1 ]]; then - echo "bootstrap-selfhost-helloworld-probe: OK emit_path=native ${COMPILE_DRIVER} -> ${AOT_OUT}" + echo "bootstrap-selfhost-helloworld-probe: OK emit_path=native ${EMIT_HELPER} -> ${AOT_OUT}" else echo "bootstrap-selfhost-helloworld-probe: OK emit_path=zend partial ${PROBE} -> ${AOT_OUT} (native run)" fi diff --git a/test/bootstrap-aot/helloworld_m3_emit_native_entry.php b/test/bootstrap-aot/helloworld_m3_emit_native_entry.php new file mode 100644 index 00000000000..6dbb2e499c2 --- /dev/null +++ b/test/bootstrap-aot/helloworld_m3_emit_native_entry.php @@ -0,0 +1,109 @@ +assertStringContainsString('BOOTSTRAP_M3_HELLOWORLD_STRICT=1', $script); $this->assertStringContainsString('block_reason=', $script); $this->assertStringContainsString('M3_NATIVE_COMPILE=1', $script); - $this->assertStringContainsString('helloworld_compile_smoke: compile OK', $script); + $this->assertStringContainsString('compile_smoke_m3_emit: compile OK', $script); + $this->assertStringContainsString('helloworld_m3_emit_native_entry.php', $script); + $this->assertStringContainsString('PHP_COMPILER_EMIT_HELPER_LINK=1', $script); $this->assertStringContainsString('missing executable', $script); } + public function testHelloWorldM3EmitNativeEntryExists(): void + { + $entry = self::$root.'/test/bootstrap-aot/helloworld_m3_emit_native_entry.php'; + $this->assertFileExists($entry); + $source = (string) file_get_contents($entry); + $this->assertStringContainsString('compile_smoke_m3_emit', $source); + } + public function testCiLocalDocumentsM3StrictGate(): void { $common = (string) file_get_contents(self::$root.'/script/ci-common.sh'); diff --git a/test/unit/PhpcDoctorTest.php b/test/unit/PhpcDoctorTest.php index 9eefd64d5e0..1a6f9d53953 100644 --- a/test/unit/PhpcDoctorTest.php +++ b/test/unit/PhpcDoctorTest.php @@ -44,6 +44,30 @@ public function testHelpDocumentsAotProjectProbe(): void $this->assertStringContainsString('--aot-project-probe', $result['stdout']); } + public function testHelpDocumentsSelfhostFlag(): void + { + $result = $this->runPhpc(['help']); + $this->assertSame(0, $result['exit']); + $this->assertStringContainsString('--selfhost', $result['stdout']); + } + + public function testDoctorSelfhostPrintsBootstrapGates(): void + { + $result = $this->runPhpc(['doctor', '--selfhost']); + $this->assertSame(0, $result['exit'], $result['stdout']."\n".$result['stderr']); + $this->assertStringContainsString('North Star 2 self-host gates', $result['stdout']); + $this->assertStringContainsString('BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE', $result['stdout']); + $this->assertStringContainsString('BOOTSTRAP_M3_COMPILE_SMOKE_STRICT_GATE', $result['stdout']); + $this->assertStringContainsString('NORTH_STAR2_VERIFY_GATE', $result['stdout']); + $this->assertStringContainsString('SELFHOST_SPINE_COUNT_SYNC_GATE', $result['stdout']); + $this->assertStringContainsString('SELFHOST_SPINE_COVERAGE_SYNC_GATE', $result['stdout']); + $this->assertStringContainsString('BOOTSTRAP_LOOP_PROBE_GATE', $result['stdout']); + $this->assertStringContainsString('bootstrap-selfhost-helloworld-probe.sh', $result['stdout']); + $this->assertStringContainsString('helloworld_m3_emit_native_entry.php', $result['stdout']); + $this->assertStringContainsString('2. M2 spine', $result['stdout']); + $this->assertStringContainsString('bootstrap-spine-count.php', $result['stdout']); + } + public function testDoctorJitProbeWhenLlvmPresent(): void { $repoRoot = dirname(__DIR__, 2);