Skip to content

Commit e57f1f4

Browse files
committed
opcache: fix PASS_15 constant inlining correctness
- define() must not be inlined; it works at runtime. Record a sentinel to also block inlining of any subsequent const redeclaration of the same name. - Attributed constants must not be inlined; attributes like #[\Deprecated] need to fire at access time, which inlining bypasses. Same sentinel approach blocks subsequent redeclarations. Extract sentinel logic into zend_optimizer_block_constant().
1 parent 93dba17 commit e57f1f4

4 files changed

Lines changed: 29 additions & 3 deletions

File tree

Zend/Optimizer/pass1.c

Lines changed: 12 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) &&
@@ -287,6 +292,12 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
287292
replace_by_const_or_qm_assign(op_array, opline, &result);
288293
break;
289294
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;
290301
case ZEND_DECLARE_CONST:
291302
if (collect_constants &&
292303
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_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);

ext/opcache/tests/const_inline_define.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
const and define() at file scope are inlined by the optimizer
2+
const at file scope is inlined by the optimizer, define() is not
33
--EXTENSIONS--
44
opcache
55
--INI--
@@ -39,4 +39,5 @@ use_define:
3939
; (lines=%d, args=%d, vars=%d, tmps=%d)
4040
; (after optimizer)
4141
; %s
42-
0000 RETURN int(2)
42+
0000 T%d = FETCH_CONSTANT string("DEFINE_VAL")
43+
0001 RETURN %s

0 commit comments

Comments
 (0)