Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4,486 changes: 4,485 additions & 1 deletion ext/standard/JitPowIntegerEmitHigh.php

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion lib/JIT/DiscardedPureCallElisionNativeLongPowFolds.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ trait DiscardedPureCallElisionNativeLongPowFolds
* {@code zend_pow} / {@code mul_function}; ext/standard/math.c
* {@code PHP_FUNCTION(pow)}.
*
* @return 'one'|'identity'|'square'|'cube'|'fourth'|'fifth'|'sixth'|'seventh'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth'|'thirteenth'|'fourteenth'|'fifteenth'|'sixteenth'|'seventeenth'|'eighteenth'|'nineteenth'|'twentieth'|'twentyfirst'|'twentysecond'|'twentythird'|'twentyfourth'|'twentyfifth'|'twentysixth'|'twentyseventh'|'twentyeighth'|'twentyninth'|'thirtieth'|'thirtyfirst'|'thirtysecond'|'thirtythird'|'thirtyfourth'|'thirtyfifth'|'thirtysixth'|'thirtyseventh'|'thirtyeighth'|'thirtyninth'|'fortieth'|'fortyfirst'|'fortysecond'|'fortythird'|'fortyfourth'|'fortyfifth'|'fortysixth'|'fortyseventh'|'fortyeighth'|'fortyninth'|'fiftieth'|'fiftyfirst'|'fiftysecond'|'fiftythird'|'fiftyfourth'|'fiftyfifth'|'fiftysixth'|'fiftyseventh'|'fiftyeighth'|'fiftyninth'|'sixtieth'|'sixtyfirst'|'sixtysecond'|'sixtythird'|'sixtyfourth'|'sixtyfifth'|'sixtysixth'|'sixtyseventh'|'sixtyeighth'|'sixtyninth'|'seventieth'|'seventyfirst'|'seventysecond'|'seventythird'|'seventyfourth'|'seventyfifth'|'seventysixth'|'seventyseventh'|'seventyeighth'|'seventyninth'|'eightieth'|'eightyfirst'|'eightysecond'|'eightythird'|'eightyfourth'|'eightyfifth'|'eightysixth'|'eightyseventh'|'eightyeighth'|'eightyninth'|'ninetieth'|'ninetyfirst'|'ninetysecond'|'ninetythird'|'ninetyfourth'|'ninetyfifth'|'ninetysixth'|'ninetyseventh'|'ninetyeighth'|'ninetyninth'|'hundredth'|'hundredfirst'|'hundredsecond'|'hundredthird'|'hundredfourth'|'hundredfifth'|'hundredsixth'|'hundredseventh'|'hundredeighth'|'hundredninth'|'hundredtenth'|null fold to 1, keep base, mul square/cube/fourth/fifth/sixth/seventh/eighth/ninth/tenth/eleventh/twelfth/thirteenth/fourteenth/fifteenth/sixteenth/seventeenth/eighteenth/nineteenth/twentieth/twentyfirst/twentysecond/twentythird/twentyfourth/twentyfifth/twentysixth/twentyseventh/twentyeighth/twentyninth/thirtieth/thirtyfirst/thirtysecond/thirtythird/thirtyfourth/thirtyfifth/thirtysixth/thirtyseventh/thirtyeighth/thirtyninth/fortieth/fortyfirst/fortysecond/fortythird/fortyfourth/fortyfifth/fortysixth/fortyseventh/fortyeighth/fortyninth/fiftieth/fiftyfirst/fiftysecond/fiftythird/fiftyfourth/fiftyfifth/fiftysixth/fiftyseventh/fiftyeighth/fiftyninth/sixtieth/sixtyfirst/sixtysecond/sixtythird/sixtyfourth, sixtyfifth, sixtysixth, sixtyseventh, sixtyeighth, sixtyninth, seventieth, seventyfirst, seventysecond, seventythird, seventyfourth, seventyfifth, seventysixth, seventyseventh, seventyeighth, seventyninth, eightieth, eightyfirst, eightysecond, eightythird, eightyfourth, eightyfifth, eightysixth, eightyseventh, eightyeighth, eightyninth, ninetieth, ninetyfirst, ninetysecond, ninetythird, ninetyfourth, ninetyfifth, ninetysixth, ninetyseventh, ninetyeighth, ninetyninth, hundredth, hundredfirst, hundredsecond, hundredthird, hundredfourth, hundredfifth, hundredsixth, hundredseventh, hundredeighth, hundredninth, hundredtenth, or null
* @return 'one'|'identity'|'square'|'cube'|'fourth'|'fifth'|'sixth'|'seventh'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth'|'thirteenth'|'fourteenth'|'fifteenth'|'sixteenth'|'seventeenth'|'eighteenth'|'nineteenth'|'twentieth'|'twentyfirst'|'twentysecond'|'twentythird'|'twentyfourth'|'twentyfifth'|'twentysixth'|'twentyseventh'|'twentyeighth'|'twentyninth'|'thirtieth'|'thirtyfirst'|'thirtysecond'|'thirtythird'|'thirtyfourth'|'thirtyfifth'|'thirtysixth'|'thirtyseventh'|'thirtyeighth'|'thirtyninth'|'fortieth'|'fortyfirst'|'fortysecond'|'fortythird'|'fortyfourth'|'fortyfifth'|'fortysixth'|'fortyseventh'|'fortyeighth'|'fortyninth'|'fiftieth'|'fiftyfirst'|'fiftysecond'|'fiftythird'|'fiftyfourth'|'fiftyfifth'|'fiftysixth'|'fiftyseventh'|'fiftyeighth'|'fiftyninth'|'sixtieth'|'sixtyfirst'|'sixtysecond'|'sixtythird'|'sixtyfourth'|'sixtyfifth'|'sixtysixth'|'sixtyseventh'|'sixtyeighth'|'sixtyninth'|'seventieth'|'seventyfirst'|'seventysecond'|'seventythird'|'seventyfourth'|'seventyfifth'|'seventysixth'|'seventyseventh'|'seventyeighth'|'seventyninth'|'eightieth'|'eightyfirst'|'eightysecond'|'eightythird'|'eightyfourth'|'eightyfifth'|'eightysixth'|'eightyseventh'|'eightyeighth'|'eightyninth'|'ninetieth'|'ninetyfirst'|'ninetysecond'|'ninetythird'|'ninetyfourth'|'ninetyfifth'|'ninetysixth'|'ninetyseventh'|'ninetyeighth'|'ninetyninth'|'hundredth'|'hundredfirst'|'hundredsecond'|'hundredthird'|'hundredfourth'|'hundredfifth'|'hundredsixth'|'hundredseventh'|'hundredeighth'|'hundredninth'|'hundredtenth'|'hundredeleventh'|null fold to 1, keep base, mul square/cube/fourth/fifth/sixth/seventh/eighth/ninth/tenth/eleventh/twelfth/thirteenth/fourteenth/fifteenth/sixteenth/seventeenth/eighteenth/nineteenth/twentieth/twentyfirst/twentysecond/twentythird/twentyfourth/twentyfifth/twentysixth/twentyseventh/twentyeighth/twentyninth/thirtieth/thirtyfirst/thirtysecond/thirtythird/thirtyfourth/thirtyfifth/thirtysixth/thirtyseventh/thirtyeighth/thirtyninth/fortieth/fortyfirst/fortysecond/fortythird/fortyfourth/fortyfifth/fortysixth/fortyseventh/fortyeighth/fortyninth/fiftieth/fiftyfirst/fiftysecond/fiftythird/fiftyfourth/fiftyfifth/fiftysixth/fiftyseventh/fiftyeighth/fiftyninth/sixtieth/sixtyfirst/sixtysecond/sixtythird/sixtyfourth, sixtyfifth, sixtysixth, sixtyseventh, sixtyeighth, sixtyninth, seventieth, seventyfirst, seventysecond, seventythird, seventyfourth, seventyfifth, seventysixth, seventyseventh, seventyeighth, seventyninth, eightieth, eightyfirst, eightysecond, eightythird, eightyfourth, eightyfifth, eightysixth, eightyseventh, eightyeighth, eightyninth, ninetieth, ninetyfirst, ninetysecond, ninetythird, ninetyfourth, ninetyfifth, ninetysixth, ninetyseventh, ninetyeighth, ninetyninth, hundredth, hundredfirst, hundredsecond, hundredthird, hundredfourth, hundredfifth, hundredsixth, hundredseventh, hundredeighth, hundredninth, hundredtenth, hundredeleventh, or null
*/
public static function nativeLongPowCompileTimeExponentFold(
Variable $exponent
Expand Down Expand Up @@ -546,6 +546,9 @@ public static function nativeLongPowCompileTimeExponentFold(
if (110 === $e) {
return 'hundredtenth';
}
if (111 === $e) {
return 'hundredeleventh';
}

return null;
}
Expand Down
10 changes: 5 additions & 5 deletions script/size-budgets.json
Original file line number Diff line number Diff line change
Expand Up @@ -644,9 +644,9 @@
"note": "nativeLongCompare* + nativeLongMul* + nativeLongArithIsCompileTimeIdentityOrZero extracted from NativeLongFolds (#36387); php-src Zend/zend_operators.c compare/mul/add/sub"
},
"lib/JIT/DiscardedPureCallElisionNativeLongPowFolds.php": {
"budget": 552,
"budget": 555,
"target": 600,
"note": "nativeLongPowCompileTimeExponentFold extracted from NativeLongFolds (#36387/#36386); includes ninetyninth/hundredth/hundredfirst/hundredsecond/hundredthird/hundredfourth/hundredfifth/hundredsixth/hundredseventh/hundredeighth/hundredninth/hundredtenth fold; php-src Zend/zend_operators.c pow_function / zend_pow / mul_function; ext/standard/math.c PHP_FUNCTION(pow)"
"note": "nativeLongPowCompileTimeExponentFold extracted from NativeLongFolds (#36387/#36386); includes ninetyninth/hundredth/hundredfirst/hundredsecond/hundredthird/hundredfourth/hundredfifth/hundredsixth/hundredseventh/hundredeighth/hundredninth/hundredtenth/hundredeleventh fold; php-src Zend/zend_operators.c pow_function / zend_pow / mul_function; ext/standard/math.c PHP_FUNCTION(pow)"
},
"lib/JIT/DiscardedPureCallElisionIntrospectOps.php": {
"budget": 407,
Expand Down Expand Up @@ -724,9 +724,9 @@
"note": "integer pow()/ ** hub: Low/Mid/Exponents50to69/70to79/80to85/86to88/80to91/92to95/96to99/100to104/High dispatch (#36387/#36386)"
},
"ext/standard/JitPowIntegerEmitHigh.php": {
"budget": 24865,
"target": 30000,
"note": "integer pow()/ ** chained-smul emit for hundredfifth..hundredtenth; 100..104 in Exponents100to104 (#36387/#36386)"
"budget": 29349,
"target": 34349,
"note": "integer pow()/ ** chained-smul emit for hundredfifth..hundredeleventh; 100..104 in Exponents100to104 (#36387/#36386)"
},
"ext/standard/JitPowIntegerEmitLow.php": {
"budget": 3985,
Expand Down
1 change: 1 addition & 0 deletions test/differential/OPCODE-CORPUS.md5
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ test/differential/cases/pow_fortythird_smul_36386.php bbcb71930aa9d623408734a0ef
test/differential/cases/pow_fourteenth_smul_36386.php b90de9e0839b7185521ce15c3db89099
test/differential/cases/pow_fourth_smul_36386.php ee5396091f6f9c186dd9a87555014642
test/differential/cases/pow_hundredeighth_smul_36386.php 296f3bd0a6c8760c4039df7657af7b39
test/differential/cases/pow_hundredeleventh_smul_36386.php 5dfbe6a2e279d26c42a764455deab031
test/differential/cases/pow_hundredfifth_smul_36386.php 2709a90617a47111702929bd2b9f2589
test/differential/cases/pow_hundredfirst_smul_36386.php 28eb08f97c159ece7564e39ec64f11c5
test/differential/cases/pow_hundredfourth_smul_36386.php 60dbc68597d338b8c321c72458c12ee5
Expand Down
18 changes: 18 additions & 0 deletions test/differential/cases/pow_hundredeleventh_smul_36386.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
function work(int $n): int {
return $n ** 111;
}
function viaPow(int $n): int {
return pow($n, 111);
}
function overflow(int $n) {
return $n ** 111;
}
echo work(0), "\n";
echo work(1), "\n";
echo work(-1), "\n";
echo viaPow(0), "\n";
echo gettype(overflow(2)), "\n";
echo gettype(overflow(-2)), "\n";
echo gettype(overflow(PHP_INT_MAX)), "\n";
173 changes: 173 additions & 0 deletions test/unit/NoThrowPowHundredeleventhMulAotTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\Test\Unit;

use PHPUnit\Framework\TestCase;

/**
* Typed integer {@code **} / {@code pow()} with compile-time exponent
* {@code 111} → {@code (hundredtenth×n)} chained smul
* overflow→float — omit {@code llvm.pow.f64} (#36386).
*
* php-src: Zend/zend_operators.c pow_function / zend_pow / mul_function;
* ext/standard/math.c PHP_FUNCTION(pow).
* Peer: ** 2–** 110 (#37273…/#37715), ** 0 / ** 1 (#37269).
* Note: {@code (±2)**111} and larger magnitudes promote to float (signed i64).
* Odd exponent: {@code (-1)**111 === -1}.
*
* @group aot-lint
*/
final class NoThrowPowHundredeleventhMulAotTest extends TestCase
{
public function testPowHundredeleventhOmitsFpowUsesSmul(): void
{
$src = <<<'PHP'
<?php
declare(strict_types=1);
function work(int $n): int {
return $n ** 111;
}
echo work(0), "\n";
echo work(1), "\n";
echo work(-1), "\n";
PHP;
$this->assertPowHundredeleventh($src, 'work', ['0', '1', '-1']);
}

public function testBuiltinPowHundredeleventh(): void
{
$src = <<<'PHP'
<?php
declare(strict_types=1);
function work(int $n): int {
return pow($n, 111);
}
echo work(-1), "\n";
PHP;
$this->assertPowHundredeleventh($src, 'work', ['-1']);
}

public function testPowHundredeleventhOverflowPromotesToFloat(): void
{
$src = <<<'PHP'
<?php
declare(strict_types=1);
function work(int $n) {
return $n ** 111;
}
echo gettype(work(2)), "\n";
echo gettype(work(-2)), "\n";
echo gettype(work(PHP_INT_MAX)), "\n";
PHP;
$path = sys_get_temp_dir().'/phpc_pow111_ov_'.getmypid().'.php';
$bin = sys_get_temp_dir().'/phpc_pow111_ov_'.getmypid().'.bin';
file_put_contents($path, $src);
try {
putenv('PHP_COMPILER_DUMP_IR=1');
putenv('PHP_COMPILER_CACHE=0');
$cmd = escapeshellarg(PHP_BINARY).' '
.escapeshellarg(__DIR__.'/../../bin/compile.php').' -o '
.escapeshellarg($bin).' '.escapeshellarg($path).' 2>&1';
exec($cmd, $out, $rc);
$this->assertSame(0, $rc, implode("\n", $out));
$body = $this->functionBody((string) file_get_contents('/tmp/phpc-last.ll'), 'work');
$this->assertStringNotContainsString('llvm.pow.f64', $body);
$this->assertStringContainsString('smul.with.overflow', $body);
exec(escapeshellarg($bin), $runOut, $runRc);
$this->assertSame(0, $runRc);
$this->assertSame(['double', 'double', 'double'], $runOut);
} finally {
putenv('PHP_COMPILER_DUMP_IR');
putenv('PHP_COMPILER_CACHE');
@unlink($path);
@unlink($bin);
}
}

public function testRuntimeExponentKeepsPow(): void
{
$src = <<<'PHP'
<?php
declare(strict_types=1);
function work(int $n, int $e): int {
return $n ** $e;
}
echo work(-1, 111), "\n";
PHP;
$path = sys_get_temp_dir().'/phpc_pow111_rt_'.getmypid().'.php';
$bin = sys_get_temp_dir().'/phpc_pow111_rt_'.getmypid().'.bin';
file_put_contents($path, $src);
try {
putenv('PHP_COMPILER_DUMP_IR=1');
putenv('PHP_COMPILER_CACHE=0');
$cmd = escapeshellarg(PHP_BINARY).' '
.escapeshellarg(__DIR__.'/../../bin/compile.php').' -o '
.escapeshellarg($bin).' '.escapeshellarg($path).' 2>&1';
exec($cmd, $out, $rc);
$this->assertSame(0, $rc, implode("\n", $out));
$body = $this->functionBody((string) file_get_contents('/tmp/phpc-last.ll'), 'work');
$this->assertTrue(
false !== strpos($body, 'llvm.pow.f64')
|| false !== strpos($body, '@llvm.pow')
|| false !== strpos($body, 'phpc_fpow')
|| false !== strpos($body, '__phpc_pow_int'),
"expected pow intrinsic/helper in IR:\n".$body
);
exec(escapeshellarg($bin), $runOut, $runRc);
$this->assertSame(0, $runRc);
$this->assertSame(['-1'], $runOut);
} finally {
putenv('PHP_COMPILER_DUMP_IR');
putenv('PHP_COMPILER_CACHE');
@unlink($path);
@unlink($bin);
}
}

/**
* @param list<string> $expectedRun
*/
private function assertPowHundredeleventh(string $src, string $fn, array $expectedRun): void
{
$path = sys_get_temp_dir().'/phpc_pow111_'.getmypid().'.php';
$bin = sys_get_temp_dir().'/phpc_pow111_'.getmypid().'.bin';
file_put_contents($path, $src);
try {
putenv('PHP_COMPILER_DUMP_IR=1');
putenv('PHP_COMPILER_CACHE=0');
$cmd = escapeshellarg(PHP_BINARY).' '
.escapeshellarg(__DIR__.'/../../bin/compile.php').' -o '
.escapeshellarg($bin).' '.escapeshellarg($path).' 2>&1';
exec($cmd, $out, $rc);
$this->assertSame(0, $rc, implode("\n", $out));
$body = $this->functionBody((string) file_get_contents('/tmp/phpc-last.ll'), $fn);
$this->assertStringNotContainsString('llvm.pow.f64', $body);
$this->assertStringNotContainsString('@llvm.pow', $body);
$this->assertStringNotContainsString('phpc_fpow', $body);
$this->assertStringContainsString('smul.with.overflow', $body);
exec(escapeshellarg($bin), $runOut, $runRc);
$this->assertSame(0, $runRc);
$this->assertSame($expectedRun, $runOut);
} finally {
putenv('PHP_COMPILER_DUMP_IR');
putenv('PHP_COMPILER_CACHE');
@unlink($path);
@unlink($bin);
}
}

private function functionBody(string $ll, string $fn): string
{
if (!preg_match(
'/define[^\n]*@'.preg_quote($fn, '/').'[^\n]*\{(.*?)\n\}/s',
$ll,
$m
)) {
$this->fail("function @{$fn} not found in IR");
}

return $m[1];
}
}
Loading