# HG changeset patch # User Bram Moolenaar # Date 1627148704 -7200 # Node ID 0c5b84c5862a38a6ce62715955bf8c3149704a81 # Parent d631e1a737d299d8922103503a490f77c461cf56 patch 8.2.3212: Vim9: execution speed can be improved Commit: https://github.com/vim/vim/commit/5a9e5845e1539592e94963fcdf5b3ad4fdc59db4 Author: Dominique Pelle 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) diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9execute.c b/src/vim9execute.c --- 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);