From 33b094479b7d2a61ecee52e7ce2bd389426349e9 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 23 Nov 2017 15:58:34 +0300 Subject: [PATCH] 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)) --- Zend/zend_compile.c | 6 +- Zend/zend_vm_def.h | 47 ++++---- Zend/zend_vm_execute.h | 172 +++++++++++++++++++----------- Zend/zend_vm_gen.php | 3 +- Zend/zend_vm_opcodes.c | 2 +- Zend/zend_vm_opcodes.h | 1 + ext/opcache/Optimizer/sccp.c | 17 ++- ext/opcache/Optimizer/zend_dump.c | 36 +++++++ ext/opcache/Optimizer/zend_ssa.c | 6 +- 9 files changed, 190 insertions(+), 100 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 75bee6708d..4381a2492f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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; } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9e4dd1156c..88db654015 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 239e8b18fb..e9dcc536e8 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index ecb7401a29..e6ddecfed3 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -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, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index ecfe4645fa..01e658e8f9 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -346,7 +346,7 @@ static uint32_t zend_vm_opcodes_flags[198] = { 0x00001003, 0x00000007, 0x00000003, - 0x07000003, + 0x09000003, 0x00000103, 0x00002003, 0x03000001, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 3be364150f..f17b40a0ff 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -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 diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 347e1cc77a..5580395f5a 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -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; diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 3910631335..280a77034b 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -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<extended_value) { case ZEND_EVAL: diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 7eb1333eef..6dfb13886e 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -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; } } -- 2.50.1