Patch 8.2.4823
Problem:    Concatenating more than 2 strings in a :def function is
            inefficient.
Solution:   Add a count to the CONCAT instruction. (closes #10276)
Files:      src/vim9.h, src/vim9cmds.c, src/vim9compile.c, src/vim9execute.c,
            src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.4822/src/vim9.h  2022-03-30 21:12:16.451923056 +0100
--- src/vim9.h  2022-04-25 12:30:38.844650280 +0100
***************
*** 152,158 ****
      ISN_COMPAREANY,
  
      // expression operations
!     ISN_CONCAT,
      ISN_STRINDEX,   // [expr] string index
      ISN_STRSLICE,   // [expr:expr] string slice
      ISN_LISTAPPEND, // append to a list, like add()
--- 152,158 ----
      ISN_COMPAREANY,
  
      // expression operations
!     ISN_CONCAT,     // concatenate isn_arg.number strings
      ISN_STRINDEX,   // [expr] string index
      ISN_STRSLICE,   // [expr:expr] string slice
      ISN_LISTAPPEND, // append to a list, like add()
*** ../vim-8.2.4822/src/vim9cmds.c      2022-04-02 19:43:53.927491819 +0100
--- src/vim9cmds.c      2022-04-25 12:30:38.844650280 +0100
***************
*** 2125,2131 ****
            generate_instr_type(cctx, ISN_REDIREND, &t_string);
  
            if (lhs->lhs_append)
!               generate_instr_drop(cctx, ISN_CONCAT, 1);
  
            if (lhs->lhs_has_index)
            {
--- 2125,2131 ----
            generate_instr_type(cctx, ISN_REDIREND, &t_string);
  
            if (lhs->lhs_append)
!               generate_CONCAT(cctx, 2);
  
            if (lhs->lhs_has_index)
            {
*** ../vim-8.2.4822/src/vim9compile.c   2022-04-21 23:29:58.948561831 +0100
--- src/vim9compile.c   2022-04-25 12:37:43.708353791 +0100
***************
*** 988,1001 ****
      if (evalstr && (p = (char_u *)strstr((char *)str, "`=")) != NULL)
      {
        char_u  *start = str;
  
        // Need to evaluate expressions of the form `=<expr>` in the string.
        // Split the string into literal strings and Vim expressions and
        // generate instructions to concatenate the literal strings and the
        // result of evaluating the Vim expressions.
-       val = vim_strsave((char_u *)"");
-       generate_PUSHS(cctx, &val);
- 
        for (;;)
        {
            if (p > start)
--- 988,999 ----
      if (evalstr && (p = (char_u *)strstr((char *)str, "`=")) != NULL)
      {
        char_u  *start = str;
+       int     count = 0;
  
        // Need to evaluate expressions of the form `=<expr>` in the string.
        // Split the string into literal strings and Vim expressions and
        // generate instructions to concatenate the literal strings and the
        // result of evaluating the Vim expressions.
        for (;;)
        {
            if (p > start)
***************
*** 1003,1009 ****
                // literal string before the expression
                val = vim_strnsave(start, p - start);
                generate_PUSHS(cctx, &val);
!               generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            p += 2;
  
--- 1001,1007 ----
                // literal string before the expression
                val = vim_strnsave(start, p - start);
                generate_PUSHS(cctx, &val);
!               count++;
            }
            p += 2;
  
***************
*** 1011,1017 ****
            if (compile_expr0(&p, cctx) == FAIL)
                return FAIL;
            may_generate_2STRING(-1, TRUE, cctx);
!           generate_instr_drop(cctx, ISN_CONCAT, 1);
  
            p = skipwhite(p);
            if (*p != '`')
--- 1009,1015 ----
            if (compile_expr0(&p, cctx) == FAIL)
                return FAIL;
            may_generate_2STRING(-1, TRUE, cctx);
!           count++;
  
            p = skipwhite(p);
            if (*p != '`')
***************
*** 1029,1039 ****
                {
                    val = vim_strsave(start);
                    generate_PUSHS(cctx, &val);
!                   generate_instr_drop(cctx, ISN_CONCAT, 1);
                }
                break;
            }
        }
      }
      else
      {
--- 1027,1040 ----
                {
                    val = vim_strsave(start);
                    generate_PUSHS(cctx, &val);
!                   count++;
                }
                break;
            }
        }
+ 
+       if (count > 1)
+           generate_CONCAT(cctx, count);
      }
      else
      {
***************
*** 2382,2388 ****
  
            if (*op == '.')
            {
!               if (generate_instr_drop(cctx, ISN_CONCAT, 1) == NULL)
                    goto theend;
            }
            else if (*op == '+')
--- 2383,2389 ----
  
            if (*op == '.')
            {
!               if (generate_CONCAT(cctx, 2) == FAIL)
                    goto theend;
            }
            else if (*op == '+')
*** ../vim-8.2.4822/src/vim9execute.c   2022-04-15 20:50:13.463235819 +0100
--- src/vim9execute.c   2022-04-25 12:40:20.660175789 +0100
***************
*** 120,125 ****
--- 120,167 ----
  }
  
  /*
+  * Create a new string from "count" items at the bottom of the stack.
+  * A trailing NUL is appended.
+  * When "count" is zero an empty string is added to the stack.
+  */
+     static int
+ exe_concat(int count, ectx_T *ectx)
+ {
+     int               idx;
+     int               len = 0;
+     typval_T  *tv;
+     garray_T  ga;
+ 
+     ga_init2(&ga, sizeof(char), 1);
+     // Preallocate enough space for the whole string to avoid having to grow
+     // and copy.
+     for (idx = 0; idx < count; ++idx)
+     {
+       tv = STACK_TV_BOT(idx - count);
+       if (tv->vval.v_string != NULL)
+           len += (int)STRLEN(tv->vval.v_string);
+     }
+ 
+     if (ga_grow(&ga, len + 1) == FAIL)
+       return FAIL;
+ 
+     for (idx = 0; idx < count; ++idx)
+     {
+       tv = STACK_TV_BOT(idx - count);
+       ga_concat(&ga, tv->vval.v_string);
+       clear_tv(tv);
+     }
+ 
+     // add a terminating NUL
+     ga_append(&ga, NUL);
+ 
+     ectx->ec_stack.ga_len -= count - 1;
+     STACK_TV_BOT(-1)->vval.v_string = ga.ga_data;
+ 
+     return OK;
+ }
+ 
+ /*
   * Create a new list from "count" items at the bottom of the stack.
   * When "count" is zero an empty list is added to the stack.
   * When "count" is -1 a NULL list is added to the stack.
***************
*** 3536,3541 ****
--- 3578,3588 ----
                }
                break;
  
+           case ISN_CONCAT:
+               if (exe_concat(iptr->isn_arg.number, ectx) == FAIL)
+                   goto theend;
+               break;
+ 
            // create a partial with NULL value
            case ISN_NEWPARTIAL:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
***************
*** 4343,4362 ****
                }
                break;
  
-           case ISN_CONCAT:
-               {
-                   char_u *str1 = STACK_TV_BOT(-2)->vval.v_string;
-                   char_u *str2 = STACK_TV_BOT(-1)->vval.v_string;
-                   char_u *res;
- 
-                   res = concat_str(str1, str2);
-                   clear_tv(STACK_TV_BOT(-2));
-                   clear_tv(STACK_TV_BOT(-1));
-                   --ectx->ec_stack.ga_len;
-                   STACK_TV_BOT(-1)->vval.v_string = res;
-               }
-               break;
- 
            case ISN_STRINDEX:
            case ISN_STRSLICE:
                {
--- 4390,4395 ----
***************
*** 6083,6089 ****
            case ISN_ADDBLOB: smsg("%s%4d ADDBLOB", pfx, current); break;
  
            // expression operations
!           case ISN_CONCAT: smsg("%s%4d CONCAT", pfx, current); break;
            case ISN_STRINDEX: smsg("%s%4d STRINDEX", pfx, current); break;
            case ISN_STRSLICE: smsg("%s%4d STRSLICE", pfx, current); break;
            case ISN_BLOBINDEX: smsg("%s%4d BLOBINDEX", pfx, current); break;
--- 6116,6125 ----
            case ISN_ADDBLOB: smsg("%s%4d ADDBLOB", pfx, current); break;
  
            // expression operations
!           case ISN_CONCAT:
!               smsg("%s%4d CONCAT size %lld", pfx, current,
!                                           
(varnumber_T)(iptr->isn_arg.number));
!               break;
            case ISN_STRINDEX: smsg("%s%4d STRINDEX", pfx, current); break;
            case ISN_STRSLICE: smsg("%s%4d STRSLICE", pfx, current); break;
            case ISN_BLOBINDEX: smsg("%s%4d BLOBINDEX", pfx, current); break;
*** ../vim-8.2.4822/src/vim9expr.c      2022-04-02 21:59:02.481697389 +0100
--- src/vim9expr.c      2022-04-25 12:30:38.848650279 +0100
***************
*** 2513,2519 ****
                if (may_generate_2STRING(-2, FALSE, cctx) == FAIL
                        || may_generate_2STRING(-1, FALSE, cctx) == FAIL)
                    return FAIL;
!               generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            else
                generate_two_op(cctx, op);
--- 2513,2520 ----
                if (may_generate_2STRING(-2, FALSE, cctx) == FAIL
                        || may_generate_2STRING(-1, FALSE, cctx) == FAIL)
                    return FAIL;
!               if (generate_CONCAT(cctx, 2) == FAIL)
!                   return FAIL;
            }
            else
                generate_two_op(cctx, op);
*** ../vim-8.2.4822/src/vim9instr.c     2022-03-31 20:02:52.422045605 +0100
--- src/vim9instr.c     2022-04-25 12:30:38.848650279 +0100
***************
*** 472,477 ****
--- 472,504 ----
  }
  
  /*
+  * Generate an ISN_CONCAT instruction.
+  * "count" is the number of stack elements to join together and it must be
+  * greater or equal to one.
+  * The caller ensures all the "count" elements on the stack have the right 
type.
+  */
+     int
+ generate_CONCAT(cctx_T *cctx, int count)
+ {
+     isn_T     *isn;
+     garray_T  *stack = &cctx->ctx_type_stack;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+ 
+     if (count < 1)
+       return FAIL;
+ 
+     if ((isn = generate_instr(cctx, ISN_CONCAT)) == NULL)
+       return FAIL;
+     isn->isn_arg.number = count;
+ 
+     // drop the argument types
+     stack->ga_len -= count - 1;
+ 
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_2BOOL instruction.
   * "offset" is the offset in the type stack.
   */
*** ../vim-8.2.4822/src/proto/vim9instr.pro     2022-03-30 21:12:16.451923056 
+0100
--- src/proto/vim9instr.pro     2022-04-25 12:30:38.844650280 +0100
***************
*** 62,67 ****
--- 62,68 ----
  int generate_EXECCONCAT(cctx_T *cctx, int count);
  int generate_RANGE(cctx_T *cctx, char_u *range);
  int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
+ int generate_CONCAT(cctx_T *cctx, int count);
  int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
  int generate_undo_cmdmods(cctx_T *cctx);
  int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int 
vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name);
*** ../vim-8.2.4822/src/testdir/test_vim9_disassemble.vim       2022-04-02 
21:59:02.481697389 +0100
--- src/testdir/test_vim9_disassemble.vim       2022-04-25 12:30:38.844650280 
+0100
***************
*** 208,214 ****
          ' redir END\_s*' ..
          '\d LOAD $0\_s*' ..
          '\d REDIR END\_s*' ..
!         '\d CONCAT\_s*' ..
          '\d STORE $0\_s*' ..
          '\d RETURN void',
          res)
