Skip to content

Commit b3d0178

Browse files
committed
Rearrange code to enable inner precedure inference
1 parent 7a2f60d commit b3d0178

File tree

4 files changed

+133
-89
lines changed

4 files changed

+133
-89
lines changed

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 81 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -30,50 +30,41 @@
3030
#include "zend_inference.h"
3131
#include "zend_dump.h"
3232

33-
void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
33+
int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags)
3434
{
35-
void *checkpoint;
3635
uint32_t build_flags;
37-
uint32_t flags = 0;
38-
zend_ssa ssa;
3936

4037
/* Build SSA */
41-
memset(&ssa, 0, sizeof(ssa));
42-
checkpoint = zend_arena_checkpoint(ctx->arena);
38+
memset(ssa, 0, sizeof(zend_ssa));
4339

44-
if (zend_build_cfg(&ctx->arena, op_array, 0, &ssa.cfg, &flags) != SUCCESS) {
45-
zend_arena_release(&ctx->arena, checkpoint);
46-
return;
40+
if (zend_build_cfg(&ctx->arena, op_array, 0, &ssa->cfg, flags) != SUCCESS) {
41+
return FAILURE;
4742
}
4843

49-
if (flags & ZEND_FUNC_TOO_DYNAMIC) {
50-
zend_arena_release(&ctx->arena, checkpoint);
51-
return;
44+
if (*flags & ZEND_FUNC_TOO_DYNAMIC) {
45+
return FAILURE;
5246
}
5347

54-
if (zend_cfg_build_predecessors(&ctx->arena, &ssa.cfg) != SUCCESS) {
55-
zend_arena_release(&ctx->arena, checkpoint);
56-
return;
48+
if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
49+
return FAILURE;
5750
}
5851

5952
if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
60-
zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNUSED_VARS, "dfa cfg", &ssa.cfg);
53+
zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNUSED_VARS, "dfa cfg", &ssa->cfg);
6154
}
6255

6356
/* Compute Dominators Tree */
64-
if (zend_cfg_compute_dominators_tree(op_array, &ssa.cfg) != SUCCESS) {
65-
zend_arena_release(&ctx->arena, checkpoint);
66-
return;
57+
if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
58+
return FAILURE;
6759
}
6860

6961
/* Identify reducible and irreducible loops */
70-
if (zend_cfg_identify_loops(op_array, &ssa.cfg, &flags) != SUCCESS) {
71-
zend_arena_release(&ctx->arena, checkpoint);
72-
return;
62+
if (zend_cfg_identify_loops(op_array, &ssa->cfg, flags) != SUCCESS) {
63+
return FAILURE;
7364
}
7465

7566
if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
76-
zend_dump_dominators(op_array, &ssa.cfg);
67+
zend_dump_dominators(op_array, &ssa->cfg);
7768
}
7869

7970
build_flags = 0;
@@ -83,45 +74,45 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
8374
if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
8475
build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
8576
}
86-
if (zend_build_ssa(&ctx->arena, op_array, build_flags, &ssa, &flags) != SUCCESS) {
87-
zend_arena_release(&ctx->arena, checkpoint);
88-
return;
77+
if (zend_build_ssa(&ctx->arena, op_array, build_flags, ssa, flags) != SUCCESS) {
78+
return FAILURE;
8979
}
9080

9181
if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
92-
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", &ssa);
82+
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", ssa);
9383
}
9484

9585

96-
if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, &ssa) != SUCCESS){
97-
zend_arena_release(&ctx->arena, checkpoint);
98-
return;
86+
if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
87+
return FAILURE;
9988
}
10089

101-
if (zend_ssa_find_false_dependencies(op_array, &ssa) != SUCCESS) {
102-
zend_arena_release(&ctx->arena, checkpoint);
103-
return;
90+
if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
91+
return FAILURE;
10492
}
10593

106-
if (zend_ssa_find_sccs(op_array, &ssa) != SUCCESS){
107-
zend_arena_release(&ctx->arena, checkpoint);
108-
return;
94+
if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
95+
return FAILURE;
10996
}
11097

