]> granicus.if.org Git - php/commitdiff
Implemented builtin instruction for strlen()
authorDmitry Stogov <dmitry@zend.com>
Fri, 11 Jul 2014 14:52:27 +0000 (18:52 +0400)
committerDmitry Stogov <dmitry@zend.com>
Fri, 11 Jul 2014 14:52:27 +0000 (18:52 +0400)
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/mbstring/mbstring.c
ext/opcache/Optimizer/pass1_5.c

index 132def4b1491485beb73d095112b634fb8e79c95..56d0d979cd3f8940aff99d8bf28dc041c405ad51 100644 (file)
@@ -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 &&
index 0f077517f55c9eec296c44bc40933792840720b7..3124c35208e69de31e512c50bb6d952c505a641a 100644 (file)
@@ -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
 
index e6a69a10b64fb9adebbebc206b3b4170ee2b9c6f..dfe6651f3b8ab3ac41dcfc8b28dd804d7ae5abaa 100644 (file)
@@ -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)
index 95cd54f49415b7af6c9cbfd9ad65910ad061d708..ecd15ffe9ae9fb828da7146b7330b6a9ed86c1c7 100644 (file)
@@ -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,
index 87d6a56b365bdb6aa3cda11d4ddd67d90bbb4ab6..7ae371e068e17c47de02b2c8475e28c6bf3215d7 100644 (file)
@@ -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,
index cd448f51de48f8a661ff3a27fff72c4b546f8e6a..1c20fc6228b956a472d6866d623699a5e132c264 100644 (file)
@@ -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
index df264e02af812c80e79f707b6dc271c4103c8423..248bd98535443f9e8d6c958c47aa6f1a2f8ac625 100644 (file)
@@ -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
index 91b78ec6f01d92b4f892f3f347fd91fcf7a4967d..c1235b637e0bba1d74e508951a7948422df2f899 100644 (file)
@@ -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 &&