mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Improved SCDF<->SCCP interface
- "get_feasible_successors" callback is changed into "mark_feasible_successors" and should mark necessary edges through scdf_mark_edge_feasible() - SCDF takes care about OP_DATA instruction - SCDF code is re-arranged to avoid repeatable checks
This commit is contained in:
parent
e0ad5dd489
commit
db0cd64dfa
3 changed files with 83 additions and 97 deletions
|
@ -601,15 +601,10 @@ static inline int ct_eval_func_call(
|
||||||
|
|
||||||
#define SKIP_IF_TOP(op) if (IS_TOP(op)) break;
|
#define SKIP_IF_TOP(op) if (IS_TOP(op)) break;
|
||||||
|
|
||||||
static void sccp_visit_instr(scdf_ctx *scdf, void *void_ctx, zend_op *opline, zend_ssa_op *ssa_op) {
|
static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
|
||||||
sccp_ctx *ctx = (sccp_ctx *) void_ctx;
|
sccp_ctx *ctx = (sccp_ctx *) scdf->ctx;
|
||||||
zval *op1, *op2, zv; /* zv is a temporary to hold result values */
|
zval *op1, *op2, zv; /* zv is a temporary to hold result values */
|
||||||
|
|
||||||
if (opline->opcode == ZEND_OP_DATA) {
|
|
||||||
opline--;
|
|
||||||
ssa_op--;
|
|
||||||
}
|
|
||||||
|
|
||||||
op1 = get_op1_value(ctx, opline, ssa_op);
|
op1 = get_op1_value(ctx, opline, ssa_op);
|
||||||
op2 = get_op2_value(ctx, opline, ssa_op);
|
op2 = get_op2_value(ctx, opline, ssa_op);
|
||||||
|
|
||||||
|
@ -1032,11 +1027,13 @@ static void sccp_visit_instr(scdf_ctx *scdf, void *void_ctx, zend_op *opline, ze
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns whether there is a successor */
|
/* Returns whether there is a successor */
|
||||||
static zend_bool sccp_get_feasible_successors(
|
static void sccp_mark_feasible_successors(
|
||||||
scdf_ctx *scdf, void *void_ctx, zend_basic_block *block,
|
scdf_ctx *scdf,
|
||||||
zend_op *opline, zend_ssa_op *ssa_op, zend_bool *suc) {
|
int block_num, zend_basic_block *block,
|
||||||
sccp_ctx *ctx = (sccp_ctx *) void_ctx;
|
zend_op *opline, zend_ssa_op *ssa_op) {
|
||||||
|
sccp_ctx *ctx = (sccp_ctx *) scdf->ctx;
|
||||||
zval *op1;
|
zval *op1;
|
||||||
|
int s;
|
||||||
|
|
||||||
/* We can't determine the branch target at compile-time for these */
|
/* We can't determine the branch target at compile-time for these */
|
||||||
switch (opline->opcode) {
|
switch (opline->opcode) {
|
||||||
|
@ -1046,51 +1043,56 @@ static zend_bool sccp_get_feasible_successors(
|
||||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||||
case ZEND_FE_FETCH_R:
|
case ZEND_FE_FETCH_R:
|
||||||
case ZEND_FE_FETCH_RW:
|
case ZEND_FE_FETCH_RW:
|
||||||
suc[0] = 1;
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
|
||||||
suc[1] = 1;
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
op1 = get_op1_value(ctx, opline, ssa_op);
|
op1 = get_op1_value(ctx, opline, ssa_op);
|
||||||
|
|
||||||
/* Branch target not yet known */
|
/* Branch target can be either one */
|
||||||
if (IS_TOP(op1)) {
|
if (!op1 || IS_BOT(op1)) {
|
||||||
return 0;
|
for (s = 0; s < block->successors_count; s++) {
|
||||||
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Branch target can be either one */
|
/* Branch target not yet known */
|
||||||
if (IS_BOT(op1)) {
|
if (IS_TOP(op1)) {
|
||||||
suc[0] = 1;
|
return;
|
||||||
suc[1] = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (opline->opcode) {
|
switch (opline->opcode) {
|
||||||
case ZEND_JMPZ:
|
case ZEND_JMPZ:
|
||||||
case ZEND_JMPZNZ:
|
case ZEND_JMPZNZ:
|
||||||
case ZEND_JMPZ_EX:
|
case ZEND_JMPZ_EX:
|
||||||
suc[zend_is_true(op1)] = 1;
|
s = zend_is_true(op1);
|
||||||
break;
|
break;
|
||||||
case ZEND_JMPNZ:
|
case ZEND_JMPNZ:
|
||||||
case ZEND_JMPNZ_EX:
|
case ZEND_JMPNZ_EX:
|
||||||
case ZEND_JMP_SET:
|
case ZEND_JMP_SET:
|
||||||
suc[!zend_is_true(op1)] = 1;
|
s = !zend_is_true(op1);
|
||||||
break;
|
break;
|
||||||
case ZEND_COALESCE:
|
case ZEND_COALESCE:
|
||||||
suc[Z_TYPE_P(op1) == IS_NULL] = 1;
|
s = (Z_TYPE_P(op1) == IS_NULL);
|
||||||
break;
|
break;
|
||||||
case ZEND_FE_RESET_R:
|
case ZEND_FE_RESET_R:
|
||||||
case ZEND_FE_RESET_RW:
|
case ZEND_FE_RESET_RW:
|
||||||
if (Z_TYPE_P(op1) == IS_ARRAY) {
|
if (Z_TYPE_P(op1) != IS_ARRAY) {
|
||||||
suc[zend_hash_num_elements(Z_ARR_P(op1)) != 0] = 1;
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
|
||||||
} else {
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
|
||||||
suc[0] = 1;
|
return;
|
||||||
suc[1] = 1;
|
|
||||||
}
|
}
|
||||||
|
s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
|
||||||
break;
|
break;
|
||||||
EMPTY_SWITCH_DEFAULT_CASE()
|
default:
|
||||||
|
for (s = 0; s < block->successors_count; s++) {
|
||||||
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return 1;
|
scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void join_phi_values(zval *a, zval *b) {
|
static void join_phi_values(zval *a, zval *b) {
|
||||||
|
@ -1108,8 +1110,8 @@ static void join_phi_values(zval *a, zval *b) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sccp_visit_phi(scdf_ctx *scdf, void *void_ctx, zend_ssa_phi *phi) {
|
static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
|
||||||
sccp_ctx *ctx = (sccp_ctx *) void_ctx;
|
sccp_ctx *ctx = (sccp_ctx *) scdf->ctx;
|
||||||
zend_ssa *ssa = ctx->ssa;
|
zend_ssa *ssa = ctx->ssa;
|
||||||
ZEND_ASSERT(phi->ssa_var >= 0);
|
ZEND_ASSERT(phi->ssa_var >= 0);
|
||||||
if (!IS_BOT(&ctx->values[phi->ssa_var])) {
|
if (!IS_BOT(&ctx->values[phi->ssa_var])) {
|
||||||
|
@ -1372,7 +1374,7 @@ void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_in
|
||||||
|
|
||||||
scdf.handlers.visit_instr = sccp_visit_instr;
|
scdf.handlers.visit_instr = sccp_visit_instr;
|
||||||
scdf.handlers.visit_phi = sccp_visit_phi;
|
scdf.handlers.visit_phi = sccp_visit_phi;
|
||||||
scdf.handlers.get_feasible_successors = sccp_get_feasible_successors;
|
scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
|
||||||
|
|
||||||
scdf_init(&scdf, op_array, ssa, &ctx);
|
scdf_init(&scdf, op_array, ssa, &ctx);
|
||||||
scdf_solve(&scdf, "SCCP");
|
scdf_solve(&scdf, "SCCP");
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
#define DEBUG_PRINT(...)
|
#define DEBUG_PRINT(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void mark_edge_feasible(scdf_ctx *ctx, int from, int to) {
|
void scdf_mark_edge_feasible(scdf_ctx *ctx, int from, int to) {
|
||||||
uint32_t edge = scdf_edge(&ctx->ssa->cfg, from, to);
|
uint32_t edge = scdf_edge(&ctx->ssa->cfg, from, to);
|
||||||
|
|
||||||
if (zend_bitset_in(ctx->feasible_edges, edge)) {
|
if (zend_bitset_in(ctx->feasible_edges, edge)) {
|
||||||
|
@ -75,50 +75,7 @@ static void mark_edge_feasible(scdf_ctx *ctx, int from, int to) {
|
||||||
zend_ssa_phi *phi;
|
zend_ssa_phi *phi;
|
||||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||||
zend_bitset_excl(ctx->phi_var_worklist, phi->ssa_var);
|
zend_bitset_excl(ctx->phi_var_worklist, phi->ssa_var);
|
||||||
ctx->handlers.visit_phi(ctx, ctx->ctx, phi);
|
ctx->handlers.visit_phi(ctx, phi);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns whether there is a successor */
|
|
||||||
static inline zend_bool get_feasible_successors(
|
|
||||||
scdf_ctx *ctx, zend_basic_block *block,
|
|
||||||
zend_op *opline, zend_ssa_op *ssa_op, zend_bool *suc) {
|
|
||||||
/* Terminal block without successors */
|
|
||||||
if (block->successors_count == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unconditional jump */
|
|
||||||
if (block->successors_count == 1) {
|
|
||||||
suc[0] = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx->handlers.get_feasible_successors(ctx, ctx->ctx, block, opline, ssa_op, suc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_instr(scdf_ctx *ctx, int block_num, zend_op *opline, zend_ssa_op *ssa_op) {
|
|
||||||
zend_basic_block *block = &ctx->ssa->cfg.blocks[block_num];
|
|
||||||
ctx->handlers.visit_instr(ctx, ctx->ctx, opline, ssa_op);
|
|
||||||
|
|
||||||
if (opline - ctx->op_array->opcodes == block->start + block->len - 1) {
|
|
||||||
if (opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING) {
|
|
||||||
// TODO For now consider all edges feasible
|
|
||||||
int s;
|
|
||||||
for (s = 0; s < block->successors_count; s++) {
|
|
||||||
mark_edge_feasible(ctx, block_num, block->successors[s]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zend_bool suc[2] = {0};
|
|
||||||
if (get_feasible_successors(ctx, block, opline, ssa_op, suc)) {
|
|
||||||
if (suc[0]) {
|
|
||||||
mark_edge_feasible(ctx, block_num, block->successors[0]);
|
|
||||||
}
|
|
||||||
if (suc[1]) {
|
|
||||||
mark_edge_feasible(ctx, block_num, block->successors[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,14 +125,28 @@ void scdf_solve(scdf_ctx *ctx, const char *name) {
|
||||||
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
||||||
ZEND_ASSERT(phi);
|
ZEND_ASSERT(phi);
|
||||||
if (zend_bitset_in(ctx->executable_blocks, phi->block)) {
|
if (zend_bitset_in(ctx->executable_blocks, phi->block)) {
|
||||||
ctx->handlers.visit_phi(ctx, ctx->ctx, phi);
|
ctx->handlers.visit_phi(ctx, phi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((i = zend_bitset_pop_first(ctx->instr_worklist, ctx->instr_worklist_len)) >= 0) {
|
while ((i = zend_bitset_pop_first(ctx->instr_worklist, ctx->instr_worklist_len)) >= 0) {
|
||||||
int block_num = ssa->cfg.map[i];
|
int block_num = ssa->cfg.map[i];
|
||||||
if (zend_bitset_in(ctx->executable_blocks, block_num)) {
|
if (zend_bitset_in(ctx->executable_blocks, block_num)) {
|
||||||
handle_instr(ctx, block_num, &ctx->op_array->opcodes[i], &ssa->ops[i]);
|
zend_basic_block *block = &ssa->cfg.blocks[block_num];
|
||||||
|
zend_op *opline = &ctx->op_array->opcodes[i];
|
||||||
|
zend_ssa_op *ssa_op = &ssa->ops[i];
|
||||||
|
if (opline->opcode == ZEND_OP_DATA) {
|
||||||
|
opline--;
|
||||||
|
ssa_op--;
|
||||||
|
}
|
||||||
|
ctx->handlers.visit_instr(ctx, opline, ssa_op);
|
||||||
|
if (i == block->start + block->len - 1) {
|
||||||
|
if (block->successors_count == 1) {
|
||||||
|
scdf_mark_edge_feasible(ctx, block_num, block->successors[0]);
|
||||||
|
} else if (block->successors_count > 1) {
|
||||||
|
ctx->handlers.mark_feasible_successors(ctx, block_num, block, opline, ssa_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,21 +162,32 @@ void scdf_solve(scdf_ctx *ctx, const char *name) {
|
||||||
zend_ssa_phi *phi;
|
zend_ssa_phi *phi;
|
||||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||||
zend_bitset_excl(ctx->phi_var_worklist, phi->ssa_var);
|
zend_bitset_excl(ctx->phi_var_worklist, phi->ssa_var);
|
||||||
ctx->handlers.visit_phi(ctx, ctx->ctx, phi);
|
ctx->handlers.visit_phi(ctx, phi);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int j, end = block->start + block->len;
|
|
||||||
for (j = block->start; j < end; j++) {
|
|
||||||
zend_bitset_excl(ctx->instr_worklist, j);
|
|
||||||
handle_instr(ctx, i, &ctx->op_array->opcodes[j], &ssa->ops[j]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block->len == 0) {
|
if (block->len == 0) {
|
||||||
/* Zero length blocks don't have a last instruction that would normally do this */
|
/* Zero length blocks don't have a last instruction that would normally do this */
|
||||||
mark_edge_feasible(ctx, i, block->successors[0]);
|
scdf_mark_edge_feasible(ctx, i, block->successors[0]);
|
||||||
|
} else {
|
||||||
|
zend_op *opline;
|
||||||
|
int j, end = block->start + block->len;
|
||||||
|
for (j = block->start; j < end; j++) {
|
||||||
|
opline = &ctx->op_array->opcodes[j];
|
||||||
|
zend_bitset_excl(ctx->instr_worklist, j);
|
||||||
|
if (opline->opcode != ZEND_OP_DATA) {
|
||||||
|
ctx->handlers.visit_instr(ctx, opline, &ssa->ops[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block->successors_count == 1) {
|
||||||
|
scdf_mark_edge_feasible(ctx, i, block->successors[0]);
|
||||||
|
} else if (block->successors_count > 1) {
|
||||||
|
if (opline->opcode == ZEND_OP_DATA) {
|
||||||
|
opline--;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
ctx->handlers.mark_feasible_successors(ctx, i, block, opline, &ssa->ops[j-1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,12 +38,12 @@ typedef struct _scdf_ctx {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
void (*visit_instr)(
|
void (*visit_instr)(
|
||||||
struct _scdf_ctx *scdf, void *ctx, zend_op *opline, zend_ssa_op *ssa_op);
|
struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
|
||||||
void (*visit_phi)(
|
void (*visit_phi)(
|
||||||
struct _scdf_ctx *scdf, void *ctx, zend_ssa_phi *phi);
|
struct _scdf_ctx *scdf, zend_ssa_phi *phi);
|
||||||
zend_bool (*get_feasible_successors)(
|
void (*mark_feasible_successors)(
|
||||||
struct _scdf_ctx *scdf, void *ctx, zend_basic_block *block,
|
struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
|
||||||
zend_op *opline, zend_ssa_op *ssa_op, zend_bool *suc);
|
zend_op *opline, zend_ssa_op *ssa_op);
|
||||||
} handlers;
|
} handlers;
|
||||||
} scdf_ctx;
|
} scdf_ctx;
|
||||||
|
|
||||||
|
@ -96,4 +96,6 @@ static inline zend_bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to)
|
||||||
return zend_bitset_in(scdf->feasible_edges, edge);
|
return zend_bitset_in(scdf->feasible_edges, edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scdf_mark_edge_feasible(scdf_ctx *ctx, int from, int to);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue