mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Extend ZEND_SEND_ARRAY to eliminate array_slice() call for "call_user_func_array(_, array_slice(_, LONG, _))" pattern.
This commit is contained in:
parent
c45e3632a2
commit
017d65d74a
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
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));
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
|
@ -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);
|
||||
|
||||
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));
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
|
Loading…
Reference in New Issue
Block a user