]> granicus.if.org Git - php/commitdiff
Extend ZEND_SEND_ARRAY to eliminate array_slice() call for "call_user_func_array...
authorDmitry Stogov <dmitry@zend.com>
Tue, 30 May 2017 09:25:46 +0000 (12:25 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 30 May 2017 09:25:46 +0000 (12:25 +0300)
Zend/zend_compile.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 75ceb36a488bd1d0e3528f28847905d6348a4005..e210a26e43560dbe63850b6a33a7e856a36429b2 100644 (file)
@@ -3554,6 +3554,33 @@ int zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcna
        }
 
        zend_compile_init_user_func(args->child[0], 0, lcname);
+       if (args->child[1]->kind == ZEND_AST_CALL
+        && args->child[1]->child[0]->kind == ZEND_AST_ZVAL
+        && args->child[1]->child[1]->kind == ZEND_AST_ARG_LIST) {
+               zval *name = zend_ast_get_zval(args->child[1]->child[0]);
+               zend_ast_list *list = zend_ast_get_list(args->child[1]->child[1]);
+
+               if (Z_TYPE_P(name) == IS_STRING
+                && zend_string_equals_literal_ci(Z_STR_P(name), "array_slice")
+                && list->children == 3
+                && list->child[1]->kind == ZEND_AST_ZVAL) {
+                       zval *zv = zend_ast_get_zval(list->child[1]);
+
+                       if (Z_TYPE_P(zv) == IS_LONG
+                        && Z_LVAL_P(zv) >= 0
+                        && Z_LVAL_P(zv) <= 0x7fffffff) {
+                               zend_op *opline;
+                               znode len_node;
+
+                               zend_compile_expr(&arg_node, list->child[0]);
+                               zend_compile_expr(&len_node, list->child[2]);
+                               opline = zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, &len_node);
+                               opline->extended_value = Z_LVAL_P(zv);
+                               zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+                               return SUCCESS;
+                       }
+               }
+       }
        zend_compile_expr(&arg_node, args->child[1]);
        zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL);
        zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
