Skip to content

Commit 88eae43

Browse files
committed
Remove uses of VARs in extended_value
The DECLARE_(ANON_)INHERITED_CLASS(_DELAYED) opcodes were referencing the parent ce VAR through extended_value. This is hacky and we can't track the def-use chain in SSA. To avoid this, the layout of declaration opcodes is changed as follows: op1 points to the lcname and rtd_key literals, in that order. (For anon/lambda declarations only one of lcname or rtd_key is present.) This frees up op2, which is now used to reference the parent ce VAR in inheriting declarations. The jmp offset for anon class declarations is moved frop op2 to extended_value. The changes were applied both to class and function declarations to keep everything symmetric.
1 parent 1fe8a1d commit 88eae43

16 files changed

+210
-252
lines changed

Zend/zend_compile.c

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -979,24 +979,24 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
979979
ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
980980
{
981981
zend_function *function, *new_function;
982-
zval *op1, *op2;
982+
zval *lcname, *rtd_key;
983983

984984
if (compile_time) {
985-
op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
986-
op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
985+
lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
986+
rtd_key = lcname + 1;
987987
} else {
988-
op1 = RT_CONSTANT(op_array, opline->op1);
989-
op2 = RT_CONSTANT(op_array, opline->op2);
988+
lcname = RT_CONSTANT(op_array, opline->op1);
989+
rtd_key = lcname + 1;
990990
}
991991

992-
function = zend_hash_find_ptr(function_table, Z_STR_P(op1));
992+
function = zend_hash_find_ptr(function_table, Z_STR_P(rtd_key));
993993
new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
994994
memcpy(new_function, function, sizeof(zend_op_array));
995-
if (zend_hash_add_ptr(function_table, Z_STR_P(op2), new_function) == NULL) {
995+
if (zend_hash_add_ptr(function_table, Z_STR_P(lcname), new_function) == NULL) {
996996
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
997997
zend_function *old_function;
998998

999-
if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(op2))) != NULL
999+
if ((old_function = zend_hash_find_ptr(function_table, Z_STR_P(lcname))) != NULL
10001000
&& old_function->type == ZEND_USER_FUNCTION
10011001
&& old_function->op_array.last > 0) {
10021002
zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
@@ -1020,21 +1020,21 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opli
10201020
ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time) /* {{{ */
10211021
{
10221022
zend_class_entry *ce;
1023-
zval *op1, *op2;
1023+
zval *lcname, *rtd_key;
10241024

10251025
if (compile_time) {
1026-
op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
1027-
op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
1026+
lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
1027+
rtd_key = lcname + 1;
10281028
} else {
1029-
op1 = RT_CONSTANT(op_array, opline->op1);
1030-
op2 = RT_CONSTANT(op_array, opline->op2);
1029+
lcname = RT_CONSTANT(op_array, opline->op1);
1030+
rtd_key = lcname + 1;
10311031
}
1032-
if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(op1))) == NULL) {
1033-
zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1));
1032+
if ((ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key))) == NULL) {
1033+
zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(rtd_key));
10341034
return NULL;
10351035
}
10361036
ce->refcount++;
1037-
if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
1037+
if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
10381038
ce->refcount--;
10391039
if (!compile_time) {
10401040
/* If we're in compile time, in practice, it's quite possible
@@ -1057,17 +1057,17 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze
10571057
ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time) /* {{{ */
10581058
{
10591059
zend_class_entry *ce;
1060-
zval *op1, *op2;
1060+
zval *lcname, *rtd_key;
10611061

10621062
if (compile_time) {
1063-
op1 = CT_CONSTANT_EX(op_array, opline->op1.constant);
1064-
op2 = CT_CONSTANT_EX(op_array, opline->op2.constant);
1063+
lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
1064+
rtd_key = lcname + 1;
10651065
} else {
1066-
op1 = RT_CONSTANT(op_array, opline->op1);
1067-
op2 = RT_CONSTANT(op_array, opline->op2);
1066+
lcname = RT_CONSTANT(op_array, opline->op1);
1067+
rtd_key = lcname + 1;
10681068
}
10691069

1070-
ce = zend_hash_find_ptr(class_table, Z_STR_P(op1));
1070+
ce = zend_hash_find_ptr(class_table, Z_STR_P(rtd_key));
10711071

10721072
if (!ce) {
10731073
if (!compile_time) {
@@ -1076,12 +1076,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
10761076
* so we shut up about it. This allows the if (!defined('FOO')) { return; }
10771077
* approach to work.
10781078
*/
1079-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(op2)), Z_STRVAL_P(op2));
1079+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)), Z_STRVAL_P(lcname));
10801080
}
10811081
return NULL;
10821082
}
10831083

