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