feat(php): 添加 std::array 类型支持并优化编译器功能

- 新增 StdArrayParser trait 处理 std::array 类型解析
- 添加 std::array 类型检测和类型转换功能
- 实现 std::array 的维度检查和安全索引访问
- 添加 polyfills.php 文件提供类型定义和数组填充功能
- 更新 C++ 标准从 c++14 到 c++17
- 修复表达式解析中的类型检测逻辑
- 优化编译器的并发编译功能
- 添加数组循环性能测试示例
- 实现 std::array 的多维数组支持
pull/1/head
韩天峰 4 months ago
parent 673f3b3dc5
commit 2b088d71c5
  1. 1
      cli.php
  2. 17
      examples/array-loop/jit.php
  3. 32
      examples/array-loop/loop.cc
  4. 23
      examples/array-loop/main.php
  5. 14
      examples/array-loop/std-array.php
  6. 2
      project.yml
  7. 5
      src/Php/AstNodeType.php
  8. 65
      src/Php/CompilerBase.php
  9. 2
      src/Php/Constants.php
  10. 4
      src/Php/Context/FunctionContext.php
  11. 146
      src/Php/Parser/StdArrayParser.php
  12. 5
      src/Php/Symbol.php
  13. 15
      src/Php/Translator.php
  14. 13
      src/polyfills.php

@ -1,4 +1,5 @@
#!/usr/bin/env php
<?php
require __DIR__ . '/src/polyfills.php';
include $argv[1];
main($argc, $argv);

@ -0,0 +1,17 @@
<?php
$u = (int)$argv[1];
echo "u: $u\n";
$r = rand(0, 10000);
$a = array_fill(0, 10000, 0);
$begin = microtime(true);
for ($i = 0; $i < 10000; $i++) {
for ($j = 0; $j < 100000; $j++) {
$a[$i] += $j % $u;
}
$a[$i] += $r;
}
echo $a[$r] . "\n";
$end = microtime(true);
echo "sec: " . ($end - $begin) . "\n";

@ -0,0 +1,32 @@
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <chrono>
int main(int argc, char* argv[]) {
std::srand(static_cast<unsigned>(std::time(nullptr)));
long u = std::stoi(argv[1]);
std::cout << "u: " << u << "\n";
long r = std::rand() % 10001;
std::vector<long> a(10000, 0);
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 100000; j++) {
a[i] += j % u;
}
a[i] += r;
}
std::cout << a[r] << "\n";
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end - begin;
std::cout << "sec: " << diff.count() << "\n";
return 0;
}

@ -0,0 +1,23 @@
<?php
use native_types;
function main(int $argc, array $argv): void
{
$u = (int)$argv[2];
echo "u: $u\n";
$r = rand(0, 10000);
$a = std::array(native_types::type_int, 10000);
$begin = microtime(true);
for ($i = 0; $i < 10000; $i++) {
for ($j = 0; $j < 100000; $j++) {
$a[$i] += $j % $u;
}
$a[$i] += $r;
}
echo $a[$r] . "\n";
$end = microtime(true);
echo "sec: " . ($end - $begin) . "\n";
}

@ -0,0 +1,14 @@
<?php
use native_types;
function main()
{
$array = std::array(std::array(native_types::type_int, 10), 10);
$index = 9;
$index2 = 5;
$array[$index2][$index] = 2026;
// $array = std::array(native_types::type_int, 100);
// $array[99] = 2026;
var_dump($array);
}

@ -1,7 +1,7 @@
name: swoole-compiler
build-mode: bin
version: 0.1.0
cxx-std: c++14
cxx-std: c++17
cxx-flags:
- -Wall

