Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * (author) Author M. Kersten
15 : * For documentation see website
16 : */
17 : #include "monetdb_config.h"
18 : #include "mal_instruction.h"
19 : #include "mal_function.h" /* for getPC() */
20 : #include "mal_utils.h"
21 : #include "mal_exception.h"
22 : #include "mal_private.h"
23 :
24 : /* to avoid memory fragmentation stmt and var blocks are allocated in chunks */
25 : #define MALCHUNK 256
26 :
27 : /* If we encounter an error it can be left behind in the MalBlk
28 : * for the upper layers to abandon the track
29 : */
30 : void
31 0 : addMalException(MalBlkPtr mb, str msg)
32 : {
33 0 : if (msg == NULL)
34 : return;
35 0 : if (mb->errors) {
36 0 : mb->errors = concatErrors(mb->errors, msg);
37 : } else {
38 0 : mb->errors = dupError(msg);
39 : }
40 : }
41 :
42 : Symbol
43 3618255 : newSymbol(const char *nme, int kind)
44 : {
45 3618255 : Symbol cur;
46 :
47 3618255 : assert(kind == COMMANDsymbol || kind == PATTERNsymbol || kind == FUNCTIONsymbol);
48 3618255 : if (nme == NULL)
49 : return NULL;
50 3618255 : cur = (Symbol) GDKmalloc(sizeof(SymRecord));
51 3618257 : if (cur == NULL)
52 : return NULL;
53 7236514 : *cur = (SymRecord) {
54 3618257 : .name = putName(nme),
55 : .kind = kind,
56 : };
57 3618257 : if (cur->name == NULL) {
58 0 : GDKfree(cur);
59 0 : return NULL;
60 : }
61 3618257 : if (kind == FUNCTIONsymbol) {
62 50120 : cur->def = newMalBlk(STMT_INCREMENT);
63 50120 : if (cur->def == NULL) {
64 0 : GDKfree(cur);
65 0 : return NULL;
66 : }
67 : }
68 : return cur;
69 : }
70 :
71 : void
72 3598313 : freeSymbol(Symbol s)
73 : {
74 3598313 : if (s == NULL)
75 : return;
76 3598313 : if (s->def) {
77 50119 : freeMalBlk(s->def);
78 50119 : s->def = NULL;
79 3548194 : } else if (s->allocated && s->func) {
80 1742663 : GDKfree((char*)s->func->comment);
81 1742663 : GDKfree((char*)s->func->cname);
82 1742663 : GDKfree(s->func->args);
83 1742663 : GDKfree(s->func);
84 : }
85 3598313 : GDKfree(s);
86 : }
87 :
88 : void
89 201451 : freeSymbolList(Symbol s)
90 : {
91 201451 : Symbol t = s;
92 :
93 3799239 : while (s) {
94 3597788 : t = s->peer;
95 3597788 : s->peer = NULL;
96 3597788 : freeSymbol(s);
97 3597788 : s = t;
98 : }
99 201451 : }
100 :
101 : int
102 4059656 : newMalBlkStmt(MalBlkPtr mb, int maxstmts)
103 : {
104 4059656 : InstrPtr *p;
105 4059656 : maxstmts = maxstmts % MALCHUNK == 0 ? maxstmts : ((maxstmts / MALCHUNK) + 1) * MALCHUNK;
106 :
107 4059656 : p = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * maxstmts);
108 4060806 : if (p == NULL)
109 : return -1;
110 4060806 : mb->stmt = p;
111 4060806 : mb->stop = 0;
112 4060806 : mb->ssize = maxstmts;
113 4060806 : return 0;
114 : }
115 :
116 : MalBlkPtr
117 63060 : newMalBlk(int elements)
118 : {
119 63060 : MalBlkPtr mb;
120 63060 : VarRecord *v;
121 :
122 63060 : mb = (MalBlkPtr) GDKmalloc(sizeof(MalBlkRecord));
123 63069 : if (mb == NULL)
124 : return NULL;
125 :
126 : /* each MAL instruction implies at least one variable
127 : * we reserve some extra for constants */
128 63069 : assert(elements >= 0);
129 63069 : elements += 8;
130 63069 : if (elements % MALCHUNK != 0)
131 63069 : elements = (elements / MALCHUNK + 1) * MALCHUNK;
132 63069 : v = (VarRecord *) GDKzalloc(sizeof(VarRecord) * elements);
133 63067 : if (v == NULL) {
134 0 : GDKfree(mb);
135 0 : return NULL;
136 : }
137 63067 : *mb = (MalBlkRecord) {
138 : .var = v,
139 : .vsize = elements,
140 : .maxarg = MAXARG, /* the minimum for each instruction */
141 : .workers = ATOMIC_VAR_INIT(1),
142 : };
143 63067 : if (newMalBlkStmt(mb, elements) < 0) {
144 0 : GDKfree(mb->var);
145 0 : GDKfree(mb);
146 0 : return NULL;
147 : }
148 : return mb;
149 : }
150 :
151 : int
152 79734 : resizeMalBlk(MalBlkPtr mb, int elements)
153 : {
154 79734 : int i;
155 79734 : assert(elements >= 0);
156 79734 : if (elements % MALCHUNK != 0)
157 10453 : elements = (elements / MALCHUNK + 1) * MALCHUNK;
158 :
159 79734 : if (elements > mb->ssize) {
160 69282 : InstrPtr *ostmt = mb->stmt;
161 69282 : mb->stmt = GDKrealloc(mb->stmt, elements * sizeof(InstrPtr));
162 69282 : if (mb->stmt) {
163 17681857 : for (i = mb->ssize; i < elements; i++)
164 17612575 : mb->stmt[i] = 0;
165 69282 : mb->ssize = elements;
166 : } else {
167 0 : mb->stmt = ostmt; /* reinstate old pointer */
168 0 : mb->errors = createMalException(mb, 0, TYPE,
169 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
170 0 : return -1;
171 : }
172 : }
173 : return 0;
174 : }
175 :
176 : /* For a MAL session we have to keep the variables around
177 : * and only need to reset the instruction pointer
178 : */
179 : void
180 588960 : resetMalTypes(MalBlkPtr mb, int stop)
181 : {
182 588960 : int i;
183 :
184 28509533 : for (i = 0; i < stop; i++)
185 27920573 : mb->stmt[i]->typeresolved = false;
186 588960 : mb->stop = stop;
187 588960 : mb->errors = NULL;
188 588960 : }
189 :
190 : /* For SQL operations we have to cleanup variables and trim the space
191 : * A portion is retained for the next query */
192 : void
193 574485 : resetMalBlk(MalBlkPtr mb)
194 : {
195 574485 : int i;
196 574485 : InstrPtr *new;
197 574485 : VarRecord *vnew;
198 :
199 156137388 : for (i = 1/*MALCHUNK*/; i < mb->ssize; i++) {
200 155562873 : freeInstruction(mb->stmt[i]);
201 155562903 : mb->stmt[i] = NULL;
202 : }
203 574515 : if (mb->ssize != MALCHUNK) {
204 18827 : new = GDKrealloc(mb->stmt, sizeof(InstrPtr) * MALCHUNK);
205 18827 : if (new == NULL) {
206 : /* the only place to return an error signal at this stage. */
207 : /* The Client context should be passed around more deeply */
208 0 : mb->errors = createMalException(mb, 0, TYPE,
209 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
210 0 : return;
211 : }
212 18827 : mb->stmt = new;
213 18827 : mb->ssize = MALCHUNK;
214 : }
215 : /* Reuse the initial function statement */
216 574515 : mb->stop = 1;
217 :
218 66791700 : for (i = 0; i < mb->vtop; i++) {
219 66217229 : if (mb->var[i].name)
220 355490 : GDKfree(mb->var[i].name);
221 66217121 : mb->var[i].name = NULL;
222 66217121 : if (isVarConstant(mb, i))
223 18626806 : VALclear(&getVarConstant(mb, i));
224 : }
225 :
226 574471 : if (mb->vsize != MALCHUNK) {
227 27406 : vnew = GDKrealloc(mb->var, sizeof(VarRecord) * MALCHUNK);
228 27406 : if (vnew == NULL) {
229 : /* the only place to return an error signal at this stage. */
230 : /* The Client context should be passed around more deeply */
231 0 : mb->errors = createMalException(mb, 0, TYPE,
232 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
233 0 : return;
234 : }
235 27406 : mb->var = vnew;
236 27406 : mb->vsize = MALCHUNK;
237 : }
238 574471 : mb->vtop = 0;
239 : }
240 :
241 :
242 : /* The freeMalBlk code is quite defensive. It is used to localize an
243 : * illegal reuse of a MAL blk. */
244 : void
245 63292 : freeMalBlk(MalBlkPtr mb)
246 : {
247 63292 : int i;
248 :
249 16361365 : for (i = 0; i < mb->ssize; i++)
250 16298058 : if (mb->stmt[i]) {
251 177546 : freeInstruction(mb->stmt[i]);
252 177561 : mb->stmt[i] = NULL;
253 : }
254 63307 : mb->stop = 0;
255 350203 : for (i = 0; i < mb->vtop; i++) {
256 286898 : if (mb->var[i].name)
257 10326 : GDKfree(mb->var[i].name);
258 286896 : mb->var[i].name = NULL;
259 286896 : if (isVarConstant(mb, i))
260 61161 : VALclear(&getVarConstant(mb, i));
261 : }
262 63305 : mb->vtop = 0;
263 63305 : GDKfree(mb->stmt);
264 63307 : mb->stmt = 0;
265 63307 : GDKfree(mb->var);
266 63307 : mb->var = 0;
267 :
268 63307 : mb->binding[0] = 0;
269 63307 : mb->tag = 0;
270 63307 : mb->memory = 0;
271 63307 : if (mb->help)
272 0 : GDKfree(mb->help);
273 63307 : mb->help = 0;
274 63307 : mb->inlineProp = 0;
275 63307 : mb->unsafeProp = 0;
276 63307 : freeException(mb->errors);
277 63304 : GDKfree(mb);
278 63307 : }
279 :
280 : /* The routine below should assure that all referenced structures are
281 : * private. The copying is memory conservative. */
282 : MalBlkPtr
283 237 : copyMalBlk(MalBlkPtr old)
284 : {
285 237 : MalBlkPtr mb;
286 237 : int i;
287 :
288 237 : mb = (MalBlkPtr) GDKzalloc(sizeof(MalBlkRecord));
289 237 : if (mb == NULL)
290 : return NULL;
291 :
292 237 : mb->var = (VarRecord *) GDKzalloc(sizeof(VarRecord) * old->vsize);
293 237 : if (mb->var == NULL) {
294 0 : GDKfree(mb);
295 0 : return NULL;
296 : }
297 :
298 237 : mb->vsize = old->vsize;
299 :
300 : /* copy all variable records */
301 12134 : for (i = 0; i < old->vtop; i++) {
302 11897 : mb->var[i] = old->var[i];
303 11897 : if (mb->var[i].name) {
304 631 : mb->var[i].name = GDKstrdup(mb->var[i].name);
305 631 : if (!mb->var[i].name)
306 0 : goto bailout;
307 : }
308 11897 : if (VALcopy(&(mb->var[i].value), &(old->var[i].value)) == NULL) {
309 0 : mb->vtop = i;
310 0 : goto bailout;
311 : }
312 : }
313 237 : mb->vtop = old->vtop;
314 :
315 237 : mb->stmt = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * old->ssize);
316 237 : if (mb->stmt == NULL) {
317 0 : goto bailout;
318 : }
319 :
320 237 : mb->ssize = old->ssize;
321 237 : assert(old->stop < old->ssize);
322 11213 : for (i = 0; i < old->stop; i++) {
323 10976 : mb->stmt[i] = copyInstruction(old->stmt[i]);
324 10976 : if (mb->stmt[i] == NULL) {
325 0 : mb->stop = i;
326 0 : goto bailout;
327 : }
328 : }
329 237 : mb->stop = old->stop;
330 237 : if (old->help && (mb->help = GDKstrdup(old->help)) == NULL) {
331 0 : goto bailout;
332 : }
333 :
334 237 : strcpy_len(mb->binding, old->binding, sizeof(mb->binding));
335 237 : mb->errors = old->errors ? GDKstrdup(old->errors) : 0;
336 237 : mb->tag = old->tag;
337 237 : mb->runtime = old->runtime;
338 237 : mb->calls = old->calls;
339 237 : mb->optimize = old->optimize;
340 237 : mb->maxarg = old->maxarg;
341 237 : mb->inlineProp = old->inlineProp;
342 237 : mb->unsafeProp = old->unsafeProp;
343 237 : return mb;
344 :
345 : bailout:
346 0 : for (i = 0; i < old->stop; i++)
347 0 : freeInstruction(mb->stmt[i]);
348 0 : for (i = 0; i < old->vtop; i++) {
349 0 : if (mb->var[i].name)
350 0 : GDKfree(mb->var[i].name);
351 0 : VALclear(&mb->var[i].value);
352 : }
353 0 : GDKfree(mb->var);
354 0 : GDKfree(mb->stmt);
355 0 : GDKfree(mb);
356 0 : return NULL;
357 : }
358 :
359 : /* The MAL records should be managed from a pool to
360 : * avoid repeated alloc/free and reduce probability of
361 : * memory fragmentation. (todo)
362 : * The complicating factor is their variable size,
363 : * which leads to growing records as a result of pushArguments
364 : * Allocation of an instruction should always succeed.
365 : */
366 : InstrPtr
367 34564295 : newInstructionArgs(MalBlkPtr mb, const char *modnme, const char *fcnnme,
368 : int args)
369 : {
370 34564295 : InstrPtr p;
371 :
372 34564295 : if (mb && mb->errors)
373 : return NULL;
374 34564280 : if (args <= 0)
375 : args = 1;
376 34564280 : p = GDKmalloc(args * sizeof(p->argv[0]) + offsetof(InstrRecord, argv));
377 34569571 : if (p == NULL) {
378 0 : if (mb)
379 0 : mb->errors = createMalException(mb, 0, TYPE,
380 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
381 0 : return NULL;
382 : }
383 34569571 : *p = (InstrRecord) {
384 : .maxarg = args,
385 : .typeresolved = false,
386 : .modname = modnme,
387 : .fcnname = fcnnme,
388 : .argc = 1,
389 : .retc = 1,
390 : /* Flow of control instructions are always marked as an assignment
391 : * with modifier */
392 : .token = ASSIGNsymbol,
393 : };
394 34569571 : memset(p->argv, 0, args * sizeof(p->argv[0]));
395 34569571 : p->argv[0] = -1;
396 34569571 : return p;
397 : }
398 :
399 : InstrPtr
400 4614803 : newInstruction(MalBlkPtr mb, const char *modnme, const char *fcnnme)
401 : {
402 4614803 : return newInstructionArgs(mb, modnme, fcnnme, MAXARG);
403 : }
404 :
405 : InstrPtr
406 25274651 : copyInstructionArgs(const InstrRecord *p, int args)
407 : {
408 25274651 : if (args < p->maxarg)
409 : args = p->maxarg;
410 25274651 : InstrPtr new = (InstrPtr) GDKmalloc(offsetof(InstrRecord, argv) +
411 : args * sizeof(p->argv[0]));
412 25275514 : if (new == NULL)
413 : return new;
414 25275514 : memcpy(new, p,
415 25275514 : offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]));
416 25275514 : if (args > p->maxarg)
417 3312183 : memset(new->argv + p->maxarg, 0,
418 3312183 : (args - p->maxarg) * sizeof(new->argv[0]));
419 25275514 : new->typeresolved = false;
420 25275514 : new->maxarg = args;
421 25275514 : return new;
422 : }
423 :
424 : InstrPtr
425 17823921 : copyInstruction(const InstrRecord *p)
426 : {
427 17823921 : return copyInstructionArgs(p, p->maxarg);
428 : }
429 :
430 : void
431 651362 : clrFunction(InstrPtr p)
432 : {
433 651362 : p->token = ASSIGNsymbol;
434 651362 : p->fcn = 0;
435 651362 : p->blk = 0;
436 651362 : p->typeresolved = false;
437 651362 : setModuleId(p, NULL);
438 651371 : setFunctionId(p, NULL);
439 651443 : }
440 :
441 : void
442 0 : clrInstruction(InstrPtr p)
443 : {
444 0 : clrFunction(p);
445 0 : memset(p, 0, offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]));
446 0 : }
447 :
448 : void
449 183749933 : freeInstruction(InstrPtr p)
450 : {
451 183749933 : GDKfree(p);
452 183664839 : }
453 :
454 : /* Query optimizers walk their way through a MAL program block. They
455 : * require some primitives to move instructions around and to remove
456 : * superfluous instructions. The removal is based on the assumption
457 : * that indeed the instruction belonged to the block. */
458 : void
459 0 : removeInstruction(MalBlkPtr mb, InstrPtr p)
460 : {
461 0 : int i;
462 0 : for (i = 0; i < mb->stop - 1; i++)
463 0 : if (mb->stmt[i] == p)
464 : break;
465 0 : if (i == mb->stop)
466 : return;
467 0 : for (; i < mb->stop - 1; i++)
468 0 : mb->stmt[i] = mb->stmt[i + 1];
469 0 : mb->stmt[i] = 0;
470 0 : mb->stop--;
471 0 : assert(i == mb->stop); /* move statement after stop */
472 0 : mb->stmt[i] = p;
473 : }
474 :
475 : void
476 0 : removeInstructionBlock(MalBlkPtr mb, int pc, int cnt)
477 : {
478 0 : int i;
479 0 : InstrPtr p;
480 0 : for (i = pc; i < pc + cnt; i++) {
481 0 : p = getInstrPtr(mb, i);
482 0 : freeInstruction(p);
483 0 : mb->stmt[i] = NULL;
484 0 : } for (i = pc; i < mb->stop - cnt; i++)
485 0 : mb->stmt[i] = mb->stmt[i + cnt];
486 0 : mb->stop -= cnt;
487 0 : for (; i < mb->stop; i++)
488 : mb->stmt[i] = 0;
489 0 : }
490 :
491 : void
492 0 : moveInstruction(MalBlkPtr mb, int pc, int target)
493 : {
494 0 : InstrPtr p;
495 0 : int i;
496 0 : p = getInstrPtr(mb, pc);
497 0 : if (pc > target) {
498 0 : for (i = pc; i > target; i--)
499 0 : mb->stmt[i] = mb->stmt[i - 1];
500 0 : mb->stmt[i] = p;
501 : } else {
502 0 : for (i = target; i > pc; i--)
503 0 : mb->stmt[i] = mb->stmt[i - 1];
504 0 : mb->stmt[i] = p;
505 : }
506 0 : }
507 :
508 : /* Beware that the first argument of a signature is reserved for the
509 : * function return type , which should be equal to the destination
510 : * variable type.
511 : */
512 : int
513 669407 : findVariable(MalBlkPtr mb, const char *name)
514 : {
515 669407 : int i;
516 669407 : if (name == NULL)
517 : return -1;
518 5959823 : for (i = mb->vtop - 1; i >= 0; i--)
519 5640966 : if (mb->var[i].name && idcmp(name, mb->var[i].name) == 0)
520 350550 : return i;
521 : return -1;
522 : }
523 :
524 : /* The second version of findVariable assumes you have not yet
525 : * allocated a private structure. This is particularly useful during
526 : * parsing, because most variables are already defined. This way we
527 : * safe GDKmalloc/GDKfree. */
528 : int
529 54321 : findVariableLength(MalBlkPtr mb, const char *name, int len)
530 : {
531 54321 : int i;
532 2582016 : for (i = mb->vtop - 1; i >= 0; i--) {
533 2538933 : const char *s = mb->var[i].name;
534 2538933 : if (s && strncmp(name, s, len) == 0 && s[len] == 0)
535 11238 : return i;
536 : }
537 : return -1;
538 : }
539 :
540 : str
541 174 : getArgDefault(MalBlkPtr mb, InstrPtr p, int idx)
542 : {
543 174 : ValPtr v = &getVarConstant(mb, getArg(p, idx));
544 174 : if (v->vtype == TYPE_str)
545 174 : return v->val.sval;
546 : return NULL;
547 : }
548 :
549 : /* Beware, the symbol table structure assumes that it is relatively
550 : * cheap to perform a linear search to a variable or constant. */
551 : static int
552 66258963 : makeVarSpace(MalBlkPtr mb)
553 : {
554 66258963 : if (mb->vtop >= mb->vsize) {
555 103832 : VarRecord *new;
556 103832 : int s = (mb->vtop / MALCHUNK + 1) * MALCHUNK;
557 103832 : new = (VarRecord *) GDKrealloc(mb->var, s * sizeof(VarRecord));
558 103832 : if (new == NULL) {
559 : /* the only place to return an error signal at this stage. */
560 : /* The Client context should be passed around more deeply */
561 0 : mb->errors = createMalException(mb, 0, TYPE, SQLSTATE(HY013) MAL_MALLOC_FAIL);
562 0 : return -1;
563 : }
564 103832 : memset(new + mb->vsize, 0, (s - mb->vsize) * sizeof(VarRecord));
565 103832 : mb->vsize = s;
566 103832 : mb->var = new;
567 : }
568 : return 0;
569 : }
570 :
571 : /* create and initialize a variable record*/
572 : void
573 66262023 : setVariableType(MalBlkPtr mb, const int n, malType type)
574 : {
575 66262023 : assert(n >= 0 && n < mb->vtop);
576 66262023 : setVarType(mb, n, type);
577 66262023 : setRowCnt(mb, n, 0);
578 66262023 : clrVarFixed(mb, n);
579 66262023 : clrVarUsed(mb, n);
580 66262023 : clrVarInit(mb, n);
581 66262023 : clrVarDisabled(mb, n);
582 66262023 : clrVarConstant(mb, n);
583 66262023 : clrVarCleanup(mb, n);
584 66262023 : }
585 :
586 : char *
587 25910 : getVarNameIntoBuffer(MalBlkPtr mb, int idx, char *buf)
588 : {
589 25910 : char *s = mb->var[idx].name;
590 25910 : if (s == NULL) {
591 25771 : char kind = getVarKind(mb, idx);
592 51551 : (void) snprintf(buf, IDLENGTH, "%c_%d", kind ? kind : REFMARKER, idx);
593 : } else {
594 139 : strcpy_len(buf, s, IDLENGTH);
595 : }
596 25910 : return buf;
597 : }
598 :
599 : int
600 66257693 : newVariable(MalBlkPtr mb, const char *name, size_t len, malType type)
601 : {
602 66257693 : int n;
603 66257693 : int kind = REFMARKER;
604 66257693 : if (mb->errors)
605 : return -1;
606 66257693 : if (len >= IDLENGTH) {
607 1 : mb->errors = createMalException(mb, 0, TYPE, "newVariable: id too long");
608 1 : return -1;
609 : }
610 66257692 : if (makeVarSpace(mb)) { /* no space for a new variable */
611 : return -1;
612 : }
613 66259857 : n = mb->vtop;
614 66259857 : mb->var[n].name = NULL;
615 66259857 : if (name && len > 0) {
616 365179 : char *nme = GDKmalloc(len+1);
617 365188 : if (!nme) {
618 0 : mb->errors = createMalException(mb, 0, TYPE, SQLSTATE(HY013) MAL_MALLOC_FAIL);
619 0 : return -1;
620 : }
621 365188 : mb->var[n].name = nme;
622 3047863 : for (size_t i = 0; i < len; i++)
623 2682675 : nme[i] = name[i];
624 365188 : nme[len] = 0;
625 365188 : kind = nme[0];
626 : }
627 66259866 : mb->vtop++;
628 66259866 : setVarKind(mb, n, kind);
629 66259866 : setVariableType(mb, n, type);
630 66259866 : return n;
631 : }
632 :
633 : /* Simplified cloning. */
634 : int
635 0 : cloneVariable(MalBlkPtr tm, MalBlkPtr mb, int x)
636 : {
637 0 : int res;
638 0 : if (isVarConstant(mb, x))
639 0 : res = cpyConstant(tm, getVar(mb, x));
640 : else {
641 0 : res = newTmpVariable(tm, getVarType(mb, x));
642 0 : if (mb->var[x].name)
643 0 : tm->var[x].name = GDKstrdup(mb->var[x].name);
644 : }
645 0 : if (res < 0)
646 : return res;
647 0 : if (isVarFixed(mb, x))
648 0 : setVarFixed(tm, res);
649 0 : if (isVarUsed(mb, x))
650 0 : setVarUsed(tm, res);
651 0 : if (isVarInit(mb, x))
652 0 : setVarInit(tm, res);
653 0 : if (isVarDisabled(mb, x))
654 0 : setVarDisabled(tm, res);
655 0 : if (isVarCleanup(mb, x))
656 0 : setVarCleanup(tm, res);
657 0 : getVarSTC(tm, x) = getVarSTC(mb, x);
658 0 : setVarKind(tm, x, getVarKind(mb, x));
659 0 : return res;
660 : }
661 :
662 : int
663 65893986 : newTmpVariable(MalBlkPtr mb, malType type)
664 : {
665 65893986 : return newVariable(mb, 0, 0, type);
666 : }
667 :
668 : int
669 273 : newTypeVariable(MalBlkPtr mb, malType type)
670 : {
671 273 : int n, i;
672 1281 : for (i = 0; i < mb->vtop; i++)
673 1051 : if (isVarTypedef(mb, i) && getVarType(mb, i) == type)
674 : break;
675 273 : if (i < mb->vtop)
676 : return i;
677 230 : n = newTmpVariable(mb, type);
678 230 : if (n >= 0)
679 230 : setVarTypedef(mb, n);
680 : return n;
681 : }
682 :
683 : void
684 69112 : clearVariable(MalBlkPtr mb, int varid)
685 : {
686 69112 : VarPtr v;
687 69112 : v = getVar(mb, varid);
688 69112 : if (isVarConstant(mb, varid) || isVarDisabled(mb, varid))
689 26532 : VALclear(&v->value);
690 69112 : if (v->name)
691 0 : GDKfree(v->name);
692 69112 : v->name = NULL;
693 69112 : v->type = 0;
694 69112 : v->constant = 0;
695 69112 : v->typevar = 0;
696 69112 : v->fixedtype = 0;
697 69112 : v->cleanup = 0;
698 69112 : v->initialized = 0;
699 69112 : v->used = 0;
700 69112 : v->rowcnt = 0;
701 69112 : v->eolife = 0;
702 69112 : v->stc = 0;
703 69112 : }
704 :
705 : void
706 81 : freeVariable(MalBlkPtr mb, int varid)
707 : {
708 81 : clearVariable(mb, varid);
709 81 : }
710 :
711 : /* A special action is to reduce the variable space by removing all
712 : * that do not contribute.
713 : * All temporary variables are renamed in the process to trim the varid.
714 : */
715 : void
716 3 : trimMalVariables_(MalBlkPtr mb, MalStkPtr glb)
717 : {
718 3 : int *alias, cnt = 0, i, j;
719 3 : InstrPtr q;
720 3 : if (mb->vtop == 0)
721 : return;
722 3 : alias = (int *) GDKzalloc(mb->vtop * sizeof(int));
723 3 : if (alias == NULL)
724 : return; /* forget it if we run out of memory *//* build the alias table */
725 677 : for (i = 0; i < mb->vtop; i++) {
726 674 : if (isVarUsed(mb, i) == 0) {
727 81 : if (glb && i < glb->stktop && isVarConstant(mb, i))
728 0 : VALclear(&glb->stk[i]);
729 81 : freeVariable(mb, i);
730 81 : continue;
731 : }
732 593 : if (i > cnt) { /* remap temporary variables */
733 533 : VarRecord t = mb->var[cnt];
734 533 : mb->var[cnt] = mb->var[i];
735 533 : mb->var[i] = t;
736 : } /* valgrind finds a leak when we move these variable record * pointers around. */
737 593 : alias[i] = cnt;
738 593 : if (glb && i < glb->stktop && i != cnt) {
739 0 : glb->stk[cnt] = glb->stk[i];
740 0 : VALempty(&glb->stk[i]);
741 : }
742 593 : cnt++;
743 : } /* remap all variable references to their new position. */
744 3 : if (cnt < mb->vtop) {
745 385 : for (i = 0; i < mb->stop; i++) {
746 382 : q = getInstrPtr(mb, i);
747 2271 : for (j = 0; j < q->argc; j++) {
748 1889 : getArg(q, j) = alias[getArg(q, j)];
749 : }
750 : }
751 3 : mb->vtop = cnt;
752 : }
753 3 : GDKfree(alias);
754 : }
755 :
756 : void
757 3 : trimMalVariables(MalBlkPtr mb, MalStkPtr stk)
758 : {
759 3 : int i, j;
760 3 : InstrPtr q; /* reset the use bit for all non-signature arguments */
761 677 : for (i = 0; i < mb->vtop; i++)
762 674 : clrVarUsed(mb, i); /* build the use table */
763 385 : for (i = 0; i < mb->stop; i++) {
764 382 : q = getInstrPtr(mb, i);
765 2271 : for (j = 0; j < q->argc; j++)
766 1889 : setVarUsed(mb, getArg(q, j));
767 : }
768 3 : trimMalVariables_(mb, stk);
769 3 : }
770 :
771 : /* MAL constants
772 : * Constants are stored in the symbol table and referenced by a
773 : * variable identifier. This means that per MAL instruction, we may
774 : * end up with MAXARG entries in the symbol table. This may lead to
775 : * long searches for variables. An optimization strategy deployed in
776 : * the current implementation is to look around for a similar
777 : * (constant) definition and to reuse its identifier. This avoids an
778 : * exploding symbol table with a lot of temporary variables (as in
779 : * tst400cHuge)
780 : *
781 : * But then the question becomes how far to search? Searching through
782 : * all variables is only useful when the list remains short or when
783 : * the constant-variable-name is easily derivable from its literal
784 : * value and a hash-based index leads you quickly to it.
785 : *
786 : * For the time being, we use a MAL system parameter, MAL_VAR_WINDOW,
787 : * to indicate the number of symbol table entries to consider. Setting
788 : * it to >= MAXARG will at least capture repeated use of a constant
789 : * within a single function call or repeated use within a small block
790 : * of code.
791 : *
792 : * The final step is to prepare a GDK value record, from which the
793 : * internal representation can be obtained during MAL interpretation.
794 : *
795 : * The constant values are linked together to improve searching
796 : * them. This start of the constant list is kept in the MalBlk.
797 : *
798 : * Conversion of a constant to another type is limited to well-known
799 : * coercion rules. Errors are reported and the nil value is set. */
800 :
801 : /* Converts the constant in vr to the MAL type type. Conversion is
802 : * done in the vr struct. */
803 : str
804 280515 : convertConstant(int type, ValPtr vr)
805 : {
806 280515 : if (type > GDKatomcnt)
807 0 : throw(SYNTAX, "convertConstant", "type index out of bound");
808 280515 : if (vr->vtype == type)
809 : return MAL_SUCCEED;
810 280512 : if (isaBatType(type)) { /* BAT variables can only be set to nil */
811 0 : if (vr->vtype != TYPE_void)
812 0 : throw(SYNTAX, "convertConstant", "BAT conversion error");
813 0 : VALclear(vr);
814 0 : vr->vtype = getBatType(type);
815 0 : vr->bat = true;
816 0 : vr->val.bval = bat_nil;
817 0 : return MAL_SUCCEED;
818 : }
819 280512 : if (type == TYPE_ptr) { /* all coercions should be avoided to protect against memory probing */
820 32 : if (vr->vtype == TYPE_void) {
821 32 : VALclear(vr);
822 32 : vr->vtype = type;
823 32 : vr->val.pval = NULL;
824 32 : return MAL_SUCCEED;
825 : }
826 0 : if (vr->vtype != type)
827 0 : throw(SYNTAX, "convertConstant", "pointer conversion error");
828 : return MAL_SUCCEED;
829 : }
830 280480 : if (type == TYPE_any) {
831 : #ifndef DEBUG_MAL_INSTR
832 : assert(0);
833 : #endif
834 0 : throw(SYNTAX, "convertConstant", "missing type");
835 : }
836 280480 : if (VALconvert(type, vr) == NULL) {
837 3 : if (vr->vtype == TYPE_str)
838 0 : throw(SYNTAX, "convertConstant", "parse error in '%s'", vr->val.sval);
839 3 : throw(SYNTAX, "convertConstant", "coercion failed");
840 : }
841 : return MAL_SUCCEED;
842 : }
843 :
844 : int
845 52158835 : fndConstant(MalBlkPtr mb, const ValRecord *cst, int depth)
846 : {
847 52158835 : int i, k;
848 52158835 : const void *p; /* pointers never match */
849 52158835 : if (ATOMstorage(cst->vtype) == TYPE_ptr)
850 : return -1;
851 52001302 : p = VALptr(cst);
852 52001302 : k = mb->vtop - depth;
853 52001302 : if (k < 0)
854 : k = 0;
855 589409129 : for (i = k; i < mb->vtop - 1; i++) {
856 569293416 : VarPtr v = getVar(mb, i);
857 569293416 : if (v->constant) {
858 200181360 : if (v && v->type == cst->vtype &&
859 116625072 : v->value.len == cst->len &&
860 192393405 : isaBatType(v->type) == cst->bat &&
861 96204142 : ATOMcmp(cst->vtype, VALptr(&v->value), p) == 0)
862 31875461 : return i;
863 : }
864 : }
865 : return -1;
866 : }
867 :
868 : int
869 3334 : cpyConstant(MalBlkPtr mb, VarPtr vr)
870 : {
871 3334 : int i;
872 3334 : ValRecord cst;
873 3334 : if (VALcopy(&cst, &vr->value) == NULL)
874 : return -1;
875 3334 : i = defConstant(mb, vr->type, &cst);
876 3334 : if (i < 0)
877 : return -1;
878 : return i;
879 : }
880 :
881 : int
882 46894702 : defConstant(MalBlkPtr mb, int type, ValPtr cst)
883 : {
884 46894702 : int k;
885 46894702 : str msg;
886 :
887 46894702 : assert(!isaBatType(type) || cst->bat);
888 46894702 : cst->bat = false;
889 46894702 : if (isaBatType(type)) {
890 474007 : if (cst->vtype == TYPE_void) {
891 474006 : cst->vtype = getBatType(type);
892 474006 : cst->bat = true;
893 474006 : cst->val.bval = bat_nil;
894 : } else {
895 1 : mb->errors = createMalException(mb, 0, TYPE, "BAT coercion error");
896 1 : VALclear(cst); // it could contain allocated space
897 1 : return -1;
898 : }
899 46420695 : } else if (cst->vtype != type && !isPolyType(type)) {
900 1917 : int otype = cst->vtype;
901 1917 : assert(type != TYPE_any); /* help Coverity */
902 1917 : msg = convertConstant(getBatType(type), cst);
903 1917 : if (msg) {
904 3 : str ft, tt; /* free old value */
905 3 : ft = getTypeName(otype);
906 3 : tt = getTypeName(type);
907 3 : if (ft && tt)
908 3 : mb->errors = createMalException(mb, 0, TYPE,
909 : "constant coercion error from %s to %s",
910 : ft, tt);
911 : else
912 0 : mb->errors = createMalException(mb, 0, TYPE,
913 : "constant coercion error");
914 3 : GDKfree(ft);
915 3 : GDKfree(tt);
916 3 : freeException(msg);
917 3 : VALclear(cst); /* it could contain allocated space */
918 3 : return -1;
919 : } else {
920 1914 : assert(cst->vtype == type);
921 : }
922 : }
923 46894698 : if (cst->vtype != TYPE_any) {
924 46895578 : k = fndConstant(mb, cst, MAL_VAR_WINDOW);
925 46896766 : if (k >= 0) { /* protect against leaks coming from constant reuse */
926 28219225 : VALclear(cst);
927 28219225 : return k;
928 : }
929 : }
930 18676661 : k = newTmpVariable(mb, type);
931 18718016 : if (k < 0) {
932 0 : VALclear(cst);
933 0 : return -1;
934 : }
935 18718016 : setVarConstant(mb, k);
936 18718016 : setVarFixed(mb, k);
937 18718016 : if (type >= 0 && type < GDKatomcnt && ATOMextern(type))
938 4752273 : setVarCleanup(mb, k);
939 : else
940 13965743 : clrVarCleanup(mb, k); /* if cst is external, we give its allocated buffer away, so clear * it to avoid confusion */
941 18718016 : getVarConstant(mb, k) = *cst;
942 18718016 : VALempty(cst);
943 18718016 : return k;
944 : }
945 :
946 : /* Argument handling
947 : * The number of arguments for procedures is currently
948 : * limited. Furthermore, we should assure that no variable is
949 : * referenced before being assigned. Failure to obey should mark the
950 : * instruction as type-error. */
951 : static InstrPtr
952 319 : extendInstruction(MalBlkPtr mb, InstrPtr p)
953 : {
954 319 : InstrPtr pn = p;
955 319 : if (p->argc == p->maxarg) {
956 319 : int space = p->maxarg * sizeof(p->argv[0]) + offsetof(InstrRecord, argv);
957 319 : pn = (InstrPtr) GDKrealloc(p, space + MAXARG * sizeof(p->argv[0]));
958 319 : if (pn == NULL) { /* In the exceptional case we can not allocate more space * then we show an exception, mark the block as erroneous * and leave the instruction as is. */
959 0 : mb->errors = createMalException(mb, 0, TYPE,
960 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
961 0 : return p;
962 : }
963 319 : memset(((char *) pn) + space, 0, MAXARG * sizeof(pn->argv[0]));
964 319 : pn->maxarg += MAXARG;
965 : }
966 : return pn;
967 : }
968 :
969 : InstrPtr
970 175058559 : pushArgument(MalBlkPtr mb, InstrPtr p, int varid)
971 : {
972 175058559 : if (p == NULL || mb->errors)
973 : return p;
974 175058559 : if (varid < 0) { /* leave everything as is in this exceptional programming error */
975 0 : mb->errors = createMalException(mb, 0, TYPE, "improper variable id");
976 0 : return p;
977 : }
978 175058559 : if (p->argc == p->maxarg) {
979 : #ifndef NDEBUG
980 1011 : for (int i = 0; i < mb->stop; i++)
981 692 : assert(mb->stmt[i] != p);
982 : #endif
983 319 : p = extendInstruction(mb, p);
984 319 : if (mb->errors)
985 : return p;
986 : } /* protect against the case that the instruction is malloced in isolation */
987 175058559 : if (mb->maxarg < p->maxarg)
988 12438 : mb->maxarg = p->maxarg;
989 175058559 : p->argv[p->argc++] = varid;
990 175058559 : return p;
991 : }
992 :
993 : InstrPtr
994 858948 : setArgument(MalBlkPtr mb, InstrPtr p, int idx, int varid)
995 : {
996 858948 : int i;
997 858948 : if (p == NULL || mb->errors)
998 : return p;
999 858958 : p = pushArgument(mb, p, varid); /* make space */
1000 862907 : for (i = p->argc - 1; i > idx; i--)
1001 3952 : getArg(p, i) = getArg(p, i - 1);
1002 858955 : getArg(p, i) = varid;
1003 858955 : return p;
1004 : }
1005 :
1006 : InstrPtr
1007 859899 : pushReturn(MalBlkPtr mb, InstrPtr p, int varid)
1008 : {
1009 859899 : if (p == NULL || mb->errors)
1010 : return p;
1011 859899 : if (p->retc == 1 && p->argv[0] == -1) {
1012 931 : p->argv[0] = varid;
1013 931 : return p;
1014 : }
1015 858968 : p = setArgument(mb, p, p->retc, varid);
1016 858967 : p->retc++;
1017 858967 : return p;
1018 : }
1019 :
1020 : /* Store the information of a destination variable in the signature
1021 : * structure of each instruction. This code is largely equivalent to
1022 : * pushArgument, but it is more efficient in searching and collecting
1023 : * the information.
1024 : * TODO */
1025 : /* swallows name argument */
1026 : InstrPtr
1027 5431 : pushArgumentId(MalBlkPtr mb, InstrPtr p, const char *name)
1028 : {
1029 5431 : int v;
1030 5431 : if (p == NULL || mb->errors)
1031 : return p;
1032 5431 : v = findVariable(mb, name);
1033 5431 : if (v < 0) {
1034 389 : size_t namelen = strlen(name);
1035 389 : if ((v = newVariable(mb, name, namelen, getAtomIndex(name, namelen, TYPE_any))) < 0) {
1036 : /* set the MAL block to erroneous and simply return without
1037 : * doing anything */
1038 : /* mb->errors already set */
1039 : return p;
1040 : }
1041 : }
1042 5430 : return pushArgument(mb, p, v);
1043 : }
1044 :
1045 : /* The alternative is to remove arguments from an instruction
1046 : * record. This is typically part of instruction constructions. */
1047 : void
1048 1480760 : delArgument(InstrPtr p, int idx)
1049 : {
1050 1480760 : int i;
1051 2256877 : for (i = idx; i < p->argc - 1; i++)
1052 776117 : p->argv[i] = p->argv[i + 1];
1053 1480760 : p->argc--;
1054 1480760 : if (idx < p->retc)
1055 182695 : p->retc--;
1056 1480760 : }
1057 :
1058 : void
1059 35519 : setArgType(MalBlkPtr mb, InstrPtr p, int i, int tpe)
1060 : {
1061 35519 : assert(p->argv[i] < mb->vsize);
1062 35519 : setVarType(mb, getArg(p, i), tpe);
1063 35519 : }
1064 :
1065 : void
1066 0 : setReturnArgument(InstrPtr p, int i)
1067 : {
1068 0 : setDestVar(p, i);
1069 0 : }
1070 :
1071 : malType
1072 0 : destinationType(MalBlkPtr mb, InstrPtr p)
1073 : {
1074 0 : if (p->argc > 0)
1075 0 : return getVarType(mb, getDestVar(p));
1076 : return TYPE_any;
1077 : }
1078 :
1079 : /* For polymorphic instructions we should keep around the maximal
1080 : * index to later allocate sufficient space for type resolutions maps.
1081 : * Beware, that we should only consider the instruction polymorphic if
1082 : * it has a positive index or belongs to the signature.
1083 : * BATs can only have a polymorphic type at the tail.
1084 : */
1085 : inline void
1086 9251 : setPolymorphic(InstrPtr p, int tpe, int force /* just any isn't polymorphic */)
1087 : {
1088 9251 : int any = isAnyExpression(tpe) || tpe == TYPE_any, index = 0;
1089 9251 : if ((force == FALSE && tpe == TYPE_any) || !any)
1090 : return;
1091 34 : if (getTypeIndex(tpe) > 0)
1092 : index = getTypeIndex(tpe);
1093 34 : if (any && (index + 1) >= p->polymorphic)
1094 31 : p->polymorphic = index + 1;
1095 : }
1096 :
1097 : /* Instructions are simply appended to a MAL block. It should always succeed.
1098 : * The assumption is to push it when you are completely done with its preparation.
1099 : */
1100 : void
1101 298237153 : pushInstruction(MalBlkPtr mb, InstrPtr p)
1102 : {
1103 298237153 : int i;
1104 298237153 : InstrPtr q;
1105 298237153 : if (p == NULL)
1106 : return;
1107 298237153 : if (mb->stop + 1 >= mb->ssize) {
1108 69282 : int s = (mb->ssize / MALCHUNK + 1) * MALCHUNK;
1109 69282 : if (resizeMalBlk(mb, s) < 0) {
1110 : /* we are now left with the situation that the new
1111 : * instruction is dangling. The hack is to take an
1112 : * instruction out of the block that is likely not
1113 : * referenced independently. The last resort is to take the
1114 : * first, which should always be there. This assumes that
1115 : * no references are kept elsewhere to the statement. */
1116 0 : assert(mb->errors != NULL);
1117 0 : for (i = 1; i < mb->stop; i++) {
1118 0 : q = getInstrPtr(mb, i);
1119 0 : if (q->token == REMsymbol) {
1120 0 : freeInstruction(q);
1121 0 : mb->stmt[i] = p;
1122 0 : return;
1123 : }
1124 : }
1125 0 : freeInstruction(getInstrPtr(mb, 0));
1126 0 : mb->stmt[0] = p;
1127 0 : return;
1128 : }
1129 : }
1130 298237153 : if (mb->stmt[mb->stop])
1131 19087 : freeInstruction(mb->stmt[mb->stop]);
1132 298251244 : p->pc = mb->stop;
1133 298251244 : mb->stmt[mb->stop++] = p;
1134 : }
|