op1_def_info, OP1_DEF_REG_ADDR(),
res_use_info, res_info,
res_addr,
- (op1_def_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, 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,
- (res_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, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
if (!zend_jit_assign_op(&dasm_state, opline, op_array,
op1_info, op1_def_info, OP1_RANGE(),
op2_info, OP2_RANGE(),
- (op1_def_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, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
if ((ssa->vars[i].use_chain >= 0 /*|| ssa->vars[i].phi_use_chain*/)
&& zend_jit_var_supports_reg(ssa, i)) {
start[i] = 0;
- if (i < parent_vars_count && STACK_REG(parent_stack, i) != ZREG_NONE) {
+ if (i < parent_vars_count
+ && STACK_REG(parent_stack, i) != ZREG_NONE
+ && STACK_REG(parent_stack, i) < ZREG_NUM) {
/* We will try to reuse register from parent trace */
count += 2;
} else {
}
while (i > 0) {
i--;
- if (intervals[i] && STACK_REG(parent_stack, i) != ZREG_NONE) {
+ if (intervals[i]
+ && STACK_REG(parent_stack, i) != ZREG_NONE
+ && STACK_REG(parent_stack, i) < ZREG_NUM) {
list[j].ssa_var = - 1;
list[j].reg = STACK_REG(parent_stack, i);
list[j].flags = 0;
if (ra && ra[i] && ra[i]->reg == STACK_REG(parent_stack, i)) {
/* register already loaded by parent trace */
SET_STACK_REG(stack, i, ra[i]->reg);
- } else if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
- goto jit_failure;
+ } else if (STACK_REG(parent_stack, i) < ZREG_NUM) {
+ if (!zend_jit_store_var(&dasm_state, ssa->var_info[i].type, i, STACK_REG(parent_stack, i))) {
+ goto jit_failure;
+ }
+ } else {
+ SET_STACK_REG(stack, i, ZREG_NONE);
+ if (!zend_jit_store_const(&dasm_state, i, STACK_REG(parent_stack, i))) {
+ goto jit_failure;
+ }
}
}
}
op1_def_info, OP1_DEF_REG_ADDR(),
res_use_info, res_info,
res_addr,
- (op1_def_info & MAY_BE_LONG) && (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_ex(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,
- (res_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_ex(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, op_array,
op1_info, op1_def_info, OP1_RANGE(),
op2_info, OP2_RANGE(),
- (op1_def_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_ex(opline, ssa_op, op_array, ssa),
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
for (i = 0; i < op_array->last_var + op_array->T; i++) {
if (STACK_REG(stack, i) != ZREG_NONE) {
// TODO: optimize out useless stores ????
- if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
- goto jit_failure;
+ if (STACK_REG(stack, i) < ZREG_NUM) {
+ if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
+ goto jit_failure;
+ }
+ } else {
+ if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
+ goto jit_failure;
+ }
}
}
}
stack = zend_jit_traces[trace_num].stack_map + zend_jit_traces[trace_num].exit_info[exit_num].stack_offset;
for (i = 0; i < stack_size; i++) {
if (STACK_REG(stack, i) != ZREG_NONE) {
- if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
- goto jit_failure;
+ if (STACK_REG(stack, i) < ZREG_NUM) {
+ if (!zend_jit_store_var(&dasm_state, 1 << STACK_TYPE(stack, i), i, STACK_REG(stack, i))) {
+ goto jit_failure;
+ }
+ } else {
+ if (!zend_jit_store_const(&dasm_state, i, STACK_REG(stack, i))) {
+ goto jit_failure;
+ }
}
}
}
} else {
fprintf(stderr, "%s", zend_get_type_by_const(type));
if (STACK_REG(stack, j) != ZREG_NONE) {
- fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
+ if (STACK_REG(stack, j) < ZREG_NUM) {
+ fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
+ } else {
+ fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
+ }
}
}
}
for (i = 0; i < stack_size; i++) {
if (STACK_REG(stack, i) != ZREG_NONE) {
if (STACK_TYPE(stack, i) == IS_LONG) {
- ZVAL_LONG(EX_VAR_NUM(i), regs->r[STACK_REG(stack, i)]);
+ zend_long val;
+
+ if (STACK_REG(stack, i) < ZREG_NUM) {
+ val = regs->r[STACK_REG(stack, i)];
+ } else if (STACK_REG(stack, i) == ZREG_LONG_MIN) {
+ val = ZEND_LONG_MIN;
+ } else if (STACK_REG(stack, i) == ZREG_LONG_MAX) {
+ val = ZEND_LONG_MAX;
+ } else {
+ ZEND_ASSERT(0);
+ }
+ ZVAL_LONG(EX_VAR_NUM(i), val);
} else if (STACK_TYPE(stack, i) == IS_DOUBLE) {
- ZVAL_DOUBLE(EX_VAR_NUM(i), regs->xmm[STACK_REG(stack, i) - ZREG_XMM0]);
+ double val;
+
+ if (STACK_REG(stack, i) < ZREG_NUM) {
+ val = regs->xmm[STACK_REG(stack, i) - ZREG_XMM0];
+ } else if (STACK_REG(stack, i) == ZREG_LONG_MIN_MINUS_1) {
+ val = (double)ZEND_LONG_MIN - 1.0;
+ } else if (STACK_REG(stack, i) == ZREG_LONG_MAX_PLUS_1) {
+ val = (double)ZEND_LONG_MAX + 1.0;
+ } else {
+ ZEND_ASSERT(0);
+ }
+ ZVAL_DOUBLE(EX_VAR_NUM(i), val);
} else {
ZEND_ASSERT(0);
}
return 1;
}
+static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
+{
+ zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var));
+
+ if (reg == ZREG_LONG_MIN_MINUS_1) {
+ |.if X64
+ | SET_ZVAL_LVAL dst, 0x00000000
+ | SET_ZVAL_W2 dst, 0xc3e00000
+ |.else
+ | SET_ZVAL_LVAL dst, 0x00200000
+ | SET_ZVAL_W2 dst, 0xc1e00000
+ |.endif
+ | SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
+ } else if (reg == ZREG_LONG_MIN) {
+ |.if X64
+ | SET_ZVAL_LVAL dst, 0x00000000
+ | SET_ZVAL_W2 dst, 0x80000000
+ |.else
+ | SET_ZVAL_LVAL dst, ZEND_LONG_MIN
+ |.endif
+ | SET_ZVAL_TYPE_INFO dst, IS_LONG
+ } else if (reg == ZREG_LONG_MAX) {
+ |.if X64
+ | SET_ZVAL_LVAL dst, 0xffffffff
+ | SET_ZVAL_W2 dst, 0x7fffffff
+ |.else
+ | SET_ZVAL_LVAL dst, ZEND_LONG_MAX
+ |.endif
+ | SET_ZVAL_TYPE_INFO dst, IS_LONG
+ } else if (reg == ZREG_LONG_MAX_PLUS_1) {
+ |.if X64
+ | SET_ZVAL_LVAL dst, 0
+ | SET_ZVAL_W2 dst, 0x43e00000
+ |.else
+ | SET_ZVAL_LVAL dst, 0
+ | SET_ZVAL_W2 dst, 0x41e00000
+ |.endif
+ | SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
+ } else {
+ ZEND_ASSERT(0);
+ }
+ return 1;
+}
+
static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op1_def_info, zend_jit_addr op1_def_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_overflow, int may_throw)
{
if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_LONG)) {
| LONG_OP_WITH_CONST sub, op1_def_addr, Z_L(1)
}
- if (may_overflow && ((op1_def_info & MAY_BE_GUARD) || (opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD)))) {
- int32_t exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, 0);
- const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
+ if (may_overflow &&
+ (((op1_def_info & MAY_BE_GUARD) && (op1_def_info & MAY_BE_LONG)) ||
+ ((opline->result_type != IS_UNUSED && (res_info & MAY_BE_GUARD) && (res_info & MAY_BE_LONG))))) {
+ int32_t exit_point;
+ const void *exit_addr;
+ zend_jit_trace_stack *stack;
+ uint32_t old_op1_info, old_res_info;
+
+ stack = JIT_G(current_frame)->stack;
+ old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_DOUBLE);
+ if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MAX_PLUS_1);
+ } else {
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_LONG_MIN_MINUS_1);
+ }
+ if (opline->result_type != IS_UNUSED) {
+ old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
+ if (opline->opcode == ZEND_PRE_INC) {
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX_PLUS_1);
+ } else if (opline->opcode == ZEND_PRE_DEC) {
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN_MINUS_1);
+ } else if (opline->opcode == ZEND_POST_INC) {
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MAX);
+ } else if (opline->opcode == ZEND_POST_DEC) {
+ SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG);
+ SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_LONG_MIN);
+ }
+ }
+
+ exit_point = zend_jit_trace_get_exit_point(opline, opline + 1, NULL, 0);
+ exit_addr = zend_jit_trace_get_exit_addr(exit_point);
| jo &exit_addr
+
if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
opline->result_type != IS_UNUSED) {
| ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_LONG, ZREG_R0, ZREG_R1
}
+
+ SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
+ if (opline->result_type != IS_UNUSED) {
+ SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
+ }
} else if (may_overflow) {
| jo >1
if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
| SET_ZVAL_W2 op1_def_addr, 0xc1e00000
|.endif
}
- | SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
+ if (Z_MODE(op1_def_addr) == IS_MEM_ZVAL) {
+ | SET_ZVAL_TYPE_INFO op1_def_addr, IS_DOUBLE
+ }
if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
opline->result_type != IS_UNUSED) {
| ZVAL_COPY_VALUE res_addr, res_use_info, op1_def_addr, MAY_BE_DOUBLE, ZREG_R0, ZREG_R1
ZREG_XMM15,
#endif
- ZREG_NUM
+ ZREG_NUM,
+
+ /* pseudo constants used by deoptimizer */
+ ZREG_LONG_MIN_MINUS_1,
+ ZREG_LONG_MIN,
+ ZREG_LONG_MAX,
+ ZREG_LONG_MAX_PLUS_1,
} zend_reg;
typedef struct _zend_jit_registers_buf {