TYPE_CHECK instruction changed. Now it keeps in extended_value a type mask.

This makes check for "boolean" cheaper and allows check combination e.g. (is_string($a) || is_null($a))
This commit is contained in:
Dmitry Stogov 2017-11-23 15:58:34 +03:00
parent d5dd27987f
commit 33b094479b
9 changed files with 190 additions and 100 deletions

View File

@ -3389,7 +3389,11 @@ int zend_compile_func_typecheck(znode *result, zend_ast_list *args, uint32_t typ
zend_compile_expr(&arg_node, args->child[0]);
opline = zend_emit_op_tmp(result, ZEND_TYPE_CHECK, &arg_node, NULL);
opline->extended_value = type;
if (type != _IS_BOOL) {
opline->extended_value = (1 << type);
} else {
opline->extended_value = (1 << IS_FALSE) | (1 << IS_TRUE);
}
return SUCCESS;
}
/* }}} */

View File

@ -7697,33 +7697,44 @@ ZEND_VM_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY, TYPE)
ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY, TYPE_MASK)
{
USE_OPLINE
zval *value;
int result = 0;
zend_free_op free_op1;
SAVE_OPLINE();
value = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) {
if (UNEXPECTED(Z_TYPE_P(value) == IS_RESOURCE)) {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value));
if (EXPECTED(type_name != NULL)) {
result = 1;
}
} else {
value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
ZEND_VM_C_LABEL(type_check_resource):
if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
|| EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
result = 1;
}
} else if (UNEXPECTED(opline->extended_value == _IS_BOOL) &&
EXPECTED(Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE)) {
result = 1;
} else if ((OP1_TYPE & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
ZEND_VM_C_GOTO(type_check_resource);
}
} else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
result = ((1 << IS_NULL) & opline->extended_value) != 0;
SAVE_OPLINE();
GET_OP1_UNDEF_CV(value, BP_VAR_R);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) {
SAVE_OPLINE();
FREE_OP1();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
ZEND_VM_SMART_BRANCH(result, 0);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE();
}
FREE_OP1();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(122, ZEND_DEFINED, CONST, ANY)

View File

