@@ -5172,11 +5172,15 @@ static bool zend_handle_loops_and_finally_ex(zend_long depth, uint32_t excluded_
5172
5172
SET_NODE (opline -> op2 , return_value );
5173
5173
}
5174
5174
opline -> op1 .num = loop_var -> try_catch_offset ;
5175
+ /* Only used in pass_two(). */
5176
+ opline -> extended_value = loop_var -> try_catch_offset ;
5175
5177
} else if (loop_var -> opcode == ZEND_DISCARD_EXCEPTION ) {
5176
5178
zend_op * opline = get_next_op ();
5177
5179
opline -> opcode = ZEND_DISCARD_EXCEPTION ;
5178
5180
opline -> op1_type = IS_TMP_VAR ;
5179
5181
opline -> op1 .var = loop_var -> var_num ;
5182
+ /* Only used in pass_two(). */
5183
+ opline -> extended_value = loop_var -> try_catch_offset ;
5180
5184
} else {
5181
5185
ZEND_ASSERT (loop_var -> opcode == ZEND_FREE || loop_var -> opcode == ZEND_FE_FREE );
5182
5186
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) /* {{{ */
5399
5403
zend_label * dest ;
5400
5404
int current , remove_oplines = opline -> op1 .num ;
5401
5405
zval * label ;
5402
- uint32_t opnum = opline - op_array -> opcodes ;
5403
5406
5404
- label = CT_CONSTANT_EX ( op_array , opline -> op2 . constant );
5407
+ label = RT_CONSTANT ( opline , opline -> op2 );
5405
5408
if (CG (context ).labels == NULL ||
5406
5409
(dest = zend_hash_find_ptr (CG (context ).labels , Z_STR_P (label ))) == NULL
5407
5410
) {
@@ -5422,21 +5425,6 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
5422
5425
CG (zend_lineno ) = opline -> lineno ;
5423
5426
zend_error_noreturn (E_COMPILE_ERROR , "'goto' into loop or switch statement is disallowed" );
5424
5427
}
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
- }
5440
5428
}
5441
5429
5442
5430
opline -> opcode = ZEND_JMP ;
@@ -5446,16 +5434,43 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
5446
5434
opline -> op1 .opline_num = dest -> opline_num ;
5447
5435
opline -> extended_value = 0 ;
5448
5436
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
+ }
5459
5474
}
5460
5475
/* }}} */
5461
5476
@@ -6313,6 +6328,7 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
6313
6328
discard_exception .opcode = ZEND_DISCARD_EXCEPTION ;
6314
6329
discard_exception .var_type = IS_TMP_VAR ;
6315
6330
discard_exception .var_num = CG (context ).fast_call_var ;
6331
+ discard_exception .try_catch_offset = try_catch_offset ;
6316
6332
zend_stack_push (& CG (loop_var_stack ), & discard_exception );
6317
6333
6318
6334
CG (zend_lineno ) = finally_ast -> lineno ;
0 commit comments