move pgo from new to static creation method

This commit is contained in:
henderkes
2026-04-30 19:46:07 +07:00
parent 5293f76eaf
commit 814d3cba58
3 changed files with 66 additions and 67 deletions

View File

@@ -23,6 +23,8 @@ class LinuxBuilder extends UnixBuilderBase
/** @var bool Micro patch phar flag */
private bool $phar_patched = false;
private ?PgoManager $pgo = null;
public function __construct(array $options = [])
{
$this->options = $options;
@@ -49,6 +51,8 @@ class LinuxBuilder extends UnixBuilderBase
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{
$this->pgo = PgoManager::fromBuilder($this, $build_target);
$cflags = $this->arch_c_flags;
f_putenv('CFLAGS=' . $cflags);
@@ -134,7 +138,7 @@ class LinuxBuilder extends UnixBuilderBase
$this->cleanMake();
$pgo = PgoManager::active();
$pgo = $this->pgo;
$needsClean = false;
$sapiBuilds = [
['cli', $enableCli, true, fn () => $this->buildCli()],

View File

@@ -12,7 +12,6 @@ use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\LicenseDumper;
use SPC\util\PgoManager;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
@@ -215,20 +214,6 @@ class BuildPHPCommand extends BuildCommand
// clean old modules that may conflict with the new php build
FileSystem::removeDir(BUILD_MODULES_PATH);
$pgi = (bool) $this->getOption('pgi');
$csPgi = (bool) $this->getOption('cs-pgi');
$pgo = (bool) $this->getOption('pgo');
if (((int) $pgi + (int) $csPgi + (int) $pgo) > 1) {
$this->output->writeln('<error>--pgi, --cs-pgi, and --pgo are mutually exclusive</error>');
return static::FAILURE;
}
if ($pgi) {
(new PgoManager())->setupInstrument($rule);
} elseif ($csPgi) {
(new PgoManager())->setupCsInstrument($rule);
} elseif ($pgo) {
(new PgoManager())->setupUse($rule);
}
$builder->buildPHP($rule);
$builder->testPHP($rule);

View File

@@ -43,64 +43,29 @@ class PgoManager
private string $mode;
private static ?self $active = null;
public function __construct()
private function __construct()
{
$this->profileRoot = BUILD_ROOT_PATH . '/pgo-data';
}
public static function active(): ?self
/** Build a PgoManager for the active --pgi/--cs-pgi/--pgo option, or null if none set. */
public static function fromBuilder(BuilderBase $builder, int $rule): ?self
{
return self::$active;
}
/** Setup --pgi: build with -fprofile-generate=<sapi-dir>. */
public function setupInstrument(int $rule): void
{
$this->validateRule($rule);
FileSystem::removeDir($this->profileRoot);
f_mkdir($this->profileRoot, recursive: true);
foreach ($this->trainableIn($rule) as $sapi) {
f_mkdir($this->rawDir($sapi), recursive: true);
$modes = array_filter(['pgi', 'cs-pgi', 'pgo'], fn ($m) => (bool) $builder->getOption($m));
if (count($modes) > 1) {
throw new WrongUsageException('--pgi, --cs-pgi, and --pgo are mutually exclusive');
}
$this->mode = self::MODE_INSTRUMENT;
self::$active = $this;
$this->applyShutdownPatches();
$this->applyForSapi($this->trainableIn($rule)[0]);
logger()->info('pgo --pgi: instrumented build, profraw will land under ' . $this->profileRoot . '/<sapi>/');
}
/** Setup --cs-pgi: build with -fprofile-use=<sapi.profdata> -fcs-profile-generate=<cs-dir>. Requires existing .profdata. */
public function setupCsInstrument(int $rule): void
{
$this->validateRule($rule);
foreach ($this->trainableIn($rule) as $sapi) {
if (!is_file($this->profDataFile($sapi))) {
throw new WrongUsageException("--cs-pgi: missing {$sapi}.profdata; run --pgi + --pgo first");
}
f_mkdir($this->csRawDir($sapi), recursive: true);
$mode = array_values($modes)[0] ?? null;
if ($mode === null) {
return null;
}
$this->mode = self::MODE_CS_INSTRUMENT;
self::$active = $this;
$this->applyShutdownPatches();
$this->applyForSapi($this->trainableIn($rule)[0]);
logger()->info('pgo --cs-pgi: cs-instrumented build, cs-profraw under ' . $this->profileRoot . '/cs-<sapi>/');
}
/** Setup --pgo: merge collected .profraw, then build with -fprofile-use=<sapi.profdata>. */
public function setupUse(int $rule): void
{
$this->validateRule($rule);
if (trim((string) shell_exec('command -v llvm-profdata 2>/dev/null')) === '') {
throw new WrongUsageException('--pgo: llvm-profdata not on PATH');
}
foreach ($this->trainableIn($rule) as $sapi) {
$this->mergeSapi($sapi);
}
$this->mode = self::MODE_USE;
self::$active = $this;
$this->applyForSapi($this->trainableIn($rule)[0]);
$instance = new self();
match ($mode) {
'pgi' => $instance->setupInstrument($rule),
'cs-pgi' => $instance->setupCsInstrument($rule),
'pgo' => $instance->setupUse($rule),
};
return $instance;
}
/** Patches php-src/libtool to passthrough -fcs-profile-* flags (otherwise dropped during shared lib link). */
@@ -155,6 +120,51 @@ class PgoManager
logger()->info("pgo {$this->mode} ({$sapi})");
}
/** Setup --pgi: build with -fprofile-generate=<sapi-dir>. */
private function setupInstrument(int $rule): void
{
$this->validateRule($rule);
FileSystem::removeDir($this->profileRoot);
f_mkdir($this->profileRoot, recursive: true);
foreach ($this->trainableIn($rule) as $sapi) {
f_mkdir($this->rawDir($sapi), recursive: true);
}
$this->mode = self::MODE_INSTRUMENT;
$this->applyShutdownPatches();
$this->applyForSapi($this->trainableIn($rule)[0]);
logger()->info('pgo --pgi: instrumented build, profraw will land under ' . $this->profileRoot . '/<sapi>/');
}
/** Setup --cs-pgi: build with -fprofile-use=<sapi.profdata> -fcs-profile-generate=<cs-dir>. Requires existing .profdata. */
private function setupCsInstrument(int $rule): void
{
$this->validateRule($rule);
foreach ($this->trainableIn($rule) as $sapi) {
if (!is_file($this->profDataFile($sapi))) {
throw new WrongUsageException("--cs-pgi: missing {$sapi}.profdata; run --pgi + --pgo first");
}
f_mkdir($this->csRawDir($sapi), recursive: true);
}
$this->mode = self::MODE_CS_INSTRUMENT;
$this->applyShutdownPatches();
$this->applyForSapi($this->trainableIn($rule)[0]);
logger()->info('pgo --cs-pgi: cs-instrumented build, cs-profraw under ' . $this->profileRoot . '/cs-<sapi>/');
}
/** Setup --pgo: merge collected .profraw, then build with -fprofile-use=<sapi.profdata>. */
private function setupUse(int $rule): void
{
$this->validateRule($rule);
if (trim((string) shell_exec('command -v llvm-profdata 2>/dev/null')) === '') {
throw new WrongUsageException('--pgo: llvm-profdata not on PATH');
}
foreach ($this->trainableIn($rule) as $sapi) {
$this->mergeSapi($sapi);
}
$this->mode = self::MODE_USE;
$this->applyForSapi($this->trainableIn($rule)[0]);
}
/**
* Static-embed mode links libphp.a into frankenphp; both end up in one
* binary so must share one profdata. Shared-embed mode keeps libphp.so