1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Support for IS_IDENTICAL/IS_NOT_IDENTICAL

This commit is contained in:
Dmitry Stogov
2021-04-21 21:34:34 +03:00
parent c826f3f1df
commit f2c161eda0
3 changed files with 546 additions and 3 deletions

View File

@@ -1078,8 +1078,9 @@ static void* dasm_labels[zend_lb_MAX];
| bne label
|.endmacro
|.macro IF_Z_TYPE, zv, val, label
| IF_TYPE byte [zv+offsetof(zval, u1.v.type)], val, label
|.macro IF_Z_TYPE, zv, val, label, tmp_reg
| ldrb tmp_reg, [zv, #offsetof(zval, u1.v.type)]
| IF_TYPE tmp_reg, val, label
|.endmacro
|.macro IF_NOT_Z_TYPE, zv, val, label, tmp_reg
@@ -6127,7 +6128,387 @@ static int zend_jit_identical(dasm_State **Dst,
uint32_t identical_label = (uint32_t)-1;
uint32_t not_identical_label = (uint32_t)-1;
| NIY // TODO
if (smart_branch_opcode && !exit_addr) {
if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
if (smart_branch_opcode == ZEND_JMPZ) {
not_identical_label = target_label;
} else if (smart_branch_opcode == ZEND_JMPNZ) {
identical_label = target_label;
} else if (smart_branch_opcode == ZEND_JMPZNZ) {
not_identical_label = target_label;
identical_label = target_label2;
} else {
ZEND_UNREACHABLE();
}
} else {
if (smart_branch_opcode == ZEND_JMPZ) {
identical_label = target_label;
} else if (smart_branch_opcode == ZEND_JMPNZ) {
not_identical_label = target_label;
} else if (smart_branch_opcode == ZEND_JMPZNZ) {
identical_label = target_label;
not_identical_label = target_label2;
} else {
ZEND_UNREACHABLE();
}
}
}
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_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr, skip_comparison)) {
return 0;
}
return 1;
} else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE &&
(op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) {
if (!zend_jit_cmp_double_double(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) {
return 0;
}
return 1;
}
if ((op1_info & MAY_BE_UNDEF) && (op2_info & MAY_BE_UNDEF)) {
op1_info |= MAY_BE_NULL;
op2_info |= MAY_BE_NULL;
| LOAD_ZVAL_ADDR FCARG1x, op1_addr
| IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
|.cold_code
|1:
| // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
| SET_EX_OPLINE opline, REG0
| LOAD_32BIT_VAL FCARG1w, opline->op1.var
| EXT_CALL zend_jit_undefined_op_helper, REG0
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
| LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
| b >1
|.code
|1:
| LOAD_ZVAL_ADDR FCARG2x, op2_addr
| IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
|.cold_code
|1:
| // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
| SET_EX_OPLINE opline, REG0
| str FCARG1x, T1 // save
| LOAD_32BIT_VAL FCARG1w, opline->op2.var
| EXT_CALL zend_jit_undefined_op_helper, REG0
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
| ldr FCARG1x, T1 // restore
| LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
| b >1
|.code
|1:
} else if (op1_info & MAY_BE_UNDEF) {
op1_info |= MAY_BE_NULL;
| LOAD_ZVAL_ADDR FCARG1x, op1_addr
| IF_Z_TYPE FCARG1x, IS_UNDEF, >1, TMP1w
|.cold_code
|1:
| // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
| SET_EX_OPLINE opline, REG0
| LOAD_32BIT_VAL FCARG1w, opline->op1.var
| EXT_CALL zend_jit_undefined_op_helper, REG0
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
| LOAD_ADDR_ZTS FCARG1x, executor_globals, uninitialized_zval
| b >1
|.code
|1:
if (opline->op2_type != IS_CONST) {
| LOAD_ZVAL_ADDR FCARG2x, op2_addr
}
} else if (op2_info & MAY_BE_UNDEF) {
op2_info |= MAY_BE_NULL;
| LOAD_ZVAL_ADDR FCARG2x, op2_addr
| IF_Z_TYPE FCARG2x, IS_UNDEF, >1, TMP1w
|.cold_code
|1:
| // zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))));
| SET_EX_OPLINE opline, REG0
| LOAD_32BIT_VAL FCARG1w, opline->op2.var
| EXT_CALL zend_jit_undefined_op_helper, REG0
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
| LOAD_ADDR_ZTS FCARG2x, executor_globals, uninitialized_zval
| b >1
|.code
|1:
if (opline->op1_type != IS_CONST) {
| LOAD_ZVAL_ADDR FCARG1x, op1_addr
}
} else if ((op1_info & op2_info & MAY_BE_ANY) != 0) {
if (opline->op1_type != IS_CONST) {
if (Z_MODE(op1_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op1.var);
if (!zend_jit_spill_store(Dst, op1_addr, real_addr, op1_info, 1)) {
return 0;
}
op1_addr = real_addr;
}
| LOAD_ZVAL_ADDR FCARG1x, op1_addr
}
if (opline->op2_type != IS_CONST) {
if (Z_MODE(op2_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var);
if (!zend_jit_spill_store(Dst, op2_addr, real_addr, op2_info, 1)) {
return 0;
}
op2_addr = real_addr;
}
| LOAD_ZVAL_ADDR FCARG2x, op2_addr
}
}
if ((op1_info & op2_info & MAY_BE_ANY) == 0) {
if ((opline->opcode != ZEND_CASE_STRICT &&
(opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
(op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
| SET_EX_OPLINE opline, REG0
if (opline->opcode != ZEND_CASE_STRICT) {
| FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
}
| FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
}
if (smart_branch_opcode) {
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPZ) {
| NIY // b &exit_addr
}
} else if (not_identical_label != (uint32_t)-1) {
| b =>not_identical_label
}
} else {
| SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
if (may_throw) {
zend_jit_check_exception(Dst);
}
}
return 1;
}
if (opline->op1_type & (IS_CV|IS_VAR)) {
| ZVAL_DEREF FCARG1x, op1_info, TMP1w
}
if (opline->op2_type & (IS_CV|IS_VAR)) {
| ZVAL_DEREF FCARG2x, op2_info, TMP1w
}
if (has_concrete_type(op1_info)
&& has_concrete_type(op2_info)
&& concrete_type(op1_info) == concrete_type(op2_info)
&& concrete_type(op1_info) <= IS_TRUE) {
if (smart_branch_opcode) {
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPNZ) {
| NIY // b &exit_addr
}
} else if (identical_label != (uint32_t)-1) {
| b =>identical_label
}
} else {
| SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
}
} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) == IS_CONST_ZVAL) {
if (zend_is_identical(Z_ZV(op1_addr), Z_ZV(op2_addr))) {
if (smart_branch_opcode) {
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPNZ) {
| NIY // b &exit_addr
}
} else if (identical_label != (uint32_t)-1) {
| b =>identical_label
}
} else {
| SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_TRUE : IS_FALSE), TMP1w, TMP2
}
} else {
if (smart_branch_opcode) {
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPZ) {
| NIY // b &exit_addr
}
} else if (not_identical_label != (uint32_t)-1) {
| b =>not_identical_label
}
} else {
| SET_ZVAL_TYPE_INFO res_addr, (opline->opcode != ZEND_IS_NOT_IDENTICAL ? IS_FALSE : IS_TRUE), TMP1w, TMP2
}
}
} else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op1_addr)) <= IS_TRUE) {
zval *val = Z_ZV(op1_addr);
| ldrb TMP1w, [FCARG2x, #offsetof(zval, u1.v.type)]
| cmp TMP1w, #Z_TYPE_P(val)
if (smart_branch_opcode) {
if (opline->op2_type == IS_VAR && (op2_info & MAY_BE_REF)) {
| bne >8
| SET_EX_OPLINE opline, REG0
| FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
| NIY // b &exit_addr
} else if (identical_label != (uint32_t)-1) {
| b =>identical_label
} else {
| b >9
}
|8:
} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
| NIY // beq &exit_addr
} else if (identical_label != (uint32_t)-1) {
| beq =>identical_label
} else {
| beq >9
}
} else {
if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
| cset REG0w, eq
} else {
| cset REG0w, ne
}
| add REG0w, REG0w, #2
| SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
}
if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
(op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
| SET_EX_OPLINE opline, REG0
| FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
}
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPZ) {
| NIY // b &exit_addr
}
} else if (smart_branch_opcode && not_identical_label != (uint32_t)-1) {
| b =>not_identical_label
}
} else if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_TYPE_P(Z_ZV(op2_addr)) <= IS_TRUE) {
zval *val = Z_ZV(op2_addr);
| ldrb TMP1w, [FCARG1x, #offsetof(zval, u1.v.type)]
| cmp TMP1w, #Z_TYPE_P(val)
if (smart_branch_opcode) {
if (opline->opcode != ZEND_CASE_STRICT
&& opline->op1_type == IS_VAR && (op1_info & MAY_BE_REF)) {
| bne >8
| SET_EX_OPLINE opline, REG0
| FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
| NIY // b &exit_addr
} else if (identical_label != (uint32_t)-1) {
| b =>identical_label
} else {
| b >9
}
|8:
} else if (exit_addr && smart_branch_opcode == ZEND_JMPNZ) {
| NIY // beq &exit_addr
} else if (identical_label != (uint32_t)-1) {
| beq =>identical_label
} else {
| beq >9
}
} else {
if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
| cset REG0w, eq
} else {
| cset REG0w, ne
}
| add REG0w, REG0w, #2
| SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1
}
if (opline->opcode != ZEND_CASE_STRICT
&& (opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
| SET_EX_OPLINE opline, REG0
| FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
}
if (smart_branch_opcode) {
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPZ) {
| NIY // b &exit_addr
}
} else if (not_identical_label != (uint32_t)-1) {
| b =>not_identical_label
}
}
} else {
if (opline->op1_type == IS_CONST) {
| LOAD_ZVAL_ADDR FCARG1x, op1_addr
}
if (opline->op2_type == IS_CONST) {
| LOAD_ZVAL_ADDR FCARG2x, op2_addr
}
| EXT_CALL zend_is_identical, REG0
if ((opline->opcode != ZEND_CASE_STRICT &&
(opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
(op1_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) ||
((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
(op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)))) {
| str REG0, T1 // save
| SET_EX_OPLINE opline, REG0
if (opline->opcode != ZEND_CASE_STRICT) {
| FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline, ZREG_TMP1, ZREG_TMP2
}
| FREE_OP opline->op2_type, opline->op2, op2_info, 1, opline, ZREG_TMP1, ZREG_TMP2
if (may_throw) {
zend_jit_check_exception_undef_result(Dst, opline);
}
| ldr REG0, T1 // restore
}
if (smart_branch_opcode) {
| cmp RETVALw, #0
if (exit_addr) {
if (smart_branch_opcode == ZEND_JMPNZ) {
| NIY // bne &exit_addr
} else {
| NIY // beq &exit_addr
}
} else if (not_identical_label != (uint32_t)-1) {
| beq =>not_identical_label
if (identical_label != (uint32_t)-1) {
| b =>identical_label
}
} else if (identical_label != (uint32_t)-1) {
| bne =>identical_label
}
} else {
if (opline->opcode != ZEND_IS_NOT_IDENTICAL) {
| add RETVALw, RETVALw, #2
} else {
| neg RETVALw, RETVALw
| add RETVALw, RETVALw, #3
}
| SET_ZVAL_TYPE_INFO_FROM_REG res_addr, RETVALw, TMP1
}
}
|9:
if (may_throw) {
zend_jit_check_exception(Dst);
}
return 1;
}

View File

@@ -0,0 +1,31 @@
--TEST--
JIT IDENTICAL: 001
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_update_protection=0
opcache.jit_buffer_size=1M
opcache.protect_memory=1
--EXTENSIONS--
opcache
--FILE--
<?php
function foo($a, $b) {
return $a === $b;
}
var_dump(foo(0, 0));
var_dump(foo(0, 1));
var_dump(foo(0, 0.0));
var_dump(foo(0.0, 0.0));
var_dump(foo(0.0, 1.0));
var_dump(foo("ab", "ab"));
var_dump(foo("ab", "cd"));
?>
--EXPECT--
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)

