Combined constants substitutaion and dead instruction removing in single pass. This eleminates substitution in dead instructions.

This commit is contained in:
Dmitry Stogov 2017-07-06 12:35:13 +03:00
parent f890375c16
commit cffee2f7e5

View file

@ -1186,10 +1186,13 @@ static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
* if they have a certain type. */ * if they have a certain type. */
static void replace_constant_operands(sccp_ctx *ctx) { static void replace_constant_operands(sccp_ctx *ctx) {
zend_ssa *ssa = ctx->ssa; zend_ssa *ssa = ctx->ssa;
zend_op_array *op_array = ctx->op_array;
int i; int i;
zval tmp; zval tmp;
for (i = 0; i < ssa->vars_count; i++) { /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
* and INIT_ARRAY. */
for (i = ssa->vars_count - 1; i >= 0; i--) {
zend_ssa_var *var = &ssa->vars[i]; zend_ssa_var *var = &ssa->vars[i];
zval *value; zval *value;
int use; int use;
@ -1204,7 +1207,7 @@ static void replace_constant_operands(sccp_ctx *ctx) {
} }
FOREACH_USE(var, use) { FOREACH_USE(var, use) {
zend_op *opline = &ctx->op_array->opcodes[use]; zend_op *opline = &op_array->opcodes[use];
zend_ssa_op *ssa_op = &ssa->ops[use]; zend_ssa_op *ssa_op = &ssa->ops[use];
if (try_replace_op1(ctx, opline, ssa_op, i, value)) { if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
ZEND_ASSERT(ssa_op->op1_def == -1); ZEND_ASSERT(ssa_op->op1_def == -1);
@ -1223,10 +1226,8 @@ static void replace_constant_operands(sccp_ctx *ctx) {
ssa_op->op2_use_chain = -1; ssa_op->op2_use_chain = -1;
} }
} FOREACH_USE_END(); } FOREACH_USE_END();
}
}
/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result /* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
* value(s) were determined by SCCP. It removes dead computational instructions and converts * value(s) were determined by SCCP. It removes dead computational instructions and converts
* CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons: * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
* a) During operand replacement we eliminate FREEs. The corresponding computational instructions * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
@ -1237,16 +1238,8 @@ static void replace_constant_operands(sccp_ctx *ctx) {
* not. As such doing DCE here will allow us to eliminate more dead code in combination. * not. As such doing DCE here will allow us to eliminate more dead code in combination.
* c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
* we need to collect. */ * we need to collect. */
static void eliminate_dead_instructions(sccp_ctx *ctx) {
zend_ssa *ssa = ctx->ssa;
zend_op_array *op_array = ctx->op_array;
int i;
/* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE if (var->definition >= 0 && value_known(&ctx->values[i])) {
* and INIT_ARRAY. */
for (i = ssa->vars_count - 1; i >= 0; i--) {
zend_ssa_var *var = &ssa->vars[i];
if (value_known(&ctx->values[i]) && var->definition >= 0) {
zend_op *opline = &op_array->opcodes[var->definition]; zend_op *opline = &op_array->opcodes[var->definition];
zend_ssa_op *ssa_op = &ssa->ops[var->definition]; zend_ssa_op *ssa_op = &ssa->ops[var->definition];
if (opline->opcode == ZEND_ASSIGN) { if (opline->opcode == ZEND_ASSIGN) {
@ -1254,11 +1247,11 @@ static void eliminate_dead_instructions(sccp_ctx *ctx) {
continue; continue;
} }
if (ssa_op->result_def >= 0 if (ssa_op->result_def == i
&& ssa_op->op1_def < 0 && ssa_op->op1_def < 0
&& ssa_op->op2_def < 0 && ssa_op->op2_def < 0
&& ssa->vars[ssa_op->result_def].use_chain < 0 && var->use_chain < 0
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { && var->phi_use_chain == NULL) {
if (opline->opcode == ZEND_DO_ICALL) { if (opline->opcode == ZEND_DO_ICALL) {
/* Call instruction -> remove opcodes that are part of the call */ /* Call instruction -> remove opcodes that are part of the call */
zend_call_info *call = ctx->call_map[var->definition]; zend_call_info *call = ctx->call_map[var->definition];
@ -1278,10 +1271,8 @@ static void eliminate_dead_instructions(sccp_ctx *ctx) {
zend_ssa_remove_result_def(ssa, ssa_op); zend_ssa_remove_result_def(ssa, ssa_op);
zend_ssa_remove_instr(ssa, opline, ssa_op); zend_ssa_remove_instr(ssa, opline, ssa_op);
} }
} else if (ssa_op->op1_def >= 0) { } else if (ssa_op->op1_def == i) {
/* Compound assign or incdec -> convert to direct ASSIGN */ /* Compound assign or incdec -> convert to direct ASSIGN */
zval *val = &ctx->values[ssa_op->op1_def];
ZEND_ASSERT(value_known(val));
/* Destroy previous op2 */ /* Destroy previous op2 */
if (opline->op2_type == IS_CONST) { if (opline->op2_type == IS_CONST) {
@ -1308,8 +1299,8 @@ static void eliminate_dead_instructions(sccp_ctx *ctx) {
/* Convert to ASSIGN */ /* Convert to ASSIGN */
opline->opcode = ZEND_ASSIGN; opline->opcode = ZEND_ASSIGN;
opline->op2_type = IS_CONST; opline->op2_type = IS_CONST;
opline->op2.constant = zend_optimizer_add_literal(op_array, val); opline->op2.constant = zend_optimizer_add_literal(op_array, value);
Z_TRY_ADDREF_P(val); Z_TRY_ADDREF_P(value);
} }
} }
/*if (var->definition_phi /*if (var->definition_phi
@ -1354,17 +1345,6 @@ static void sccp_context_free(sccp_ctx *ctx) {
efree(ctx->values); efree(ctx->values);
} }
static void sccp_apply_results(sccp_ctx *ctx) {
replace_constant_operands(ctx);
#if 0
zend_dump_op_array(ctx->op_array, ZEND_DUMP_SSA, "SCCP-1", ctx->ssa);
#endif
eliminate_dead_instructions(ctx);
#if 0
zend_dump_op_array(ctx->op_array, ZEND_DUMP_SSA, "SCCP-2", ctx->ssa);
#endif
}
void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map) void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
{ {
scdf_ctx scdf; scdf_ctx scdf;
@ -1380,7 +1360,7 @@ void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_in
scdf_solve(&scdf, "SCCP"); scdf_solve(&scdf, "SCCP");
scdf_remove_unreachable_blocks(&scdf); scdf_remove_unreachable_blocks(&scdf);
sccp_apply_results(&ctx); replace_constant_operands(&ctx);
scdf_free(&scdf); scdf_free(&scdf);
sccp_context_free(&ctx); sccp_context_free(&ctx);