From 15aa891c5ec3e5254d5af8b51052c898d23a32e7 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 26 May 2020 17:59:15 +0300 Subject: [PATCH] Fixed JIT for integer overflow checks --- ext/opcache/jit/zend_jit.c | 6 +- ext/opcache/jit/zend_jit_trace.c | 77 +++++++++++++++++++++----- ext/opcache/jit/zend_jit_x86.dasc | 92 +++++++++++++++++++++++++++++-- ext/opcache/jit/zend_jit_x86.h | 8 ++- 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 { -- 2.50.1