Skip to content

Commit cffee2f

Browse files
committed
Combined constants substitutaion and dead instruction removing in single pass. This eleminates substitution in dead instructions.
1 parent f890375 commit cffee2f

File tree

1 file changed

+25
-45
lines changed

1 file changed

+25
-45
lines changed

ext/opcache/Optimizer/sccp.c

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,10 +1186,13 @@ static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
11861186
* if they have a certain type. */
11871187
static void replace_constant_operands(sccp_ctx *ctx) {
11881188
zend_ssa *ssa = ctx->ssa;
1189+
zend_op_array *op_array = ctx->op_array;
11891190
int i;
11901191
zval tmp;
11911192

1192-
for (i = 0; i < ssa->vars_count; i++) {
1193+
/* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
1194+
* and INIT_ARRAY. */
1195+
for (i = ssa->vars_count - 1; i >= 0; i--) {
11931196
zend_ssa_var *var = &ssa->vars[i];
11941197
zval *value;
11951198
int use;
@@ -1204,7 +1207,7 @@ static void replace_constant_operands(sccp_ctx *ctx) {
12041207
}
12051208

12061209
FOREACH_USE(var, use) {
1207-
zend_op *opline = &ctx->op_array->opcodes[use];
1210+
zend_op *opline = &op_array->opcodes[use];
12081211
zend_ssa_op *ssa_op = &ssa->ops[use];
12091212
if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
12101213
ZEND_ASSERT(ssa_op->op1_def == -1);
@@ -1223,42 +1226,32 @@ static void replace_constant_operands(sccp_ctx *ctx) {
12231226
ssa_op->op2_use_chain = -1;
12241227
}
12251228
} FOREACH_USE_END();
1226-
}
1227-
}
1228-
1229-
/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
1230-
* value(s) were determined by SCCP. It removes dead computational instructions and converts
1231-
* CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
1232-
* a) During operand replacement we eliminate FREEs. The corresponding computational instructions
1233-
* must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
1234-
* b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
1235-
* and can't be DCEd. This means that it will not be able collect all instructions rendered dead
1236-
* by SCCP, because they may have potentially side-effecting types, but the actual values are
1237-
* not. As such doing DCE here will allow us to eliminate more dead code in combination.
1238-
* c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
1239-
* we need to collect. */
1240-
static void eliminate_dead_instructions(sccp_ctx *ctx) {
1241-
zend_ssa *ssa = ctx->ssa;
1242-
zend_op_array *op_array = ctx->op_array;
1243-
int i;
12441229

1245-
/* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
1246-
* and INIT_ARRAY. */
1247-
for (i = ssa->vars_count - 1; i >= 0; i--) {
1248-
zend_ssa_var *var = &ssa->vars[i];
1249-
if (value_known(&ctx->values[i]) && var->definition >= 0) {
1230+
/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
1231+
* value(s) were determined by SCCP. It removes dead computational instructions and converts
1232+
* CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
1233+
* a) During operand replacement we eliminate FREEs. The corresponding computational instructions
1234+
* must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
1235+
* b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
1236+
* and can't be DCEd. This means that it will not be able collect all instructions rendered dead
1237+
* by SCCP, because they may have potentially side-effecting types, but the actual values are
1238+
* not. As such doing DCE here will allow us to eliminate more dead code in combination.
1239+
* c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
1240+
* we need to collect. */
1241+
1242+
if (var->definition >= 0 && value_known(&ctx->values[i])) {
12501243
zend_op *opline = &op_array->opcodes[var->definition];
12511244
zend_ssa_op *ssa_op = &ssa->ops[var->definition];
12521245
if (opline->opcode == ZEND_ASSIGN) {
12531246
/* Leave assigns to DCE (due to dtor effects) */
12541247
continue;
12551248
}
12561249

1257-
if (ssa_op->result_def >= 0
1250+
if (ssa_op->result_def == i
12581251
&& ssa_op->op1_def < 0
12591252
&& ssa_op->op2_def < 0
1260-
&& ssa->vars[ssa_op->result_def].use_chain < 0
1261-
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
1253+
&& var->use_chain < 0
1254+
&& var->phi_use_chain == NULL) {
12621255
if (opline->opcode == ZEND_DO_ICALL) {
12631256
/* Call instruction -> remove opcodes that are part of the call */
12641257
zend_call_info *call = ctx->call_map[var->definition];
@@ -1278,10 +1271,8 @@ static void eliminate_dead_instructions(sccp_ctx *ctx) {
12781271
zend_ssa_remove_result_def(ssa, ssa_op);
12791272
zend_ssa_remove_instr(ssa, opline, ssa_op);
12801273
}
1281-
} else if (ssa_op->op1_def >= 0) {
1274+
} else if (ssa_op->op1_def == i) {
12821275
/* Compound assign or incdec -> convert to direct ASSIGN */
1283-
zval *val = &ctx->values[ssa_op->op1_def];
1284-
ZEND_ASSERT(value_known(val));
12851276

12861277
/* Destroy previous op2 */
12871278
if (opline->op2_type == IS_CONST) {
@@ -1308,8 +1299,8 @@ static void eliminate_dead_instructions(sccp_ctx *ctx) {
13081299
/* Convert to ASSIGN */
13091300
opline->opcode = ZEND_ASSIGN;
13101301
opline->op2_type = IS_CONST;
1311-
opline->op2.constant = zend_optimizer_add_literal(op_array, val);
1312-
Z_TRY_ADDREF_P(val);
1302+
opline->op2.constant = zend_optimizer_add_literal(op_array, value);
1303+
Z_TRY_ADDREF_P(value);
13131304
}
13141305
}
13151306
/*if (var->definition_phi
@@ -1354,17 +1345,6 @@ static void sccp_context_free(sccp_ctx *ctx) {
13541345
efree(ctx->values);
13551346
}
13561347

1357-
static void sccp_apply_results(sccp_ctx *ctx) {
1358-
replace_constant_operands(ctx);
1359-
#if 0
1360-
zend_dump_op_array(ctx->op_array, ZEND_DUMP_SSA, "SCCP-1", ctx->ssa);
1361-
#endif
1362-
eliminate_dead_instructions(ctx);
1363-
#if 0
1364-
zend_dump_op_array(ctx->op_array, ZEND_DUMP_SSA, "SCCP-2", ctx->ssa);
1365-
#endif
1366-
}
1367-
13681348
void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
13691349
{
13701350
scdf_ctx scdf;
@@ -1380,7 +1360,7 @@ void sccp_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_call_in
13801360
scdf_solve(&scdf, "SCCP");
13811361

13821362
scdf_remove_unreachable_blocks(&scdf);
1383-
sccp_apply_results(&ctx);
1363+
replace_constant_operands(&ctx);
13841364

13851365
scdf_free(&scdf);
13861366
sccp_context_free(&ctx);

0 commit comments

Comments
 (0)