pull/1/head
韩天峰 5 months ago
parent f17ffa6b6f
commit 1441a7e5be
  1. 10
      src/Php/AstNodeType.php
  2. 94
      src/Php/CompilerBase.php
  3. 13
      src/Php/Context/FunctionContext.php
  4. 4
      src/Php/Generator/ClosureGenerator.php
  5. 4
      src/Php/Generator/PlaceHolderGenerator.php
  6. 16
      src/Php/MagicMethodDetector.php
  7. 2
      src/Php/Preprocessor.php
  8. 31
      src/Php/Translator.php

@ -10,8 +10,8 @@ namespace PhpAot\Php;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\NodeAbstract;
use PhpParser\Node\VariadicPlaceholder;
use PhpParser\NodeAbstract;
trait AstNodeType
{
@ -102,9 +102,9 @@ trait AstNodeType
protected function isCallExpr(NodeAbstract $expr): bool
{
return $expr instanceof Node\Expr\FuncCall
or $expr instanceof Node\Expr\MethodCall
or $expr instanceof Node\Expr\StaticCall;
return $expr instanceof Expr\FuncCall
or $expr instanceof Expr\MethodCall
or $expr instanceof Expr\StaticCall;
}
protected function isPlaceholderExpr(NodeAbstract $expr): bool
@ -127,6 +127,6 @@ trait AstNodeType
if ($expr instanceof Node\Stmt\Expression) {
$expr = $expr->expr;
}
return $expr instanceof Node\Expr\Exit_;
return $expr instanceof Expr\Exit_;
}
}

