Skip to content

Add SSA optimization to remove useless FREE and result var stores #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
VAR_SOURCE(opline->op1) = NULL;
}
break;

#if 0
case ZEND_FREE:
/* Note: Only remove the source if the source is local to this block.
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
Expand Down Expand Up @@ -330,7 +330,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
}
break;

#endif
#if 0
/* pre-evaluate functions:
constant(x)
Expand Down
86 changes: 84 additions & 2 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
#include "zend_inference.h"
#include "zend_dump.h"

#ifndef ZEND_DEBUG_DFA
// #ifndef ZEND_DEBUG_DFA
# define ZEND_DEBUG_DFA ZEND_DEBUG
#endif
// #endif

#if ZEND_DEBUG_DFA
# include "ssa_integrity.c"
Expand Down Expand Up @@ -1076,6 +1076,84 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
return 0;
}

static int zend_dfa_remove_only_free_uses(zend_op_array *op_array, zend_ssa *ssa)
{
int times_applied = 0;
for (uint32_t i = 0; i < op_array->last; i++) {
zend_op *opline = op_array->opcodes + i;
if (opline->opcode != ZEND_FREE) {
continue;
}
int op1_use = ssa->ops[i].op1_use;
/* Possible if it's unreachable. */
if (op1_use < 0) {
continue;
}
int definition = ssa->vars[op1_use].definition;
if (definition < 0) {
continue;
}
zend_op *defining_opline = op_array->opcodes + definition;
if (opline->op1_type == IS_TMP_VAR) {
switch (defining_opline->opcode) {
case ZEND_ASSIGN:
case ZEND_ASSIGN_DIM:
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_OP:
case ZEND_ASSIGN_DIM_OP:
case ZEND_ASSIGN_OBJ_OP:
case ZEND_ASSIGN_STATIC_PROP_OP:
case ZEND_PRE_INC:
case ZEND_PRE_DEC:
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP:
break;
default:
continue;
}
} else if (opline->op1_type == IS_VAR) {
switch (defining_opline->opcode) {
case ZEND_FETCH_R:
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_DIM_R:
case ZEND_FETCH_OBJ_R:
case ZEND_NEW:
case ZEND_FETCH_THIS:
continue;
default:
break;
}
}
int use;
bool all_free = true;
int result_def = ssa->ops[definition].result_def;
if (result_def < 0) {
continue;
}
FOREACH_USE(ssa->vars + result_def, use) {
if (op_array->opcodes[use].opcode != ZEND_FREE) {
all_free = false;
break;
}
} FOREACH_USE_END();
if (all_free) {
FOREACH_USE(ssa->vars + result_def, use) {
MAKE_NOP(op_array->opcodes + use);
ssa->ops[use].op1_use_chain = -1;
ssa->ops[use].op1_use = -1;
} FOREACH_USE_END();
defining_opline->result_type = IS_UNUSED;
zend_ssa_remove_uses_of_var(ssa, result_def);
zend_ssa_remove_result_def(ssa, ssa->ops + definition);
times_applied++;
}
}
return times_applied;
}

void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
{
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
Expand All @@ -1094,6 +1172,10 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
ssa_verify_integrity(op_array, ssa, "before dfa");
#endif

if (zend_dfa_remove_only_free_uses(op_array, ssa)) {
remove_nops = 1;
}

if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
remove_nops = 1;
Expand Down
16 changes: 6 additions & 10 deletions ext/opcache/tests/opt/gh11245_2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ switch (++X::$prop) {
?>
--EXPECTF--
$_main:
; (lines=7, args=0, vars=1, tmps=2)
; (lines=5, args=0, vars=1, tmps=1)
; (after optimizer)
; %s
0000 T1 = PRE_INC_STATIC_PROP string("prop") string("X")
0001 T2 = ISSET_ISEMPTY_CV (empty) CV0($xx)
0002 JMPZ T2 0005
0003 FREE T1
0004 RETURN null
0005 FREE T1
0006 RETURN int(1)
LIVE RANGES:
1: 0001 - 0005 (tmp/var)
0000 PRE_INC_STATIC_PROP string("prop") string("X")
0001 T1 = ISSET_ISEMPTY_CV (empty) CV0($xx)
0002 JMPZ T1 0004
0003 RETURN null
0004 RETURN int(1)