--- 208,214 ----
          ' redir END\_s*' ..
          '\d LOAD $0\_s*' ..
          '\d REDIR END\_s*' ..
!         '\d CONCAT size 2\_s*' ..
          '\d STORE $0\_s*' ..
          '\d RETURN void',
          res)
***************
*** 883,889 ****
          'local ..= arg\_s*' ..
          '\d LOADOUTER level 1 $0\_s*' ..
          '\d LOAD arg\[-1\]\_s*' ..
!         '\d CONCAT\_s*' ..
          '\d STOREOUTER level 1 $0\_s*' ..
          '\d RETURN void',
          res)
--- 883,889 ----
          'local ..= arg\_s*' ..
          '\d LOADOUTER level 1 $0\_s*' ..
          '\d LOAD arg\[-1\]\_s*' ..
!         '\d CONCAT size 2\_s*' ..
          '\d STOREOUTER level 1 $0\_s*' ..
          '\d RETURN void',
          res)
***************
*** 973,979 ****
          '6 LOAD arg\[-2]\_s*' ..
          '\d LOAD arg\[-1]\_s*' ..
          '\d 2STRING stack\[-1]\_s*' ..
!         '\d\+ CONCAT\_s*' ..
          '\d\+ RETURN',
          res)
  enddef
--- 973,979 ----
          '6 LOAD arg\[-2]\_s*' ..
          '\d LOAD arg\[-1]\_s*' ..
          '\d 2STRING stack\[-1]\_s*' ..
