Description
The following code:
<?phpfinalclass It implements Iterator {
publicreadonlyarray$values;
publicint$position = 0;
publicfunction__construct(array$values) {
$this->values = $values;
}
publicfunctionrewind(): void {}
publicfunctionvalid(): bool {
return$this->position === 0;
}
publicfunctioncurrent(): mixed {
if (!isset($this->values[$this->position])) {
thrownewException();
}
return$this->values[$this->position];
}
publicfunctionkey(): mixed {
return$this->position;
}
publicfunctionnext(): void {
$this->position++;
}
}
functioniter(It$it) {
foreach ($itas$value) {
var_dump($value);
if (!$valueinstanceof stdClass) {
continue;
}
}
}
echo"# First run\n";
for ($i = 0; $i < 5; $i++) {
getenv('F')(newIt([getenv('F')])); // non-immutable, packed array
}
// Next side-exit should deoptimizevar_dump(ini_set('opcache.jit_max_side_traces', '0'));
var_dump(ini_set('opcache.jit_blacklist_side_trace', '0'));
echo"# Second run\n";
for ($i = 0; $i < 5; $i++) {
getenv('F')(newIt([getenv('F'), 'map' => true])); // non-immutable, map, triggers exit
}
?>When executed with
F=iter php -n -d opcache.enable_cli=1 -d opcache.jit=tracing -d opcache.jit_hot_func=1 -d opcache.jit_hot_side_exit=1 test.php
Resulted in this output:
# First run
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(3) "128"
string(1) "8"
# Second run
string(4) "iter"
Fatal error: Uncaught Exception in test.php:18
Stack trace:
#0 test.php(34): It->current()
#1 test.php(53): iter(Object(It))
#2 {main}
thrown in test.php on line 18
But I expected this output instead:
# First run
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(3) "128"
string(1) "8"
# Second run
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
string(4) "iter"
Cause:
During the second run phase, the compiled trace for It::current() exits because $this->values is not a packed array anymore. The side exit is blacklisted, but the generated code clobbers registers before saving them:
ESCAPE-6-4: cmpb$0,0x69(%r12)je .L1 ; try_addref(T1)movq0x60(%r12), %rax addl$1, (%rax).L1: ; save T2 (%rax), but it was clobbered abovemovq %rax,0x70(%r12) movl$4,0x78(%r12) leaq -0xb01677d(%rip), %r13 addq$0x38, %rsp jmpq *(%r13)
This causes isset($this->values[$this->position]) to evaluate to false.
PHP Version
Operating System
No response
Description
The following code:
When executed with
Resulted in this output:
But I expected this output instead:
Cause:
During the second run phase, the compiled trace for
It::current()exits because$this->valuesis not a packed array anymore. The side exit is blacklisted, but the generated code clobbers registers before saving them:This causes
isset($this->values[$this->position])to evaluate tofalse.PHP Version
Operating System
No response