mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Allow optimizer to depend on preloaded symbols (#15021)
* Allow optimizer to depend on preloaded symbols It is safe for the optimizer to rely on preloaded symbols. This can occur when compiling non-preloaded files, referencing preloaded ones. * Disable inline pass for observer test * Move duplicated code into functions * Add comment to specific optimization value * Optimizer should only rely on preloaded symbols in the symbol table * Fix skipif for windows
This commit is contained in:
parent
521178709e
commit
2e9cc9bc30
@ -792,6 +792,42 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
|
||||
}
|
||||
}
|
||||
|
||||
static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename)
|
||||
{
|
||||
zend_class_entry *ce = Z_PTR_P(ce_zv);
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_PRELOADED) {
|
||||
Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val));
|
||||
size_t offset = ce_bucket - EG(class_table)->arData;
|
||||
if (offset < EG(persistent_classes_count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ce->type == ZEND_USER_CLASS
|
||||
&& (!ce->info.user.filename || ce->info.user.filename != filename);
|
||||
}
|
||||
|
||||
static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
|
||||
{
|
||||
zend_function *fbc = Z_PTR_P(fbc_zv);
|
||||
|
||||
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
|
||||
return false;
|
||||
} else if (fbc->type == ZEND_USER_FUNCTION) {
|
||||
if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) {
|
||||
Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val));
|
||||
size_t offset = fbc_bucket - EG(function_table)->arData;
|
||||
if (offset < EG(persistent_functions_count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !fbc->op_array.filename || fbc->op_array.filename != filename;
|
||||
} else {
|
||||
ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
zend_class_entry *zend_optimizer_get_class_entry(
|
||||
const zend_script *script, const zend_op_array *op_array, zend_string *lcname) {
|
||||
zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
|
||||
@ -799,11 +835,9 @@ zend_class_entry *zend_optimizer_get_class_entry(
|
||||
return ce;
|
||||
}
|
||||
|
||||
ce = zend_hash_find_ptr(CG(class_table), lcname);
|
||||
if (ce
|
||||
&& (ce->type == ZEND_INTERNAL_CLASS
|
||||
|| (op_array && ce->info.user.filename == op_array->filename))) {
|
||||
return ce;
|
||||
zval *ce_zv = zend_hash_find(CG(class_table), lcname);
|
||||
if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array ? op_array->filename : NULL)) {
|
||||
return Z_PTR_P(ce_zv);
|
||||
}
|
||||
|
||||
if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) {
|
||||
@ -844,15 +878,9 @@ const zend_class_constant *zend_fetch_class_const_info(
|
||||
if (script) {
|
||||
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1));
|
||||
} else {
|
||||
zend_class_entry *tmp = zend_hash_find_ptr(EG(class_table), Z_STR_P(op1 + 1));
|
||||
if (tmp != NULL) {
|
||||
if (tmp->type == ZEND_INTERNAL_CLASS) {
|
||||
ce = tmp;
|
||||
} else if (tmp->type == ZEND_USER_CLASS
|
||||
&& tmp->info.user.filename
|
||||
&& tmp->info.user.filename == op_array->filename) {
|
||||
ce = tmp;
|
||||
}
|
||||
zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1));
|
||||
if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) {
|
||||
ce = Z_PTR_P(ce_zv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -897,15 +925,12 @@ zend_function *zend_optimizer_get_called_func(
|
||||
{
|
||||
zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
|
||||
zend_function *func;
|
||||
zval *func_zv;
|
||||
if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
|
||||
return func;
|
||||
} else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
|
||||
if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
return func;
|
||||
} else if (func->type == ZEND_USER_FUNCTION &&
|
||||
func->op_array.filename &&
|
||||
func->op_array.filename == op_array->filename) {
|
||||
return func;
|
||||
} else if ((func_zv = zend_hash_find(EG(function_table), function_name)) != NULL) {
|
||||
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
|
||||
return Z_PTR_P(func_zv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -915,15 +940,12 @@ zend_function *zend_optimizer_get_called_func(
|
||||
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
|
||||
zval *function_name = CRT_CONSTANT(opline->op2) + 1;
|
||||
zend_function *func;
|
||||
zval *func_zv;
|
||||
if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
|
||||
return func;
|
||||
} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
|
||||
if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
return func;
|
||||
} else if (func->type == ZEND_USER_FUNCTION &&
|
||||
func->op_array.filename &&
|
||||
func->op_array.filename == op_array->filename) {
|
||||
return func;
|
||||
} else if ((func_zv = zend_hash_find(EG(function_table), Z_STR_P(function_name))) != NULL) {
|
||||
if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) {
|
||||
return Z_PTR_P(func_zv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
ext/opcache/tests/gh15021.phpt
Normal file
31
ext/opcache/tests/gh15021.phpt
Normal file
@ -0,0 +1,31 @@
|
||||
--TEST--
|
||||
GH-15021: Optimizer only relies on preloaded top-level symbols
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.preload={PWD}/gh15021_preload.inc
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
putenv('RUNTIME=1');
|
||||
$firstRun = !isset(opcache_get_status()['scripts'][__DIR__ . DIRECTORY_SEPARATOR . 'gh15021_required.inc']);
|
||||
|
||||
if ($firstRun) {
|
||||
require __DIR__ . '/gh15021_a.inc';
|
||||
$expected = 1;
|
||||
} else {
|
||||
require __DIR__ . '/gh15021_b.inc';
|
||||
$expected = 2;
|
||||
}
|
||||
|
||||
require __DIR__ . '/gh15021_required.inc';
|
||||
|
||||
var_dump(f() === $expected);
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
7
ext/opcache/tests/gh15021_a.inc
Normal file
7
ext/opcache/tests/gh15021_a.inc
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (getenv('RUNTIME')) {
|
||||
function g(): int {
|
||||
return 1;
|
||||
}
|
||||
}
|
7
ext/opcache/tests/gh15021_b.inc
Normal file
7
ext/opcache/tests/gh15021_b.inc
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (getenv('RUNTIME')) {
|
||||
function g(): int {
|
||||
return 2;
|
||||
}
|
||||
}
|
4
ext/opcache/tests/gh15021_preload.inc
Normal file
4
ext/opcache/tests/gh15021_preload.inc
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
opcache_compile_file(__DIR__ . '/gh15021_a.inc');
|
||||
opcache_compile_file(__DIR__ . '/gh15021_b.inc');
|
5
ext/opcache/tests/gh15021_required.inc
Normal file
5
ext/opcache/tests/gh15021_required.inc
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
function f(): int {
|
||||
return g();
|
||||
}
|
5
ext/opcache/tests/preload_optimizer.inc
Normal file
5
ext/opcache/tests/preload_optimizer.inc
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
function foo() {
|
||||
return 42;
|
||||
}
|
43
ext/opcache/tests/preload_optimizer.phpt
Normal file
43
ext/opcache/tests/preload_optimizer.phpt
Normal file
@ -0,0 +1,43 @@
|
||||
--TEST--
|
||||
Optimizer may rely on preloaded symbols
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.preload={PWD}/preload_optimizer.inc
|
||||
opcache.opt_debug_level=0x20000
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
echo foo();
|
||||
?>
|
||||
--EXPECTF--
|
||||
$_main:
|
||||
; (lines=1, args=0, vars=0, tmps=%d)
|
||||
; (after optimizer)
|
||||
; $PRELOAD$:0-0
|
||||
0000 RETURN null
|
||||
|
||||
foo:
|
||||
; (lines=1, args=0, vars=0, tmps=%d)
|
||||
; (after optimizer)
|
||||
; %spreload_optimizer.inc:3-5
|
||||
0000 RETURN int(42)
|
||||
|
||||
$_main:
|
||||
; (lines=1, args=0, vars=0, tmps=%d)
|
||||
; (after optimizer)
|
||||
; %spreload_optimizer.inc:1-6
|
||||
0000 RETURN int(1)
|
||||
|
||||
$_main:
|
||||
; (lines=2, args=0, vars=0, tmps=%d)
|
||||
; (after optimizer)
|
||||
; %spreload_optimizer.php:1-4
|
||||
0000 ECHO string("42")
|
||||
0001 RETURN int(1)
|
||||
42
|
@ -10,7 +10,8 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.optimization_level=-1
|
||||
; Disable inlining pass
|
||||
opcache.optimization_level=0x7ffe3fff
|
||||
opcache.preload={PWD}/observer_preload.inc
|
||||
opcache.file_cache=
|
||||
opcache.file_cache_only=0
|
||||
|
Loading…
Reference in New Issue
Block a user