From 89c794f4af8dab92f87e87fae12fa2852e05a5be Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 26 May 2026 19:54:30 +0000 Subject: [PATCH] Self-host M3: LLVM stubs for emit-TU link without PHP CFG (#2540) Route Runtime/Compiler spine and bundled lib/ to native LLVM stubs during M3 emit-TU link instead of PHP CFG lowering (LLVM 9 global ctor segfault). Simplify {main} to PHP CFG and avoid object allocate in the emit bridge. Closes #2540 Co-authored-by: Cursor --- lib/JIT.php | 163 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 29 deletions(-) diff --git a/lib/JIT.php b/lib/JIT.php index 4e5814c2138..8e1bc8c2b25 100644 --- a/lib/JIT.php +++ b/lib/JIT.php @@ -296,6 +296,9 @@ private function jitFunctionSkipName(?string $name, Block $block): string private function compileBlock(Block $block, ?string $funcName = null): PHPLLVM\Value { $logicalName = $funcName; + if (null !== $logicalName && null !== $block->func) { + JIT\Progress::noteFunction($block->func->getScopedName()); + } $skipName = $this->jitFunctionSkipName($logicalName, $block); if (!is_null($funcName)) { $internalName = $this->llvmInternalName($funcName); @@ -308,6 +311,31 @@ private function compileBlock(Block $block, ?string $funcName = null): PHPLLVM\V if ($this->shouldUseM3EmitTuNativeBridge() && $this->isM3EmitTuScriptMain($block)) { return $this->compileM3EmitTuMainNative($internalName, $block, $logicalName); } + if ($this->shouldUseM3EmitTuNativeBridge() && null !== $logicalName) { + $m3EmitRuntime = strtolower($logicalName); + if ($this->isM3EmitTuRuntimeSpineLoweringName($m3EmitRuntime)) { + return $this->compileM3EmitTuRuntimeSpineStub( + $internalName, + $block, + $logicalName, + $m3EmitRuntime + ); + } + if ($this->isM3EmitTuCompilerSpineLoweringName($m3EmitRuntime)) { + if ('phpcompiler\\compiler::compileemitsmoke' === $m3EmitRuntime) { + return $this->emitM3EmitTuCompilerCompileEmitSmokeNativeFunction( + $internalName, + $logicalName + ); + } + + return $this->compileSkippedCompilerSplitCfgStub( + $internalName, + $block, + $logicalName ?? $internalName + ); + } + } if ( null !== $logicalName && $this->shouldUseM3EmitTuNativeBridge() @@ -402,6 +430,10 @@ private function compileBlock(Block $block, ?string $funcName = null): PHPLLVM\V ) { return $this->compileSkippedCompilerSplitCfgStub($internalName, $block, $logicalName ?? $internalName); } + // Emit TU: never PHP-lowering bundled lib/ (LLVM 9 global ctor; #2540). + if ($this->shouldUseM3EmitTuNativeBridge() && null !== $logicalName) { + return $this->compileSkippedCompilerSplitCfgStub($internalName, $block, $logicalName ?? $internalName); + } return $this->compileBlockPhpLowering($internalName, $block, $logicalName, $funcName); } @@ -895,8 +927,8 @@ private function isM3EmitHelperCompilerPhpLoweringName(string $lower): bool if (!$this->shouldUseEmitHelperLinkStubs()) { return false; } - // Emit TU uses native/stub Compiler spine — not full CFG lowering (#2442). - if ($this->shouldUseM3EmitTuNativeBridge() && $this->isM3EmitTuCompilerSpineLoweringName($lower)) { + // Emit TU links via native bridge + LLVM stubs; PHP CFG here segfaults LLVM 9 (#2540). + if ($this->shouldUseM3EmitTuNativeBridge()) { return false; } if ($this->isM3EmitTuCompilerSpineLoweringName($lower)) { @@ -1041,6 +1073,7 @@ private function isM3EmitTuRuntimeSpineLoweringName(string $lower): bool 'compileemitsmoke', 'parseandcompile', 'parseandcompileemitsmoke', + 'parseandcompilefile', 'standalone', 'loadjit', 'jitcompileblock', @@ -1209,7 +1242,8 @@ private function isSkippedLibSpineSmokeHotPathName(string $name): bool || str_contains($lower, '\\jit\\builtin\\stringparsestr::') || str_contains($lower, '\\builtinparamnames::') || str_contains($lower, '\\jit\\builtin\\type\\object_::') - || str_contains($lower, '\\jit\\builtin\\type\\hashtable::'); + || str_contains($lower, '\\jit\\builtin\\type\\hashtable::') + || ($this->shouldUseEmitHelperLinkStubs() && str_contains($lower, '\\phptypes\\')); } /** IncludePathResolver methods with safe LLVM 9 lowering during self-host AOT (#816). */ @@ -1484,36 +1518,12 @@ private function compileBootstrapCompileSmokeM3EmitNative( return $func; } - /** Native {main} for M3 emit TU — getenv + bridge only (#1937). */ + /** Native {main} for M3 emit TU — thin PHP in entry; bridge on BootstrapAot\\*_m3_emit (#1937, #2540). */ private function compileM3EmitTuMainNative(string $internalName, Block $block, ?string $logicalName): PHPLLVM\Value { - $this->runQueue(); $this->compileM3EmitTuRuntimeSpineDecls(); - $lcname = strtolower($logicalName ?? '{main}'); - if (isset($this->context->functions[$lcname])) { - return $this->context->functions[$lcname]; - } - $i64 = $this->context->getTypeFromString('int64'); - $func = $this->context->module->addFunction( - $this->llvmInternalName($internalName), - $this->context->context->functionType($i64, false) - ); - $bb = $func->appendBasicBlock('entry'); - $saved = $this->context->builder; - $this->context->builder = $this->context->context->builderCreate(); - $this->context->builder->positionAtEnd($bb); - $logPrefix = getenv('PHP_COMPILER_M3_EMIT_LOG_PREFIX'); - if (!is_string($logPrefix) || '' === $logPrefix) { - $logPrefix = 'compile_smoke_m3_emit'; - } - \PHPCompiler\JIT\BootstrapCompileSmokeM3Emit::emitMainEntry($this->context, $logPrefix); - $this->context->builder->clearInsertionPosition(); - $this->context->builder = $saved; - $this->context->functions[$lcname] = $func; - $this->context->functionReturnType[$lcname] = 'int64'; - $this->context->functionProxies[$lcname] = new JIT\Call\Native($func, $logicalName ?? '{main}', [], []); - return $func; + return $this->compileBlockPhpLowering($internalName, $block, $logicalName, $logicalName); } /** Lower Runtime/Compiler spine before native emit bridge (#1937). */ @@ -1543,10 +1553,67 @@ private function compileM3EmitTuRuntimeSpineDecls(): void 'PHPCompiler\\Runtime::__construct', $stubBlock ); + foreach ([ + 'PHPCompiler\\Runtime::compile', + 'PHPCompiler\\Runtime::parseAndCompile', + 'PHPCompiler\\Runtime::parseAndCompileEmitSmoke', + ] as $logical) { + $this->emitM3EmitTuRuntimeBlockPtrStubNative( + $this->llvmInternalName($logical), + $logical, + $stubBlock + ); + } $this->compileM3EmitTuCompilerEmitSmokeNativeDecl(); $this->runQueue(); } + /** + * Emit TU: native LLVM stubs for Runtime spine — avoid PHP CFG (PHPTypes global ctor; #2540). + */ + private function compileM3EmitTuRuntimeSpineStub( + string $internalName, + Block $block, + string $logicalName, + string $lower + ): PHPLLVM\Value { + if (str_ends_with($lower, '\\runtime::__construct')) { + return $this->emitM3EmitTuRuntimeConstructNativeFunction($internalName, $logicalName, $block); + } + if (str_ends_with($lower, '\\runtime::initvmcontext')) { + return $this->emitM3EmitTuRuntimeInitVoidStub($internalName, $logicalName, $block); + } + if ( + str_ends_with($lower, '\\runtime::initparsepipeline') + || str_ends_with($lower, '\\runtime::initcompiler') + || str_ends_with($lower, '\\runtime::loadcoremodules') + || str_ends_with($lower, '\\runtime::loadjit') + || str_ends_with($lower, '\\runtime::jitcompileblock') + || str_ends_with($lower, '\\runtime::jitemitinplace') + ) { + return $this->emitM3EmitTuRuntimeInitVoidStub($internalName, $logicalName, $block); + } + if (str_ends_with($lower, '\\runtime::parse')) { + return $this->emitM3EmitTuRuntimeParseStubNative($internalName, $logicalName, $block); + } + if (str_ends_with($lower, '\\runtime::compileemitsmoke')) { + return $this->emitM3EmitTuRuntimeCompileEmitSmokeNative($internalName, $logicalName, $block); + } + if (str_ends_with($lower, '\\runtime::standalone')) { + return $this->emitM3EmitTuRuntimeStandaloneStubNative($internalName, $logicalName, $block); + } + if ( + str_ends_with($lower, '\\runtime::compile') + || str_ends_with($lower, '\\runtime::parseandcompile') + || str_ends_with($lower, '\\runtime::parseandcompileemitsmoke') + || str_ends_with($lower, '\\runtime::parseandcompilefile') + ) { + return $this->emitM3EmitTuRuntimeBlockPtrStubNative($internalName, $logicalName, $block); + } + + throw new \LogicException('Unhandled M3 emit TU Runtime spine: '.$logicalName); + } + /** Stub Runtime::parse for emit TU link — Batch A replaces with real parser (#2516). */ private function emitM3EmitTuRuntimeParseStubNative( string $internalName, @@ -1582,6 +1649,44 @@ private function emitM3EmitTuRuntimeParseStubNative( return $func; } + /** Stub Runtime methods returning ?Block for emit TU link (#2540). */ + private function emitM3EmitTuRuntimeBlockPtrStubNative( + string $internalName, + string $logicalName, + Block $block + ): PHPLLVM\Value { + $lcname = strtolower($logicalName); + if (isset($this->context->functions[$lcname])) { + return $this->context->functions[$lcname]; + } + $objectPtr = $this->context->getTypeFromString('__object__*'); + $args = $this->normalizeSelfHostNativeCallArgTypes( + $this->collectStubFunctionArgTypes($block), + $logicalName + ); + $func = $this->context->module->addFunction( + $internalName, + $this->context->context->functionType($objectPtr, false, ...$args) + ); + $bb = $func->appendBasicBlock('entry'); + $saved = $this->context->builder; + $this->context->builder = $this->context->context->builderCreate(); + $this->context->builder->positionAtEnd($bb); + $this->context->builder->returnValue($objectPtr->constNull()); + $this->context->builder->clearInsertionPosition(); + $this->context->builder = $saved; + $this->context->functions[$lcname] = $func; + $this->context->functionReturnType[$lcname] = '__object__*'; + $this->context->functionProxies[$lcname] = new JIT\Call\Native( + $func, + $logicalName, + $args, + $this->collectParamDefaults($block) + ); + + return $func; + } + /** Native Runtime::compileEmitSmoke — reuse Compiler emit-smoke block stub (#2442). */ private function emitM3EmitTuRuntimeCompileEmitSmokeNative( string $internalName,