Skip to content

Commit b0af9ac

Browse files
committed
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.
1 parent 07ad75c commit b0af9ac

File tree

11 files changed

+41
-94
lines changed

11 files changed

+41
-94
lines changed

Zend/zend_compile.c

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,6 @@ static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start
629629
if (!zend_stack_is_empty(&CG(loop_var_stack))) {
630630
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
631631
zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
632-
int check_opcodes = 0;
633632

634633
for (; loop_var >= base; loop_var--) {
635634
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
639638
loop_var->opcode == ZEND_FE_FREE) {
640639
if (loop_var->u.live_range_offset >= n) {
641640
loop_var->u.live_range_offset++;
642-
check_opcodes = 1;
643641
} else {
644642
break;
645643
}
646644
}
647645
}
648-
649-
/* update previously generated FREE/FE_FREE opcodes */
650-
if (check_opcodes) {
651-
zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start;
652-
zend_op *end = op_array->opcodes + op_array->last;
653-
654-
while (opline < end) {
655-
if ((opline->opcode == ZEND_FREE ||
656-
opline->opcode == ZEND_FE_FREE) &&
657-
(opline->extended_value & ZEND_FREE_ON_RETURN) &&
658-
opline->op2.num >= n) {
659-
opline->op2.num++;
660-
}
661-
opline++;
662-
}
663-
}
664646
}
665647
return n;
666648
}
@@ -4463,7 +4445,6 @@ static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value
44634445
opline->op1_type = loop_var->var_type;
44644446
opline->op1.var = loop_var->var_num;
44654447
SET_UNUSED(opline->op2);
4466-
opline->op2.num = loop_var->u.live_range_offset;
44674448
opline->extended_value = ZEND_FREE_ON_RETURN;
44684449
depth--;
44694450
}

Zend/zend_execute.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,6 +2576,20 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
25762576
}
25772577
/* }}} */
25782578

2579+
static const zend_live_range *find_live_range(const zend_op_array *op_array, uint32_t op_num, uint32_t var_num) /* {{{ */
2580+
{
2581+
int i;
2582+
for (i = 0; i < op_array->last_live_range; i++) {
2583+
const zend_live_range *range = &op_array->live_range[i];
2584+
if (op_num >= range->start && op_num < range->end
2585+
&& var_num == (range->var & ~ZEND_LIVE_MASK)) {
2586+
return range;
2587+
}
2588+
}
2589+
return NULL;
2590+
}
2591+
/* }}} */
2592+
25792593
static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
25802594
{
25812595
int i;

Zend/zend_vm_def.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,7 +2730,7 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
27302730
ZEND_VM_JMP(opline);
27312731
}
27322732

2733-
ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE)
2733+
ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
27342734
{
27352735
USE_OPLINE
27362736

@@ -2739,7 +2739,7 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE)
27392739
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
27402740
}
27412741

2742-
ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, LIVE_RANGE)
2742+
ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
27432743
{
27442744
zval *var;
27452745
USE_OPLINE
@@ -7129,16 +7129,15 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
71297129
uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes;
71307130
int i, current_try_catch_offset = -1;
71317131

7132-
{
7133-
const zend_op *exc_opline = EG(opline_before_exception);
7134-
if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE)
7135-
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
7136-
/* exceptions thrown because of loop var destruction on return/break/...
7137-
* are logically thrown at the end of the foreach loop, so adjust the
7138-
* throw_op_num.
7139-
*/
7140-
throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
7141-
}
7132+
if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE)
7133+
&& throw_op->extended_value & ZEND_FREE_ON_RETURN) {
7134+
/* exceptions thrown because of loop var destruction on return/break/...
7135+
* are logically thrown at the end of the foreach loop, so adjust the
7136+
* throw_op_num.
7137+
*/
7138+
const zend_live_range *range = find_live_range(
7139+
&EX(func)->op_array, throw_op_num, throw_op->op1.var);
7140+
throw_op_num = range->end;
71427141
}
71437142

71447143
/* Find the innermost try/catch/finally the exception was thrown in */

Zend/zend_vm_execute.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,16 +1726,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
17261726
uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes;
17271727
int i, current_try_catch_offset = -1;
17281728

1729-
{
1730-
const zend_op *exc_opline = EG(opline_before_exception);
1731-
if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE)
1732-
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
1733-
/* exceptions thrown because of loop var destruction on return/break/...
1734-
* are logically thrown at the end of the foreach loop, so adjust the
1735-
* throw_op_num.
1736-
*/
1737-
throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
1738-
}
1729+
if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE)
1730+
&& throw_op->extended_value & ZEND_FREE_ON_RETURN) {
1731+
/* exceptions thrown because of loop var destruction on return/break/...
1732+
* are logically thrown at the end of the foreach loop, so adjust the
1733+
* throw_op_num.
1734+
*/
1735+
const zend_live_range *range = find_live_range(
1736+
&EX(func)->op_array, throw_op_num, throw_op->op1.var);
1737+
throw_op_num = range->end;
17391738
}
17401739

17411740
/* Find the innermost try/catch/finally the exception was thrown in */

