From 92d8add2aa5354b3be153135121efd1c53c39e28 Mon Sep 17 00:00:00 2001 From: PurHur Date: Sat, 23 May 2026 18:45:41 +0200 Subject: [PATCH 1/2] Fix self-host probe after value-box property slot regression (#1080). Restore typed object-property loadValue/operandJitType, narrow isValueOperand to TYPE_VALUE slots, unwrap value-box property slots to __object__* for native call args, and map VM null defaults for class properties. Co-authored-by: Cursor --- lib/JIT/Call/Native.php | 19 +++++++++++++++++++ lib/JIT/Call/Native.pre | 19 +++++++++++++++++++ lib/JIT/Helper.php | 33 ++++++++++++++++++++++++++------- lib/JIT/Helper.pre | 33 ++++++++++++++++++++++++++------- lib/JIT/JitValueBox.php | 4 +--- 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/lib/JIT/Call/Native.php b/lib/JIT/Call/Native.php index e36a7cbb..1971024a 100644 --- a/lib/JIT/Call/Native.php +++ b/lib/JIT/Call/Native.php @@ -64,6 +64,25 @@ protected function compileArg(Context $context, Variable $arg, int $argNum): Val $value = $context->helper->loadValue($arg); switch ($typeName) { case '__object__*': + if ( + null !== $arg->objectPropertySlot + && Variable::TYPE_VALUE === $arg->objectPropertyType + ) { + return $context->builder->call( + $context->lookupFunction('__value__readObject'), + $value + ); + } + $valueTy = $value->typeOf(); + if ( + \PHPLLVM\Type::KIND_POINTER === $valueTy->getKind() + && '__value__' === $context->getStringFromType($valueTy->getElementType()) + ) { + return $context->builder->call( + $context->lookupFunction('__value__readObject'), + $value + ); + } switch ($arg->type) { case Variable::TYPE_OBJECT: return $value; diff --git a/lib/JIT/Call/Native.pre b/lib/JIT/Call/Native.pre index 571aaa06..69d4f2f2 100755 --- a/lib/JIT/Call/Native.pre +++ b/lib/JIT/Call/Native.pre @@ -63,6 +63,25 @@ class Native implements Call { $value = $context->helper->loadValue($arg); switch ($typeName) { case '__object__*': + if ( + null !== $arg->objectPropertySlot + && Variable::TYPE_VALUE === $arg->objectPropertyType + ) { + return $context->builder->call( + $context->lookupFunction('__value__readObject'), + $value + ); + } + $valueTy = $value->typeOf(); + if ( + \PHPLLVM\Type::KIND_POINTER === $valueTy->getKind() + && '__value__' === $context->getStringFromType($valueTy->getElementType()) + ) { + return $context->builder->call( + $context->lookupFunction('__value__readObject'), + $value + ); + } switch ($arg->type) { case Variable::TYPE_OBJECT: return $value; diff --git a/lib/JIT/Helper.php b/lib/JIT/Helper.php index 338934a9..0b1e6bcd 100644 --- a/lib/JIT/Helper.php +++ b/lib/JIT/Helper.php @@ -978,16 +978,38 @@ public function loadValue(Variable $variable): PHPLLVM\Value { $this->context->getTypeFromString('__hashtable__*') ); } + if (Variable::TYPE_OBJECT === $variable->objectPropertyType) { + return $this->context->builder->pointerCast( + $loaded, + $this->context->getTypeFromString('__object__*') + ); + } if (Variable::TYPE_STRING === $variable->objectPropertyType) { return $this->context->builder->pointerCast( $loaded, $this->context->getTypeFromString('__string__*') ); } + if (Variable::TYPE_VALUE === $variable->objectPropertyType) { + $valuePtr = $this->context->builder->pointerCast( + $loaded, + $this->context->getTypeFromString('__value__*') + ); + if (Variable::TYPE_OBJECT === $variable->type) { + return $this->context->builder->call( + $this->context->lookupFunction('__value__readObject'), + $valuePtr + ); + } + + return $valuePtr; + } + + $llvmType = Variable::getStringType($variable->objectPropertyType); return $this->context->builder->pointerCast( $loaded, - $this->context->getTypeFromString('__value__*') + $this->context->getTypeFromString($llvmType) ); } if ($variable->kind === Variable::KIND_VALUE) { @@ -1007,14 +1029,11 @@ private static function isOrderedCompareOpcode(int $opcodeType): bool private function operandJitType(Variable $var): int { if (null !== $var->objectPropertySlot && null !== $var->objectPropertyType) { - if (Variable::TYPE_HASHTABLE === $var->objectPropertyType) { - return Variable::TYPE_HASHTABLE; - } - if (Variable::TYPE_STRING === $var->objectPropertyType) { - return Variable::TYPE_STRING; + if (Variable::TYPE_VALUE === $var->objectPropertyType) { + return Variable::TYPE_VALUE; } - return Variable::TYPE_VALUE; + return $var->objectPropertyType; } return $var->type; diff --git a/lib/JIT/Helper.pre b/lib/JIT/Helper.pre index 249ebd66..9b121a81 100644 --- a/lib/JIT/Helper.pre +++ b/lib/JIT/Helper.pre @@ -444,16 +444,38 @@ return_bool: $this->context->getTypeFromString('__hashtable__*') ); } + if (Variable::TYPE_OBJECT === $variable->objectPropertyType) { + return $this->context->builder->pointerCast( + $loaded, + $this->context->getTypeFromString('__object__*') + ); + } if (Variable::TYPE_STRING === $variable->objectPropertyType) { return $this->context->builder->pointerCast( $loaded, $this->context->getTypeFromString('__string__*') ); } + if (Variable::TYPE_VALUE === $variable->objectPropertyType) { + $valuePtr = $this->context->builder->pointerCast( + $loaded, + $this->context->getTypeFromString('__value__*') + ); + if (Variable::TYPE_OBJECT === $variable->type) { + return $this->context->builder->call( + $this->context->lookupFunction('__value__readObject'), + $valuePtr + ); + } + + return $valuePtr; + } + + $llvmType = Variable::getStringType($variable->objectPropertyType); return $this->context->builder->pointerCast( $loaded, - $this->context->getTypeFromString('__value__*') + $this->context->getTypeFromString($llvmType) ); } if ($variable->kind === Variable::KIND_VALUE) { @@ -465,14 +487,11 @@ return_bool: private function operandJitType(Variable $var): int { if (null !== $var->objectPropertySlot && null !== $var->objectPropertyType) { - if (Variable::TYPE_HASHTABLE === $var->objectPropertyType) { - return Variable::TYPE_HASHTABLE; - } - if (Variable::TYPE_STRING === $var->objectPropertyType) { - return Variable::TYPE_STRING; + if (Variable::TYPE_VALUE === $var->objectPropertyType) { + return Variable::TYPE_VALUE; } - return Variable::TYPE_VALUE; + return $var->objectPropertyType; } return $var->type; diff --git a/lib/JIT/JitValueBox.php b/lib/JIT/JitValueBox.php index 1eeec18d..0359c4a6 100644 --- a/lib/JIT/JitValueBox.php +++ b/lib/JIT/JitValueBox.php @@ -43,9 +43,7 @@ public static function isValueOperand(Variable $var): bool return true; } return null !== $var->objectPropertySlot - && null !== $var->objectPropertyType - && Variable::TYPE_HASHTABLE !== $var->objectPropertyType - && Variable::TYPE_STRING !== $var->objectPropertyType; + && Variable::TYPE_VALUE === $var->objectPropertyType; } /** From 6c70656f593837fab0775892c92f4dadd3e5f689 Mon Sep 17 00:00:00 2001 From: PurHur Date: Sat, 23 May 2026 20:23:10 +0200 Subject: [PATCH 2/2] Fix self-host probe: property slots, object ===, stub hot paths (#1080) - Restore TYPE_OBJECT property loadValue/operandJitType paths - Native call: unwrap __value__ property slots to __object__* - Object identical compare via void* + ptrToInt - Stub isRedundantCoalesceTailAssign, all Superglobals, resolve() under self-host AOT Co-authored-by: Cursor --- lib/JIT.php | 10 ++++++++-- lib/JIT/Helper.php | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/JIT.php b/lib/JIT.php index 3cefbf24..0fe081dd 100644 --- a/lib/JIT.php +++ b/lib/JIT.php @@ -381,7 +381,8 @@ private function isSkippedCompilerHotPathName(string $name): bool || str_contains($lower, 'findcoalesce') || str_contains($lower, 'resolvecoalesce') || str_contains($lower, 'resolveisset') - || str_contains($lower, 'operandschainequal'); + || str_contains($lower, 'operandschainequal') + || str_contains($lower, 'isredundantcoalescetailassign'); } private function isSkippedSelfHostEntryName(string $name): bool @@ -416,7 +417,7 @@ private function isSkippedWebBootstrapHotPathName(string $name): bool || str_contains($lower, 'deployroot') || str_contains($lower, 'sourcebundler') || (str_contains($lower, '\\web\\conststringfolder::') && !$this->isConstStringFolderRealLoweringMethod($lower)) - || (str_contains($lower, '\\web\\superglobals::') && !str_ends_with($lower, '::issuperglobalname')); + || str_contains($lower, '\\web\\superglobals::'); } @@ -439,6 +440,11 @@ private function isSkippedLibSpineSmokeHotPathName(string $name): bool /** IncludePathResolver methods with safe LLVM 9 lowering during self-host AOT (#816). */ private function isIncludePathResolverRealLoweringMethod(string $lower): bool { + // resolve() nullable return still hits ICmp type mismatch in full self-host probe (#1097). + if ($this->shouldUseSelfHostJitStubs()) { + return false; + } + return str_ends_with($lower, '::resolve'); } diff --git a/lib/JIT/Helper.php b/lib/JIT/Helper.php index 0b1e6bcd..1fcb68b4 100644 --- a/lib/JIT/Helper.php +++ b/lib/JIT/Helper.php @@ -884,12 +884,18 @@ public function binaryOp(OpCode $opcode, Variable $left, Variable $right): Varia } } if (Variable::TYPE_OBJECT === $leftType && $leftType === $rightType) { + $voidp = $this->context->getTypeFromString('void')->pointerType(0); + $leftNorm = $this->context->builder->pointerCast($leftValue, $voidp); + $rightNorm = $this->context->builder->pointerCast($rightValue, $voidp); + $sizeT = $this->context->getTypeFromString('size_t'); + $leftPtr = $this->context->builder->ptrToInt($leftNorm, $sizeT); + $rightPtr = $this->context->builder->ptrToInt($rightNorm, $sizeT); if (OpCode::TYPE_IDENTICAL === $opcode->type) { - $result = $this->context->builder->icmp(Builder::INT_EQ, $leftValue, $rightValue); + $result = $this->context->builder->icmp(Builder::INT_EQ, $leftPtr, $rightPtr); goto return_bool; } if (OpCode::TYPE_NOT_IDENTICAL === $opcode->type) { - $result = $this->context->builder->icmp(Builder::INT_NE, $leftValue, $rightValue); + $result = $this->context->builder->icmp(Builder::INT_NE, $leftPtr, $rightPtr); goto return_bool; } }