1084-
if (zend_hash_exists(class_table, Z_STR_P(op2))) {
1084+
if (zend_hash_exists(class_table, Z_STR_P(lcname))) {
10851085
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
10861086
}
10871087

@@ -1090,7 +1090,7 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
10901090
ce->refcount++;
10911091

10921092
/* Register the derived class */
1093-
if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) {
1093+
if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
10941094
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
10951095
}
10961096
return ce;
@@ -1164,9 +1164,9 @@ void zend_do_early_binding(void) /* {{{ */
11641164
return;
11651165
}
11661166

1167-
zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)));
1167+
zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)+1));
1168+
zend_del_literal(CG(active_op_array), opline->op1.constant+1);
11681169
zend_del_literal(CG(active_op_array), opline->op1.constant);
1169-
zend_del_literal(CG(active_op_array), opline->op2.constant);
11701170
MAKE_NOP(opline);
11711171
}
11721172
/* }}} */
@@ -3590,7 +3590,7 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
35903590
}
35913591
class_node.op_type = opline->result_type;
35923592
class_node.u.op.var = opline->result.var;
3593-
opline->op1.opline_num = get_next_op_number(CG(active_op_array));
3593+
opline->extended_value = get_next_op_number(CG(active_op_array));
35943594
} else {
35953595
zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
35963596
}
@@ -5027,7 +5027,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
50275027
static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) /* {{{ */
50285028
{
50295029
zend_ast *params_ast = decl->child[0];
5030-
zend_string *name = decl->name, *lcname;
5030+
zend_string *name = decl->name, *lcname, *key;
50315031
zend_op *opline;
50325032

50335033
op_array->function_name = name = zend_prefix_with_ns(name);
@@ -5049,22 +5049,20 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
50495049
ZEND_AUTOLOAD_FUNC_NAME);
50505050
}
50515051

5052+
key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
5053+
zend_hash_update_ptr(CG(function_table), key, op_array);
5054+
50525055
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
50535056
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
5057+
opline->op1_type = IS_CONST;
5058+
LITERAL_STR(opline->op1, key);
50545059
} else {
50555060
opline = get_next_op(CG(active_op_array));
50565061
opline->opcode = ZEND_DECLARE_FUNCTION;
5057-
opline->op2_type = IS_CONST;
5058-
LITERAL_STR(opline->op2, zend_string_copy(lcname));
5059-
}
5060-
5061-
{
5062-
zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
5063-
50645062
opline->op1_type = IS_CONST;
5065-
LITERAL_STR(opline->op1, key);
5066-
5067-
zend_hash_update_ptr(CG(function_table), key, op_array);
5063+
LITERAL_STR(opline->op1, zend_string_copy(lcname));
5064+
/* RTD key is placed after lcname literal in op1 */
5065+
zend_add_literal_string(CG(active_op_array), &key);
50685066
}
50695067

50705068
zend_string_release(lcname);
@@ -5492,34 +5490,31 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
54925490

54935491
GET_NODE(&FC(implementing_class), opline->result);
54945492

5495-
opline->op2_type = IS_CONST;
5496-
LITERAL_STR(opline->op2, lcname);
5493+
opline->op1_type = IS_CONST;
5494+
LITERAL_STR(opline->op1, lcname);
54975495

54985496
if (decl->flags & ZEND_ACC_ANON_CLASS) {
54995497
if (extends_ast) {
55005498
opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
5501-
opline->extended_value = extends_node.u.op.var;
5499+
SET_NODE(opline->op2, &extends_node);
55025500
} else {
55035501
opline->opcode = ZEND_DECLARE_ANON_CLASS;
55045502
}
55055503

5506-
opline->op1_type = IS_UNUSED;
5507-
55085504
zend_hash_update_ptr(CG(class_table), lcname, ce);
55095505
} else {
55105506
zend_string *key;
55115507

55125508
if (extends_ast) {
55135509
opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
5514-
opline->extended_value = extends_node.u.op.var;
5510+
SET_NODE(opline->op2, &extends_node);
55155511
} else {
55165512
opline->opcode = ZEND_DECLARE_CLASS;
55175513
}
55185514

55195515
key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
5520-
5521-
opline->op1_type = IS_CONST;
5522-
LITERAL_STR(opline->op1, key);
5516+
/* RTD key is placed after lcname literal in op1 */
5517+
zend_add_literal_string(CG(active_op_array), &key);
55235518

55245519
zend_hash_update_ptr(CG(class_table), key, ce);
55255520
}

