From 5e95aec9e5d3c13b2da798b01b6c6d0b05f3869a Mon Sep 17 00:00:00 2001 From: nielsdos <7771979+nielsdos@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:34:23 +0200 Subject: [PATCH 1/4] Initial prototyping implicit move Fix NPE, tweaks The optimization may not affect backtraces --- Zend/Optimizer/dfa_pass.c | 50 ++++++++++++++ Zend/zend_vm_def.h | 16 ++++- Zend/zend_vm_execute.h | 140 ++++++++++++++++++++++---------------- Zend/zend_vm_handlers.h | 15 ++-- 4 files changed, 154 insertions(+), 67 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index ac99455c1f808..3d99fd85747be 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -1066,6 +1066,51 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss return 0; } +/* 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, zend_ssa *ssa, zend_call_info **call_map) +{ + for (int v = 0; v < ssa->vars_count; v++) { + int var = ssa->vars[v].var; + if (var >= op_array->last_var) { + continue; + } + + uint32_t type = ssa->var_info[v].type; + /* 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. */ + if ((type & (MAY_BE_REF|MAY_BE_UNDEF)) || !(type & MAY_BE_RC1) || !(type & (MAY_BE_STRING|MAY_BE_ARRAY))) { + continue; + } + + int use = ssa->vars[v].use_chain; + if (use >= 0 + && (op_array->opcodes[use].opcode == ZEND_SEND_VAR || op_array->opcodes[use].opcode == ZEND_SEND_VAR_EX) // TODO + && op_array->opcodes[use].op2_type == IS_UNUSED) { + int next_use = zend_ssa_next_use(ssa->ops, v, use); + + /* The next use must be an assignment of the call result, immediately after the call such that the + * unset variable can never be observed. + * It is also safe to optimize if there are no indirect accesses through func_get_args() etc, + * no more uses, and it is not part of a loop. */ + if ((next_use < 0 + && var >= op_array->num_args /* NULL must not be visible in backtraces */ + && !(ssa->cfg.flags & ZEND_FUNC_VARARG) + && !(ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER) + && ssa->cfg.blocks[ssa->cfg.map[use]].loop_header == -1) + || (next_use >= 0 + && op_array->opcodes[next_use].opcode == ZEND_ASSIGN + && ssa->ops[next_use].op1_use == v + && ssa->ops[next_use].op2_use >= 0 + && call_map[use] + && call_map[use]->caller_call_opline + 1 == op_array->opcodes + next_use + && ssa->ops[call_map[use]->caller_call_opline - op_array->opcodes].result_def == ssa->ops[next_use].op2_use)) { + ZEND_ASSERT(op_array->opcodes[use].extended_value == 0); + op_array->opcodes[use].extended_value = 1; + } + } + } +} + 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) { @@ -1124,6 +1169,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. Pass depends on CFG & call graph. */ + if (call_map && op_array->function_name) { + zend_dfa_optimize_send_copies(op_array, ssa, call_map); + } + for (v = op_array->last_var; v < ssa->vars_count; v++) { op_1 = ssa->vars[v].definition; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0b6604217fa35..bfd2b2b8cc67e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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; @@ -9917,6 +9917,20 @@ 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, 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->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 diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 21b927c02b895..266b043d809ea 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -40531,6 +40531,20 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SP ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *varptr, *arg; + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56760,6 +56774,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_VAR_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_SIMPLE_SPEC_CV_LABEL, + (void*)&&ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_LABEL, @@ -60487,6 +60502,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_SEND_VAR_SIMPLE_SPEC_CV) ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV): + VM_TRACE(ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CONST): VM_TRACE(ZEND_DIV_SPEC_CV_CONST) ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -64864,6 +64883,7 @@ void zend_vm_init(void) ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER, + ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_HANDLER, @@ -64921,7 +64941,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3470, + 3471, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -65079,59 +65099,59 @@ void zend_vm_init(void) 2564, 2565, 2566, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, - 3470, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, + 3471, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -65508,17 +65528,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3466; + spec = 3467; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3461 | SPEC_RULE_OP1; + spec = 3462 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3468 | SPEC_RULE_RETVAL; + spec = 3469 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -65531,12 +65551,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3467; + spec = 3468; } break; case ZEND_SEND_VAR: - if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + if (op->op2_type == IS_UNUSED && !op->extended_value && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { spec = 3456 | SPEC_RULE_OP1; + } else if (op->extended_value /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3461; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index fae2138ef912e..f6943a3a2ce80 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1850,10 +1850,11 @@ _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ _(3458, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ _(3460, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3463, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3465, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3466, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3467, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3468, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3469+1, ZEND_NULL) + _(3461, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \ + _(3464, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3466, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3467, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3468, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3470+1, ZEND_NULL) From 085a404d8b4738702f3c7c71b39e29f85a9f6d15 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 21 Apr 2023 22:24:23 +0200 Subject: [PATCH 2/4] Alternative implementation take 1 Switch to new approach and add extra checks Implement support for ZEND_SEND_VAR_EX Deduplicate dominates() function Fix comment JIT build fix Fix num args comparison --- Zend/Optimizer/dfa_pass.c | 94 +++++++++++++++--------- Zend/Optimizer/zend_cfg.c | 2 +- Zend/Optimizer/zend_cfg.h | 1 + Zend/Optimizer/zend_ssa.c | 8 +-- Zend/zend_vm_def.h | 21 +++++- Zend/zend_vm_execute.h | 143 ++++++++++++++++++++++--------------- Zend/zend_vm_handlers.h | 11 +-- ext/opcache/jit/zend_jit.c | 2 +- 8 files changed, 175 insertions(+), 107 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 3d99fd85747be..bb68317ea1940 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -1066,46 +1066,72 @@ 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, zend_ssa *ssa, zend_call_info **call_map) +static void zend_dfa_optimize_send_copies(zend_op_array *op_array, const zend_ssa *ssa) { - for (int v = 0; v < ssa->vars_count; v++) { - int var = ssa->vars[v].var; - if (var >= op_array->last_var) { + /* 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; + } + + /* NULL must not be visible in backtraces */ + int ssa_cv = ssa->ops[i].op1_use; + if (ssa->vars[ssa_cv].var < op_array->num_args) { continue; } - uint32_t type = ssa->var_info[v].type; - /* 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. */ + /* 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; } - int use = ssa->vars[v].use_chain; - if (use >= 0 - && (op_array->opcodes[use].opcode == ZEND_SEND_VAR || op_array->opcodes[use].opcode == ZEND_SEND_VAR_EX) // TODO - && op_array->opcodes[use].op2_type == IS_UNUSED) { - int next_use = zend_ssa_next_use(ssa->ops, v, use); - - /* The next use must be an assignment of the call result, immediately after the call such that the - * unset variable can never be observed. - * It is also safe to optimize if there are no indirect accesses through func_get_args() etc, - * no more uses, and it is not part of a loop. */ - if ((next_use < 0 - && var >= op_array->num_args /* NULL must not be visible in backtraces */ - && !(ssa->cfg.flags & ZEND_FUNC_VARARG) - && !(ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER) - && ssa->cfg.blocks[ssa->cfg.map[use]].loop_header == -1) - || (next_use >= 0 - && op_array->opcodes[next_use].opcode == ZEND_ASSIGN - && ssa->ops[next_use].op1_use == v - && ssa->ops[next_use].op2_use >= 0 - && call_map[use] - && call_map[use]->caller_call_opline + 1 == op_array->opcodes + next_use - && ssa->ops[call_map[use]->caller_call_opline - op_array->opcodes].result_def == ssa->ops[next_use].op2_use)) { - ZEND_ASSERT(op_array->opcodes[use].extended_value == 0); - op_array->opcodes[use].extended_value = 1; + 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"); } } } @@ -1169,9 +1195,9 @@ 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. Pass depends on CFG & call graph. */ - if (call_map && op_array->function_name) { - zend_dfa_optimize_send_copies(op_array, ssa, call_map); + /* 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++) { diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c index 219738e6f692b..8cba36ffa77bf 100644 --- a/Zend/Optimizer/zend_cfg.c +++ b/Zend/Optimizer/zend_cfg.c @@ -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; diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h index 93d455060686e..3094a6dbf47e8 100644 --- a/Zend/Optimizer/zend_cfg.h +++ b/Zend/Optimizer/zend_cfg.h @@ -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() diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 67165a9b26d7a..2072e41debc9d 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -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) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index bfd2b2b8cc67e..5a16f7a930f40 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -9931,7 +9931,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->extended_value /* extended_valu 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_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; @@ -9953,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 diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 266b043d809ea..4286880cd2f91 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -49802,6 +49802,25 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + 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_TAIL_CALL(ZEND_SEND_REF_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); + } + + varptr = EX_VAR(opline->op1.var); + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + + ZVAL_COPY_VALUE(arg, varptr); + ZVAL_UNDEF(varptr); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56780,6 +56799,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_LABEL, + (void*)&&ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_LABEL, (void*)&&ZEND_SEND_VAL_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_LABEL, (void*)&&ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_LABEL, @@ -61126,6 +61146,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED): + VM_TRACE(ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_DIV_SPEC_CV_CV): VM_TRACE(ZEND_DIV_SPEC_CV_CV) ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -64889,6 +64913,7 @@ void zend_vm_init(void) ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED_HANDLER, + ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED_HANDLER, ZEND_SEND_VAL_SIMPLE_SPEC_CONST_HANDLER, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_HANDLER, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER, @@ -64941,7 +64966,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3471, + 3472, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -65099,59 +65124,59 @@ void zend_vm_init(void) 2564, 2565, 2566, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, - 3471, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, + 3472, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -65528,17 +65553,19 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3467; + spec = 3468; } break; case ZEND_SEND_VAR_EX: - if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { + if (!op->extended_value && op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { spec = 3462 | SPEC_RULE_OP1; + } else if (op->extended_value && op->op2.num <= MAX_ARG_FLAG_NUM /* extended_value implies here OP2 UNUSED and OP1 not UNDEF or REF */) { + spec = 3467; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3469 | SPEC_RULE_RETVAL; + spec = 3470 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -65551,7 +65578,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3468; + spec = 3469; } break; case ZEND_SEND_VAR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index f6943a3a2ce80..38c1dc3eae3b3 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1853,8 +1853,9 @@ _(3461, ZEND_SEND_VAR_SIMPLE_EXT_SPEC_CV) \ _(3464, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ _(3466, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3467, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3468, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3469, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3470+1, ZEND_NULL) + _(3467, ZEND_SEND_VAR_EX_SIMPLE_EXT_SPEC_CV_UNUSED) \ + _(3468, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3469, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3470, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3471, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3471+1, ZEND_NULL) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 231c976d0bab7..832e2139d2abb 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -156,7 +156,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, zend_jit_addr res_addr, bool check_exception); -static bool dominates(const 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; } From b81d3790cae28038b26844fdfaf46e0e21a0880a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 22 Apr 2023 13:44:55 +0200 Subject: [PATCH 3/4] Implementation using NO_VAL Fixup DFG check condition Re-enable other benches Tweak Fix regression --- Zend/Optimizer/dfa_pass.c | 73 ++++++++++++--------------------- Zend/Optimizer/sccp.c | 27 ++++++++++-- Zend/Optimizer/zend_cfg.c | 2 +- Zend/Optimizer/zend_cfg.h | 1 - Zend/Optimizer/zend_dfg.c | 4 ++ Zend/Optimizer/zend_inference.c | 6 +++ Zend/Optimizer/zend_ssa.c | 22 +++++++++- Zend/Optimizer/zend_ssa.h | 1 + benchmark/benchmark.php | 11 ++--- ext/opcache/jit/zend_jit.c | 2 +- 10 files changed, 90 insertions(+), 59 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index bb68317ea1940..12e7fd0b188cf 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -466,7 +466,10 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) int var_num = ssa_op->op1_use; zend_ssa_var *var = ssa->vars + var_num; - ZEND_ASSERT(ssa_op->op1_def < 0); + if (ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_op1_use(ssa, ssa_op); + } + zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); ssa_op->op1_use = -1; ssa_op->op1_use_chain = -1; @@ -1066,46 +1069,35 @@ 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) +static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa) { - /* func_get_args() etc could make the optimization observable */ + /* func_get_args(), indirect accesses and exceptions could make the optimization observable. + * The latter two cases are already tested before applying the DFA pass. */ 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]; + 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; } - /* NULL must not be visible in backtraces */ - int ssa_cv = ssa->ops[i].op1_use; + zend_ssa_op *ssa_op = ssa->ops + i; + int op1_def = ssa_op->op1_def; + if (op1_def == -1) { + continue; + } + + int ssa_cv = ssa_op->op1_use; + +#if 0 + /* Argument move must not be observable 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. */ @@ -1114,25 +1106,11 @@ static void zend_dfa_optimize_send_copies(zend_op_array *op_array, const zend_ss 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"); - } + zend_ssa_var *ssa_var = ssa->vars + op1_def; + + if (ssa_var->no_val && !ssa_var->alias) { + /* Flag will be used by VM type spec handler */ + opline->extended_value = 1; } } } @@ -1198,6 +1176,9 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx /* Optimization should not be done on main because of globals. */ if (op_array->function_name) { zend_dfa_optimize_send_copies(op_array, ssa); +#if ZEND_DEBUG_DFA + ssa_verify_integrity(op_array, ssa, "after optimize send copies"); +#endif } for (v = op_array->last_var; v < ssa->vars_count; v++) { diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index f6144f87b4fac..ce15d570eb1e4 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -98,7 +98,7 @@ typedef struct _sccp_ctx { #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP) #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT) -static void scp_dump_value(zval *zv) { +static void scp_dump_value(const zval *zv) { if (IS_TOP(zv)) { fprintf(stderr, " top"); } else if (IS_BOT(zv)) { @@ -1050,6 +1050,12 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o case ZEND_SEND_VAL: case ZEND_SEND_VAR: { + SKIP_IF_TOP(op1); + + if (opline->opcode == ZEND_SEND_VAR) { + SET_RESULT(op1, op1); + } + /* If the value of a SEND for an ICALL changes, we need to reconsider the * ICALL result value. Otherwise we can ignore the opcode. */ zend_call_info *call; @@ -1058,7 +1064,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - if (IS_TOP(op1) || !call || !call->caller_call_opline + if (!call || !call->caller_call_opline || call->caller_call_opline->opcode != ZEND_DO_ICALL) { return; } @@ -2034,8 +2040,14 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) &ssa->ops[call->caller_init_opline - op_array->opcodes]); for (i = 0; i < call->num_args; i++) { - zend_ssa_remove_instr(ssa, call->arg_info[i].opline, - &ssa->ops[call->arg_info[i].opline - op_array->opcodes]); + zend_op *op = call->arg_info[i].opline; + zend_ssa_op *this_ssa_op = &ssa->ops[op - op_array->opcodes]; + + if (op->opcode == ZEND_SEND_VAR && this_ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_op1_use(ssa, this_ssa_op); + } + + zend_ssa_remove_instr(ssa, op, this_ssa_op); } // TODO: remove call_info completely??? @@ -2188,6 +2200,10 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, return 0; } + if (opline->opcode == ZEND_SEND_VAR) { + return 0; + } + /* Compound assign or incdec -> convert to direct ASSIGN */ if (!value) { @@ -2330,6 +2346,9 @@ static int replace_constant_operands(sccp_ctx *ctx) { FOREACH_USE(var, use) { zend_op *opline = &op_array->opcodes[use]; zend_ssa_op *ssa_op = &ssa->ops[use]; + if (opline->opcode == ZEND_SEND_VAR && ssa_op->op1_use == i && ssa_op->op1_def >= 0) { + zend_ssa_replace_op1_def_op1_use(ssa, ssa_op); + } if (try_replace_op1(ctx, opline, ssa_op, i, value)) { if (opline->opcode == ZEND_NOP) { removed_ops++; diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c index 8cba36ffa77bf..219738e6f692b 100644 --- a/Zend/Optimizer/zend_cfg.c +++ b/Zend/Optimizer/zend_cfg.c @@ -761,7 +761,7 @@ ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, ze } /* }}} */ -bool dominates(const zend_basic_block *blocks, int a, int b) /* {{{ */ +static bool dominates(zend_basic_block *blocks, int a, int b) /* {{{ */ { while (blocks[b].level > blocks[a].level) { b = blocks[b].idom; diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h index 3094a6dbf47e8..93d455060686e 100644 --- a/Zend/Optimizer/zend_cfg.h +++ b/Zend/Optimizer/zend_cfg.h @@ -120,7 +120,6 @@ 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() diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c index 2207b594b85a5..73cc205cd5374 100644 --- a/Zend/Optimizer/zend_dfg.c +++ b/Zend/Optimizer/zend_dfg.c @@ -174,6 +174,10 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_ } break; case ZEND_SEND_VAR: + if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { + goto add_op1_def; + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 944283af71abc..688ba17960fcf 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2950,6 +2950,9 @@ static zend_always_inline zend_result _zend_update_type_info( if (t1 & (MAY_BE_RC1|MAY_BE_REF)) { tmp |= MAY_BE_RCN; } + if ((t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { + tmp |= MAY_BE_UNDEF; + } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); } @@ -2991,6 +2994,9 @@ static zend_always_inline zend_result _zend_update_type_info( case ZEND_SEND_FUNC_ARG: if (ssa_op->op1_def >= 0) { tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + if (opline->opcode == ZEND_SEND_VAR_EX && (t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { + tmp |= MAY_BE_UNDEF; + } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); } break; diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 2072e41debc9d..e3a1b4d21f8db 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -18,13 +18,19 @@ */ #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) { @@ -697,6 +703,10 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, } break; case ZEND_SEND_VAR: + if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { + goto add_op1_def; + } + break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: @@ -1674,3 +1684,13 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types old_var->phi_use_chain = NULL; } /* }}} */ + +void zend_ssa_replace_op1_def_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op) +{ + int op1_new = ssa_op->op1_use; + ZEND_ASSERT(op1_new >= 0); + ZEND_ASSERT(ssa_op->op1_def >= 0); + /* zend_ssa_rename_var_uses() clear use_chain & phi_use_chain for us */ + zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, op1_new, true); + zend_ssa_remove_op1_def(ssa, ssa_op); +} diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h index 5a6fce38d2f81..4541d465b2951 100644 --- a/Zend/Optimizer/zend_ssa.h +++ b/Zend/Optimizer/zend_ssa.h @@ -159,6 +159,7 @@ void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num); void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b); void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types); void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int b); +void zend_ssa_replace_op1_def_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op); static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var) { diff --git a/benchmark/benchmark.php b/benchmark/benchmark.php index 9ca015f0a223b..4e92b7a225257 100644 --- a/benchmark/benchmark.php +++ b/benchmark/benchmark.php @@ -13,12 +13,12 @@ function main() { global $storeResult; $data = []; - $data['Zend/bench.php'] = runBench(false); +/* $data['Zend/bench.php'] = runBench(false); $data['Zend/bench.php JIT'] = runBench(true); $data['Symfony Demo 2.2.3'] = runSymfonyDemo(false); - $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true); + $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true);*/ $data['Wordpress 6.2'] = runWordpress(false); - $data['Wordpress 6.2 JIT'] = runWordpress(true); +// $data['Wordpress 6.2 JIT'] = runWordpress(true); $result = json_encode($data, JSON_PRETTY_PRINT) . "\n"; fwrite(STDOUT, $result); @@ -78,7 +78,7 @@ function runWordpress(bool $jit): array { // Warmup runPhpCommand([$dir . '/index.php'], $dir); - return runValgrindPhpCgiCommand([$dir . '/index.php'], cwd: $dir, jit: $jit, warmup: 50, repeat: 50); + return runValgrindPhpCgiCommand([$dir . '/index.php'], cwd: $dir, jit: $jit, warmup: 2, repeat: 2); } function runPhpCommand(array $args, ?string $cwd = null): ProcessResult { @@ -97,11 +97,12 @@ function runValgrindPhpCgiCommand( 'valgrind', '--tool=callgrind', '--dump-instr=yes', - '--callgrind-out-file=/dev/null', + //'--callgrind-out-file=/dev/null', '--', $phpCgi, '-T' . ($warmup ? $warmup . ',' : '') . $repeat, '-d max_execution_time=0', + '-c', '/run/media/niels/MoreData/php-src', '-d opcache.enable=1', '-d opcache.jit_buffer_size=' . ($jit ? '128M' : '0'), '-d opcache.validate_timestamps=0', diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 832e2139d2abb..231c976d0bab7 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -156,7 +156,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, zend_jit_addr res_addr, bool check_exception); -bool dominates(const zend_basic_block *blocks, int a, int b) { +static bool dominates(const zend_basic_block *blocks, int a, int b) { while (blocks[b].level > blocks[a].level) { b = blocks[b].idom; } From dc8badf71b392cee66e5b94d847a7168031cbd3f Mon Sep 17 00:00:00 2001 From: nielsdos <7771979+nielsdos@users.noreply.github.com> Date: Wed, 26 Apr 2023 16:44:41 +0200 Subject: [PATCH 4/4] Revert "Implementation using NO_VAL", testing This reverts commit b81d3790cae28038b26844fdfaf46e0e21a0880a. --- Zend/Optimizer/dfa_pass.c | 73 +++++++++++++++++++++------------ Zend/Optimizer/sccp.c | 27 ++---------- Zend/Optimizer/zend_cfg.c | 2 +- Zend/Optimizer/zend_cfg.h | 1 + Zend/Optimizer/zend_dfg.c | 4 -- Zend/Optimizer/zend_inference.c | 6 --- Zend/Optimizer/zend_ssa.c | 22 +--------- Zend/Optimizer/zend_ssa.h | 1 - benchmark/benchmark.php | 11 +++-- ext/opcache/jit/zend_jit.c | 2 +- 10 files changed, 59 insertions(+), 90 deletions(-) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 12e7fd0b188cf..8801bd3e71ebc 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -466,10 +466,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) int var_num = ssa_op->op1_use; zend_ssa_var *var = ssa->vars + var_num; - if (ssa_op->op1_def >= 0) { - zend_ssa_replace_op1_def_op1_use(ssa, ssa_op); - } - + ZEND_ASSERT(ssa_op->op1_def < 0); zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); ssa_op->op1_use = -1; ssa_op->op1_use_chain = -1; @@ -1069,36 +1066,47 @@ 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, zend_ssa *ssa) +static void zend_dfa_optimize_send_copies(zend_op_array *op_array, const zend_ssa *ssa) { - /* func_get_args(), indirect accesses and exceptions could make the optimization observable. - * The latter two cases are already tested before applying the DFA pass. */ + /* 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++) { - zend_op *opline = op_array->opcodes + 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; } - - zend_ssa_op *ssa_op = ssa->ops + i; - int op1_def = ssa_op->op1_def; - if (op1_def == -1) { - continue; - } - - int ssa_cv = ssa_op->op1_use; - + int ssa_cv = ssa->ops[i].op1_use; #if 0 - /* Argument move must not be observable in backtraces */ + /* 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; @@ -1106,11 +1114,25 @@ static void zend_dfa_optimize_send_copies(zend_op_array *op_array, zend_ssa *ssa continue; } - zend_ssa_var *ssa_var = ssa->vars + op1_def; - - if (ssa_var->no_val && !ssa_var->alias) { - /* Flag will be used by VM type spec handler */ - opline->extended_value = 1; + 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"); + } } } } @@ -1176,9 +1198,6 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx /* Optimization should not be done on main because of globals. */ if (op_array->function_name) { zend_dfa_optimize_send_copies(op_array, ssa); -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after optimize send copies"); -#endif } for (v = op_array->last_var; v < ssa->vars_count; v++) { diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index ce15d570eb1e4..f6144f87b4fac 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -98,7 +98,7 @@ typedef struct _sccp_ctx { #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP) #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT) -static void scp_dump_value(const zval *zv) { +static void scp_dump_value(zval *zv) { if (IS_TOP(zv)) { fprintf(stderr, " top"); } else if (IS_BOT(zv)) { @@ -1050,12 +1050,6 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o case ZEND_SEND_VAL: case ZEND_SEND_VAR: { - SKIP_IF_TOP(op1); - - if (opline->opcode == ZEND_SEND_VAR) { - SET_RESULT(op1, op1); - } - /* If the value of a SEND for an ICALL changes, we need to reconsider the * ICALL result value. Otherwise we can ignore the opcode. */ zend_call_info *call; @@ -1064,7 +1058,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o } call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - if (!call || !call->caller_call_opline + if (IS_TOP(op1) || !call || !call->caller_call_opline || call->caller_call_opline->opcode != ZEND_DO_ICALL) { return; } @@ -2040,14 +2034,8 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) &ssa->ops[call->caller_init_opline - op_array->opcodes]); for (i = 0; i < call->num_args; i++) { - zend_op *op = call->arg_info[i].opline; - zend_ssa_op *this_ssa_op = &ssa->ops[op - op_array->opcodes]; - - if (op->opcode == ZEND_SEND_VAR && this_ssa_op->op1_def >= 0) { - zend_ssa_replace_op1_def_op1_use(ssa, this_ssa_op); - } - - zend_ssa_remove_instr(ssa, op, this_ssa_op); + zend_ssa_remove_instr(ssa, call->arg_info[i].opline, + &ssa->ops[call->arg_info[i].opline - op_array->opcodes]); } // TODO: remove call_info completely??? @@ -2200,10 +2188,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, return 0; } - if (opline->opcode == ZEND_SEND_VAR) { - return 0; - } - /* Compound assign or incdec -> convert to direct ASSIGN */ if (!value) { @@ -2346,9 +2330,6 @@ static int replace_constant_operands(sccp_ctx *ctx) { FOREACH_USE(var, use) { zend_op *opline = &op_array->opcodes[use]; zend_ssa_op *ssa_op = &ssa->ops[use]; - if (opline->opcode == ZEND_SEND_VAR && ssa_op->op1_use == i && ssa_op->op1_def >= 0) { - zend_ssa_replace_op1_def_op1_use(ssa, ssa_op); - } if (try_replace_op1(ctx, opline, ssa_op, i, value)) { if (opline->opcode == ZEND_NOP) { removed_ops++; diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c index 219738e6f692b..8cba36ffa77bf 100644 --- a/Zend/Optimizer/zend_cfg.c +++ b/Zend/Optimizer/zend_cfg.c @@ -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; diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h index 93d455060686e..3094a6dbf47e8 100644 --- a/Zend/Optimizer/zend_cfg.h +++ b/Zend/Optimizer/zend_cfg.h @@ -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() diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c index 73cc205cd5374..2207b594b85a5 100644 --- a/Zend/Optimizer/zend_dfg.c +++ b/Zend/Optimizer/zend_dfg.c @@ -174,10 +174,6 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_ } break; case ZEND_SEND_VAR: - if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { - goto add_op1_def; - } - break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 688ba17960fcf..944283af71abc 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2950,9 +2950,6 @@ static zend_always_inline zend_result _zend_update_type_info( if (t1 & (MAY_BE_RC1|MAY_BE_REF)) { tmp |= MAY_BE_RCN; } - if ((t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { - tmp |= MAY_BE_UNDEF; - } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); } @@ -2994,9 +2991,6 @@ static zend_always_inline zend_result _zend_update_type_info( case ZEND_SEND_FUNC_ARG: if (ssa_op->op1_def >= 0) { tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - if (opline->opcode == ZEND_SEND_VAR_EX && (t1 & (MAY_BE_ARRAY|MAY_BE_STRING)) && (t1 & MAY_BE_RC1) && !(t1 & (MAY_BE_UNDEF|MAY_BE_REF)) && ssa_vars[ssa_op->op1_def].no_val && !ssa_vars[ssa_op->op1_def].alias) { - tmp |= MAY_BE_UNDEF; - } UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); } break; diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index e3a1b4d21f8db..2072e41debc9d 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -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) { @@ -703,10 +697,6 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, } break; case ZEND_SEND_VAR: - if (opline->op1_type == IS_CV && ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->op2_type == IS_UNUSED)) { - goto add_op1_def; - } - break; case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: @@ -1684,13 +1674,3 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types old_var->phi_use_chain = NULL; } /* }}} */ - -void zend_ssa_replace_op1_def_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op) -{ - int op1_new = ssa_op->op1_use; - ZEND_ASSERT(op1_new >= 0); - ZEND_ASSERT(ssa_op->op1_def >= 0); - /* zend_ssa_rename_var_uses() clear use_chain & phi_use_chain for us */ - zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, op1_new, true); - zend_ssa_remove_op1_def(ssa, ssa_op); -} diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h index 4541d465b2951..5a6fce38d2f81 100644 --- a/Zend/Optimizer/zend_ssa.h +++ b/Zend/Optimizer/zend_ssa.h @@ -159,7 +159,6 @@ void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num); void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b); void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types); void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int b); -void zend_ssa_replace_op1_def_op1_use(zend_ssa *ssa, zend_ssa_op *ssa_op); static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var) { diff --git a/benchmark/benchmark.php b/benchmark/benchmark.php index 4e92b7a225257..9ca015f0a223b 100644 --- a/benchmark/benchmark.php +++ b/benchmark/benchmark.php @@ -13,12 +13,12 @@ function main() { global $storeResult; $data = []; -/* $data['Zend/bench.php'] = runBench(false); + $data['Zend/bench.php'] = runBench(false); $data['Zend/bench.php JIT'] = runBench(true); $data['Symfony Demo 2.2.3'] = runSymfonyDemo(false); - $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true);*/ + $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true); $data['Wordpress 6.2'] = runWordpress(false); -// $data['Wordpress 6.2 JIT'] = runWordpress(true); + $data['Wordpress 6.2 JIT'] = runWordpress(true); $result = json_encode($data, JSON_PRETTY_PRINT) . "\n"; fwrite(STDOUT, $result); @@ -78,7 +78,7 @@ function runWordpress(bool $jit): array { // Warmup runPhpCommand([$dir . '/index.php'], $dir); - return runValgrindPhpCgiCommand([$dir . '/index.php'], cwd: $dir, jit: $jit, warmup: 2, repeat: 2); + return runValgrindPhpCgiCommand([$dir . '/index.php'], cwd: $dir, jit: $jit, warmup: 50, repeat: 50); } function runPhpCommand(array $args, ?string $cwd = null): ProcessResult { @@ -97,12 +97,11 @@ function runValgrindPhpCgiCommand( 'valgrind', '--tool=callgrind', '--dump-instr=yes', - //'--callgrind-out-file=/dev/null', + '--callgrind-out-file=/dev/null', '--', $phpCgi, '-T' . ($warmup ? $warmup . ',' : '') . $repeat, '-d max_execution_time=0', - '-c', '/run/media/niels/MoreData/php-src', '-d opcache.enable=1', '-d opcache.jit_buffer_size=' . ($jit ? '128M' : '0'), '-d opcache.validate_timestamps=0', diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 231c976d0bab7..832e2139d2abb 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -156,7 +156,7 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, zend_jit_addr res_addr, bool check_exception); -static bool dominates(const 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; }