Skip to content

Commit 06bacd6

Browse files
committed
Implement goto var/fast_call/discard_exception elimination
1 parent 6ff333d commit 06bacd6

5 files changed

+64
-36
lines changed

Zend/tests/alternative_offset_syntax_compile_error_in_const_expr.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ const FOO_COMPILE_ERROR = "BAR"{0};
66
var_dump(FOO_COMPILE_ERROR);
77
?>
88
--EXPECTF--
9-
Fatal error: Array and string offset access syntax with curly braces is no longer supported in %s on line 2
9+
Parse error: syntax error, unexpected token "{", expecting "," or ";" in %s on line %d

Zend/tests/alternative_offset_syntax_compile_error_outside_const_expr.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ $foo = 'BAR';
66
var_dump($foo{0});
77
?>
88
--EXPECTF--
9-
Fatal error: Array and string offset access syntax with curly braces is no longer supported in %s on line 3
9+
Parse error: syntax error, unexpected token "{", expecting ")" in %s on line %d

Zend/tests/alternative_offset_syntax_in_encaps_string.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ Alternative offset syntax should emit only E_COMPILE_ERROR in string interpolati
33
--FILE--
44
<?php "{$g{'h'}}"; ?>
55
--EXPECTF--
6-
Fatal error: Array and string offset access syntax with curly braces is no longer supported in %s on line 1
6+
Parse error: syntax error, unexpected token "{", expecting "->" or "?->" or "[" in %s on line %d

Zend/zend_compile.c

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5200,11 +5200,15 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, uint32_t excluded_
52005200
SET_NODE(opline->op2, return_value);
52015201
}
52025202
opline->op1.num = loop_var->try_catch_offset;
5203+
/* Only used in pass_two(). */
5204+
opline->extended_value = loop_var->try_catch_offset;
52035205
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
52045206
zend_op *opline = get_next_op();
52055207
opline->opcode = ZEND_DISCARD_EXCEPTION;
52065208
opline->op1_type = IS_TMP_VAR;
52075209
opline->op1.var = loop_var->var_num;
5210+
/* Only used in pass_two(). */
5211+
opline->extended_value = loop_var->try_catch_offset;
52085212
} else {
52095213
ZEND_ASSERT(loop_var->opcode == ZEND_FREE || loop_var->opcode == ZEND_FE_FREE);
52105214
ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR));
@@ -5427,9 +5431,8 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
54275431
zend_label *dest;
54285432
int current, remove_oplines = opline->op1.num;
54295433
zval *label;
5430-
uint32_t opnum = opline - op_array->opcodes;
54315434

5432-
label = CT_CONSTANT_EX(op_array, opline->op2.constant);
5435+
label = RT_CONSTANT(opline, opline->op2);
54335436
if (CG(context).labels == NULL ||
54345437
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL
54355438
) {
@@ -5450,21 +5453,6 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
54505453
CG(zend_lineno) = opline->lineno;
54515454
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
54525455
}
5453-
if (CG(context).brk_cont_array[current].start >= 0) {
5454-
remove_oplines--;
5455-
}
5456-
}
5457-
5458-
for (current = 0; current < op_array->last_try_catch; ++current) {
5459-
zend_try_catch_element *elem = &op_array->try_catch_array[current];
5460-
if (elem->try_op > opnum) {
5461-
break;
5462-
}
5463-
if (elem->finally_op && opnum < elem->finally_op - 1
5464-
&& (dest->opline_num > elem->finally_end || dest->opline_num < elem->try_op)
5465-
) {
5466-
remove_oplines--;
5467-
}
54685456
}
54695457

54705458
opline->opcode = ZEND_JMP;
@@ -5474,16 +5462,43 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
54745462
opline->op1.opline_num = dest->opline_num;
54755463
opline->extended_value = 0;
54765464