111-
if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, &ssa) != SUCCESS) {
112-
return;
98+
if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) {
99+
return FAILURE;
113100
}
114101

115102
if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
116-
zend_dump_ssa_variables(op_array, &ssa);
103+
zend_dump_ssa_variables(op_array, ssa);
117104
}
118105

106+
return SUCCESS;
107+
}
108+
109+
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
110+
{
119111
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
120-
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", &ssa);
112+
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "before dfa pass", ssa);
121113
}
122114

123-
//TODO: Add optimization patterns ???
124-
if (ssa.var_info) {
115+
if (ssa->var_info) {
125116
int i;
126117
int remove_nops = 0;
127118

@@ -132,48 +123,48 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
132123
// --
133124
// 2: ASSIGN #2.CV [undef,scalar] -> #3.CV, #1.CV | 3.CV = QM_ASSIGN #1.CV
134125

135-
for (i = 0; i < ssa.vars_count; i++) {
136-
int op2 = ssa.vars[i].definition;
126+
for (i = 0; i < ssa->vars_count; i++) {
127+
int op2 = ssa->vars[i].definition;
137128

138129
if (op2 >= 0
139130
&& op_array->opcodes[op2].opcode == ZEND_ASSIGN
140131
&& op_array->opcodes[op2].op1_type == IS_CV
141132
&& !RETURN_VALUE_USED(&op_array->opcodes[op2])
142133
) {
143-
int var2 = ssa.ops[op2].op1_use;
134+
int var2 = ssa->ops[op2].op1_use;
144135

145136
if (var2 >= 0
146-
&& !(ssa.var_info[var2].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
137+
&& !(ssa->var_info[var2].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
147138
) {
148139

149140
if (op_array->opcodes[op2].op2_type & (IS_TMP_VAR|IS_VAR)) {
150-
int var1 = ssa.ops[op2].op2_use;
141+
int var1 = ssa->ops[op2].op2_use;
151142

152143
if (var1 >= 0
153-
&& !(ssa.var_info[var1].type & MAY_BE_REF)
154-
&& ssa.vars[var1].definition >= 0
155-
&& ssa.ops[ssa.vars[var1].definition].result_def == var1
156-
&& ssa.ops[ssa.vars[var1].definition].result_use < 0
157-
&& ssa.vars[var1].use_chain == op2
158-
&& ssa.ops[op2].op2_use_chain < 0
159-
&& !ssa.vars[var1].phi_use_chain
160-
&& !ssa.vars[var1].sym_use_chain
144+
&& !(ssa->var_info[var1].type & MAY_BE_REF)
145+
&& ssa->vars[var1].definition >= 0
146+
&& ssa->ops[ssa->vars[var1].definition].result_def == var1
147+
&& ssa->ops[ssa->vars[var1].definition].result_use < 0
148+
&& ssa->vars[var1].use_chain == op2
149+
&& ssa->ops[op2].op2_use_chain < 0
150+
&& !ssa->vars[var1].phi_use_chain
151+
&& !ssa->vars[var1].sym_use_chain
161152
) {
162-
int op1 = ssa.vars[var1].definition;
153+
int op1 = ssa->vars[var1].definition;
163154
int var3 = i;
164155

165-
if (zend_ssa_unlink_use_chain(&ssa, op2, var2)) {
156+
if (zend_ssa_unlink_use_chain(ssa, op2, var2)) {
166157
/* Reconstruct SSA */
167-
ssa.vars[var3].definition = op1;
168-
ssa.ops[op1].result_def = var3;
158+
ssa->vars[var3].definition = op1;
159+
ssa->ops[op1].result_def = var3;
169160

170-
ssa.vars[var1].definition = -1;
171-
ssa.vars[var1].use_chain = -1;
161+
ssa->vars[var1].definition = -1;
162+
ssa->vars[var1].use_chain = -1;
172163

173-
ssa.ops[op2].op1_use = -1;
174-
ssa.ops[op2].op2_use = -1;
175-
ssa.ops[op2].op1_def = -1;
176-
ssa.ops[op2].op1_use_chain = -1;
164+
ssa->ops[op2].op1_use = -1;
165+
ssa->ops[op2].op2_use = -1;
166+
ssa->ops[op2].op1_def = -1;
167+
ssa->ops[op2].op1_use_chain = -1;
177168

178169
/* Update opcodes */
179170
op_array->opcodes[op1].result_type = op_array->opcodes[op2].op1_type;
@@ -184,20 +175,20 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
184175
}
185176
} else if (op_array->opcodes[op2].op2_type == IS_CONST
186177
|| (op_array->opcodes[op2].op2_type == IS_CV
187-
&& ssa.ops[op2].op2_use >= 0
188-
&& ssa.ops[op2].op2_def < 0
189-
&& !(ssa.var_info[ssa.ops[op2].op2_use].type & MAY_BE_REF))
178+
&& ssa->ops[op2].op2_use >= 0
179+
&& ssa->ops[op2].op2_def < 0
180+
&& !(ssa->var_info[ssa->ops[op2].op2_use].type & MAY_BE_REF))
190181
) {
191182
int var3 = i;
192183

193-
if (zend_ssa_unlink_use_chain(&ssa, op2, var2)) {
184+
if (zend_ssa_unlink_use_chain(ssa, op2, var2)) {
194185
/* Reconstruct SSA */
195-
ssa.ops[op2].result_def = var3;
196-
ssa.ops[op2].op1_def = -1;
197-
ssa.ops[op2].op1_use = ssa.ops[op2].op2_use;
198-
ssa.ops[op2].op1_use_chain = ssa.ops[op2].op2_use_chain;
199-
ssa.ops[op2].op2_use = -1;
200-
ssa.ops[op2].op2_use_chain = -1;
186+
ssa->ops[op2].result_def = var3;
187+
ssa->ops[op2].op1_def = -1;
188+
ssa->ops[op2].op1_use = ssa->ops[op2].op2_use;
189+
ssa->ops[op2].op1_use_chain = ssa->ops[op2].op2_use_chain;
190+
ssa->ops[op2].op2_use = -1;
191+
ssa->ops[op2].op2_use_chain = -1;
201192

202193
/* Update opcode */
203194
op_array->opcodes[op2].result_type = op_array->opcodes[op2].op1_type;
@@ -217,11 +208,24 @@ void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
217208
}
218209
}
219210

220-
221211
if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
222-
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "after dfa pass", &ssa);
212+
zend_dump_op_array(op_array, ZEND_DUMP_SSA | ZEND_DUMP_HIDE_UNUSED_VARS, "after dfa pass", ssa);
213+
}
214+
}
215+
216+
void optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
217+
{
218+
void *checkpoint = zend_arena_checkpoint(ctx->arena);
219+
uint32_t flags = 0;
220+
zend_ssa ssa;
221+
222+
if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, &flags) != SUCCESS) {
223+
zend_arena_release(&ctx->arena, checkpoint);
224+
return;
223225
}
224226

227+
zend_dfa_optimize_op_array(op_array, ctx, &ssa);
228+
225229
/* Destroy SSA */
226230
zend_arena_release(&ctx->arena, checkpoint);
227231
}

ext/opcache/Optimizer/zend_inference.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,7 +3897,7 @@ void zend_func_return_info(const zend_op_array *op_array,
38973897
}
38983898

38993899
if (opline->op1_type == IS_CONST) {
3900-
if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_NULL) {
3900+
if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_NULL) {
39013901
if (tmp_has_range < 0) {
39023902
tmp_has_range = 1;
39033903
tmp_range.underflow = 0;
@@ -3912,7 +3912,7 @@ void zend_func_return_info(const zend_op_array *op_array,
39123912
tmp_range.max = MAX(tmp_range.max, 0);
39133913
}
39143914
}
3915-
} else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_FALSE) {
3915+
} else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_FALSE) {
39163916
if (tmp_has_range < 0) {
39173917
tmp_has_range = 1;
39183918
tmp_range.underflow = 0;
@@ -3927,7 +3927,7 @@ void zend_func_return_info(const zend_op_array *op_array,
39273927
tmp_range.max = MAX(tmp_range.max, 0);
39283928
}
39293929
}
3930-
} else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_TRUE) {
3930+
} else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_TRUE) {
39313931
if (tmp_has_range < 0) {
39323932
tmp_has_range = 1;
39333933
tmp_range.underflow = 0;
@@ -3942,19 +3942,19 @@ void zend_func_return_info(const zend_op_array *op_array,
39423942
tmp_range.max = MAX(tmp_range.max, 1);
39433943
}
39443944
}
3945-
} else if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) == IS_LONG) {
3945+
} else if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)) == IS_LONG) {
39463946
if (tmp_has_range < 0) {
39473947
tmp_has_range = 1;
39483948
tmp_range.underflow = 0;
3949-
tmp_range.min = Z_LVAL_P(RT_CONSTANT(op_array, opline->op1));
3950-
tmp_range.max = Z_LVAL_P(RT_CONSTANT(op_array, opline->op1));
3949+
tmp_range.min = Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants));
3950+
tmp_range.max = Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants));
39513951
tmp_range.overflow = 0;
39523952
} else if (tmp_has_range) {
39533953
if (!tmp_range.underflow) {
3954-
tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(RT_CONSTANT(op_array, opline->op1)));
3954+
tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)));
39553955
}
39563956
if (!tmp_range.overflow) {
3957-
tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(RT_CONSTANT(op_array, opline->op1)));
3957+
tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(CRT_CONSTANT_EX(op_array, opline->op1, info->ssa.rt_constants)));
39583958
}
39593959
}
39603960
} else {

ext/opcache/Optimizer/zend_optimizer.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,25 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer
727727
}
728728
}
729729

