Skip to content

Commit 35cd64d

Browse files
committed
opcache: re-enable PASS_15 (constant collection) by default
Disabled in Dec 2015 (940c68b, 4070279) to fix bug #71127. define() and attributed constants are blocked from inlining via a sentinel entry. define() can be overridden at runtime (bug #71127) and attributed constants need to fire at access time. The sentinel also prevents inlining of any subsequent const redeclaration of the same name. Adds zend_optimizer_block_constant() for sentinel logic.
1 parent 43b56c9 commit 35cd64d

8 files changed

Lines changed: 198 additions & 2 deletions

Zend/Optimizer/pass1.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
151151
if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &result)) {
152152
break;
153153
}
154+
if (Z_TYPE(result) == IS_UNDEF) {
155+
/* blocked by define() or attributed const */
156+
break;
157+
}
154158
}
155159
if (Z_TYPE(result) == IS_CONSTANT_AST) {
156160
break;
@@ -224,8 +228,9 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
224228

225229
if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && send2_opline) {
226230

231+
/* define() can be overridden at runtime; block inlining */
227232
if (collect_constants) {
228-
zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
233+
zend_optimizer_block_constant(ctx, &ZEND_OP1_LITERAL(send1_opline));
229234
}
230235

231236
if (RESULT_UNUSED(opline) &&
@@ -286,6 +291,13 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
286291
literal_dtor(&ZEND_OP1_LITERAL(opline));
287292
replace_by_const_or_qm_assign(op_array, opline, &result);
288293
break;
294+
case ZEND_DECLARE_ATTRIBUTED_CONST:
295+
/* attributes must fire at access time; block inlining */
296+
if (collect_constants &&
297+
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
298+
zend_optimizer_block_constant(ctx, &ZEND_OP1_LITERAL(opline));
299+
}
300+
break;
289301
case ZEND_DECLARE_CONST:
290302
if (collect_constants &&
291303
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&

Zend/Optimizer/zend_optimizer.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@ void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, const zval *name,
5151
}
5252
}
5353

54+
/* prevent inlining of a constant; UNDEF sentinel blocks later DECLARE_CONST collection */
55+
void zend_optimizer_block_constant(zend_optimizer_ctx *ctx, const zval *name)
56+
{
57+
if (!ctx->constants) {
58+
ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
59+
zend_hash_init(ctx->constants, 16, NULL, zval_ptr_dtor_nogc, 0);
60+
}
61+
62+
zval undef;
63+
ZVAL_UNDEF(&undef);
64+
zend_hash_add(ctx->constants, Z_STR_P(name), &undef);
65+
}
66+
5467
zend_result zend_optimizer_eval_binary_op(zval *result, uint8_t opcode, zval *op1, zval *op2) /* {{{ */
5568
{
5669
if (zend_binary_op_produces_error(opcode, op1, op2)) {

Zend/Optimizer/zend_optimizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
4848

49-
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF"
49+
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEFFFF"
5050

5151

5252
#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1

Zend/Optimizer/zend_optimizer_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ void zend_optimizer_convert_to_free_op1(const zend_op_array *op_array, zend_op *
8080
uint32_t zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv);
8181
bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, bool copy);
8282
void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, const zval *name, zval* value);
83+
void zend_optimizer_block_constant(zend_optimizer_ctx *ctx, const zval *name);
8384
bool zend_optimizer_get_collected_constant(const HashTable *constants, const zval *name, zval* value);
8485
zend_result zend_optimizer_eval_binary_op(zval *result, uint8_t opcode, zval *op1, zval *op2);
8586
zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
const at file scope is inlined by the optimizer, define() is not
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.opt_debug_level=0x20000
8+
--FILE--
9+
<?php
10+
11+
const CONST_VAL = 1;
12+
define('DEFINE_VAL', 2);
13+
14+
function use_const() {
15+
return CONST_VAL;
16+
}
17+
18+
function use_define() {
19+
return DEFINE_VAL;
20+
}
21+
22+
?>
23+
--EXPECTF--
24+
$_main:
25+
; (lines=%d, args=%d, vars=%d, tmps=%d)
26+
; (after optimizer)
27+
; %s
28+
0000 DECLARE_CONST string("CONST_VAL") int(1)
29+
0001 DECLARE_CONST string("DEFINE_VAL") int(2)
30+
0002 RETURN int(1)
31+
32+
use_const:
33+
; (lines=%d, args=%d, vars=%d, tmps=%d)
34+
; (after optimizer)
35+
; %s
36+
0000 RETURN int(1)
37+
38+
use_define:
39+
; (lines=%d, args=%d, vars=%d, tmps=%d)
40+
; (after optimizer)
41+
; %s
42+
0000 T%d = FETCH_CONSTANT string("DEFINE_VAL")
43+
0001 RETURN %s
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Global and namespace const declarations are inlined by the optimizer
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.opt_debug_level=0x20000
8+
--FILE--
9+
<?php
10+
11+
const GLOBAL_CONST = 42;
12+
13+
function use_global_const() {
14+
return GLOBAL_CONST;
15+
}
16+
17+
function use_global_const_fqn() {
18+
return \GLOBAL_CONST;
19+
}
20+
21+
class MyClass {
22+
const CLASS_CONST = 99;
23+
24+
public function use_class_const() {
25+
return self::CLASS_CONST;
26+
}
27+
}
28+
29+
function use_class_const_static() {
30+
return MyClass::CLASS_CONST;
31+
}
32+
33+
?>
34+
--EXPECTF--
35+
$_main:
36+
; (lines=%d, args=%d, vars=%d, tmps=%d)
37+
; (after optimizer)
38+
; %s
39+
0000 DECLARE_CONST string("GLOBAL_CONST") int(42)
40+
0001 RETURN int(1)
41+
42+
use_global_const:
43+
; (lines=%d, args=%d, vars=%d, tmps=%d)
44+
; (after optimizer)
45+
; %s
46+
0000 RETURN int(42)
47+
48+
use_global_const_fqn:
49+
; (lines=%d, args=%d, vars=%d, tmps=%d)
50+
; (after optimizer)
51+
; %s
52+
0000 RETURN int(42)
53+
54+
use_class_const_static:
55+
; (lines=%d, args=%d, vars=%d, tmps=%d)
56+
; (after optimizer)
57+
; %s
58+
0000 RETURN int(99)
59+
60+
MyClass::use_class_const:
61+
; (lines=%d, args=%d, vars=%d, tmps=%d)
62+
; (after optimizer)
63+
; %s
64+
0000 RETURN int(99)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Namespace const declarations are inlined by the optimizer
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.opt_debug_level=0x20000
8+
--FILE--
9+
<?php
10+
namespace MyNS;
11+
12+
const NS_CONST = 100;
13+
14+
function use_ns_const() {
15+
return NS_CONST;
16+
}
17+
18+
function use_ns_const_fqn() {
19+
return \MyNS\NS_CONST;
20+
}
21+
22+
?>
23+
--EXPECTF--
24+
$_main:
25+
; (lines=%d, args=%d, vars=%d, tmps=%d)
26+
; (after optimizer)
27+
; %s
28+
0000 DECLARE_CONST string("MyNS\\NS_CONST") int(100)
29+
0001 RETURN int(1)
30+
31+
MyNS\use_ns_const:
32+
; (lines=%d, args=%d, vars=%d, tmps=%d)
33+
; (after optimizer)
34+
; %s
35+
0000 RETURN int(100)
36+
37+
MyNS\use_ns_const_fqn:
38+
; (lines=%d, args=%d, vars=%d, tmps=%d)
39+
; (after optimizer)
40+
; %s
41+
0000 RETURN int(100)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
define() followed by const redeclaration is not inlined
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
--FILE--
8+
<?php
9+
10+
define('FOO', 'first');
11+
const FOO = 'second';
12+
13+
function get_foo() {
14+
return FOO;
15+
}
16+
17+
var_dump(get_foo());
18+
19+
?>
20+
--EXPECTF--
21+
Warning: Constant FOO already defined, this will be an error in PHP 9 in %s on line %d
22+
string(5) "first"

0 commit comments

Comments
 (0)