Basic JIT support for verify return

This commit is contained in:
Nikita Popov 2019-12-06 15:02:15 +01:00 committed by Dmitry Stogov
parent e9b991d6a0
commit bc6bab6cb4
5 changed files with 103 additions and 10 deletions

View file

@ -1224,7 +1224,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *
}
}
static ZEND_COLD void zend_verify_return_error(
ZEND_API ZEND_COLD void zend_verify_return_error(
const zend_function *zf, void **cache_slot, zval *value)
{
const zend_arg_info *arg_info = &zf->common.arg_info[-1];

View file

@ -64,6 +64,8 @@ ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, z
ZEND_API ZEND_COLD void zend_verify_arg_error(
const zend_function *zf, const zend_arg_info *arg_info,
int arg_num, void **cache_slot, zval *value);
ZEND_API ZEND_COLD void zend_verify_return_error(
const zend_function *zf, void **cache_slot, zval *value);
ZEND_API zend_bool zend_verify_ref_array_assignable(zend_reference *ref);
#define ZEND_REF_TYPE_SOURCES(ref) \

View file

@ -2739,6 +2739,27 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
goto jit_failure;
}
goto done;
case ZEND_VERIFY_RETURN_TYPE:
if (opline->op1_type == IS_UNUSED) {
/* Always throws */
break;
}
if (opline->op1_type == IS_CONST) {
/* TODO Different instruction format, has return value */
break;
}
if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
/* Not worth bothering with */
break;
}
if (OP1_INFO() & MAY_BE_REF) {
/* TODO May need reference unwrapping. */
break;
}
if (!zend_jit_verify_return_type(&dasm_state, opline, op_array, OP1_INFO())) {
goto jit_failure;
}
goto done;
default:
break;
}

View file

@ -1141,7 +1141,7 @@ static zval* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_execute_data *execu
return value;
}
static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, const zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot)
static zend_always_inline zend_bool zend_jit_verify_type_common(zval *arg, const zend_op_array *op_array, zend_arg_info *arg_info, void **cache_slot)
{
uint32_t type_mask;
@ -1162,7 +1162,7 @@ static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, const zend_op_arra
*cache_slot = ce;
}
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
return;
return 1;
}
cache_slot++;
} ZEND_TYPE_LIST_FOREACH_END();
@ -1177,7 +1177,7 @@ static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, const zend_op_arra
*cache_slot = (void *) ce;
}
if (instanceof_function(Z_OBJCE_P(arg), ce)) {
return;
return 1;
}
}
}
@ -1185,16 +1185,29 @@ static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, const zend_op_arra
builtin_types:
type_mask = ZEND_TYPE_FULL_MASK(arg_info->type);
if ((type_mask & MAY_BE_CALLABLE) && zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
return;
return 1;
}
if ((type_mask & MAY_BE_ITERABLE) && zend_is_iterable(arg)) {
return;
return 1;
}
if (zend_verify_scalar_type_hint(type_mask, arg, ZEND_ARG_USES_STRICT_TYPES(), /* is_internal */ 0)) {
return;
return 1;
}
return 0;
}
zend_verify_arg_error((zend_function*)op_array, arg_info, arg_num, cache_slot, arg);
static void ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, const zend_op_array *op_array, uint32_t arg_num, zend_arg_info *arg_info, void **cache_slot)
{
if (UNEXPECTED(!zend_jit_verify_type_common(arg, op_array, arg_info, cache_slot))) {
zend_verify_arg_error((zend_function*)op_array, arg_info, arg_num, cache_slot, arg);
}
}
static void ZEND_FASTCALL zend_jit_verify_return_slow(zval *arg, const zend_op_array *op_array, zend_arg_info *arg_info, void **cache_slot)
{
if (UNEXPECTED(!zend_jit_verify_type_common(arg, op_array, arg_info, cache_slot))) {
zend_verify_return_error((zend_function*)op_array, cache_slot, arg);
}
}
static void ZEND_FASTCALL zend_jit_zval_copy_deref_helper(zval *dst, zval *src)

View file

@ -8647,7 +8647,9 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_
}
uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
if (is_power_of_two(type_mask)) {
if (type_mask == 0) {
| jmp >8
} else if (is_power_of_two(type_mask)) {
uint32_t type_code = concrete_type(type_mask);
| cmp byte [r0 + 8], type_code
| jne >8
@ -8765,7 +8767,9 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen
| ZVAL_DEREF r0, MAY_BE_REF
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
if (is_power_of_two(type_mask)) {
if (type_mask == 0) {
| jmp >8
} else if (is_power_of_two(type_mask)) {
uint32_t type_code = concrete_type(type_mask);
| cmp byte [r0 + 8], type_code
| jne >8
@ -9341,6 +9345,59 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o
return 1;
}
static zend_bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info)
{
zend_arg_info *arg_info = &op_array->arg_info[-1];
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
zend_jit_addr op1_addr = OP1_ADDR();
| LOAD_ZVAL_ADDR r0, op1_addr
uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
if (type_mask == 0) {
| jmp >8
} else if (is_power_of_two(type_mask)) {
uint32_t type_code = concrete_type(type_mask);
| cmp byte [r0 + 8], type_code
| jne >8
} else {
| mov edx, 1
| mov cl, byte [r0 + 8]
| shl edx, cl
| test edx, type_mask
| je >8
}
|.cold_code
|8:
| mov FCARG1a, r0
| mov r0, EX->run_time_cache
| add r0, opline->op2.num
| LOAD_ADDR FCARG2a, (ptrdiff_t)op_array
|.if X64WIN
| LOAD_ADDR CARG3, (ptrdiff_t)arg_info
| mov aword A4, r0
| SAVE_VALID_OPLINE opline
| EXT_CALL zend_jit_verify_return_slow, r0
|.elif X64
| LOAD_ADDR CARG3, (ptrdiff_t)arg_info
| mov CARG4, r0
| SAVE_VALID_OPLINE opline
| EXT_CALL zend_jit_verify_return_slow, r0
|.else
| push r0
| push (ptrdiff_t)arg_info
| SAVE_VALID_OPLINE opline
| EXT_CALL zend_jit_verify_return_slow, r0
|.endif
if (!zend_jit_check_exception(Dst)) {
return 0;
}
| jmp >9
|.code
|9:
return 1;
}
static zend_bool zend_jit_may_reuse_reg(const zend_op_array *op_array, zend_ssa *ssa, uint32_t position, int def_var, int use_var)
{
if (ssa->var_info[def_var].type != ssa->var_info[use_var].type) {