mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix GH-15656: php8.4beta4 JIT erronous results (#15732)
* Improve trace SSA construction and type inference * Fix incorrect abstract stack maintenance * Add missing register store * Avoid IR binding for the dangerous case * Fix access to possibly uninitilezed variable * Improve trace SSA construction and type inference * Fix IR constuction Force load values into regesters before any branches to guarantee SSA dominance property
This commit is contained in:
@@ -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;
|
||||
@@ -5563,6 +5573,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);
|
||||
@@ -6090,6 +6113,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);
|
||||
}
|
||||
|
||||
@@ -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 @@ propagate_arg:
|
||||
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 @@ propagate_arg:
|
||||
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 @@ propagate_arg:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user