mirror of
https://github.com/php/php-src.git
synced 2026-04-04 06:32:49 +02:00
Support CFG construction without live range splitting
We must not split at live range boundaries for SSA constructions, otherwise an OP_DATA instruction may be separated into new block and not picked up during renaming. It's also unnecessary for this use case and only blows up the CFG.
This commit is contained in:
@@ -1749,7 +1749,7 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
|
||||
/* Build CFG */
|
||||
checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg, NULL) != SUCCESS) {
|
||||
if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg, NULL) != SUCCESS) {
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -91,33 +91,35 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
|
||||
do {
|
||||
changed = 0;
|
||||
|
||||
/* Add brk/cont paths */
|
||||
for (j = 0; j < op_array->last_live_range; j++) {
|
||||
if (op_array->live_range[j].var == (uint32_t)-1) {
|
||||
/* this live range already removed */
|
||||
continue;
|
||||
}
|
||||
b = blocks + block_map[op_array->live_range[j].start];
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
while (op_array->opcodes[b->start].opcode == ZEND_NOP && b->start != b->end) {
|
||||
b->start++;
|
||||
}
|
||||
if (op_array->opcodes[b->start].opcode == ZEND_NOP &&
|
||||
b->start == b->end &&
|
||||
b->successors[0] == block_map[op_array->live_range[j].end]) {
|
||||
/* mark as removed (empty live range) */
|
||||
op_array->live_range[j].var = (uint32_t)-1;
|
||||
if (cfg->split_at_live_ranges) {
|
||||
/* Add live range paths */
|
||||
for (j = 0; j < op_array->last_live_range; j++) {
|
||||
if (op_array->live_range[j].var == (uint32_t)-1) {
|
||||
/* this live range already removed */
|
||||
continue;
|
||||
}
|
||||
b->flags |= ZEND_BB_GEN_VAR;
|
||||
b = blocks + block_map[op_array->live_range[j].end];
|
||||
b->flags |= ZEND_BB_KILL_VAR;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, blocks, b);
|
||||
b = blocks + block_map[op_array->live_range[j].start];
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
while (op_array->opcodes[b->start].opcode == ZEND_NOP && b->start != b->end) {
|
||||
b->start++;
|
||||
}
|
||||
if (op_array->opcodes[b->start].opcode == ZEND_NOP &&
|
||||
b->start == b->end &&
|
||||
b->successors[0] == block_map[op_array->live_range[j].end]) {
|
||||
/* mark as removed (empty live range) */
|
||||
op_array->live_range[j].var = (uint32_t)-1;
|
||||
continue;
|
||||
}
|
||||
b->flags |= ZEND_BB_GEN_VAR;
|
||||
b = blocks + block_map[op_array->live_range[j].end];
|
||||
b->flags |= ZEND_BB_KILL_VAR;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, blocks, b);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->live_range[j].end]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->live_range[j].end]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,6 +246,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
|
||||
zend_basic_block *blocks;
|
||||
zval *zv;
|
||||
|
||||
cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
|
||||
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
|
||||
if (!block_map) {
|
||||
return FAILURE;
|
||||
@@ -391,10 +394,14 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < op_array->last_live_range; j++) {
|
||||
BB_START(op_array->live_range[j].start);
|
||||
BB_START(op_array->live_range[j].end);
|
||||
|
||||
if (cfg->split_at_live_ranges) {
|
||||
for (j = 0; j < op_array->last_live_range; j++) {
|
||||
BB_START(op_array->live_range[j].start);
|
||||
BB_START(op_array->live_range[j].end);
|
||||
}
|
||||
}
|
||||
|
||||
if (op_array->last_try_catch) {
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
BB_START(op_array->try_catch_array[j].try_op);
|
||||
|
||||
@@ -86,6 +86,7 @@ typedef struct _zend_cfg {
|
||||
zend_basic_block *blocks; /* array of basic blocks */
|
||||
int *predecessors;
|
||||
uint32_t *map;
|
||||
unsigned int split_at_live_ranges : 1;
|
||||
} zend_cfg;
|
||||
|
||||
/* Build Flags */
|
||||
@@ -94,6 +95,7 @@ typedef struct _zend_cfg {
|
||||
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
|
||||
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
|
||||
#define ZEND_SSA_RC_INFERENCE (1<<27)
|
||||
#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
|
||||
|
||||
#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
|
||||
((rt_constants) ? \
|
||||
|
||||
@@ -957,10 +957,17 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons
|
||||
if (op_array->last_live_range) {
|
||||
fprintf(stderr, "LIVE RANGES:\n");
|
||||
for (i = 0; i < op_array->last_live_range; i++) {
|
||||
fprintf(stderr, " %u: BB%u - BB%u ",
|
||||
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
||||
cfg->map[op_array->live_range[i].start],
|
||||
cfg->map[op_array->live_range[i].end]);
|
||||
if (cfg->split_at_live_ranges) {
|
||||
fprintf(stderr, " %u: BB%u - BB%u ",
|
||||
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
||||
cfg->map[op_array->live_range[i].start],
|
||||
cfg->map[op_array->live_range[i].end]);
|
||||
} else {
|
||||
fprintf(stderr, " %u: L%u - L%u ",
|
||||
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
||||
op_array->live_range[i].start,
|
||||
op_array->live_range[i].end);
|
||||
}
|
||||
switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
|
||||
case ZEND_LIVE_TMPVAR:
|
||||
fprintf(stderr, "(tmp/var)\n");
|
||||
|
||||
Reference in New Issue
Block a user