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 最相近的现有技术方案
现有技术包括:
-
纯解释执行方案
PHP 源码由 Zend VM 解释执行。该方案兼容性强,但每次函数调用、属性访问、数组操作都依赖运行时动态机制。 -
纯静态编译方案
将源程序整体转换为 C/C++ 或机器码。该方案性能高,但对于动态语言特性支持困难,通常需要限制大量语法或改变语言语义。 -
JIT 即时编译方案
运行时根据热点路径和类型反馈进行编译。该方案可提升热点性能,但仍需要解释器协作,且编译结果依赖运行时状态。 -
手写扩展或 FFI 方案
通过 C/C++ 扩展或外部函数接口让动态语言调用静态代码。该方案可以局部提升性能,但静态代码与动态代码之间缺乏统一语言级编译模型。
4. 现有技术缺点及本发明目的
4.1 现有技术缺点
现有方案存在以下问题:
- 纯解释执行无法充分利用静态类型和本地代码性能。
- 纯静态编译难以兼容动态语言的动态调用、魔术方法、动态属性和反射等特性。
- JIT 依赖运行时热点和类型反馈,部署前难以得到稳定可预测的编译产物。
- 手写扩展需要开发者显式维护动态语言与静态语言之间的接口、类型转换和生命周期。
- 动态语言中的函数、类、属性和方法查找通常依赖字符串,重复查找成本较高。
- 传统编译器往往以“能否完全静态化”为判断标准,缺少一种表达同一程序内多种执行状态共存的语言级模型。
4.2 本发明目的
本发明的目的在于提供一种混合态编程语言的编译执行方法及系统,使同一动态语言程序能够被划分为静态态和动态态:
- 在静态态中,编译器生成 C++ 直接调用、强类型变量、强类型容器和本地代码;
- 在动态态中,系统保留 PHP VM 的动态查找、动态调用、动态属性、回调和通用 zval 语义;
- 在两种状态之间,通过 ArgInfo、包装函数、符号缓存、类型转换和动态回退机制实现互通。
由此实现静态语言的性能优势和动态语言的灵活优势兼得。
5. 本发明技术方案
5.1 混合态语言模型
本发明提出“混合态”语言模型。该模型不是简单地把动态语言编译成静态语言,而是在编译器和运行时共同支持下,将程序中的语句、表达式、函数、方法、变量和调用划分为不同状态。
混合态至少包括:
- 静态函数态:函数定义、参数类型和返回类型可确定,生成 C++ Native 函数。
- 静态对象态:对象变量的类可确定,方法调用可直连到 C++ 函数。
- 静态数据态:变量可映射为 C++ 原生类型或 C++ 模版容器。
- 动态值态:变量使用
php::Var、zval 或 PHP 对象保存,保留动态类型语义。 - 动态调用态:函数名、方法名或对象类型无法确定时,通过 PHP 运行时调用。
- 边界桥接态:静态态与动态态之间进行参数提取、类型转换、返回值写回和符号缓存。
5.2 系统组成
图 1:混合态编程语言系统组成图
PHP 源码 / 项目配置
|
v
预处理与符号提取模块
|
v
依赖排序与语义分析模块
|
v
混合态判定模块
|
+--> 静态态代码生成模块
| |
| +--> C++ 函数直连
| +--> C++ 原生类型
| +--> C++ 模版容器
|
+--> 动态态代码生成模块
| |
| +--> PHP VM 动态调用
| +--> zval / php::Var 动态值
| +--> 魔术方法和回调
|
+--> 边界桥接模块
|
+--> ArgInfo 参数转换
+--> Zend 包装函数
+--> 符号缓存
+--> 动态回退
|
v
C++ 源码与扩展注册代码
|
v
PHP 扩展或可执行二进制
5.3 方法流程
图 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 描述函数和方法参数,包括:
参数名称;
参数类型;
对象类名;
是否引用参数;
是否变长参数;
是否可空;
默认值;
是否为 UnsafePtr;
是否为构造器属性提升参数。
ArgInfo 同时服务于两个方向:
- AOT 内部直连调用:编译器根据 ArgInfo 重排命名参数、填充默认值、合并变长参数并生成 C++ 直接调用。
- Zend 运行时入口调用:当编译后函数作为 PHP 扩展函数被 PHP VM 调用时,包装函数根据 ArgInfo 从调用栈读取参数并转换类型。
该设计使静态态和动态态共享同一套参数语义。
5.6 静态态代码生成
当编译器能够确定调用目标时,生成 C++ 直接调用。
示例:
function add(int $a, int $b): int
{
return $a + $b;
}
add(1, 2);
生成类似:
php_add(php::toInt(1L), php::toInt(2L));
对于对象方法:
$obj->run($arg);
如果 $obj 的类可确定,且不存在方法重写或动态魔术方法风险,则生成:
php_Class_run(obj, converted_arg);
静态态还包括:
int、float、bool等原生类型;std::array、std::vector等 C++ 模版容器;- 可确定类的对象属性访问;
- 可确定函数的默认参数和变长参数处理;
- 编译期可确定的常量和类常量访问。
5.7 动态态代码生成
当出现以下情况时,编译器进入动态态:
- 函数名来自变量;
- 方法名来自变量;
- 对象真实类型无法确定;
- 类可能存在
__call()魔术方法; - 方法被子类重写,静态直连可能改变动态分派语义;
- 需要调用 PHP 内置动态函数;
- 回调、闭包或占位符表达式无法完全静态确定;
- 变量使用通用
mixed或动态数组语义。
动态态生成类似:
php::call(function_ptr, arg_list);
或:
object.call(method_ptr, arg_list);
动态态保持 PHP VM 的函数查找、方法分派、zval 类型和动态回调语义。
5.8 静态直连与动态回退判定
本发明不要求所有代码都静态化,而是在每个调用点执行判定。
图 3:调用点状态判定流程
调用表达式
|
v
函数名或方法名是否为字面量?
|
+-- 否 --> 动态态调用
|
+-- 是
|
v
是否可在符号表中找到 Native 目标?
|
+-- 否 --> 动态态调用
|
+-- 是
|
v
对象类或函数签名是否可确定?
|
+-- 否 --> 动态态调用
|
+-- 是
|
v
是否存在魔术方法、重写、动态回调等风险?
|
+-- 是 --> 动态态调用
|
+-- 否 --> 静态态直连
该机制使语言运行时不是单一状态,而是按代码位置自动选择最佳状态。
5.9 边界桥接机制
静态态和动态态之间通过边界桥接机制互通。
5.9.1 动态调用静态函数
当 PHP VM 调用编译后的函数时,进入 Zend 包装函数:
Zend 调用入口
|
v
读取调用栈参数
|
v
根据 ArgInfo 转换参数
|
v
调用 Native 函数
|
v
将返回值写回 return_value
5.9.2 静态代码调用动态函数
当 AOT 代码无法确定目标时,生成动态调用:
C++ 静态代码
|
v
构造 PHP 动态参数列表
|
v
查找函数或方法
|
v
调用 PHP VM 动态路径
|
v
返回 php::Var 动态值
5.9.3 静态数据流入动态态
例如强类型容器赋值给普通 PHP 变量时,自动转换为 PHP Array:
$arr = $vector;
生成:
arr = php::toArray(vector);
5.9.4 动态对象流入静态态
当从动态数组或函数返回值取出对象,而后续需要静态方法调用时,可通过编译期类型声明函数或类型转换函数告知编译器对象类名,从而进入静态对象态。
5.10 符号缓存机制
为减少动态态运行时字符串查找开销,本发明为符号建立整数编号和缓存数组:
zend_class_entry *class_map[N];
zend_function *func_map[M];
uint32_t property_map[K];
首次查找时根据字符串名称取得 Zend 结构指针并缓存;后续调用通过整数 ID 快速访问。
示例:
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 双运行形态
本发明支持两种运行形态:
- 扩展形态:编译结果是 PHP 扩展,可加载到 PHP-FPM 或 CLI 中,由 PHP VM 调用。
- 二进制形态:编译结果是可执行程序,由
main()函数作为入口,可直接运行。
这两种形态共享同一套混合态编译模型。
6. 关键点和欲保护点
- 提出一种混合态编程语言模型,使同一动态语言程序中同时存在静态态和动态态。
- 在编译器中按函数、方法、变量、表达式和调用点自动判定静态态或动态态。
- 对可确定目标生成 C++ 静态直连代码,对不可确定目标自动回退到 PHP 动态调用代码。
- 使用 ArgInfo 作为静态态和动态态共享的参数语义模型。
- 生成 Zend 包装函数,使动态运行时可以调用 AOT 生成的 Native 函数。
- 在 AOT 静态代码中构造动态参数列表,使静态代码可以调用 PHP 动态函数、动态方法或回调。
- 使用符号缓存机制减少动态态运行时的函数、类、方法、属性字符串查找开销。
- 支持扩展形态和二进制形态两种运行产物,均基于同一混合态语言模型。
- 在静态态中支持原生类型和 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 边界桥接机制和符号缓存方案。正式申请前建议作为内部技术资料管理。