@ -95,6 +95,11 @@ trait AstNodeType
return $expr instanceof Node\Scalar;
}
protected function isScalarInt(NodeAbstract $expr): bool
{
return $expr instanceof Node\Scalar\Int_;
}
protected function isMatchExpr(NodeAbstract $expr): bool
{
return $expr instanceof Expr\Match_;

@ -25,6 +25,7 @@ use PhpAot\Php\Generator\ClosureGenerator;
use PhpAot\Php\Generator\PlaceHolderGenerator;
use PhpAot\Php\Generator\PropertyPromotion;
use PhpAot\Php\Generator\Utils;
use PhpAot\Php\Parser\StdArrayParser;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
@ -50,6 +51,7 @@ class CompilerBase extends \PhpAot\Core\Translator
use PlaceHolderGenerator;
use PropertyPromotion;
use MagicMethodDetector;
use StdArrayParser;
use Utils;
public const string TYPE_VAR = 'php::Var';
@ -65,6 +67,7 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string TYPE_STR = 'php::Str';
public const string TYPE_REF = 'php::Ref';
public const string TYPE_VOID = 'void';
public const string TYPE_STD_ARRAY = 'std::array';
public const int DECL_TYPE_OF_RETURN = 1;
public const int DECL_TYPE_OF_PROPERTY = 2;
public const int DECL_TYPE_OF_CONST = 3;
@ -157,13 +160,13 @@ class CompilerBase extends \PhpAot\Core\Translator
protected int $maxJob = 4;
protected string $buildMode = 'bin';
protected string $cxxflags = '';
protected string $cxxStd = 'c++14';
protected string $cxxStd = 'c++17';
protected string $ldflags = '';
protected string $linker = 'link'; // Windows linker: link.exe or lld-link
protected int $floatPrecision = 17;
protected bool $debugInfo = false;
protected bool $formatCode = true;
protected bool $printBacktraceOnError = false;
protected bool $printBacktraceOnError = true;
protected bool $noLiteralStrings = false;
protected bool $noConsole = false; // Windows: hide console window
protected string $sanitize = ''; // Sanitizer type (address, undefined, etc.)
@ -313,18 +316,15 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($this->isClangAvailable()) {
// 优先使用 Clang(如果可用)
$this->cppCompiler = 'clang++';
$this->cxxStd = 'c++17';
$this->climate->info('Using Clang compiler (clang++)');
} else {
// 默认使用 MSVC
$this->cppCompiler = 'cl';
$this->cxxStd = 'c++17';
$this->climate->info('Using MSVC compiler (cl)');
}
} else {
// Unix/Linux/macOS 使用 g++
$this->cppCompiler = 'g++';
$this->cxxStd = 'c++14';
}
}
@ -1511,7 +1511,6 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($left, 'Cannot re-assign $this');
}
$expr = $this->parseExpr($right);
$type = $this->detectTypeOfExpr($right);
if ($this->isVarExpr($left)) {
@ -1550,14 +1549,22 @@ class CompilerBase extends \PhpAot\Core\Translator
} else {
$type = $type === self::TYPE_VOID ? self::TYPE_VAR : $type;
}
} elseif ($this->isStaticCall($right) and $this->isIdExpr($right->name)) {
} elseif ($this->isStaticCall($right) and $this->isNameExpr($right->class) and $this->isIdExpr($right->name)) {
$class = $this->parseIdentifier($right->class);
if ($class === 'std') {
$valueExpr = $this->parseStdCall($right);
if (!$this->hasVar($var)) {
$this->addLocalVar($var, $right->getAttribute('nativeType'));
if ($right->name->toString() === 'array') {
if ($this->hasVar($var)) {
$this->fatalError($left, "Cannot re-assign `\${$var}` to std::array");
}
$this->addLocalVar($var, self::TYPE_STD_ARRAY);
return $this->parseStdArray($var, $right);
} else {
$valueExpr = $this->parseStdCall($right);
if (!$this->hasVar($var)) {
$this->addLocalVar($var, $right->getAttribute('nativeType'));
}
return $var . ' = ' . $valueExpr;
}
return $var . ' = ' . $valueExpr;
}
} elseif ($this->isVarExpr($right)) {
$rightVar = $this->parseIdentifier($right);
@ -1584,7 +1591,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseAssignArrayDim($left, $right);
}
return $var . ' = ' . $this->convertExprType($expr, $this->detectTypeOfExpr($left), $this->detectTypeOfExpr($right));
return $var . ' = ' . $this->convertExprType($this->parseExpr($right), $this->detectTypeOfExpr($left), $this->detectTypeOfExpr($right));
}
protected function parseEcho(mixed $v): string
@ -2058,7 +2065,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function detectVarType($var): string
{
$name = $this->parseIdentifier($var);
if ($this->isStdArray($name)) {
return self::TYPE_ARRAY;
}
return $this->getVarType($name);
}
@ -2141,6 +2150,11 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
break;
case 'Expr_ArrayDimFetch':
if ($this->isStdArrayExpr($expr)) {
return $this->getStdArrayInfo($expr)['type'];
}
break;
case 'Expr_New':
return self::TYPE_OBJECT;
case 'Expr_Assign':
@ -2902,6 +2916,9 @@ class CompilerBase extends \PhpAot\Core\Translator
}
if ($this->isArrayDimFetch($node->var)) {
if ($this->isStdArrayExpr($node->var)) {
return $this->parseStdArrayAssignOp($node, $op);
}
/**
* $count[$r] -= 1;
* 需要转为下面语句:
@ -3044,6 +3061,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseArrayDimFetch(Expr\ArrayDimFetch $node, bool $write): string
{
if ($this->isStdArrayExpr($node)) {
return $this->parseStdArrayDimFetch($node);
}
$var = $this->parseIdentifier($node->var);
if ($this->isVarExpr($node->var)) {
if ($var === 'GLOBALS') {
@ -3433,7 +3454,11 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseArg(Node\Arg $arg): string
{
return $this->parseIdentifier($arg->value);
$expr = $this->parseIdentifier($arg->value);
if ($this->isVarExpr($arg->value) and $this->isStdArray($arg->value->name)) {
return $this->convertArrayExpr($expr);
}
return $expr;
}
protected function parseArrayArg(Node\Arg $expr): string
@ -5611,9 +5636,15 @@ class CompilerBase extends \PhpAot\Core\Translator
if (isset($this->context->arguments[$name])) {
continue;
}
$code .= $this->getIndent() . $type . ' ' . $name;
if ($type === self::TYPE_INT or $type === self::TYPE_FLOAT or $type === self::TYPE_BOOL) {
$code .= ' = 0';
$code .= $this->getIndent();
if ($type === self::TYPE_STD_ARRAY) {
$info = $this->context->stdArrays[$name];
$code .= $info['decl'] . ' ' . $name . '{}';
} else {
$code .= $type . ' ' . $name;
if ($type === self::TYPE_INT or $type === self::TYPE_FLOAT or $type === self::TYPE_BOOL) {
$code .= ' = 0';
}
}
$code .= ';' . PHP_EOL;
}

@ -151,7 +151,7 @@ class Constants
],
'cxx-std' => [
'longPrefix' => 'cxx-std',
'description' => 'C++ standard version (c++14, c++17, c++20, etc.)',
'description' => 'C++ standard version (c++17, c++20, etc.)',
'required' => false,
'defaultValue' => 'c++17',
],

@ -14,6 +14,10 @@ class FunctionContext
* @var array<string, string>
*/
public array $objects = [];
/**
* @var array<string, array>
*/
public array $stdArrays = [];
public array $localVars = [];
public array $staticVars = [];
public array $globalVars = [];

@ -0,0 +1,146 @@
<?php
namespace PhpAot\Php\Parser;
use PhpAot\Php\Symbol;
use PhpParser\Node\Expr;
trait StdArrayParser
{
protected function isStdArray(string $var): bool
{
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_ARRAY;
}
protected function isStdArrayExpr(Expr\ArrayDimFetch $expr): bool
{
$info = $this->getStdArrayInfo($expr);
return $info !== null;
}
protected function fillStdArray(Expr\StaticCall $expr): string
{
if (!$this->isVarExpr($expr->args[0]->value) or !$this->isStdArray($this->parseIdentifier($expr->args[0]->value))) {
$this->fatalError($expr, 'fill() only support std::array');
}
$array = $this->parseIdentifier($expr->args[0]->value);
$valueExpr = $this->parseExpr($expr->args[1]->value);
$type = $this->context->stdArrays[$array]['type'];
$value = $this->convertExprFromType($type, $valueExpr);
return "{$array}.fill({$value})";
}
protected function getStdArrayInfo(Expr\ArrayDimFetch $expr): ?array
{
$tmp = $expr->var;
while (true) {
if ($this->isArrayDimFetch($tmp)) {
$tmp = $tmp->var;
} elseif ($this->isVarExpr($tmp) and $this->isStdArray($this->parseVariable($tmp))) {
return $this->context->stdArrays[$this->parseVariable($tmp)];
} else {
return null;
}
}
}
protected function parseStdArrayAssignOp(Expr\AssignOp $expr, string $op): string
{
$binaryOp = $this->removeAssignOp($op);
if ($binaryOp === '.') {
$this->fatalError($expr, 'Cannot concat string to std::array');
}
$info = $this->getStdArrayInfo($expr->var);
$arrayDimFetch = $this->parseStdArrayDimFetch($expr->var);
return $arrayDimFetch . ' ' . $binaryOp . '= ' . $this->convertExprFromType($info['type'], $this->parseExpr($expr->expr));
}
protected function parseStdArrayDimFetch(Expr\ArrayDimFetch $expr): string
{
$tmp = $expr;
$nesting = [];
$level = 0;
$info = $this->getStdArrayInfo($expr);
while (true) {
if ($this->isArrayDimFetch($tmp)) {
if ($tmp->dim === null) {
$this->fatalError($tmp, 'std::array() expects an index');
}
$size = $info['sizes'][$level];
if ($this->isScalarInt($tmp->dim)) {
if ($tmp->dim->value < 0 || $tmp->dim->value >= $size) {
$this->fatalError($tmp, "Array index out of bounds: index {$tmp->dim->value}, size {$size}");
}
}
$index = $this->parseExpr($tmp->dim);
$nesting[] = '[' . Symbol::safeIndex($index, $info['sizes'][$level]) . ']';
$tmp = $tmp->var;
$level++;
} else {
$nesting[] = $this->parseVariable($tmp);
break;
}
}
return implode('', array_reverse($nesting));
}
protected function parseStdArray(string $var, Expr\StaticCall $expr): string
{
$tmp = $expr;
$nesting = [];
while(true) {
if (count($tmp->args) !== 2) {
$this->fatalError($tmp, 'std::array() expects two arguments');
}
if (!$this->isScalarInt($tmp->args[1]->value)) {
$this->fatalError($tmp, 'std::array() expects second argument to be an integer');
}
$size = $this->parseScalar($tmp->args[1]->value);
$nesting[] = $size;
$typeExpr = $tmp->args[0]->value;
if ($this->isClassConstFetch($typeExpr)) {
if (!$this->isNameExpr($typeExpr->class) || !$this->isIdExpr($typeExpr->name) || $typeExpr->class->toString() !== 'native_types') {
$this->fatalError($tmp, 'An incorrect `std::array` definition');
}
switch ($typeExpr->name->name) {
case 'type_int':
$type = self::TYPE_INT;
break;
case 'type_float':
$type = self::TYPE_FLOAT;
break;
case 'type_bool':
$type = self::TYPE_BOOL;
break;
default:
$this->fatalError($tmp, 'An incorrect `std::array` definition');
break;
}
break;
} elseif ($this->isStaticCall($typeExpr)) {
$tmp = $typeExpr;
if (!$this->isNameExpr($tmp->class) || !$this->isIdExpr($tmp->name) || $tmp->class->toString() !== 'std' || $tmp->name->toString() !== 'array') {
$this->fatalError($tmp, 'An incorrect `std::array` definition');
}
} else {
$this->fatalError($tmp, 'std::array() expects first argument to be a class constant');
}
}
$decl = str_repeat('std::array<', count($nesting));
$decl .= $type;
for ($i = count($nesting) - 1; $i >= 0; $i--) {
$decl .= ', ' . $nesting[$i] . '>';
}
$this->context->stdArrays[$var] = [
'decl' => $decl,
'type' => $type,
'sizes' => array_reverse($nesting),
];
return '';
}
}

@ -54,4 +54,9 @@ class Symbol
{
return 'php::ArgList';
}
public static function safeIndex(string $index, string $size): string
{
return "php::safeIndex($index, $size)";
}
}

