summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/opcache/jit/zend_jit.c6
-rw-r--r--ext/opcache/jit/zend_jit_trace.c77
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc92
-rw-r--r--ext/opcache/jit/zend_jit_x86.h8
4 files changed, 161 insertions, 22 deletions
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index a3b3661d74..450a037f77 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -2213,7 +2213,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
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;
}
@@ -2295,7 +2295,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
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;
}
@@ -2375,7 +2375,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
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;
}
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index ef8ea3696a..5906aca444 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -1866,7 +1866,9 @@ static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace
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 {
@@ -2197,7 +2199,9 @@ static zend_lifetime_interval** zend_jit_trace_allocate_registers(zend_jit_trace
}
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;
@@ -2602,8 +2606,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
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;
+ }
}
}
}
@@ -2760,7 +2771,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
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;
}
@@ -2882,7 +2893,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
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;
}
@@ -2979,7 +2990,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
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;
}
@@ -4058,8 +4069,14 @@ done:
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;
+ }
}
}
}
@@ -4183,8 +4200,14 @@ static const void *zend_jit_trace_exit_to_vm(uint32_t trace_num, uint32_t exit_n
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;
+ }
}
}
}
@@ -4595,7 +4618,11 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
} 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);
+ }
}
}
}
@@ -5014,9 +5041,31 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
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);
}
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 6bd05b05e4..cf0d74646a 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -3511,6 +3511,50 @@ static int zend_jit_update_regs(dasm_State **Dst, zend_jit_addr src, zend_jit_ad
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)) {
@@ -3528,14 +3572,52 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_
| 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) &&
@@ -3561,7 +3643,9 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, const zend_
| 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
diff --git a/ext/opcache/jit/zend_jit_x86.h b/ext/opcache/jit/zend_jit_x86.h
index 5bf2521815..3368820d44 100644
--- a/ext/opcache/jit/zend_jit_x86.h
+++ b/ext/opcache/jit/zend_jit_x86.h
@@ -62,7 +62,13 @@ typedef enum _zend_reg {
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 {