@@ -5200,11 +5200,15 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, uint32_t excluded_
5200
5200
SET_NODE (opline -> op2 , return_value );
5201
5201
}
5202
5202
opline -> op1 .num = loop_var -> try_catch_offset ;
5203
+ /* Only used in pass_two(). */
5204
+ opline -> extended_value = loop_var -> try_catch_offset ;
5203
5205
} else if (loop_var -> opcode == ZEND_DISCARD_EXCEPTION ) {
5204
5206
zend_op * opline = get_next_op ();
5205
5207
opline -> opcode = ZEND_DISCARD_EXCEPTION ;
5206
5208
opline -> op1_type = IS_TMP_VAR ;
5207
5209
opline -> op1 .var = loop_var -> var_num ;
5210
+ /* Only used in pass_two(). */
5211
+ opline -> extended_value = loop_var -> try_catch_offset ;
5208
5212
} else {
5209
5213
ZEND_ASSERT (loop_var -> opcode == ZEND_FREE || loop_var -> opcode == ZEND_FE_FREE );
5210
5214
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) /* {{{ */
5427
5431
zend_label * dest ;
5428
5432
int current , remove_oplines = opline -> op1 .num ;
5429
5433
zval * label ;
5430
- uint32_t opnum = opline - op_array -> opcodes ;
5431
5434
5432
- label = CT_CONSTANT_EX ( op_array , opline -> op2 . constant );
5435
+ label = RT_CONSTANT ( opline , opline -> op2 );
5433
5436
if (CG (context ).labels == NULL ||
5434
5437
(dest = zend_hash_find_ptr (CG (context ).labels , Z_STR_P (label ))) == NULL
5435
5438
) {
@@ -5450,21 +5453,6 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
5450
5453
CG (zend_lineno ) = opline -> lineno ;
5451
5454
zend_error_noreturn (E_COMPILE_ERROR , "'goto' into loop or switch statement is disallowed" );
5452
5455
}
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
- }
5468
5456
}
5469
5457
5470
5458
opline -> opcode = ZEND_JMP ;
@@ -5474,16 +5462,43 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
5474
5462
opline -> op1 .opline_num = dest -> opline_num ;
5475
5463
opline -> extended_value = 0 ;
5476
5464
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
+ }
5487
5502
}
5488
5503
/* }}} */
5489
5504
@@ -6341,6 +6356,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
6341
6356
discard_exception .opcode = ZEND_DISCARD_EXCEPTION ;
6342
6357
discard_exception .var_type = IS_TMP_VAR ;
6343
6358
discard_exception .var_num = CG (context ).fast_call_var ;
6359
+ discard_exception .try_catch_offset = try_catch_offset ;
6344
6360
zend_stack_push (& CG (loop_var_stack ), & discard_exception );
6345
6361
6346
6362
CG (zend_lineno ) = finally_ast -> lineno ;
0 commit comments