diff --git a/Makefile b/Makefile index e1b83ff3198..dfa7add0a06 100755 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ test-docker-exec: .PHONY: test-docker-quick test-docker-quick: test-docker-fast -.PHONY: bootstrap-inventory bootstrap-spine-phpcfg-parse-check bootstrap-profile bootstrap-aot-lint bootstrap-aot-link bootstrap-aot-link-lib bootstrap-vendor-prelink-bundles bootstrap-vendor-objects bootstrap-selfhost-probe bootstrap-selfhost-link bootstrap-selfhost-compile-smoke bootstrap-selfhost-compile-smoke-strict bootstrap-selfhost-runtime-compile-smoke bootstrap-selfhost-runtime-compile-smoke-strict bootstrap-m3-emit-tu-execute bootstrap-selfhost-compiler-driver-smoke bootstrap-selfhost-compiler-unit-probe bootstrap-selfhost-compiler-unit-probe-strict bootstrap-selfhost-jit-unit-probe bootstrap-selfhost-vm-unit-probe bootstrap-selfhost-parser-unit-probe bootstrap-selfhost-types-unit-probe bootstrap-selfhost-lib-spine-smoke bootstrap-selfhost-lib-spine-vm-smoke bootstrap-selfhost-vm-driver-execute-probe bootstrap-selfhost-helloworld bootstrap-selfhost-helloworld-compile-bin bootstrap-selfhost-cli-driver-emit bootstrap-native-compile-driver-smoke bootstrap-loop-gen1-link bootstrap-loop-gen1-full-spine-emit bootstrap-loop-probe bootstrap-loop-probe-dry bootstrap-loop-probe-dry-run bootstrap-wave-check +.PHONY: bootstrap-inventory bootstrap-spine-phpcfg-parse-check bootstrap-profile bootstrap-aot-lint bootstrap-aot-link bootstrap-aot-link-lib bootstrap-vendor-prelink-bundles bootstrap-vendor-objects bootstrap-selfhost-probe bootstrap-selfhost-link bootstrap-selfhost-compile-smoke bootstrap-selfhost-compile-smoke-strict bootstrap-selfhost-runtime-compile-smoke bootstrap-selfhost-runtime-compile-smoke-strict bootstrap-m3-emit-tu-execute bootstrap-selfhost-compiler-driver-smoke bootstrap-selfhost-compiler-unit-probe bootstrap-selfhost-compiler-unit-probe-strict bootstrap-selfhost-jit-unit-probe bootstrap-selfhost-vm-unit-probe bootstrap-selfhost-parser-unit-probe bootstrap-selfhost-types-unit-probe bootstrap-selfhost-lib-spine-smoke bootstrap-selfhost-lib-spine-vm-smoke bootstrap-selfhost-vm-driver-execute-probe bootstrap-selfhost-helloworld bootstrap-selfhost-helloworld-compile-bin bootstrap-selfhost-cli-driver-emit bootstrap-native-compile-driver-smoke bootstrap-selfhost-driver-smoke bootstrap-loop-gen1-link bootstrap-loop-gen1-full-spine-emit bootstrap-loop-probe bootstrap-loop-probe-dry bootstrap-loop-probe-dry-run bootstrap-wave-check bootstrap-inventory: php script/bootstrap-inventory.php .PHONY: bootstrap-inventory-check bootstrap-inventory-regenerate @@ -333,6 +333,8 @@ bootstrap-selfhost-cli-driver-emit: ./script/bootstrap-selfhost-cli-driver-emit.sh bootstrap-native-compile-driver-smoke: ./script/bootstrap-native-compile-driver-smoke.sh +bootstrap-selfhost-driver-smoke: + ./script/bootstrap-selfhost-driver-smoke.sh bootstrap-loop-gen1-link: BOOTSTRAP_M4_LINK_COMPILE_DRIVER=1 \ BOOTSTRAP_M4_COMPILE_DRIVER_REAL_LOWERING=1 \ diff --git a/docs/bootstrap-selfhost.md b/docs/bootstrap-selfhost.md index 8d8bb434cac..bb0caccfe51 100644 --- a/docs/bootstrap-selfhost.md +++ b/docs/bootstrap-selfhost.md @@ -45,6 +45,7 @@ | 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) | | M5 CLI driver native emit | `make bootstrap-selfhost-cli-driver-emit` or `./script/bootstrap-selfhost-cli-driver-emit.sh` | ✅ `bin/vm.php` + `src/cli_driver.php` via M3 link-time sidecars ([#2699](https://github.com/PurHur/php-compiler/issues/2699)); requires `make bootstrap-selfhost-helloworld-compile-bin` | +| M5 bootstrap driver smoke | `make bootstrap-selfhost-driver-smoke` or `BOOTSTRAP_M5_DRIVER_GATE=1` in `ci-local.sh` llvm tail | ✅ native gen-2 compile + run (`compiler smoke`) via `build/selfhost-helloworld-compile` — no Zend on compile step ([#1521](https://github.com/PurHur/php-compiler/issues/1521)); opt-in CI gate default **off** | | M3 compile-smoke self-host probe | `./script/bootstrap-selfhost-compile-smoke-probe.sh` | 🚧 **partial** — bundle link ✅; compile-smoke AOT **run** native ✅; native emit via `compile_smoke_m3_emit_native_entry.php` blocked at emit-TU runtime init ([#1937](https://github.com/PurHur/php-compiler/issues/1937)); `emit_path=native` when `compile_smoke_m3_emit: compile OK`; Zend fallback (`emit_path=zend partial`) otherwise — see [bootstrap-m5-fast-path.md](bootstrap-m5-fast-path.md) | | M3 compile-smoke probe CI gate | `BOOTSTRAP_M3_COMPILE_SMOKE_PROBE_GATE=1 ./script/ci-local.sh` | default-on ([#1937](https://github.com/PurHur/php-compiler/issues/1937)); runs `bootstrap-selfhost-compile-smoke-probe.sh` (partial Zend emit + native run) | | M3 compile-smoke strict probe | `BOOTSTRAP_M3_COMPILE_SMOKE_STRICT=1 ./script/bootstrap-selfhost-compile-smoke-probe.sh` or `make bootstrap-selfhost-compile-smoke-strict` | ✅ `emit_path=native` — strict auto-enables compile-driver link env ([#2610](https://github.com/PurHur/php-compiler/issues/2610); refuses `emit_path=zend_fallback_would_be_used`) | diff --git a/patches/php-types-fromdecl-junk-fragments.patch b/patches/php-types-fromdecl-junk-fragments.patch new file mode 100644 index 00000000000..79eaf150176 --- /dev/null +++ b/patches/php-types-fromdecl-junk-fragments.patch @@ -0,0 +1,17 @@ +diff --git vendor/ircmaxell/php-types/lib/PHPTypes/Type.php vendor/ircmaxell/php-types/lib/PHPTypes/Type.php +index 1111111..2222222 100644 +--- vendor/ircmaxell/php-types/lib/PHPTypes/Type.php ++++ vendor/ircmaxell/php-types/lib/PHPTypes/Type.php +@@ -371,6 +371,12 @@ class Type + ]))->simplify(); + } + $decl = self::stripTrailingDocText($decl); ++ $trimmedDecl = trim($decl); ++ // Malformed phpdoc fragments in vendor trees (north-star5 prelink; #2743, #2745). ++ if ('' === $trimmedDecl || '*' === $trimmedDecl || '*/' === $trimmedDecl ++ || str_starts_with($trimmedDecl, '*/')) { ++ return self::mixed(); ++ } + if (preg_match('/^array\{/i', $decl)) { + return new self(self::TYPE_ARRAY); + } diff --git a/script/apply-patches.sh b/script/apply-patches.sh index 9c6a4a1afe2..2bdf134a055 100755 --- a/script/apply-patches.sh +++ b/script/apply-patches.sh @@ -54,6 +54,9 @@ patch_already_applied() { php-types-docblock-trailing-text.patch) grep -q "stripTrailingDocText" "$ROOT/vendor/ircmaxell/php-types/lib/PHPTypes/Type.php" 2>/dev/null ;; + php-types-fromdecl-junk-fragments.patch) + grep -q "str_starts_with(\$trimmedDecl, '\\*/')" "$ROOT/vendor/ircmaxell/php-types/lib/PHPTypes/Type.php" 2>/dev/null + ;; php-types-ns-func-call.patch) grep -q 'function resolveOp_Expr_NsFuncCall' "$ROOT/vendor/ircmaxell/php-types/lib/PHPTypes/TypeReconstructor.php" 2>/dev/null ;; @@ -443,6 +446,7 @@ if [[ -d "$ROOT/vendor/ircmaxell/php-types" ]]; then apply_patch "$PATCH_DIR/php-types-generics-fallback.patch" apply_patch "$PATCH_DIR/php-types-generics-list-array.patch" apply_patch "$PATCH_DIR/php-types-docblock-trailing-text.patch" + apply_patch "$PATCH_DIR/php-types-fromdecl-junk-fragments.patch" apply_patch "$PATCH_DIR/php-types-ns-func-call.patch" apply_patch "$PATCH_DIR/php-types-arrow-function.patch" apply_patch "$PATCH_DIR/php-types-magic-script-const.patch" diff --git a/script/bootstrap-selfhost-driver-smoke.sh b/script/bootstrap-selfhost-driver-smoke.sh new file mode 100755 index 00000000000..ffb82feb776 --- /dev/null +++ b/script/bootstrap-selfhost-driver-smoke.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# M5 bootstrap driver smoke (#1521): native helloworld compile driver compiles + runs a +# bootstrap fixture without Zend on the compile step (gen-1 emit → gen-2 run). +# +# Stage 0: Zend links build/selfhost-helloworld-compile (M3 compile-driver TU). +# Stage 1: Native driver emits test/bootstrap-aot/compiler_smoke_standalone.php → gen-2 binary. +# Stage 2: Run gen-2; expect "compiler smoke" (same as M4 loop gen-2 slice). +# +# Optional slice: compiled bin/compile.php -l echo_hello (no Zend on lint) when present. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +# shellcheck source=php-env.sh +source "${ROOT}/script/php-env.sh" +ci_apply_llvm_memory_env + +DRIVER="${ROOT}/build/selfhost-helloworld-compile" +COMPILED_COMPILE="${ROOT}/build/bin-compile-aot" +SOURCE="${ROOT}/test/bootstrap-aot/compiler_smoke_standalone.php" +GEN2_OUT="${ROOT}/build/selfhost-driver-smoke-gen2" +EXPECTED_STDOUT="compiler smoke" + +if [[ -z "${PHP_COMPILER_LLVM_PATH:-}" || ! -f "${PHP_COMPILER_LLVM_PATH}/libLLVM-9.so.1" ]]; then + echo "bootstrap-selfhost-driver-smoke: LLVM 9 not found (skip)" >&2 + exit 2 +fi + +if [[ ! -f "${SOURCE}" ]]; then + echo "bootstrap-selfhost-driver-smoke: missing ${SOURCE}" >&2 + exit 1 +fi + +mkdir -p "${ROOT}/build" +rm -f "${GEN2_OUT}" + +echo "bootstrap-selfhost-driver-smoke: stage 0 — link native M3 compile driver (Zend link only)" +export PHP_COMPILER_M3_SOURCE="${ROOT}/bin/compile.php" +export PHP_COMPILER_M3_OUT="${COMPILED_COMPILE}" +if ! ./script/bootstrap-selfhost-helloworld-compile-bin.sh >/dev/null; then + echo "bootstrap-selfhost-driver-smoke: helloworld compile-driver link failed" >&2 + exit 1 +fi + +if [[ ! -x "${DRIVER}" ]]; then + echo "bootstrap-selfhost-driver-smoke: missing executable ${DRIVER}" >&2 + exit 1 +fi + +echo "bootstrap-selfhost-driver-smoke: stage 1 — native gen-2 compile (no Zend on compile)" +set +e +compile_out="$( + env PHP_COMPILER_M3_COMPILE_MODE=compile \ + PHP_COMPILER_M3_SOURCE="${SOURCE}" \ + PHP_COMPILER_M3_OUT="${GEN2_OUT}" \ + "${DRIVER}" 2>&1 +)" +compile_code=$? +set -e +printf '%s\n' "${compile_out}" + +if [[ "${compile_code}" -ne 0 ]]; then + echo "bootstrap-selfhost-driver-smoke: native compile failed (exit ${compile_code})" >&2 + exit 1 +fi + +if ! grep -q 'helloworld_compile_smoke: compile OK' <<< "${compile_out}"; then + echo "bootstrap-selfhost-driver-smoke: expected helloworld_compile_smoke: compile OK" >&2 + exit 1 +fi + +if [[ ! -x "${GEN2_OUT}" ]]; then + echo "bootstrap-selfhost-driver-smoke: missing gen-2 binary ${GEN2_OUT}" >&2 + exit 1 +fi + +echo "bootstrap-selfhost-driver-smoke: stage 2 — run gen-2 binary" +set +e +run_out="$("${GEN2_OUT}" 2>&1)" +run_code=$? +set -e +printf '%s\n' "${run_out}" + +if [[ "${run_code}" -ne 0 ]]; then + echo "bootstrap-selfhost-driver-smoke: gen-2 run failed (exit ${run_code})" >&2 + exit 1 +fi + +if ! grep -qx "${EXPECTED_STDOUT}" <<< "${run_out}"; then + echo "bootstrap-selfhost-driver-smoke: unexpected stdout (want ${EXPECTED_STDOUT})" >&2 + exit 1 +fi + +if [[ -x "${COMPILED_COMPILE}" ]]; then + echo "bootstrap-selfhost-driver-smoke: stage 3 — compiled bin/compile.php lint (no Zend)" + set +e + lint_out="$("${COMPILED_COMPILE}" -l "${ROOT}/test/bootstrap-aot/echo_hello.php" 2>&1)" + lint_code=$? + set -e + if [[ "${lint_code}" -ne 0 ]]; then + printf '%s\n' "${lint_out}" >&2 + echo "bootstrap-selfhost-driver-smoke: compiled driver lint failed (exit ${lint_code})" >&2 + exit 1 + fi +fi + +echo "bootstrap-selfhost-driver-smoke: emit_path=native (gen-2 compile + run)" +echo "bootstrap-selfhost-driver-smoke: OK ${DRIVER} -> ${GEN2_OUT}" diff --git a/script/bootstrap-vendor-objects.php b/script/bootstrap-vendor-objects.php index 36c496649ff..5004248557c 100644 --- a/script/bootstrap-vendor-objects.php +++ b/script/bootstrap-vendor-objects.php @@ -159,7 +159,8 @@ @unlink($buildBase.'.o'); @unlink($objectAbs); - $cmd = escapeshellarg($phpBin).' '.escapeshellarg($compileBin) + $cmd = 'PHP_COMPILER_VENDOR_PRELINK=1 PHP_COMPILER_SELFHOST_AOT=0 PHP_COMPILER_KEEP_OBJECT_FILE=1 ' + .escapeshellarg($phpBin).' '.escapeshellarg($compileBin) .' -o '.escapeshellarg($buildBase).' '.escapeshellarg($bundleAbs).' 2>&1'; $output = []; exec($cmd, $output, $code); diff --git a/script/ci-common.sh b/script/ci-common.sh index 8ae343f0d56..4c45c7a9506 100644 --- a/script/ci-common.sh +++ b/script/ci-common.sh @@ -859,6 +859,19 @@ ci_run_bootstrap_m3_strict() { } # M4 bootstrap-loop dry-run probe in ci-local LLVM tail (issue #2058); after M3 strict gates. +# M5 bootstrap driver: native helloworld compile driver emits + runs gen-2 without Zend on compile (#1521). +ci_run_bootstrap_m5_driver_smoke() { + if [[ "${BOOTSTRAP_M5_DRIVER_GATE:-0}" != "1" ]]; then + return 0 + fi + if ! ci_llvm_ready; then + echo "bootstrap-selfhost-driver-smoke: skipped (LLVM 9 not available)" + return 0 + fi + echo "bootstrap-selfhost-driver-smoke (BOOTSTRAP_M5_DRIVER_GATE=1, issue #1521)..." + "$_CI_SCRIPT_DIR/bootstrap-selfhost-driver-smoke.sh" +} + ci_run_bootstrap_m4_loop_probe() { if [[ "${BOOTSTRAP_M4_LOOP_PROBE:-0}" != "1" ]]; then return 0 diff --git a/script/ci-defaults.env b/script/ci-defaults.env index 10f559c27be..044ce57c5ea 100755 --- a/script/ci-defaults.env +++ b/script/ci-defaults.env @@ -92,6 +92,7 @@ export BOOTSTRAP_M3_STRICT_SYNC_GATE="${BOOTSTRAP_M3_STRICT_SYNC_GATE:-0}" # op export M3_ALLOWLIST_SYNC_GATE="${M3_ALLOWLIST_SYNC_GATE:-1}" # default on (#1905); opt-out M3_ALLOWLIST_SYNC_GATE=0 for bulk symbol PRs export BOOTSTRAP_M5_DOC_SYNC_GATE="${BOOTSTRAP_M5_DOC_SYNC_GATE:-1}" # default on (#1984); opt-out BOOTSTRAP_M5_DOC_SYNC_GATE=0 for doc-only iteration export BOOTSTRAP_VENDOR_INVENTORY_SYNC_GATE="${BOOTSTRAP_VENDOR_INVENTORY_SYNC_GATE:-1}" # default on (#2040, #2030); opt-out BOOTSTRAP_VENDOR_INVENTORY_SYNC_GATE=0 for vendor-only iteration +export BOOTSTRAP_M5_DRIVER_GATE="${BOOTSTRAP_M5_DRIVER_GATE:-0}" # opt-in (#1521); native gen-2 compile+run via helloworld compile driver (bootstrap-selfhost-driver-smoke) export BOOTSTRAP_VENDOR_PRELINK_GATE="${BOOTSTRAP_VENDOR_PRELINK_GATE:-0}" # opt-in (#1416); make bootstrap-vendor-objects when M3 vendor JIT stable export BOOTSTRAP_VENDOR_PRELINK_SYNC_GATE="${BOOTSTRAP_VENDOR_PRELINK_SYNC_GATE:-1}" # default on; ci-fast checks bundle/manifest sync (#1416) export BOOTSTRAP_INVENTORY_LINT_SYNC_GATE="${BOOTSTRAP_INVENTORY_LINT_SYNC_GATE:-0}" # opt-in (#2210); flip default-on after inventory lint snapshot stable diff --git a/script/ci-local.sh b/script/ci-local.sh index db2e8c33e2b..cc490f45b69 100755 --- a/script/ci-local.sh +++ b/script/ci-local.sh @@ -89,6 +89,7 @@ if ci_llvm_ready; then ci_run_bootstrap_m3_compile_smoke_strict ci_run_bootstrap_runtime_compile_smoke_strict ci_run_bootstrap_m4_loop_probe + ci_run_bootstrap_m5_driver_smoke echo "PHPUnit: JIT compliance (@group jit)..." LLVM_JUNIT="$(mktemp "${TMPDIR:-/tmp}/llvm-jit-junit.XXXXXX.xml")" diff --git a/test/unit/BootstrapSelfhostDriverSmokeTest.php b/test/unit/BootstrapSelfhostDriverSmokeTest.php new file mode 100644 index 00000000000..cafae913311 --- /dev/null +++ b/test/unit/BootstrapSelfhostDriverSmokeTest.php @@ -0,0 +1,53 @@ +assertStringContainsString('bootstrap-selfhost-driver-smoke:', $script); + $this->assertStringContainsString('compiler_smoke_standalone.php', $script); + $this->assertStringContainsString('PHP_COMPILER_M3_COMPILE_MODE=compile', $script); + $this->assertStringContainsString('emit_path=native', $script); + $this->assertStringContainsString('compiler smoke', $script); + } + + public function testMakefileExposesDriverSmokeTarget(): void + { + $makefile = (string) file_get_contents(self::$root.'/Makefile'); + $this->assertStringContainsString('bootstrap-selfhost-driver-smoke:', $makefile); + $this->assertStringContainsString('./script/bootstrap-selfhost-driver-smoke.sh', $makefile); + } + + public function testCiDefaultsDefinesM5DriverGate(): void + { + $defaults = (string) file_get_contents(self::$root.'/script/ci-defaults.env'); + $this->assertStringContainsString('BOOTSTRAP_M5_DRIVER_GATE', $defaults); + } + + public function testCiCommonWiresM5DriverSmoke(): void + { + $common = (string) file_get_contents(self::$root.'/script/ci-common.sh'); + $this->assertStringContainsString('ci_run_bootstrap_m5_driver_smoke', $common); + $this->assertStringContainsString('bootstrap-selfhost-driver-smoke.sh', $common); + } + + public function testFromDeclJunkFragmentsPatchRegistered(): void + { + $apply = (string) file_get_contents(self::$root.'/script/apply-patches.sh'); + $this->assertStringContainsString('php-types-fromdecl-junk-fragments.patch', $apply); + } +} diff --git a/test/unit/PhpTypesFromDeclJunkFragmentsPatchTest.php b/test/unit/PhpTypesFromDeclJunkFragmentsPatchTest.php new file mode 100644 index 00000000000..7b1976b4385 --- /dev/null +++ b/test/unit/PhpTypesFromDeclJunkFragmentsPatchTest.php @@ -0,0 +1,31 @@ +markTestSkipped('vendor/ircmaxell/php-types not installed'); + } + + foreach (['*/', '*'] as $decl) { + $type = Type::fromDecl($decl); + $this->assertInstanceOf(Type::class, $type); + $this->assertNotSame(Type::TYPE_OBJECT, $type->type, 'decl='.$decl); + } + + $this->expectException(\RuntimeException::class); + Type::fromDecl(''); + + $trailing = Type::fromDecl('PHPTypes\\Type The type'); + $this->assertSame(Type::TYPE_OBJECT, $trailing->type); + $this->assertSame('PHPTypes\\Type', $trailing->name); + } +}