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.
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 4f22654..9ecb865 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -267,9 +267,10 @@
 
 /*
  * 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)
+call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
 {
     typval_T	argvars[MAX_FUNC_ARGS];
     funcexe_T   funcexe;
@@ -277,8 +278,17 @@
     int		idx;
 
     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);
+    }
 
     if (call_prepare(argcount, argvars, ectx) == FAIL)
 	return FAIL;
@@ -305,10 +315,11 @@
 /*
  * 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)
+call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
 {
     ufunc_T *ufunc;
 
@@ -325,7 +336,7 @@
 
     ufunc = find_func(name, NULL);
     if (ufunc != NULL)
-	return call_ufunc(ufunc, argcount, ectx);
+	return call_ufunc(ufunc, argcount, ectx, iptr);
 
     return FAIL;
 }
@@ -341,12 +352,12 @@
 	partial_T *pt = tv->vval.v_partial;
 
 	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;
     }
     else
 	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)
 	    semsg(_(e_unknownfunc), name);
@@ -372,13 +383,14 @@
 /*
  * 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)
+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) == FAIL
+    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,7 +995,7 @@
 
 		    SOURCING_LNUM = iptr->isn_lnum;
 		    if (call_eval_func(cufunc->cuf_name,
-					  cufunc->cuf_argcount, &ectx) == FAIL)
+				    cufunc->cuf_argcount, &ectx, iptr) == FAIL)
 			goto failed;
 		}
 		break;
@@ -1558,10 +1570,7 @@
 
 		    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;
@@ -1627,6 +1636,10 @@
     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);