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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Presenter script and troubleshooting: [`docs/GETTING-STARTED.md`](docs/GETTING-S
| **003-MiniWebApp** VM + AOT link | ✅ |
| **003** AOT execute (all routes) | 🚧 partial — home ✅ ([#1044](https://github.com/PurHur/php-compiler/issues/1044)) |
| Self-host **M0–M1** | ✅ |
| Self-host **M2** spine | 🚧 **579** / 586 units ([#1492](https://github.com/PurHur/php-compiler/issues/1492)) |
| Self-host **M2** spine | 🚧 **582** / 586 units ([#1492](https://github.com/PurHur/php-compiler/issues/1492)) |
| Full Zend PHP compatibility | ❌ — subset only (matrices in `docs/`, not on status site) |

MiniWebApp gates: [docs/miniwebapp-gates.md](docs/miniwebapp-gates.md) ([#472](https://github.com/PurHur/php-compiler/issues/472)). Docker image: `php-compiler:22.04-dev`.
Expand Down Expand Up @@ -141,7 +141,7 @@ Matrix details: [docs/local-ci-matrix.md](docs/local-ci-matrix.md).
|-----------|----------------|--------|
| **M0 — Bundled subset runs** | ~**109** literal `require_once` units in `test/selfhost/compiler_minimal/main.php` compile+link under AOT; native binary prints `compiler_minimal bundle OK` | ✅ ([#557](https://github.com/PurHur/php-compiler/issues/557), [#913](https://github.com/PurHur/php-compiler/issues/913)) |
| **M1 — Compiler-shaped bundle** | Same bundle **lints** as one translation unit; **compile-smoke** links a tiny fixture and runs AOT echo (`compiler smoke`); driver smoke bundles `bin/compile.php`-adjacent units | ✅ ([#1025](https://github.com/PurHur/php-compiler/issues/1025)) |
| **M2 — Full top-level `lib/` + spine** | All **14** top-level `lib/*.php` lint ✅; **`compiler_lib_spine_smoke`** (**579** / **586** inventory units) native link ✅; grow toward full `bin/vm.php` path | 🚧 ~99% of inventory |
| **M2 — Full top-level `lib/` + spine** | All **14** top-level `lib/*.php` lint ✅; **`compiler_lib_spine_smoke`** (**582** / **586** inventory units) native link ✅; grow toward full `bin/vm.php` path | 🚧 ~99% of inventory |
| **M3 — Native compiles PHP** | Self-host bundle links; HelloWorld AOT **runs** natively; **compile emit still Zend fallback** | 🚧 partial |
| **M4 — Bootstrap loop** | Native toolchain rebuilds the **next** compiler sources | ⬜ |
| **M5 — Full self-host** | Real `bin/vm.php` / `bin/compile.php` on full inventory; **no Zend bootstrap** | ⬜ **north star** ([#1492](https://github.com/PurHur/php-compiler/issues/1492)) |
Expand Down
2 changes: 1 addition & 1 deletion docs/bootstrap-selfhost.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
| Self-host probe in full CI | `./script/ci-local.sh` (LLVM tail) | ✅ default-on when LLVM 9 present; `BOOTSTRAP_SELFHOST_PROBE_GATE=0` to skip ([#829](https://github.com/PurHur/php-compiler/issues/829)) |
| Wave gate in full CI | `./script/ci-local.sh` (LLVM tail) | ✅ default-on when LLVM 9 present; `BOOTSTRAP_WAVE_CHECK=0` to skip; `./script/bootstrap-wave-check.sh --fail-fast` |
| Self-host native link | `./script/bootstrap-selfhost-link.sh` | ✅ `build/selfhost` prints `compiler_minimal bundle OK` ([#557](https://github.com/PurHur/php-compiler/issues/557), [#913](https://github.com/PurHur/php-compiler/issues/913)) |
| M2 lib spine smoke lint | `php bin/compile.php -l test/selfhost/compiler_lib_spine_smoke/main.php` | ✅ **579** units ([#1492](https://github.com/PurHur/php-compiler/issues/1492), [#1419](https://github.com/PurHur/php-compiler/issues/1419)
| M2 lib spine smoke lint | `php bin/compile.php -l test/selfhost/compiler_lib_spine_smoke/main.php` | ✅ **582** units ([#1492](https://github.com/PurHur/php-compiler/issues/1492), [#1419](https://github.com/PurHur/php-compiler/issues/1419)
| M2 lib spine smoke native run | `./script/bootstrap-selfhost-lib-spine-smoke-link.sh` or `BOOTSTRAP_LIB_SPINE_SMOKE=1 make bootstrap-selfhost-lib-spine-smoke` | ✅ `build/selfhost-lib-spine-smoke` prints `compiler_lib_spine_smoke bundle OK` |
| 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) |
Expand Down
4 changes: 1 addition & 3 deletions docs/fixtures/m2-spine-open-issues.json
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
[
{"number": 1543, "title": "M2 spine: add `ext/standard/VmSplAutoload.php` to compiler_lib_spine_smoke"}
]
[]
10 changes: 5 additions & 5 deletions docs/pages/development-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permalink: /development-status.html
| **Overall progress** | ~**50%** toward web-capable + self-hosting compiler (indicative) |
| **Wave 3 (May 2026)** | Language **10/13** · Stdlib **12/13** ([#1380](https://github.com/PurHur/php-compiler/issues/1380)) |
| **North Star 1** | Reference web app — VM ✅ · AOT link ✅ · AOT execute **partial** ([#1044](https://github.com/PurHur/php-compiler/issues/1044)) |
| **North Star 2** | Self-compile — M0–M1 ✅ · M2 spine **579/586** · M5 ⬜ ([#1492](https://github.com/PurHur/php-compiler/issues/1492)
| **North Star 2** | Self-compile — M0–M1 ✅ · M2 spine **582/586** · M5 ⬜ ([#1492](https://github.com/PurHur/php-compiler/issues/1492)
| **Not Zend parity** | Subset compiler (not full Zend PHP) |

[← Visual overview](index.html) · [Repository](https://github.com/PurHur/php-compiler)
Expand Down Expand Up @@ -51,7 +51,7 @@ Indicative composite toward a **web-capable, self-hosting** compiler (not line-c
| **Stdlib** | ~58% | Wave-3 batch ([#1367](https://github.com/PurHur/php-compiler/issues/1367)–[#1379](https://github.com/PurHur/php-compiler/issues/1379)): **13/13** closed on master |
| **Web AOT** (build, deploy) | ~65% | Project link ✅; home-route execute ✅; PATH_INFO / layout chain 🚧 |
| **Reference app** (MiniWebApp) | ~55% | VM ✅; AOT link ✅; AOT execute **partial** |
| **Self-host** (North Star 2, M0–M5) | ~98% | M0–M1 ✅; M2 spine **579/586**; M3 partial (native run ✅, Zend emit); M4–M5 ⬜ — [self-host-target.md](https://github.com/PurHur/php-compiler/blob/master/docs/self-host-target.md
| **Self-host** (North Star 2, M0–M5) | ~99% | M0–M1 ✅; M2 spine **582/586**; M3 partial (native run ✅, Zend emit); M4–M5 ⬜ — [self-host-target.md](https://github.com/PurHur/php-compiler/blob/master/docs/self-host-target.md

**Overall (indicative): ~52%** toward the stated north stars below.

Expand Down Expand Up @@ -200,7 +200,7 @@ Zend PHP still runs `bin/compile.php` during bootstrap. The output is a **curate
|-----------|---------|--------|
| **M0 — Bundled subset runs** | ~109 literal `require_once` units in `test/selfhost/compiler_minimal/main.php` → `build/selfhost` prints `compiler_minimal bundle OK` | ✅ [#557](https://github.com/PurHur/php-compiler/issues/557), [#913](https://github.com/PurHur/php-compiler/issues/913) |
| **M1 — Compiler-shaped bundle** | Bundled `Compiler.php` AOT lint; compile-smoke native link + AOT echo (`compiler smoke`); driver smoke toward `bin/compile.php` | ✅ [#1025](https://github.com/PurHur/php-compiler/issues/1025), [#1095](https://github.com/PurHur/php-compiler/issues/1095) |
| **M2 — Lib spine growth** | `compiler_lib_spine_smoke` bundle toward full `bin/vm.php` inventory (**586** files) | 🚧 **579** / 586 units (~99%; [#1492](https://github.com/PurHur/php-compiler/issues/1492)
| **M2 — Lib spine growth** | `compiler_lib_spine_smoke` bundle toward full `bin/vm.php` inventory (**586** files) | 🚧 **582** / 586 units (~99%; [#1492](https://github.com/PurHur/php-compiler/issues/1492)
| **M3 — Native compiles PHP** | Self-hosted bundle links; HelloWorld AOT **runs** natively; **emit still uses Zend** `bin/compile.php` fallback | 🚧 partial ([#1492](https://github.com/PurHur/php-compiler/issues/1492)) |
| **M4 — Bootstrap loop** | Native toolchain rebuilds the **next** compiler sources (same tree, new revision) | ⬜ |
| **M5 — Full self-host** | Real `bin/vm.php` / `bin/compile.php` path on full inventory; **no Zend bootstrap** | ⬜ **north star** ([#1492](https://github.com/PurHur/php-compiler/issues/1492)) |
Expand All @@ -210,7 +210,7 @@ Zend PHP still runs `bin/compile.php` during bootstrap. The output is a **curate
| Set | ~Files | Notes |
|-----|--------|-------|
| `compiler_minimal` bundle (M0) | **109** | Literal `require_once` closure |
| `compiler_lib_spine_smoke` (M2) | **579** | vm.php-path lib/ + ext/standard growth bundl
| `compiler_lib_spine_smoke` (M2) | **582** | vm.php-path lib/ + ext/standard growth bundl
| Top-level `lib/*.php` | **14** | Per-file AOT lint ✅ |
| Full vm.php inventory | **586** | Phase A inventory (`php script/bootstrap-inventory.php`) |

Expand Down Expand Up @@ -245,7 +245,7 @@ Details: [self-host-target.md](https://github.com/PurHur/php-compiler/blob/maste
| Native link + run | `./script/bootstrap-selfhost-link.sh` | ✅ M0 |
| Compile smoke link | `make bootstrap-selfhost-compile-smoke` | ✅ M1 |
| Compile smoke AOT echo | `make bootstrap-selfhost-compile-smoke-run` | ✅ M1 |
| M2 spine native link | `BOOTSTRAP_LIB_SPINE_SMOKE=1 make bootstrap-selfhost-lib-spine-smoke` | ✅ `compiler_lib_spine_smoke bundle OK` (**579** units
| M2 spine native link | `BOOTSTRAP_LIB_SPINE_SMOKE=1 make bootstrap-selfhost-lib-spine-smoke` | ✅ `compiler_lib_spine_smoke bundle OK` (**582** units
| M3 HelloWorld probe | `make bootstrap-selfhost-helloworld` | 🚧 partial — native **run** ✅; emit Zend fallback |
| Wave gate | `./script/bootstrap-wave-check.sh` | ✅ locally / Docker; GHA workflow disabled |
| Next includes probe | `php script/bootstrap-selfhost-next-includes.php` | 🚧 bundle growth |
Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap-wave3.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Regenerate capability truth: `php script/capability-matrix.php`, `php script/cap

Related merges outside this wave: `goto` ([#1228](https://github.com/PurHur/php-compiler/issues/1228) / [#1333](https://github.com/PurHur/php-compiler/pull/1333)), anonymous classes ([#1233](https://github.com/PurHur/php-compiler/issues/1233) / [#1386](https://github.com/PurHur/php-compiler/pull/1386)).

**M2 spine:** **579** / **586** inventory (`compiler_lib_spine_smoke` native link ✅; [#1492](https://github.com/PurHur/php-compiler/issues/1492), M2 batches [#1419](https://github.com/PurHur/php-compiler/issues/1419), [#1497](https://github.com/PurHur/php-compiler/issues/1497)). Target doc: [self-host-target.md](self-host-target.m
**M2 spine:** **582** / **586** inventory (`compiler_lib_spine_smoke` native link ✅; [#1492](https://github.com/PurHur/php-compiler/issues/1492), M2 batches [#1419](https://github.com/PurHur/php-compiler/issues/1419), [#1497](https://github.com/PurHur/php-compiler/issues/1497)). Target doc: [self-host-target.md](self-host-target.m

## Language (#1354–#1366)

Expand Down
10 changes: 5 additions & 5 deletions docs/self-host-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ That is **M5**. Everything below is the honest path from today’s bootstrap to
| Layer | Today | Target |
|-------|-------|--------|
| **Bootstrap driver** | Zend runs `php bin/compile.php` | Compiled `bin/compile.php` |
| **Bundle size** | **579** curated `require_once` in spine smoke (108 minimal overlap + **471** M2-only) | **586** inventory files |
| **Inventory coverage** | **~99%** of vm.php path in spine smoke (**579/586**) | **100%*
| **Bundle size** | **582** curated `require_once` in spine smoke (108 minimal overlap + **474** M2-only) | **586** inventory files |
| **Inventory coverage** | **~99%** of vm.php path in spine smoke (**582/586**) | **100%*
| **HelloWorld** | Native **run** ✅; **emit** still Zend fallback | Native compile + emit |
| **Bootstrap loop (M4)** | Gen-1 link + gen-2 Zend partial | Native gen-2 emit + full tree rebuild |
| **Vendor** | `composer install` + patches on host | Prelinked artifacts only |
Expand All @@ -61,7 +61,7 @@ That is **M5**. Everything below is the honest path from today’s bootstrap to
|-----------|----------------|--------|-----|
| **M0** | AOT can link a **small** honest `lib/` subset | ✅ | 100% |
| **M1** | Bundle is **compiler-shaped** (lint + compile-smoke) | ✅ | 100% |
| **M2** | Spine grows toward full `bin/vm.php` inventory | 🚧 **579/586** units | **~99%*
| **M2** | Spine grows toward full `bin/vm.php` inventory | 🚧 **582/586** units | **~99%*
| **M3** | Self-host binary **compiles external PHP** (HelloWorld) without Zend emit | 🚧 partial | **~45%** |
| **M4** | Self-host binary **rebuilds** the next compiler tree | ⬜ | 0% |
| **M5** | Full self-host; Zend retired from loop | ⬜ north star | 0% |
Expand All @@ -72,7 +72,7 @@ That is **M5**. Everything below is the honest path from today’s bootstrap to

| Indicator | Formula | May 2026 |
|-----------|---------|----------|
| **M2 spine progress** | `require_once` units in `compiler_lib_spine_smoke` ÷ Phase A inventory file count | **579 / 586 ≈ 99%*
| **M2 spine progress** | `require_once` units in `compiler_lib_spine_smoke` ÷ Phase A inventory file count | **582 / 586 ≈ 99%*
| **Public “Self-host” row** | Same M2 ratio until M3–M5 gates add weight ([`development-status.md`](pages/development-status.md)) | **~61%** |
| **Composite (internal)** | Milestone weights in table above (M0–M1 = 100%, M2 = spine %, M3–M5 = gate %) | **~54%** |

Expand Down Expand Up @@ -124,7 +124,7 @@ Parallel batches ([#1419](https://github.com/PurHur/php-compiler/issues/1419), [
| Entry | Units | Role |
|-------|------:|------|
| `test/selfhost/compiler_minimal/main.php` | **109** | M0 core |
| `test/selfhost/compiler_lib_spine_smoke/main.php` | **579** | M2 growth (ext/standard + Vm* spine batch
| `test/selfhost/compiler_lib_spine_smoke/main.php` | **582** | M2 growth (ext/standard + Vm* spine batch
| `test/selfhost/compiler_helloworld_smoke/` | — | M3 probe + compile driver |
| `test/selfhost/bootstrap_loop_smoke/` | — | M4 scaffold (gen-1→gen-2 loop probe; [#1498](https://github.com/PurHur/php-compiler/issues/1498)) |

Expand Down
78 changes: 39 additions & 39 deletions ext/standard/VmSplAutoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

use PHPCompiler\Func\PHP as PhpFunc;
use PHPCompiler\VM\Context;
use PHPCompiler\VM\ObjectEntry;
use PHPCompiler\VM\Variable;

/**
* spl_autoload_register() stack and callback invocation (issue #1369).
*
* Runner classes avoid Expr_Closure so this unit AOT-lints in the self-host spine (#1056, #1543).
*/
final class VmSplAutoload
{
Expand Down Expand Up @@ -40,7 +43,7 @@ public static function runStack(Context $ctx, string $className): bool
return true;
}
foreach ($ctx->splAutoloadCallbacks as $runner) {
$runner($ctx, $className);
$runner->run($ctx, $className);
if (isset($ctx->classes[$lc])) {
return true;
}
Expand All @@ -49,10 +52,7 @@ public static function runStack(Context $ctx, string $className): bool
return false;
}

/**
* @return callable
*/
private static function bindCallback(Context $ctx, Variable $callback)
private static function bindCallback(Context $ctx, Variable $callback): SplAutoloadRunner
{
$callback = $callback->resolveIndirect();
if (Variable::TYPE_STRING === $callback->type) {
Expand All @@ -71,35 +71,22 @@ private static function bindCallback(Context $ctx, Variable $callback)
);
}

/**
* @return callable
*/
private static function bindFunction(Context $ctx, string $name)
private static function bindFunction(Context $ctx, string $name): SplAutoloadRunner
{
$func = VmUserCall::resolveStringCallback($ctx, $name);

return static function (Context $ctx, string $className) use ($func): void {
self::invokeFunction($ctx, $func, $className);
};
return new SplAutoloadFunctionRunner($func);
}

/**
* @return callable
*/
private static function bindStaticName(Context $ctx, string $callable)
private static function bindStaticName(Context $ctx, string $callable): SplAutoloadRunner
{
[$className, $methodName] = explode('::', $callable, 2);
$func = self::resolveStaticMethod($ctx, $className, $methodName);

return static function (Context $ctx, string $className) use ($func): void {
self::invokeFunction($ctx, $func, $className);
};
return new SplAutoloadFunctionRunner($func);
}

/**
* @return callable
*/
private static function bindArrayCallable(Context $ctx, Variable $callable)
private static function bindArrayCallable(Context $ctx, Variable $callable): SplAutoloadRunner
{
$table = $callable->toArray();
$idx0 = new Variable(Variable::TYPE_INTEGER);
Expand All @@ -118,9 +105,7 @@ private static function bindArrayCallable(Context $ctx, Variable $callable)
if (Variable::TYPE_STRING === $target->type) {
$func = self::resolveStaticMethod($ctx, $target->toString(), $method);

return static function (Context $ctx, string $className) use ($func): void {
self::invokeFunction($ctx, $func, $className);
};
return new SplAutoloadFunctionRunner($func);
}
if (Variable::TYPE_OBJECT === $target->type) {
$class = $target->toObject()->class;
Expand All @@ -130,9 +115,7 @@ private static function bindArrayCallable(Context $ctx, Variable $callable)
}
$func = $class->methods[$methodLc];

return static function (Context $ctx, string $className) use ($func, $target): void {
self::invokeInstanceMethod($ctx, $func, $target->toObject(), $className);
};
return new SplAutoloadInstanceMethodRunner($func, $target->toObject());
}
throw new \LogicException(
'spl_autoload_register() array callback first element must be a class name string or object'
Expand Down Expand Up @@ -160,24 +143,41 @@ private static function resolveStaticMethod(Context $ctx, string $className, str

return $func;
}
}

interface SplAutoloadRunner
{
public function run(Context $ctx, string $className): void;
}

final class SplAutoloadFunctionRunner implements SplAutoloadRunner
{
public function __construct(private PhpFunc $func)
{
}

private static function invokeFunction(Context $ctx, PhpFunc $func, string $className): void
public function run(Context $ctx, string $className): void
{
$arg = new Variable();
$arg->string($className);
$ctx->runtime->vm->invokePhpFunction($func, $arg);
$ctx->runtime->vm->invokePhpFunction($this->func, $arg);
}
}

private static function invokeInstanceMethod(
Context $ctx,
PhpFunc $func,
\PHPCompiler\VM\ObjectEntry $receiver,
string $className
): void {
final class SplAutoloadInstanceMethodRunner implements SplAutoloadRunner
{
public function __construct(
private PhpFunc $func,
private ObjectEntry $receiver
) {
}

public function run(Context $ctx, string $className): void
{
$recv = new Variable();
$recv->object($receiver);
$recv->object($this->receiver);
$arg = new Variable();
$arg->string($className);
$ctx->runtime->vm->invokePhpFunction($func, $recv, $arg);
$ctx->runtime->vm->invokePhpFunction($this->func, $recv, $arg);
}
}
2 changes: 1 addition & 1 deletion lib/VM/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Context {
public array $enums = [];
/** @var list<callable(string): bool> */
public array $classAutoloaders = [];
/** @var list<callable(Context, string): void> spl_autoload_register() stack (#1369) */
/** @var list<\PHPCompiler\ext\standard\SplAutoloadRunner> spl_autoload_register() stack (#1369) */
public array $splAutoloadCallbacks = [];
/** @var array<string, true> */
private array $loadedCompileUnits = [];
Expand Down
Loading