Skip to content

Commit 624f76d

Browse files
committed
Set RETURN_VALUE_UNUSED instead of additional FREE opcode, if possible.
Keep alive dead instructions that have to free two temporary variables.
1 parent 94c9b26 commit 624f76d

File tree

1 file changed

+77
-7
lines changed
  • ext/opcache/Optimizer

1 file changed

+77
-7
lines changed

ext/opcache/Optimizer/dce.c

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,68 @@ static inline zend_bool is_var_dead(context *ctx, int var_num) {
276276
}
277277
}
278278

279+
// Sometimes we can mark the var as EXT_UNUSED
280+
static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
281+
if (use_chain >= 0) {
282+
return 0;
283+
}
284+
zend_ssa_var *var = &ctx->ssa->vars[free_var];
285+
int def = var->definition;
286+
287+
if (def >= 0) {
288+
zend_ssa_op *def_op = &ctx->ssa->ops[def];
289+
290+
if (def_op->result_def == free_var
291+
&& var->phi_use_chain == NULL
292+
&& var->use_chain == (opline - ctx->op_array->opcodes)) {
293+
zend_op *def_opline = &ctx->op_array->opcodes[def];
294+
295+
switch (def_opline->opcode) {
296+
case ZEND_ASSIGN:
297+
case ZEND_ASSIGN_REF:
298+
case ZEND_ASSIGN_DIM:
299+
case ZEND_ASSIGN_OBJ:
300+
case ZEND_ASSIGN_ADD:
301+
case ZEND_ASSIGN_SUB:
302+
case ZEND_ASSIGN_MUL:
303+
case ZEND_ASSIGN_DIV:
304+
case ZEND_ASSIGN_MOD:
305+
case ZEND_ASSIGN_SL:
306+
case ZEND_ASSIGN_SR:
307+
case ZEND_ASSIGN_CONCAT:
308+
case ZEND_ASSIGN_BW_OR:
309+
case ZEND_ASSIGN_BW_AND:
310+
case ZEND_ASSIGN_BW_XOR:
311+
case ZEND_ASSIGN_POW:
312+
case ZEND_PRE_INC:
313+
case ZEND_POST_INC:
314+
case ZEND_PRE_DEC:
315+
case ZEND_POST_DEC:
316+
case ZEND_PRE_INC_OBJ:
317+
case ZEND_POST_INC_OBJ:
318+
case ZEND_PRE_DEC_OBJ:
319+
case ZEND_POST_DEC_OBJ:
320+
case ZEND_DO_ICALL:
321+
case ZEND_DO_UCALL:
322+
case ZEND_DO_FCALL_BY_NAME:
323+
case ZEND_DO_FCALL:
324+
case ZEND_INCLUDE_OR_EVAL:
325+
case ZEND_YIELD:
326+
case ZEND_YIELD_FROM:
327+
case ZEND_ASSERT_CHECK:
328+
def_opline->result_type = IS_UNUSED;
329+
def_opline->result.var = 0;
330+
def_op->result_def = -1;
331+
var->definition = -1;
332+
return 1;
333+
default:
334+
break;
335+
}
336+
}
337+
}
338+
return 0;
339+
}
340+
279341
/* Returns whether the instruction has been DCEd */
280342
static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
281343
zend_ssa *ssa = ctx->ssa;
@@ -291,20 +353,28 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
291353
return 0;
292354
}
293355

294-
// TODO Two free vars?
295356
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op1_use)) {
296-
free_var = ssa_op->op1_use;
297-
free_var_type = opline->op1_type;
298-
} else if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
299-
free_var = ssa_op->op2_use;
300-
free_var_type = opline->op2_type;
357+
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
358+
free_var = ssa_op->op1_use;
359+
free_var_type = opline->op1_type;
360+
}
361+
}
362+
if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
363+
if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
364+
if (free_var >= 0) {
365+
// TODO: We can't free two vars. Keep instruction alive.
366+
zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
367+
return 0;
368+
}
369+
free_var = ssa_op->op2_use;
370+
free_var_type = opline->op2_type;
371+
}
301372
}
302373

303374
zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
304375
zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
305376

306377
if (free_var >= 0) {
307-
// TODO Sometimes we can mark the var as EXT_UNUSED
308378
opline->opcode = ZEND_FREE;
309379
opline->op1.var = (uintptr_t) ZEND_CALL_VAR_NUM(NULL, ssa->vars[free_var].var);
310380
opline->op1_type = free_var_type;

0 commit comments

Comments
 (0)