Keep boolean condition snapshots in native storage (#106)

* perf(compiler): keep boolean condition snapshots unboxed

* fix(compiler): isolate static initializer temporary names

* test: close PHP section in static initializer PHPT
master
yavon007 4 weeks ago committed by GitHub
parent f63eab17a3
commit 8332bfedd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 25
      phpunit/code/native-condition-temporaries.php
  2. 28
      phpunit/src/NativeConditionTemporaryTest.php
  3. 11
      src/CompilerBase.php
  4. 6
      src/Parser/BinaryOpTrait.php
  5. 14
      tests/compiler/optimizations/native-condition-static-initializer.phpt

@ -0,0 +1,25 @@
<?php
function nativeCondition(array $values): int
{
if ($values[0] === 1) {
return 1;
}
if ($values[0] === 2 || $values[1] === 3) {
return 2;
}
return 0;
}
function dynamicConditionValue(mixed $value): mixed
{
return $value;
}
function dynamicCondition(array $values): bool
{
if (dynamicConditionValue($values[0])) {
return true;
}
return false;
}

@ -0,0 +1,28 @@
<?php
use TypePhp\CompilerTest;
final class NativeConditionTemporaryTest extends BaseTest
{
public function testBooleanConditionsWithOperandCleanupRemainUnboxed(): void
{
global $translator;
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$translator = $compiler;
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/native-condition-temporaries.php';
$compiler->addFiles([$source]);
$compiler->prepareFile($source);
$code = file_get_contents($compiler->convertFile($source));
preg_match_all('/(tmp_var_\d+) = php::toBool\(php::same\(/', $code, $matches);
self::assertCount(2, $matches[1]);
foreach ($matches[1] as $temporary) {
self::assertStringContainsString('php::Bool ' . $temporary . ' = 0;', $code);
}
$dynamicBody = explode('php::Bool php_dynamiccondition(', $code, 2)[1];
$dynamicBody = explode("\n}", $dynamicBody, 2)[0];
self::assertStringContainsString('php::Var tmp_var_1;', $dynamicBody);
self::assertStringContainsString('tmp_var_1 = php_dynamicconditionvalue(', $dynamicBody);
}
}

@ -1983,7 +1983,13 @@ class CompilerBase implements PropertyAccessContext
$code = '';
$code .= $this->formatCapturedStmtLines($beforeStmts);
if ($afterStmts) {
$tmpVar = $this->addTmpVar(Type::VAR);
// Boolean results own no zval resources and can survive operand
// cleanup in native storage without changing evaluation order.
$type = $this->detectTypeOfExpr($cond) === Type::BOOL ? Type::BOOL : Type::VAR;
$tmpVar = $this->addTmpVar($type);
if ($type === Type::BOOL) {
$condExpr = $this->convertBoolExpr($condExpr);
}
$code .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($afterStmts);
$condExpr = $tmpVar;
@ -4500,6 +4506,9 @@ class CompilerBase implements PropertyAccessContext
$this->context = new FunctionContext();
$this->context->arguments = $oriCtx->localVars;
// Outer locals are captured arguments. New initializer temporaries
// must not reuse their names and inherit an incompatible scalar type.
$this->context->tmpVarIndex = $oriCtx->tmpVarIndex;
$code = '([&](){' . PHP_EOL;
$body = $this->getIndent() . $varName . ' = ' . $this->parseExpr($var->default) . ';';

@ -1275,7 +1275,11 @@ trait BinaryOpTrait
$this->indentLevel++;
$code .= $this->formatCapturedStmtLines($rightBeforeStmts);
if ($rightAfterStmts) {
$rightTmpVar = $this->addTmpVar(Type::VAR);
$type = $this->detectTypeOfExpr($right) === Type::BOOL ? Type::BOOL : Type::VAR;
$rightTmpVar = $this->addTmpVar($type);
if ($type === Type::BOOL) {
$rightExpr = $this->convertBoolExpr($rightExpr);
}
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($rightAfterStmts);
$rightExpr = $rightTmpVar;

@ -0,0 +1,14 @@
--TEST--
Static initializer temporaries do not reuse outer native condition snapshots
--FILE--
<?php
function conditionBeforeStatic(array $args): void {
if ($args === []) { return; }
static $table = [[[7, 'value']]];
echo $table[0][0][1], "\n";
}
function main(): void { conditionBeforeStatic([1]); conditionBeforeStatic([2]); }
?>
--EXPECT--
value
value
Loading…
Cancel
Save