Mercurial > vim
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 { |