730+
static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
731+
{
732+
zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
733+
734+
if (func_info) {
735+
zend_call_info *call_info =func_info->callee_info;
736+
737+
while (call_info) {
738+
zend_op *opline = call_info->caller_init_opline;
739+
740+
if (opline && call_info->callee_func) {
741+
ZEND_ASSERT(opline->opcode == ZEND_INIT_FCALL);
742+
opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
743+
}
744+
call_info = call_info->next_callee;
745+
}
746+
}
747+
}
748+
730749
int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
731750
{
732751
uint idx, j;
@@ -777,19 +796,28 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend
777796
(ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
778797
zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
779798
/* Optimize using call-graph */
780-
uint32_t i;
799+
void *checkpoint = zend_arena_checkpoint(ctx.arena);
800+
int i;
781801
zend_func_info *func_info;
782802

783803
for (i = 0; i < call_graph.op_arrays_count; i++) {
784804
zend_revert_pass_two(call_graph.op_arrays[i]);
805+
}
806+
807+
for (i = 0; i < call_graph.op_arrays_count; i++) {
785808
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
786809
if (func_info) {
787-
func_info->ssa.rt_constants = 0;
810+
zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
788811
}
789812
}
790813

814+
//TODO: perform inner-script inference???
815+
791816
for (i = 0; i < call_graph.op_arrays_count; i++) {
792-
optimize_dfa(call_graph.op_arrays[i], &ctx);
817+
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
818+
if (func_info) {
819+
zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
820+
}
793821
}
794822

795823
if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
@@ -798,11 +826,19 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend
798826
}
799827
}
800828

829+
if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
830+
for (i = 0; i < call_graph.op_arrays_count; i++) {
831+
zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
832+
}
833+
}
834+
801835
for (i = 0; i < call_graph.op_arrays_count; i++) {
802836
zend_redo_pass_two(call_graph.op_arrays[i]);
803837
ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
804838
}
805-
}
839+
840+
zend_arena_release(&ctx.arena, checkpoint);
841+
} else
806842
#endif
807843

808844
if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {

0 commit comments

Comments
 (0)