TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.2 KiB
307 lines
9.2 KiB
# 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.
|
|
|