index 9b8d0670bb8bf9089cc389a60e42e88473767e4d..d26f9e78cbe186a99b8ddc340209bdc2c38e0d61 100644 (file)
@@ -4465,8 +4465,8 @@ ZEND_VM_C_LABEL(send_again):
 ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
 {
        USE_OPLINE
-       zend_free_op free_op1;
-       zval *args;
+       zend_free_op free_op1, free_op2;
+       zval *args, *op2;
 
        SAVE_OPLINE();
        args = GET_OP1_ZVAL_PTR(BP_VAR_R);
@@ -4488,44 +4488,98 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
                EX(call)->func = (zend_function*)&zend_pass_function;
                Z_OBJ(EX(call)->This) = NULL;
                ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+               FREE_UNFETCHED_OP2();
        } else {
                uint32_t arg_num;
                HashTable *ht;
                zval *arg, *param;
 
+
 ZEND_VM_C_LABEL(send_array):
                ht = Z_ARRVAL_P(args);
-               zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
+               if (OP2_TYPE != IS_UNUSED) {
+                       zend_free_op free_op2;
+                       zval *op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
+                       uint32_t skip = opline->extended_value;
+                       uint32_t count = zend_hash_num_elements(ht);
+                       zend_long len = zval_get_long(op2);
+
+                       if (len < 0) {
+                               len += (zend_long)(count - skip);
+                       }
+                       if (skip < count && len > 0) {
+                               if (len > (zend_long)(count - skip)) {
+                                       len = (zend_long)(count - skip);
+                               }
+                               zend_vm_stack_extend_call_frame(&EX(call), 0, len);
+                               arg_num = 1;
+                               param = ZEND_CALL_ARG(EX(call), 1);
+                               ZEND_HASH_FOREACH_VAL(ht, arg) {
+                                       if (skip > 0) {
+                                               skip--;
+                                               continue;
+                                       } else if ((zend_long)(arg_num - 1) >= len) {
+                                               break;
+                                       } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                               if (UNEXPECTED(!Z_ISREF_P(arg))) {
+                                                       if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                                               /* By-value send is not allowed -- emit a warning,
+                                                                * but still perform the call. */
+                                                               zend_error(E_WARNING,
+                                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                                                       arg_num,
+                                                                       EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
+                                                                       EX(call)->func->common.scope ? "::" : "",
+                                                                       ZSTR_VAL(EX(call)->func->common.function_name));
 
-               arg_num = 1;
-               param = ZEND_CALL_ARG(EX(call), 1);
-               ZEND_HASH_FOREACH_VAL(ht, arg) {
-                       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-                               if (UNEXPECTED(!Z_ISREF_P(arg))) {
-                                       if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-                                               /* By-value send is not allowed -- emit a warning,
-                                                * but still perform the call. */
-                                               zend_error(E_WARNING,
-                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
-                                                       arg_num,
-                                                       EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
-                                                       EX(call)->func->common.scope ? "::" : "",
-                                                       ZSTR_VAL(EX(call)->func->common.function_name));
+                                                       }
+                                               }
+                                       } else {
+                                               if (Z_ISREF_P(arg) &&
+                                                   !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                                                       /* don't separate references for __call */
+                                                       arg = Z_REFVAL_P(arg);
+                                               }
+                                       }
+                                       ZVAL_COPY(param, arg);
+                                       ZEND_CALL_NUM_ARGS(EX(call))++;
+                                       arg_num++;
+                                       param++;
+                               } ZEND_HASH_FOREACH_END();
+                       }
+                       FREE_OP2();
+               } else {
+                       zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
+                       arg_num = 1;
+                       param = ZEND_CALL_ARG(EX(call), 1);
+                       ZEND_HASH_FOREACH_VAL(ht, arg) {
+                               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                       if (UNEXPECTED(!Z_ISREF_P(arg))) {
+                                               if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                                       /* By-value send is not allowed -- emit a warning,
+                                                        * but still perform the call. */
+                                                       zend_error(E_WARNING,
+                                                               "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                                               arg_num,
+                                                               EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
+                                                               EX(call)->func->common.scope ? "::" : "",
+                                                               ZSTR_VAL(EX(call)->func->common.function_name));
 
+                                               }
+                                       }
+                               } else {
+                                       if (Z_ISREF_P(arg) &&
+                                           !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                                               /* don't separate references for __call */
+                                               arg = Z_REFVAL_P(arg);
                                        }
                                }
-                       } else {
-                               if (Z_ISREF_P(arg) &&
-                                   !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
-                                       /* don't separate references for __call */
-                                       arg = Z_REFVAL_P(arg);
-                               }
-                       }
-                       ZVAL_COPY(param, arg);
-                       ZEND_CALL_NUM_ARGS(EX(call))++;
-                       arg_num++;
-                       param++;
-               } ZEND_HASH_FOREACH_END();
+                               ZVAL_COPY(param, arg);
+                               ZEND_CALL_NUM_ARGS(EX(call))++;
+                               arg_num++;
+                               param++;
+                       } ZEND_HASH_FOREACH_END();
+               }
        }
        FREE_OP1();
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
index aa2175e4c9ad84cc4521951aa725e41fc254d8d0..2427327af6d9c64100ff7fbe0e7763e8d351daa7 100644 (file)
@@ -1322,8 +1322,8 @@ send_again:
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
-       zend_free_op free_op1;
-       zval *args;
+       zend_free_op free_op1, free_op2;
+       zval *args, *op2;
 
        SAVE_OPLINE();
        args = get_zval_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, BP_VAR_R);
@@ -1345,44 +1345,98 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
                EX(call)->func = (zend_function*)&zend_pass_function;
                Z_OBJ(EX(call)->This) = NULL;
                ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
