changeset 25350:0c5b84c5862a v8.2.3212

patch 8.2.3212: Vim9: execution speed can be improved Commit: https://github.com/vim/vim/commit/5a9e5845e1539592e94963fcdf5b3ad4fdc59db4 Author: Dominique Pelle <dominique.pelle@gmail.com> Date: Sat Jul 24 19:32:12 2021 +0200 patch 8.2.3212: Vim9: execution speed can be improved Problem: Vim9: execution speed can be improved. Solution: Use __builtin_expect() to have the compiler produce better code. (Dominique Pell?, closes #8613)
author Bram Moolenaar <Bram@vim.org>
date Sat, 24 Jul 2021 19:45:04 +0200
parents d631e1a737d2
children b1237e9ced5f
files src/version.c src/vim9execute.c
diffstat 2 files changed, 54 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3212,
+/**/
     3211,
 /**/
     3210,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -22,6 +22,14 @@
 
 #include "vim9.h"
 
+#if defined(__GNUC__) || defined(__clang__)
+# define likely(x)    __builtin_expect((x), 1)
+# define unlikely(x)  __builtin_expect((x), 0)
+#else
+# define unlikely(x)  (x)
+# define likely(x)    (x)
+#endif
+
 // Structure put on ec_trystack when ISN_TRY is encountered.
 typedef struct {
     int	    tcd_frame_idx;	// ec_frame_idx at ISN_TRY
@@ -137,7 +145,7 @@ exe_newlist(int count, ectx_T *ectx)
 
     if (count > 0)
 	ectx->ec_stack.ga_len -= count - 1;
-    else if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+    else if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 	return FAIL;
     else
 	++ectx->ec_stack.ga_len;
@@ -210,7 +218,7 @@ call_dfunc(
 #ifdef FEAT_PROFILE
     if (do_profiling == PROF_YES)
     {
-	if (ga_grow(&profile_info_ga, 1) == OK)
+	if (likely(ga_grow(&profile_info_ga, 1) == OK))
 	{
 	    profinfo_T *info = ((profinfo_T *)profile_info_ga.ga_data)
 						      + profile_info_ga.ga_len;
@@ -289,8 +297,8 @@ call_dfunc(
     // - if needed: a counter for number of closures created in
     //   ectx->ec_funcrefs.
     varcount = dfunc->df_varcount + dfunc->df_has_closure;
-    if (ga_grow(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount)
-								       == FAIL)
+    if (unlikely(ga_grow(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE
+							  + varcount) == FAIL))
 	return FAIL;
 
     // If depth of calling is getting too high, don't execute the function.
@@ -361,7 +369,7 @@ call_dfunc(
 	else
 	{
 	    ref->or_outer = ALLOC_CLEAR_ONE(outer_T);
-	    if (ref->or_outer == NULL)
+	    if (unlikely(ref->or_outer == NULL))
 	    {
 		vim_free(ref);
 		return FAIL;
@@ -703,7 +711,7 @@ call_prepare(int argcount, typval_T *arg
     // Result replaces the arguments on the stack.
     if (argcount > 0)
 	ectx->ec_stack.ga_len -= argcount - 1;
-    else if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+    else if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 	return FAIL;
     else
 	++ectx->ec_stack.ga_len;
@@ -941,7 +949,7 @@ call_partial(
 	{
 	    // Make space for arguments from the partial, shift the "argcount"
 	    // arguments up.
-	    if (ga_grow(&ectx->ec_stack, pt->pt_argc) == FAIL)
+	    if (unlikely(ga_grow(&ectx->ec_stack, pt->pt_argc) == FAIL))
 		return FAIL;
 	    for (i = 1; i <= argcount; ++i)
 		*STACK_TV_BOT(-i + pt->pt_argc) = *STACK_TV_BOT(-i);
@@ -1371,7 +1379,7 @@ fill_partial_and_closure(partial_T *pt, 
 	// If this function returns and the closure is still being used, we
 	// need to make a copy of the context (arguments and local variables).
 	// Store a reference to the partial so we can handle that.
-	if (ga_grow(&ectx->ec_funcrefs, 1) == FAIL)
+	if (unlikely(ga_grow(&ectx->ec_funcrefs, 1) == FAIL))
 	{
 	    vim_free(pt);
 	    return FAIL;
@@ -1504,7 +1512,7 @@ handle_debug(isn_T *iptr, ectx_T *ectx)
 	    p = skipwhite(p);
 	    if (*p == '#')
 		break;
-	    if (ga_grow(&ga, 1) == OK)
+	    if (likely(ga_grow(&ga, 1) == OK))
 		((char_u **)(ga.ga_data))[ga.ga_len++] = p;
 	    if (STRNCMP(p, "def ", 4) == 0)
 		break;
@@ -1544,12 +1552,12 @@ exec_instructions(ectx_T *ectx)
 	isn_T	    *iptr;
 	typval_T    *tv;
 
-	if (++breakcheck_count >= 100)
+	if (unlikely(++breakcheck_count >= 100))
 	{
 	    line_breakcheck();
 	    breakcheck_count = 0;
 	}
-	if (got_int)
+	if (unlikely(got_int))
 	{
 	    // Turn CTRL-C into an exception.
 	    got_int = FALSE;
@@ -1558,7 +1566,7 @@ exec_instructions(ectx_T *ectx)
 	    did_throw = TRUE;
 	}
 
-	if (did_emsg && msg_list != NULL && *msg_list != NULL)
+	if (unlikely(did_emsg && msg_list != NULL && *msg_list != NULL))
 	{
 	    // Turn an error message into an exception.
 	    did_emsg = FALSE;
@@ -1568,7 +1576,7 @@ exec_instructions(ectx_T *ectx)
 	    *msg_list = NULL;
 	}
 
-	if (did_throw)
+	if (unlikely(did_throw))
 	{
 	    garray_T	*trystack = &ectx->ec_trystack;
 	    trycmd_T    *trycmd = NULL;
@@ -1605,7 +1613,7 @@ exec_instructions(ectx_T *ectx)
 	    {
 		// Not inside try or need to return from current functions.
 		// Push a dummy return value.
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		tv = STACK_TV_BOT(0);
 		tv->v_type = VAR_NUMBER;
@@ -1680,7 +1688,7 @@ exec_instructions(ectx_T *ectx)
 		    int	    res;
 		    int	    save_flags = cmdmod.cmod_flags;
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    tv = STACK_TV_BOT(0);
 		    init_tv(tv);
@@ -1696,7 +1704,7 @@ exec_instructions(ectx_T *ectx)
 	    // push typeval VAR_INSTR with instructions to be executed
 	    case ISN_INSTR:
 		{
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    tv = STACK_TV_BOT(0);
 		    tv->vval.v_instr = ALLOC_ONE(instr_T);
@@ -1761,7 +1769,7 @@ exec_instructions(ectx_T *ectx)
 		    clear_redir_lval();
 		    redir_vname = 0;
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    {
 			vim_free(res);
 			goto theend;
@@ -1828,7 +1836,7 @@ exec_instructions(ectx_T *ectx)
 			if (pass == 1)
 			{
 			    cmd = alloc(len + 1);
-			    if (cmd == NULL)
+			    if (unlikely(cmd == NULL))
 				goto theend;
 			    len = 0;
 			}
@@ -1897,7 +1905,7 @@ exec_instructions(ectx_T *ectx)
 			    p = tv_stringify(tv, buf);
 
 			len = (int)STRLEN(p);
-			if (ga_grow(&ga, len + 2) == FAIL)
+			if (unlikely(ga_grow(&ga, len + 2) == FAIL))
 			    failed = TRUE;
 			else
 			{
@@ -1948,7 +1956,7 @@ exec_instructions(ectx_T *ectx)
 
 	    // load local variable or argument
 	    case ISN_LOAD:
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		copy_tv(STACK_TV_VAR(iptr->isn_arg.number), STACK_TV_BOT(0));
 		++ectx->ec_stack.ga_len;
@@ -1956,7 +1964,7 @@ exec_instructions(ectx_T *ectx)
 
 	    // load v: variable
 	    case ISN_LOADV:
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		copy_tv(get_vim_var_tv(iptr->isn_arg.number), STACK_TV_BOT(0));
 		++ectx->ec_stack.ga_len;
@@ -1972,7 +1980,7 @@ exec_instructions(ectx_T *ectx)
 		    if (sv == NULL)
 			goto theend;
 		    allocate_if_null(sv->sv_tv);
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    copy_tv(sv->sv_tv, STACK_TV_BOT(0));
 		    ++ectx->ec_stack.ga_len;
@@ -1995,7 +2003,7 @@ exec_instructions(ectx_T *ectx)
 		    }
 		    else
 		    {
-			if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+			if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			    goto theend;
 			copy_tv(&di->di_tv, STACK_TV_BOT(0));
 			++ectx->ec_stack.ga_len;
@@ -2045,7 +2053,7 @@ exec_instructions(ectx_T *ectx)
 		    }
 		    else
 		    {
-			if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+			if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			    goto theend;
 			copy_tv(&di->di_tv, STACK_TV_BOT(0));
 			++ectx->ec_stack.ga_len;
@@ -2058,7 +2066,7 @@ exec_instructions(ectx_T *ectx)
 		{
 		    char_u *name = iptr->isn_arg.string;
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    SOURCING_LNUM = iptr->isn_lnum;
 		    if (eval_variable(name, (int)STRLEN(name),
@@ -2085,7 +2093,7 @@ exec_instructions(ectx_T *ectx)
 			default:  // Cannot reach here
 			    goto theend;
 		    }
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    tv = STACK_TV_BOT(0);
 		    tv->v_type = VAR_DICT;
@@ -2104,7 +2112,7 @@ exec_instructions(ectx_T *ectx)
 
 		    // This is not expected to fail, name is checked during
 		    // compilation: don't set SOURCING_LNUM.
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    if (eval_option(&name, &optval, TRUE) == FAIL)
 			goto theend;
@@ -2119,7 +2127,7 @@ exec_instructions(ectx_T *ectx)
 		    typval_T	optval;
 		    char_u	*name = iptr->isn_arg.string;
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    // name is always valid, checked when compiling
 		    (void)eval_env_var(&name, &optval, TRUE);
@@ -2130,7 +2138,7 @@ exec_instructions(ectx_T *ectx)
 
 	    // load @register
 	    case ISN_LOADREG:
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		tv = STACK_TV_BOT(0);
 		tv->v_type = VAR_STRING;
@@ -2565,7 +2573,7 @@ exec_instructions(ectx_T *ectx)
 				    + iptr->isn_arg.outer.outer_idx;
 		    if (iptr->isn_type == ISN_LOADOUTER)
 		    {
-			if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+			if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			    goto theend;
 			copy_tv(tv, STACK_TV_BOT(0));
 			++ectx->ec_stack.ga_len;
@@ -2753,7 +2761,7 @@ exec_instructions(ectx_T *ectx)
 	    case ISN_PUSHFUNC:
 	    case ISN_PUSHCHANNEL:
 	    case ISN_PUSHJOB:
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		tv = STACK_TV_BOT(0);
 		tv->v_lock = 0;
@@ -2842,7 +2850,7 @@ exec_instructions(ectx_T *ectx)
 		    char_u	*key;
 		    int		idx;
 
-		    if (dict == NULL)
+		    if (unlikely(dict == NULL))
 			goto theend;
 		    for (idx = 0; idx < count; ++idx)
 		    {
@@ -2861,7 +2869,7 @@ exec_instructions(ectx_T *ectx)
 			}
 			item = dictitem_alloc(key);
 			clear_tv(tv);
-			if (item == NULL)
+			if (unlikely(item == NULL))
 			{
 			    dict_unref(dict);
 			    goto theend;
@@ -2878,7 +2886,7 @@ exec_instructions(ectx_T *ectx)
 
 		    if (count > 0)
 			ectx->ec_stack.ga_len -= 2 * count - 1;
-		    else if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    else if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    else
 			++ectx->ec_stack.ga_len;
@@ -2960,7 +2968,7 @@ exec_instructions(ectx_T *ectx)
 
 	    // return from a :def function call without a value
 	    case ISN_RETURN_VOID:
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		tv = STACK_TV_BOT(0);
 		++ectx->ec_stack.ga_len;
@@ -3002,7 +3010,7 @@ exec_instructions(ectx_T *ectx)
 
 		    if (pt == NULL)
 			goto theend;
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    {
 			vim_free(pt);
 			goto theend;
@@ -3097,7 +3105,7 @@ exec_instructions(ectx_T *ectx)
 		    typval_T	*idxtv =
 				   STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    if (ltv->v_type == VAR_LIST)
 		    {
@@ -3204,7 +3212,7 @@ exec_instructions(ectx_T *ectx)
 		{
 		    trycmd_T    *trycmd = NULL;
 
-		    if (GA_GROW(&ectx->ec_trystack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_trystack, 1) == FAIL))
 			goto theend;
 		    trycmd = ((trycmd_T *)ectx->ec_trystack.ga_data)
 						     + ectx->ec_trystack.ga_len;
@@ -3229,7 +3237,7 @@ exec_instructions(ectx_T *ectx)
 		    iemsg("Evaluating catch while current_exception is NULL");
 		    goto theend;
 		}
-		if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 		    goto theend;
 		tv = STACK_TV_BOT(0);
 		++ectx->ec_stack.ga_len;
@@ -3888,7 +3896,7 @@ exec_instructions(ectx_T *ectx)
 		    tv = STACK_TV_BOT(-1 - gi->gi_with_op);
 		    li = list_find(tv->vval.v_list, gi->gi_index);
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    ++ectx->ec_stack.ga_len;
 		    copy_tv(&li->li_tv, STACK_TV_BOT(-1));
@@ -4123,7 +4131,7 @@ exec_instructions(ectx_T *ectx)
 		    if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
 			goto on_error;
 
-		    if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, 1) == FAIL))
 			goto theend;
 		    ++ectx->ec_stack.ga_len;
 		    tv = STACK_TV_BOT(-1);
@@ -4225,7 +4233,7 @@ exec_instructions(ectx_T *ectx)
 		    }
 
 		    CHECK_LIST_MATERIALIZE(l);
-		    if (GA_GROW(&ectx->ec_stack, count - 1) == FAIL)
+		    if (unlikely(GA_GROW(&ectx->ec_stack, count - 1) == FAIL))
 			goto theend;
 		    ectx->ec_stack.ga_len += count - 1;
 
@@ -4499,7 +4507,7 @@ call_def_function(
     CLEAR_FIELD(ectx);
     ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
     ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
-    if (ga_grow(&ectx.ec_stack, 20) == FAIL)
+    if (unlikely(ga_grow(&ectx.ec_stack, 20) == FAIL))
     {
 	funcdepth_decrement();
 	return FAIL;
@@ -5506,7 +5514,7 @@ list_instructions(char *pfx, isn_T *inst
 				  NULL, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);
 
 		    buf = alloc(len + 1);
-		    if (buf != NULL)
+		    if (likely(buf != NULL))
 		    {
 			(void)produce_cmdmods(
 				   buf, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);