1
0
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:
Dmitry Stogov
2024-09-09 09:28:57 +03:00
committed by GitHub
parent 837a8b60bf
commit 4c11168f60
2 changed files with 93 additions and 9 deletions

View File

@@ -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);
}

View File

@@ -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