To: vim_dev@googlegroups.com Subject: Patch 8.2.0323 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///