View File

@@ -0,0 +1,131 @@
--TEST--
JIT IDENTICAL: 002 Comparison with NaN
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_update_protection=0
opcache.jit_buffer_size=1M
opcache.protect_memory=1
--EXTENSIONS--
opcache
--FILE--
<?php
function t() {
echo "!";
return true;
}
function f() {
echo "!";
return false;
}
$a = 0.0;
$b = 0.0;
$c = 1.0;
$d = NAN;
var_dump($a === $b);
var_dump($a === $c);
var_dump($a === $d);
var_dump($a !== $b);
var_dump($a !== $c);
var_dump($a !== $d);
var_dump($a === $b ? 1 : 0);
var_dump($a === $c ? 1 : 0);
var_dump($a === $d ? 1 : 0);
var_dump($a !== $b ? 1 : 0);
var_dump($a !== $c ? 1 : 0);
var_dump($a !== $d ? 1 : 0);
if ($a === $b) {
echo "1\n";
}
if ($a === $c) {
echo "2\n";
}
if ($a === $d) {
echo "3\n";
}
if ($a !== $b) {
echo "4\n";
}
if ($a !== $c) {
echo "5\n";
}
if ($a !== $d) {
echo "6\n";
}
if ($a === $b) {
} else {
echo "7\n";
}
if ($a === $c) {
} else {
echo "8\n";
}
if ($a === $d) {
} else {
echo "9\n";
}
if ($a !== $b) {
} else {
echo "A\n";
}
if ($a !== $c) {
} else {
echo "B\n";
}
if ($a !== $d) {
} else {
echo "C\n";
}
var_dump($a === $b && t());
var_dump($a === $c && t());
var_dump($a === $d && t());
var_dump($a !== $b && t());
var_dump($a !== $c && t());
var_dump($a !== $d && t());
var_dump($a === $b || f());
var_dump($a === $c || f());
var_dump($a === $d || f());
var_dump($a !== $b || f());
var_dump($a !== $c || f());
var_dump($a !== $d || f());
$a=NAN;
var_dump($a === $d);
var_dump($a !== $d);
var_dump($a === $d ? 1 : 0);
var_dump($a !== $d ? 1 : 0);
?>
--EXPECT--
bool(true)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
int(1)
int(0)
int(0)
int(0)
int(1)
int(1)
1
5
6
8
9
A
!bool(true)
bool(false)
bool(false)
bool(false)
!bool(true)
!bool(true)
bool(true)
!bool(false)
!bool(false)
!bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
int(0)
int(1)