@ -4055,26 +4055,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CONST_HANDLER(
int result = 0;
SAVE_OPLINE();
value = RT_CONSTANT(opline, opline->op1);
if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) {
if (UNEXPECTED(Z_TYPE_P(value) == IS_RESOURCE)) {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value));
if (EXPECTED(type_name != NULL)) {
result = 1;
}
} else {
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
type_check_resource:
if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
|| EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
result = 1;
}
} else if (UNEXPECTED(opline->extended_value == _IS_BOOL) &&
EXPECTED(Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE)) {
result = 1;
} else if ((IS_CONST & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
goto type_check_resource;
}
} else if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
result = ((1 << IS_NULL) & opline->extended_value) != 0;
SAVE_OPLINE();
GET_OP1_UNDEF_CV(value, BP_VAR_R);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (IS_CONST & (IS_TMP_VAR|IS_VAR)) {
SAVE_OPLINE();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
ZEND_VM_SMART_BRANCH(result, 0);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DEFINED_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@ -13594,26 +13605,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_TMP_HANDLER(ZE
int result = 0;
zend_free_op free_op1;
SAVE_OPLINE();
value = _get_zval_ptr_tmp(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) {
if (UNEXPECTED(Z_TYPE_P(value) == IS_RESOURCE)) {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value));
if (EXPECTED(type_name != NULL)) {
result = 1;
}
} else {
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
type_check_resource:
if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
|| EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
result = 1;
}
} else if (UNEXPECTED(opline->extended_value == _IS_BOOL) &&
EXPECTED(Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE)) {
result = 1;
} else if ((IS_TMP_VAR & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
goto type_check_resource;
}
} else if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
result = ((1 << IS_NULL) & opline->extended_value) != 0;
SAVE_OPLINE();
GET_OP1_UNDEF_CV(value, BP_VAR_R);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (IS_TMP_VAR & (IS_TMP_VAR|IS_VAR)) {
SAVE_OPLINE();
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
ZEND_VM_SMART_BRANCH(result, 0);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE();
}
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@ -17658,26 +17680,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZE
int result = 0;
zend_free_op free_op1;
SAVE_OPLINE();
value = _get_zval_ptr_var_deref(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) {
if (UNEXPECTED(Z_TYPE_P(value) == IS_RESOURCE)) {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value));
if (EXPECTED(type_name != NULL)) {
result = 1;
}
} else {
value = _get_zval_ptr_var(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
type_check_resource:
if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
|| EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
result = 1;
}
} else if (UNEXPECTED(opline->extended_value == _IS_BOOL) &&
EXPECTED(Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE)) {
result = 1;
} else if ((IS_VAR & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
goto type_check_resource;
}
} else if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
result = ((1 << IS_NULL) & opline->extended_value) != 0;
SAVE_OPLINE();
GET_OP1_UNDEF_CV(value, BP_VAR_R);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (IS_VAR & (IS_TMP_VAR|IS_VAR)) {
SAVE_OPLINE();
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
ZEND_VM_SMART_BRANCH(result, 0);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE();
}
zval_ptr_dtor_nogc(free_op1);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@ -34812,26 +34845,37 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CV_HANDLER(ZEN
int result = 0;
SAVE_OPLINE();
value = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) {
if (UNEXPECTED(Z_TYPE_P(value) == IS_RESOURCE)) {
const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(value));
if (EXPECTED(type_name != NULL)) {
result = 1;
}
} else {
value = _get_zval_ptr_cv_undef(opline->op1.var EXECUTE_DATA_CC);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
type_check_resource:
if (EXPECTED(Z_TYPE_P(value) != IS_RESOURCE)
|| EXPECTED(NULL != zend_rsrc_list_get_rsrc_type(Z_RES_P(value)))) {
result = 1;
}
} else if (UNEXPECTED(opline->extended_value == _IS_BOOL) &&
EXPECTED(Z_TYPE_P(value) == IS_TRUE || Z_TYPE_P(value) == IS_FALSE)) {
result = 1;
} else if ((IS_CV & (IS_CV|IS_VAR)) && Z_ISREF_P(value)) {
value = Z_REFVAL_P(value);
if ((1 << (uint32_t)Z_TYPE_P(value) & opline->extended_value)) {
goto type_check_resource;
}
} else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
result = ((1 << IS_NULL) & opline->extended_value) != 0;
SAVE_OPLINE();
GET_OP1_UNDEF_CV(value, BP_VAR_R);
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
if (IS_CV & (IS_TMP_VAR|IS_VAR)) {
SAVE_OPLINE();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
ZEND_VM_SMART_BRANCH(result, 1);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
} else {
ZEND_VM_SMART_BRANCH(result, 0);
ZVAL_BOOL(EX_VAR(opline->result.var), result);
ZEND_VM_NEXT_OPCODE();
}
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAR_SIMPLE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

View File

