refactor(optimizer): 重构SSA对象稳定性分析以支持动态方法调用优化

- 将对象稳定性分析从属性提升优化中分离为独立方法
- 添加analyzeStableObjects方法用于分析方法分派的对象稳定性
- 修改optimizeObjectProps方法只在启用原生类型时执行属性提升
- 提取collectStableObjectCandidates辅助方法收集稳定对象候选者
- 更新翻译器中的优化执行顺序和条件判断逻辑
- 添加两个新的动态调用测试用例验证重
pull/3/head
韩天峰 2 months ago
parent 8148ffce1e
commit a7b5b248fb
  1. 82
      src/Php/Optimizer/SsaPropOptimizer.php
  2. 14
      src/Php/Translator.php
  3. 43
      tests/aot/dynamic_call/call-namespaced-parent-override.phpt
  4. 35
      tests/aot/dynamic_call/call-parent-return-override-no-native-types.phpt

@ -1,6 +1,6 @@
<?php
/**
* SSA-based object property reference hoisting optimizer.
* SSA-based object stability and property reference hoisting optimizer.
*
* Extends the existing $this->intProp optimization to any SSA-proven stable
* object. When an object variable has a single definition, no escape/reference/
@ -33,7 +33,8 @@ use PhpParser\Node\Expr;
trait SsaPropOptimizer
{
/**
* Analyze object stability and identify safe property accesses.
* Analyze object stability for method dispatch and other non-native
* optimizations.
* Called after SSA build and var type optimization in parseFunction().
*
* Scans the function body AST to find object assignments (e.g. $o = new Foo()),
@ -41,12 +42,28 @@ trait SsaPropOptimizer
* This must be done during analysis because $this->context->objects is only
* populated during code generation (after analysis).
*/
protected function optimizeObjectProps(SsaBuilder $ssa): void
protected function analyzeStableObjects(SsaBuilder $ssa): void
{
if (!$this->nativeTypes) {
return;
[$objectAssigns] = $this->collectStableObjectCandidates($ssa);
foreach ($objectAssigns as $objName => $className) {
if (!$className || $className === 'stdClass' || !$this->hasClass($className)) {
continue;
}
if (!$this->isObjectSsaStable($ssa, $objName, $objectAssigns)) {
continue;
}
$this->context->stableObjects[$objName] = $className;
}
}
/**
* Identify safe property accesses for native typed property hoisting.
*/
protected function optimizeObjectProps(SsaBuilder $ssa): void
{
if ($this->classDef && !$this->classDef->trait) {
if ($this->isClassSafeForPropHoisting($this->getFullClassName())) {
$unsafeProps = $this->collectDangerousPropOps('this_', $ssa->getStmts());
@ -58,36 +75,11 @@ trait SsaPropOptimizer
}
}
if (empty($ssa->ssaVars)) {
return;
}
// Seed with function parameters that are typed objects, then propagate
// simple object aliases such as `$next = $right`.
$objectAssigns = [];
foreach ($this->context->objects as $objName => $className) {
if ($objName === 'this_') {
continue;
}
$objectAssigns[$objName] = $className;
}
$objectAliases = [];
$objectAssigns = $this->collectObjectAssignments($ssa->getStmts(), $objectAssigns, $objectAliases);
foreach ($objectAssigns as $objName => $className) {
if ($objName === 'this_') {
continue;
}
if (!$className || $className === 'stdClass' || !$this->hasClass($className)) {
continue;
}
if (!$this->isObjectSsaStable($ssa, $objName, $objectAssigns)) {
continue;
}
[, $objectAliases] = $this->collectStableObjectCandidates($ssa);
foreach ($this->context->stableObjects as $objName => $className) {
if (!$this->isClassSafeForPropHoisting($className)) {
$this->context->unsafeObjectProps[$objName] = ['*' => true];
continue;
}
@ -98,9 +90,31 @@ trait SsaPropOptimizer
if ($unsafeProps) {
$this->context->unsafeObjectProps[$objName] = $unsafeProps;
}
}
}
$this->context->stableObjects[$objName] = $className;
/**
* @return array{0: array<string, string>, 1: array<string, string>}
*/
protected function collectStableObjectCandidates(SsaBuilder $ssa): array
{
if (empty($ssa->ssaVars)) {
return [[], []];
}
$objectAssigns = [];
foreach ($this->context->objects as $objName => $className) {
if ($objName === 'this_') {
continue;
}
$objectAssigns[$objName] = $className;
}
$objectAliases = [];
return [
$this->collectObjectAssignments($ssa->getStmts(), $objectAssigns, $objectAliases),
$objectAliases,
];
}
/**

@ -2835,12 +2835,14 @@ CODE;
$ssaBuilder = new SsaBuilder($v->stmts, $this->functionDef->argInfoList);
$ssaBuilder->build();
$this->context->ssaBuilder = $ssaBuilder;
// Narrow local variable types based on SSA analysis
$this->optimizeVarTypes($ssaBuilder);
// Narrow range-proven loop counters independent of native_types
$this->optimizeLoopVars($ssaBuilder);
// Analyze object stability for property reference hoisting
$this->optimizeObjectProps($ssaBuilder);
$this->analyzeStableObjects($ssaBuilder);
if ($this->nativeTypes) {
// Narrow local variable types based on SSA analysis.
$this->optimizeVarTypes($ssaBuilder);
// Narrow range-proven loop counters and native property accesses.
$this->optimizeLoopVars($ssaBuilder);
$this->optimizeObjectProps($ssaBuilder);
}
$this->context->resetAnalysisTemporaries($oriLocalVars, $oriTmpVarIndex);
}

@ -0,0 +1,43 @@
--TEST--
call overridden method through namespaced parent type
--FILE--
<?php
namespace Demo {
use native_types;
class Base
{
public function run(): string
{
return 'base';
}
}
class Impl extends Base
{
public function run(): string
{
return 'impl';
}
}
function run(Base $obj): string
{
$alias = $obj;
return $alias->run();
}
}
namespace {
use Demo\Impl;
function main(): int
{
$r = Demo\run(new Impl());
echo "result: $r\n";
return $r === 'impl' ? 0 : 1;
}
}
?>
--EXPECT--
result: impl

@ -0,0 +1,35 @@
--TEST--
call overridden method through parent parameter type without native_types
--FILE--
<?php
class Base
{
public function run(): string
{
return 'base';
}
}
class Impl extends Base
{
public function run(): string
{
return 'impl';
}
}
function run(Base $obj): string
{
$alias = $obj;
return $alias->run();
}
function main(): int
{
$r = run(new Impl());
echo "result: $r\n";
return $r === 'impl' ? 0 : 1;
}
?>
--EXPECT--
result: impl
Loading…
Cancel
Save