Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9d19583
Bootstrap: fix stale self:: class binding blocking gen-0 spine refres…
PurHur Jul 23, 2026
5183b00
Bootstrap: literal GD2 defaults so Zend spine AOT can refresh gen-0 (…
PurHur Jul 23, 2026
85ce516
Bootstrap: harden #22642 self:: queue repro with VmIni requires
PurHur Jul 23, 2026
a136123
Bootstrap: honor raised LLVM memory limit for honest Zend spine (#22642)
PurHur Jul 23, 2026
df879c6
Bootstrap: raise CI ulimit -v for Zend full-spine gen-0 refresh (#22642)
PurHur Jul 23, 2026
08c92ec
Bootstrap: drop ulimit -v for Zend full-spine gen-0 refresh (#22642)
PurHur Jul 23, 2026
ae0bbb2
Bootstrap: ClassConstFetchHelper self-requires trait for Zend spine (…
PurHur Jul 23, 2026
b4e7c4e
Bootstrap: harness-protected exclusive gen-0 refresh launcher (#22642)
PurHur Jul 23, 2026
44b278e
Bootstrap: pin HEAD + trait self-require in exclusive gen-0 launcher …
PurHur Jul 24, 2026
945a5f7
Bootstrap: snapshot-bind exclusive gen-0 refresh against checkout dri…
PurHur Jul 24, 2026
a934620
Bootstrap: fall back to tar when rsync missing for gen-0 snapshot (#2…
PurHur Jul 24, 2026
03dedf1
Bootstrap: fix exclusive gen-0 heartbeat RSS (match php compile only)…
PurHur Jul 24, 2026
b417dff
Bootstrap: allow cross-unit trait use in TraitClassConstConflictCheck…
PurHur Jul 24, 2026
14a0482
Bootstrap: log parseAndCompile entry path in exclusive gen-0 heartbea…
PurHur Jul 24, 2026
7ad2d18
Bootstrap: isolate exclusive gen-0 refresh from live worktree mounts …
PurHur Jul 24, 2026
7ab92c0
Bootstrap: fix DateTime*/ docblock aborting Zend spine parse (#22642)
PurHur Jul 24, 2026
950385d
Bootstrap: spine nikic preflight + drop ghost file_get_contents kerne…
PurHur Jul 24, 2026
7acc4a0
Bootstrap: stop exclusive gen-0 launches from yanking a live peer sna…
PurHur Jul 24, 2026
8ea67da
Bootstrap: regenerate inventory after rebase onto master (#22642)
PurHur Jul 24, 2026
8de4995
Bootstrap: avoid VmMbstring mimeheader STATICCALL_INIT on spine (#22642)
PurHur Jul 24, 2026
525ec9c
Bootstrap: restore Zend spine pace — flat include slots + no cold hel…
PurHur Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 176 additions & 175 deletions docs/bootstrap-inventory.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions ext/gd/VmGd.php
Original file line number Diff line number Diff line change
Expand Up @@ -4108,8 +4108,8 @@ public static function createFromGdBytes(Frame $frame, string $data): ObjectEntr
*/
public static function encodedGd2Bytes(
ObjectEntry $image,
int $chunkSize = VmGdGd::GD2_CHUNKSIZE,
int $mode = VmGdGd::GD2_FMT_RAW
int $chunkSize = 128, // VmGdGd::GD2_CHUNKSIZE — literal default (#3803 / #22642 spine AOT)
int $mode = 1 // VmGdGd::GD2_FMT_RAW
): string {
$state = GdRegistry::state($image);
if (null === $state || !$state->hasRaster()) {
Expand All @@ -4122,8 +4122,8 @@ public static function encodedGd2Bytes(
public static function writeGd2ToOutput(
Frame $frame,
ObjectEntry $image,
int $chunkSize = VmGdGd::GD2_CHUNKSIZE,
int $mode = VmGdGd::GD2_FMT_RAW
int $chunkSize = 128, // VmGdGd::GD2_CHUNKSIZE — literal default (#3803 / #22642 spine AOT)
int $mode = 1 // VmGdGd::GD2_FMT_RAW
): bool {
OutputBuffer::append(self::encodedGd2Bytes($image, $chunkSize, $mode), $frame->scriptPath ?: null);

Expand Down
15 changes: 14 additions & 1 deletion lib/Compiler/TraitClassConstConflictCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
* Compile-time check: incompatible trait/class constant composition (#8882, #7012, #5385).
*
* php-src: Zend/zend_traits.c — zend_traits_compile_role_constants
*
* Incremental AOT ({@see \PHPCompiler\JIT\IncludeHelper}) compiles each include as its
* own {@see Script}. A class that `use`s a trait defined only in a prior unit must not
* fatal here — const conflict checks simply do not apply without that trait's AST
* (#22642 ClassConstFetchHelper / ClassConstFetchHelperTrait spine split).
*/
final class TraitClassConstConflictCheck
{
Expand Down Expand Up @@ -177,7 +182,15 @@ private function verifyTypeComposition(string $lc, array $type, bool $isTrait):
if (LazyGhostTraitSupport::isLazyGhostTrait($traitUse['display'])) {
continue;
}
$this->throwTraitNotFound($traitUse['display'], $traitUse['file'], $traitUse['line']);
// Class/enum in this Script used as a trait (#12868, class_use_self_not_trait).
if (isset($this->compositions[$traitLc])) {
$this->throwTraitNotFound($traitUse['display'], $traitUse['file'], $traitUse['line']);
}
// Trait lives in another include unit (incremental AOT / spine). Const
// composition checks need the trait AST in *this* Script; skip when
// absent so e.g. ClassConstFetchHelper.php can compile after
// ClassConstFetchHelperTrait.php (#22642). Same-file conflicts still run.
continue;
}
foreach ($this->effectiveTraitConstants($traitLc) as $constLc => $traitConst) {
if (null === $traitConst['value']) {
Expand Down
18 changes: 15 additions & 3 deletions lib/JIT.php
Original file line number Diff line number Diff line change
Expand Up @@ -358,17 +358,29 @@ private function runQueue(): void {
// runQueue copies the previous item's className; after DECLARE_CLASS popScope it is
// empty, and after another helper's method it is stale — both break NestedJIT
// self::$props (#22037). Traits keep composing-class binding from above (#18878).
// Prefer activeFunction's "Class::method" prefix as SSOT — func->class can lag the
// queued scoped name in large spine bundles (IniJitHelper vs InArrayJitHelper, #22642).
$declaring = '';
if (null !== $cfgBlock->func->class) {
$declaring = (string) $cfgBlock->func->class->value;
}
$active = $this->context->activeFunction;
$activeSep = strpos($active, '::');
if (false !== $activeSep) {
$fromActive = substr($active, 0, $activeSep);
if ('' !== $fromActive) {
$declaring = $fromActive;
}
}
if ('' !== $declaring) {
$declLc = strtolower(ltrim($declaring, '\\'));
if (!$this->context->type->object->isTraitClass($declLc)) {
$this->context->scope->className = $declLc;
if ($this->context->type->object->hasDeclaredClass($declaring)) {
$this->context->scope->classId = $this->context->type->object->lookup($declaring);
}
$this->context->scope->classId = $this->context->type->object->lookup($declaring);
$this->context->scope->calledClassName = $declLc;
}
}

} else {
foreach ($this->context->functions as $name => $candidate) {
if ($candidate === $llvmFunc) {
Expand Down
10 changes: 10 additions & 0 deletions lib/JIT/Builtin/Type/Object_.php
Original file line number Diff line number Diff line change
Expand Up @@ -4503,6 +4503,16 @@ public function resolveClassId(Operand $classOp): int
$name = strtolower($classOp->value);
if ('self' === $name) {
$declaringClass = $this->context->scope->className;
// Prefer activeFunction "Class::method" when scope className is stale from a prior
// queue item (large spine: IniJitHelper::iniGet saw InArrayJitHelper — #22642 / #22037).
$active = strtolower((string) ($this->context->activeFunction ?? ''));
$activeSep = strpos($active, '::');
if (false !== $activeSep) {
$fromActive = substr($active, 0, $activeSep);
if ('' !== $fromActive && !$this->isTraitClass($fromActive)) {
$declaringClass = $fromActive;
}
}
if ('' === $declaringClass) {
PseudoClassScope::fatalInGlobalScope('self');
}
Expand Down
4 changes: 4 additions & 0 deletions lib/JIT/ClassConstFetchHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

namespace PHPCompiler\JIT;

// Spine inventory is alphabetical (Helper before HelperTrait). Require the trait here so
// Zend full-spine AOT cannot hit "Trait not found" when composer autoload is not in play (#22642).
require_once __DIR__ . '/ClassConstFetchHelperTrait.php';

/**
* Intentionally small entrypoint: the lowering bodies live in {@see ClassConstFetchHelperTrait}
* to keep the helper file narrow while the php-in-php migration iterates (#10200).
Expand Down
14 changes: 12 additions & 2 deletions lib/JIT/IncludeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,18 @@ public static function compileInlinedBlock(
$context = $jit->context;

// Remap colliding parent slots — callee opcodes keep their indices; parent
// locals are name-bound via IncludeBindingJitHelper (#22845).
$included->inheritScopeFrom($callerBlock, true);
// locals are name-bound via IncludeBindingJitHelper (#22845 MiniWebApp).
// Zend full-spine may opt out via PHP_COMPILER_INCLUDE_SCOPE_REMAP=0: always-on
// remapping made honest gen-0 refresh ~25× slower (r14 vs r13 @ gmp, #22642).
$remapCollidingSlots = true;
$remapFlag = getenv('PHP_COMPILER_INCLUDE_SCOPE_REMAP');
if (is_string($remapFlag)) {
$remapLc = strtolower($remapFlag);
if ('0' === $remapFlag || 'false' === $remapLc || 'off' === $remapLc) {
$remapCollidingSlots = false;
}
}
$included->inheritScopeFrom($callerBlock, $remapCollidingSlots);
$included->inheritUndefinedLocals = true;

$context->inlineIncludeCallerBlocks[] = $callerBlock;
Expand Down
9 changes: 6 additions & 3 deletions lib/VM/SplArrayCastJitHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
/**
* Thin JIT/AOT helper for special (array) casts before zend_std property enumeration.
*
* Order: ArrayObject/ArrayIterator backing (#19631), then DateTime*/DateTimeZone/
* DateInterval/DatePeriod Zend wire (#22424, #22425, #22435). Returns null so the
* caller can fall back to get_object_vars.
* Order: ArrayObject/ArrayIterator backing (#19631), then DateTime / DateTimeImmutable /
* DateTimeZone / DateInterval / DatePeriod Zend wire (#22424, #22425, #22435). Returns
* null so the caller can fall back to get_object_vars.
*
* php-src: ext/spl/spl_array.c — spl_array_get_properties_for(ZEND_PROP_PURPOSE_ARRAY_CAST)
* php-src: ext/date/php_date.c — date_object_get_properties
*
* Note (#22642 r9): do not write a star immediately before a slash in block comments
* (e.g. DateTime-star-slash) — that token ends the docblock and breaks spine parseAndCompile.
*/
final class SplArrayCastJitHelper
{
Expand Down
13 changes: 13 additions & 0 deletions script/bootstrap-gen0-install-prelinked-driver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,20 @@ bootstrap_compiler_lib_honest_zend_compile() {
mkdir -p "$(dirname "${out}")"
rm -f "${out}"
# ci_apply_llvm_memory_env pins 4096M; full-spine sidecar host-compile OOMs below 8GB (#8559).
# Honor a higher PHP_COMPILER_MEMORY_LIMIT / PHP_COMPILER_LLVM_MEMORY_LIMIT when callers raise
# the Docker budget for verified-fresh gen-0 refresh (#22642) — never drop below 8192M.
local mem_limit="8192M"
local cand="" cand_num=0 mem_num=8192
mem_num="${mem_limit%[MmGg]}"
for cand in "${PHP_COMPILER_LLVM_MEMORY_LIMIT:-}" "${PHP_COMPILER_MEMORY_LIMIT:-}"; do
if [[ -n "${cand}" ]]; then
cand_num="${cand%[MmGg]}"
if [[ "${cand_num}" =~ ^[0-9]+$ && "${mem_num}" =~ ^[0-9]+$ && "${cand_num}" -gt "${mem_num}" ]]; then
mem_limit="${cand}"
mem_num="${cand_num}"
fi
fi
done
if [[ "${mode}" == "sidecar" ]]; then
env PHP_COMPILER_M3_EMIT_SIDECAR_RECURSION_GUARD=1 \
PHP_COMPILER_SELFHOST_AOT=1 \
Expand Down
18 changes: 17 additions & 1 deletion script/bootstrap-gen0-manifest-lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ function bootstrap_gen0_manifest_refresh_from_disk(string $root): array
/**
* Record lowering provenance after a verified-fresh copy into prelinked/bootstrap-gen0/ (#21905).
*
* When stamping the live fingerprint (null $fingerprint), requires build/.bootstrap_lowering_source.sha
* to already match — written by script/bootstrap-refresh-gen0-sidecar.sh after spine link.
* That rejects restamp-only edits that invent provenance without a verified-fresh build (#22642).
*
* @return array<string, mixed>
*/
function bootstrap_gen0_manifest_stamp_lowering_fingerprint(string $root, ?string $fingerprint = null): array
Expand All @@ -288,13 +292,25 @@ function bootstrap_gen0_manifest_stamp_lowering_fingerprint(string $root, ?strin
throw new \RuntimeException('missing or invalid '.bootstrap_gen0_manifest_path($root));
}
$fp = $fingerprint;
if (null === $fp || '' === $fp) {
$stampingLive = null === $fp || '' === $fp;
if ($stampingLive) {
$fp = bootstrap_gen0_manifest_live_lowering_fingerprint($root);
}
$fp = strtolower(trim($fp));
if (!preg_match('/^[a-f0-9]{64}$/', $fp)) {
throw new \RuntimeException('invalid lowering_source_fingerprint (want 64 hex chars)');
}
if ($stampingLive) {
$buildStamp = rtrim($root, '/').'/build/.bootstrap_lowering_source.sha';
$have = is_readable($buildStamp) ? strtolower(trim((string) file_get_contents($buildStamp))) : '';
if ($have !== $fp) {
throw new \RuntimeException(
'refusing to stamp live lowering_source_fingerprint without a matching build/'.
'.bootstrap_lowering_source.sha (run script/bootstrap-refresh-gen0-sidecar.sh,'.
' not a restamp-only edit — #22642/#21905)'
);
}
}
$manifest['lowering_source_fingerprint'] = $fp;
$manifest['generated_at'] = gmdate('c');

Expand Down
Loading