Skip to content

Initial prototyping implicit move #9

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

Closed
wants to merge 4 commits into from
Closed
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
76 changes: 76 additions & 0 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,77 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
return 0;
}

static bool op_dominates(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *a, const zend_op *b)
{
uint32_t a_block = ssa->cfg.map[a - op_array->opcodes];
uint32_t b_block = ssa->cfg.map[b - op_array->opcodes];
if (a_block == b_block) {
return a < b;
} else {
return dominates(ssa->cfg.blocks, a_block, b_block);
}
}

static bool op_dominates_all_uses(const zend_op_array *op_array, const zend_ssa *ssa, int start) {
int use;
FOREACH_USE(ssa->vars + ssa->ops[start].op1_def, use) {
if (!op_dominates(op_array, ssa, op_array->opcodes + start, op_array->opcodes + use)) {
return false;
}
} FOREACH_USE_END();
return true;
}

/* Sets a flag on SEND ops when a copy can be a avoided. */
static void zend_dfa_optimize_send_copies(zend_op_array *op_array, const zend_ssa *ssa)
{
/* func_get_args() etc could make the optimization observable */
if (ssa->cfg.flags & ZEND_FUNC_VARARG) {
return;
}

for (uint32_t i = 0; i < op_array->last; i++) {
const zend_op *opline = &op_array->opcodes[i];
if ((opline->opcode != ZEND_SEND_VAR && opline->opcode != ZEND_SEND_VAR_EX) || opline->op2_type != IS_UNUSED || opline->op1_type != IS_CV) {
continue;
}
int ssa_cv = ssa->ops[i].op1_use;
#if 0
/* NULL must not be visible in backtraces */
if (ssa->vars[ssa_cv].var < op_array->num_args) {
continue;
}
#endif
/* Unsetting a CV is always fine if it gets overwritten afterwards.
* Since type inference often infers very wide types, we are very loose in matching types. */
uint32_t type = ssa->var_info[ssa_cv].type;
if ((type & (MAY_BE_REF|MAY_BE_UNDEF)) || !(type & MAY_BE_RC1) || !(type & (MAY_BE_STRING|MAY_BE_ARRAY))) {
continue;
}

if (opline->opcode == ZEND_SEND_VAR) {
/* Check if the call dominates the assignment and the assignment dominates all the future uses of this SSA variable */
int next_use = ssa->ops[i].op1_use_chain;
if (next_use >= 0
&& op_array->opcodes[next_use].opcode == ZEND_ASSIGN
&& ssa->ops[next_use].op1_use == ssa_cv
&& ssa->ops[next_use].op2_use >= 0
&& op_dominates(op_array, ssa, opline, op_array->opcodes + next_use)) {
if (op_dominates_all_uses(op_array, ssa, next_use)) {
op_array->opcodes[i].extended_value = 1;
//fprintf(stderr, "yes optimize 1\n");
}
}
} else /* ZEND_SEND_VAR_EX */ {
ZEND_ASSERT(ssa->ops[i].op1_def != -1);
if (ssa->vars[ssa->ops[i].op1_def].no_val) {
op_array->opcodes[i].extended_value = 1;
//fprintf(stderr, "yes optimize 2\n");
}
}
}
}

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 Down Expand Up @@ -1124,6 +1195,11 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
#endif
}

/* Optimization should not be done on main because of globals. */
if (op_array->function_name) {
zend_dfa_optimize_send_copies(op_array, ssa);
}

for (v = op_array->last_var; v < ssa->vars_count; v++) {

op_1 = ssa->vars[v].definition;
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/zend_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, ze
}
/* }}} */

static bool dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
bool dominates(const zend_basic_block *blocks, int a, int b) /* {{{ */
{
while (blocks[b].level > blocks[a].level) {
b = blocks[b].idom;
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *c
ZEND_API void zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
bool dominates(const zend_basic_block *blocks, int a, int b);

END_EXTERN_C()

Expand Down
8 changes: 1 addition & 7 deletions Zend/Optimizer/zend_ssa.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,13 @@
*/

#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_dfg.h"
#include "zend_ssa.h"
#include "zend_dump.h"
#include "zend_inference.h"
#include "Optimizer/zend_optimizer_internal.h"

static bool dominates(const zend_basic_block *blocks, int a, int b) {
while (blocks[b].level > blocks[a].level) {
b = blocks[b].idom;
}
return a == b;
}

static bool will_rejoin(
const zend_cfg *cfg, const zend_dfg *dfg, const zend_basic_block *block,
int other_successor, int exclude, int var) {
Expand Down
37 changes: 35 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -9900,7 +9900,7 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_undef):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

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)
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)
{
USE_OPLINE
zval *varptr, *arg;
Expand All @@ -9917,7 +9917,21 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_i
ZEND_VM_NEXT_OPCODE();
}

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)
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)
{
USE_OPLINE
zval *varptr, *arg;

varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
arg = ZEND_CALL_VAR(EX(call), opline->result.var);

ZVAL_COPY_VALUE(arg, varptr);
ZVAL_UNDEF(varptr);

ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, !op->extended_value && 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)
{
USE_OPLINE
zval *varptr, *arg;
Expand All @@ -9939,6 +9953,25 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op-
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */, ZEND_SEND_VAR_EX_SIMPLE_EXT, CV, UNUSED|NUM)
{
USE_OPLINE
zval *varptr, *arg;
uint32_t arg_num = opline->op2.num;

if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
}

varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
arg = ZEND_CALL_VAR(EX(call), opline->result.var);

ZVAL_COPY_VALUE(arg, varptr);
ZVAL_UNDEF(varptr);

ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_SIMPLE, CONST, NUM)
{
USE_OPLINE
Expand Down
Loading