Skip to content

Commit b45c117

Browse files
committed
Implement goto var/fast_call/discard_exception elimination
1 parent 095d538 commit b45c117

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
@@ -5172,11 +5172,15 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, uint32_t excluded_
51725172
SET_NODE(opline->op2, return_value);
51735173
}
51745174
opline->op1.num = loop_var->try_catch_offset;
5175+
/* Only used in pass_two(). */
5176+
opline->extended_value = loop_var->try_catch_offset;
51755177
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
51765178
zend_op *opline = get_next_op();
51775179
opline->opcode = ZEND_DISCARD_EXCEPTION;
51785180
opline->op1_type = IS_TMP_VAR;
51795181
opline->op1.var = loop_var->var_num;
5182+
/* Only used in pass_two(). */
5183+
opline->extended_value = loop_var->try_catch_offset;
51805184
} else {
51815185
ZEND_ASSERT(loop_var->opcode == ZEND_FREE || loop_var->opcode == ZEND_FE_FREE);
51825186
ZEND_ASSERT(loop_var->var_type & (IS_VAR|IS_TMP_VAR));
@@ -5399,9 +5403,8 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
53995403
zend_label *dest;
54005404
int current, remove_oplines = opline->op1.num;
54015405
zval *label;
5402-
uint32_t opnum = opline - op_array->opcodes;
54035406

5404-
label = CT_CONSTANT_EX(op_array, opline->op2.constant);
5407+
label = RT_CONSTANT(opline, opline->op2);
54055408
if (CG(context).labels == NULL ||
54065409
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL
54075410
) {
@@ -5422,21 +5425,6 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
54225425
CG(zend_lineno) = opline->lineno;
54235426
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
54245427
}
5425-
if (CG(context).brk_cont_array[current].start >= 0) {
5426-
remove_oplines--;
5427-
}
5428-
}
5429-
5430-
for (current = 0; current < op_array->last_try_catch; ++current) {
5431-
zend_try_catch_element *elem = &op_array->try_catch_array[current];
5432-
if (elem->try_op > opnum) {
5433-
break;
5434-
}
5435-
if (elem->finally_op && opnum < elem->finally_op - 1
5436-
&& (dest->opline_num > elem->finally_end || dest->opline_num < elem->try_op)
5437-
) {
5438-
remove_oplines--;
5439-
}
54405428
}
54415429

54425430
opline->opcode = ZEND_JMP;
@@ -5446,16 +5434,43 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
54465434
opline->op1.opline_num = dest->opline_num;
54475435
opline->extended_value = 0;
54485436

5449-
/* FIXME: This needs to be updated. The idea is to eliminate FREE/FE_FREE calls
5450-
* based on live-ranges. I.e. if we're jumping out of a live-range the value needs
5451-
* to be freed. Similarly, if we're jumping out of a try block, we need to keep the
5452-
* opcodes for FAST_CALL. */
5453-
// ZEND_ASSERT(remove_oplines >= 0);
5454-
// while (remove_oplines--) {
5455-
// opline--;
5456-
// MAKE_NOP(opline);
5457-
// ZEND_VM_SET_OPCODE_HANDLER(opline);
5458-
// }
5437+
for (; remove_oplines > 0; remove_oplines--) {
5438+
zend_op *predecessor = opline - remove_oplines;
5439+
switch (predecessor->opcode) {
5440+
case ZEND_FREE:
5441+
case ZEND_FE_FREE:;
5442+
zend_live_range *range = op_array->live_range;
5443+
zend_live_range *range_end = range + op_array->last_live_range;
5444+
while (range < range_end) {
5445+
if ((range->var & ~ZEND_LIVE_MASK) == predecessor->op1.var) {
5446+
if (dest->opline_num >= range->start && dest->opline_num < range->end) {
5447+
MAKE_NOP(predecessor);
5448+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5449+
}
5450+
}
5451+
range++;
5452+
}
5453+
break;
5454+
case ZEND_FAST_CALL: {
5455+
zend_try_catch_element *try_catch_elem = &op_array->try_catch_array[predecessor->extended_value];
5456+
/* We don't need to call finally if we stay within the try/catch block. */
5457+
if (dest->opline_num >= try_catch_elem->try_op && dest->opline_num < try_catch_elem->finally_op) {
5458+
MAKE_NOP(predecessor);
5459+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5460+
}
5461+
break;
5462+
}
5463+
case ZEND_DISCARD_EXCEPTION: {
5464+
zend_try_catch_element *try_catch_elem = &op_array->try_catch_array[predecessor->extended_value];
5465+
/* We don't need to call finally if we stay within the finally block. */
5466+
if (dest->opline_num >= try_catch_elem->finally_op && dest->opline_num < try_catch_elem->finally_end) {
5467+
MAKE_NOP(predecessor);
5468+
ZEND_VM_SET_OPCODE_HANDLER(predecessor);
5469+
}
5470+
break;
5471+
}
5472+
}
5473+
}
54595474
}
54605475
/* }}} */
54615476

@@ -6313,6 +6328,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
63136328
discard_exception.opcode = ZEND_DISCARD_EXCEPTION;
63146329
discard_exception.var_type = IS_TMP_VAR;
63156330
discard_exception.var_num = CG(context).fast_call_var;
6331+
discard_exception.try_catch_offset = try_catch_offset;
63166332
zend_stack_push(&CG(loop_var_stack), &discard_exception);
63176333

63186334
CG(zend_lineno) = finally_ast->lineno;

Zend/zend_opcode.c

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

1075+
bool has_goto = false;
10751076
opline = op_array->opcodes;
10761077
end = opline + op_array->last;
10771078
while (opline < end) {
@@ -1105,11 +1106,8 @@ ZEND_API void pass_two(zend_op_array *op_array)
11051106
}
11061107
break;
11071108
case ZEND_GOTO:
1108-
zend_resolve_goto_label(op_array, opline);
1109-
if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1110-
zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1111-
}
1112-
ZEND_FALLTHROUGH;
1109+
has_goto = true;
1110+
break;
11131111
case ZEND_JMP:
11141112
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
11151113
break;
@@ -1188,7 +1186,21 @@ ZEND_API void pass_two(zend_op_array *op_array)
11881186

11891187
zend_calc_live_ranges(op_array, NULL);
11901188

1191-
return;
1189+
if (has_goto) {
1190+
opline = op_array->opcodes;
1191+
end = opline + op_array->last;
1192+
while (opline < end) {
1193+
if (opline->opcode == ZEND_GOTO) {
1194+
zend_resolve_goto_label(op_array, opline);
1195+
if (op_array->fn_flags &ZEND_ACC_HAS_FINALLY_BLOCK) {
1196+
zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1197+
}
1198+
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1199+
ZEND_VM_SET_OPCODE_HANDLER(opline);
1200+
}
1201+
opline++;
1202+
}
1203+
}
11921204
}
11931205

11941206
ZEND_API unary_op_type get_unary_op(int opcode)

0 commit comments

Comments
 (0)