+               FREE_UNFETCHED_OP(opline->op2_type, opline->op2.var);
        } else {
                uint32_t arg_num;
                HashTable *ht;
                zval *arg, *param;
 
+
 send_array:
                ht = Z_ARRVAL_P(args);
-               zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
+               if (opline->op2_type != IS_UNUSED) {
+                       zend_free_op free_op2;
+                       zval *op2 = get_zval_ptr_deref(opline->op2_type, opline->op2, execute_data, &free_op2, BP_VAR_R);
+                       uint32_t skip = opline->extended_value;
+                       uint32_t count = zend_hash_num_elements(ht);
+                       zend_long len = zval_get_long(op2);
+
+                       if (len < 0) {
+                               len += (zend_long)(count - skip);
+                       }
+                       if (skip < count && len > 0) {
+                               if (len > (zend_long)(count - skip)) {
+                                       len = (zend_long)(count - skip);
+                               }
+                               zend_vm_stack_extend_call_frame(&EX(call), 0, len);
+                               arg_num = 1;
+                               param = ZEND_CALL_ARG(EX(call), 1);
+                               ZEND_HASH_FOREACH_VAL(ht, arg) {
+                                       if (skip > 0) {
+                                               skip--;
+                                               continue;
+                                       } else if ((zend_long)(arg_num - 1) >= len) {
+                                               break;
+                                       } else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                               if (UNEXPECTED(!Z_ISREF_P(arg))) {
+                                                       if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                                               /* By-value send is not allowed -- emit a warning,
+                                                                * but still perform the call. */
+                                                               zend_error(E_WARNING,
+                                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                                                       arg_num,
+                                                                       EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
+                                                                       EX(call)->func->common.scope ? "::" : "",
+                                                                       ZSTR_VAL(EX(call)->func->common.function_name));
 
-               arg_num = 1;
-               param = ZEND_CALL_ARG(EX(call), 1);
-               ZEND_HASH_FOREACH_VAL(ht, arg) {
-                       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-                               if (UNEXPECTED(!Z_ISREF_P(arg))) {
-                                       if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-                                               /* By-value send is not allowed -- emit a warning,
-                                                * but still perform the call. */
-                                               zend_error(E_WARNING,
-                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
-                                                       arg_num,
-                                                       EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
-                                                       EX(call)->func->common.scope ? "::" : "",
-                                                       ZSTR_VAL(EX(call)->func->common.function_name));
+                                                       }
+                                               }
+                                       } else {
+                                               if (Z_ISREF_P(arg) &&
+                                                   !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                                                       /* don't separate references for __call */
+                                                       arg = Z_REFVAL_P(arg);
+                                               }
+                                       }
+                                       ZVAL_COPY(param, arg);
+                                       ZEND_CALL_NUM_ARGS(EX(call))++;
+                                       arg_num++;
+                                       param++;
+                               } ZEND_HASH_FOREACH_END();
+                       }
+                       FREE_OP(free_op2);
+               } else {
+                       zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
+                       arg_num = 1;
+                       param = ZEND_CALL_ARG(EX(call), 1);
+                       ZEND_HASH_FOREACH_VAL(ht, arg) {
+                               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                       if (UNEXPECTED(!Z_ISREF_P(arg))) {
+                                               if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
+                                                       /* By-value send is not allowed -- emit a warning,
+                                                        * but still perform the call. */
+                                                       zend_error(E_WARNING,
+                                                               "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                                               arg_num,
+                                                               EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
+                                                               EX(call)->func->common.scope ? "::" : "",
+                                                               ZSTR_VAL(EX(call)->func->common.function_name));
 
+                                               }
+                                       }
+                               } else {
+                                       if (Z_ISREF_P(arg) &&
+                                           !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                                               /* don't separate references for __call */
+                                               arg = Z_REFVAL_P(arg);
                                        }
                                }
-                       } else {
-                               if (Z_ISREF_P(arg) &&
-                                   !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
-                                       /* don't separate references for __call */
-                                       arg = Z_REFVAL_P(arg);
-                               }
-                       }
-                       ZVAL_COPY(param, arg);
-                       ZEND_CALL_NUM_ARGS(EX(call))++;
-                       arg_num++;
-                       param++;
-               } ZEND_HASH_FOREACH_END();
+                               ZVAL_COPY(param, arg);
+                               ZEND_CALL_NUM_ARGS(EX(call))++;
+                               arg_num++;
+                               param++;
+                       } ZEND_HASH_FOREACH_END();
+               }
        }
        FREE_OP(free_op1);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();