comparison src/vim9execute.c @ 19981:014daa59ba50 v8.2.0546

patch 8.2.0546: Vim9: varargs implementation is inefficient Commit: https://github.com/vim/vim/commit/fe270817247b73a9315bb10f0a51b6eca406d300 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Apr 11 22:31:27 2020 +0200 patch 8.2.0546: Vim9: varargs implementation is inefficient Problem: Vim9: varargs implementation is inefficient. Solution: Create list without moving the arguments.
author Bram Moolenaar <Bram@vim.org>
date Sat, 11 Apr 2020 22:45:03 +0200
parents 4e8e0ce576af
children aadd1cae2ff5
comparison
equal deleted inserted replaced
19980:ee7fafbfbf8a 19981:014daa59ba50
103 if (defcount < 0) 103 if (defcount < 0)
104 defcount = 0; 104 defcount = 0;
105 ectx->ec_iidx = ufunc->uf_def_arg_idx[ 105 ectx->ec_iidx = ufunc->uf_def_arg_idx[
106 ufunc->uf_def_args.ga_len - defcount]; 106 ufunc->uf_def_args.ga_len - defcount];
107 } 107 }
108 }
109
110 /*
111 * Create a new list from "count" items at the bottom of the stack.
112 * When "count" is zero an empty list is added to the stack.
113 */
114 static int
115 exe_newlist(int count, ectx_T *ectx)
116 {
117 list_T *list = list_alloc_with_items(count);
118 int idx;
119 typval_T *tv;
120
121 if (list == NULL)
122 return FAIL;
123 for (idx = 0; idx < count; ++idx)
124 list_set_item(list, idx, STACK_TV_BOT(idx - count));
125
126 if (count > 0)
127 ectx->ec_stack.ga_len -= count - 1;
128 else if (ga_grow(&ectx->ec_stack, 1) == FAIL)
129 return FAIL;
130 else
131 ++ectx->ec_stack.ga_len;
132 tv = STACK_TV_BOT(-1);
133 tv->v_type = VAR_LIST;
134 tv->vval.v_list = list;
135 ++list->lv_refcount;
136 return OK;
108 } 137 }
109 138
110 /* 139 /*
111 * Call compiled function "cdf_idx" from compiled code. 140 * Call compiled function "cdf_idx" from compiled code.
112 * 141 *
135 return FAIL; 164 return FAIL;
136 } 165 }
137 166
138 if (ufunc->uf_va_name != NULL) 167 if (ufunc->uf_va_name != NULL)
139 { 168 {
140 int iidx; 169 // Need to make a list out of the vararg arguments.
141 isn_T *iptr;
142
143 // Need to make a list out of the vararg arguments. There is a
144 // ISN_NEWLIST instruction at the start of the function body, we need
145 // to move the arguments below the stack frame and pass the count.
146 // Stack at time of call with 2 varargs: 170 // Stack at time of call with 2 varargs:
147 // normal_arg 171 // normal_arg
148 // optional_arg 172 // optional_arg
149 // vararg_1 173 // vararg_1
150 // vararg_2 174 // vararg_2
151 // When starting execution: 175 // After creating the list:
176 // normal_arg
177 // optional_arg
178 // vararg-list
179 // With missing optional arguments we get:
152 // normal_arg 180 // normal_arg
153 // optional_arg 181 // After creating the list
154 // space list of varargs 182 // normal_arg
155 // STACK_FRAME 183 // (space for optional_arg)
156 // [local variables] 184 // vararg-list
157 // vararg_1
158 // vararg_2
159 // TODO: This doesn't work if the same function is used for a default
160 // argument value. Forbid that somehow?
161 vararg_count = argcount - ufunc->uf_args.ga_len; 185 vararg_count = argcount - ufunc->uf_args.ga_len;
162 if (vararg_count < 0) 186 if (vararg_count < 0)
163 vararg_count = 0; 187 vararg_count = 0;
164 else 188 else
165 argcount -= vararg_count; 189 argcount -= vararg_count;
166 if (ufunc->uf_def_arg_idx == NULL) 190 if (exe_newlist(vararg_count, ectx) == FAIL)
167 iidx = 0;
168 else
169 iidx = ufunc->uf_def_arg_idx[ufunc->uf_def_args.ga_len];
170 iptr = &dfunc->df_instr[iidx];
171 if (iptr->isn_type != ISN_NEWLIST)
172 {
173 iemsg("Not a ISN_NEWLIST instruction");
174 return FAIL; 191 return FAIL;
175 } 192
176 iptr->isn_arg.number = vararg_count; 193 vararg_count = 1;
177 } 194 }
178 195
179 arg_to_add = ufunc_argcount(ufunc) - argcount; 196 arg_to_add = ufunc->uf_args.ga_len - argcount;
180 if (arg_to_add < 0) 197 if (arg_to_add < 0)
181 { 198 {
182 iemsg("Argument count wrong?"); 199 iemsg("Argument count wrong?");
183 return FAIL; 200 return FAIL;
184 } 201 }
185 if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL) 202 if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL)
186 return FAIL; 203 return FAIL;
187 204
188 if (vararg_count > 0) 205 // Move the vararg-list to below the missing optional arguments.
189 { 206 if (vararg_count > 0 && arg_to_add > 0)
190 int stack_added = arg_to_add + STACK_FRAME_SIZE + dfunc->df_varcount; 207 *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
191
192 // Move the varargs to below the stack frame.
193 // TODO: use mch_memmove()
194 for (idx = 1; idx <= vararg_count; ++idx)
195 *STACK_TV_BOT(stack_added - idx) = *STACK_TV_BOT(-idx);
196 ectx->ec_stack.ga_len -= vararg_count;
197 }
198 208
199 // Reserve space for omitted optional arguments, filled in soon. 209 // Reserve space for omitted optional arguments, filled in soon.
200 // Also room for a list of varargs, if there is one.
201 for (idx = 0; idx < arg_to_add; ++idx) 210 for (idx = 0; idx < arg_to_add; ++idx)
202 STACK_TV_BOT(idx)->v_type = VAR_UNKNOWN; 211 STACK_TV_BOT(idx - vararg_count)->v_type = VAR_UNKNOWN;
203 ectx->ec_stack.ga_len += arg_to_add; 212 ectx->ec_stack.ga_len += arg_to_add;
204 213
205 // Store current execution state in stack frame for ISN_RETURN. 214 // Store current execution state in stack frame for ISN_RETURN.
206 // TODO: If the actual number of arguments doesn't match what the called 215 // TODO: If the actual number of arguments doesn't match what the called
207 // function expects things go bad. 216 // function expects things go bad.
211 ectx->ec_frame = ectx->ec_stack.ga_len; 220 ectx->ec_frame = ectx->ec_stack.ga_len;
212 221
213 // Initialize local variables 222 // Initialize local variables
214 for (idx = 0; idx < dfunc->df_varcount; ++idx) 223 for (idx = 0; idx < dfunc->df_varcount; ++idx)
215 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; 224 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
216 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount 225 ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount;
217 + vararg_count;
218 226
219 // Set execution state to the start of the called function. 227 // Set execution state to the start of the called function.
220 ectx->ec_dfunc_idx = cdf_idx; 228 ectx->ec_dfunc_idx = cdf_idx;
221 ectx->ec_instr = dfunc->df_instr; 229 ectx->ec_instr = dfunc->df_instr;
222 estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1); 230 estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
977 break; 985 break;
978 986
979 // create a list from items on the stack; uses a single allocation 987 // create a list from items on the stack; uses a single allocation
980 // for the list header and the items 988 // for the list header and the items
981 case ISN_NEWLIST: 989 case ISN_NEWLIST:
982 { 990 if (exe_newlist(iptr->isn_arg.number, &ectx) == FAIL)
983 int count = iptr->isn_arg.number; 991 goto failed;
984 list_T *list = list_alloc_with_items(count);
985
986 if (list == NULL)
987 goto failed;
988 for (idx = 0; idx < count; ++idx)
989 list_set_item(list, idx, STACK_TV_BOT(idx - count));
990
991 if (count > 0)
992 ectx.ec_stack.ga_len -= count - 1;
993 else if (ga_grow(&ectx.ec_stack, 1) == FAIL)
994 goto failed;
995 else
996 ++ectx.ec_stack.ga_len;
997 tv = STACK_TV_BOT(-1);
998 tv->v_type = VAR_LIST;
999 tv->vval.v_list = list;
1000 ++list->lv_refcount;
1001 }
1002 break; 992 break;
1003 993
1004 // create a dict from items on the stack 994 // create a dict from items on the stack
1005 case ISN_NEWDICT: 995 case ISN_NEWDICT:
1006 { 996 {