5477-
/* FIXME: This needs to be updated. The idea is to eliminate FREE/FE_FREE calls
5478-
* based on live-ranges. I.e. if we're jumping out of a live-range the value needs
5479-
* to be freed. Similarly, if we're jumping out of a try block, we need to keep the
5480-
* opcodes for FAST_CALL. */
5481-
// ZEND_ASSERT(remove_oplines >= 0);
5482-
// while (remove_oplines--) {
5483-
// opline--;
5484-
// MAKE_NOP(opline);
5485-
// ZEND_VM_SET_OPCODE_HANDLER(opline);
5486-
// }
5465+
for (; remove_oplines > 0; remove_oplines--) {
5466+
zend_op *predecessor = opline - remove_oplines;
5467+
switch (predecessor->opcode) {
5468+
case ZEND_FREE:
5469+
case ZEND_FE_FREE:;
5470+
zend_live_range *range = op_array->live_range;
5471+
zend_live_range *range_end = range + op_array->last_live_range;
5472+
while (range < range_end) {
5473+
if ((range->var & ~ZEND_LIVE_MASK) == predecessor->op1.var) {
5474+
if (dest->opline_num >= range->start && dest->opline_num < range->end) {
5475+
MAKE_NOP(predecessor);
5476+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5477+
}
5478+
}
5479+
range++;
5480+
}
5481+
break;
5482+
case ZEND_FAST_CALL: {
5483+
zend_try_catch_element *try_catch_elem = &op_array->try_catch_array[predecessor->extended_value];
5484+
/* We don't need to call finally if we stay within the try/catch block. */
5485+
if (dest->opline_num >= try_catch_elem->try_op && dest->opline_num < try_catch_elem->finally_op) {
5486+
MAKE_NOP(predecessor);
5487+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5488+
}
5489+
break;
5490+
}
5491+
case ZEND_DISCARD_EXCEPTION: {
5492+
zend_try_catch_element *try_catch_elem = &op_array->try_catch_array[predecessor->extended_value];
5493+
/* We don't need to call finally if we stay within the finally block. */
5494+
if (dest->opline_num >= try_catch_elem->finally_op && dest->opline_num < try_catch_elem->finally_end) {
5495+
MAKE_NOP(predecessor);
5496+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5497+
}
5498+
break;
5499+
}
5500+
}
5501+
}
54875502
}
54885503
/* }}} */
54895504

@@ -6341,6 +6356,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
63416356
discard_exception.opcode = ZEND_DISCARD_EXCEPTION;
63426357
discard_exception.var_type = IS_TMP_VAR;
63436358
discard_exception.var_num = CG(context).fast_call_var;
6359+
discard_exception.try_catch_offset = try_catch_offset;
63446360
zend_stack_push(&CG(loop_var_stack), &discard_exception);
63456361

63466362
CG(zend_lineno) = finally_ast->lineno;

Zend/zend_opcode.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,7 @@ ZEND_API void pass_two(zend_op_array *op_array)
10641064
* happens correctly if any of the following fixups generate a fatal error. */
10651065
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
10661066

1067+
bool has_goto = false;
10671068
opline = op_array->opcodes;
10681069
end = opline + op_array->last;
10691070
while (opline < end) {
@@ -1097,11 +1098,8 @@ ZEND_API void pass_two(zend_op_array *op_array)
10971098
}
10981099
break;
10991100
case ZEND_GOTO:
1100-
zend_resolve_goto_label(op_array, opline);
1101-
if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1102-
zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1103-
}
1104-
ZEND_FALLTHROUGH;
1101+
has_goto = true;
1102+
break;
11051103
case ZEND_JMP:
11061104
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
11071105
break;
@@ -1180,7 +1178,21 @@ ZEND_API void pass_two(zend_op_array *op_array)
11801178

11811179
zend_calc_live_ranges(op_array, NULL);
11821180

1183-
return;
1181+
if (has_goto) {
1182+
opline = op_array->opcodes;
1183+
end = opline + op_array->last;
1184+
while (opline < end) {
1185+
if (opline->opcode == ZEND_GOTO) {
1186+
zend_resolve_goto_label(op_array, opline);
1187+
if (op_array->fn_flags &ZEND_ACC_HAS_FINALLY_BLOCK) {
1188+
zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1189+
}
1190+
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1191+
ZEND_VM_SET_OPCODE_HANDLER(opline);
1192+
}
1193+
opline++;
1194+
}
1195+
}
11841196
}
11851197

11861198
ZEND_API unary_op_type get_unary_op(int opcode)

0 commit comments

Comments
 (0)