Use range inference to eliminate useless comparisons

This commit is contained in:
Dmitry Stogov 2020-10-05 22:33:45 +03:00
parent 90b80c8278
commit 31f54586b5
3 changed files with 188 additions and 25 deletions

View file

@ -192,6 +192,7 @@ static zend_bool zend_long_is_power_of_two(zend_long x)
#define OP_RANGE(ssa_op, opN) \ #define OP_RANGE(ssa_op, opN) \
(((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \ (((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
ssa->var_info && \
(ssa_op)->opN##_use >= 0 && \ (ssa_op)->opN##_use >= 0 && \
ssa->var_info[(ssa_op)->opN##_use].has_range) ? \ ssa->var_info[(ssa_op)->opN##_use].has_range) ? \
&ssa->var_info[(ssa_op)->opN##_use].range : NULL) &ssa->var_info[(ssa_op)->opN##_use].range : NULL)
@ -2796,8 +2797,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
target_label = target_label2 = (uint32_t)-1; target_label = target_label2 = (uint32_t)-1;
} }
if (!zend_jit_cmp(&dasm_state, opline, if (!zend_jit_cmp(&dasm_state, opline,
OP1_INFO(), OP1_REG_ADDR(), OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
OP2_INFO(), OP2_REG_ADDR(), OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
res_addr, res_addr,
zend_may_throw(opline, ssa_op, op_array, ssa), zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, target_label, target_label2, smart_branch_opcode, target_label, target_label2,
@ -2825,8 +2826,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
target_label = target_label2 = (uint32_t)-1; target_label = target_label2 = (uint32_t)-1;
} }
if (!zend_jit_identical(&dasm_state, opline, if (!zend_jit_identical(&dasm_state, opline,
OP1_INFO(), OP1_REG_ADDR(), OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
OP2_INFO(), OP2_REG_ADDR(), OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
RES_REG_ADDR(), RES_REG_ADDR(),
zend_may_throw(opline, ssa_op, op_array, ssa), zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, target_label, target_label2, smart_branch_opcode, target_label, target_label2,

View file

@ -4299,18 +4299,26 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto jit_failure; goto jit_failure;
} }
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ; smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
if (!zend_jit_cmp(&dasm_state, opline,
op1_info, OP1_RANGE(), OP1_REG_ADDR(),
op2_info, OP2_RANGE(), OP2_REG_ADDR(),
RES_REG_ADDR(),
zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, -1, -1, exit_addr)) {
goto jit_failure;
}
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else { } else {
smart_branch_opcode = 0; smart_branch_opcode = 0;
exit_addr = NULL; exit_addr = NULL;
} if (!zend_jit_cmp(&dasm_state, opline,
if (!zend_jit_cmp(&dasm_state, opline, op1_info, OP1_RANGE(), OP1_REG_ADDR(),
op1_info, OP1_REG_ADDR(), op2_info, OP2_RANGE(), OP2_REG_ADDR(),
op2_info, OP2_REG_ADDR(), RES_REG_ADDR(),
RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, -1, -1, exit_addr)) {
smart_branch_opcode, -1, -1, exit_addr)) { goto jit_failure;
goto jit_failure; }
} }
goto done; goto done;
case ZEND_IS_IDENTICAL: case ZEND_IS_IDENTICAL:
@ -4337,18 +4345,26 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
exit_if_true = !exit_if_true; exit_if_true = !exit_if_true;
} }
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ; smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
if (!zend_jit_identical(&dasm_state, opline,
op1_info, OP1_RANGE(), OP1_REG_ADDR(),
op2_info, OP2_RANGE(), OP2_REG_ADDR(),
RES_REG_ADDR(),
zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, -1, -1, exit_addr)) {
goto jit_failure;
}
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else { } else {
smart_branch_opcode = 0; smart_branch_opcode = 0;
exit_addr = NULL; exit_addr = NULL;
} if (!zend_jit_identical(&dasm_state, opline,
if (!zend_jit_identical(&dasm_state, opline, op1_info, OP1_RANGE(), OP1_REG_ADDR(),
op1_info, OP1_REG_ADDR(), op2_info, OP2_RANGE(), OP2_REG_ADDR(),
op2_info, OP2_REG_ADDR(), RES_REG_ADDR(),
RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, -1, -1, exit_addr)) {
smart_branch_opcode, -1, -1, exit_addr)) { goto jit_failure;
goto jit_failure; }
} }
goto done; goto done;
case ZEND_DEFINED: case ZEND_DEFINED:

View file

@ -6572,9 +6572,129 @@ static int zend_jit_assign_op(dasm_State **Dst, const zend_op *opline, uint32_t
return result; return result;
} }
static int zend_jit_cmp_long_long(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
zend_ssa_range *op1_range,
zend_jit_addr op1_addr,
zend_ssa_range *op2_range,
zend_jit_addr op2_addr,
zend_bool *result)
{
zend_long op1_min;
zend_long op1_max;
zend_long op2_min;
zend_long op2_max;
if (op1_range) {
op1_min = op1_range->min;
op1_max = op1_range->max;
} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
} else {
return 0;
}
if (op2_range) {
op2_min = op2_range->min;
op2_max = op2_range->max;
} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
} else {
return 0;
}
switch (opline->opcode) {
case ZEND_IS_EQUAL:
case ZEND_IS_IDENTICAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
*result = 1;
return 1;
} else if (op1_max < op2_min || op1_min > op2_max) {
*result = 0;
return 1;
}
return 0;
case ZEND_IS_NOT_EQUAL:
case ZEND_IS_NOT_IDENTICAL:
if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
*result = 0;
return 1;
} else if (op1_max < op2_min || op1_min > op2_max) {
*result = 1;
return 1;
}
return 0;
case ZEND_IS_SMALLER:
if (op1_max < op2_min) {
*result = 1;
return 1;
} else if (op1_min >= op2_max) {
*result = 0;
return 1;
}
return 0;
case ZEND_IS_SMALLER_OR_EQUAL:
if (op1_max <= op2_min) {
*result = 1;
return 1;
} else if (op1_min > op2_max) {
*result = 0;
return 1;
}
return 0;
default:
ZEND_UNREACHABLE();
}
return 0;
}
static int zend_jit_cmp_long_long(dasm_State **Dst,
const zend_op *opline,
zend_ssa_range *op1_range,
zend_jit_addr op1_addr,
zend_ssa_range *op2_range,
zend_jit_addr op2_addr,
zend_jit_addr res_addr,
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr)
{ {
zend_bool swap = 0; zend_bool swap = 0;
zend_bool result;
if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
if (!smart_branch_opcode ||
smart_branch_opcode == ZEND_JMPZ_EX ||
smart_branch_opcode == ZEND_JMPNZ_EX) {
| SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE)
}
if (smart_branch_opcode && !exit_addr) {
if (smart_branch_opcode == ZEND_JMPZ ||
smart_branch_opcode == ZEND_JMPZ_EX) {
if (!result) {
| jmp => target_label
}
} else if (smart_branch_opcode == ZEND_JMPNZ ||
smart_branch_opcode == ZEND_JMPNZ_EX) {
if (result) {
| jmp => target_label
}
} else if (smart_branch_opcode == ZEND_JMPZNZ) {
if (!result) {
| jmp => target_label
} else {
| jmp => target_label2
}
} else {
ZEND_UNREACHABLE();
}
}
return 1;
}
if (Z_MODE(op1_addr) == IS_REG) { if (Z_MODE(op1_addr) == IS_REG) {
if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
@ -7350,7 +7470,20 @@ static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_a
return 1; return 1;
} }
static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) static int zend_jit_cmp(dasm_State **Dst,
const zend_op *opline,
uint32_t op1_info,
zend_ssa_range *op1_range,
zend_jit_addr op1_addr,
uint32_t op2_info,
zend_ssa_range *op2_range,
zend_jit_addr op2_addr,
zend_jit_addr res_addr,
int may_throw,
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr)
{ {
zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var); zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
zend_bool has_slow; zend_bool has_slow;
@ -7386,7 +7519,7 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in
| IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9 | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
} }
} }
if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
return 0; return 0;
} }
if (op1_info & MAY_BE_DOUBLE) { if (op1_info & MAY_BE_DOUBLE) {
@ -7575,7 +7708,20 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in
return 1; return 1;
} }
static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) static int zend_jit_identical(dasm_State **Dst,
const zend_op *opline,
uint32_t op1_info,
zend_ssa_range *op1_range,
zend_jit_addr op1_addr,
uint32_t op2_info,
zend_ssa_range *op2_range,
zend_jit_addr op2_addr,
zend_jit_addr res_addr,
int may_throw,
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
const void *exit_addr)
{ {
uint32_t identical_label = (uint32_t)-1; uint32_t identical_label = (uint32_t)-1;
uint32_t not_identical_label = (uint32_t)-1; uint32_t not_identical_label = (uint32_t)-1;
@ -7608,7 +7754,7 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t
if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
(op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
return 0; return 0;
} }
return 1; return 1;