@@ -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 ;
0 commit comments