From 744decca7e0cbd8871151dadf4b15c0627c1d78a Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 1 Sep 2026 19:35:05 +0000 Subject: [PATCH] Fix php-llvm-structgep-assert.patch drift blocking CI (#36209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-diff structgep patch against pristine php-llvm Builder (removed fictional null-check context), add --verify-pristine gate with committed snapshot, require reverse-apply proof for structgep skip, fix loop-exit-tick Do anchor (Echo not Enum), and run verify-pristine in workflows before apply-patches. Verified: script/apply-patches.sh --verify-pristine rm -rf vendor && composer install && script/apply-patches.sh ./script/aot-smoke.sh → 8/8 make dev-verify-fast → OK (59s) Closes #36209 Co-authored-by: Cursor --- .github/workflows/bootstrap-spine-gate.yml | 2 + .github/workflows/compiler-gate.yml | 2 + .github/workflows/compliance-host-compare.yml | 1 + .github/workflows/compliance-nightly.yml | 1 + .github/workflows/compliance-pr.yml | 1 + patches/php-llvm-structgep-assert.patch | 10 +- .../php-llvm/lib/LLVMAbstract/Builder.php | 525 ++++++++++++++++++ script/apply-patches.sh | 88 ++- script/check-generated-docs.sh | 3 + script/ci-common.sh | 2 +- 10 files changed, 626 insertions(+), 9 deletions(-) create mode 100755 patches/pristine-snapshots/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php diff --git a/.github/workflows/bootstrap-spine-gate.yml b/.github/workflows/bootstrap-spine-gate.yml index c3ef2f318b9..e4dfbd68e3f 100644 --- a/.github/workflows/bootstrap-spine-gate.yml +++ b/.github/workflows/bootstrap-spine-gate.yml @@ -84,6 +84,7 @@ jobs: php-compiler:22.04-dev bash -lc ' set -euo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh echo "=== bootstrap-inventory --check-spine ===" @@ -134,6 +135,7 @@ jobs: php-compiler:22.04-dev bash -lc " set -euo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh ./script/bootstrap-loop-probe.sh ${HONEST_ARGS} " diff --git a/.github/workflows/compiler-gate.yml b/.github/workflows/compiler-gate.yml index 65bdbd99b2e..708c009e871 100644 --- a/.github/workflows/compiler-gate.yml +++ b/.github/workflows/compiler-gate.yml @@ -95,6 +95,7 @@ jobs: php-compiler:22.04-dev bash -lc ' set -euo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh # Tier 0 — syntax. Seconds. A parse error in a spine file has killed multi-hour @@ -144,6 +145,7 @@ jobs: php-compiler:22.04-dev bash -lc ' set -uo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh script/differential-sweep.sh --aot --repeat 3 || true echo diff --git a/.github/workflows/compliance-host-compare.yml b/.github/workflows/compliance-host-compare.yml index bf240b28e96..d09e31797d1 100644 --- a/.github/workflows/compliance-host-compare.yml +++ b/.github/workflows/compliance-host-compare.yml @@ -82,6 +82,7 @@ jobs: php-compiler:hostcmp bash -lc ' set -uo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh script/shard-compliance.sh --suite="$SUITE" --shards=24 --shard=${{ matrix.shard }} ' diff --git a/.github/workflows/compliance-nightly.yml b/.github/workflows/compliance-nightly.yml index dc203240e2e..ba0fb92dc24 100644 --- a/.github/workflows/compliance-nightly.yml +++ b/.github/workflows/compliance-nightly.yml @@ -80,6 +80,7 @@ jobs: php-compiler:22.04-dev bash -lc ' set -uo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh script/shard-compliance.sh --suite="$SUITE" --shards=24 --shard=${{ matrix.shard }} ' diff --git a/.github/workflows/compliance-pr.yml b/.github/workflows/compliance-pr.yml index 39beb4ba4de..d982e63c14e 100644 --- a/.github/workflows/compliance-pr.yml +++ b/.github/workflows/compliance-pr.yml @@ -125,6 +125,7 @@ jobs: php-compiler:22.04-dev bash -lc ' set -uo pipefail composer install --no-interaction --ignore-platform-reqs -q + script/apply-patches.sh --verify-pristine script/apply-patches.sh script/shard-compliance.sh --suite="$SUITE" --shards=24 --shard=${{ matrix.shard }} ' diff --git a/patches/php-llvm-structgep-assert.patch b/patches/php-llvm-structgep-assert.patch index 9cbd17f38e4..75e0588420a 100644 --- a/patches/php-llvm-structgep-assert.patch +++ b/patches/php-llvm-structgep-assert.patch @@ -1,9 +1,9 @@ --- vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php +++ vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php -@@ -313,6 +313,27 @@ - if (null === $struct->value) { - throw new \LogicException('LLVM struct value is null in structGep()'); - } +@@ -303,6 +303,27 @@ + } + + public function structGep(CoreValue $struct, int $index): CoreValue { + if ('1' === \getenv('PHP_COMPILER_LLVM_ASSERT')) { + // LLVM 9 release builds do not bounds-check StructGEP: a non-struct + // receiver or out-of-range field index reads a garbage Type* and @@ -28,7 +28,7 @@ return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildStructGEP($this->builder, $struct->value, $index, '')); } -@@ -329,6 +350,12 @@ +@@ -319,6 +340,12 @@ } public function zExt(CoreValue $value, CoreType $type): CoreValue { diff --git a/patches/pristine-snapshots/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php b/patches/pristine-snapshots/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php new file mode 100755 index 00000000000..c631f77785e --- /dev/null +++ b/patches/pristine-snapshots/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php @@ -0,0 +1,525 @@ +llvm = $llvm; + $this->context = $context; + $this->builder = $builder; + } + + public function __destruct() { + $this->dispose(); + } + + public function position(CoreBasicBlock $block, CoreValue $instr): void { + $this->llvm->lib->LLVMPositionBuilder($this->builder, $block->block, $instr->value); + } + + public function positionBefore(CoreValue $instr): void { + $this->llvm->lib->LLVMPositionBuilderBefore($this->builder, $instr->value); + } + + public function positionAtEnd(CoreBasicBlock $block): void { + $this->llvm->lib->LLVMPositionBuilderAtEnd($this->builder, $block->block); + } + + public function getInsertBlock(): CoreBasicBlock { + return $this->llvm->factory->basicBlock($this->context, $this->llvm->lib->LLVMGetInsertBlock($this->builder)); + } + + public function clearInsertionPosition(): void { + $this->llvm->lib->LLVMClearInsertionPosition($this->builder); + } + + public function insertIntoBuilder(CoreValue $instr): void { + $this->llvm->lib->LLVMInsertIntoBuilder($this->builder, $instr->value); + } + + public function insertIntoBuilderWithName(CoreValue $instr, string $name): void { + $this->llvm->lib->LLVMInsertIntoBuilderWithName($this->builder, $instr->value, $name); + } + + public function dispose(): void { + $this->llvm->lib->LLVMDisposeBuilder($this->builder); + } + + public function setCurrentDebugLocation(CoreValue $l): void { + $this->llvm->lib->LLVMSetCurrentDebugLocation($this->builder, $l->value); + } + + public function getCurrentDebugLocation(): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMGetCurrentDebugLocation($this->builder)); + } + + public function setInstDebugLocation(CoreValue $inst): void { + $this->llvm->lib->LLVMSetInstDebugLocation($this->builder, $inst->value); + } + + public function returnVoid(): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildRetVoid($this->builder)); + } + + public function returnValue(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildRet($this->builder, $value->value)); + } + + public function returnAggregate(CoreValue ...$values): CoreValue { + $valueWrapper = $this->llvm->lib->makeArray( + LLVMValueRef_ptr::class, + array_map( + function(Value $value) { + return $value->value; + }, + $values + ) + ); + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAggregateRet($this->builder, $valueWrapper, count($values))); + } + + public function branch(CoreBasicBlock $dest): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildBr($this->builder, $dest->block)); + } + + public function branchIf(CoreValue $cond, CoreBasicBlock $if, CoreBasicBlock $else): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildCondBr($this->builder, $cond->value, $if->block, $else->block)); + } + + public function branchSwitch(CoreValue $value, CoreBasicBlock $default, int $numCases): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSwitch($this->builder, $value->value, $default->block, $numCases)); + } + + public function branchIndirect(CoreValue $addr, int $numDests): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildIndirectBr($this->builder, $addr->value, $numDests)); + } + + public function add(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAdd($this->builder, $left->value, $right->value, '')); + } + + public function addNoSignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNSWAdd($this->builder, $left->value, $right->value, '')); + } + + public function addNoUnsignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNUWAdd($this->builder, $left->value, $right->value, '')); + } + + public function fAdd(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFAdd($this->builder, $left->value, $right->value, '')); + } + + public function sub(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSub($this->builder, $left->value, $right->value, '')); + } + + public function subNoSignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNSWSub($this->builder, $left->value, $right->value, '')); + } + + public function subNoUnsignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNUWSub($this->builder, $left->value, $right->value, '')); + } + + public function fSub(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFSub($this->builder, $left->value, $right->value, '')); + } + + public function mul(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildMul($this->builder, $left->value, $right->value, '')); + } + + public function mulNoSignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNSWMul($this->builder, $left->value, $right->value, '')); + } + + public function mulNoUnsignedWrap(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNUWMul($this->builder, $left->value, $right->value, '')); + } + + public function fMul(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFMul($this->builder, $left->value, $right->value, '')); + } + + public function unsignedDiv(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildUDIV($this->builder, $left->value, $right->value, '')); + } + + public function exactUnsignedDiv(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildExactUDiv($this->builder, $left->value, $right->value, '')); + } + + public function signedDiv(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSDiv($this->builder, $left->value, $right->value, '')); + } + + public function exactSignedDiv(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildExactSDiv($this->builder, $left->value, $right->value, '')); + } + + public function fDiv(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFDiv($this->builder, $left->value, $right->value, '')); + } + + public function unsigendRem(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildURem($this->builder, $left->value, $right->value, '')); + } + + public function signedRem(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSRem($this->builder, $left->value, $right->value, '')); + } + + public function fRem(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFRem($this->builder, $left->value, $right->value, '')); + } + + public function shl(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildShl($this->builder, $left->value, $right->value, '')); + } + + public function lShr(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildLShr($this->builder, $left->value, $right->value, '')); + } + + public function aShr(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAShr($this->builder, $left->value, $right->value, '')); + } + + public function bitwiseAnd(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAnd($this->builder, $left->value, $right->value, '')); + } + + public function bitwiseOr(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildOr($this->builder, $left->value, $right->value, '')); + } + + public function bitwiseXor(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildXor($this->builder, $left->value, $right->value, '')); + } + + public function negate(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNeg($this->builder, $value->value, '')); + } + + public function negateNoSignedWrap(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNSWNeg($this->builder, $value->value, '')); + } + + public function negateNoUnsignedWrap(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNUWNeg($this->builder, $value->value, '')); + } + + public function fNegate(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFNeg($this->builder, $value->value, '')); + } + + public function not(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildNot($this->builder, $value->value, '')); + } + + public function malloc(CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildMalloc($this->builder, $type->type, '')); + } + + public function arrayMalloc(CoreType $type, CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildArrayMalloc($this->builder, $type->type, $value->value, '')); + } + + public function alloca(CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAlloca($this->builder, $type->type, '')); + } + + public function arrayAlloca(CoreType $type, CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildArrayAlloca($this->builder, $type->type, $value->value, '')); + } + + public function free(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFree($this->builder, $value->value, '')); + } + + public function load(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildLoad($this->builder, $value->value, '')); + } + + public function store(CoreValue $value, CoreValue $pointer): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildStore($this->builder, $value->value, $pointer->value, '')); + } + + public function gep(CoreValue $pointer, CoreValue ... $indices): CoreValue { + $valueWrapper = $this->llvm->lib->makeArray( + LLVMValueRef_ptr::class, + array_map( + function(Value $value) { + return $value->value; + }, + $indices + ) + ); + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildGEP($this->builder, $pointer->value, $valueWrapper, count($indices), '')); + } + + public function inBoundsGep(CoreValue $pointer, CoreValue ... $indices): CoreValue { + $valueWrapper = $this->llvm->lib->makeArray( + LLVMValueRef_ptr::class, + array_map( + function(Value $value) { + return $value->value; + }, + $indices + ) + ); + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildInBoundsGEP($this->builder, $pointer->value, $valueWrapper, count($indices), '')); + } + + public function structGep(CoreValue $struct, int $index): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildStructGEP($this->builder, $struct->value, $index, '')); + } + + public function globalString(string $string): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildGlobalString($this->builder, $string, '')); + } + + public function globalStringPointer(string $string): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildGlobalStringPtr($this->builder, $string, '')); + } + + public function trunc(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildTrunc($this->builder, $value->value, $type->type, '')); + } + + public function zExt(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildZExt($this->builder, $value->value, $type->type, '')); + } + + public function sExt(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSExt($this->builder, $value->value, $type->type, '')); + } + + public function fpToUi(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFPToUI($this->builder, $value->value, $type->type, '')); + } + + public function fpToSi(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFPToSI($this->builder, $value->value, $type->type, '')); + } + + public function uiToFp(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildUIToFP($this->builder, $value->value, $type->type, '')); + } + + public function siToFp(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSIToFP($this->builder, $value->value, $type->type, '')); + } + + public function fpTrunc(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFPTrunc($this->builder, $value->value, $type->type, '')); + } + + public function fpExt(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFPExt($this->builder, $value->value, $type->type, '')); + } + + public function ptrToInt(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildPtrToInt($this->builder, $value->value, $type->type, '')); + } + + public function intToPtr(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildIntToPtr($this->builder, $value->value, $type->type, '')); + } + + public function bitCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildBitCast($this->builder, $value->value, $type->type, '')); + } + + public function addrSpaceCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildAddrSpaceCast($this->builder, $value->value, $type->type, '')); + } + + public function zExtOrBitCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildZExtOrBitCast($this->builder, $value->value, $type->type, '')); + } + + public function sExtOrBitCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSExtOrBitCast($this->builder, $value->value, $type->type, '')); + } + + public function truncOrBitCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildTruncOrBitCast($this->builder, $value->value, $type->type, '')); + } + + public function pointerCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildPointerCast($this->builder, $value->value, $type->type, '')); + } + + public function intCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildIntCast($this->builder, $value->value, $type->type, '')); + } + + public function fpCast(CoreValue $value, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFPCast($this->builder, $value->value, $type->type, '')); + } + + public function iCmp(int $op, CoreValue $left, CoreValue $right): CoreValue { + switch ($op) { + case self::INT_EQ: + $predicate = lib::LLVMIntEQ; + break; + case self::INT_NE: + $predicate = lib::LLVMIntNE; + break; + case self::INT_UGT: + $predicate = lib::LLVMIntUGT; + break; + case self::INT_UGE: + $predicate = lib::LLVMIntUGE; + break; + case self::INT_ULT: + $predicate = lib::LLVMIntULT; + break; + case self::INT_ULE: + $predicate = lib::LLVMIntULE; + break; + case self::INT_SGT: + $predicate = lib::LLVMIntSGT; + break; + case self::INT_SGE: + $predicate = lib::LLVMIntSGE; + break; + case self::INT_SLT: + $predicate = lib::LLVMIntSLT; + break; + case self::INT_SLE : + $predicate = lib::LLVMIntSLE; + break; + default: + throw new \LogicException("Unknown operator for icmp: $op"); + } + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildICmp($this->builder, $predicate, $left->value, $right->value, '')); + } + + public function fCmp(int $op, CoreValue $left, CoreValue $right): CoreValue { + switch ($op) { + case self::REAL_OEQ: + $predicate = lib::LLVMRealOEQ; + break; + case self::REAL_OGT: + $predicate = lib::LLVMRealOGT; + break; + case self::REAL_OGE: + $predicate = lib::LLVMRealOGE; + break; + case self::REAL_OLT: + $predicate = lib::LLVMRealOLT; + break; + case self::REAL_OLE: + $predicate = lib::LLVMRealOLE; + break; + case self::REAL_ONE: + $predicate = lib::LLVMRealONE; + break; + case self::REAL_ORD: + $predicate = lib::LLVMRealORD; + break; + case self::REAL_UNO: + $predicate = lib::LLVMRealUNO; + break; + case self::REAL_UEQ: + $predicate = lib::LLVMRealUEQ; + break; + case self::REAL_UGT: + $predicate = lib::LLVMRealUGT; + break; + case self::REAL_UGE: + $predicate = lib::LLVMRealUGE; + break; + case self::REAL_ULT: + $predicate = lib::LLVMRealULT; + break; + case self::REAL_ULE: + $predicate = lib::LLVMRealULE; + break; + case self::REAL_UNE: + $predicate = lib::LLVMRealUNE; + break; + default: + throw new \LogicException("Unknown operator for fcmp: $op"); + } + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildFCmp($this->builder, $predicate, $left->value, $right->value, '')); + } + + public function phi(CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildPhi($this->builder, $type->type, '')); + } + + public function call(CoreValue\Function_ $function, CoreValue ... $args): CoreValue { + $valueWrapper = $this->llvm->lib->makeArray( + LLVMValueRef_ptr::class, + array_map( + function(Value $value) { + return $value->value; + }, + $args + ) + ); + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildCall($this->builder, $function->value, $valueWrapper, count($args), '')); + } + + public function select(CoreValue $if, CoreValue $then, CoreValue $else): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildSelect($this->builder, $if->value, $$then->value, $else->value, '')); + } + + public function vaarg(CoreValue $list, CoreType $type): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildVAArg($this->builder, $list->value, $type->type, '')); + } + + public function extractElement(CoreValue $vector, CoreValue $index): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildExtractElement($this->builder, $vector->value, $index->value, '')); + } + + public function insertElement(CoreValue $vector, CoreValue $value, CoreValue $index): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildInsertElement($this->builder, $vector->value, $value->value, $index->value, '')); + } + + public function shuffleVector(CoreValue $v1, CoreValue $v2, CoreValue $mask): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildShuffleVector($this->builder, $v1->value, $v2->value, $mask->value, '')); + } + + public function extractValue(CoreValue $aggVal, int $index): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildExtractValue($this->builder, $aggVal->value, $index, '')); + } + + public function insertValue(CoreValue $aggVal, CoreValue $value, int $index): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildInsertValue($this->builder, $aggVal->value, $value->value, $index, '')); + } + + public function isNull(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildIsNull($this->builder, $value->value, '')); + } + + public function isNotNull(CoreValue $value): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildIsNotNull($this->builder, $value->value, '')); + } + + public function ptrDiff(CoreValue $left, CoreValue $right): CoreValue { + return $this->llvm->factory->value($this->context, $this->llvm->lib->LLVMBuildPtrDiff($this->builder, $left->value, $right->value, '')); + } +} \ No newline at end of file diff --git a/script/apply-patches.sh b/script/apply-patches.sh index 7f752b4b22e..78ddc97321d 100755 --- a/script/apply-patches.sh +++ b/script/apply-patches.sh @@ -29,7 +29,8 @@ patch_already_applied() { grep -q 'array \$implements, Block \$stmts, ?Operand \$extends = null' "$ROOT/vendor/ircmaxell/php-cfg/lib/PHPCfg/Op/Stmt/Class_.php" 2>/dev/null ;; php-llvm-structgep-assert.patch) - grep -q 'PHP_COMPILER_LLVM_ASSERT' "$ROOT/vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php" 2>/dev/null + grep -q 'PHP_COMPILER_LLVM_ASSERT' "$ROOT/vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php" 2>/dev/null \ + && patch_is_fully_applied "$ROOT/patches/php-llvm-structgep-assert.patch" ;; php-llvm-icmp-assert.patch) grep -q 'iCmp: operands are not of the same type' "$ROOT/vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php" 2>/dev/null @@ -5411,7 +5412,7 @@ old_d = """ $cond = $this->readVariable($this->parseExprNode($node->cond) $this->block = $loopEnd; } - protected function parseStmt_Enum(Stmt\\Enum_ $node)""" + protected function parseStmt_Echo(Stmt\\Echo_ $node)""" new_d = """ $cond = $this->readVariable($this->parseExprNode($node->cond)); // Zend emits ZEND_TICKS after the do-while statement (on loop exit) (#25621). $jumpIf = new JumpIf($cond, $loopBody, $loopEnd, $this->mapAttributes($node)); @@ -5425,7 +5426,7 @@ new_d = """ $cond = $this->readVariable($this->parseExprNode($node->cond) $this->block = $loopEnd; } - protected function parseStmt_Enum(Stmt\\Enum_ $node)""" + protected function parseStmt_Echo(Stmt\\Echo_ $node)""" old_d = old_d.replace("\\\\", "\\") new_d = new_d.replace("\\\\", "\\") if old_d not in text: @@ -6571,6 +6572,76 @@ print('Repaired php-types-static-var Terminal_StaticVar array type (#32806)') PY } +# True when the patch is genuinely present (reverse-apply succeeds), not just a grep marker. +patch_is_fully_applied() { + local patch="$1" + if git -C "$ROOT" apply --check -p0 -R "$patch" >/dev/null 2>&1; then + return 0 + fi + if git -C "$ROOT" apply --check -p1 -R "$patch" >/dev/null 2>&1; then + return 0 + fi + if command -v patch >/dev/null 2>&1; then + if patch -p0 --reverse --dry-run -s -f < "$patch" >/dev/null 2>&1; then + return 0 + fi + if patch -p1 --reverse --dry-run -s -f < "$patch" >/dev/null 2>&1; then + return 0 + fi + fi + return 1 +} + +# Verify a single patch applies to a pristine snapshot (vendor *.orig or patches/pristine/). +verify_pristine_patch_on_orig() { + local patch="$1" orig_rel="$2" + local orig="$ROOT/$orig_rel" dest_rel scratch + if [[ ! -f "$patch" ]]; then + return 0 + fi + if [[ ! -f "$orig" ]]; then + local snap_rel="${orig_rel#vendor/}" + snap_rel="${snap_rel%.orig}" + orig="$ROOT/patches/pristine-snapshots/$snap_rel" + fi + if [[ ! -f "$orig" ]]; then + echo "verify-pristine: skip $(basename "$patch") (no pristine snapshot for ${orig_rel})" + return 0 + fi + dest_rel="${orig_rel%.orig}" + dest_rel="${dest_rel#vendor/}" + scratch="$(mktemp -d "${TMPDIR:-/tmp}/phpc-pristine-one.XXXXXX")" + mkdir -p "$scratch/vendor/$(dirname "$dest_rel")" + cp "$orig" "$scratch/vendor/$dest_rel" + if (cd "$scratch" && git apply --check -p0 "$patch") >/dev/null 2>&1; then + rm -rf "$scratch" + echo "verify-pristine: OK $(basename "$patch")" + return 0 + fi + if (cd "$scratch" && git apply --check -p1 "$patch") >/dev/null 2>&1; then + rm -rf "$scratch" + echo "verify-pristine: OK $(basename "$patch") (-p1)" + return 0 + fi + rm -rf "$scratch" + echo "verify-pristine: FAIL $(basename "$patch") (does not apply to pristine ${orig_rel})" >&2 + return 1 +} + +# Fast gate: llvm git patches must apply to pristine vendor snapshots (*.orig). +verify_pristine_patches() { + local failed=0 + local llvm_builder_orig="vendor/ircmaxell/php-llvm/lib/LLVMAbstract/Builder.php.orig" + verify_pristine_patch_on_orig "$PATCH_DIR/php-llvm-structgep-assert.patch" "$llvm_builder_orig" || failed=1 + verify_pristine_patch_on_orig "$PATCH_DIR/php-llvm-icmp-assert.patch" "$llvm_builder_orig" || failed=1 + if [[ "$failed" -ne 0 ]]; then + echo "verify-pristine: patch drift detected — re-diff against pristine vendor (#36209)" >&2 + return 1 + fi + echo "verify-pristine: llvm patches apply to pristine vendor snapshots" + return 0 +} + # Apply a patch file with git/patch(1) only — no overlay dispatch (avoids recursion). apply_patch_file_direct() { local patch="$1" @@ -7045,6 +7116,17 @@ PY esac } +case "${1:-}" in + --verify-pristine) + verify_pristine_patches + exit $? + ;; + --help|-h) + echo "Usage: $0 [--verify-only | --verify-pristine]" >&2 + exit 0 + ;; +esac + if [[ "${1:-}" != "--verify-only" ]]; then apply_patch "$PATCH_DIR/php-parser-final-property.patch" apply_patch "$PATCH_DIR/php-llvm-chooser.patch" diff --git a/script/check-generated-docs.sh b/script/check-generated-docs.sh index 817637051f6..d318aa192d2 100755 --- a/script/check-generated-docs.sh +++ b/script/check-generated-docs.sh @@ -39,6 +39,9 @@ step "bootstrap-sdk-platform --check" "$PHP_BIN" script/check-bootstrap-sdk-plat # 5. composer.lock content-hash vs composer.json — #15620 class of drift step "composer.lock content-hash" "$PHP_BIN" script/check-composer-lock-hash.php +# 6. Vendor patches apply to pristine *.orig snapshots — #36209 +step "apply-patches --verify-pristine" script/apply-patches.sh --verify-pristine + if [[ "$fail" -ne 0 ]]; then echo "check-generated-docs: drift detected — regenerate in the pinned env (see CONTRIBUTING 'Generated docs')" >&2 exit 1 diff --git a/script/ci-common.sh b/script/ci-common.sh index 7c3b7dccac5..8213150449e 100644 --- a/script/ci-common.sh +++ b/script/ci-common.sh @@ -56,7 +56,7 @@ ci_install_deps() { python3 -c "import urllib.request; urllib.request.urlretrieve('https://getcomposer.org/download/latest-stable/composer.phar','/tmp/composer.phar')" COMPOSER=("$PHP_BIN" -d "extension=$ext_dir/phar.so" -d "extension=$ext_dir/mbstring.so" /tmp/composer.phar) fi - "${COMPOSER[@]}" install --no-interaction --ignore-platform-reqs 2>/dev/null || true + "${COMPOSER[@]}" install --no-interaction --ignore-platform-reqs chmod +x script/install-llvm9.sh script/apply-patches.sh 2>/dev/null || true if [[ -z "${PHP_COMPILER_LLVM_PATH:-}" || ! -f "${PHP_COMPILER_LLVM_PATH}/libLLVM-9.so.1" ]]; then