重构,拆分内置类、函数、属性表,这些是 MINIT 阶段就确定的,无需 thread_local,可预加载,request shutdown 阶段无需释放

pull/48/head
韩天峰 2 weeks ago
parent 8118fbd3dc
commit 997bbffb65
  1. 112
      src/CompilerBase.php
  2. 112
      src/Translator.php

@ -239,6 +239,9 @@ class CompilerBase implements PropertyAccessContext
public const string CLASS_MAP = 'class_map';
public const string FUNC_MAP = 'func_map';
public const string PROP_MAP = 'property_map';
public const string INTERNAL_CLASS_MAP = 'internal_class_map';
public const string INTERNAL_FUNC_MAP = 'internal_func_map';
public const string INTERNAL_PROP_MAP = 'internal_property_map';
public const string NAMESPACE_SEPARATOR = '__';
public const string PREFIX = 'php_';
@ -270,9 +273,16 @@ class CompilerBase implements PropertyAccessContext
protected int $classIndex = 0;
/**
* 用户定义(请求生命周期)类名 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理
* @var array<string, int>
*/
protected array $classMap = [];
/**
* 内置/编译产物(进程生命周期)类名 → ID,运行期为全局缓存,MINIT 填充,永不清理
* @var array<string, int>
*/
protected array $internalClassMap = [];
protected int $internalClassIndex = 0;
/**
* @var array<string, int>
*/
@ -280,11 +290,28 @@ class CompilerBase implements PropertyAccessContext
protected int $funcIndex = 0;
/**
* 用户定义(请求生命周期)函数/方法 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理
* key 为函数名或 `Class::method`
* @var array<string, int>
*/
protected array $funcMap = [];
/**
* 内置/编译产物(进程生命周期)函数/方法 → ID,运行期为全局缓存,MINIT 填充,永不清理
* key 为函数名或 `Class::method`
* @var array<string, int>
*/
protected array $internalFuncMap = [];
protected int $internalFuncIndex = 0;
protected int $propIndex = 0;
/**
* 用户定义类的属性 offset 缓存,key 为 `Class::prop`,RSHUTDOWN 清理
*/
protected array $propMap = [];
/**
* 内置/编译产物类的属性 offset 缓存,key 为 `Class::prop`,MINIT 填充,永不清理
*/
protected array $internalPropMap = [];
protected int $internalPropIndex = 0;
protected const array PHP_RUNTIME_TYPE_MAP = [
'integer' => Type::INT,
'double' => Type::FLOAT,
@ -884,6 +911,7 @@ class CompilerBase implements PropertyAccessContext
abort($expr);
break;
}
return '';
}
public function stop(string $string): never
@ -1194,10 +1222,47 @@ class CompilerBase implements PropertyAccessContext
return implode(self::NAMESPACE_SEPARATOR, $names);
}
/**
* 判断类的符号指针是否进程级稳定(MINIT 注册,跨请求缓存安全)。
* 编译产物(本单元编译的类/接口)与 PHP 内置类/接口均满足条件。
*/
protected function isProcessStableClass(string $className): bool
{
if ($this->hasClass($className) || $this->hasInterface($className)) {
return true;
}
$ref = Reflection::getClass(ltrim($className, '\\'));
return $ref !== null && $ref->isInternal();
}
/**
* 判断函数/方法符号指针是否进程级稳定。
* `Class::method` 形式的 key 以其所属类的稳定性为准。
*/
protected function isProcessStableFunction(string $funcName): bool
{
if (str_contains($funcName, '::')) {
[$class] = explode('::', $funcName, 2);
return $this->isProcessStableClass($class);
}
if ($this->hasFunction($funcName)) {
return true;
}
$ref = Reflection::getFunction(ltrim($funcName, '\\'));
return $ref !== null && $ref->isInternal();
}
protected function getClassId(string $className): int
{
if (isset($this->classMap[$className])) {
$id = $this->classMap[$className];
return $this->classMap[$className];
}
if (isset($this->internalClassMap[$className])) {
return $this->internalClassMap[$className];
}
if ($this->isProcessStableClass($className)) {
$id = $this->internalClassIndex++;
$this->internalClassMap[$className] = $id;
} else {
$id = $this->classIndex++;
$this->classMap[$className] = $id;
@ -1208,7 +1273,14 @@ class CompilerBase implements PropertyAccessContext
protected function getFuncId(string $funcName): int
{
if (isset($this->funcMap[$funcName])) {
$id = $this->funcMap[$funcName];
return $this->funcMap[$funcName];
}
if (isset($this->internalFuncMap[$funcName])) {
return $this->internalFuncMap[$funcName];
}
if ($this->isProcessStableFunction($funcName)) {
$id = $this->internalFuncIndex++;
$this->internalFuncMap[$funcName] = $id;
} else {
$id = $this->funcIndex++;
$this->funcMap[$funcName] = $id;
@ -1223,7 +1295,14 @@ class CompilerBase implements PropertyAccessContext
{
$key = $className . '::' . $propName;
if (isset($this->propMap[$key])) {
$id = $this->propMap[$key];
return $this->propMap[$key];
}
if (isset($this->internalPropMap[$key])) {
return $this->internalPropMap[$key];
}
if ($this->isProcessStableClass($className)) {
$id = $this->internalPropIndex++;
$this->internalPropMap[$key] = $id;
} else {
$id = $this->propIndex++;
$this->propMap[$key] = $id;
@ -1234,7 +1313,8 @@ class CompilerBase implements PropertyAccessContext
protected function getClassEntryPtr(string $className): string
{
$id = $this->getClassId($className);
return 'php_get_class(' . $id . ', ' . $this->getLiteralString($className) . ')';
$helper = isset($this->internalClassMap[$className]) ? 'php_get_internal_class' : 'php_get_class';
return $helper . '(' . $id . ', ' . $this->getLiteralString($className) . ')';
}
protected function getCeWrapper(string $className): string
@ -1243,28 +1323,32 @@ class CompilerBase implements PropertyAccessContext
return $this->context->ceWrappers[$className];
}
$object = $this->addTmpVar(Type::OBJECT);
$this->context->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';';
$this->context->ceWrappers[$className] = $object;
return $object;
}
protected function getFuncPtr(string $funcName): string
{
return 'php_get_func(' . $this->getFuncId($funcName) . ', ' . $this->getLiteralString($funcName) . ')';
$id = $this->getFuncId($funcName);
$helper = isset($this->internalFuncMap[$funcName]) ? 'php_get_internal_func' : 'php_get_func';
return $helper . '(' . $id . ', ' . $this->getLiteralString($funcName) . ')';
}
protected function getMethodPtr(string $class, string $method): string
{
$funcId = $this->getFuncId($class . '::' . $method);
$classId = $this->getClassId($class);
return 'php_get_method(' . $funcId . ', ' . $this->getLiteralString($method) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')';
// 方法的稳定性与所属类一致,因此 class_id 必定落在同一张表中
$helper = isset($this->internalFuncMap[$class . '::' . $method]) ? 'php_get_internal_method' : 'php_get_method';
return $helper . '(' . $funcId . ', ' . $this->getLiteralString($method) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')';
}
protected function getPropertyOffset(string $class, string $prop): string
{
$funcId = $this->getPropertyId($class, $prop);
$propId = $this->getPropertyId($class, $prop);
$classId = $this->getClassId($class);
return 'php_get_prop(' . $funcId . ', ' . $this->getLiteralString($prop) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')';
$helper = isset($this->internalPropMap[$class . '::' . $prop]) ? 'php_get_internal_prop' : 'php_get_prop';
return $helper . '(' . $propId . ', ' . $this->getLiteralString($prop) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')';
}
protected function writeLog($msg): void
@ -1319,6 +1403,7 @@ class CompilerBase implements PropertyAccessContext
abort($expr);
break;
}
return '';
}
/**
@ -4313,6 +4398,15 @@ class CompilerBase implements PropertyAccessContext
$code .= $this->getIndent() . 'int _cnt_flag = 0;' . PHP_EOL;
}
$code .= $this->genLocalVarDecl($this->context->localVars);
// Native static calls pass a lightweight Object containing the called
// class entry. A wrapper can be shared by all calls to the same class,
// but its initialization must dominate every control-flow branch that
// may use it. Emitting it at the first call site is unsafe when that
// site belongs to an if/switch/loop branch that is not executed.
foreach ($this->context->ceWrappers as $className => $object) {
$code .= $this->getIndent() . 'Z_PTR_P(' . $object . '.ptr()) = '
. $this->getClassEntryPtr($className) . ';' . PHP_EOL;
}
foreach ($this->context->globalVars as $name => $type) {
// $GLOBALS is handled via php_globals_array() at each read site
if ($name === 'GLOBALS') {

@ -744,9 +744,13 @@ class Translator extends Preprocessor
// 确保数组大小至少为 1,避免 C/C++ 编译错误
$classCount = max(1, count($this->classMap));
$lines[] = 'extern THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . $classCount . '];' . PHP_EOL;
$internalClassCount = max(1, count($this->internalClassMap));
$lines[] = 'extern zend_class_entry *' . self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . $internalClassCount . '];' . PHP_EOL;
$funcCount = max(1, count($this->funcMap));
$lines[] = 'extern THREAD_LOCAL zend_function *' . self::PREFIX . self::FUNC_MAP . '[' . $funcCount . '];' . PHP_EOL;
$internalFuncCount = max(1, count($this->internalFuncMap));
$lines[] = 'extern zend_function *' . self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $internalFuncCount . '];' . PHP_EOL;
$pythonModuleDeclarations = $this->genPythonModuleDataDeclarations();
if ($pythonModuleDeclarations !== '') {
@ -755,6 +759,8 @@ class Translator extends Preprocessor
$propCount = max(1, count($this->propMap));
$lines[] = 'extern THREAD_LOCAL uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . $propCount . '];' . PHP_EOL;
$internalPropCount = max(1, count($this->internalPropMap));
$lines[] = 'extern uint32_t ' . self::PREFIX . self::INTERNAL_PROP_MAP . '[' . $internalPropCount . '];' . PHP_EOL;
foreach ($this->getClassLikesWithConstants() as $classDef) {
foreach ($classDef->constants as $constant) {
@ -812,14 +818,18 @@ class Translator extends Preprocessor
$code .= "// class entry \n";
// 确保数组大小至少为 1,避免 C/C++ 编译错误
$code .= 'THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . max(1, count($this->classMap)) . '];' . PHP_EOL;
// 内置/编译产物类,进程级缓存,MINIT 填充,无需 THREAD_LOCAL,RSHUTDOWN 不清理
$code .= 'zend_class_entry *' . self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . max(1, count($this->internalClassMap)) . '];' . PHP_EOL;
$code .= "// func \n";
$code .= 'THREAD_LOCAL zend_function *' . self::PREFIX . self::FUNC_MAP . '[' . max(1, count($this->funcMap)) . '];' . PHP_EOL;
$code .= 'zend_function *' . self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . max(1, count($this->internalFuncMap)) . '];' . PHP_EOL;
$code .= $this->genPythonModuleStorage();
$code .= "// property \n";
$code .= 'THREAD_LOCAL uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . max(1, count($this->propMap)) . '];' . PHP_EOL;
$code .= 'uint32_t ' . self::PREFIX . self::INTERNAL_PROP_MAP . '[' . max(1, count($this->internalPropMap)) . '];' . PHP_EOL;
$code .= "// functions \n";
@ -852,8 +862,60 @@ uint32_t php_get_prop(int prop_id, const php::Str &prop_name, int class_id, cons
}
return php_property_map[prop_id] - 1024;
}
zend_class_entry *php_get_internal_class(int class_id, const php::Str &class_name) {
if (UNEXPECTED(php_internal_class_map[class_id] == nullptr)) {
php_internal_class_map[class_id] = php::getClassEntrySafe(class_name);
}
return php_internal_class_map[class_id];
}
zend_function *php_get_internal_func(int func_id, const php::Str &func_name) {
if (UNEXPECTED(php_internal_func_map[func_id] == nullptr)) {
php_internal_func_map[func_id] = php::getFunction(func_name);
}
return php_internal_func_map[func_id];
}
zend_function *php_get_internal_method(int func_id, const php::Str &method_name, int class_id, const php::Str &class_name) {
if (UNEXPECTED(php_internal_func_map[func_id] == nullptr)) {
auto ce = php_get_internal_class(class_id, class_name);
php_internal_func_map[func_id] = php::getMethod(ce, method_name);
}
return php_internal_func_map[func_id];
}
uint32_t php_get_internal_prop(int prop_id, const php::Str &prop_name, int class_id, const php::Str &class_name) {
if (UNEXPECTED(php_internal_property_map[prop_id] == 0)) {
php_internal_property_map[prop_id] = php::getPropertyOffset(class_name, prop_name) + 1024;
}
return php_internal_property_map[prop_id] - 1024;
}
CODE;
$code .= "\n\n";
// MINIT 阶段填充内部符号缓存使用的容错查找函数(查找失败返回空,留给运行期惰性重试)
if ($this->internalFuncMap !== []) {
$code .= <<<'CODE'
static zend_function *php_find_internal_function(const php::Str &name) {
auto lc_name = zend_string_tolower(name.str());
auto fn = (zend_function *) zend_hash_find_ptr(CG(function_table), lc_name);
zend_string_release(lc_name);
return fn;
}
static zend_function *php_find_internal_method(zend_class_entry *ce, const php::Str &name) {
if (UNEXPECTED(ce == nullptr)) {
return nullptr;
}
auto lc_name = zend_string_tolower(name.str());
auto fn = (zend_function *) zend_hash_find_ptr(&ce->function_table, lc_name);
zend_string_release(lc_name);
return fn;
}
CODE;
$code .= "\n\n";
}
$code .= $this->genPythonModuleGetter();
@ -936,6 +998,7 @@ CODE;
foreach ($this->registerSymbols as $registerSymbolFn) {
$code .= $registerSymbolFn . '(module_number);' . PHP_EOL;
}
$code .= $this->genInternalSymbolCacheInit();
$code .= '} zend_end_try();' . PHP_EOL;
$code .= 'return SUCCESS;' . PHP_EOL;
$code .= '}' . PHP_EOL . PHP_EOL;
@ -1073,7 +1136,8 @@ CODE;
}
}
// 扩展模式,需要在 RSHUTDOWN 阶段中清理函数、类、属性表
// 扩展模式,需要在 RSHUTDOWN 阶段中清理用户定义的函数、类、属性缓存;
// 内置/编译产物符号在 internal_*_map 中,进程级存活,无需清理
if ($this->isBuildModeExt()) {
$code .= 'std::memset(' . self::PREFIX . self::FUNC_MAP . ', 0, sizeof(' . self::PREFIX . self::FUNC_MAP . '));' . PHP_EOL;
$code .= 'std::memset(' . self::PREFIX . self::CLASS_MAP . ', 0, sizeof(' . self::PREFIX . self::CLASS_MAP . '));' . PHP_EOL;
@ -1147,6 +1211,52 @@ CODE;
return $file;
}
/**
* 生成 MINIT 阶段填充内部符号缓存(进程级类/函数/属性)的代码。
* 类与函数使用容错查找:跨扩展符号可能因模块初始化顺序尚未注册,
* 查找失败时留空,由运行期 helper 惰性重试(此时抛错语义与拆分前一致)。
* 属性缓存的所属类必为本模块编译产物(已在上方注册完毕),直接调用 helper 填充。
*/
protected function genInternalSymbolCacheInit(): string
{
if ($this->internalClassMap === [] && $this->internalFuncMap === [] && $this->internalPropMap === []) {
return '';
}
$code = '// internal symbol caches' . PHP_EOL;
foreach ($this->internalClassMap as $name => $id) {
$code .= self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . $id . '] = php::getClassEntry('
. $this->genSymbolNameExpr($name) . ');' . PHP_EOL;
}
foreach ($this->internalFuncMap as $key => $id) {
if (str_contains($key, '::')) {
[$class, $method] = explode('::', $key, 2);
$code .= self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $id . '] = php_find_internal_method(php::getClassEntry('
. $this->genSymbolNameExpr($class) . '), ' . $this->genSymbolNameExpr($method) . ');' . PHP_EOL;
} else {
$code .= self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $id . '] = php_find_internal_function('
. $this->genSymbolNameExpr($key) . ');' . PHP_EOL;
}
}
foreach ($this->internalPropMap as $key => $id) {
[$class, $prop] = explode('::', $key, 2);
$code .= '(void) php_get_internal_prop(' . $id . ', ' . $this->genSymbolNameExpr($prop)
. ', 0, ' . $this->genSymbolNameExpr($class) . ');' . PHP_EOL;
}
return $code;
}
/**
* 生成符号名称的 C++ 表达式。优先复用字面量字符串表中的已有条目;
* 表条目在 MINIT 代码生成前已输出,缺失时(不应发生)退化为内联字符串。
*/
private function genSymbolNameExpr(string $name): string
{
if (!$this->noLiteralStrings && isset($this->literalStrings[$name])) {
return self::LITERAL_STRINGS . '[' . $this->literalStrings[$name] . ']';
}
return Type::STR . '{ZEND_STRL(' . $this->genCharPtr($name, true) . '), true}';
}
public function getModuleName(): string
{
return Constants::EXTENSION_PREFIX . $this->targetName;

Loading…
Cancel
Save