diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2018-02-16 21:25:49 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2018-02-16 21:30:48 +0100 |
commit | b0af9ac7331e3efa0dcee4f43b2ba8b1e4e52f2f (patch) | |
tree | 5b40edba9eee94ab22fe494ce0c919b3ffdfebe4 | |
parent | 07ad75ca9603b8b0195ddcd90229ae9dbe650900 (diff) | |
download | php-git-b0af9ac7331e3efa0dcee4f43b2ba8b1e4e52f2f.tar.gz |
Avoid live range references in opcodes
Don't store the live range of the freed variable for FREE_ON_RETURN
frees, instead look it up at runtime. As this is an extremely
unlikely codepath (in particular, it requires a loop variable with
a throwing destructor), saving the runtime lookup of the live range
is not worth the extra complexity this adds everywhere else.
-rw-r--r-- | Zend/zend_compile.c | 19 | ||||
-rw-r--r-- | Zend/zend_execute.c | 14 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 23 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 19 | ||||
-rw-r--r-- | Zend/zend_vm_gen.php | 3 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 4 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 24 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_dump.c | 4 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 20 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_opcode.c | 4 |
11 files changed, 41 insertions, 94 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9c8fe3f4d7..1190cef1b4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -629,7 +629,6 @@ static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start if (!zend_stack_is_empty(&CG(loop_var_stack))) { zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack)); zend_loop_var *base = zend_stack_base(&CG(loop_var_stack)); - int check_opcodes = 0; for (; loop_var >= base; loop_var--) { if (loop_var->opcode == ZEND_RETURN) { @@ -639,28 +638,11 @@ static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start loop_var->opcode == ZEND_FE_FREE) { if (loop_var->u.live_range_offset >= n) { loop_var->u.live_range_offset++; - check_opcodes = 1; } else { break; } } } - - /* update previously generated FREE/FE_FREE opcodes */ - if (check_opcodes) { - zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start; - zend_op *end = op_array->opcodes + op_array->last; - - while (opline < end) { - if ((opline->opcode == ZEND_FREE || - opline->opcode == ZEND_FE_FREE) && - (opline->extended_value & ZEND_FREE_ON_RETURN) && - opline->op2.num >= n) { - opline->op2.num++; - } - opline++; - } - } } return n; } @@ -4463,7 +4445,6 @@ static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value opline->op1_type = loop_var->var_type; opline->op1.var = loop_var->var_num; SET_UNUSED(opline->op2); - opline->op2.num = loop_var->u.live_range_offset; opline->extended_value = ZEND_FREE_ON_RETURN; depth--; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 80217b72f3..63073f5957 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2576,6 +2576,20 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o } /* }}} */ +static const zend_live_range *find_live_range(const zend_op_array *op_array, uint32_t op_num, uint32_t var_num) /* {{{ */ +{ + int i; + for (i = 0; i < op_array->last_live_range; i++) { + const zend_live_range *range = &op_array->live_range[i]; + if (op_num >= range->start && op_num < range->end + && var_num == (range->var & ~ZEND_LIVE_MASK)) { + return range; + } + } + return NULL; +} +/* }}} */ + static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { int i; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c105450563..a838dd0668 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2730,7 +2730,7 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR) ZEND_VM_JMP(opline); } -ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE) +ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY) { USE_OPLINE @@ -2739,7 +2739,7 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, LIVE_RANGE) +ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY) { zval *var; USE_OPLINE @@ -7129,16 +7129,15 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; int i, current_try_catch_offset = -1; - { - const zend_op *exc_opline = EG(opline_before_exception); - if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE) - && exc_opline->extended_value & ZEND_FREE_ON_RETURN) { - /* exceptions thrown because of loop var destruction on return/break/... - * are logically thrown at the end of the foreach loop, so adjust the - * throw_op_num. - */ - throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end; - } + if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE) + && throw_op->extended_value & ZEND_FREE_ON_RETURN) { + /* exceptions thrown because of loop var destruction on return/break/... + * are logically thrown at the end of the foreach loop, so adjust the + * throw_op_num. + */ + const zend_live_range *range = find_live_range( + &EX(func)->op_array, throw_op_num, throw_op->op1.var); + throw_op_num = range->end; } /* Find the innermost try/catch/finally the exception was thrown in */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7b2a15d28a..1f6ab1b412 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1726,16 +1726,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER( uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; int i, current_try_catch_offset = -1; - { - const zend_op *exc_opline = EG(opline_before_exception); - if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE) - && exc_opline->extended_value & ZEND_FREE_ON_RETURN) { - /* exceptions thrown because of loop var destruction on return/break/... - * are logically thrown at the end of the foreach loop, so adjust the - * throw_op_num. - */ - throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end; - } + if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE) + && throw_op->extended_value & ZEND_FREE_ON_RETURN) { + /* exceptions thrown because of loop var destruction on return/break/... + * are logically thrown at the end of the foreach loop, so adjust the + * throw_op_num. + */ + const zend_live_range *range = find_live_range( + &EX(func)->op_array, throw_op_num, throw_op->op1.var); + throw_op_num = range->end; } /* Find the innermost try/catch/finally the exception was thrown in */ diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 37a660127f..f9b2280d31 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -64,7 +64,7 @@ $vm_op_flags = array( "ZEND_VM_OP_NUM" => 0x10, "ZEND_VM_OP_JMP_ADDR" => 0x20, "ZEND_VM_OP_TRY_CATCH" => 0x30, - "ZEND_VM_OP_LIVE_RANGE" => 0x40, + // unused 0x40 "ZEND_VM_OP_THIS" => 0x50, "ZEND_VM_OP_NEXT" => 0x60, "ZEND_VM_OP_CLASS_FETCH" => 0x70, @@ -110,7 +110,6 @@ $vm_op_decode = array( "NUM" => ZEND_VM_OP_NUM, "JMP_ADDR" => ZEND_VM_OP_JMP_ADDR, "TRY_CATCH" => ZEND_VM_OP_TRY_CATCH, - "LIVE_RANGE" => ZEND_VM_OP_LIVE_RANGE, "THIS" => ZEND_VM_OP_THIS, "NEXT" => ZEND_VM_OP_NEXT, "CLASS_FETCH" => ZEND_VM_OP_CLASS_FETCH, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 52b7cb855c..f37b769da3 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -295,7 +295,7 @@ static uint32_t zend_vm_opcodes_flags[199] = { 0x00001001, 0x0100a173, 0x01040300, - 0x00004005, + 0x00000005, 0x00186703, 0x00106703, 0x08000007, @@ -352,7 +352,7 @@ static uint32_t zend_vm_opcodes_flags[199] = { 0x0000a103, 0x00002003, 0x03000001, - 0x00004005, + 0x00000005, 0x01000700, 0x00000000, 0x00000000, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 6f9b323bb0..b5c0a483d8 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -42,7 +42,6 @@ #define ZEND_VM_OP_NUM 0x00000010 #define ZEND_VM_OP_JMP_ADDR 0x00000020 #define ZEND_VM_OP_TRY_CATCH 0x00000030 -#define ZEND_VM_OP_LIVE_RANGE 0x00000040 #define ZEND_VM_OP_THIS 0x00000050 #define ZEND_VM_OP_NEXT 0x00000060 #define ZEND_VM_OP_CLASS_FETCH 0x00000070 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 97e44fe0b2..b402cce276 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -347,10 +347,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src->opcode != ZEND_FETCH_DIM_R && src->opcode != ZEND_FETCH_OBJ_R && src->opcode != ZEND_NEW) { - if (opline->extended_value & ZEND_FREE_ON_RETURN) { - /* mark as removed (empty live range) */ - op_array->live_range[opline->op2.num].var = (uint32_t)-1; - } src->result_type = IS_UNUSED; MAKE_NOP(opline); } @@ -1040,10 +1036,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op /* adjust loop jump targets & remove unused live range entries */ if (op_array->last_live_range) { int i, j; - uint32_t *map; - ALLOCA_FLAG(use_heap); - - map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap); for (i = 0, j = 0; i < op_array->last_live_range; i++) { if (op_array->live_range[i].var == (uint32_t)-1) { @@ -1062,7 +1054,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op } op_array->live_range[i].start = start_op; op_array->live_range[i].end = end_op; - map[i] = j; if (i != j) { op_array->live_range[j] = op_array->live_range[i]; } @@ -1071,23 +1062,12 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op } if (i != j) { - if ((op_array->last_live_range = j)) { - zend_op *opline = new_opcodes; - zend_op *end = opline + len; - while (opline != end) { - if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) && - opline->extended_value == ZEND_FREE_ON_RETURN) { - ZEND_ASSERT(opline->op2.num < (uint32_t) i); - opline->op2.num = map[opline->op2.num]; - } - opline++; - } - } else { + op_array->last_live_range = j; + if (j == 0) { efree(op_array->live_range); op_array->live_range = NULL; } } - free_alloca(map, use_heap); } /* adjust early binding list */ diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 7fb55355fd..e4516b59cc 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -118,10 +118,6 @@ static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t fla if (op.num != (uint32_t)-1) { fprintf(stderr, " try-catch(%u)", op.num); } - } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) { - if (opline->extended_value & ZEND_FREE_ON_RETURN) { - fprintf(stderr, " live-range(%u)", op.num); - } } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) { fprintf(stderr, " THIS"); } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) { diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 07860f3998..ffc04854e2 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -566,14 +566,9 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) if (op_array->last_live_range) { int i = 0; int j = 0; - uint32_t *map; - ALLOCA_FLAG(use_heap); - - map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap); do { if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) { - map[i] = j; if (i != j) { op_array->live_range[j] = op_array->live_range[i]; } @@ -582,23 +577,12 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) i++; } while (i < op_array->last_live_range); if (i != j) { - if ((op_array->last_live_range = j)) { - zend_op *opline = op_array->opcodes; - zend_op *end = opline + op_array->last; - - while (opline != end) { - if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) && - opline->extended_value == ZEND_FREE_ON_RETURN) { - opline->op2.num = map[opline->op2.num]; - } - opline++; - } - } else { + op_array->last_live_range = j; + if (j == 0) { efree(op_array->live_range); op_array->live_range = NULL; } } - free_alloca(map, use_heap); } } diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index d066674b90..b14a1c23e2 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -77,10 +77,6 @@ char *phpdbg_decode_input_op( if (op.num != (uint32_t)-1) { spprintf(&result, 0, "try-catch(%" PRIu32 ")", op.num); } - } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) { - if (opline->extended_value & ZEND_FREE_ON_RETURN) { - spprintf(&result, 0, "live-range(%" PRIu32 ")", op.num); - } } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) { result = estrdup("THIS"); } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) { |