Skip to content

Commit 941afb2

Browse files
committed
Add SSA optimization to remove useless FREE and result var stores
The block optimization pass already removes FREEs, but this is limited to local tranformations only. This adds an SSA variant of that optimization.
1 parent 14a868b commit 941afb2

File tree

3 files changed

+92
-14
lines changed

3 files changed

+92
-14
lines changed

Zend/Optimizer/block_pass.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
262262
VAR_SOURCE(opline->op1) = NULL;
263263
}
264264
break;
265-
265+
#if 0
266266
case ZEND_FREE:
267267
/* Note: Only remove the source if the source is local to this block.
268268
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
@@ -330,7 +330,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
330330
}
331331
}
332332
break;
333-
333+
#endif
334334
#if 0
335335
/* pre-evaluate functions:
336336
constant(x)

Zend/Optimizer/dfa_pass.c

+84-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
#include "zend_inference.h"
3131
#include "zend_dump.h"
3232

33-
#ifndef ZEND_DEBUG_DFA
33+
// #ifndef ZEND_DEBUG_DFA
3434
# define ZEND_DEBUG_DFA ZEND_DEBUG
35-
#endif
35+
// #endif
3636

3737
#if ZEND_DEBUG_DFA
3838
# include "ssa_integrity.c"
@@ -1076,6 +1076,84 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
10761076
return 0;
10771077
}
10781078

1079+
static int zend_dfa_remove_only_free_uses(zend_op_array *op_array, zend_ssa *ssa)
1080+
{
1081+
int times_applied = 0;
1082+
for (uint32_t i = 0; i < op_array->last; i++) {
1083+
zend_op *opline = op_array->opcodes + i;
1084+
if (opline->opcode != ZEND_FREE) {
1085+
continue;
1086+
}
1087+
int op1_use = ssa->ops[i].op1_use;
1088+
/* Possible if it's unreachable. */
1089+
if (op1_use < 0) {
1090+
continue;
1091+
}
1092+
int definition = ssa->vars[op1_use].definition;
1093+
if (definition < 0) {
1094+
continue;
1095+
}
1096+
zend_op *defining_opline = op_array->opcodes + definition;
1097+
if (opline->op1_type == IS_TMP_VAR) {
1098+
switch (defining_opline->opcode) {
1099+
case ZEND_ASSIGN:
1100+
case ZEND_ASSIGN_DIM:
1101+
case ZEND_ASSIGN_OBJ:
1102+
case ZEND_ASSIGN_STATIC_PROP:
1103+
case ZEND_ASSIGN_OP:
1104+
case ZEND_ASSIGN_DIM_OP:
1105+
case ZEND_ASSIGN_OBJ_OP:
1106+
case ZEND_ASSIGN_STATIC_PROP_OP:
1107+
case ZEND_PRE_INC:
1108+
case ZEND_PRE_DEC:
1109+
case ZEND_PRE_INC_OBJ:
1110+
case ZEND_PRE_DEC_OBJ:
1111+
case ZEND_PRE_INC_STATIC_PROP:
1112+
case ZEND_PRE_DEC_STATIC_PROP:
1113+
break;
1114+
default:
1115+
continue;
1116+
}
1117+
} else if (opline->op1_type == IS_VAR) {
1118+
switch (defining_opline->opcode) {
1119+
case ZEND_FETCH_R:
1120+
case ZEND_FETCH_STATIC_PROP_R:
1121+
case ZEND_FETCH_DIM_R:
1122+
case ZEND_FETCH_OBJ_R:
1123+
case ZEND_NEW:
1124+
case ZEND_FETCH_THIS:
1125+
continue;
1126+
default:
1127+
break;
1128+
}
1129+
}
1130+
int use;
1131+
bool all_free = true;
1132+
int result_def = ssa->ops[definition].result_def;
1133+
if (result_def < 0) {
1134+
continue;
1135+
}
1136+
FOREACH_USE(ssa->vars + result_def, use) {
1137+
if (op_array->opcodes[use].opcode != ZEND_FREE) {
1138+
all_free = false;
1139+
break;
1140+
}
1141+
} FOREACH_USE_END();
1142+
if (all_free) {
1143+
FOREACH_USE(ssa->vars + result_def, use) {
1144+
MAKE_NOP(op_array->opcodes + use);
1145+
ssa->ops[use].op1_use_chain = -1;
1146+
ssa->ops[use].op1_use = -1;
1147+
} FOREACH_USE_END();
1148+
defining_opline->result_type = IS_UNUSED;
1149+
zend_ssa_remove_uses_of_var(ssa, result_def);
1150+
zend_ssa_remove_result_def(ssa, ssa->ops + definition);
1151+
times_applied++;
1152+
}
1153+
}
1154+
return times_applied;
1155+
}
1156+
10791157
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
10801158
{
10811159
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -1094,6 +1172,10 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
10941172
ssa_verify_integrity(op_array, ssa, "before dfa");
10951173
#endif
10961174

1175+
if (zend_dfa_remove_only_free_uses(op_array, ssa)) {
1176+
remove_nops = 1;
1177+
}
1178+
10971179
if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
10981180
if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
10991181
remove_nops = 1;

ext/opcache/tests/opt/gh11245_2.phpt

+6-10
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,11 @@ switch (++X::$prop) {
2121
?>
2222
--EXPECTF--
2323
$_main:
24-
; (lines=7, args=0, vars=1, tmps=2)
24+
; (lines=5, args=0, vars=1, tmps=1)
2525
; (after optimizer)
2626
; %s
27-
0000 T1 = PRE_INC_STATIC_PROP string("prop") string("X")
28-
0001 T2 = ISSET_ISEMPTY_CV (empty) CV0($xx)
29-
0002 JMPZ T2 0005
30-
0003 FREE T1
31-
0004 RETURN null
32-
0005 FREE T1
33-
0006 RETURN int(1)
34-
LIVE RANGES:
35-
1: 0001 - 0005 (tmp/var)
27+
0000 PRE_INC_STATIC_PROP string("prop") string("X")
28+
0001 T1 = ISSET_ISEMPTY_CV (empty) CV0($xx)
29+
0002 JMPZ T1 0004
30+
0003 RETURN null
31+
0004 RETURN int(1)

0 commit comments

Comments
 (0)