Patch 8.2.0323
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.
Files: src/vim9execute.c, src/testdir/test_vim9_script.vim,
src/testdir/test_vim9_disassemble.vim
*** ../vim-8.2.0322/src/vim9execute.c 2020-02-26 18:23:39.558650843 +0100
--- src/vim9execute.c 2020-02-26 21:10:19.496702678 +0100
***************
*** 267,275 ****
/*
* Execute a user defined function.
*/
static int
! call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx)
{
typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe;
--- 267,276 ----
/*
* Execute a user defined function.
+ * "iptr" can be used to replace the instruction with a more efficient one.
*/
static int
! call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
{
typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe;
***************
*** 277,284 ****
int idx;
if (ufunc->uf_dfunc_idx >= 0)
! // The function has been compiled, can call it quickly.
return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx);
if (call_prepare(argcount, argvars, ectx) == FAIL)
return FAIL;
--- 278,294 ----
int idx;
if (ufunc->uf_dfunc_idx >= 0)
! {
! // 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);
+ }
if (call_prepare(argcount, argvars, ectx) == FAIL)
return FAIL;
***************
*** 305,314 ****
/*
* Execute a function by "name".
* This can be a builtin function or a user function.
* Returns FAIL if not found without an error message.
*/
static int
! call_by_name(char_u *name, int argcount, ectx_T *ectx)
{
ufunc_T *ufunc;
--- 315,325 ----
/*
* Execute a function by "name".
* 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.
*/
static int
! call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
{
ufunc_T *ufunc;
***************
*** 325,331 ****
ufunc = find_func(name, NULL);
if (ufunc != NULL)
! return call_ufunc(ufunc, argcount, ectx);
return FAIL;
}
--- 336,342 ----
ufunc = find_func(name, NULL);
if (ufunc != NULL)
! return call_ufunc(ufunc, argcount, ectx, iptr);
return FAIL;
}
***************
*** 341,352 ****
partial_T *pt = tv->vval.v_partial;
if (pt->pt_func != NULL)
! return call_ufunc(pt->pt_func, argcount, ectx);
name = pt->pt_name;
}
else
name = tv->vval.v_string;
! if (call_by_name(name, argcount, ectx) == FAIL)
{
if (called_emsg == called_emsg_before)
semsg(_(e_unknownfunc), name);
--- 352,363 ----
partial_T *pt = tv->vval.v_partial;
if (pt->pt_func != NULL)
! return call_ufunc(pt->pt_func, argcount, ectx, NULL);
name = pt->pt_name;
}
else
name = tv->vval.v_string;
! if (call_by_name(name, argcount, ectx, NULL) == FAIL)
{
if (called_emsg == called_emsg_before)
semsg(_(e_unknownfunc), name);
***************
*** 372,384 ****
/*
* Execute a function by "name".
* This can be a builtin function, user function or a funcref.
*/
static int
! call_eval_func(char_u *name, int argcount, ectx_T *ectx)
{
int called_emsg_before = called_emsg;
! if (call_by_name(name, argcount, ectx) == FAIL
&& called_emsg == called_emsg_before)
{
// "name" may be a variable that is a funcref or partial
--- 383,396 ----
/*
* Execute a function by "name".
* 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
! call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
{
int called_emsg_before = called_emsg;
! if (call_by_name(name, argcount, ectx, iptr) == FAIL
&& called_emsg == called_emsg_before)
{
// "name" may be a variable that is a funcref or partial
***************
*** 983,989 ****
SOURCING_LNUM = iptr->isn_lnum;
if (call_eval_func(cufunc->cuf_name,
! cufunc->cuf_argcount, &ectx) == FAIL)
goto failed;
}
break;
--- 995,1001 ----
SOURCING_LNUM = iptr->isn_lnum;
if (call_eval_func(cufunc->cuf_name,
! cufunc->cuf_argcount, &ectx, iptr) == FAIL)
goto failed;
}
break;
***************
*** 1558,1567 ****
tv = STACK_TV_BOT(-1);
if (check_not_string(tv) == FAIL)
- {
- --ectx.ec_stack.ga_len;
goto failed;
- }
(void)tv_get_number_chk(tv, &error);
if (error)
goto failed;
--- 1570,1576 ----
***************
*** 1627,1632 ****
--- 1636,1645 ----
ret = OK;
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)
clear_tv(STACK_TV(idx));
vim_free(ectx.ec_stack.ga_data);
*** ../vim-8.2.0322/src/testdir/test_vim9_script.vim 2020-02-26 20:15:14.944051070 +0100
--- src/testdir/test_vim9_script.vim 2020-02-26 21:18:22.942880429 +0100
***************
*** 212,217 ****
--- 212,230 ----
return a:arg
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()
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
*** ../vim-8.2.0322/src/testdir/test_vim9_disassemble.vim 2020-02-26 18:23:39.558650843 +0100
--- src/testdir/test_vim9_disassemble.vim 2020-02-26 21:14:48.599676404 +0100
***************
*** 222,227 ****
--- 222,259 ----
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
return arg
enddef
*** ../vim-8.2.0322/src/version.c 2020-02-26 20:15:14.944051070 +0100
--- src/version.c 2020-02-26 20:33:15.872922657 +0100
***************
*** 740,741 ****
--- 740,743 ----
{ /* Add new patch number below this line */
+ /**/
+ 323,
/**/
--
hundred-and-one symptoms of being an internet addict:
125. You begin to wonder how often it REALLY is necessary to get up
and shower or bathe.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///