diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 7e231ccf664c7..e0fa41ea7e4a8 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1289,6 +1289,16 @@ static bool zend_jit_spilling_may_cause_conflict(zend_jit_ctx *jit, int var, ir_ // } if (jit->ssa->vars[var].var < jit->current_op_array->last_var) { /* IS_CV */ + if (jit->ctx.ir_base[val].op == IR_LOAD + && jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op == IR_ADD + && jit->ctx.ir_base[jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op1].op == IR_RLOAD + && jit->ctx.ir_base[jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op1].op2 == ZREG_FP + && IR_IS_CONST_REF(jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op2) + && jit->ctx.ir_base[jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op2].val.addr != (uintptr_t)EX_NUM_TO_VAR(jit->ssa->vars[var].var) + && EX_VAR_TO_NUM(jit->ctx.ir_base[jit->ctx.ir_base[jit->ctx.ir_base[val].op2].op2].val.addr) < jit->current_op_array->last_var) { + /* binding between different CVs may cause spill conflict */ + return 1; + } return 0; } return 1; @@ -5561,6 +5571,19 @@ static int zend_jit_long_math_helper(zend_jit_ctx *jit, ir_refs_init(res_inputs, 2); + if (Z_MODE(op1_addr) == IS_REG + && Z_LOAD(op1_addr) + && jit->ra[Z_SSA_VAR(op1_addr)].ref == IR_NULL) { + /* Force load */ + zend_jit_use_reg(jit, op1_addr); + } + if (Z_MODE(op2_addr) == IS_REG + && Z_LOAD(op2_addr) + && jit->ra[Z_SSA_VAR(op2_addr)].ref == IR_NULL) { + /* Force load */ + zend_jit_use_reg(jit, op2_addr); + } + if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) { if_long1 = jit_if_Z_TYPE(jit, op1_addr, IS_LONG); ir_IF_TRUE(if_long1); @@ -6088,6 +6111,10 @@ static int zend_jit_assign_op(zend_jit_ctx *jit, ZEND_UNREACHABLE(); } + if (!zend_jit_store_var_if_necessary_ex(jit, opline->op1.var, op1_def_addr, op1_def_info, op1_addr, op1_info)) { + return 0; + } + if (op1_info & MAY_BE_REF) { ir_MERGE_WITH(slow_path); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 69187ca3814ee..79c89c80b8212 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -805,7 +805,12 @@ static bool zend_jit_trace_is_false_loop(const zend_op_array *op_array, const ze } } -static int zend_jit_trace_copy_ssa_var_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op **tssa_opcodes, zend_ssa *tssa, int ssa_var) +static int zend_jit_trace_copy_ssa_var_info(const zend_op_array *op_array, + const zend_ssa *ssa, + const zend_op **tssa_opcodes, + zend_ssa *tssa, + int ssa_var, + const zend_op *opline) { int var, use, def, src; zend_ssa_op *op; @@ -913,6 +918,54 @@ static int zend_jit_trace_copy_ssa_var_info(const zend_op_array *op_array, const assert(0); return 0; } + if (opline) { + /* Try to find a difinition in SSA dominators tree */ + var = tssa->vars[ssa_var].var; + uint32_t op_num = opline - op_array->opcodes; + uint32_t b = ssa->cfg.map[op_num]; + zend_basic_block *bb = ssa->cfg.blocks + b; + zend_ssa_phi *pi, *phi; + + while (1) { + while (op_num > bb->start) { + op_num--; + op = ssa->ops + op_num; + if (op->result_def >= 0 && ssa->vars[op->result_def].var == var) { + src = op->result_def; + goto copy_info; + } else if (op->op2_def >= 0 && ssa->vars[op->op2_def].var == var) { + src = op->op2_def; + goto copy_info; + } else if (op->op1_def >= 0 && ssa->vars[op->op1_def].var == var) { + src = op->op1_def; + goto copy_info; + } + } + phi = ssa->blocks[b].phis; + pi = NULL; + while (phi) { + if (ssa->vars[phi->ssa_var].var == var) { + if (phi->pi >= 0) { + pi = phi; + } else { + src = phi->ssa_var; + goto copy_info; + } + } + phi = phi->next; + } + if (pi) { + src = pi->ssa_var; + goto copy_info; + } + if (bb->idom < 0) { + break; + } + b = bb->idom; + bb = ssa->cfg.blocks + b; + op_num = bb->start + bb->len; + } + } goto copy_info; } return 0; @@ -1586,6 +1639,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin /* 4. Type inference */ op_array = trace_buffer->op_array; + opline = trace_buffer[1].opline; jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); ssa = &jit_extension->func_info.ssa; @@ -1597,7 +1651,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin while (i < op_array->last_var) { if (i < op_array->num_args) { if (ssa->var_info - && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i)) { + && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i, NULL)) { /* pass */ } else { if (ssa->vars) { @@ -1652,7 +1706,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin } while (i < op_array->last_var + op_array->T) { if (!ssa->var_info - || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i)) { + || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, i, opline)) { if (ssa->vars && i < ssa->vars_count) { ssa_vars[i].alias = ssa->vars[i].alias; } else { @@ -1690,7 +1744,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin while (phi) { if (!ssa->var_info - || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, phi->ssa_var)) { + || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, phi->ssa_var, NULL)) { ssa_vars[phi->ssa_var].alias = ssa_vars[phi->sources[0]].alias; ssa_var_info[phi->ssa_var].type = ssa_var_info[phi->sources[0]].type; } @@ -2409,7 +2463,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin ssa_vars[v].var = i; if (i < op_array->num_args) { if (ssa->var_info - && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) { + && zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v, NULL)) { /* pass */ } else { ssa_vars[v].alias = zend_jit_var_may_alias(op_array, ssa, i); @@ -2460,7 +2514,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin while (i < op_array->last_var) { ssa_vars[v].var = i; if (!ssa->var_info - || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) { + || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v, NULL)) { ssa_var_info[v].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; } i++; @@ -2469,7 +2523,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin while (i < op_array->last_var + op_array->T) { ssa_vars[v].var = i; if (!ssa->var_info - || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v)) { + || !zend_jit_trace_copy_ssa_var_info(op_array, ssa, ssa_opcodes, tssa, v, NULL)) { ssa_var_info[v].type = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; } i++; @@ -3308,6 +3362,7 @@ static void zend_jit_trace_cleanup_stack(zend_jit_ctx *jit, zend_jit_trace_stack CLEAR_STACK_REF(stack, EX_VAR_TO_NUM(opline->op1.var)); } if (ssa_op->op2_use >= 0 + && ssa_op->op2_use != ssa_op->op1_use && jit->ra[ssa_op->op2_use].ref && (jit->ra[ssa_op->op2_use].flags & ZREG_LAST_USE) && (ssa_op->op2_use_chain == -1 @@ -3315,6 +3370,8 @@ static void zend_jit_trace_cleanup_stack(zend_jit_ctx *jit, zend_jit_trace_stack CLEAR_STACK_REF(stack, EX_VAR_TO_NUM(opline->op2.var)); } if (ssa_op->result_use >= 0 + && ssa_op->result_use != ssa_op->op1_use + && ssa_op->result_use != ssa_op->op2_use && jit->ra[ssa_op->result_use].ref && (jit->ra[ssa_op->result_use].flags & ZREG_LAST_USE) && (ssa_op->res_use_chain == -1 @@ -4143,8 +4200,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } else if (i < parent_vars_count && STACK_TYPE(parent_stack, i) != IS_UNKNOWN) { /* This must be already handled by trace type inference */ - ZEND_UNREACHABLE(); - // SET_STACK_TYPE(stack, i, STACK_TYPE(parent_stack, i)); + ZEND_ASSERT(ssa->vars[i].use_chain < 0 && !ssa->vars[i].phi_use_chain); + SET_STACK_TYPE(stack, i, STACK_TYPE(parent_stack, i), 1); } else if ((info & MAY_BE_GUARD) != 0 && (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL