Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
12 changes: 10 additions & 2 deletions bin/compile.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion docs/bootstrap-m5-fast-path.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
2 changes: 1 addition & 1 deletion docs/bootstrap-selfhost.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
Expand Down
9 changes: 5 additions & 4 deletions script/bootstrap-selfhost-compile-smoke-probe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
168 changes: 168 additions & 0 deletions script/north-star2-verify.sh
Original file line number Diff line number Diff line change
@@ -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)"
9 changes: 8 additions & 1 deletion test/unit/BootstrapSelfhostCompileSmokeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion test/unit/CiScriptsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
75 changes: 75 additions & 0 deletions test/unit/NorthStar2VerifyScriptTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

use PHPUnit\Framework\TestCase;

/**
* make north-star2-verify / script/north-star2-verify.sh (issue #1865).
*/
final class NorthStar2VerifyScriptTest extends TestCase
{
public function testNorthStar2VerifyScriptExistsAndPrintsHelp(): void
{
$repoRoot = dirname(__DIR__, 2);
$script = $repoRoot.'/script/north-star2-verify.sh';
$this->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);
}
}