0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 8.2.0546: Vim9: varargs implementation is inefficient

Problem:    Vim9: varargs implementation is inefficient.
Solution:   Create list without moving the arguments.
This commit is contained in:
Bram Moolenaar
2020-04-11 22:31:27 +02:00
parent d19a8f97ad
commit fe27081724
3 changed files with 52 additions and 69 deletions

View File

@@ -738,6 +738,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
546,
/**/ /**/
545, 545,
/**/ /**/

View File

@@ -5584,15 +5584,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
ufunc->uf_def_arg_idx[count] = instr->ga_len; ufunc->uf_def_arg_idx[count] = instr->ga_len;
} }
// If varargs is use, push a list. Empty if no more arguments.
if (ufunc->uf_va_name != NULL)
{
if (generate_NEWLIST(&cctx, 0) == FAIL
|| generate_STORE(&cctx, ISN_STORE,
-STACK_FRAME_SIZE - 1, NULL) == FAIL)
goto erret;
}
/* /*
* Loop over all the lines of the function and generate instructions. * Loop over all the lines of the function and generate instructions.
*/ */

View File

@@ -107,6 +107,35 @@ init_instr_idx(ufunc_T *ufunc, int argcount, ectx_T *ectx)
} }
} }
/*
* Create a new list from "count" items at the bottom of the stack.
* When "count" is zero an empty list is added to the stack.
*/
static int
exe_newlist(int count, ectx_T *ectx)
{
list_T *list = list_alloc_with_items(count);
int idx;
typval_T *tv;
if (list == NULL)
return FAIL;
for (idx = 0; idx < count; ++idx)
list_set_item(list, idx, STACK_TV_BOT(idx - count));
if (count > 0)
ectx->ec_stack.ga_len -= count - 1;
else if (ga_grow(&ectx->ec_stack, 1) == FAIL)
return FAIL;
else
++ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_LIST;
tv->vval.v_list = list;
++list->lv_refcount;
return OK;
}
/* /*
* Call compiled function "cdf_idx" from compiled code. * Call compiled function "cdf_idx" from compiled code.
* *
@@ -137,46 +166,34 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
if (ufunc->uf_va_name != NULL) if (ufunc->uf_va_name != NULL)
{ {
int iidx; // Need to make a list out of the vararg arguments.
isn_T *iptr;
// Need to make a list out of the vararg arguments. There is a
// ISN_NEWLIST instruction at the start of the function body, we need
// to move the arguments below the stack frame and pass the count.
// Stack at time of call with 2 varargs: // Stack at time of call with 2 varargs:
// normal_arg // normal_arg
// optional_arg // optional_arg
// vararg_1 // vararg_1
// vararg_2 // vararg_2
// When starting execution: // After creating the list:
// normal_arg
// optional_arg
// vararg-list
// With missing optional arguments we get:
// normal_arg // normal_arg
// optional_arg // After creating the list
// space list of varargs // normal_arg
// STACK_FRAME // (space for optional_arg)
// [local variables] // vararg-list
// vararg_1
// vararg_2
// TODO: This doesn't work if the same function is used for a default
// argument value. Forbid that somehow?
vararg_count = argcount - ufunc->uf_args.ga_len; vararg_count = argcount - ufunc->uf_args.ga_len;
if (vararg_count < 0) if (vararg_count < 0)
vararg_count = 0; vararg_count = 0;
else else
argcount -= vararg_count; argcount -= vararg_count;
if (ufunc->uf_def_arg_idx == NULL) if (exe_newlist(vararg_count, ectx) == FAIL)
iidx = 0;
else
iidx = ufunc->uf_def_arg_idx[ufunc->uf_def_args.ga_len];
iptr = &dfunc->df_instr[iidx];
if (iptr->isn_type != ISN_NEWLIST)
{
iemsg("Not a ISN_NEWLIST instruction");
return FAIL; return FAIL;
}
iptr->isn_arg.number = vararg_count; vararg_count = 1;
} }
arg_to_add = ufunc_argcount(ufunc) - argcount; arg_to_add = ufunc->uf_args.ga_len - argcount;
if (arg_to_add < 0) if (arg_to_add < 0)
{ {
iemsg("Argument count wrong?"); iemsg("Argument count wrong?");
@@ -185,21 +202,13 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL) if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL)
return FAIL; return FAIL;
if (vararg_count > 0) // Move the vararg-list to below the missing optional arguments.
{ if (vararg_count > 0 && arg_to_add > 0)
int stack_added = arg_to_add + STACK_FRAME_SIZE + dfunc->df_varcount; *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
// Move the varargs to below the stack frame.
// TODO: use mch_memmove()
for (idx = 1; idx <= vararg_count; ++idx)
*STACK_TV_BOT(stack_added - idx) = *STACK_TV_BOT(-idx);
ectx->ec_stack.ga_len -= vararg_count;
}
// Reserve space for omitted optional arguments, filled in soon. // Reserve space for omitted optional arguments, filled in soon.
// Also room for a list of varargs, if there is one.
for (idx = 0; idx < arg_to_add; ++idx) for (idx = 0; idx < arg_to_add; ++idx)
STACK_TV_BOT(idx)->v_type = VAR_UNKNOWN; STACK_TV_BOT(idx - vararg_count)->v_type = VAR_UNKNOWN;
ectx->ec_stack.ga_len += arg_to_add; ectx->ec_stack.ga_len += arg_to_add;
// Store current execution state in stack frame for ISN_RETURN. // Store current execution state in stack frame for ISN_RETURN.
@@ -213,8 +222,7 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
// Initialize local variables // Initialize local variables
for (idx = 0; idx < dfunc->df_varcount; ++idx) for (idx = 0; idx < dfunc->df_varcount; ++idx)
STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount;
+ vararg_count;
// Set execution state to the start of the called function. // Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx; ectx->ec_dfunc_idx = cdf_idx;
@@ -979,26 +987,8 @@ call_def_function(
// create a list from items on the stack; uses a single allocation // create a list from items on the stack; uses a single allocation
// for the list header and the items // for the list header and the items
case ISN_NEWLIST: case ISN_NEWLIST:
{ if (exe_newlist(iptr->isn_arg.number, &ectx) == FAIL)
int count = iptr->isn_arg.number; goto failed;
list_T *list = list_alloc_with_items(count);
if (list == NULL)
goto failed;
for (idx = 0; idx < count; ++idx)
list_set_item(list, idx, STACK_TV_BOT(idx - count));
if (count > 0)
ectx.ec_stack.ga_len -= count - 1;
else if (ga_grow(&ectx.ec_stack, 1) == FAIL)
goto failed;
else
++ectx.ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_LIST;
tv->vval.v_list = list;
++list->lv_refcount;
}
break; break;
// create a dict from items on the stack // create a dict from items on the stack