diff --git a/Makefile b/Makefile index 5c404027440..b2e8d643fa4 100755 --- a/Makefile +++ b/Makefile @@ -106,7 +106,7 @@ test-docker-fast-jit-preflight: docker-build-22 JIT_PREFLIGHT_GATE=1 ./script/docker-ci-local.sh fast # VM smoke: examples/001-SimpleWeb with ?name=Test -.PHONY: web-smoke miniwebapp-gates miniwebapp-aot-bisect north-star1-verify +.PHONY: web-smoke miniwebapp-gates miniwebapp-aot-bisect north-star1-verify north-star2-verify web-smoke: ./script/web-smoke.sh @@ -118,6 +118,9 @@ miniwebapp-gates: north-star1-verify: ./script/north-star1-verify.sh +north-star2-verify: + ./script/north-star2-verify.sh + # Ordered #764 AOT PHPT ladder (issue #879; requires LLVM 9) miniwebapp-aot-bisect: ./script/miniwebapp-aot-bisect.sh diff --git a/bin/compile.php b/bin/compile.php index a269b22247d..c5705f49584 100755 --- a/bin/compile.php +++ b/bin/compile.php @@ -19,8 +19,16 @@ function run(string $filename, string $code, array $options): void { $normalized = '-' !== $filename ? str_replace('\\', '/', $filename) : ''; if ('' !== $normalized && str_contains($normalized, '/test/bootstrap-aot/')) { - // Bootstrap AOT fixtures require real JIT lowering; ignore inherited self-host stub env (#1086). - putenv('PHP_COMPILER_SELFHOST_AOT=0'); + // 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') + && ('1' === $m3Driver || 'true' === strtolower((string) $m3Driver)); + if ($m3EmitNative) { + putenv('PHP_COMPILER_SELFHOST_AOT=1'); + } else { + // Bootstrap AOT fixtures require real JIT lowering; ignore inherited self-host stub env (#1086). + putenv('PHP_COMPILER_SELFHOST_AOT=0'); + } } if ('-' !== $filename && str_contains($normalized, '/test/selfhost/')) { $selfhostAot = getenv('PHP_COMPILER_SELFHOST_AOT'); diff --git a/docs/bootstrap-m5-fast-path.md b/docs/bootstrap-m5-fast-path.md index 29f68b70e6b..409170db9c0 100644 --- a/docs/bootstrap-m5-fast-path.md +++ b/docs/bootstrap-m5-fast-path.md @@ -40,7 +40,7 @@ Supporting fixes from #1402: | `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 | -| `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_EMIT_HELPER_LINK=1` (#1983) | +| `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` (#1983, #1937) | | Native emit runtime | `BOOTSTRAP_M3_RUNTIME_COMPILE=1` + `PHP_COMPILER_M3_EMIT_MINIMAL=1` skips eager `loadJitCompileModuleFuncs` during smoke emit | | `runtime_ctor_smoke` | `php bin/compile.php -l test/bootstrap-aot/runtime_ctor_smoke.php`; int exit (#1514) | | `runtime_parse_compile_smoke` | `php bin/compile.php -l test/bootstrap-aot/runtime_parse_compile_smoke.php` | diff --git a/docs/bootstrap-selfhost.md b/docs/bootstrap-selfhost.md index 2a6f5828cad..3d5f96096b7 100644 --- a/docs/bootstrap-selfhost.md +++ b/docs/bootstrap-selfhost.md @@ -32,7 +32,7 @@ | 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 | `NORTH_STAR2_VERIFY_GATE=1 ./script/ci-fast.sh` | opt-in runs `make north-star2-verify` when `script/north-star2-verify.sh` exists ([#1928](https://github.com/PurHur/php-compiler/issues/1928)); skips until presenter script ([#1865](https://github.com/PurHur/php-compiler/issues/1865)) | +| North Star 2 presenter in fast CI | `NORTH_STAR2_VERIFY_GATE=1 ./script/ci-fast.sh` or `make north-star2-verify` | opt-in runs presenter ([#1928](https://github.com/PurHur/php-compiler/issues/1928), [#1865](https://github.com/PurHur/php-compiler/issues/1865)) | | 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/script/bootstrap-selfhost-compile-smoke-probe.sh b/script/bootstrap-selfhost-compile-smoke-probe.sh index 9cd4193885a..6999e0cc200 100755 --- a/script/bootstrap-selfhost-compile-smoke-probe.sh +++ b/script/bootstrap-selfhost-compile-smoke-probe.sh @@ -57,11 +57,12 @@ 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_EMIT_HELPER_LINK=1) - m3_link_mode="native emit TU (compile_smoke_m3_emit_native_entry.php, full Runtime JIT)" + # Self-host M3 allowlist — full Runtime JIT without stubs segfaults at link (#1937, #1983). + m3_link_env=(env PHP_COMPILER_SELFHOST_AOT=1 PHP_COMPILER_M3_COMPILE_DRIVER=1) + m3_link_mode="selfhost M3 emit TU (compile_smoke_m3_emit_native_entry.php)" else - m3_link_env=() - m3_link_mode="standard JIT emit TU" + 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 bin/compile.php -o build/selfhost-compile-smoke-emit "${EMIT_ENTRY}" >/dev/null 2>&1 diff --git a/script/north-star2-verify.sh b/script/north-star2-verify.sh new file mode 100755 index 00000000000..9e49876bd94 --- /dev/null +++ b/script/north-star2-verify.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# North Star 2 presenter verify — self-host M0–M4 ladder (issues #1865, #1492, #1056). +# +# ./script/north-star2-verify.sh +# make north-star2-verify +# +# Order: doctor --gates → inventory --check → wave gate → M0 link → M2 spine link/VM +# → optional LLVM tail: M3 partial probes + M4 loop dry-run. +# +# Exits non-zero on the first failing step. LLVM steps are skipped when LLVM 9 is absent +# (exit 0 for those skips). Use --require-llvm to fail when LLVM is missing. +set -euo pipefail + +# shellcheck source=ci-common.sh +source "$(dirname "$0")/ci-common.sh" + +ci_cd_repo + +REQUIRE_LLVM=0 +SKIP_LLVM_TAIL=0 +STRICT_M3=0 +while [[ $# -gt 0 ]]; do + case "$1" in + --require-llvm) REQUIRE_LLVM=1; shift ;; + --skip-llvm-tail) SKIP_LLVM_TAIL=1; shift ;; + --strict) STRICT_M3=1; shift ;; + -h|--help) + cat <<'EOF' +Usage: script/north-star2-verify.sh [--require-llvm] [--skip-llvm-tail] [--strict] + +Runs North Star 2 checks in order (issue #1865, epic #1492): + + 1. phpc doctor --gates (North Star 2 section) + 2. php script/bootstrap-inventory.php --check + 3. make bootstrap-wave-check (M0–M2 probes) + 4. M0 selfhost link + M2 lib spine native link + VM -r smoke (when LLVM 9 ready) + 5. M3 HelloWorld + compile-smoke partial probes; M4 loop --dry-run (LLVM tail) + +Options: + --require-llvm fail if LLVM 9 is missing (default: skip LLVM steps) + --skip-llvm-tail skip step 5 only (step 4 still runs when LLVM ready) + --strict run BOOTSTRAP_M3_*_STRICT=1 probes (may fail until #1493/#1937) + +Environment: script/ci-defaults.env. See docs/bootstrap-selfhost.md. + +Docker: + docker run --rm -v "$(pwd):/compiler" -w /compiler php-compiler:22.04-dev make north-star2-verify +EOF + exit 0 + ;; + *) echo "north-star2-verify: unknown argument: $1" >&2; exit 1 ;; + esac +done + +ns2_hint() { + local step="$1" + case "${step}" in + 1) echo "Next: composer install; phpc doctor — see #1752" ;; + 2) echo "Next: php script/bootstrap-inventory.php; fix inventory blockers (#765)" ;; + 3) echo "Next: ./script/bootstrap-wave-check.sh --fail-fast; selfhost-lint (#816)" ;; + 4) echo "Next: make bootstrap-selfhost-link; BOOTSTRAP_LIB_SPINE_SMOKE=1 make bootstrap-selfhost-lib-spine-smoke (#1492)" ;; + 5) echo "Next: BOOTSTRAP_M3_HELLOWORLD_STRICT=1 ./script/bootstrap-selfhost-helloworld-probe.sh (#1493)" ;; + *) echo "Next: see https://github.com/PurHur/php-compiler/issues/1492" ;; + esac +} + +ns2_run() { + local step="$1" + local label="$2" + shift 2 + echo + echo "=== north-star2-verify step ${step}: ${label} ===" + if "$@"; then + echo "north-star2-verify: step ${step} ok" + return 0 + fi + echo "north-star2-verify: step ${step} FAILED (${label})" >&2 + ns2_hint "${step}" >&2 + exit 1 +} + +if [[ ! -x "${_CI_REPO_ROOT}/phpc" ]]; then + echo "north-star2-verify: phpc wrapper missing; run composer install" >&2 + exit 1 +fi + +ci_prepare_test_runtime +ci_install_deps + +ns2_run 1 "phpc doctor --gates" "${_CI_REPO_ROOT}/phpc" doctor --gates +ns2_run 2 "bootstrap inventory --check" ci_ensure_generated_doc "${_CI_REPO_ROOT}/script/bootstrap-inventory.php" "${_CI_REPO_ROOT}/docs/bootstrap-inventory.md" + +if ! ci_llvm_ready; then + if [[ "${REQUIRE_LLVM}" -eq 1 ]]; then + echo "north-star2-verify: LLVM 9 required (--require-llvm) but not found at $(ci_llvm_dir)" >&2 + echo "Next: script/install-llvm9.sh or export PHP_COMPILER_LLVM_PATH" >&2 + exit 1 + fi + echo + echo "=== north-star2-verify: steps 3–5 skipped (LLVM 9 not available) ===" + echo "north-star2-verify: runnable steps passed (inventory + spine sync)" + exit 0 +fi + +ci_apply_llvm_memory_env + +ns2_run 3 "bootstrap wave gate" "${_CI_SCRIPT_DIR}/bootstrap-wave-check.sh" --fail-fast + +ns2_run 4a "M0 selfhost native link" "${_CI_SCRIPT_DIR}/bootstrap-selfhost-link.sh" +export BOOTSTRAP_LIB_SPINE_SMOKE=1 +ns2_run 4b "M2 lib spine native link" make bootstrap-selfhost-lib-spine-smoke +export BOOTSTRAP_LIB_SPINE_VM_SMOKE=1 +ns2_run 4c "M2 lib spine VM smoke" "${_CI_SCRIPT_DIR}/bootstrap-selfhost-lib-spine-vm-smoke.sh" + +if [[ "${SKIP_LLVM_TAIL}" -eq 1 ]]; then + echo + echo "north-star2-verify: step 5 skipped (--skip-llvm-tail)" + echo "north-star2-verify: all steps passed (#1492 presenter)" + exit 0 +fi + +echo +echo "=== north-star2-verify step 5: M3 partial probes + M4 loop dry-run ===" +if [[ "${STRICT_M3}" -eq 1 ]]; then + export BOOTSTRAP_M3_HELLOWORLD_STRICT_GATE=1 + export BOOTSTRAP_M3_COMPILE_SMOKE_STRICT_GATE=1 + ci_run_bootstrap_m3_strict || { + echo "north-star2-verify: M3 HelloWorld strict probe failed" >&2 + ns2_hint 5 >&2 + exit 1 + } + ci_run_bootstrap_m3_compile_smoke_strict || { + echo "north-star2-verify: M3 compile-smoke strict probe failed" >&2 + ns2_hint 5 >&2 + exit 1 + } +else + BOOTSTRAP_M3_LINK_COMPILE_DRIVER=1 \ + BOOTSTRAP_M3_COMPILE_DRIVER_REAL_LOWERING=1 \ + BOOTSTRAP_M3_RUNTIME_COMPILE=1 \ + "${_CI_SCRIPT_DIR}/bootstrap-selfhost-helloworld-probe.sh" || { + echo "north-star2-verify: M3 HelloWorld partial probe failed" >&2 + ns2_hint 5 >&2 + exit 1 + } + BOOTSTRAP_M3_LINK_COMPILE_DRIVER=1 \ + BOOTSTRAP_M3_COMPILE_DRIVER_REAL_LOWERING=1 \ + BOOTSTRAP_M3_RUNTIME_COMPILE=1 \ + "${_CI_SCRIPT_DIR}/bootstrap-selfhost-compile-smoke-probe.sh" || { + echo "north-star2-verify: M3 compile-smoke partial probe failed" >&2 + ns2_hint 5 >&2 + exit 1 + } +fi + +if [[ -x "${_CI_SCRIPT_DIR}/bootstrap-loop-probe.sh" ]]; then + BOOTSTRAP_LOOP_PROBE_GATE=1 "${_CI_SCRIPT_DIR}/bootstrap-loop-probe.sh" --dry-run || { + echo "north-star2-verify: M4 loop dry-run failed" >&2 + ns2_hint 5 >&2 + exit 1 + } +else + echo "north-star2-verify: bootstrap-loop-probe.sh missing — M4 dry-run skipped" +fi + +echo "north-star2-verify: step 5 ok" +echo +echo "north-star2-verify: all steps passed (#1492 presenter gate)" diff --git a/test/unit/BootstrapSelfhostCompileSmokeTest.php b/test/unit/BootstrapSelfhostCompileSmokeTest.php index 1e5962e6623..3fe4ff715a5 100644 --- a/test/unit/BootstrapSelfhostCompileSmokeTest.php +++ b/test/unit/BootstrapSelfhostCompileSmokeTest.php @@ -96,7 +96,7 @@ public function testCompileSmokeProbeScriptWiresNativeCompileDriver(): void $source = (string) file_get_contents($script); $this->assertStringContainsString('BOOTSTRAP_M3_LINK_COMPILE_DRIVER', $source); $this->assertStringContainsString('compile_smoke_m3_emit_native_entry.php', $source); - $this->assertStringContainsString('PHP_COMPILER_EMIT_HELPER_LINK', $source); + $this->assertStringContainsString('PHP_COMPILER_M3_COMPILE_DRIVER=1', $source); $this->assertStringContainsString('compile_smoke_m3_emit: compile OK', $source); } @@ -108,4 +108,11 @@ public function testCompileDriverLintPasses(): void exec($cmd, $lines, $exitCode); $this->assertSame(0, $exitCode, implode("\n", $lines)); } + + public function testCompilePhpPreservesSelfhostAotForM3NativeEmitEntry(): void + { + $compile = (string) file_get_contents(self::$root.'/bin/compile.php'); + $this->assertStringContainsString('compile_smoke_m3_emit_native_entry.php', $compile); + $this->assertStringContainsString('PHP_COMPILER_M3_COMPILE_DRIVER', $compile); + } } diff --git a/test/unit/CiScriptsTest.php b/test/unit/CiScriptsTest.php index 9893f09ad45..1e6383afd65 100644 --- a/test/unit/CiScriptsTest.php +++ b/test/unit/CiScriptsTest.php @@ -611,7 +611,7 @@ public function testCiFastHonorsNorthStar2VerifyGate(): void $this->assertStringContainsString('NORTH_STAR2_VERIFY_GATE', $common); $this->assertStringContainsString('NORTH_STAR2_VERIFY_GATE:-0', $common); $this->assertStringContainsString('north-star2-verify.sh', $common); - $this->assertStringContainsString('pending #1865', $common); + $this->assertFileExists(dirname(__DIR__, 2).'/script/north-star2-verify.sh'); $defaults = (string) file_get_contents(dirname(__DIR__, 2).'/script/ci-defaults.env'); $this->assertStringContainsString( diff --git a/test/unit/NorthStar2VerifyScriptTest.php b/test/unit/NorthStar2VerifyScriptTest.php new file mode 100644 index 00000000000..52939d30adb --- /dev/null +++ b/test/unit/NorthStar2VerifyScriptTest.php @@ -0,0 +1,75 @@ +assertFileExists($script); + $this->assertTrue(is_executable($script)); + + $descriptorSpec = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $proc = proc_open(['bash', $script, '--help'], $descriptorSpec, $pipes, $repoRoot); + $this->assertIsResource($proc); + fclose($pipes[0]); + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $exit = proc_close($proc); + + $combined = trim(($stdout !== false ? $stdout : '')."\n".($stderr !== false ? $stderr : '')); + $this->assertSame(0, $exit, $combined); + $this->assertStringContainsString('north-star2-verify', $combined); + $this->assertStringContainsString('phpc doctor --gates', $combined); + $this->assertStringContainsString('bootstrap-inventory.php', $combined); + $this->assertStringContainsString('bootstrap-wave-check', $combined); + $this->assertStringContainsString('bootstrap-wave-check', $combined); + $this->assertStringContainsString('HelloWorld', $combined); + $this->assertStringContainsString('#1492', $combined); + $this->assertStringContainsString('#1865', $combined); + } + + public function testNorthStar2VerifyScriptDocumentsSteps(): void + { + $body = (string) file_get_contents(dirname(__DIR__, 2).'/script/north-star2-verify.sh'); + $this->assertStringContainsString('doctor --gates', $body); + $this->assertStringContainsString('bootstrap-inventory.php', $body); + $this->assertStringContainsString('bootstrap-wave-check.sh', $body); + $this->assertStringContainsString('bootstrap-selfhost-link.sh', $body); + $this->assertStringContainsString('bootstrap-selfhost-lib-spine-vm-smoke', $body); + $this->assertStringContainsString('bootstrap-selfhost-helloworld-probe.sh', $body); + $this->assertStringContainsString('bootstrap-selfhost-compile-smoke-probe.sh', $body); + $this->assertStringContainsString('bootstrap-loop-probe.sh', $body); + $this->assertStringContainsString('ci_llvm_ready', $body); + $this->assertStringContainsString('--strict', $body); + } + + public function testMakefileDeclaresNorthStar2VerifyTarget(): void + { + $makefile = (string) file_get_contents(dirname(__DIR__, 2).'/Makefile'); + $this->assertStringContainsString('north-star2-verify:', $makefile); + $this->assertStringContainsString('script/north-star2-verify.sh', $makefile); + } + + public function testCompileSmokeProbeUsesSelfhostM3EmitLinkEnv(): void + { + $source = (string) file_get_contents(dirname(__DIR__, 2).'/script/bootstrap-selfhost-compile-smoke-probe.sh'); + $this->assertStringContainsString('PHP_COMPILER_SELFHOST_AOT=1 PHP_COMPILER_M3_COMPILE_DRIVER=1', $source); + $this->assertStringContainsString('compile_smoke_m3_emit_native_entry.php', $source); + } +}