summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2020-10-05 22:33:45 +0300
committerDmitry Stogov <dmitry@zend.com>2020-10-05 22:33:45 +0300
commit31f54586b5464c2232d2e628a816b85b663eb877 (patch)
tree5f263d1d2eb5f1c42f66c5e630e3b71ba9f3cb6a
parent90b80c82787f42b5ded3e313b93f55f865643cf6 (diff)
downloadphp-git-31f54586b5464c2232d2e628a816b85b663eb877.tar.gz
Use range inference to eliminate useless comparisons
-rw-r--r--ext/opcache/jit/zend_jit.c9
-rw-r--r--ext/opcache/jit/zend_jit_trace.c48
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc156
3 files changed, 188 insertions, 25 deletions
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 7c9a8bb7cf..ad1f835bd5 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -192,6 +192,7 @@ static zend_bool zend_long_is_power_of_two(zend_long x)
#define OP_RANGE(ssa_op, opN) \
(((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
+ ssa->var_info && \
(ssa_op)->opN##_use >= 0 && \
ssa->var_info[(ssa_op)->opN##_use].has_range) ? \
&ssa->var_info[(ssa_op)->opN##_use].range : NULL)
@@ -2796,8 +2797,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
target_label = target_label2 = (uint32_t)-1;
}
if (!zend_jit_cmp(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(),
- OP2_INFO(), OP2_REG_ADDR(),
+ OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
+ OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
res_addr,
zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, target_label, target_label2,
@@ -2825,8 +2826,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
target_label = target_label2 = (uint32_t)-1;
}
if (!zend_jit_identical(&dasm_state, opline,
- OP1_INFO(), OP1_REG_ADDR(),
- OP2_INFO(), OP2_REG_ADDR(),
+ OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
+ OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
RES_REG_ADDR(),
zend_may_throw(opline, ssa_op, op_array, ssa),
smart_branch_opcode, target_label, target_label2,
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 751e492a4f..11f66b9b06 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -4299,18 +4299,26 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto jit_failure;
}
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+ if (!zend_jit_cmp(&dasm_state, opline,
+ op1_info, OP1_RANGE(), OP1_REG_ADDR(),
+ op2_info, OP2_RANGE(), OP2_REG_ADDR(),
+ RES_REG_ADDR(),
+ zend_may_throw(opline, ssa_op, op_array, ssa),
+ smart_branch_opcode, -1, -1, exit_addr)) {
+ goto jit_failure;
+ }
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else {
smart_branch_opcode = 0;
exit_addr = NULL;
- }
- if (!zend_jit_cmp(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(),
- op2_info, OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
+ if (!zend_jit_cmp(&dasm_state, opline,
+ op1_info, OP1_RANGE(), OP1_REG_ADDR(),
+ op2_info, OP2_RANGE(), OP2_REG_ADDR(),
+ RES_REG_ADDR(),
+ zend_may_throw(opline, ssa_op, op_array, ssa),
+ smart_branch_opcode, -1, -1, exit_addr)) {
+ goto jit_failure;
+ }
}
goto done;
case ZEND_IS_IDENTICAL:
@@ -4337,18 +4345,26 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
exit_if_true = !exit_if_true;
}
smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+ if (!zend_jit_identical(&dasm_state, opline,
+ op1_info, OP1_RANGE(), OP1_REG_ADDR(),
+ op2_info, OP2_RANGE(), OP2_REG_ADDR(),
+ RES_REG_ADDR(),
+ zend_may_throw(opline, ssa_op, op_array, ssa),
+ smart_branch_opcode, -1, -1, exit_addr)) {
+ goto jit_failure;
+ }
zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
} else {
smart_branch_opcode = 0;
exit_addr = NULL;
- }
- if (!zend_jit_identical(&dasm_state, opline,
- op1_info, OP1_REG_ADDR(),
- op2_info, OP2_REG_ADDR(),
- RES_REG_ADDR(),
- zend_may_throw(opline, ssa_op, op_array, ssa),
- smart_branch_opcode, -1, -1, exit_addr)) {
- goto jit_failure;
+ if (!zend_jit_identical(&dasm_state, opline,
+ op1_info, OP1_RANGE(), OP1_REG_ADDR(),
+ op2_info, OP2_RANGE(), OP2_REG_ADDR(),
+ RES_REG_ADDR(),
+ zend_may_throw(opline, ssa_op, op_array, ssa),
+ smart_branch_opcode, -1, -1, exit_addr)) {
+ goto jit_failure;
+ }
}
goto done;
case ZEND_DEFINED:
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index 5e097e27e7..d930ea0585 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -6572,9 +6572,129 @@ static int zend_jit_assign_op(dasm_State **Dst, const zend_op *opline, uint32_t
return result;
}
-static int zend_jit_cmp_long_long(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
+ zend_ssa_range *op1_range,
+ zend_jit_addr op1_addr,
+ zend_ssa_range *op2_range,
+ zend_jit_addr op2_addr,
+ zend_bool *result)
+{
+ zend_long op1_min;
+ zend_long op1_max;
+ zend_long op2_min;
+ zend_long op2_max;
+
+ if (op1_range) {
+ op1_min = op1_range->min;
+ op1_max = op1_range->max;
+ } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
+ ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
+ op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
+ } else {
+ return 0;
+ }
+
+ if (op2_range) {
+ op2_min = op2_range->min;
+ op2_max = op2_range->max;
+ } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
+ ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
+ op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
+ } else {
+ return 0;
+ }
+
+ switch (opline->opcode) {
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_CASE:
+ case ZEND_CASE_STRICT:
+ if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
+ *result = 1;
+ return 1;
+ } else if (op1_max < op2_min || op1_min > op2_max) {
+ *result = 0;
+ return 1;
+ }
+ return 0;
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
+ *result = 0;
+ return 1;
+ } else if (op1_max < op2_min || op1_min > op2_max) {
+ *result = 1;
+ return 1;
+ }
+ return 0;
+ case ZEND_IS_SMALLER:
+ if (op1_max < op2_min) {
+ *result = 1;
+ return 1;
+ } else if (op1_min >= op2_max) {
+ *result = 0;
+ return 1;
+ }
+ return 0;
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ if (op1_max <= op2_min) {
+ *result = 1;
+ return 1;
+ } else if (op1_min > op2_max) {
+ *result = 0;
+ return 1;
+ }
+ return 0;
+ default:
+ ZEND_UNREACHABLE();
+ }
+ return 0;
+}
+
+static int zend_jit_cmp_long_long(dasm_State **Dst,
+ const zend_op *opline,
+ zend_ssa_range *op1_range,
+ zend_jit_addr op1_addr,
+ zend_ssa_range *op2_range,
+ zend_jit_addr op2_addr,
+ zend_jit_addr res_addr,
+ zend_uchar smart_branch_opcode,
+ uint32_t target_label,
+ uint32_t target_label2,
+ const void *exit_addr)
{
zend_bool swap = 0;
+ zend_bool result;
+
+ if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) {
+ if (!smart_branch_opcode ||
+ smart_branch_opcode == ZEND_JMPZ_EX ||
+ smart_branch_opcode == ZEND_JMPNZ_EX) {
+ | SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE)
+ }
+ if (smart_branch_opcode && !exit_addr) {
+ if (smart_branch_opcode == ZEND_JMPZ ||
+ smart_branch_opcode == ZEND_JMPZ_EX) {
+ if (!result) {
+ | jmp => target_label
+ }
+ } else if (smart_branch_opcode == ZEND_JMPNZ ||
+ smart_branch_opcode == ZEND_JMPNZ_EX) {
+ if (result) {
+ | jmp => target_label
+ }
+ } else if (smart_branch_opcode == ZEND_JMPZNZ) {
+ if (!result) {
+ | jmp => target_label
+ } else {
+ | jmp => target_label2
+ }
+ } else {
+ ZEND_UNREACHABLE();
+ }
+ }
+ return 1;
+ }
if (Z_MODE(op1_addr) == IS_REG) {
if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) {
@@ -7350,7 +7470,20 @@ static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_a
return 1;
}
-static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+static int zend_jit_cmp(dasm_State **Dst,
+ const zend_op *opline,
+ uint32_t op1_info,
+ zend_ssa_range *op1_range,
+ zend_jit_addr op1_addr,
+ uint32_t op2_info,
+ zend_ssa_range *op2_range,
+ zend_jit_addr op2_addr,
+ zend_jit_addr res_addr,
+ int may_throw,
+ zend_uchar smart_branch_opcode,
+ uint32_t target_label,
+ uint32_t target_label2,
+ const void *exit_addr)
{
zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var);
zend_bool has_slow;
@@ -7386,7 +7519,7 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in
| IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9
}
}
- if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+ if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
return 0;
}
if (op1_info & MAY_BE_DOUBLE) {
@@ -7575,7 +7708,20 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in
return 1;
}
-static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr)
+static int zend_jit_identical(dasm_State **Dst,
+ const zend_op *opline,
+ uint32_t op1_info,
+ zend_ssa_range *op1_range,
+ zend_jit_addr op1_addr,
+ uint32_t op2_info,
+ zend_ssa_range *op2_range,
+ zend_jit_addr op2_addr,
+ zend_jit_addr res_addr,
+ int may_throw,
+ zend_uchar smart_branch_opcode,
+ uint32_t target_label,
+ uint32_t target_label2,
+ const void *exit_addr)
{
uint32_t identical_label = (uint32_t)-1;
uint32_t not_identical_label = (uint32_t)-1;
@@ -7608,7 +7754,7 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t
if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG &&
(op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) {
- if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
+ if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
return 0;
}
return 1;