}
}
+static zend_bool zend_jit_may_skip_comparison(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, const zend_op **ssa_opcodes)
+{
+ zend_uchar prev_opcode;
+
+ if (opline->op1_type == IS_CONST
+ && Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_LONG
+ && Z_LVAL_P(RT_CONSTANT(opline, opline->op1)) == 0) {
+ if (ssa_op->op2_use >= 0) {
+ if ((ssa_op-1)->op1_def == ssa_op->op2_use) {
+ prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode;
+ if (prev_opcode == ZEND_PRE_INC
+ || prev_opcode == ZEND_PRE_DEC
+ || prev_opcode == ZEND_POST_INC
+ || prev_opcode == ZEND_POST_DEC) {
+ return 1;
+ }
+ } else if ((ssa_op-1)->result_def == ssa_op->op2_use) {
+ prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode;
+ if (prev_opcode == ZEND_ADD
+ || prev_opcode == ZEND_SUB) {
+ return 1;
+ }
+ }
+ }
+ } else if (opline->op2_type == IS_CONST
+ && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG
+ && Z_LVAL_P(RT_CONSTANT(opline, opline->op2)) == 0) {
+ if (ssa_op->op1_use >= 0) {
+ if ((ssa_op-1)->op1_def == ssa_op->op1_use) {
+ prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode;
+ if (prev_opcode == ZEND_PRE_INC
+ || prev_opcode == ZEND_PRE_DEC
+ || prev_opcode == ZEND_POST_INC
+ || prev_opcode == ZEND_POST_DEC) {
+ return 1;
+ }
+ } else if ((ssa_op-1)->result_def == ssa_op->op1_use) {
+ prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode;
+ if (prev_opcode == ZEND_ADD
+ || prev_opcode == ZEND_SUB) {
+ return 1;
+ }
+ }
+ }
+ } else {
+ const zend_ssa_op *prev_ssa_op = ssa_op - 1;
+ prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode;
+
+ if ((prev_opcode == ZEND_JMPZ || prev_opcode == ZEND_JMPNZ)
+ && prev_ssa_op != ssa->ops
+ && prev_ssa_op->op1_use >= 0
+ && prev_ssa_op->op1_use == (prev_ssa_op-1)->result_def) {
+ prev_ssa_op--;
+ prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode;
+ }
+
+ if (ssa_op->op1_use == prev_ssa_op->op1_use
+ && ssa_op->op2_use == prev_ssa_op->op2_use) {
+ if (prev_opcode == ZEND_IS_EQUAL
+ || prev_opcode == ZEND_IS_NOT_EQUAL
+ || prev_opcode == ZEND_IS_SMALLER
+ || prev_opcode == ZEND_IS_SMALLER_OR_EQUAL
+ || prev_opcode == ZEND_CASE
+ || prev_opcode == ZEND_IS_IDENTICAL
+ || prev_opcode == ZEND_IS_NOT_IDENTICAL
+ || prev_opcode == ZEND_CASE_STRICT) {
+ if (ssa_op->op1_use < 0) {
+ if (opline->op1.constant != ssa_opcodes[prev_ssa_op - ssa->ops]->op1.constant) {
+ return 0;
+ }
+ }
+ if (ssa_op->op2_use < 0) {
+ if (opline->op2.constant != ssa_opcodes[prev_ssa_op - ssa->ops]->op2.constant) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
{
const void *handler = NULL;
const void *exit_addr;
uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info;
zend_bool send_result = 0;
+ zend_bool skip_comparison;
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
zend_class_entry *ce;
zend_bool ce_is_instanceof;
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_CASE:
op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
op2_info = OP2_INFO();
+ skip_comparison =
+ ssa_op != ssa->ops &&
+ (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
+ (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
+ zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes);
+ CHECK_OP1_TRACE_TYPE();
CHECK_OP2_TRACE_TYPE();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
zend_bool exit_if_true = 0;
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)) {
+ smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
goto jit_failure;
}
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
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)) {
+ smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
goto jit_failure;
}
}
case ZEND_IS_NOT_IDENTICAL:
case ZEND_CASE_STRICT:
op1_info = OP1_INFO();
- CHECK_OP1_TRACE_TYPE();
op2_info = OP2_INFO();
+ skip_comparison =
+ ssa_op != ssa->ops &&
+ (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
+ (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
+ zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes);
+ CHECK_OP1_TRACE_TYPE();
CHECK_OP2_TRACE_TYPE();
if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
zend_bool exit_if_true = 0;
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)) {
+ smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
goto jit_failure;
}
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
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)) {
+ smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) {
goto jit_failure;
}
}
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
- const void *exit_addr)
+ const void *exit_addr,
+ zend_bool skip_comparison)
{
zend_bool swap = 0;
zend_bool result;
return 1;
}
- if (Z_MODE(op1_addr) == IS_REG) {
+ if (skip_comparison) {
+ if (Z_MODE(op1_addr) != IS_REG &&
+ (Z_MODE(op2_addr) == IS_REG ||
+ (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) {
+ swap = 1;
+ }
+ } else if (Z_MODE(op1_addr) == IS_REG) {
if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
| test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr))
} else {
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
- const void *exit_addr)
+ const void *exit_addr,
+ zend_bool skip_comparison)
{
zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
zend_bool has_slow;
| IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
}
}
- 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)) {
+ 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, skip_comparison)) {
return 0;
}
if (op1_info & MAY_BE_DOUBLE) {
zend_uchar smart_branch_opcode,
uint32_t target_label,
uint32_t target_label2,
- const void *exit_addr)
+ const void *exit_addr,
+ zend_bool skip_comparison)
{
uint32_t identical_label = (uint32_t)-1;
uint32_t not_identical_label = (uint32_t)-1;
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) {
- 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)) {
+ 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, skip_comparison)) {
return 0;
}
return 1;