!         '\d\+ CONCAT size 2\_s*' ..
          '\d\+ RETURN',
          res)
  enddef
***************
*** 1245,1253 ****
          '\d PUSHS "X"\_s*' ..
          '\d LOAD arg\[-1\]\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
!         '\d CONCAT\_s*' ..
          '\d PUSHS "X"\_s*' ..
!         '\d CONCAT\_s*' ..
          '\d RETURN',
          instr)
  enddef
--- 1245,1253 ----
          '\d PUSHS "X"\_s*' ..
          '\d LOAD arg\[-1\]\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
!         '\d CONCAT size 2\_s*' ..
          '\d PUSHS "X"\_s*' ..
!         '\d CONCAT size 2\_s*' ..
          '\d RETURN',
          instr)
  enddef
***************
*** 1432,1438 ****
          '\d\+ LOAD $0\_s*' ..
          '\d\+ LOAD $2\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
!         '\d\+ CONCAT\_s*' ..
          '\d\+ STORE $0\_s*' ..
          'endfor\_s*' ..
          '\d\+ JUMP -> 5\_s*' ..
--- 1432,1438 ----
          '\d\+ LOAD $0\_s*' ..
          '\d\+ LOAD $2\_s*' ..
          '\d 2STRING_ANY stack\[-1\]\_s*' ..
!         '\d\+ CONCAT size 2\_s*' ..
          '\d\+ STORE $0\_s*' ..
          'endfor\_s*' ..
          '\d\+ JUMP -> 5\_s*' ..
***************
*** 2142,2148 ****
          "execute 'help ' .. tag\\_s*" ..
          '\d\+ PUSHS "help "\_s*' ..
          '\d\+ LOAD $1\_s*' ..
!         '\d\+ CONCAT\_s*' ..
          '\d\+ EXECUTE 1\_s*' ..
          '\d\+ RETURN void',
          res)
--- 2142,2148 ----
          "execute 'help ' .. tag\\_s*" ..
          '\d\+ PUSHS "help "\_s*' ..
          '\d\+ LOAD $1\_s*' ..
!         '\d\+ CONCAT size 2\_s*' ..
          '\d\+ EXECUTE 1\_s*' ..
          '\d\+ RETURN void',
          res)
*** ../vim-8.2.4822/src/version.c       2022-04-24 21:54:56.061612118 +0100
--- src/version.c       2022-04-25 12:36:00.756428126 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4823,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
48. You get a tatoo that says "This body best viewed with Netscape 3.1 or
    higher."

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220425114402.070FF1C4111%40moolenaar.net.

Raspunde prin e-mail lui