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

patch 8.2.0323: Vim9: calling a function that is defined later is slow

Problem:    Vim9: calling a function that is defined later is slow.
Solution:   Once the function is found update the instruction so it can be
            called directly.
This commit is contained in:
Bram Moolenaar
2020-02-26 21:24:23 +01:00
parent b35efa5ed0
commit 7eeefd4a39
4 changed files with 72 additions and 12 deletions

View File

@@ -222,6 +222,38 @@ def Test_disassemble_call()
enddef enddef
def FuncWithForwardCall(): string
return DefinedLater("yes")
enddef
def DefinedLater(arg: string): string
return arg
enddef
def Test_disassemble_update_instr()
let res = execute('disass FuncWithForwardCall')
assert_match('FuncWithForwardCall.*'
\ .. 'return DefinedLater("yes").*'
\ .. '\d PUSHS "yes".*'
\ .. '\d UCALL DefinedLater(argc 1).*'
\ .. '\d CHECKTYPE string stack\[-1].*'
\ .. '\d RETURN.*'
\, res)
" Calling the function will change UCALL into the faster DCALL
assert_equal('yes', FuncWithForwardCall())
res = execute('disass FuncWithForwardCall')
assert_match('FuncWithForwardCall.*'
\ .. 'return DefinedLater("yes").*'
\ .. '\d PUSHS "yes".*'
\ .. '\d DCALL DefinedLater(argc 1).*'
\ .. '\d CHECKTYPE string stack\[-1].*'
\ .. '\d RETURN.*'
\, res)
enddef
def FuncWithDefault(arg: string = 'default'): string def FuncWithDefault(arg: string = 'default'): string
return arg return arg
enddef enddef

View File

@@ -212,6 +212,19 @@ func DefinedLater(arg)
return a:arg return a:arg
endfunc endfunc
def FuncWithForwardCall()
return DefinedEvenLater("yes")
enddef
def DefinedEvenLater(arg: string): string
return arg
enddef
def Test_error_in_nested_function()
" Error in called function requires unwinding the call stack.
assert_fails('call FuncWithForwardCall()', 'E1029')
enddef
def Test_return_type_wrong() def Test_return_type_wrong()
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string') CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number') CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')

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 */
/**/
323,
/**/ /**/
322, 322,
/**/ /**/

View File

@@ -267,9 +267,10 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)
/* /*
* Execute a user defined function. * Execute a user defined function.
* "iptr" can be used to replace the instruction with a more efficient one.
*/ */
static int static int
call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx) call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
{ {
typval_T argvars[MAX_FUNC_ARGS]; typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe; funcexe_T funcexe;
@@ -277,8 +278,17 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
int idx; int idx;
if (ufunc->uf_dfunc_idx >= 0) if (ufunc->uf_dfunc_idx >= 0)
// The function has been compiled, can call it quickly. {
// The function has been compiled, can call it quickly. For a function
// that was defined later: we can call it directly next time.
if (iptr != NULL)
{
iptr->isn_type = ISN_DCALL;
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
iptr->isn_arg.dfunc.cdf_argcount = argcount;
}
return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx); return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx);
}
if (call_prepare(argcount, argvars, ectx) == FAIL) if (call_prepare(argcount, argvars, ectx) == FAIL)
return FAIL; return FAIL;
@@ -305,10 +315,11 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
/* /*
* Execute a function by "name". * Execute a function by "name".
* This can be a builtin function or a user function. * This can be a builtin function or a user function.
* "iptr" can be used to replace the instruction with a more efficient one.
* Returns FAIL if not found without an error message. * Returns FAIL if not found without an error message.
*/ */
static int static int
call_by_name(char_u *name, int argcount, ectx_T *ectx) call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
{ {
ufunc_T *ufunc; ufunc_T *ufunc;
@@ -325,7 +336,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx)
ufunc = find_func(name, NULL); ufunc = find_func(name, NULL);
if (ufunc != NULL) if (ufunc != NULL)
return call_ufunc(ufunc, argcount, ectx); return call_ufunc(ufunc, argcount, ectx, iptr);
return FAIL; return FAIL;
} }
@@ -341,12 +352,12 @@ call_partial(typval_T *tv, int argcount, ectx_T *ectx)
partial_T *pt = tv->vval.v_partial; partial_T *pt = tv->vval.v_partial;
if (pt->pt_func != NULL) if (pt->pt_func != NULL)
return call_ufunc(pt->pt_func, argcount, ectx); return call_ufunc(pt->pt_func, argcount, ectx, NULL);
name = pt->pt_name; name = pt->pt_name;
} }
else else
name = tv->vval.v_string; name = tv->vval.v_string;
if (call_by_name(name, argcount, ectx) == FAIL) if (call_by_name(name, argcount, ectx, NULL) == FAIL)
{ {
if (called_emsg == called_emsg_before) if (called_emsg == called_emsg_before)
semsg(_(e_unknownfunc), name); semsg(_(e_unknownfunc), name);
@@ -372,13 +383,14 @@ store_var(char_u *name, typval_T *tv)
/* /*
* Execute a function by "name". * Execute a function by "name".
* This can be a builtin function, user function or a funcref. * This can be a builtin function, user function or a funcref.
* "iptr" can be used to replace the instruction with a more efficient one.
*/ */
static int static int
call_eval_func(char_u *name, int argcount, ectx_T *ectx) call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
{ {
int called_emsg_before = called_emsg; int called_emsg_before = called_emsg;
if (call_by_name(name, argcount, ectx) == FAIL if (call_by_name(name, argcount, ectx, iptr) == FAIL
&& called_emsg == called_emsg_before) && called_emsg == called_emsg_before)
{ {
// "name" may be a variable that is a funcref or partial // "name" may be a variable that is a funcref or partial
@@ -983,7 +995,7 @@ call_def_function(
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
if (call_eval_func(cufunc->cuf_name, if (call_eval_func(cufunc->cuf_name,
cufunc->cuf_argcount, &ectx) == FAIL) cufunc->cuf_argcount, &ectx, iptr) == FAIL)
goto failed; goto failed;
} }
break; break;
@@ -1558,10 +1570,7 @@ call_def_function(
tv = STACK_TV_BOT(-1); tv = STACK_TV_BOT(-1);
if (check_not_string(tv) == FAIL) if (check_not_string(tv) == FAIL)
{
--ectx.ec_stack.ga_len;
goto failed; goto failed;
}
(void)tv_get_number_chk(tv, &error); (void)tv_get_number_chk(tv, &error);
if (error) if (error)
goto failed; goto failed;
@@ -1627,6 +1636,10 @@ done:
ret = OK; ret = OK;
failed: failed:
// When failed need to unwind the call stack.
while (ectx.ec_frame != initial_frame_ptr)
func_return(&ectx);
for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx) for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
clear_tv(STACK_TV(idx)); clear_tv(STACK_TV(idx));
vim_free(ectx.ec_stack.ga_data); vim_free(ectx.ec_stack.ga_data);