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.
453 lines
17 KiB
453 lines
17 KiB
# 专利申请技术交底书:一种混合态编程语言的编译执行方法及系统
|
|
|
|
> 本文档为专利申请技术交底草案,面向专利代理人说明技术方案。文中“本发明”指“一种混合态编程语言的编译执行方法及系统”。本文档不构成法律意见,正式权利要求应由专利代理人结合检索结果进一步撰写。
|
|
|
|
## 1. 技术应用产品
|
|
|
|
本发明应用于 Swoole-Compiler PHP AOT 编译器。Swoole-Compiler 并非简单地将 PHP 完全静态化,也不是传统意义上的纯解释器或纯即时编译器,而是一种“混合态编程语言编译执行系统”。
|
|
|
|
在该系统中,同一份 PHP 程序可以同时包含:
|
|
|
|
- 可被静态分析和预先编译的静态态代码;
|
|
- 需要保留 PHP 动态语义的动态态代码;
|
|
- 静态态与动态态之间的边界转换代码;
|
|
- 可作为 PHP 扩展被动态运行时调用的入口;
|
|
- 可作为独立二进制程序执行的入口。
|
|
|
|
该系统使动态语言具备静态语言的性能优势,同时保留动态语言的灵活优势。
|
|
|
|
## 2. 术语解释
|
|
|
|
| 术语 | 中文说明 |
|
|
| --- | --- |
|
|
| 混合态 | 同一程序中同时存在静态编译态和动态解释态,并可在两种状态之间切换 |
|
|
| 静态态 | 编译器可在编译期确定类型、函数、方法、属性或控制路径的状态 |
|
|
| 动态态 | 需要在运行时依据 PHP 语义解析类型、函数、方法或属性的状态 |
|
|
| AOT | Ahead-Of-Time,预先编译 |
|
|
| VM | Virtual Machine,虚拟机,这里指 PHP Zend VM |
|
|
| Native 函数 | 被 AOT 编译为 C++ 函数的 PHP 函数或方法 |
|
|
| 动态调用 | 运行时根据函数名、方法名、对象类型或回调对象查找并调用目标 |
|
|
| 静态直连 | 编译期确定调用目标后生成 C++ 直接函数调用 |
|
|
| ArgInfo | 参数信息,包括类型、默认值、引用、变长参数等 |
|
|
| 符号表 | 编译器记录函数、类、接口、trait、常量、属性、方法等信息的数据结构 |
|
|
| 包装函数 | 将 Zend VM 调用入口转换为 Native 函数调用的桥接函数 |
|
|
| 回退 | 当静态分析不能保证语义正确时,退回动态运行时路径 |
|
|
|
|
## 3. 技术背景及现有技术方案
|
|
|
|
### 3.1 大的技术背景
|
|
|
|
传统编程语言通常可分为两类。
|
|
|
|
第一类是静态编译语言,例如 C、C++、Go、Rust。它们在运行前完成编译,类型、函数签名和内存布局大多在编译期确定,因此运行性能高。但这类语言通常开发灵活性较弱,动态加载、动态方法调用、运行时修改数据结构等能力有限。
|
|
|
|
第二类是动态解释语言,例如 PHP、Python、Ruby、JavaScript。它们在运行时解析变量类型、函数调用、对象属性和方法分派,因此开发效率高、表达灵活。但这类语言通常需要虚拟机、解释器、动态类型判断和运行时查找,性能不如静态编译语言。
|
|
|
|
现有 JIT 技术试图在运行时将热点代码编译为机器码,但 JIT 仍依赖运行时采样、类型反馈和热点判断,且难以在部署前得到完全确定的本地代码。传统 AOT 技术则倾向于将程序全部静态化,但面对动态语言的变量函数、动态方法、魔术方法、反射、动态属性、闭包和回调时容易丢失语言语义。
|
|
|
|
### 3.2 小的技术背景
|
|
|
|
Swoole-Compiler 面向 PHP 程序。PHP 程序具有以下动态特征:
|
|
|
|
- 变量可以保存任意类型;
|
|
- 函数和方法可以通过字符串或数组形式动态调用;
|
|
- 对象可通过 `__call()`、`__get()`、`__set()` 等魔术方法处理动态行为;
|
|
- 命名空间、use 别名、trait、继承、方法重写会影响真实调用目标;
|
|
- 参数支持默认值、命名参数、变长参数、引用参数;
|
|
- PHP 内置函数和用户函数可混合调用;
|
|
- 程序既可能作为 PHP 扩展运行,也可能作为独立二进制程序运行。
|
|
|
|
如果编译器强行将所有 PHP 代码转换为静态 C++ 调用,会破坏上述动态语义。如果完全保留动态解释执行,则无法获得 AOT 的性能优势。
|
|
|
|
因此,需要一种新的混合态编程语言设计方案:在可静态确定的区域进入静态态,在不可静态确定的区域进入动态态,并通过统一边界机制实现两种状态的互通。
|
|
|
|
### 3.3 最相近的现有技术方案
|
|
|
|
现有技术包括:
|
|
|
|
1. **纯解释执行方案**
|
|
PHP 源码由 Zend VM 解释执行。该方案兼容性强,但每次函数调用、属性访问、数组操作都依赖运行时动态机制。
|
|
|
|
2. **纯静态编译方案**
|
|
将源程序整体转换为 C/C++ 或机器码。该方案性能高,但对于动态语言特性支持困难,通常需要限制大量语法或改变语言语义。
|
|
|
|
3. **JIT 即时编译方案**
|
|
运行时根据热点路径和类型反馈进行编译。该方案可提升热点性能,但仍需要解释器协作,且编译结果依赖运行时状态。
|
|
|
|
4. **手写扩展或 FFI 方案**
|
|
通过 C/C++ 扩展或外部函数接口让动态语言调用静态代码。该方案可以局部提升性能,但静态代码与动态代码之间缺乏统一语言级编译模型。
|
|
|
|
## 4. 现有技术缺点及本发明目的
|
|
|
|
### 4.1 现有技术缺点
|
|
|
|
现有方案存在以下问题:
|
|
|
|
1. 纯解释执行无法充分利用静态类型和本地代码性能。
|
|
2. 纯静态编译难以兼容动态语言的动态调用、魔术方法、动态属性和反射等特性。
|
|
3. JIT 依赖运行时热点和类型反馈,部署前难以得到稳定可预测的编译产物。
|
|
4. 手写扩展需要开发者显式维护动态语言与静态语言之间的接口、类型转换和生命周期。
|
|
5. 动态语言中的函数、类、属性和方法查找通常依赖字符串,重复查找成本较高。
|
|
6. 传统编译器往往以“能否完全静态化”为判断标准,缺少一种表达同一程序内多种执行状态共存的语言级模型。
|
|
|
|
### 4.2 本发明目的
|
|
|
|
本发明的目的在于提供一种混合态编程语言的编译执行方法及系统,使同一动态语言程序能够被划分为静态态和动态态:
|
|
|
|
- 在静态态中,编译器生成 C++ 直接调用、强类型变量、强类型容器和本地代码;
|
|
- 在动态态中,系统保留 PHP VM 的动态查找、动态调用、动态属性、回调和通用 zval 语义;
|
|
- 在两种状态之间,通过 ArgInfo、包装函数、符号缓存、类型转换和动态回退机制实现互通。
|
|
|
|
由此实现静态语言的性能优势和动态语言的灵活优势兼得。
|
|
|
|
## 5. 本发明技术方案
|
|
|
|
### 5.1 混合态语言模型
|
|
|
|
本发明提出“混合态”语言模型。该模型不是简单地把动态语言编译成静态语言,而是在编译器和运行时共同支持下,将程序中的语句、表达式、函数、方法、变量和调用划分为不同状态。
|
|
|
|
混合态至少包括:
|
|
|
|
1. **静态函数态**:函数定义、参数类型和返回类型可确定,生成 C++ Native 函数。
|
|
2. **静态对象态**:对象变量的类可确定,方法调用可直连到 C++ 函数。
|
|
3. **静态数据态**:变量可映射为 C++ 原生类型或 C++ 模版容器。
|
|
4. **动态值态**:变量使用 `php::Var`、zval 或 PHP 对象保存,保留动态类型语义。
|
|
5. **动态调用态**:函数名、方法名或对象类型无法确定时,通过 PHP 运行时调用。
|
|
6. **边界桥接态**:静态态与动态态之间进行参数提取、类型转换、返回值写回和符号缓存。
|
|
|
|
### 5.2 系统组成
|
|
|
|
```text
|
|
图 1:混合态编程语言系统组成图
|
|
|
|
PHP 源码 / 项目配置
|
|
|
|
|
v
|
|
预处理与符号提取模块
|
|
|
|
|
v
|
|
依赖排序与语义分析模块
|
|
|
|
|
v
|
|
混合态判定模块
|
|
|
|
|
+--> 静态态代码生成模块
|
|
| |
|
|
| +--> C++ 函数直连
|
|
| +--> C++ 原生类型
|
|
| +--> C++ 模版容器
|
|
|
|
|
+--> 动态态代码生成模块
|
|
| |
|
|
| +--> PHP VM 动态调用
|
|
| +--> zval / php::Var 动态值
|
|
| +--> 魔术方法和回调
|
|
|
|
|
+--> 边界桥接模块
|
|
|
|
|
+--> ArgInfo 参数转换
|
|
+--> Zend 包装函数
|
|
+--> 符号缓存
|
|
+--> 动态回退
|
|
|
|
|
v
|
|
C++ 源码与扩展注册代码
|
|
|
|
|
v
|
|
PHP 扩展或可执行二进制
|
|
```
|
|
|
|
### 5.3 方法流程
|
|
|
|
```text
|
|
图 2:混合态编译执行流程图
|
|
|
|
步骤 S1:读取 PHP 源文件和项目配置;
|
|
步骤 S2:解析抽象语法树;
|
|
步骤 S3:提取函数、类、接口、trait、常量、属性、方法等符号;
|
|
步骤 S4:根据符号使用关系对文件进行依赖排序;
|
|
步骤 S5:解析函数和方法的 ArgInfo;
|
|
步骤 S6:对每个表达式或调用点判断是否可进入静态态;
|
|
步骤 S7:若目标、类型和参数可确定,则生成 C++ 静态直连代码;
|
|
步骤 S8:若存在动态语义风险,则生成动态态调用代码;
|
|
步骤 S9:为静态态与动态态边界生成参数转换、返回值转换和包装函数;
|
|
步骤 S10:为函数、类、方法、属性生成运行时缓存映射;
|
|
步骤 S11:编译 C++ 代码,生成 PHP 扩展或可执行程序;
|
|
步骤 S12:运行时根据生成代码执行静态直连或动态回退路径。
|
|
```
|
|
|
|
### 5.4 预处理与符号提取
|
|
|
|
编译器在正式生成 C++ 代码前,先扫描 PHP 源码,提取:
|
|
|
|
- 函数定义;
|
|
- 类定义;
|
|
- 接口定义;
|
|
- trait 定义;
|
|
- 类属性;
|
|
- 类方法;
|
|
- 类常量;
|
|
- 全局常量;
|
|
- 命名空间和 use 别名;
|
|
- 函数、类、方法、常量的使用关系。
|
|
|
|
随后构建符号依赖图,并对源文件进行拓扑排序。这样可提高静态分析成功率,避免因为文件顺序导致函数或类暂不可见。
|
|
|
|
### 5.5 ArgInfo 统一参数模型
|
|
|
|
本发明使用 ArgInfo 描述函数和方法参数,包括:
|
|
|
|
```text
|
|
参数名称;
|
|
参数类型;
|
|
对象类名;
|
|
是否引用参数;
|
|
是否变长参数;
|
|
是否可空;
|
|
默认值;
|
|
是否为 UnsafePtr;
|
|
是否为构造器属性提升参数。
|
|
```
|
|
|
|
ArgInfo 同时服务于两个方向:
|
|
|
|
1. **AOT 内部直连调用**:编译器根据 ArgInfo 重排命名参数、填充默认值、合并变长参数并生成 C++ 直接调用。
|
|
2. **Zend 运行时入口调用**:当编译后函数作为 PHP 扩展函数被 PHP VM 调用时,包装函数根据 ArgInfo 从调用栈读取参数并转换类型。
|
|
|
|
该设计使静态态和动态态共享同一套参数语义。
|
|
|
|
### 5.6 静态态代码生成
|
|
|
|
当编译器能够确定调用目标时,生成 C++ 直接调用。
|
|
|
|
示例:
|
|
|
|
```php
|
|
function add(int $a, int $b): int
|
|
{
|
|
return $a + $b;
|
|
}
|
|
|
|
add(1, 2);
|
|
```
|
|
|
|
生成类似:
|
|
|
|
```cpp
|
|
php_add(php::toInt(1L), php::toInt(2L));
|
|
```
|
|
|
|
对于对象方法:
|
|
|
|
```php
|
|
$obj->run($arg);
|
|
```
|
|
|
|
如果 `$obj` 的类可确定,且不存在方法重写或动态魔术方法风险,则生成:
|
|
|
|
```cpp
|
|
php_Class_run(obj, converted_arg);
|
|
```
|
|
|
|
静态态还包括:
|
|
|
|
- `int`、`float`、`bool` 等原生类型;
|
|
- `std::array`、`std::vector` 等 C++ 模版容器;
|
|
- 可确定类的对象属性访问;
|
|
- 可确定函数的默认参数和变长参数处理;
|
|
- 编译期可确定的常量和类常量访问。
|
|
|
|
### 5.7 动态态代码生成
|
|
|
|
当出现以下情况时,编译器进入动态态:
|
|
|
|
- 函数名来自变量;
|
|
- 方法名来自变量;
|
|
- 对象真实类型无法确定;
|
|
- 类可能存在 `__call()` 魔术方法;
|
|
- 方法被子类重写,静态直连可能改变动态分派语义;
|
|
- 需要调用 PHP 内置动态函数;
|
|
- 回调、闭包或占位符表达式无法完全静态确定;
|
|
- 变量使用通用 `mixed` 或动态数组语义。
|
|
|
|
动态态生成类似:
|
|
|
|
```cpp
|
|
php::call(function_ptr, arg_list);
|
|
```
|
|
|
|
或:
|
|
|
|
```cpp
|
|
object.call(method_ptr, arg_list);
|
|
```
|
|
|
|
动态态保持 PHP VM 的函数查找、方法分派、zval 类型和动态回调语义。
|
|
|
|
### 5.8 静态直连与动态回退判定
|
|
|
|
本发明不要求所有代码都静态化,而是在每个调用点执行判定。
|
|
|
|
```text
|
|
图 3:调用点状态判定流程
|
|
|
|
调用表达式
|
|
|
|
|
v
|
|
函数名或方法名是否为字面量?
|
|
|
|
|
+-- 否 --> 动态态调用
|
|
|
|
|
+-- 是
|
|
|
|
|
v
|
|
是否可在符号表中找到 Native 目标?
|
|
|
|
|
+-- 否 --> 动态态调用
|
|
|
|
|
+-- 是
|
|
|
|
|
v
|
|
对象类或函数签名是否可确定?
|
|
|
|
|
+-- 否 --> 动态态调用
|
|
|
|
|
+-- 是
|
|
|
|
|
v
|
|
是否存在魔术方法、重写、动态回调等风险?
|
|
|
|
|
+-- 是 --> 动态态调用
|
|
|
|
|
+-- 否 --> 静态态直连
|
|
```
|
|
|
|
该机制使语言运行时不是单一状态,而是按代码位置自动选择最佳状态。
|
|
|
|
### 5.9 边界桥接机制
|
|
|
|
静态态和动态态之间通过边界桥接机制互通。
|
|
|
|
#### 5.9.1 动态调用静态函数
|
|
|
|
当 PHP VM 调用编译后的函数时,进入 Zend 包装函数:
|
|
|
|
```text
|
|
Zend 调用入口
|
|
|
|
|
v
|
|
读取调用栈参数
|
|
|
|
|
v
|
|
根据 ArgInfo 转换参数
|
|
|
|
|
v
|
|
调用 Native 函数
|
|
|
|
|
v
|
|
将返回值写回 return_value
|
|
```
|
|
|
|
#### 5.9.2 静态代码调用动态函数
|
|
|
|
当 AOT 代码无法确定目标时,生成动态调用:
|
|
|
|
```text
|
|
C++ 静态代码
|
|
|
|
|
v
|
|
构造 PHP 动态参数列表
|
|
|
|
|
v
|
|
查找函数或方法
|
|
|
|
|
v
|
|
调用 PHP VM 动态路径
|
|
|
|
|
v
|
|
返回 php::Var 动态值
|
|
```
|
|
|
|
#### 5.9.3 静态数据流入动态态
|
|
|
|
例如强类型容器赋值给普通 PHP 变量时,自动转换为 PHP Array:
|
|
|
|
```php
|
|
$arr = $vector;
|
|
```
|
|
|
|
生成:
|
|
|
|
```cpp
|
|
arr = php::toArray(vector);
|
|
```
|
|
|
|
#### 5.9.4 动态对象流入静态态
|
|
|
|
当从动态数组或函数返回值取出对象,而后续需要静态方法调用时,可通过编译期类型声明函数或类型转换函数告知编译器对象类名,从而进入静态对象态。
|
|
|
|
### 5.10 符号缓存机制
|
|
|
|
为减少动态态运行时字符串查找开销,本发明为符号建立整数编号和缓存数组:
|
|
|
|
```cpp
|
|
zend_class_entry *class_map[N];
|
|
zend_function *func_map[M];
|
|
uint32_t property_map[K];
|
|
```
|
|
|
|
首次查找时根据字符串名称取得 Zend 结构指针并缓存;后续调用通过整数 ID 快速访问。
|
|
|
|
示例:
|
|
|
|
```cpp
|
|
zend_function *get_func(int id, const php::Str &name) {
|
|
if (func_map[id] == nullptr) {
|
|
func_map[id] = php::getFunction(name);
|
|
}
|
|
return func_map[id];
|
|
}
|
|
```
|
|
|
|
该机制使动态态仍然比纯解释路径更高效。
|
|
|
|
### 5.11 双运行形态
|
|
|
|
本发明支持两种运行形态:
|
|
|
|
1. **扩展形态**:编译结果是 PHP 扩展,可加载到 PHP-FPM 或 CLI 中,由 PHP VM 调用。
|
|
2. **二进制形态**:编译结果是可执行程序,由 `main()` 函数作为入口,可直接运行。
|
|
|
|
这两种形态共享同一套混合态编译模型。
|
|
|
|
## 6. 关键点和欲保护点
|
|
|
|
1. 提出一种混合态编程语言模型,使同一动态语言程序中同时存在静态态和动态态。
|
|
2. 在编译器中按函数、方法、变量、表达式和调用点自动判定静态态或动态态。
|
|
3. 对可确定目标生成 C++ 静态直连代码,对不可确定目标自动回退到 PHP 动态调用代码。
|
|
4. 使用 ArgInfo 作为静态态和动态态共享的参数语义模型。
|
|
5. 生成 Zend 包装函数,使动态运行时可以调用 AOT 生成的 Native 函数。
|
|
6. 在 AOT 静态代码中构造动态参数列表,使静态代码可以调用 PHP 动态函数、动态方法或回调。
|
|
7. 使用符号缓存机制减少动态态运行时的函数、类、方法、属性字符串查找开销。
|
|
8. 支持扩展形态和二进制形态两种运行产物,均基于同一混合态语言模型。
|
|
9. 在静态态中支持原生类型和 C++ 模版容器,在动态态中保留 zval、php::Var、PHP Array 和 PHP 对象语义。
|
|
|
|
## 7. 与现有技术相比的优点
|
|
|
|
与传统静态编译语言相比,本发明保留了动态语言的动态调用、动态类型、动态对象和回调能力,不要求程序完全静态化。
|
|
|
|
与传统动态解释语言相比,本发明可在编译期确定的区域生成 C++ 直接调用、原生类型和强类型容器,显著减少运行时查找和动态类型开销。
|
|
|
|
与 JIT 相比,本发明在部署前生成稳定的编译产物,不依赖运行时热点采样和类型反馈,适合需要稳定部署、保护源码和提升性能的生产环境。
|
|
|
|
与手写扩展相比,本发明由编译器自动生成包装函数、参数转换、符号缓存和动态回退路径,降低开发者在 PHP 与 C++ 之间手动桥接的成本。
|
|
|
|
## 8. 可选实施方式
|
|
|
|
本发明不限于 PHP,也可用于 Python、JavaScript、Ruby、Lua 等动态语言。目标静态语言不限于 C++,也可为 Rust、Go、C 或 LLVM 中间表示。
|
|
|
|
混合态判定粒度可以是函数级、基本块级、语句级或表达式级。符号缓存也可使用哈希表、数组、句柄表或运行时内联缓存实现。
|
|
|
|
## 9. 保密说明
|
|
|
|
本文档涉及 Swoole-Compiler 的整体编译模型、静态直连与动态回退策略、ArgInfo 边界桥接机制和符号缓存方案。正式申请前建议作为内部技术资料管理。
|
|
|