From 1ea7ec07646c3d7dd5b0312c81477dbee7be513a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Jun 2026 13:56:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E5=88=86=E6=9E=90=E5=92=8C=20objval=20=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 Clang、Gcc 和 Msvc 编译后端中添加 PROF_OUTPUT_FILE 宏定义支持 - 实现编译器性能分析模式,通过 --profile 参数启用并自动链接 -lprofiler 库 - 添加 .prof 文件分析功能,支持 php bin/compiler.php app.prof 格式调用 pprof 工具 - 实现 objval() 函数的 AOT 编译支持,用于对象类型转换 - 更新编译器缓存逻辑,启用性能分析时强制重新编译 - 添加 Linux 平台限制检查,非 Linux 系统使用 --profile 参数时显示错误提示 - 创建 objval 函数的单元测试用例 --- bin/prof.php | 8 ------- src/Php/Backend/Clang.php | 3 +++ src/Php/Backend/Gcc.php | 3 +++ src/Php/Backend/Msvc.php | 3 +++ src/Php/CompilerBase.php | 25 +++++++++++++++++++++- src/Php/Translator.php | 10 ++++++--- src/compiler.php | 33 +++++++++++++++++++++++++++++ tests/aot/class/objval.phpt | 42 +++++++++++++++++++++++++++++++++++++ 8 files changed, 115 insertions(+), 12 deletions(-) delete mode 100755 bin/prof.php create mode 100644 tests/aot/class/objval.phpt diff --git a/bin/prof.php b/bin/prof.php deleted file mode 100755 index f8d0ceb1..00000000 --- a/bin/prof.php +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env php - profile.pdf"); diff --git a/src/Php/Backend/Clang.php b/src/Php/Backend/Clang.php index 8374746c..d8f9ce4c 100644 --- a/src/Php/Backend/Clang.php +++ b/src/Php/Backend/Clang.php @@ -410,6 +410,9 @@ class Clang extends CompilerBackend // 性能分析宏 if (!empty($config['enable_profiler'])) { $cmd .= ' -DPPROF_ON=1'; + if (!empty($config['prof_output'])) { + $cmd .= ' -DPROF_OUTPUT_FILE=\'"' . $config['prof_output'] . '"\''; + } } // 用户自定义编译标志 diff --git a/src/Php/Backend/Gcc.php b/src/Php/Backend/Gcc.php index 79d6b53e..f50c4e87 100644 --- a/src/Php/Backend/Gcc.php +++ b/src/Php/Backend/Gcc.php @@ -292,6 +292,9 @@ class Gcc extends CompilerBackend // 性能分析宏 if (!empty($config['enable_profiler'])) { $cmd .= ' -DPPROF_ON=1'; + if (!empty($config['prof_output'])) { + $cmd .= ' -DPROF_OUTPUT_FILE=\'"' . $config['prof_output'] . '"\''; + } } // 用户自定义编译标志 diff --git a/src/Php/Backend/Msvc.php b/src/Php/Backend/Msvc.php index 4121a3bf..751995b8 100644 --- a/src/Php/Backend/Msvc.php +++ b/src/Php/Backend/Msvc.php @@ -379,6 +379,9 @@ class Msvc extends CompilerBackend // 性能分析宏 if (!empty($config['enable_profiler'])) { $cmd .= ' /DPPROF_ON=1'; + if (!empty($config['prof_output'])) { + $cmd .= ' /DPROF_OUTPUT_FILE=\'"' . $config['prof_output'] . '"\''; + } } // 用户自定义编译标志 diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 588f31af..62b74fc1 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -2883,6 +2883,7 @@ class CompilerBase extends \PhpAot\Core\Translator 'is_zts' => $this->isPhpZts, 'build_mode' => $this->buildMode, 'enable_profiler' => $this->enableProfiler, + 'prof_output' => $this->targetName . '.prof', 'suppressed_warnings' => Constants::MSVC_SUPPRESSED_WARNINGS ?? [], 'cxxflags' => $this->cxxFlags, ]; @@ -2920,10 +2921,16 @@ class CompilerBase extends \PhpAot\Core\Translator protected function getLinkCommandOptions(): array { + $ldflags = $this->ldflags; + + if ($this->enableProfiler) { + $ldflags .= ' -lprofiler'; + } + $options = [ 'library_paths' => $this->getLibraryPaths(), 'libraries' => $this->getLibraries(), - 'ldflags' => $this->ldflags, + 'ldflags' => $ldflags, 'debug' => $this->debug, 'no_console' => $this->noConsole, 'build_mode' => $this->buildMode, @@ -3408,6 +3415,9 @@ class CompilerBase extends \PhpAot\Core\Translator if (in_array($name, Constants::UNSUPPORTED_FUNCTIONS)) { $this->fatalError($expr, 'Unsupported function: `' . $name . '`'); } + if ($name === 'objval') { + return $this->genObjvalCall($expr); + } $nativeFn = $this->findNativeFunction($name); if ($nativeFn) { $expr->setAttribute('nativeCall', $nativeFn); @@ -5277,6 +5287,19 @@ class CompilerBase extends \PhpAot\Core\Translator return $left . ' = &' . $tmpVar; } + protected function genObjvalCall(Expr\FuncCall $expr): string + { + if (count($expr->args) !== 2) { + $this->fatalError($expr, 'objval() requires exactly 2 arguments'); + } + $receiver = $this->parseExpr($expr->args[0]->value); + $className = $this->resolveClassNameArg($expr->args[1]->value); + if ($className === '') { + $this->fatalError($expr, 'The second parameter of objval() only supports string literals or `ClassName::class` constant'); + } + return 'php::toObject(' . $receiver . ', ' . $this->getClassEntryPtr($className) . ', true)'; + } + protected function genToObjectCall(Expr\MethodCall $expr, string $receiver): string { if (empty($expr->args)) { diff --git a/src/Php/Translator.php b/src/Php/Translator.php index d162f0b6..9d2bdd55 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -113,7 +113,7 @@ class Translator extends Preprocessor $climate->bold('OPTIONS:'); $climate->tab()->out('-O Optimization level (0-3, default: 0)'); - $climate->tab()->out('-p, --profile Enable performance profiling'); + $climate->tab()->out('--profile Enable performance profiling (adds -lprofiler, forces recompile)'); $climate->tab()->out('-d, --debug Enable debug mode (auto-disable optimizations, add debug symbols)'); $climate->tab()->out('--cxx-std C++ standard version (c++17, c++20, etc.)'); $climate->tab()->out('-o, --output Output binary name (default: input basename)'); @@ -177,8 +177,12 @@ class Translator extends Preprocessor $this->noLiteralStrings = true; } - // 启用性能分析 + // 启用性能分析(需强制重编译 misc 文件以确保 PPROF_ON 宏生效,仅 Linux 支持) if ($this->climate->arguments->defined('profile')) { + if (!$this->isLinux()) { + $this->climate->error('--profile is only supported on Linux (requires gperftools)'); + exit(1); + } $this->enableProfiler = true; } @@ -694,7 +698,7 @@ CODE; */ public function hasMiscObjectFileCache(string $cppFile): bool { - if ($this->climate->arguments->defined('force')) { + if ($this->climate->arguments->defined('force') || $this->enableProfiler) { return false; } diff --git a/src/compiler.php b/src/compiler.php index 46594fd0..2fd129c7 100644 --- a/src/compiler.php +++ b/src/compiler.php @@ -7,6 +7,12 @@ function main(int $argc, array $argv): void define("ROOT_PATH", getcwd()); } + // .prof 文件分析模式:php bin/compiler.php app.prof + if ($argc >= 2 && str_ends_with($argv[1], '.prof')) { + profileAnalyze($argc, $argv); + return; + } + require_once ROOT_PATH . '/vendor/autoload.php'; global $translator; @@ -25,3 +31,30 @@ function main(int $argc, array $argv): void $translator->run($binaryFile); // never returns } } + +function profileAnalyze(int $argc, array $argv): void +{ + $profFile = $argv[1]; + + if (!file_exists($profFile)) { + fwrite(STDERR, "Profile file not found: {$profFile}\n"); + exit(1); + } + + // 从 prof 文件名推导二进制文件名(app.prof → app) + $binary = basename($profFile, '.prof'); + if (!file_exists($binary) && file_exists('./' . $binary)) { + $binary = './' . $binary; + } + + if (!file_exists($binary)) { + fwrite(STDERR, "Binary not found: {$binary} (expected from prof file name)\n"); + fwrite(STDERR, "Usage: php bin/compiler.php .prof\n"); + exit(1); + } + + $cmd = 'pprof --web ' . escapeshellarg($binary) . ' ' . escapeshellarg($profFile); + fwrite(STDERR, "Running: {$cmd}\n"); + passthru($cmd, $exitCode); + exit($exitCode); +} diff --git a/tests/aot/class/objval.phpt b/tests/aot/class/objval.phpt new file mode 100644 index 00000000..5d78ed4a --- /dev/null +++ b/tests/aot/class/objval.phpt @@ -0,0 +1,42 @@ +--TEST-- +objval +--FILE-- +x = $x; + $this->y = $y; + $this->action = $action; + } + + public function getX(): int { + return $this->x; + } +} + +function wrapObjval($ev): TestEvent +{ + return objval($ev, TestEvent::class); +} + +function main() { + $ev = new TestEvent(42, 0, 'base'); + $ev2 = wrapObjval($ev); + var_dump($ev2->x); + var_dump($ev2->getX()); + + echo "done\n"; +} + +?> +--EXPECT-- +int(42) +int(42) +done