@ -84,10 +84,8 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $phpxDir = '~/workspace/projects/phpx';
protected string $lang = 'PHP';
protected string $cppCompiler = 'g++';
protected array $literalStrings = [];
protected int $literalStringIndex = 0;
protected int $anonClassIndex = 0;
protected int $classIndex = 0;
@ -96,6 +94,7 @@ class CompilerBase extends \PhpAot\Core\Translator
*/
protected array $classMap = [];
protected int $funcIndex = 0;
/**
* @var array<string, int>
*/
@ -682,9 +681,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
/**
* @param string $propName
* @param string $className 必须是带有命名空间的完整类名
* @return int
*/
protected function getPropertyId(string $className, string $propName): int
{
@ -948,8 +945,6 @@ class CompilerBase extends \PhpAot\Core\Translator
/**
* @param $params array<Node\Param>
* @param FunctionDef $functionDef
* @return void
*/
protected function parseParams(array $params, FunctionDef $functionDef): void
{
@ -2223,8 +2218,6 @@ class CompilerBase extends \PhpAot\Core\Translator
/**
* @param array<Node\Arg|Node\VariadicPlaceholder> $callArgs
* @param string $nativeFunc
* @return string
*/
protected function parseNativeCallArgs(array $callArgs, string $nativeFunc): string
{
@ -2259,7 +2252,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($argInfo->variadic) {
$argsSlice = array_slice($args, $i);
if (count($argsSlice) === 1 and $argsSlice[0]->unpack) {
if ($this->isVarExpr($arg->value) ) {
if ($this->isVarExpr($arg->value)) {
$var =$this->parseIdentifier($arg->value);
if ($this->getVarType($var) === self::TYPE_ARRAY) {
$argList[] = $var;
@ -2357,7 +2350,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$list_args[] = $globalVar;
}
continue;
} elseif ($this->isVarExpr($arg->value->var) and !$this->hasVar($array)) {
}
if ($this->isVarExpr($arg->value->var) and !$this->hasVar($array)) {
$this->fatalError($arg, 'Undefined variable `$' . $array . '`');
}
if ($byRef) {
@ -3078,7 +3072,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($node->byRef) {
if (!$this->hasVar($valueVar)) {
$this->addLocalVar($valueVar, self::TYPE_REF);
} else if ($this->getVarType($valueVar) !== self::TYPE_REF) {
} elseif ($this->getVarType($valueVar) !== self::TYPE_REF) {
$this->fatalError($node, 'Cannot assign value to reference of type');
}
$code .= $this->getIndent() . ' ' . $valueVar . ' = iter.valueRef();' . PHP_EOL;
@ -3137,8 +3131,6 @@ class CompilerBase extends \PhpAot\Core\Translator
* 为了兼容已有代码,默认不使用原生类型,而是将整数和浮点数作为 php 变量处理
* 原生 int/float/bool 类型,是不支持自动转换的,例如如果 int 计算超过最大值后,会自动转为 float,除法若不能除尽,则会转为 float
* 某些情况下高性能计算,可能需要使用原生类型,使用 $a = std::int(0) 来显式地使用原生类型
* @param string $type
* @return string
*/
protected function getNativeType(string $type): string
{
@ -4201,7 +4193,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isCallExpr($expr->expr)) {
$nativeCall = $expr->expr->getAttribute('nativeCall');
if ($nativeCall and $this->nativeFunctions[$nativeCall]->returnType === self::TYPE_VOID) {
return $beforeCode . PHP_EOL . $code . ";" . PHP_EOL . "return " . self::VALUE_NULL . ';';
return $beforeCode . PHP_EOL . $code . ';' . PHP_EOL . 'return ' . self::VALUE_NULL . ';';
}
}
return $beforeCode . PHP_EOL . 'return ' . $code . ';';
@ -4250,38 +4242,6 @@ class CompilerBase extends \PhpAot\Core\Translator
return $stmts[array_key_last($stmts)] instanceof Node\Stmt\Return_;
}
/**
* 混杂数组赋值,需要拆分为多行插入
*/
private function parseArrayMixed(Node\Expr\Array_ $node): string
{
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_ARRAY);
$items = $node->items;
foreach ($items as $item) {
$value = $this->parseIdentifier($item->value);
if ($item->unpack) {
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.merge(' . $value . ');';
} elseif ($item->key) {
$key = $this->parseIdentifier($item->key);
if (str_starts_with($key, self::LITERAL_STRINGS)) {
$key = "{$key}.toStdString()";
} elseif ($key === '0L') {
$key = 'php::zero';
}
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.set(' . $key . ', ' . $value . ');';
} else {
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.append(' . $value . ');';
}
}
// 释放临时变量,避免修改数组产生数组复制操作
$this->context->afterStmtLines[] = $this->getIndent() . $tmpVar . '.unset();';
return $tmpVar;
}
protected function parseNullsafePropertyFetch(Node\Expr\NullsafePropertyFetch $expr): string
{
return $this->parseNullsafeExpr($expr);
@ -4330,22 +4290,54 @@ class CompilerBase extends \PhpAot\Core\Translator
foreach ($list as $key => $item) {
$tmpVar = $this->addTmpVar($key !== $last ? self::TYPE_OBJECT : self::TYPE_VAR);
$code .= "if ($object.isNull()) { return " . self::VALUE_NULL . "; }";
$code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }';
if ($item[0] == 'property') {
$code .= $this->getIndent() . "$tmpVar = $object.attr({$item[1]}, $update);";
$code .= $this->getIndent() . "{$tmpVar} = {$object}.attr({$item[1]}, {$update});";
} else {
$args = $this->parseCallArgs($item[2]);
$code .= $this->getIndent() . "$tmpVar = $object.exec({$item[1]}, $args);";
$code .= $this->getIndent() . "{$tmpVar} = {$object}.exec({$item[1]}, {$args});";
}
$object = $tmpVar;
}
$code .= $this->getIndent() . "return $object; };";
$code .= $this->getIndent() . "return {$object}; };";
$this->context->beforeStmtLines[] = $code;
return "$tmpFn()";
return "{$tmpFn}()";
}
protected function parseFullyQualifiedName(Node\Name\FullyQualified $expr): string
{
return $expr->name;
}
/**
* 混杂数组赋值,需要拆分为多行插入
*/
private function parseArrayMixed(Node\Expr\Array_ $node): string
{
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_ARRAY);
$items = $node->items;
foreach ($items as $item) {
$value = $this->parseIdentifier($item->value);
if ($item->unpack) {
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.merge(' . $value . ');';
} elseif ($item->key) {
$key = $this->parseIdentifier($item->key);
if (str_starts_with($key, self::LITERAL_STRINGS)) {
$key = "{$key}.toStdString()";
} elseif ($key === '0L') {
$key = 'php::zero';
}
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.set(' . $key . ', ' . $value . ');';
} else {
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.append(' . $value . ');';
}
}
// 释放临时变量,避免修改数组产生数组复制操作
$this->context->afterStmtLines[] = $this->getIndent() . $tmpVar . '.unset();';
return $tmpVar;
}
}

@ -1,4 +1,10 @@
<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace PhpAot\Php\Context;
@ -10,10 +16,12 @@ class FunctionContext
public array $objects = [];
public array $localVars = [];
public array $staticVars = [];
/**
* @var array<string, string>
*/
public array $objectWrappers = [];
/**
* @var array<string, string>
*/
@ -22,6 +30,7 @@ class FunctionContext
public array $arguments = [];
public bool $inLoop = false;
public bool $inClosure = false;
/**
* 赋值表达式的左值,写操作,右值为读操作.
*/
@ -29,7 +38,7 @@ class FunctionContext
public array $beforeStmtLines = [];
public array $afterStmtLines = [];
function __construct()
public function __construct()
{
$this->localVars = [];
$this->staticVars = [];
@ -42,4 +51,4 @@ class FunctionContext
$this->inClosure = false;
$this->inAssignExpr = false;
}
}
}

@ -16,8 +16,8 @@ trait ClosureGenerator
protected function genScopeSwitchCode(): string
{
$tmpScope = $this->genTmpVarName();
$code = "auto $tmpScope = php_switch_scope(this_);" . PHP_EOL;
$code .= "ON_SCOPE_EXIT({ php_restore_scope($tmpScope); });" . PHP_EOL;
$code = "auto {$tmpScope} = php_switch_scope(this_);" . PHP_EOL;
$code .= "ON_SCOPE_EXIT({ php_restore_scope({$tmpScope}); });" . PHP_EOL;
return $code;
}

@ -16,8 +16,8 @@ trait PlaceHolderGenerator
$fn = $ce . ', ' . $this->getFuncPtr('Closure::fromCallable');
$tmpVar = $this->genTmpVarName();
if ($this->classDef) {
$this->context->beforeStmtLines[] = "auto $tmpVar = php_switch_scope(this_);";
$this->context->afterStmtLines[] = "php_restore_scope($tmpVar);";
$this->context->beforeStmtLines[] = "auto {$tmpVar} = php_switch_scope(this_);";
$this->context->afterStmtLines[] = "php_restore_scope({$tmpVar});";
}
return 'php::call(' . $fn . ', {' . $callable . '})';
}

