Skip to content

Commit

Permalink
Optimizing LONG MUL to SHIFT: add overflow detection for x86
Browse files Browse the repository at this point in the history
Similar to the AArch64 implementation, we compare "op" and "op << n >>
n" to detect integer overflow.

Note that instructions 'pushf' and 'popf' are used to save/restore the
flags set by 'cmp', otherwise, the follow-up 'shl' might change the
flags.

Note that test case "mul_004.phpt" can pass now for x86.

Change-Id: Ieee037f41dce298979b8a0964ca97598939c495c
  • Loading branch information
shqking authored and dstogov committed May 18, 2021
1 parent 999720b commit 5e05c70
Showing 1 changed file with 55 additions and 6 deletions.
61 changes: 55 additions & 6 deletions ext/opcache/jit/zend_jit_x86.dasc
Expand Up @@ -4180,6 +4180,7 @@ static int zend_jit_math_long_long(dasm_State **Dst,
bool same_ops = zend_jit_same_addr(op1_addr, op2_addr);
zend_reg result_reg;
zend_reg tmp_reg = ZREG_R0;
bool use_ovf_flag = 1;

if (Z_MODE(res_addr) == IS_REG && (res_info & MAY_BE_LONG)) {
if (may_overflow && (res_info & MAY_BE_GUARD)
Expand Down Expand Up @@ -4211,7 +4212,23 @@ static int zend_jit_math_long_long(dasm_State **Dst,
| GET_ZVAL_LVAL result_reg, op1_addr
| shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
if (may_overflow) {
// TODO: check overflow.
/* Compare 'op' and '((op << n) >> n)' for overflow.
* Flag: jne -> overflow. je -> no overflow.
*/
use_ovf_flag = 0;
| sar Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
| cmp Ra(result_reg), Z_LVAL_P(Z_ZV(op1_addr))
} else if (Z_MODE(op1_addr) == IS_MEM_ZVAL) {
| cmp Ra(result_reg), [Ra(Z_REG(op1_addr))+Z_OFFSET(op1_addr)]
} else if (Z_MODE(op1_addr) == IS_REG) {
| cmp Ra(result_reg), Ra(Z_REG(op1_addr))
} else {
ZEND_UNREACHABLE();
}
| pushf
| shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op2_addr)))
| popf
}
}
} else if (opcode == ZEND_MUL &&
Expand All @@ -4226,7 +4243,23 @@ static int zend_jit_math_long_long(dasm_State **Dst,
| GET_ZVAL_LVAL result_reg, op2_addr
| shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
if (may_overflow) {
// TODO: check overflow.
/* Compare 'op' and '((op << n) >> n)' for overflow.
* Flag: jne -> overflow. je -> no overflow.
*/
use_ovf_flag = 0;
| sar Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
| cmp Ra(result_reg), Z_LVAL_P(Z_ZV(op2_addr))
} else if (Z_MODE(op2_addr) == IS_MEM_ZVAL) {
| cmp Ra(result_reg), [Ra(Z_REG(op2_addr))+Z_OFFSET(op2_addr)]
} else if (Z_MODE(op2_addr) == IS_REG) {
| cmp Ra(result_reg), Ra(Z_REG(op2_addr))
} else {
ZEND_UNREACHABLE();
}
| pushf
| shl Ra(result_reg), floor_log2(Z_LVAL_P(Z_ZV(op1_addr)))
| popf
}
}
} else if (opcode == ZEND_DIV &&
Expand Down Expand Up @@ -4267,20 +4300,36 @@ static int zend_jit_math_long_long(dasm_State **Dst,
int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0);
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) {
| jo &exit_addr
if (use_ovf_flag) {
| jo &exit_addr
} else {
| jne &exit_addr
}
if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) {
| mov Ra(Z_REG(res_addr)), Ra(result_reg)
}
} else if ((res_info & MAY_BE_ANY) == MAY_BE_DOUBLE) {
| jno &exit_addr
if (use_ovf_flag) {
| jno &exit_addr
} else {
| je &exit_addr
}
} else {
ZEND_UNREACHABLE();
}
} else {
if (res_info & MAY_BE_LONG) {
| jo >1
if (use_ovf_flag) {
| jo >1
} else {
| jne >1
}
} else {
| jno >1
if (use_ovf_flag) {
| jno >1
} else {
| je >1
}
}
}
}
Expand Down

0 comments on commit 5e05c70

Please sign in to comment.