@ -91,7 +91,7 @@ class Translator extends Preprocessor
$climate->tab()->out('-O <level> Optimization level (0-3, default: 0)');
$climate->tab()->out('-p, --profile Enable performance profiling');
$climate->tab()->out('-d, --debug-info Enable debug info (auto-disable optimizations, add -g/-Zi)');
$climate->tab()->out('--cxx-std <version> C++ standard version (c++14, c++17, c++20, etc.)');
$climate->tab()->out('--cxx-std <version> C++ standard version (c++17, c++20, etc.)');
$climate->tab()->out('-o, --output <file> Output binary name (default: input basename)');
$climate->tab()->out('-v, --version Show version');
$climate->tab()->out('-h, --help Show this help message');
@ -812,24 +812,21 @@ CODE;
}
// Windows 不支持 pcntl_fork,使用串行编译或 proc_open
if ($this->isWindows()) {
return $this->compileOnWindows($sourceFiles);
if ($this->isWindows() or $job <= 1) {
return $this->compileSourceFile($sourceFiles);
}
// Unix/Linux/macOS 使用 pcntl 并行编译
return $this->compileWithPcntl($sourceFiles, $job);
}
/**
* Windows 平台编译(不使用 pcntl)
*/
protected function compileOnWindows(array $sourceFiles): array
protected function compileSourceFile(array $sourceFiles): array
{
$objectFiles = [];
$totalFiles = count($sourceFiles);
$failedFiles = [];
$this->climate->lightBlue("Starting compilation for {$totalFiles} files (Windows mode)");
$this->climate->lightBlue("Starting compilation for {$totalFiles} files");
foreach ($sourceFiles as $cppFile) {
$objectFile = $this->getObjectFile($cppFile);
@ -864,7 +861,7 @@ CODE;
// 检查 pcntl 扩展是否可用
if (!function_exists('pcntl_fork')) {
$this->climate->warning('pcntl extension not available, using sequential compilation');
return $this->compileOnWindows($sourceFiles);
return $this->compileSourceFile($sourceFiles);
}
$objectFiles = [];

@ -11,6 +11,7 @@ class native_types
public const type_int = 'int';
public const type_float = 'float';
public const type_bool = 'bool';
public const type_any = 'php::Var';
}
class std
@ -29,6 +30,18 @@ class std
{
return boolval($value);
}
public static function array(mixed $type, int $size): array
{
return [];
}
public static function fill(array $array, mixed $value): void
{
for ($i = 0; $i < count($array); $i++) {
$array[$i] = $value;
}
}
}
function objval(mixed $obj, string $class): object

Loading…
Cancel
Save