From 6c8db864e047271fed082562a183e141b40c130e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 11 Jul 2014 18:52:27 +0400 Subject: [PATCH] Implemented builtin instruction for strlen() --- Zend/zend_compile.c | 65 ++++++++++ Zend/zend_compile.h | 3 + Zend/zend_vm_def.h | 38 ++++++ Zend/zend_vm_execute.h | 202 ++++++++++++++++++++++++++++---- Zend/zend_vm_opcodes.c | 2 +- Zend/zend_vm_opcodes.h | 1 + ext/mbstring/mbstring.c | 2 + ext/opcache/Optimizer/pass1_5.c | 14 ++- 8 files changed, 300 insertions(+), 27 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 132def4b14..56d0d979cd 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2703,6 +2703,54 @@ static int zend_do_convert_call_user_func_array(zend_op *init_opline TSRMLS_DC) } /* }}} */ +static int zend_do_convert_strlen(zend_op *init_opline, znode *result TSRMLS_DC) /* {{{ */ +{ + zend_op *opline = init_opline + 1; + zend_op *send = NULL; + int level = 0; + + do { + switch (opline->opcode) { + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: + if (level == 0) { + if (opline->opcode == ZEND_SEND_UNPACK) { + return 0; + } + send = opline; + } + break; + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_NEW: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_FCALL: + level++; + break; + case ZEND_DO_FCALL: + level--; + break; + } + opline++; + } while (send == NULL); + + MAKE_NOP(init_opline); + send->opcode = ZEND_STRLEN; + send->extended_value = 0; + SET_UNUSED(send->op2); + send->result.var = get_temporary_variable(CG(active_op_array)); + send->result_type = IS_TMP_VAR; + GET_NODE(result, send->result); + return 1; +} +/* }}} */ + void zend_do_end_function_call(znode *function_name, znode *result, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2740,6 +2788,15 @@ void zend_do_end_function_call(znode *function_name, znode *result, int is_metho fcall->arg_num = 0; } } + } else if ((CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN) == 0 && + func->common.function_name->len == sizeof("strlen")-1 && + memcmp(func->common.function_name->val, "strlen", sizeof("strlen")-1) == 0) { + if (fcall->arg_num == 1) { + if (zend_do_convert_strlen(opline, result TSRMLS_CC)) { + zend_stack_del_top(&CG(function_call_stack)); + return; + } + } } } } @@ -2776,6 +2833,14 @@ void zend_do_pass_param(znode *param, zend_uchar op TSRMLS_DC) /* {{{ */ "Cannot use positional argument after argument unpacking"); } + if (op == ZEND_SEND_VAR && (param->op_type & (IS_CONST|IS_TMP_VAR))) { + /* Function call was converted into builtin instruction */ + zend_llist *fetch_list_ptr = zend_stack_top(&CG(bp_stack)); + zend_llist_destroy(fetch_list_ptr); + zend_stack_del_top(&CG(bp_stack)); + original_op = op = ZEND_SEND_VAL; + } + if (original_op == ZEND_SEND_REF) { if (function_ptr && function_ptr->common.function_name && diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0f077517f5..3124c35208 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -883,6 +883,9 @@ END_EXTERN_C() /* disable constant substitution at compile-time */ #define ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION (1<<5) +/* disable usage of builtin instruction for strlen() */ +#define ZEND_COMPILE_NO_BUILTIN_STRLEN (1<<6) + /* The default value for CG(compiler_options) */ #define ZEND_COMPILE_DEFAULT ZEND_COMPILE_HANDLE_OP_ARRAY diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e6a69a10b6..dfe6651f3b 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5846,4 +5846,42 @@ ZEND_VM_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(121, ZEND_STRLEN, CONST|TMP|VAR|CV, ANY) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); + } else { + if (Z_TYPE_P(value) < IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 0); + } else if (Z_TYPE_P(value) == IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 1); + } else if (Z_TYPE_P(value) <= IS_DOUBLE) { + zend_string *str = zval_get_string(value); + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else if (Z_TYPE_P(value) == IS_OBJECT) { + zend_string *str; + + if (parse_arg_object_to_str(value, &str, IS_STRING TSRMLS_CC) == FAILURE) { + ZEND_VM_C_GOTO(strlen_error); + } + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else { +ZEND_VM_C_LABEL(strlen_error): + zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + FREE_OP1(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_EXPORT_HANDLER(zend_do_fcall, ZEND_DO_FCALL) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 95cd54f494..ecd15ffe9a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3361,6 +3361,44 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + + + SAVE_OPLINE(); + value = opline->op1.zv; + if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); + } else { + if (Z_TYPE_P(value) < IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 0); + } else if (Z_TYPE_P(value) == IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 1); + } else if (Z_TYPE_P(value) <= IS_DOUBLE) { + zend_string *str = zval_get_string(value); + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else if (Z_TYPE_P(value) == IS_OBJECT) { + zend_string *str; + + if (parse_arg_object_to_str(value, &str, IS_STRING TSRMLS_CC) == FAILURE) { + goto strlen_error; + } + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else { +strlen_error: + zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8804,6 +8842,44 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_STRLEN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); + } else { + if (Z_TYPE_P(value) < IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 0); + } else if (Z_TYPE_P(value) == IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 1); + } else if (Z_TYPE_P(value) <= IS_DOUBLE) { + zend_string *str = zval_get_string(value); + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else if (Z_TYPE_P(value) == IS_OBJECT) { + zend_string *str; + + if (parse_arg_object_to_str(value, &str, IS_STRING TSRMLS_CC) == FAILURE) { + goto strlen_error; + } + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else { +strlen_error: + zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + zval_dtor(free_op1.var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -14263,6 +14339,44 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_STRLEN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + zend_free_op free_op1; + + SAVE_OPLINE(); + value = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); + if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); + } else { + if (Z_TYPE_P(value) < IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 0); + } else if (Z_TYPE_P(value) == IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 1); + } else if (Z_TYPE_P(value) <= IS_DOUBLE) { + zend_string *str = zval_get_string(value); + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else if (Z_TYPE_P(value) == IS_OBJECT) { + zend_string *str; + + if (parse_arg_object_to_str(value, &str, IS_STRING TSRMLS_CC) == FAILURE) { + goto strlen_error; + } + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else { +strlen_error: + zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + zval_ptr_dtor_nogc(free_op1.var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -31336,6 +31450,44 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *value; + + + SAVE_OPLINE(); + value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); + if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); + } else { + if (Z_TYPE_P(value) < IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 0); + } else if (Z_TYPE_P(value) == IS_TRUE) { + ZVAL_LONG(EX_VAR(opline->result.var), 1); + } else if (Z_TYPE_P(value) <= IS_DOUBLE) { + zend_string *str = zval_get_string(value); + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else if (Z_TYPE_P(value) == IS_OBJECT) { + zend_string *str; + + if (parse_arg_object_to_str(value, &str, IS_STRING TSRMLS_CC) == FAILURE) { + goto strlen_error; + } + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + STR_RELEASE(str); + } else { +strlen_error: + zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -43619,31 +43771,31 @@ void zend_init_opcodes_handlers(void) ZEND_SEND_USER_SPEC_CV_HANDLER, ZEND_SEND_USER_SPEC_CV_HANDLER, ZEND_SEND_USER_SPEC_CV_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_STRLEN_SPEC_CONST_HANDLER, + ZEND_STRLEN_SPEC_CONST_HANDLER, + ZEND_STRLEN_SPEC_CONST_HANDLER, + ZEND_STRLEN_SPEC_CONST_HANDLER, + ZEND_STRLEN_SPEC_CONST_HANDLER, + ZEND_STRLEN_SPEC_TMP_HANDLER, + ZEND_STRLEN_SPEC_TMP_HANDLER, + ZEND_STRLEN_SPEC_TMP_HANDLER, + ZEND_STRLEN_SPEC_TMP_HANDLER, + ZEND_STRLEN_SPEC_TMP_HANDLER, + ZEND_STRLEN_SPEC_VAR_HANDLER, + ZEND_STRLEN_SPEC_VAR_HANDLER, + ZEND_STRLEN_SPEC_VAR_HANDLER, + ZEND_STRLEN_SPEC_VAR_HANDLER, + ZEND_STRLEN_SPEC_VAR_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_STRLEN_SPEC_CV_HANDLER, + ZEND_STRLEN_SPEC_CV_HANDLER, + ZEND_STRLEN_SPEC_CV_HANDLER, + ZEND_STRLEN_SPEC_CV_HANDLER, + ZEND_STRLEN_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 87d6a56b36..7ae371e068 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -143,7 +143,7 @@ const char *zend_vm_opcodes_map[169] = { "ZEND_INIT_USER_CALL", "ZEND_SEND_ARRAY", "ZEND_SEND_USER", - NULL, + "ZEND_STRLEN", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index cd448f51de..1c20fc6228 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -144,6 +144,7 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode); #define ZEND_INIT_USER_CALL 118 #define ZEND_SEND_ARRAY 119 #define ZEND_SEND_USER 120 +#define ZEND_STRLEN 121 #define ZEND_PRE_INC_OBJ 132 #define ZEND_PRE_DEC_OBJ 133 #define ZEND_POST_INC_OBJ 134 diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index df264e02af..248bd98535 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -1619,6 +1619,7 @@ PHP_RINIT_FUNCTION(mbstring) if (MBSTRG(func_overload)){ p = &(mb_ovld[0]); + CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; while (p->type > 0) { if ((MBSTRG(func_overload) & p->type) == p->type && (orig = zend_hash_str_find_ptr(EG(function_table), p->save_func, @@ -1687,6 +1688,7 @@ PHP_RSHUTDOWN_FUNCTION(mbstring) } p++; } + CG(compiler_options) &= ~ZEND_COMPILE_NO_BUILTIN_STRLEN; } #if HAVE_MBREGEX diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 91b78ec6f0..c1235b637e 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -440,7 +440,8 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { MAKE_NOP(opline + 2); } } - } else if (Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("strlen")-1 && + } else if ((CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN) == 0 && + Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("strlen")-1 && !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "strlen", sizeof("strlen")-1)) { zval t; @@ -456,6 +457,17 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { } } break; + case ZEND_STRLEN: + if (ZEND_OP1_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { + zval t; + + ZVAL_LONG(&t, Z_STRLEN(ZEND_OP1_LITERAL(opline))); + replace_tmp_by_const(op_array, opline + 1, ZEND_RESULT(opline).var, &t TSRMLS_CC); + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + } + break; #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO case ZEND_DECLARE_CONST: if (collect_constants && -- 2.40.0