Skip to content

Commit 5e95aec

Browse files
committed
Initial prototyping implicit move
Fix NPE, tweaks The optimization may not affect backtraces
1 parent 56f916e commit 5e95aec

File tree

4 files changed

+154
-67
lines changed

4 files changed

+154
-67
lines changed

Zend/Optimizer/dfa_pass.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,51 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
10661066
return 0;
10671067
}
10681068

1069+
/* Sets a flag on SEND ops when a copy can be a avoided. */
1070+
static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
1071+
{
1072+
for (int v = 0; v < ssa->vars_count; v++) {
1073+
int var = ssa->vars[v].var;
1074+
if (var >= op_array->last_var) {
1075+
continue;
1076+
}
1077+
1078+
uint32_t type = ssa->var_info[v].type;
1079+
/* Unsetting a CV is always fine if it gets overwritten afterwards. Since type inference often infers
1080+
* very wide types, we are very loose in matching types. */
1081+
if ((type & (MAY_BE_REF|MAY_BE_UNDEF)) || !(type & MAY_BE_RC1) || !(type & (MAY_BE_STRING|MAY_BE_ARRAY))) {
1082+
continue;
1083+
}
1084+
1085+
int use = ssa->vars[v].use_chain;
1086+
if (use >= 0
1087+
&& (op_array->opcodes[use].opcode == ZEND_SEND_VAR || op_array->opcodes[use].opcode == ZEND_SEND_VAR_EX) // TODO
1088+
&& op_array->opcodes[use].op2_type == IS_UNUSED) {
1089+
int next_use = zend_ssa_next_use(ssa->ops, v, use);
1090+
1091+
/* The next use must be an assignment of the call result, immediately after the call such that the
1092+
* unset variable can never be observed.
1093+
* It is also safe to optimize if there are no indirect accesses through func_get_args() etc,
1094+
* no more uses, and it is not part of a loop. */
1095+
if ((next_use < 0
1096+
&& var >= op_array->num_args /* NULL must not be visible in backtraces */
1097+
&& !(ssa->cfg.flags & ZEND_FUNC_VARARG)
1098+
&& !(ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER)
1099+
&& ssa->cfg.blocks[ssa->cfg.map[use]].loop_header == -1)
1100+
|| (next_use >= 0
1101+
&& op_array->opcodes[next_use].opcode == ZEND_ASSIGN
1102+
&& ssa->ops[next_use].op1_use == v
1103+
&& ssa->ops[next_use].op2_use >= 0
1104+
&& call_map[use]
1105+
&& call_map[use]->caller_call_opline + 1 == op_array->opcodes + next_use
1106+
&& ssa->ops[call_map[use]->caller_call_opline - op_array->opcodes].result_def == ssa->ops[next_use].op2_use)) {
1107+
ZEND_ASSERT(op_array->opcodes[use].extended_value == 0);
1108+
op_array->opcodes[use].extended_value = 1;
1109+
}
1110+
}
1111+
}
1112+
}
1113+
10691114
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
10701115
{
10711116
if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -1124,6 +1169,11 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
11241169
#endif
11251170
}
11261171

1172+
/* Optimization should not be done on main because of globals. Pass depends on CFG & call graph. */
1173+
if (call_map && op_array->function_name) {
1174+
zend_dfa_optimize_send_copies(op_array, ssa, call_map);
1175+
}
1176+
11271177
for (v = op_array->last_var; v < ssa->vars_count; v++) {
11281178

11291179
op_1 = ssa->vars[v].definition;

Zend/zend_vm_def.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9900,7 +9900,7 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_undef):
99009900
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
99019901
}
99029902

9903-
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
9903+
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
99049904
{
99059905
USE_OPLINE
99069906
zval *varptr, *arg;
@@ -9917,6 +9917,20 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_i
99179917
ZEND_VM_NEXT_OPCODE();
99189918
}
99199919

9920+
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->extended_value /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_SIMPLE_EXT, CV, NUM)
9921+
{
9922+
USE_OPLINE
9923+
zval *varptr, *arg;
9924+
9925+
varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
9926+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
9927+
9928+
ZVAL_COPY_VALUE(arg, varptr);
9929+
ZVAL_UNDEF(varptr);
9930+
9931+
ZEND_VM_NEXT_OPCODE();
9932+
}
9933+
99209934
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM)
99219935
{
99229936
USE_OPLINE

Zend/zend_vm_execute.h

Lines changed: 81 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_vm_handlers.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,10 +1850,11 @@
18501850
_(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
18511851
_(3458, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
18521852
_(3460, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
1853-
_(3463, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
1854-
_(3465, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
1855-
_(3466, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
1856-
_(3467, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
1857-
_(3468, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
1858-
_(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
1859-
_(3469+1, ZEND_NULL)
1853+
_(3461, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \
1854+
_(3464, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
1855+
_(3466, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
1856+
_(3467, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
1857+
_(3468, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
1858+
_(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
1859+
_(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
1860+
_(3470+1, ZEND_NULL)

0 commit comments

Comments
 (0)