1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * (author) Author M. Kersten
11 * For documentation see website
12 */
13#include "monetdb_config.h"
14#include "mal_instruction.h"
15#include "mal_function.h" /* for getPC() */
16#include "mal_utils.h"
17#include "mal_exception.h"
18
19void
20addMalException(MalBlkPtr mb, str msg)
21{
22 str new;
23
24 if( mb->errors){
25 new = GDKzalloc(strlen(mb->errors) + strlen(msg) + 4);
26 if (new == NULL)
27 return ; // just stick to one error message, ignore rest
28 strcpy(new, mb->errors);
29 strcat(new, msg);
30 freeException(mb->errors);
31 mb->errors = new;
32 } else {
33 new = GDKstrdup(msg);
34 if( new == NULL)
35 return ; // just stick to one error message, ignore rest
36 mb->errors = new;
37 }
38}
39
40Symbol
41newSymbol(str nme, int kind)
42{
43 Symbol cur;
44
45 if (nme == NULL)
46 return NULL;
47 cur = (Symbol) GDKzalloc(sizeof(SymRecord));
48 if (cur == NULL)
49 return NULL;
50 cur->name = putName(nme);
51 if(cur->name == NULL) {
52 GDKfree(cur);
53 return NULL;
54 }
55 cur->kind = kind;
56 cur->peer = NULL;
57 cur->def = newMalBlk(kind == FUNCTIONsymbol? STMT_INCREMENT : 2);
58 if (cur->def == NULL){
59 GDKfree(cur);
60 return NULL;
61 }
62 return cur;
63}
64
65void
66freeSymbol(Symbol s)
67{
68 if (s == NULL)
69 return;
70 if (s->def) {
71 freeMalBlk(s->def);
72 s->def = NULL;
73 }
74 GDKfree(s);
75}
76
77void
78freeSymbolList(Symbol s)
79{
80 Symbol t = s;
81
82 while (s) {
83 t = s->peer;
84 s->peer = NULL;
85 freeSymbol(s);
86 s = t;
87 }
88}
89
90int
91newMalBlkStmt(MalBlkPtr mb, int maxstmts)
92{
93 InstrPtr *p;
94
95 p = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * maxstmts);
96 if (p == NULL)
97 return -1;
98 mb->stmt = p;
99 mb->stop = 0;
100 mb->ssize = maxstmts;
101 return 0;
102}
103
104MalBlkPtr
105newMalBlk(int elements)
106{
107 MalBlkPtr mb;
108 VarRecord *v;
109
110 mb = (MalBlkPtr) GDKmalloc(sizeof(MalBlkRecord));
111 if (mb == NULL)
112 return NULL;
113
114 /* each MAL instruction implies at least one variable
115 * we reserve some extra for constants */
116 v = (VarRecord *) GDKzalloc(sizeof(VarRecord) * (elements + 8) );
117 if (v == NULL) {
118 GDKfree(mb);
119 return NULL;
120 }
121 mb->var = v;
122 mb->vtop = 0;
123 mb->vid = 0;
124 mb->vsize = elements;
125 mb->help = NULL;
126 mb->binding[0] = 0;
127 mb->tag = 0;
128 mb->errors = NULL;
129 mb->alternative = NULL;
130 mb->history = NULL;
131 mb->keephistory = 0;
132 mb->maxarg = MAXARG; /* the minimum for each instruction */
133 mb->inlineProp = 0;
134 mb->unsafeProp = 0;
135 mb->sealedProp = 0;
136 mb->replica = NULL;
137 mb->starttime = 0;
138 mb->runtime = 0;
139 mb->calls = 0;
140 mb->optimize = 0;
141 mb->stmt = NULL;
142 mb->activeClients = 1;
143 if (newMalBlkStmt(mb, elements) < 0) {
144 GDKfree(mb->var);
145 GDKfree(mb->stmt);
146 GDKfree(mb);
147 return NULL;
148 }
149 return mb;
150}
151
152/* We only grow until the MAL block can be used */
153static int growBlk(int elm)
154{
155 int steps =1 ;
156 int old = elm;
157
158 while( old / 2 > 1){
159 old /= 2;
160 steps++;
161 }
162 return elm + steps * STMT_INCREMENT;
163}
164
165int
166resizeMalBlk(MalBlkPtr mb, int elements)
167{
168 int i;
169
170 if( elements > mb->ssize){
171 InstrPtr *ostmt = mb->stmt;
172 mb->stmt = (InstrPtr *) GDKrealloc(mb->stmt, elements * sizeof(InstrPtr));
173 if ( mb->stmt ){
174 for ( i = mb->ssize; i < elements; i++)
175 mb->stmt[i] = 0;
176 mb->ssize = elements;
177 } else {
178 mb->stmt = ostmt; /* reinstate old pointer */
179 mb->errors = createMalException(mb,0, TYPE, "out of memory (requested: %"PRIu64" bytes)", (uint64_t) elements * sizeof(InstrPtr));
180 return -1;
181 }
182 }
183
184
185 if( elements > mb->vsize){
186 VarRecord *ovar = mb->var;
187 mb->var = (VarRecord*) GDKrealloc(mb->var, elements * sizeof (VarRecord));
188 if ( mb->var ){
189 memset( ((char*) mb->var) + sizeof(VarRecord) * mb->vsize, 0, (elements - mb->vsize) * sizeof(VarRecord));
190 mb->vsize = elements;
191 } else{
192 mb->var = ovar;
193 mb->errors = createMalException(mb,0, TYPE, "out of memory (requested: %"PRIu64" bytes)", (uint64_t) elements * sizeof(InstrPtr));
194 return -1;
195 }
196 }
197 return 0;
198}
199/* The resetMalBlk code removes instructions, but without freeing the
200 * space. This way the structure is prepared for re-use */
201void
202resetMalBlk(MalBlkPtr mb, int stop)
203{
204 int i;
205
206 for(i=0; i<stop; i++)
207 mb->stmt[i] ->typechk = TYPE_UNKNOWN;
208 mb->stop = stop;
209 mb->errors = NULL;
210}
211
212void
213resetMalBlkAndFreeInstructions(MalBlkPtr mb, int stop)
214{
215 int i;
216
217 for(i=stop; i<mb->stop; i++) {
218 freeInstruction(mb->stmt[i]);
219 mb->stmt[i] = NULL;
220 }
221 resetMalBlk(mb, stop);
222}
223
224/* The freeMalBlk code is quite defensive. It is used to localize an
225 * illegal re-use of a MAL blk. */
226void
227freeMalBlk(MalBlkPtr mb)
228{
229 int i;
230
231 for (i = 0; i < mb->ssize; i++)
232 if (mb->stmt[i]) {
233 freeInstruction(mb->stmt[i]);
234 mb->stmt[i] = NULL;
235 }
236 mb->stop = 0;
237 for(i=0; i< mb->vtop; i++)
238 VALclear(&getVarConstant(mb,i));
239 mb->vtop = 0;
240 mb->vid = 0;
241 GDKfree(mb->stmt);
242 mb->stmt = 0;
243 GDKfree(mb->var);
244 mb->var = 0;
245
246 if (mb->history)
247 freeMalBlk(mb->history);
248 mb->binding[0] = 0;
249 mb->tag = 0;
250 if (mb->help)
251 GDKfree(mb->help);
252 mb->help = 0;
253 mb->inlineProp = 0;
254 mb->unsafeProp = 0;
255 mb->sealedProp = 0;
256 GDKfree(mb->errors);
257 GDKfree(mb);
258}
259
260/* The routine below should assure that all referenced structures are
261 * private. The copying is memory conservative. */
262MalBlkPtr
263copyMalBlk(MalBlkPtr old)
264{
265 MalBlkPtr mb;
266 int i;
267
268 mb = (MalBlkPtr) GDKzalloc(sizeof(MalBlkRecord));
269 if (mb == NULL)
270 return NULL;
271 mb->alternative = old->alternative;
272 mb->history = NULL;
273 mb->keephistory = old->keephistory;
274
275 mb->var = (VarRecord *) GDKzalloc(sizeof(VarRecord) * old->vsize);
276 if (mb->var == NULL) {
277 GDKfree(mb);
278 return NULL;
279 }
280
281 mb->activeClients = 1;
282 mb->vsize = old->vsize;
283 mb->vtop = old->vtop;
284 mb->vid = old->vid;
285
286 // copy all variable records
287 for (i = 0; i < old->vtop; i++) {
288 mb->var[i]= old->var[i];
289 if (!VALcopy(&(mb->var[i].value), &(old->var[i].value))) {
290 while (--i >= 0)
291 VALclear(&mb->var[i].value);
292 GDKfree(mb->var);
293 GDKfree(mb);
294 return NULL;
295 }
296 }
297
298 mb->stmt = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * old->ssize);
299
300 if (mb->stmt == NULL) {
301 for (i = 0; i < old->vtop; i++)
302 VALclear(&mb->var[i].value);
303 GDKfree(mb->var);
304 GDKfree(mb);
305 return NULL;
306 }
307
308 mb->stop = old->stop;
309 mb->ssize = old->ssize;
310 assert(old->stop < old->ssize);
311 for (i = 0; i < old->stop; i++) {
312 mb->stmt[i] = copyInstruction(old->stmt[i]);
313 if(!mb->stmt[i]) {
314 while (--i >= 0){
315 freeInstruction(mb->stmt[i]);
316 mb->stmt[i]= NULL;
317 }
318 for (i = 0; i < old->vtop; i++)
319 VALclear(&mb->var[i].value);
320 GDKfree(mb->var);
321 GDKfree(mb->stmt);
322 GDKfree(mb);
323 return NULL;
324 }
325 }
326 mb->help = old->help ? GDKstrdup(old->help) : NULL;
327 if (old->help && !mb->help) {
328 for (i = 0; i < old->stop; i++){
329 freeInstruction(mb->stmt[i]);
330 mb->stmt[i]= NULL;
331 }
332 for (i = 0; i < old->vtop; i++)
333 VALclear(&mb->var[i].value);
334 GDKfree(mb->var);
335 GDKfree(mb->stmt);
336 GDKfree(mb);
337 return NULL;
338 }
339 strcpy_len(mb->binding, old->binding, sizeof(mb->binding));
340 mb->errors = old->errors? GDKstrdup(old->errors):0;
341 mb->tag = old->tag;
342 mb->runtime = old->runtime;
343 mb->calls = old->calls;
344 mb->optimize = old->optimize;
345 mb->replica = old->replica;
346 mb->maxarg = old->maxarg;
347 mb->inlineProp = old->inlineProp;
348 mb->unsafeProp = old->unsafeProp;
349 mb->sealedProp = old->sealedProp;
350 return mb;
351}
352
353void
354addtoMalBlkHistory(MalBlkPtr mb)
355{
356 MalBlkPtr cpy, h;
357 if (mb->keephistory) {
358 cpy = copyMalBlk(mb);
359 if (cpy == NULL)
360 return; /* ignore history */
361 cpy->history = NULL;
362 if (mb->history == NULL)
363 mb->history = cpy;
364 else {
365 for (h = mb; h->history; h = h->history)
366 ;
367 h->history = cpy;
368 }
369 }
370}
371
372MalBlkPtr
373getMalBlkHistory(MalBlkPtr mb, int idx)
374{
375 MalBlkPtr h = mb;
376
377 while (h && idx-- >= 0)
378 h = h->history;
379 return h ? h : mb;
380}
381
382// Localize the plan using the optimizer name
383MalBlkPtr
384getMalBlkOptimized(MalBlkPtr mb, str name)
385{
386 MalBlkPtr h = mb->history;
387 InstrPtr p;
388 int i= 0;
389 char buf[IDLENGTH]= {0}, *n;
390 size_t nlen;
391
392 if( name == 0)
393 return mb;
394
395 nlen = strlen(name);
396 if (nlen >= sizeof(buf)) {
397 mb->errors = createMalException(mb,0, TYPE, "Optimizer name is too large");
398 return NULL;
399 }
400 memcpy(buf, name, nlen + 1);
401 n = strchr(buf,']');
402 if( n) *n = 0;
403
404 while (h ){
405 for( i = 1; i< h->stop; i++){
406 p = getInstrPtr(h,i);
407 if( p->token == REMsymbol && strstr(getVarConstant(h, getArg(p,0)).val.sval, buf) )
408 return h;
409 }
410 h = h->history;
411 }
412 return 0;
413}
414
415
416/* Before compiling a large string, it makes sense to allocate
417 * approximately enough space to keep the intermediate
418 * code. Otherwise, we end up with a repeated extend on the MAL block,
419 * which really consumes a lot of memcpy resources. The average MAL
420 * string length could been derived from the test cases. An error in
421 * the estimate is more expensive than just counting the lines.
422 */
423int
424prepareMalBlk(MalBlkPtr mb, str s)
425{
426 int cnt = STMT_INCREMENT;
427
428 while (s) {
429 s = strchr(s, '\n');
430 if (s) {
431 s++;
432 cnt++;
433 }
434 }
435 cnt = (int) (cnt * 1.1);
436 return resizeMalBlk(mb, cnt);
437}
438
439/* The MAL records should be managed from a pool to
440 * avoid repeated alloc/free and reduce probability of
441 * memory fragmentation. (todo)
442 * The complicating factor is their variable size,
443 * which leads to growing records as a result of pushArguments
444 * Allocation of an instruction should always succeed.
445 */
446InstrPtr
447newInstructionArgs(MalBlkPtr mb, str modnme, str fcnnme, int args)
448{
449 InstrPtr p = NULL;
450
451 p = GDKzalloc(args * sizeof(p->argv[0]) + offsetof(InstrRecord, argv));
452 if (p == NULL) {
453 /* We are facing an hard problem.
454 * The hack is to re-use an already allocated instruction.
455 * The marking of the block as containing errors should protect further actions.
456 */
457 if( mb){
458 mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL);
459 }
460 return NULL;
461 }
462 p->maxarg = args;
463 p->typechk = TYPE_UNKNOWN;
464 setModuleId(p, modnme);
465 setFunctionId(p, fcnnme);
466 p->argc = 1;
467 p->retc = 1;
468 p->mitosis = -1;
469 p->argv[0] = -1; /* watch out for direct use in variable table */
470 /* Flow of control instructions are always marked as an assignment
471 * with modifier */
472 p->token = ASSIGNsymbol;
473 return p;
474
475}
476InstrPtr
477newInstruction(MalBlkPtr mb, str modnme, str fcnnme)
478{
479 InstrPtr p = NULL;
480
481 p = GDKzalloc(MAXARG * sizeof(p->argv[0]) + offsetof(InstrRecord, argv));
482 if (p == NULL) {
483 /* We are facing an hard problem.
484 * The hack is to re-use an already allocated instruction.
485 * The marking of the block as containing errors should protect further actions.
486 */
487 if( mb){
488 mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL);
489 }
490 return NULL;
491 }
492 p->maxarg = MAXARG;
493 p->typechk = TYPE_UNKNOWN;
494 setModuleId(p, modnme);
495 setFunctionId(p, fcnnme);
496 p->argc = 1;
497 p->retc = 1;
498 p->mitosis = -1;
499 p->argv[0] = -1; /* watch out for direct use in variable table */
500 /* Flow of control instructions are always marked as an assignment
501 * with modifier */
502 p->token = ASSIGNsymbol;
503 return p;
504}
505
506/* Copying an instruction is space conservative. */
507InstrPtr
508copyInstruction(InstrPtr p)
509{
510 InstrPtr new = (InstrPtr) GDKmalloc(offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->maxarg));
511 if(new == NULL)
512 return new;
513 oldmoveInstruction(new, p);
514 return new;
515}
516
517void
518clrFunction(InstrPtr p)
519{
520 p->token = ASSIGNsymbol;
521 p->fcn = 0;
522 p->blk = 0;
523 p->typechk = TYPE_UNKNOWN;
524 setModuleId(p, NULL);
525 setFunctionId(p, NULL);
526}
527
528void
529clrInstruction(InstrPtr p)
530{
531 clrFunction(p);
532 memset((char *) p, 0, offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]));
533}
534
535void
536freeInstruction(InstrPtr p)
537{
538 GDKfree(p);
539}
540
541/* Moving instructions around calls for care, because all dependent
542 * information should also be updated. */
543void
544oldmoveInstruction(InstrPtr new, InstrPtr p)
545{
546 int space;
547
548 space = offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]);
549 memcpy((char *) new, (char *) p, space);
550 setFunctionId(new, getFunctionId(p));
551 setModuleId(new, getModuleId(p));
552 new->typechk = TYPE_UNKNOWN;
553}
554
555/* Query optimizers walk their way through a MAL program block. They
556 * require some primitives to move instructions around and to remove
557 * superflous instructions. The removal is based on the assumption
558 * that indeed the instruction belonged to the block. */
559void
560removeInstruction(MalBlkPtr mb, InstrPtr p)
561{
562 int i;
563
564 for (i = 0; i < mb->stop - 1; i++)
565 if (mb->stmt[i] == p)
566 break;
567
568 if (i == mb->stop)
569 return;
570
571 for (; i < mb->stop - 1; i++)
572 mb->stmt[i] = mb->stmt[i + 1];
573 mb->stmt[i] = 0;
574 mb->stop--;
575 assert(i == mb->stop);
576
577 /* move statement after stop */
578 mb->stmt[i] = p;
579}
580
581void
582removeInstructionBlock(MalBlkPtr mb, int pc, int cnt)
583{
584 int i;
585 InstrPtr p;
586
587 for (i = pc; i < pc + cnt; i++) {
588 p = getInstrPtr(mb, i);
589 freeInstruction(p);
590 mb->stmt[i]= NULL;
591 }
592
593 for (i = pc; i < mb->stop - cnt; i++)
594 mb->stmt[i] = mb->stmt[i + cnt];
595
596 mb->stop -= cnt;
597 for (; i < mb->stop; i++)
598 mb->stmt[i] = 0;
599}
600
601void
602moveInstruction(MalBlkPtr mb, int pc, int target)
603{
604 InstrPtr p;
605 int i;
606
607 p = getInstrPtr(mb, pc);
608 if (pc > target) {
609 for (i = pc; i > target; i--)
610 mb->stmt[i] = mb->stmt[i - 1];
611 mb->stmt[i] = p;
612 } else {
613 for (i = target; i > pc; i--)
614 mb->stmt[i] = mb->stmt[i - 1];
615 mb->stmt[i] = p;
616 }
617}
618
619/* Beware that the first argument of a signature is reserved for the
620 * function return type , which should be equal to the destination
621 * variable type.
622 */
623
624int
625findVariable(MalBlkPtr mb, const char *name)
626{
627 int i;
628
629 if (name == NULL)
630 return -1;
631 for (i = mb->vtop - 1; i >= 0; i--)
632 if (idcmp(name, getVarName(mb, i)) == 0)
633 return i;
634 return -1;
635}
636
637/* The second version of findVariable assumes you have not yet
638 * allocated a private structure. This is particularly useful during
639 * parsing, because most variables are already defined. This way we
640 * safe GDKmalloc/GDKfree. */
641int
642findVariableLength(MalBlkPtr mb, str name, int len)
643{
644 int i;
645
646 for (i = mb->vtop - 1; i >= 0; i--) {
647 const char *s = getVarName(mb, i);
648
649 if (s && strncmp(name, s, len) == 0 && s[len] == 0)
650 return i;
651 }
652 return -1;
653}
654
655/* Note that getType also checks for type names directly. They have
656 * preference over variable names. */
657malType
658getType(MalBlkPtr mb, str nme)
659{
660 int i;
661
662 i = findVariable(mb, nme);
663 if (i < 0)
664 return getAtomIndex(nme, strlen(nme), TYPE_any);
665 return getVarType(mb, i);
666}
667
668str
669getArgDefault(MalBlkPtr mb, InstrPtr p, int idx)
670{
671 ValPtr v = &getVarConstant(mb, getArg(p, idx));
672
673 if (v->vtype == TYPE_str)
674 return v->val.sval;
675 return NULL;
676}
677
678/* All variables are implicitly declared upon their first assignment.
679 *
680 * Lexical constants require some care. They typically appear as
681 * arguments in operator/function calls. To simplify program analysis
682 * later on, we stick to the situation that function/operator
683 * arguments are always references to by variables.
684 *
685 * Reserved words
686 * Although MAL has been designed as a minimal language, several
687 * identifiers are not eligible as variables. The encoding below is
688 * geared at simple and speed. */
689#if 0
690int
691isReserved(str nme)
692{
693 switch (*nme) {
694 case 'A':
695 case 'a':
696 if (idcmp("atom", nme) == 0)
697 return 1;
698 break;
699 case 'B':
700 case 'b':
701 if (idcmp("barrier", nme) == 0)
702 return 1;
703 break;
704 case 'C':
705 case 'c':
706 if (idcmp("command", nme) == 0)
707 return 1;
708 break;
709 case 'E':
710 case 'e':
711 if (idcmp("exit", nme) == 0)
712 return 1;
713 if (idcmp("end", nme) == 0)
714 return 1;
715 break;
716 case 'F':
717 case 'f':
718 if (idcmp("false", nme) == 0)
719 return 1;
720 if (idcmp("function", nme) == 0)
721 return 1;
722 if (idcmp("factory", nme) == 0)
723 return 1;
724 break;
725 case 'I':
726 case 'i':
727 if (idcmp("include", nme) == 0)
728 return 1;
729 break;
730 case 'M':
731 case 'm':
732 if (idcmp("module", nme) == 0)
733 return 1;
734 if (idcmp("macro", nme) == 0)
735 return 1;
736 break;
737 case 'O':
738 case 'o':
739 if (idcmp("orcam", nme) == 0)
740 return 1;
741 break;
742 case 'P':
743 case 'p':
744 if (idcmp("pattern", nme) == 0)
745 return 1;
746 break;
747 case 'T':
748 case 't':
749 if (idcmp("thread", nme) == 0)
750 return 1;
751 if (idcmp("true", nme) == 0)
752 return 1;
753 break;
754 }
755 return 0;
756}
757#endif
758
759/* Beware, the symbol table structure assumes that it is relatively
760 * cheap to perform a linear search to a variable or constant. */
761static int
762makeVarSpace(MalBlkPtr mb)
763{
764 if (mb->vtop >= mb->vsize) {
765 VarRecord *new;
766 int s = growBlk(mb->vsize);
767
768 new = (VarRecord*) GDKrealloc(mb->var, s * sizeof(VarRecord));
769 if (new == NULL) {
770 // the only place to return an error signal at this stage.
771 // The Client context should be passed around more deeply
772 mb->errors = createMalException(mb,0,TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL);
773 return -1;
774 }
775 memset( ((char*) new) + mb->vsize * sizeof(VarRecord), 0, (s- mb->vsize) * sizeof(VarRecord));
776 mb->vsize = s;
777 mb->var = new;
778 }
779 return 0;
780}
781
782/* create and initialize a variable record*/
783int
784newVariable(MalBlkPtr mb, const char *name, size_t len, malType type)
785{
786 int n;
787
788 if( len >= IDLENGTH)
789 return -1;
790 if (makeVarSpace(mb))
791 /* no space for a new variable */
792 return -1;
793 n = mb->vtop;
794 if( name == 0 || len == 0){
795 (void) snprintf(getVarName(mb,n), IDLENGTH,"%c%c%d", REFMARKER, TMPMARKER,mb->vid++);
796 } else {
797 (void) strcpy_len( getVarName(mb,n), name, len + 1);
798 }
799
800 setRowCnt(mb,n,0);
801 setVarType(mb, n, type);
802 clrVarFixed(mb, n);
803 clrVarUsed(mb, n);
804 clrVarInit(mb, n);
805 clrVarDisabled(mb, n);
806 clrVarUDFtype(mb, n);
807 clrVarConstant(mb, n);
808 clrVarCleanup(mb, n);
809 mb->vtop++;
810 return n;
811}
812
813/* Simplified cloning. */
814int
815cloneVariable(MalBlkPtr tm, MalBlkPtr mb, int x)
816{
817 int res;
818 if (isVarConstant(mb, x))
819 res = cpyConstant(tm, getVar(mb, x));
820 else
821 res = newVariable(tm, getVarName(mb, x), strlen(getVarName(mb,x)), getVarType(mb, x));
822 if (res < 0)
823 return res;
824 if (isVarFixed(mb, x))
825 setVarFixed(tm, res);
826 if (isVarUsed(mb, x))
827 setVarUsed(tm, res);
828 if (isVarInit(mb, x))
829 setVarInit(tm, res);
830 if (isVarDisabled(mb, x))
831 setVarDisabled(tm, res);
832 if (isVarUDFtype(mb, x))
833 setVarUDFtype(tm, res);
834 if (isVarCleanup(mb, x))
835 setVarCleanup(tm, res);
836 getVarSTC(tm,x) = getVarSTC(mb,x);
837 return res;
838}
839
840int
841newTmpVariable(MalBlkPtr mb, malType type)
842{
843 return newVariable(mb,0,0,type);
844}
845
846int
847newTypeVariable(MalBlkPtr mb, malType type)
848{
849 int n, i;
850 for (i = 0; i < mb->vtop; i++)
851 if (isVarTypedef(mb, i) && getVarType(mb, i) == type)
852 break;
853
854 if( i < mb->vtop )
855 return i;
856 n = newTmpVariable(mb, type);
857 setVarTypedef(mb, n);
858 return n;
859}
860
861void
862clearVariable(MalBlkPtr mb, int varid)
863{
864 VarPtr v;
865
866 v = getVar(mb, varid);
867 if (isVarConstant(mb, varid) || isVarDisabled(mb, varid))
868 VALclear(&v->value);
869 v->type = 0;
870 v->constant= 0;
871 v->typevar= 0;
872 v->fixedtype= 0;
873 v->udftype= 0;
874 v->cleanup= 0;
875 v->initialized= 0;
876 v->used= 0;
877 v->rowcnt = 0;
878 v->eolife = 0;
879 v->stc = 0;
880}
881
882void
883freeVariable(MalBlkPtr mb, int varid)
884{
885 clearVariable(mb, varid);
886}
887
888/* A special action is to reduce the variable space by removing all
889 * that do not contribute.
890 * All temporary variables are renamed in the process to trim the varid.
891 */
892void
893trimMalVariables_(MalBlkPtr mb, MalStkPtr glb)
894{
895 int *alias, cnt = 0, i, j;
896 InstrPtr q;
897
898 if( mb->vtop == 0)
899 return;
900 alias = (int *) GDKzalloc(mb->vtop * sizeof(int));
901 if (alias == NULL)
902 return; /* forget it if we run out of memory */
903
904 /* build the alias table */
905 for (i = 0; i < mb->vtop; i++) {
906#ifdef DEBUG_REDUCE
907 fprintf(stderr,"used %s %d\n", getVarName(mb,i), isVarUsed(mb,i));
908#endif
909 if ( isVarUsed(mb,i) == 0) {
910 if (glb && isVarConstant(mb, i))
911 VALclear(&glb->stk[i]);
912 freeVariable(mb, i);
913 continue;
914 }
915 if (i > cnt) {
916 /* remap temporary variables */
917 VarRecord t = mb->var[cnt];
918 mb->var[cnt] = mb->var[i];
919 mb->var[i] = t;
920 }
921
922 /* valgrind finds a leak when we move these variable record
923 * pointers around. */
924 alias[i] = cnt;
925 if (glb && i != cnt) {
926 glb->stk[cnt] = glb->stk[i];
927 VALempty(&glb->stk[i]);
928 }
929 cnt++;
930 }
931#ifdef DEBUG_REDUCE
932 fprintf(stderr, "Variable reduction %d -> %d\n", mb->vtop, cnt);
933 for (i = 0; i < mb->vtop; i++)
934 fprintf(stderr, "map %d->%d\n", i, alias[i]);
935#endif
936
937 /* remap all variable references to their new position. */
938 if (cnt < mb->vtop) {
939 for (i = 0; i < mb->stop; i++) {
940 q = getInstrPtr(mb, i);
941 for (j = 0; j < q->argc; j++){
942#ifdef DEBUG_REDUCE
943 fprintf(stderr, "map %d->%d\n", getArg(q,j), alias[getArg(q,j)]);
944#endif
945 getArg(q, j) = alias[getArg(q, j)];
946 }
947 }
948 }
949 /* rename the temporary variable */
950 mb->vid = 0;
951 for( i =0; i< cnt; i++)
952 if( isTmpVar(mb,i))
953 (void) snprintf(mb->var[i].id, IDLENGTH,"%c%c%d", REFMARKER, TMPMARKER,mb->vid++);
954
955#ifdef DEBUG_REDUCE
956 fprintf(stderr, "After reduction \n");
957 fprintFunction(stderr, mb, 0, 0);
958#endif
959 GDKfree(alias);
960 mb->vtop = cnt;
961}
962
963void
964trimMalVariables(MalBlkPtr mb, MalStkPtr stk)
965{
966 int i, j;
967 InstrPtr q;
968
969 /* reset the use bit for all non-signature arguments */
970 for (i = 0; i < mb->vtop; i++)
971 clrVarUsed(mb,i);
972 /* build the use table */
973 for (i = 0; i < mb->stop; i++) {
974 q = getInstrPtr(mb, i);
975
976 for (j = 0; j < q->argc; j++)
977 setVarUsed(mb,getArg(q,j));
978 }
979 trimMalVariables_(mb, stk);
980}
981
982/* MAL constants
983 * Constants are stored in the symbol table and referenced by a
984 * variable identifier. This means that per MAL instruction, we may
985 * end up with MAXARG entries in the symbol table. This may lead to
986 * long searches for variables. An optimization strategy deployed in
987 * the current implementation is to look around for a similar
988 * (constant) definition and to reuse its identifier. This avoids an
989 * exploding symbol table with a lot of temporary variables (as in
990 * tst400cHuge)
991 *
992 * But then the question becomes how far to search? Searching through
993 * all variables is only useful when the list remains short or when
994 * the constant-variable-name is easily derivable from its literal
995 * value and a hash-based index leads you quickly to it.
996 *
997 * For the time being, we use a MAL system parameter, MAL_VAR_WINDOW,
998 * to indicate the number of symbol table entries to consider. Setting
999 * it to >= MAXARG will at least capture repeated use of a constant
1000 * within a single function call or repeated use within a small block
1001 * of code.
1002 *
1003 * The final step is to prepare a GDK value record, from which the
1004 * internal representation can be obtained during MAL interpretation.
1005 *
1006 * The constant values are linked together to improve searching
1007 * them. This start of the constant list is kept in the MalBlk.
1008 *
1009 * Conversion of a constant to another type is limited to well-known
1010 * coercion rules. Errors are reported and the nil value is set. */
1011
1012/* Converts the constant in vr to the MAL type type. Conversion is
1013 * done in the vr struct. */
1014str
1015convertConstant(int type, ValPtr vr)
1016{
1017 if( type > GDKatomcnt )
1018 throw(SYNTAX, "convertConstant", "type index out of bound");
1019 if (vr->vtype == type)
1020 return MAL_SUCCEED;
1021 if (type == TYPE_bat || isaBatType(type)) {
1022 /* BAT variables can only be set to nil */
1023 VALclear(vr);
1024 vr->vtype = type;
1025 vr->val.bval = bat_nil;
1026 return MAL_SUCCEED;
1027 }
1028 if (type == TYPE_ptr) {
1029 /* all coercions should be avoided to protect against memory probing */
1030 if (vr->vtype == TYPE_void) {
1031 VALclear(vr);
1032 vr->vtype = type;
1033 vr->val.pval = 0;
1034 return MAL_SUCCEED;
1035 }
1036 if (vr->vtype != type)
1037 throw(SYNTAX, "convertConstant", "pointer conversion error");
1038 return MAL_SUCCEED;
1039 }
1040 if (type == TYPE_any) {
1041#ifndef DEBUG_MAL_INSTR
1042 assert(0);
1043#endif
1044 throw(SYNTAX, "convertConstant", "missing type");
1045 }
1046 if (VALconvert(type, vr) == NULL) {
1047 if (vr->vtype == TYPE_str)
1048 throw(SYNTAX, "convertConstant", "parse error in '%s'", vr->val.sval);
1049 throw(SYNTAX, "convertConstant", "coercion failed");
1050 }
1051 return MAL_SUCCEED;
1052}
1053
1054int
1055fndConstant(MalBlkPtr mb, const ValRecord *cst, int depth)
1056{
1057 int i, k;
1058 const void *p;
1059
1060 /* pointers never match */
1061 if (ATOMstorage(cst->vtype) == TYPE_ptr)
1062 return -1;
1063
1064 p = VALptr(cst);
1065 k = mb->vtop - depth;
1066 if (k < 0)
1067 k = 0;
1068 for (i=k; i < mb->vtop - 1; i++)
1069 if (getVar(mb,i) && isVarConstant(mb,i)){
1070 VarPtr v = getVar(mb, i);
1071 if (v && v->type == cst->vtype && ATOMcmp(cst->vtype, VALptr(&v->value), p) == 0)
1072 return i;
1073 }
1074 return -1;
1075}
1076
1077int
1078cpyConstant(MalBlkPtr mb, VarPtr vr)
1079{
1080 int i;
1081 ValRecord cst;
1082
1083 if (VALcopy(&cst, &vr->value) == NULL)
1084 return -1;
1085
1086 i = defConstant(mb, vr->type, &cst);
1087 return i;
1088}
1089
1090int
1091defConstant(MalBlkPtr mb, int type, ValPtr cst)
1092{
1093 int k;
1094 str msg;
1095
1096 if (isaBatType(type) && cst->vtype == TYPE_void) {
1097 cst->vtype = TYPE_bat;
1098 cst->val.bval = bat_nil;
1099 } else if (cst->vtype != type && !isaBatType(type) && !isPolyType(type)) {
1100 int otype = cst->vtype;
1101 assert(type != TYPE_any); /* help Coverity */
1102 msg = convertConstant(type, cst);
1103 if (msg) {
1104 str ft, tt;
1105
1106 /* free old value */
1107 ft = getTypeName(otype);
1108 tt = getTypeName(type);
1109 mb->errors = createMalException(mb, 0, TYPE, "constant coercion error from %s to %s", ft, tt);
1110 GDKfree(ft);
1111 GDKfree(tt);
1112 freeException(msg);
1113 } else {
1114 assert(cst->vtype == type);
1115 }
1116 }
1117 k = fndConstant(mb, cst, MAL_VAR_WINDOW);
1118 if (k >= 0) {
1119 /* protect against leaks coming from constant reuse */
1120 if (ATOMextern(type) && cst->val.pval)
1121 VALclear(cst);
1122 return k;
1123 }
1124 k = newTmpVariable(mb, type);
1125 setVarConstant(mb, k);
1126 setVarFixed(mb, k);
1127 if (type >= 0 && type < GDKatomcnt && ATOMextern(type))
1128 setVarCleanup(mb, k);
1129 else
1130 clrVarCleanup(mb, k);
1131 if(VALcopy( &getVarConstant(mb, k),cst) == NULL)
1132 return -1;
1133 if (ATOMextern(cst->vtype) && cst->val.pval)
1134 VALclear(cst);
1135 return k;
1136}
1137
1138/* Argument handling
1139 * The number of arguments for procedures is currently
1140 * limited. Furthermore, we should assure that no variable is
1141 * referenced before being assigned. Failure to obey should mark the
1142 * instruction as type-error. */
1143InstrPtr
1144pushArgument(MalBlkPtr mb, InstrPtr p, int varid)
1145{
1146 if (p == NULL)
1147 return NULL;
1148 if (varid < 0) {
1149 /* leave everything as is in this exceptional programming error */
1150 mb->errors = createMalException(mb, 0, TYPE,"improper variable id");
1151 return p;
1152 }
1153
1154 if (p->argc + 1 == p->maxarg) {
1155 int i = 0;
1156 int space = p->maxarg * sizeof(p->argv[0]) + offsetof(InstrRecord, argv);
1157 InstrPtr pn = (InstrPtr) GDKrealloc(p,space + MAXARG * sizeof(p->argv[0]));
1158
1159 if (pn == NULL) {
1160 /* In the exceptional case we can not allocate more space
1161 * then we show an exception, mark the block as erroneous
1162 * and leave the instruction as is.
1163 */
1164 mb->errors = createMalException(mb,0, TYPE, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1165 return p;
1166 }
1167 memset( ((char*)pn) + space, 0, MAXARG * sizeof(pn->argv[0]));
1168 pn->maxarg += MAXARG;
1169
1170 /* if the instruction is already stored in the MAL block
1171 * it should be replaced by an extended version.
1172 */
1173 if( p != pn)
1174 for (i = mb->stop - 1; i >= 0; i--)
1175 if (mb->stmt[i] == p) {
1176 mb->stmt[i] = pn;
1177 break;
1178 }
1179
1180 p = pn;
1181 /* we have to keep track on the maximal arguments/block
1182 * because it is needed by the interpreter */
1183 if (mb->maxarg < pn->maxarg)
1184 mb->maxarg = pn->maxarg;
1185 }
1186 /* protect against the case that the instruction is malloced
1187 * in isolation */
1188 if( mb->maxarg < p->maxarg)
1189 mb->maxarg= p->maxarg;
1190
1191 p->argv[p->argc++] = varid;
1192 return p;
1193}
1194
1195InstrPtr
1196setArgument(MalBlkPtr mb, InstrPtr p, int idx, int varid)
1197{
1198 int i;
1199
1200 if (p == NULL)
1201 return NULL;
1202 p = pushArgument(mb, p, varid); /* make space */
1203 if (p == NULL)
1204 return NULL;
1205 for (i = p->argc - 1; i > idx; i--)
1206 getArg(p, i) = getArg(p, i - 1);
1207 getArg(p, i) = varid;
1208 return p;
1209}
1210
1211InstrPtr
1212pushReturn(MalBlkPtr mb, InstrPtr p, int varid)
1213{
1214 if (p->retc == 1 && p->argv[0] == -1) {
1215 p->argv[0] = varid;
1216 return p;
1217 }
1218 if ((p = setArgument(mb, p, p->retc, varid)) == NULL)
1219 return NULL;
1220 p->retc++;
1221 return p;
1222}
1223
1224/* Store the information of a destination variable in the signature
1225 * structure of each instruction. This code is largely equivalent to
1226 * pushArgument, but it is more efficient in searching and collecting
1227 * the information.
1228 * TODO */
1229/* swallows name argument */
1230InstrPtr
1231pushArgumentId(MalBlkPtr mb, InstrPtr p, const char *name)
1232{
1233 int v;
1234
1235 if (p == NULL)
1236 return NULL;
1237 v = findVariable(mb, name);
1238 if (v < 0) {
1239 size_t namelen = strlen(name);
1240 if ((v = newVariable(mb, name, namelen, getAtomIndex(name, namelen, TYPE_any))) < 0) {
1241 freeInstruction(p);
1242 return NULL;
1243 }
1244 }
1245 return pushArgument(mb, p, v);
1246}
1247
1248/* The alternative is to remove arguments from an instruction
1249 * record. This is typically part of instruction constructions. */
1250void
1251delArgument(InstrPtr p, int idx)
1252{
1253 int i;
1254
1255 for (i = idx; i < p->argc - 1; i++)
1256 p->argv[i] = p->argv[i + 1];
1257 p->argc--;
1258 if (idx < p->retc)
1259 p->retc--;
1260}
1261
1262void
1263setArgType(MalBlkPtr mb, InstrPtr p, int i, int tpe)
1264{
1265 assert(p->argv[i] < mb->vsize);
1266 setVarType(mb,getArg(p, i),tpe);
1267}
1268
1269void
1270setReturnArgument(InstrPtr p, int i)
1271{
1272 setDestVar(p, i);
1273}
1274
1275malType
1276destinationType(MalBlkPtr mb, InstrPtr p)
1277{
1278 if (p->argc > 0)
1279 return getVarType(mb, getDestVar(p));
1280 return TYPE_any;
1281}
1282
1283/* For polymorphic instructions we should keep around the maximal
1284 * index to later allocate sufficient space for type resolutions maps.
1285 * Beware, that we should only consider the instruction polymorphic if
1286 * it has a positive index or belongs to the signature.
1287 * BATs can only have a polymorphic type at the tail.
1288 */
1289inline void
1290setPolymorphic(InstrPtr p, int tpe, int force)
1291{
1292 int c1 = 0, c2 = 0;
1293
1294 if (force == FALSE && tpe == TYPE_any)
1295 return;
1296 if (isaBatType(tpe))
1297 c1= TYPE_oid;
1298 if (getTypeIndex(tpe) > 0)
1299 c2 = getTypeIndex(tpe);
1300 else if (getBatType(tpe) == TYPE_any)
1301 c2 = 1;
1302 c1 = c1 > c2 ? c1 : c2;
1303 if (c1 > 0 && c1 >= p->polymorphic)
1304 p->polymorphic = c1 + 1;
1305}
1306
1307/* Instructions are simply appended to a MAL block. It should always succeed.
1308 * The assumption is to push it when you are completely done with its preparation.
1309 */
1310
1311void
1312pushInstruction(MalBlkPtr mb, InstrPtr p)
1313{
1314 int i;
1315 int extra;
1316 InstrPtr q;
1317
1318 if (p == NULL)
1319 return;
1320
1321 extra = mb->vsize - mb->vtop; // the extra variables already known
1322 if (mb->stop + 1 >= mb->ssize) {
1323 if( resizeMalBlk(mb, growBlk(mb->ssize) + extra) ){
1324 /* perhaps we can continue with a smaller increment.
1325 * But the block remains marked as faulty.
1326 */
1327 if( resizeMalBlk(mb,mb->ssize + 1)){
1328 /* we are now left with the situation that the new instruction is dangling .
1329 * The hack is to take an instruction out of the block that is likely not referenced independently
1330 * The last resort is to take the first, which should always be there
1331 * This assumes that no references are kept elsewhere to the statement
1332 */
1333 for( i = 1; i < mb->stop; i++){
1334 q= getInstrPtr(mb,i);
1335 if( q->token == REMsymbol){
1336 freeInstruction(q);
1337 mb->stmt[i] = p;
1338 return;
1339 }
1340 }
1341 freeInstruction(getInstrPtr(mb,0));
1342 mb->stmt[0] = p;
1343 return;
1344 }
1345 }
1346 }
1347 if (mb->stmt[mb->stop])
1348 freeInstruction(mb->stmt[mb->stop]);
1349 mb->stmt[mb->stop++] = p;
1350}
1351