- Document recommended pipeline from PHP Parser AST to backend-specific code generation - Define Bound AST structure preserving PHP syntax shape with resolved semantic information - Specify PHP Semantic IR close to PHP runtime semantics with suggested instruction families - Outline Runtime ABI operations for consistentpull/16/head
parent
441091fbc5
commit
fc5ad87f55
1 changed files with 307 additions and 0 deletions
@ -0,0 +1,307 @@ |
|||||||
|
# Backend-Neutral IR for C++, WASM and JavaScript |
||||||
|
|
||||||
|
TypePHP currently lowers PHP AST directly to C++ code. This works for the |
||||||
|
current C++/Zend backend, but it couples PHP semantic analysis, temporary value |
||||||
|
management, reference behavior, runtime calls and C++ code formatting in one |
||||||
|
stage. |
||||||
|
|
||||||
|
If TypePHP needs to support targets such as WASM or JavaScript, the compiler |
||||||
|
should introduce a backend-neutral semantic IR before backend-specific code |
||||||
|
generation. |
||||||
|
|
||||||
|
## Recommended Pipeline |
||||||
|
|
||||||
|
```text |
||||||
|
PHP Parser AST |
||||||
|
-> Bound AST |
||||||
|
-> PHP Semantic IR |
||||||
|
-> Backend Lowering |
||||||
|
-> C++/Zend Runtime IR -> C++ |
||||||
|
-> JavaScript Runtime IR -> JS |
||||||
|
-> WASM Runtime IR -> wasm/wat/import calls |
||||||
|
``` |
||||||
|
|
||||||
|
The important rule is that the semantic IR must describe PHP behavior, not C++ |
||||||
|
implementation details. |
||||||
|
|
||||||
|
It should not contain: |
||||||
|
|
||||||
|
- `php::Var` |
||||||
|
- `php::Ref` |
||||||
|
- `php::Array` |
||||||
|
- `zend_*` |
||||||
|
- `zval*` |
||||||
|
- C++ expression fragments |
||||||
|
|
||||||
|
Those belong to the C++ backend only. |
||||||
|
|
||||||
|
## Bound AST |
||||||
|
|
||||||
|
The Bound AST should preserve PHP syntax shape while attaching resolved semantic |
||||||
|
information. |
||||||
|
|
||||||
|
It should contain: |
||||||
|
|
||||||
|
- Resolved names for functions, classes, methods, properties and constants. |
||||||
|
- Scope information for locals, globals, statics and closure captures. |
||||||
|
- Declared and inferred types. |
||||||
|
- By-reference information for parameters, returns and assignments. |
||||||
|
- Call target classification: AOT function, native call, internal function, |
||||||
|
dynamic call or unsupported target. |
||||||
|
- L-value and R-value classification for reads, writes, `isset`, `unset` and |
||||||
|
reference access. |
||||||
|
- Target capability diagnostics, such as whether `eval`, `include`, resources or |
||||||
|
reflection are allowed for the selected backend. |
||||||
|
|
||||||
|
Most unsupported syntax should be rejected in this phase or during IR |
||||||
|
validation, not during backend code generation. |
||||||
|
|
||||||
|
## PHP Semantic IR |
||||||
|
|
||||||
|
The semantic IR should be close to PHP runtime semantics. It should not be too |
||||||
|
low-level at first, because PHP references, arrays, dynamic calls and exception |
||||||
|
flow are difficult to recover after early lowering. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
```text |
||||||
|
%value = load_local $value |
||||||
|
%class = load_local $name |
||||||
|
%ok = instanceof %value, %class |
||||||
|
return %ok |
||||||
|
``` |
||||||
|
|
||||||
|
For a more complex expression: |
||||||
|
|
||||||
|
```text |
||||||
|
%services = load_prop this, "services" |
||||||
|
%closure = make_closure closure#1 captures [$name] |
||||||
|
%filtered = call_func "array_filter", [%services, %closure, ARRAY_FILTER_USE_BOTH] |
||||||
|
%values = call_func "array_values", [%filtered] |
||||||
|
%result = coalesce_dim %values, 0, null |
||||||
|
return_checked %result, ?object |
||||||
|
``` |
||||||
|
|
||||||
|
Suggested instruction families: |
||||||
|
|
||||||
|
- `LoadLocal`, `StoreLocal` |
||||||
|
- `LoadGlobal`, `StoreGlobal` |
||||||
|
- `LoadProp`, `StoreProp`, `PropRef` |
||||||
|
- `LoadDim`, `StoreDim`, `DimRef` |
||||||
|
- `CallFunc`, `CallMethod`, `CallStatic` |
||||||
|
- `NativeCall` |
||||||
|
- `MakeClosure` |
||||||
|
- `InstanceOf` |
||||||
|
- `Isset`, `Empty`, `Coalesce` |
||||||
|
- `Cast`, `TypeCheck`, `TypeAssert` |
||||||
|
- `ToReferenceExact` |
||||||
|
- `Return`, `ReturnRef`, `ReturnChecked` |
||||||
|
- `Throw`, `TryCatchFinally` |
||||||
|
- `Branch`, `Jump`, `Label` |
||||||
|
- `ForeachInit`, `ForeachNext` |
||||||
|
- Explicit unsupported nodes for `eval`, `include`, `resource` operations and |
||||||
|
other target-dependent features. |
||||||
|
|
||||||
|
## Runtime ABI |
||||||
|
|
||||||
|
Multiple backends should implement the same PHP semantic runtime ABI. |
||||||
|
|
||||||
|
Example ABI operations: |
||||||
|
|
||||||
|
```text |
||||||
|
rt_call_function(name, args) |
||||||
|
rt_call_method(receiver, method, args) |
||||||
|
rt_call_static(class, method, args) |
||||||
|
rt_get_property(object, name) |
||||||
|
rt_set_property(object, name, value) |
||||||
|
rt_get_property_ref(object, name) |
||||||
|
rt_get_dim(value, key) |
||||||
|
rt_set_dim(value, key, value) |
||||||
|
rt_get_dim_ref(value, key) |
||||||
|
rt_to_bool(value) |
||||||
|
rt_instanceof(value, class_name) |
||||||
|
rt_make_reference(value) |
||||||
|
rt_deref(value) |
||||||
|
rt_throw(value) |
||||||
|
``` |
||||||
|
|
||||||
|
The C++ backend can map these operations to the existing `php::Var`, `php::Ref` |
||||||
|
and Zend-based runtime. JavaScript and WASM can map them to their own runtime |
||||||
|
representations. |
||||||
|
|
||||||
|
## Value Model |
||||||
|
|
||||||
|
The IR should use abstract value identities instead of physical backend types. |
||||||
|
|
||||||
|
Useful concepts: |
||||||
|
|
||||||
|
- `ValueId` |
||||||
|
- `RefId` |
||||||
|
- `ArrayId` |
||||||
|
- `ObjectId` |
||||||
|
- `ClassId` |
||||||
|
- `FunctionId` |
||||||
|
- `CallableId` |
||||||
|
- `ExceptionId` |
||||||
|
|
||||||
|
The backend decides how these are represented. For example: |
||||||
|
|
||||||
|
- C++/Zend can use `php::Var`, `php::Ref`, `zval*` and `zend_class_entry*`. |
||||||
|
- JavaScript can use tagged JS objects and boxed references. |
||||||
|
- WASM can use handles and runtime imports. |
||||||
|
|
||||||
|
## Semantics That Must Stay Explicit |
||||||
|
|
||||||
|
The following PHP semantics must remain explicit in the Bound AST or semantic |
||||||
|
IR. They should not be lowered too early: |
||||||
|
|
||||||
|
- `isset` is not the same as a normal read. |
||||||
|
- `empty` is not just `!toBool(value)`. |
||||||
|
- `??` uses `isset` semantics, not only null comparison. |
||||||
|
- `foreach` by value and by reference are different. |
||||||
|
- Return by reference must be represented as `ReturnRef`. |
||||||
|
- Call-by-reference arguments must preserve reference slots. |
||||||
|
- Array element and object property references must use explicit `DimRef` and |
||||||
|
`PropRef` operations. |
||||||
|
- PHP arrays are ordered maps, not native JS arrays or C++ vectors. |
||||||
|
- Object property visibility and typed property checks are runtime semantics. |
||||||
|
- Dynamic calls must preserve symbol lookup behavior. |
||||||
|
- `eval`, `include`, resources, reflection and extension-dependent behavior must |
||||||
|
be explicit so each backend can accept, reject or emulate them. |
||||||
|
|
||||||
|
## Target Capability Matrix |
||||||
|
|
||||||
|
Each backend should declare a feature set. The compiler should validate IR |
||||||
|
against the selected target before code generation. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
```text |
||||||
|
Feature C++/Zend JavaScript WASM |
||||||
|
function call yes yes yes |
||||||
|
class/object yes partial partial |
||||||
|
array COW Zend runtime runtime |
||||||
|
reference Zend boxed refs boxed refs |
||||||
|
internal PHP funcs yes shim/import import |
||||||
|
resource yes no/partial no |
||||||
|
eval/include partial no/partial no |
||||||
|
reflection yes partial no/partial |
||||||
|
extension classes yes shim no |
||||||
|
``` |
||||||
|
|
||||||
|
This should be represented in code as target capability metadata, for example: |
||||||
|
|
||||||
|
```text |
||||||
|
target: cpp-zend | js | wasm |
||||||
|
``` |
||||||
|
|
||||||
|
Unsupported behavior should produce deterministic diagnostics in the validation |
||||||
|
phase. |
||||||
|
|
||||||
|
## JavaScript Backend Notes |
||||||
|
|
||||||
|
JavaScript is a practical first non-C++ backend because it is dynamic and can |
||||||
|
host a PHP-like runtime in user space. |
||||||
|
|
||||||
|
Main runtime requirements: |
||||||
|
|
||||||
|
- PHP array as ordered map. |
||||||
|
- PHP references as boxes. |
||||||
|
- PHP object/class model with visibility checks. |
||||||
|
- PHP weak typing and cast helpers. |
||||||
|
- PHP exception model. |
||||||
|
- Closure capture semantics. |
||||||
|
- Dynamic function, method and static method calls. |
||||||
|
- Binary-safe string representation, because JS strings are not byte strings. |
||||||
|
- Integer handling policy, because JS numbers are doubles. |
||||||
|
|
||||||
|
The initial JS backend should target a strict PHP subset before trying to support |
||||||
|
resources, reflection, extensions, `eval` or real stream behavior. |
||||||
|
|
||||||
|
## WASM Backend Notes |
||||||
|
|
||||||
|
WASM is less suitable for directly expressing dynamic PHP semantics. It should be |
||||||
|
planned as a runtime-backed target. |
||||||
|
|
||||||
|
Possible routes: |
||||||
|
|
||||||
|
1. PHP -> C++ -> WASM |
||||||
|
|
||||||
|
This is the shortest path and reuses the current C++ backend, but Zend |
||||||
|
embedding is heavy and many OS/resource features are limited in WASM |
||||||
|
environments. |
||||||
|
|
||||||
|
2. PHP Semantic IR -> WASM + runtime imports |
||||||
|
|
||||||
|
This is cleaner but more expensive. The generated WASM should call imported |
||||||
|
runtime functions for dynamic PHP operations such as arrays, objects, |
||||||
|
references and dynamic calls. |
||||||
|
|
||||||
|
The second route is preferable long term. The first route may be useful for an |
||||||
|
early proof of concept. |
||||||
|
|
||||||
|
## Migration Strategy |
||||||
|
|
||||||
|
Do not rewrite the whole compiler at once. |
||||||
|
|
||||||
|
Recommended steps: |
||||||
|
|
||||||
|
1. Introduce target capability metadata. |
||||||
|
2. Add Bound AST metadata without changing existing C++ output. |
||||||
|
3. Add semantic IR data structures and `--dump-ir`. |
||||||
|
4. Lower a small expression subset to IR first. |
||||||
|
5. Implement `Semantic IR -> C++` for that subset. |
||||||
|
6. Keep mixed mode: unsupported IR nodes can temporarily fall back to the old |
||||||
|
direct AST-to-C++ path. |
||||||
|
7. Gradually migrate high-risk constructs: |
||||||
|
- `??` |
||||||
|
- ternary |
||||||
|
- `instanceof` |
||||||
|
- nullsafe access |
||||||
|
- function, method and static calls |
||||||
|
- `return` |
||||||
|
- `foreach` |
||||||
|
- `try/catch/finally` |
||||||
|
- reference assignment and return-by-reference |
||||||
|
|
||||||
|
The current `beforeStmtLines` and `afterStmtLines` mechanism should eventually |
||||||
|
be replaced by explicit IR instruction ordering. This avoids hidden side effects |
||||||
|
inside expression parsing and prevents formatting or comment text from changing |
||||||
|
generated program behavior. |
||||||
|
|
||||||
|
## Testing Strategy |
||||||
|
|
||||||
|
Add IR snapshot tests in addition to PHPT output tests. |
||||||
|
|
||||||
|
Example: |
||||||
|
|
||||||
|
```text |
||||||
|
--IR-- |
||||||
|
function main(): void |
||||||
|
%0 = load_local $value |
||||||
|
%1 = load_local $name |
||||||
|
%2 = instanceof %0, %1 |
||||||
|
return %2 |
||||||
|
``` |
||||||
|
|
||||||
|
This separates frontend semantic correctness from backend code generation |
||||||
|
correctness. |
||||||
|
|
||||||
|
Recommended test categories: |
||||||
|
|
||||||
|
- PHP AST -> Bound AST diagnostics. |
||||||
|
- Bound AST -> Semantic IR snapshots. |
||||||
|
- Semantic IR -> C++ output behavior. |
||||||
|
- Target capability rejection tests. |
||||||
|
- Backend-specific runtime conformance tests. |
||||||
|
|
||||||
|
## Key Design Constraint |
||||||
|
|
||||||
|
The central IR must be a PHP semantic IR, not a C++ builder IR. |
||||||
|
|
||||||
|
If the IR is designed around `php::Var`, `php::Ref` and Zend symbols, C++ will |
||||||
|
remain the compiler's semantic core and future JavaScript/WASM targets will be |
||||||
|
hard to implement. |
||||||
|
|
||||||
|
If the IR describes PHP behavior and leaves physical representation to backend |
||||||
|
lowering, C++, JavaScript and WASM can evolve as separate backends. |
||||||
Loading…
Reference in new issue