@ -84,7 +84,7 @@ $vm_op_flags = array(
"ZEND_VM_EXT_CONST_FETCH" => 0x06000000,
"ZEND_VM_EXT_TYPE" => 0x07000000,
"ZEND_VM_EXT_EVAL" => 0x08000000,
// unused 0x09000000,
"ZEND_VM_EXT_TYPE_MASK" => 0x09000000,
// unused 0x0a000000,
"ZEND_VM_EXT_SRC" => 0x0b000000,
// unused 0x0c000000,
@ -125,6 +125,7 @@ $vm_ext_decode = array(
"ARRAY_INIT" => ZEND_VM_EXT_ARRAY_INIT,
"TYPE" => ZEND_VM_EXT_TYPE,
"EVAL" => ZEND_VM_EXT_EVAL,
"TYPE_MASK" => ZEND_VM_EXT_TYPE_MASK,
"ISSET" => ZEND_VM_EXT_ISSET,
"ARG_NUM" => ZEND_VM_EXT_ARG_NUM,
"REF" => ZEND_VM_EXT_REF,

View File

@ -346,7 +346,7 @@ static uint32_t zend_vm_opcodes_flags[198] = {
0x00001003,
0x00000007,
0x00000003,
0x07000003,
0x09000003,
0x00000103,
0x00002003,
0x03000001,

View File

@ -60,6 +60,7 @@
#define ZEND_VM_EXT_CONST_FETCH 0x06000000
#define ZEND_VM_EXT_TYPE 0x07000000
#define ZEND_VM_EXT_EVAL 0x08000000
#define ZEND_VM_EXT_TYPE_MASK 0x09000000
#define ZEND_VM_EXT_SRC 0x0b000000
#define ZEND_VM_NO_CONST_CONST 0x40000000
#define ZEND_VM_COMMUTATIVE 0x80000000

View File

@ -613,12 +613,8 @@ static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, z
return SUCCESS;
}
static inline void ct_eval_type_check(zval *result, uint32_t type, zval *op1) {
if (type == _IS_BOOL) {
ZVAL_BOOL(result, Z_TYPE_P(op1) == IS_TRUE || Z_TYPE_P(op1) == IS_FALSE);
} else {
ZVAL_BOOL(result, Z_TYPE_P(op1) == type);
}
static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) {
ZVAL_BOOL(result, (1 << Z_TYPE_P(op1)) & type_mask);
}
static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
@ -961,14 +957,13 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
* even if we don't know the precise value. */
if (!value_known(op1)) {
uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
uint32_t expected_type = opline->extended_value == _IS_BOOL
? (MAY_BE_TRUE|MAY_BE_FALSE) : (1 << opline->extended_value);
if (!(type & expected_type) && !(type & MAY_BE_UNDEF)) {
uint32_t expected_type_mask = opline->extended_value;
if (!(type & expected_type_mask) && !(type & MAY_BE_UNDEF)) {
ZVAL_FALSE(&zv);
SET_RESULT(result, &zv);
return;
} else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type))
&& opline->extended_value != IS_RESOURCE) {
} else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type_mask))
&& !(expected_type_mask & MAY_BE_RESOURCE)) {
ZVAL_TRUE(&zv);
SET_RESULT(result, &zv);
return;

View File

@ -500,6 +500,42 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " (\?\?\?)");
break;
}
} else if (ZEND_VM_EXT_TYPE_MASK == (flags & ZEND_VM_EXT_MASK)) {
switch (opline->extended_value) {
case (1<<IS_NULL):
fprintf(stderr, " (null)");
break;
case (1<<IS_FALSE):
fprintf(stderr, " (false)");
break;
case (1<<IS_TRUE):
fprintf(stderr, " (true)");
break;
case (1<<IS_LONG):
fprintf(stderr, " (long)");
break;
case (1<<IS_DOUBLE):
fprintf(stderr, " (double)");
break;
case (1<<IS_STRING):
fprintf(stderr, " (string)");
break;
case (1<<IS_ARRAY):
fprintf(stderr, " (array)");
break;
case (1<<IS_OBJECT):
fprintf(stderr, " (object)");
break;
case (1<<IS_RESOURCE):
fprintf(stderr, " (resource)");
break;
case ((1<<IS_FALSE)||(1<<IS_TRUE)):
fprintf(stderr, " (bool)");
break;
default:
fprintf(stderr, " (\?\?\?)");
break;
}
} else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) {
switch (opline->extended_value) {
case ZEND_EVAL:

View File

@ -154,12 +154,10 @@ static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
pi_type_mask(phi, ~type_mask & relevant);
}
static inline uint32_t mask_for_type_check(uint32_t type) {
if (type == _IS_BOOL) {
return MAY_BE_TRUE|MAY_BE_FALSE;
} else if (type == IS_ARRAY) {
if (type & MAY_BE_ARRAY) {
return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
} else {
return 1 << type;
return type;
}
}