From 7174af40748ff0c2ac77481f5710ee20b41985fa Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 9 Feb 2016 12:40:02 +0100 Subject: [PATCH] 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. --- ext/opcache/Optimizer/block_pass.c | 2 +- ext/opcache/Optimizer/zend_cfg.c | 61 +++++++++++++++++------------- ext/opcache/Optimizer/zend_cfg.h | 2 + ext/opcache/Optimizer/zend_dump.c | 15 ++++++-- 4 files changed, 48 insertions(+), 32 deletions(-) diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 9ee28ab35fd..f0913a8bcaf 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -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; } diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index 6df4d3c6b8c..3e50bb910c4 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -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); diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h index fa96979838c..2a93ffd2c57 100644 --- a/ext/opcache/Optimizer/zend_cfg.h +++ b/ext/opcache/Optimizer/zend_cfg.h @@ -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) ? \ diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 20ba3cf381e..7cacdd01e2d 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -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");