Zend/zend_vm_gen.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"ZEND_VM_OP_NUM" => 0x10,
6565
"ZEND_VM_OP_JMP_ADDR" => 0x20,
6666
"ZEND_VM_OP_TRY_CATCH" => 0x30,
67-
"ZEND_VM_OP_LIVE_RANGE" => 0x40,
67+
// unused 0x40
6868
"ZEND_VM_OP_THIS" => 0x50,
6969
"ZEND_VM_OP_NEXT" => 0x60,
7070
"ZEND_VM_OP_CLASS_FETCH" => 0x70,
@@ -110,7 +110,6 @@
110110
"NUM" => ZEND_VM_OP_NUM,
111111
"JMP_ADDR" => ZEND_VM_OP_JMP_ADDR,
112112
"TRY_CATCH" => ZEND_VM_OP_TRY_CATCH,
113-
"LIVE_RANGE" => ZEND_VM_OP_LIVE_RANGE,
114113
"THIS" => ZEND_VM_OP_THIS,
115114
"NEXT" => ZEND_VM_OP_NEXT,
116115
"CLASS_FETCH" => ZEND_VM_OP_CLASS_FETCH,

Zend/zend_vm_opcodes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ static uint32_t zend_vm_opcodes_flags[199] = {
295295
0x00001001,
296296
0x0100a173,
297297
0x01040300,
298-
0x00004005,
298+
0x00000005,
299299
0x00186703,
300300
0x00106703,
301301
0x08000007,
@@ -352,7 +352,7 @@ static uint32_t zend_vm_opcodes_flags[199] = {
352352
0x0000a103,
353353
0x00002003,
354354
0x03000001,
355-
0x00004005,
355+
0x00000005,
356356
0x01000700,
357357
0x00000000,
358358
0x00000000,

Zend/zend_vm_opcodes.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
#define ZEND_VM_OP_NUM 0x00000010
4343
#define ZEND_VM_OP_JMP_ADDR 0x00000020
4444
#define ZEND_VM_OP_TRY_CATCH 0x00000030
45-
#define ZEND_VM_OP_LIVE_RANGE 0x00000040
4645
#define ZEND_VM_OP_THIS 0x00000050
4746
#define ZEND_VM_OP_NEXT 0x00000060
4847
#define ZEND_VM_OP_CLASS_FETCH 0x00000070

ext/opcache/Optimizer/block_pass.c

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -347,10 +347,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
347347
src->opcode != ZEND_FETCH_DIM_R &&
348348
src->opcode != ZEND_FETCH_OBJ_R &&
349349
src->opcode != ZEND_NEW) {
350-
if (opline->extended_value & ZEND_FREE_ON_RETURN) {
351-
/* mark as removed (empty live range) */
352-
op_array->live_range[opline->op2.num].var = (uint32_t)-1;
353-
}
354350
src->result_type = IS_UNUSED;
355351
MAKE_NOP(opline);
356352
}
@@ -1040,10 +1036,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
10401036
/* adjust loop jump targets & remove unused live range entries */
10411037
if (op_array->last_live_range) {
10421038
int i, j;
1043-
uint32_t *map;
1044-
ALLOCA_FLAG(use_heap);
1045-
1046-
map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
10471039

10481040
for (i = 0, j = 0; i < op_array->last_live_range; i++) {
10491041
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
10621054
}
10631055
op_array->live_range[i].start = start_op;
10641056
op_array->live_range[i].end = end_op;
1065-
map[i] = j;
10661057
if (i != j) {
10671058
op_array->live_range[j] = op_array->live_range[i];
10681059
}
@@ -1071,23 +1062,12 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
10711062
}
10721063

10731064
if (i != j) {
1074-
if ((op_array->last_live_range = j)) {
1075-
zend_op *opline = new_opcodes;
1076-
zend_op *end = opline + len;
1077-
while (opline != end) {
1078-
if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
1079-
opline->extended_value == ZEND_FREE_ON_RETURN) {
1080-
ZEND_ASSERT(opline->op2.num < (uint32_t) i);
1081-
opline->op2.num = map[opline->op2.num];
1082-
}
1083-
opline++;
1084-
}
1085-
} else {
1065+
op_array->last_live_range = j;
1066+
if (j == 0) {
10861067
efree(op_array->live_range);
10871068
op_array->live_range = NULL;
10881069
}
10891070
}
1090-
free_alloca(map, use_heap);
10911071
}
10921072

10931073
/* adjust early binding list */

ext/opcache/Optimizer/zend_dump.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,6 @@ static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t fla
118118
if (op.num != (uint32_t)-1) {
119119
fprintf(stderr, " try-catch(%u)", op.num);
120120
}
121-
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
122-
if (opline->extended_value & ZEND_FREE_ON_RETURN) {
123-
fprintf(stderr, " live-range(%u)", op.num);
124-
}
125121
} else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
126122
fprintf(stderr, " THIS");
127123
} else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {

ext/opcache/Optimizer/zend_optimizer.c

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -566,14 +566,9 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
566566
if (op_array->last_live_range) {
567567
int i = 0;
568568
int j = 0;
569-
uint32_t *map;
570-
ALLOCA_FLAG(use_heap);
571-
572-
map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
573569

574570
do {
575571
if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
576-
map[i] = j;
577572
if (i != j) {
578573
op_array->live_range[j] = op_array->live_range[i];
579574
}
@@ -582,23 +577,12 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
582577
i++;
583578
} while (i < op_array->last_live_range);
584579
if (i != j) {
585-
if ((op_array->last_live_range = j)) {
586-
zend_op *opline = op_array->opcodes;
587-
zend_op *end = opline + op_array->last;
588-
589-
while (opline != end) {
590-
if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
591-
opline->extended_value == ZEND_FREE_ON_RETURN) {
592-
opline->op2.num = map[opline->op2.num];
593-
}
594-
opline++;
595-
}
596-
} else {
580+
op_array->last_live_range = j;
581+
if (j == 0) {
597582
efree(op_array->live_range);
598583
op_array->live_range = NULL;
599584
}
600585
}
601-
free_alloca(map, use_heap);
602586
}
603587
}
604588

0 commit comments

Comments
 (0)