@ -13,14 +13,6 @@ use PhpParser\NodeAbstract;
trait MagicMethodDetector
{
protected function checkArgType(string $givenType, string $expectType, bool $canBeVar = true): bool
{
if ($canBeVar and $givenType == self::TYPE_VAR) {
return true;
}
return $givenType == $expectType;
}
public function checkRequiredArgNum(string $name, MethodDef $methodDef, NodeAbstract $v): void
{
$argInfoList = $methodDef->functionDef->argInfoList;
@ -69,4 +61,12 @@ trait MagicMethodDetector
}
}
}
protected function checkArgType(string $givenType, string $expectType, bool $canBeVar = true): bool
{
if ($canBeVar and $givenType == self::TYPE_VAR) {
return true;
}
return $givenType == $expectType;
}
}

@ -180,7 +180,7 @@ class Preprocessor extends CompilerBase
protected function prepareClass(Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): string
{
$this->class = $this->parseIdentifier($class->name);
if ($class->extends) {
if (!empty($class->extends)) {
$this->parentClass = $this->getParentClass($class->extends);
$fullClassName = $this->getNamespacedClassName($this->class);
$this->classExtends[$fullClassName] = $this->parentClass;

@ -291,20 +291,20 @@ class Translator extends Preprocessor
{
$objectFiles = [];
$job = $this->maxJob;
// 如果只有一个文件或 job 为 1,则串行编译
if (count($sourceFiles) <= 1 || $job <= 1) {
foreach ($sourceFiles as $cppFile) {
$objectFile = $this->getObjectFile($cppFile);
$this->compileFile($cppFile, $objectFile);
if (!is_file($objectFile)) {
throw new \Exception("compile error");
throw new \Exception('compile error');
}
$objectFiles[] = $objectFile;
}
return $objectFiles;
}
// 并行编译
$totalFiles = count($sourceFiles);
$runningProcesses = 0;
@ -312,19 +312,20 @@ class Translator extends Preprocessor
$fileQueue = $sourceFiles;
$compiledCount = 0;
$failedFiles = [];
$this->climate->blue("Starting parallel compilation with {$job} jobs for {$totalFiles} files");
while ($compiledCount < $totalFiles) {
// 启动新进程,直到达到最大并发数
while ($runningProcesses < $job && !empty($fileQueue)) {
$cppFile = array_shift($fileQueue);
$objectFile = $this->getObjectFile($cppFile);
$pid = pcntl_fork();
if ($pid == -1) {
throw new \Exception("Failed to fork process");
} elseif ($pid === 0) {
throw new \Exception('Failed to fork process');
}
if ($pid === 0) {
// 子进程:执行编译
try {
$this->compileFile($cppFile, $objectFile, true);
@ -342,7 +343,7 @@ class Translator extends Preprocessor
$runningProcesses++;
}
}
// 等待任意一个子进程完成
if ($runningProcesses > 0) {
$pid = pcntl_wait($status);
@ -350,7 +351,7 @@ class Translator extends Preprocessor
$processInfo = $processPipes[$pid] ?? null;
unset($processPipes[$pid]);
$runningProcesses--;
$exitCode = pcntl_wexitstatus($status);
if ($exitCode !== 0) {
$failedFile = $processInfo['file'] ?? 'unknown';
@ -365,7 +366,7 @@ class Translator extends Preprocessor
}
}
}
// 确保所有子进程都已结束
while ($runningProcesses > 0) {
$pid = pcntl_wait($status);
@ -375,13 +376,13 @@ class Translator extends Preprocessor
$compiledCount++;
}
}
if (!empty($failedFiles)) {
throw new \Exception("Compilation failed for: " . implode(', ', $failedFiles));
throw new \Exception('Compilation failed for: ' . implode(', ', $failedFiles));
}
$this->climate->green("Successfully compiled {$totalFiles} files");
return $objectFiles;
}

Loading…
Cancel
Save