Zend/zend_opcode.c

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -639,13 +639,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
639639
case ZEND_FAST_RET:
640640
zend_resolve_finally_ret(op_array, opline - op_array->opcodes);
641641
break;
642-
case ZEND_DECLARE_ANON_INHERITED_CLASS:
643-
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
644-
/* break omitted intentionally */
645-
case ZEND_DECLARE_INHERITED_CLASS:
646-
case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
647-
opline->extended_value = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->extended_value);
648-
break;
649642
case ZEND_BRK:
650643
case ZEND_CONT:
651644
{
@@ -667,13 +660,8 @@ ZEND_API int pass_two(zend_op_array *op_array)
667660
}
668661
/* break omitted intentionally */
669662
case ZEND_JMP:
670-
case ZEND_DECLARE_ANON_CLASS:
671663
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
672664
break;
673-
case ZEND_CATCH:
674-
/* absolute index to relative offset */
675-
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
676-
break;
677665
case ZEND_JMPZNZ:
678666
/* absolute index to relative offset */
679667
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
@@ -696,8 +684,12 @@ ZEND_API int pass_two(zend_op_array *op_array)
696684
}
697685
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
698686
break;
687+
case ZEND_DECLARE_ANON_CLASS:
688+
case ZEND_DECLARE_ANON_INHERITED_CLASS:
689+
case ZEND_CATCH:
699690
case ZEND_FE_FETCH_R:
700691
case ZEND_FE_FETCH_RW:
692+
/* absolute index to relative offset */
701693
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
702694
break;
703695
case ZEND_VERIFY_RETURN_TYPE:

Zend/zend_vm_def.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7058,41 +7058,42 @@ ZEND_VM_HANDLER(139, ZEND_DECLARE_CLASS, ANY, ANY)
70587058
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
70597059
}
70607060

7061-
ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, ANY, VAR)
7061+
ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, VAR)
70627062
{
70637063
USE_OPLINE
70647064

70657065
SAVE_OPLINE();
7066-
Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
7066+
Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
70677067
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
70687068
}
70697069

7070-
ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, ANY, ANY, VAR)
7070+
ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, ANY, VAR)
70717071
{
70727072
USE_OPLINE
70737073
zval *zce, *orig_zce;
70747074

70757075
SAVE_OPLINE();
7076-
if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)))) == NULL ||
7077-
((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) != NULL &&
7076+
if ((zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)))) == NULL ||
7077+
((orig_zce = zend_hash_find(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)+1))) != NULL &&
70787078
Z_CE_P(zce) != Z_CE_P(orig_zce))) {
7079-
do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0);
7079+
do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->op2.var)), 0);
70807080
}
70817081
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
70827082
}
70837083

7084-
ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, JMP_ADDR, ANY)
7084+
ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY, JMP_ADDR)
70857085
{
70867086
zend_class_entry *ce;
70877087
USE_OPLINE
70887088

70897089
SAVE_OPLINE();
7090-
ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
7090+
ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
70917091
Z_CE_P(EX_VAR(opline->result.var)) = ce;
70927092
ZEND_ASSERT(ce != NULL);
70937093

70947094
if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
7095-
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
7095+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
7096+
ZEND_VM_CONTINUE();
70967097
}
70977098

70987099
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
@@ -7102,21 +7103,22 @@ ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, JMP_ADDR, ANY)
71027103
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
71037104
}
71047105

7105-
ZEND_VM_HANDLER(172, ZEND_DECLARE_ANON_INHERITED_CLASS, JMP_ADDR, ANY, VAR)
7106+
ZEND_VM_HANDLER(172, ZEND_DECLARE_ANON_INHERITED_CLASS, ANY, VAR, JMP_ADDR)
71067107
{
71077108
zend_class_entry *ce;
71087109
USE_OPLINE
71097110

71107111
SAVE_OPLINE();
7111-
ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
7112+
ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op1)));
71127113
Z_CE_P(EX_VAR(opline->result.var)) = ce;
71137114
ZEND_ASSERT(ce != NULL);
71147115

71157116
if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
7116-
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
7117+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
7118+
ZEND_VM_CONTINUE();
71177119
}
71187120

7119-
zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->extended_value)));
7121+
zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->op2.var)));
71207122
ce->ce_flags |= ZEND_ACC_ANON_BOUND;
71217123
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
71227124
}

0 commit comments

Comments
 (0)