static const void *zend_jit_ret_trace_counter_handler = NULL;
static const void *zend_jit_loop_trace_counter_handler = NULL;
-static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa);
static void ZEND_FASTCALL zend_runtime_jit(void);
static int zend_jit_trace_op_len(const zend_op *opline);
return entry;
}
-static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
+static int zend_may_overflow(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
{
int res;
+ zend_long op1_min, op1_max, op2_min, op2_max;
if (!ssa->ops || !ssa->var_info) {
return 1;
case ZEND_PRE_INC:
case ZEND_POST_INC:
res = ssa_op->op1_def;
- return (res < 0 ||
- !ssa->var_info[res].has_range ||
- ssa->var_info[res].range.overflow);
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.overflow) {
+ if (!OP1_HAS_RANGE()) {
+ return 1;
+ }
+ op1_max = OP1_MAX_RANGE();
+ if (op1_max == ZEND_LONG_MAX) {
+ return 1;
+ }
+ }
+ return 0;
case ZEND_PRE_DEC:
case ZEND_POST_DEC:
res = ssa_op->op1_def;
- return (res < 0 ||
- !ssa->var_info[res].has_range ||
- ssa->var_info[res].range.underflow);
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.underflow) {
+ if (!OP1_HAS_RANGE()) {
+ return 1;
+ }
+ op1_min = OP1_MIN_RANGE();
+ if (op1_min == ZEND_LONG_MIN) {
+ return 1;
+ }
+ }
+ return 0;
case ZEND_ADD:
res = ssa_op->result_def;
- if (res < 0 ||
- !ssa->var_info[res].has_range) {
- return 1;
- }
- if (ssa->var_info[res].range.underflow) {
- zend_long op1_min, op2_min;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.underflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 1;
}
}
- if (ssa->var_info[res].range.overflow) {
- zend_long op1_max, op2_max;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.overflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 0;
case ZEND_SUB:
res = ssa_op->result_def;
- if (res < 0 ||
- !ssa->var_info[res].has_range) {
- return 1;
- }
- if (ssa->var_info[res].range.underflow) {
- zend_long op1_min, op2_max;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.underflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 1;
}
}
- if (ssa->var_info[res].range.overflow) {
- zend_long op1_max, op2_min;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.overflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
case ZEND_ASSIGN_OP:
if (opline->extended_value == ZEND_ADD) {
res = ssa_op->op1_def;
- if (res < 0 ||
- !ssa->var_info[res].has_range) {
- return 1;
- }
- if (ssa->var_info[res].range.underflow) {
- zend_long op1_min, op2_min;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.underflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 1;
}
}
- if (ssa->var_info[res].range.overflow) {
- zend_long op1_max, op2_max;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.overflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 0;
} else if (opline->extended_value == ZEND_SUB) {
res = ssa_op->op1_def;
- if (res < 0 ||
- !ssa->var_info[res].has_range) {
- return 1;
- }
- if (ssa->var_info[res].range.underflow) {
- zend_long op1_min, op2_max;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.underflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
return 1;
}
}
- if (ssa->var_info[res].range.overflow) {
- zend_long op1_max, op2_min;
-
+ if (res < 0
+ || !ssa->var_info[res].has_range
+ || ssa->var_info[res].range.overflow) {
if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
return 1;
}
}
}
-static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
-{
- return zend_may_overflow_ex(opline, &ssa->ops[opline - op_array->opcodes], op_array, ssa);
-}
-
static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg)
{
uint32_t flags;
op1_def_info, OP1_DEF_REG_ADDR(),
res_use_info, res_info,
res_addr,
- (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+ (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
op2_info, OP2_REG_ADDR(),
res_use_info, res_info, res_addr,
send_result,
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+ (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
if (!zend_jit_assign_op(&dasm_state, opline,
op1_info, op1_def_info, OP1_RANGE(),
op2_info, OP2_RANGE(),
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+ (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
return 1;
}
+static void zend_jit_trace_set_var_range(zend_ssa_var_info *info, zend_long min, zend_long max)
+{
+ info->has_range = 1;
+ info->range.min = min;
+ info->range.max = max;
+ info->range.underflow = 0;
+ info->range.overflow = 0;
+}
+
+static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, zend_bool exit_if_true)
+{
+ zend_long op1_min, op1_max, op2_min, op2_max;
+
+ if ((OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG
+ || (OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG) {
+ return;
+ }
+
+ op1_min = OP1_MIN_RANGE();
+ op1_max = OP1_MAX_RANGE();
+ op2_min = OP2_MIN_RANGE();
+ op2_max = OP2_MAX_RANGE();
+
+ switch (opline->opcode) {
+ case ZEND_IS_EQUAL:
+ case ZEND_CASE:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_CASE_STRICT:
+ if (!exit_if_true) {
+ /* op1 == op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ MAX(op1_min, op2_min),
+ MIN(op1_max, op2_max));
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ MAX(op2_min, op1_min),
+ MIN(op2_max, op1_max));
+ }
+ }
+ break;
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ if (exit_if_true) {
+ /* op1 == op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ MAX(op1_min, op2_min),
+ MIN(op1_max, op2_max));
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ MAX(op2_min, op1_min),
+ MIN(op2_max, op1_max));
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (!exit_if_true) {
+ /* op1 <= op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ op1_min,
+ MIN(op1_max, op2_max));
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ MAX(op2_min, op1_min),
+ op2_max);
+ }
+ } else {
+ /* op1 > op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ op2_min != ZEND_LONG_MAX ? MAX(op1_min, op2_min + 1) : op1_min,
+ op1_max);
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ op2_min,
+ op2_max != ZEND_LONG_MIN ?MIN(op2_max, op1_max - 1) : op1_max);
+ }
+ }
+ break;
+ case ZEND_IS_SMALLER:
+ if (!exit_if_true) {
+ /* op1 < op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ op1_min,
+ op2_max != ZEND_LONG_MIN ? MIN(op1_max, op2_max - 1) : op1_max);
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ op1_min != ZEND_LONG_MAX ? MAX(op2_min, op1_min + 1) : op2_min,
+ op2_max);
+ }
+ } else {
+ /* op1 >= op2 */
+ if (ssa_op->op1_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op1_use],
+ MAX(op1_min, op2_min),
+ op1_max);
+ }
+ if (ssa_op->op2_use >= 0) {
+ zend_jit_trace_set_var_range(
+ &ssa->var_info[ssa_op->op2_use],
+ op2_min,
+ MIN(op2_max, op1_max));
+ }
+ }
+ break;
+ }
+}
+
static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
{
const void *handler = NULL;
op1_def_info, OP1_DEF_REG_ADDR(),
res_use_info, res_info,
res_addr,
- (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+ (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
op2_info, OP2_REG_ADDR(),
res_use_info, res_info, res_addr,
send_result,
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+ (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
if (!zend_jit_assign_op(&dasm_state, opline,
op1_info, op1_def_info, OP1_RANGE(),
op2_info, OP2_RANGE(),
- (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+ (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
goto jit_failure;
}
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+ zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else {
smart_branch_opcode = 0;
exit_addr = NULL;
exit_if_true = !exit_if_true;
}
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+ zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else {
smart_branch_opcode = 0;
exit_addr = NULL;