-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add JIT guards for INIT_METHOD_CALL when the method may be modified #8600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8998,15 +8998,15 @@ static int zend_jit_init_method_call(dasm_State **Dst, | |
|2: | ||
} | ||
|
||
if (!func | ||
if ((!func || zend_jit_may_be_modified(func, op_array)) | ||
&& trace | ||
&& trace->op == ZEND_JIT_TRACE_INIT_CALL | ||
&& trace->func | ||
) { | ||
int32_t exit_point; | ||
const void *exit_addr; | ||
|
||
exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_METHOD_CALL); | ||
exit_point = zend_jit_trace_get_exit_point(opline, func ? ZEND_JIT_EXIT_INVALIDATE : ZEND_JIT_EXIT_METHOD_CALL); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my understanding,
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For non-polimorphic calls function guard might be checked once per request, so it would be better to do this right after zend_jit_find_method_helper(). I may optimize this on top of the PR. |
||
exit_addr = zend_jit_trace_get_exit_addr(exit_point); | ||
if (!exit_addr) { | ||
return 0; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
interface ModelInterface | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
--TEST-- | ||
Bug GH-8591 001 (JIT does not account for class re-compile) | ||
--EXTENSIONS-- | ||
opcache | ||
--INI-- | ||
opcache.enable=1 | ||
opcache.enable_cli=1 | ||
opcache.jit_buffer_size=1M | ||
opcache.jit=1255 | ||
opcache.file_update_protection=0 | ||
opcache.revalidate_freq=0 | ||
opcache.protect_memory=1 | ||
--FILE-- | ||
<?php | ||
|
||
// Checks that JITed code does not crash in --repeat 2 after the ModelInterface | ||
// interface is recompiled and Model is re-linked. | ||
|
||
require __DIR__ . '/gh8591-001.inc'; | ||
|
||
class Model implements ModelInterface | ||
{ | ||
protected static int $field = 1; | ||
|
||
public function __construct() | ||
{ | ||
for ($i = 0; $i < 10; $i++) { | ||
$this->cast(); | ||
} | ||
} | ||
|
||
private function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} | ||
|
||
new Model(); | ||
|
||
// mark the file as changed (important) | ||
touch(__DIR__ . '/gh8591-001.inc'); | ||
|
||
var_dump($x); | ||
|
||
print "OK"; | ||
--EXPECT-- | ||
int(1) | ||
OK |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
interface ModelInterface | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
--TEST-- | ||
Bug GH-8591 002 (JIT does not account for class re-compile) | ||
--EXTENSIONS-- | ||
opcache | ||
--INI-- | ||
opcache.enable=1 | ||
opcache.enable_cli=1 | ||
opcache.jit_buffer_size=1M | ||
opcache.jit=1255 | ||
opcache.file_update_protection=0 | ||
opcache.revalidate_freq=0 | ||
opcache.protect_memory=1 | ||
--FILE-- | ||
<?php | ||
|
||
// Checks that JITed code does not crash in --repeat 2 after the ModelInterface | ||
// interface changes and Model is re-linked. | ||
|
||
if (!isset(opcache_get_status()['scripts'][__DIR__ . '/gh8591-002.inc'])) { | ||
require __DIR__ . '/gh8591-001.inc'; | ||
} else { | ||
interface ModelInterace | ||
{ | ||
} | ||
} | ||
|
||
class Model implements ModelInterface | ||
{ | ||
protected static int $field = 1; | ||
|
||
public function __construct() | ||
{ | ||
for ($i = 0; $i < 10; $i++) { | ||
$this->cast(); | ||
} | ||
} | ||
|
||
private function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} | ||
|
||
new Model(); | ||
|
||
var_dump($x); | ||
|
||
print "OK"; | ||
--EXPECT-- | ||
int(1) | ||
OK |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--TEST-- | ||
Bug GH-8591 003 (JIT does not account for class re-compile) | ||
--EXTENSIONS-- | ||
opcache | ||
--INI-- | ||
opcache.enable=1 | ||
opcache.enable_cli=1 | ||
opcache.jit_buffer_size=1M | ||
opcache.jit=1255 | ||
opcache.file_update_protection=0 | ||
opcache.revalidate_freq=0 | ||
opcache.protect_memory=1 | ||
--FILE-- | ||
<?php | ||
|
||
interface ModelInterface | ||
{ | ||
} | ||
|
||
class Model implements ModelInterface | ||
{ | ||
protected static int $field = 1; | ||
|
||
public function __construct() | ||
{ | ||
for ($i = 0; $i < 10; $i++) { | ||
$this->cast(); | ||
} | ||
} | ||
|
||
private function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} | ||
|
||
new Model(); | ||
|
||
var_dump($x); | ||
|
||
print "OK"; | ||
--EXPECT-- | ||
int(1) | ||
OK |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
trait ModelTrait | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--TEST-- | ||
Bug GH-8591 004 (JIT does not account for class re-compile) | ||
--EXTENSIONS-- | ||
opcache | ||
--INI-- | ||
opcache.enable=1 | ||
opcache.enable_cli=1 | ||
opcache.jit_buffer_size=1M | ||
opcache.jit=1255 | ||
opcache.file_update_protection=0 | ||
opcache.revalidate_freq=0 | ||
opcache.protect_memory=1 | ||
--FILE-- | ||
<?php | ||
|
||
// Checks that JITed code does not crash in --repeat 2 after the ModelTrait | ||
// trait is recompiled and Model is re-linked. | ||
|
||
require __DIR__ . '/gh8591-004.inc'; | ||
|
||
class Model | ||
{ | ||
use ModelTrait; | ||
|
||
protected static int $field = 1; | ||
|
||
public function __construct() | ||
{ | ||
for ($i = 0; $i < 10; $i++) { | ||
$this->cast(); | ||
} | ||
} | ||
|
||
private function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} | ||
|
||
new Model(); | ||
|
||
// mark the file as changed (important) | ||
touch(__DIR__ . '/gh8591-004.inc'); | ||
|
||
var_dump($x); | ||
|
||
print "OK"; | ||
--EXPECT-- | ||
int(1) | ||
OK |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
class AbstractModel | ||
{ | ||
protected static int $field = 1; | ||
|
||
final protected function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
--TEST-- | ||
Bug GH-8591 001 (JIT does not account for class re-compile) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like a typo in test name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, thank you. I will update it. |
||
--EXTENSIONS-- | ||
opcache | ||
--INI-- | ||
opcache.enable=1 | ||
opcache.enable_cli=1 | ||
opcache.jit_buffer_size=1M | ||
opcache.jit=1255 | ||
opcache.file_update_protection=0 | ||
opcache.revalidate_freq=0 | ||
opcache.protect_memory=1 | ||
--FILE-- | ||
<?php | ||
|
||
// Checks that JITed code does not crash in --repeat 2 after the AbstractModel | ||
// class is recompiled and Model is re-linked. | ||
|
||
require __DIR__ . '/gh8591-005.inc'; | ||
|
||
class Model extends AbstractModel | ||
{ | ||
public function __construct() | ||
{ | ||
for ($i = 0; $i < 10; $i++) { | ||
$this->cast(); | ||
} | ||
} | ||
} | ||
|
||
new Model(); | ||
|
||
// mark the file as changed (important) | ||
touch(__DIR__ . '/gh8591-005.inc'); | ||
|
||
var_dump($x); | ||
|
||
print "OK"; | ||
--EXPECT-- | ||
int(1) | ||
OK |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
class AbstractModel | ||
{ | ||
protected static int $field = 1; | ||
|
||
final protected function cast() | ||
{ | ||
global $x; | ||
$x = static::$field; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func
here comes from static analysis / zend optimizer. In my understanding a non-nullfunc
here is an indication that the call is non polymorphic, since the function could be resolved statically. A guard was not added in this case, with the assumption thatfunc
could not change. This assumption doesn't hold true across multiple requests.