mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
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.
This commit is contained in:
@@ -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--;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user