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) M. Kersten
15 : * For documentation see website
16 : */
17 : #include "monetdb_config.h"
18 : #include "mal_function.h"
19 : #include "mal_resolve.h" /* for isPolymorphic() & chkProgram() */
20 : #include "mal_interpreter.h" /* for showErrors() */
21 : #include "mal_listing.h"
22 : #include "mal_namespace.h"
23 : #include "mal_private.h"
24 :
25 : Symbol
26 50561 : newFunctionArgs(const char *mod, const char *nme, int kind, int args)
27 : {
28 50561 : Symbol s;
29 50561 : InstrPtr p;
30 50561 : int varid;
31 :
32 50561 : if (mod == NULL || nme == NULL)
33 : return NULL;
34 :
35 50561 : s = newSymbol(nme, kind);
36 50558 : if (s == NULL)
37 : return NULL;
38 :
39 50558 : varid = newVariable(s->def, nme, strlen(nme), TYPE_any);
40 50561 : if (varid < 0) {
41 0 : freeSymbol(s);
42 0 : return NULL;
43 : }
44 :
45 50561 : if (args > 0) {
46 50543 : p = newInstructionArgs(NULL, mod, nme, args);
47 50542 : if (p == NULL) {
48 0 : freeSymbol(s);
49 0 : return NULL;
50 : }
51 50542 : p->token = kind;
52 50542 : p->barrier = 0;
53 50542 : setDestVar(p, varid);
54 50542 : pushInstruction(s->def, p);
55 50543 : if (s->def->errors) {
56 0 : freeSymbol(s);
57 0 : return NULL;
58 : }
59 : }
60 : return s;
61 : }
62 :
63 : Symbol
64 49302 : newFunction(const char *mod, const char *nme, int kind)
65 : {
66 49302 : return newFunctionArgs(mod, nme, kind, MAXARG);
67 : }
68 :
69 : /*
70 : * Optimizers may be interested in the function definition
71 : * for obtaining properties. Rather than polution of the
72 : * instruction record with a scope reference, we use a lookup function until it
73 : * becomes a performance hindrance.
74 : */
75 : Symbol
76 0 : getFunctionSymbol(Module scope, InstrPtr p)
77 : {
78 0 : Module m;
79 0 : Symbol s;
80 :
81 0 : for (m = findModule(scope, getModuleId(p)); m; m = m->link)
82 0 : if (idcmp(m->name, getModuleId(p)) == 0) {
83 0 : s = m->space[getSymbolIndex(getFunctionId(p))];
84 0 : for (; s; s = s->peer)
85 0 : if (getSignature(s)->fcn == p->fcn)
86 0 : return s;
87 : }
88 : return 0;
89 : }
90 :
91 : int
92 150401 : getPC(MalBlkPtr mb, InstrPtr p)
93 : {
94 150401 : int i;
95 1780672 : for (i = 0; i < mb->stop; i++)
96 1780672 : if (getInstrPtr(mb, i) == p)
97 150401 : return i;
98 : return -1;
99 : }
100 :
101 : /*
102 : * Checking the control flow structure is done by a single pass over the
103 : * MAL program after the program has been type-checked.
104 : * It should inspect all BARRIER and CATCH blocks for proper structure.
105 : * If the flow is correct and not dependent on an undefined typed instruction
106 : * we avoid doing this check any further.
107 : */
108 : #define DEPTH 128
109 :
110 : str
111 3091153 : chkFlow(MalBlkPtr mb)
112 : {
113 3091153 : int i, j, k, v, lastInstruction;
114 3091153 : int pc[DEPTH];
115 3091153 : int var[DEPTH];
116 3091153 : InstrPtr stmt[DEPTH];
117 3091153 : int btop = 0;
118 3091153 : int endseen = 0, retseen = 0;
119 3091153 : InstrPtr p, sig;
120 3091153 : str msg = MAL_SUCCEED;
121 :
122 3091153 : if (mb->errors != MAL_SUCCEED)
123 : return mb->errors;
124 3091117 : sig = getInstrPtr(mb, 0);
125 3091117 : lastInstruction = mb->stop - 1;
126 171811541 : for (i = 0; i < mb->stop; i++) {
127 168720437 : p = getInstrPtr(mb, i);
128 : /* we have to keep track on the maximal arguments/block
129 : because it is needed by the interpreter */
130 168720437 : switch (p->barrier) {
131 245183 : case BARRIERsymbol:
132 : case CATCHsymbol:
133 245183 : if (btop == DEPTH)
134 0 : throw(MAL, "chkFlow", "%s.%s Too many nested MAL blocks",
135 : getModuleId(sig), getFunctionId(sig));
136 245183 : pc[btop] = i;
137 245183 : v = var[btop] = getDestVar(p);
138 245183 : stmt[btop] = p;
139 :
140 246322 : for (j = btop - 1; j >= 0; j--)
141 1139 : if (v == var[j])
142 0 : throw(MAL, "chkFlow",
143 : "%s.%s recursive %s[%d] shields %s[%d]",
144 : getModuleId(sig), getFunctionId(sig), getVarName(mb,
145 : v),
146 0 : pc[j], getFcnName(mb), pc[i]);
147 :
148 245183 : btop++;
149 245183 : break;
150 245182 : case EXITsymbol:
151 245182 : v = getDestVar(p);
152 245182 : if (btop > 0 && var[btop - 1] != v)
153 3 : throw(MAL, "chkFlow",
154 : "%s.%s exit-label '%s' doesnot match '%s'",
155 : getModuleId(sig), getFunctionId(sig), getVarName(mb, v),
156 : getVarName(mb, var[btop - 1]));
157 245179 : if (btop == 0)
158 0 : throw(MAL, "chkFlow",
159 : "%s.%s exit-label '%s' without begin-label",
160 : getModuleId(sig), getFunctionId(sig), getVarName(mb, v));
161 : /* search the matching block */
162 245179 : for (j = btop - 1; j >= 0; j--)
163 245178 : if (var[j] == v)
164 : break;
165 245179 : if (j >= 0)
166 : btop = j;
167 : else
168 0 : btop--;
169 :
170 : /* retrofit LEAVE/REDO instructions */
171 245179 : stmt[btop]->jump = i;
172 15592467 : for (k = pc[btop]; k < i; k++) {
173 15347288 : InstrPtr p1 = getInstrPtr(mb, k);
174 15347288 : if (getDestVar(p1) == v) {
175 : /* handle assignments with leave/redo option */
176 246529 : if (p1->barrier == LEAVEsymbol)
177 126 : p1->jump = i;
178 246529 : if (p1->barrier == REDOsymbol)
179 1256 : p1->jump = pc[btop] + 1;
180 : }
181 : }
182 : break;
183 1396 : case LEAVEsymbol:
184 : case REDOsymbol:
185 1396 : v = getDestVar(p);
186 1405 : for (j = btop - 1; j >= 0; j--)
187 1397 : if (var[j] == v)
188 : break;
189 : if (j < 0) {
190 8 : str nme = getVarName(mb, v);
191 8 : throw(MAL, "chkFlow", "%s.%s label '%s' not in guarded block",
192 : getModuleId(sig), getFunctionId(sig), nme);
193 : }
194 : break;
195 3492 : case RETURNsymbol: {
196 3492 : InstrPtr ps = getInstrPtr(mb, 0);
197 3492 : int e;
198 3492 : if (ps->retc != p->retc) {
199 2 : throw(MAL, "chkFlow", "%s.%s invalid return target!",
200 : getModuleId(sig), getFunctionId(sig));
201 3490 : } else if (ps->typechk == TYPE_RESOLVED)
202 21951 : for (e = 0; e < p->retc; e++) {
203 18478 : if (resolvedType(getArgType(mb, ps, e), getArgType(mb, p, e)) < 0) {
204 0 : str tpname = getTypeName(getArgType(mb, p, e));
205 0 : msg = createException(MAL,
206 : "%s.%s RETURN type mismatch at type '%s'\n",
207 0 : getModuleId(p) ? getModuleId(p) :
208 : "",
209 0 : getFunctionId(p) ?
210 : getFunctionId(p) : "", tpname);
211 0 : GDKfree(tpname);
212 0 : return msg;
213 : }
214 : }
215 : //if (btop == 0)
216 : retseen = 1;
217 : break;
218 : }
219 : case RAISEsymbol:
220 : endseen = 1;
221 : break;
222 : case ENDsymbol:
223 : endseen = 1;
224 : break;
225 168224553 : default:
226 168224553 : if (isaSignature(p)) {
227 27879404 : if (p->token == REMsymbol) {
228 : /* do nothing */
229 3090989 : } else if (i) {
230 0 : str l = instruction2str(mb, 0, p, TRUE);
231 0 : msg = createException(MAL, "%s.%s signature misplaced\n!%s",
232 : getModuleId(p), getFunctionId(p), l);
233 0 : GDKfree(l);
234 0 : return msg;
235 : }
236 : }
237 : }
238 : }
239 :
240 3091104 : if (lastInstruction < mb->stop - 1)
241 0 : throw(MAL, "chkFlow", "%s.%s instructions after END", getModuleId(sig),
242 : getFunctionId(sig));
243 :
244 3091104 : if (endseen && btop > 0)
245 0 : throw(MAL, "chkFlow", "barrier '%s' without exit in %s[%d]",
246 0 : getVarName(mb, var[btop - 1]), getFcnName(mb), i);
247 3091104 : p = getInstrPtr(mb, 0);
248 3091104 : if (!isaSignature(p))
249 0 : throw(MAL, "chkFlow", "%s.%s signature missing", getModuleId(sig),
250 : getFunctionId(sig));
251 3091104 : if (retseen == 0) {
252 3088319 : if (getArgType(mb, p, 0) != TYPE_void && (p->token == FUNCTIONsymbol))
253 0 : throw(MAL, "chkFlow", "%s.%s RETURN missing", getModuleId(sig),
254 : getFunctionId(sig));
255 : }
256 : return MAL_SUCCEED;
257 : }
258 :
259 : /*
260 : * A code may contain temporary names for marking barrier blocks.
261 : * Since they are introduced by the compiler, the parser should locate
262 : * them itself when encountering the LEAVE,EXIT,REDO.
263 : * The starting position is mostly the last statement entered.
264 : * Purposely, the nameless envelops searches the name of the last
265 : * unclosed block. All others are ignored.
266 : */
267 : int
268 4 : getBarrierEnvelop(MalBlkPtr mb)
269 : {
270 4 : int pc;
271 4 : InstrPtr p;
272 10 : for (pc = mb->stop - 2; pc >= 0; pc--) {
273 9 : p = getInstrPtr(mb, pc);
274 9 : if (blockExit(p)) {
275 1 : int l = p->argv[0];
276 4 : for (; pc >= 0; pc--) {
277 4 : p = getInstrPtr(mb, pc);
278 4 : if (blockStart(p) && p->argv[0] == l)
279 : break;
280 : }
281 1 : continue;
282 : }
283 8 : if (blockStart(p))
284 3 : return p->argv[0];
285 : }
286 1 : return newTmpVariable(mb, TYPE_any);
287 : }
288 :
289 : static void
290 16 : replaceTypeVar(MalBlkPtr mb, InstrPtr p, int v, malType t)
291 : {
292 16 : int j, i, x, y;
293 :
294 89 : for (j = 0; j < mb->stop; j++) {
295 73 : p = getInstrPtr(mb, j);
296 73 : if (p->polymorphic)
297 100 : for (i = 0; i < p->argc; i++)
298 72 : if (isPolymorphic(x = getArgType(mb, p, i))) {
299 64 : if (isaBatType(x)) {
300 20 : int tail;
301 20 : int tx;
302 20 : tail = getBatType(x);
303 20 : tx = getTypeIndex(x);
304 20 : if (v && tx == v && tail == TYPE_any) {
305 20 : tx = 0;
306 20 : tail = t;
307 : }
308 20 : y = newBatType(tail);
309 20 : setTypeIndex(y, tx);
310 20 : setArgType(mb, p, i, y);
311 24 : } else if (getTypeIndex(x) == v) {
312 17 : setArgType(mb, p, i, t);
313 : } else {
314 72 : }
315 : }
316 : }
317 16 : }
318 :
319 : /* insert a symbol into the symbol table just before the symbol
320 : * "before". */
321 : static void
322 18 : insertSymbolBefore(Module scope, Symbol prg, Symbol before)
323 : {
324 18 : InstrPtr sig;
325 18 : int t;
326 18 : Symbol s;
327 :
328 18 : assert(strcmp(prg->name, before->name) == 0);
329 18 : sig = getSignature(prg);
330 18 : if (getModuleId(sig) && getModuleId(sig) != scope->name) {
331 0 : Module c = findModule(scope, getModuleId(sig));
332 0 : if (c)
333 18 : scope = c;
334 : }
335 18 : t = getSymbolIndex(getFunctionId(sig));
336 18 : assert(scope->space != NULL);
337 18 : assert(scope->space[t] != NULL);
338 18 : s = scope->space[t];
339 18 : prg->skip = before->skip;
340 18 : prg->peer = before;
341 18 : if (s == before) {
342 16 : scope->space[t] = prg;
343 : } else {
344 3 : for (;;) {
345 3 : assert(s != NULL);
346 3 : if (s->skip == before) {
347 0 : s->skip = prg;
348 : }
349 3 : if (s->peer == before) {
350 2 : s->peer = prg;
351 2 : break;
352 : }
353 : s = s->peer;
354 : }
355 : }
356 18 : }
357 :
358 : /*
359 : * Upon cloning a function we should remove all the polymorphic flags.
360 : * Otherwise we may end up with a recursive clone.
361 : */
362 : Symbol
363 18 : cloneFunction(Module scope, Symbol proc, MalBlkPtr mb, InstrPtr p)
364 : {
365 18 : Symbol new;
366 18 : int i, v;
367 18 : InstrPtr pp;
368 18 : str msg = MAL_SUCCEED;
369 :
370 18 : new = newFunctionArgs(scope->name, proc->name, getSignature(proc)->token,
371 : -1);
372 18 : if (new == NULL) {
373 : return NULL;
374 : }
375 18 : freeMalBlk(new->def);
376 18 : if ((new->def = copyMalBlk(proc->def)) == NULL) {
377 0 : freeSymbol(new);
378 0 : return NULL;
379 : }
380 : /* now change the definition of the original proc */
381 : /* check for errors after fixation , TODO */
382 18 : pp = getSignature(new);
383 63 : for (i = 0; i < pp->argc; i++)
384 45 : if (isPolymorphic(v = getArgType(new->def, pp, i))) {
385 15 : int t = getArgType(mb, p, i);
386 :
387 15 : if (v == TYPE_any)
388 1 : replaceTypeVar(new->def, pp, v, t);
389 15 : if (isaBatType(v)) {
390 14 : if (getTypeIndex(v))
391 14 : replaceTypeVar(new->def, pp, getTypeIndex(v),
392 : getBatType(t));
393 : } else
394 1 : replaceTypeVar(new->def, pp, getTypeIndex(v), t);
395 : }
396 : /* include the function at the proper place in the scope */
397 18 : insertSymbolBefore(scope, new, proc);
398 : /* clear polymorphic and type to force analysis */
399 172 : for (i = 0; i < new->def->stop; i++) {
400 136 : pp = getInstrPtr(new->def, i);
401 136 : pp->typechk = TYPE_UNKNOWN;
402 136 : pp->polymorphic = 0;
403 : }
404 : /* clear type fixations */
405 202 : for (i = 0; i < new->def->vtop; i++)
406 184 : clrVarFixed(new->def, i);
407 :
408 :
409 : /* check for errors after fixation , TODO */
410 : /* beware, we should now ignore any cloning */
411 18 : if (proc->def->errors == 0) {
412 18 : msg = chkProgram(scope, new->def);
413 18 : if (msg)
414 3 : mb->errors = msg;
415 15 : else if (new->def->errors) {
416 0 : assert(mb->errors == NULL);
417 0 : mb->errors = new->def->errors;
418 0 : mb->errors = createMalException(mb, 0, TYPE, "Error in cloned function");
419 0 : new->def->errors = 0;
420 : }
421 : }
422 :
423 : return new;
424 : }
425 :
426 : /*
427 : * For commands we do not have to clone the routine. We merely have to
428 : * assure that the type-constraints are obeyed. The resulting type
429 : * is returned.
430 : */
431 : void
432 0 : debugFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg, int first,
433 : int step)
434 : {
435 0 : int i, j;
436 0 : str ps;
437 0 : InstrPtr p;
438 :
439 0 : if (mb == NULL) {
440 0 : mnstr_printf(fd, "# function definition missing\n");
441 0 : return;
442 : }
443 0 : if (flg == 0 || step < 0 || first < 0)
444 : return;
445 :
446 0 : if (mb->errors)
447 0 : mnstr_printf(fd, "#errors seen: %s\n", mb->errors);
448 0 : for (i = first; i < first + step && i < mb->stop; i++) {
449 0 : ps = instruction2str(mb, stk, (p = getInstrPtr(mb, i)), flg);
450 0 : if (ps) {
451 0 : if (p->token == REMsymbol)
452 0 : mnstr_printf(fd, "%-40s\n", ps);
453 : else {
454 0 : mnstr_printf(fd, "%-40s\t#[%d] %s ", ps, i,
455 0 : (p->blk ? p->blk->binding : ""));
456 0 : if (flg & LIST_MAL_FLOW) {
457 0 : for (j = 0; j < p->retc; j++)
458 0 : mnstr_printf(fd, "%d ", getArg(p, j));
459 0 : if (p->argc - p->retc > 0)
460 0 : mnstr_printf(fd, "<- ");
461 0 : for (; j < p->argc; j++)
462 0 : mnstr_printf(fd, "%d ", getArg(p, j));
463 : }
464 0 : mnstr_printf(fd, "\n");
465 : }
466 0 : GDKfree(ps);
467 : } else
468 0 : mnstr_printf(fd, "#failed instruction2str()\n");
469 : }
470 : }
471 :
472 : void
473 67 : listFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg, int first,
474 : int size)
475 : {
476 67 : int i;
477 67 : int sample = 256;
478 :
479 67 : if (mb == NULL) {
480 0 : mnstr_printf(fd, "# function definition missing\n");
481 0 : return;
482 : }
483 67 : if (flg == 0)
484 : return;
485 :
486 67 : assert(size >= 0);
487 67 : assert(first >= 0 && first < mb->stop);
488 67 : renameVariables(mb);
489 67 : if (flg & LIST_MAL_MAPI) {
490 63 : size_t len = 0;
491 63 : str ps;
492 63 : mnstr_printf(fd, "&1 0 %d 1 %d\n", /* type id rows columns tuples */
493 : mb->stop, mb->stop);
494 63 : mnstr_printf(fd, "%% .explain # table_name\n");
495 63 : mnstr_printf(fd, "%% mal # name\n");
496 63 : mnstr_printf(fd, "%% clob # type\n");
497 3028 : for (i = first; i < first + size && i < mb->stop && sample-- > 0; i++) {
498 2902 : ps = instruction2str(mb, stk, getInstrPtr(mb, i), flg);
499 2902 : if (ps) {
500 2902 : size_t l = strlen(ps);
501 2902 : if (l > len)
502 : len = l;
503 2902 : GDKfree(ps);
504 : } else
505 0 : mnstr_printf(fd, "#failed instruction2str()\n");
506 : }
507 63 : mnstr_printf(fd, "%% %zu # length\n", len);
508 : }
509 3009 : for (i = first; i < first + size && i < mb->stop; i++)
510 2942 : printInstruction(fd, mb, stk, getInstrPtr(mb, i), flg);
511 : }
512 :
513 :
514 : void
515 129 : renameVariables(MalBlkPtr mb)
516 : {
517 129 : int i;
518 129 : char *s;
519 :
520 : /* Temporary variables get their name from the position in the symbol table */
521 : /* However, also MAL input may contain temporary names. At some point you need to clean it up to avoid clashes */
522 : /* Certainly when you are about to print the MAL function */
523 : /* During optimization they may be copied around, which means there name should be re-establised */
524 : /* rename all temporaries for ease of variable table interpretation */
525 : /* this code should not be necessary is variables always keep their position */
526 12159 : for (i = 0; i < mb->vtop; i++) {
527 12030 : s = getVarName(mb, i);
528 12030 : if (s[1] == '_' && (*s == 'C' || *s == 'X'))
529 11890 : snprintf(s + 2, IDLENGTH - 2, "%d", i);
530 : }
531 129 : }
532 :
533 : void
534 67 : printFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg)
535 : {
536 67 : int i, j;
537 67 : InstrPtr p;
538 :
539 :
540 : // Set the used bits properly
541 6373 : for (i = 0; i < mb->vtop; i++)
542 6306 : clrVarUsed(mb, i);
543 :
544 :
545 3009 : for (i = 0; i < mb->stop; i++) {
546 2942 : p = getInstrPtr(mb, i);
547 9496 : for (j = p->retc; j < p->argc; j++)
548 6554 : setVarUsed(mb, getArg(p, j));
549 2942 : if (p->barrier)
550 24 : for (j = 0; j < p->retc; j++)
551 12 : setVarUsed(mb, getArg(p, j));
552 : }
553 67 : listFunction(fd, mb, stk, flg, 0, mb->stop);
554 67 : }
555 :
556 : void
557 0 : traceFunction(component_t comp, MalBlkPtr mb, MalStkPtr stk, int flg)
558 : {
559 0 : int i, j;
560 0 : InstrPtr p;
561 : // Set the used bits properly
562 0 : for (i = 0; i < mb->vtop; i++)
563 0 : clrVarUsed(mb, i);
564 0 : for (i = 0; i < mb->stop; i++) {
565 0 : p = getInstrPtr(mb, i);
566 0 : for (j = p->retc; j < p->argc; j++)
567 0 : setVarUsed(mb, getArg(p, j));
568 0 : if (p->barrier)
569 0 : for (j = 0; j < p->retc; j++)
570 0 : setVarUsed(mb, getArg(p, j));
571 : }
572 0 : for (i = 0; i < mb->stop; i++)
573 0 : traceInstruction(comp, mb, stk, getInstrPtr(mb, i), flg);
574 0 : }
575 :
576 : /* initialize the static scope boundaries for all variables */
577 : void
578 2516441 : setVariableScope(MalBlkPtr mb)
579 : {
580 2516441 : int pc, k, depth = 0, dflow = -1;
581 2516441 : InstrPtr p;
582 :
583 : /* reset the scope admin */
584 248465069 : for (k = 0; k < mb->vtop; k++)
585 245948628 : if (isVarConstant(mb, k)) {
586 74511440 : setVarScope(mb, k, 0);
587 74511440 : setVarDeclared(mb, k, 0);
588 74511440 : setVarUpdated(mb, k, 0);
589 74511440 : setVarEolife(mb, k, mb->stop);
590 : } else {
591 171437188 : setVarScope(mb, k, 0);
592 171437188 : setVarDeclared(mb, k, 0);
593 171437188 : setVarUpdated(mb, k, 0);
594 171437188 : setVarEolife(mb, k, 0);
595 : }
596 :
597 125719644 : for (pc = 0; pc < mb->stop; pc++) {
598 123203203 : p = getInstrPtr(mb, pc);
599 :
600 123203203 : if (blockStart(p)) {
601 433589 : if (getModuleId(p) && getFunctionId(p)
602 429752 : && strcmp(getModuleId(p), "language") == 0
603 428153 : && strcmp(getFunctionId(p), "dataflow") == 0) {
604 428148 : if (dflow != -1)
605 0 : addMalException(mb,
606 : "setLifeSpan nested dataflow blocks not allowed");
607 : dflow = depth;
608 : } else
609 5441 : depth++;
610 : }
611 :
612 519325868 : for (k = 0; k < p->argc; k++) {
613 396122665 : int v = getArg(p, k);
614 396122665 : if (isVarConstant(mb, v) && getVarUpdated(mb, v) == 0)
615 67218527 : setVarUpdated(mb, v, pc);
616 :
617 396122665 : if (getVarDeclared(mb, v) == 0) {
618 191207196 : setVarDeclared(mb, v, pc);
619 191207196 : setVarScope(mb, v, depth);
620 : }
621 396122665 : if (k < p->retc)
622 124520837 : setVarUpdated(mb, v, pc);
623 396122665 : if (getVarScope(mb, v) == depth)
624 396076331 : setVarEolife(mb, v, pc);
625 :
626 396122665 : if (k >= p->retc && getVarScope(mb, v) < depth)
627 26219 : setVarEolife(mb, v, -1);
628 : }
629 : /*
630 : * At a block exit we can finalize all variables defined within that block.
631 : * This does not hold for dataflow blocks. They merely direct the execution
632 : * thread, not the syntactic scope.
633 : */
634 123203203 : if (blockExit(p)) {
635 109881266 : for (k = 0; k < mb->vtop; k++)
636 109447674 : if (getVarEolife(mb, k) == 0 && getVarScope(mb, k) == depth)
637 38108636 : setVarEolife(mb, k, pc);
638 71339038 : else if (getVarEolife(mb, k) == -1)
639 9850 : setVarEolife(mb, k, pc);
640 :
641 433592 : if (dflow == depth)
642 : dflow = -1;
643 : else
644 5437 : depth--;
645 : }
646 123203203 : if (blockReturn(p)) {
647 26427 : for (k = 0; k < p->argc; k++)
648 23948 : setVarEolife(mb, getArg(p, k), pc);
649 : }
650 : }
651 248605965 : for (k = 0; k < mb->vtop; k++)
652 246089524 : if (getVarEolife(mb, k) == 0)
653 22183000 : setVarEolife(mb, k, mb->stop - 1);
654 2516441 : }
655 :
656 : int
657 0 : isLoopBarrier(MalBlkPtr mb, int pc)
658 : {
659 0 : InstrPtr p;
660 0 : int varid;
661 0 : p = getInstrPtr(mb, pc);
662 0 : if (p->barrier != BARRIERsymbol)
663 : return 0;
664 0 : varid = getDestVar(p);
665 0 : for (pc++; pc < mb->stop; pc++) {
666 0 : p = getInstrPtr(mb, pc);
667 0 : if (p->barrier == REDOsymbol && getDestVar(p) == varid)
668 : return 1;
669 0 : if (p->barrier == EXITsymbol && getDestVar(p) == varid)
670 : break;
671 : }
672 : return 0;
673 : }
674 :
675 : int
676 0 : getBlockBegin(MalBlkPtr mb, int pc)
677 : {
678 0 : InstrPtr p;
679 0 : int varid = 0, i;
680 :
681 0 : for (i = pc; i < mb->stop; i++) {
682 0 : p = getInstrPtr(mb, i);
683 0 : if (p->barrier == EXITsymbol) {
684 0 : varid = getDestVar(p);
685 0 : break;
686 : }
687 : }
688 0 : if (i == mb->stop)
689 : return 0;
690 :
691 0 : for (; pc > 0; pc--) {
692 0 : p = getInstrPtr(mb, pc);
693 0 : if ((p->barrier == BARRIERsymbol || p->barrier == CATCHsymbol) &&
694 0 : getDestVar(p) == varid)
695 0 : return pc;
696 : }
697 : return 0;
698 : }
699 :
700 : int
701 0 : getBlockExit(MalBlkPtr mb, int pc)
702 : {
703 0 : InstrPtr p;
704 0 : int varid;
705 0 : p = getInstrPtr(mb, pc);
706 0 : if (p->barrier != BARRIERsymbol && p->barrier != CATCHsymbol)
707 : return 0;
708 0 : varid = getDestVar(p);
709 0 : for (pc++; pc < mb->stop; pc++) {
710 0 : p = getInstrPtr(mb, pc);
711 0 : if (p->barrier == EXITsymbol && getDestVar(p) == varid)
712 0 : return pc;
713 : }
714 : return 0;
715 : }
716 :
717 : /*
718 : * Variable declaration
719 : * Variables are implicitly declared upon first use.
720 : * This feature may become a source of runtime errors and
721 : * complicates the analyse during optimization.
722 : * Therefore, in line with the flow of control check,
723 : * we make sure that all variables are properly initialized
724 : * before being used. Since barrier blocks may be skipped at
725 : * runtime, they actually introduce a separate scope.
726 : * Variables declared within a block may not be used outside it.
727 : * Variables can only be declared once.
728 : *
729 : * In many situation chkFlow and chkDeclarations should be called
730 : * together. Moreover, an erroneous chkFlow most likely implies
731 : * errors in the declarations as well.
732 : *
733 : * Since in interactive mode each statement is handled separately,
734 : * we have to remember the scope assigned to a variable.
735 : */
736 : void
737 0 : clrDeclarations(MalBlkPtr mb)
738 : {
739 0 : int i;
740 0 : for (i = 0; i < mb->vtop; i++) {
741 0 : clrVarInit(mb, i);
742 0 : clrVarUsed(mb, i);
743 0 : clrVarDisabled(mb, i);
744 : }
745 0 : }
746 :
747 : str
748 3090901 : chkDeclarations(MalBlkPtr mb)
749 : {
750 3090901 : int pc, i, k, l;
751 3090901 : InstrPtr p, sig;
752 3090901 : short blks[MAXDEPTH], top = 0, blkId = 1;
753 3090901 : int dflow = -1;
754 3090901 : str msg = MAL_SUCCEED;
755 :
756 3090901 : if (mb->errors)
757 0 : return GDKstrdup(mb->errors);
758 3090901 : blks[top] = blkId;
759 :
760 : /* initialize the scope */
761 332677171 : for (i = 0; i < mb->vtop; i++)
762 329586270 : setVarScope(mb, i, 0);
763 :
764 : /* all signature variables are declared at outer level */
765 3090901 : sig = getInstrPtr(mb, 0);
766 6203033 : for (k = 0; k < sig->argc; k++)
767 3112132 : setVarScope(mb, getArg(sig, k), blkId);
768 :
769 168596645 : for (pc = 1; pc < mb->stop; pc++) {
770 165505749 : p = getInstrPtr(mb, pc);
771 165505749 : if (p->token == REMsymbol)
772 24788230 : continue;
773 : /* check correct use of the arguments */
774 493390655 : for (k = p->retc; k < p->argc; k++) {
775 352673141 : l = getArg(p, k);
776 352673141 : if (l < 0)
777 0 : throw(MAL, "chkFlow",
778 : "%s.%s Non-declared variable: pc=%d, var= %d",
779 : getModuleId(sig), getFunctionId(sig), pc, k);
780 352673141 : setVarUsed(mb, l);
781 352673141 : if (getVarScope(mb, l) == 0) {
782 : /*
783 : * The problem created here is that only variables are
784 : * recognized that are declared through instructions.
785 : * For interactive code, and code that is based on a global
786 : * stack this is insufficient. In those cases, the variable
787 : * can be defined in a previous execution.
788 : * We have to recognize if the declaration takes place
789 : * in the context of a global stack.
790 : */
791 151589983 : if (p->barrier == CATCHsymbol) {
792 0 : setVarScope(mb, l, blks[0]);
793 151589983 : } else if (!(isVarConstant(mb, l) || isVarTypedef(mb, l))
794 11340 : && !isVarInit(mb, l)) {
795 5 : throw(MAL, "chkFlow",
796 : "%s.%s '%s' may not be used before being initialized",
797 : getModuleId(sig), getFunctionId(sig), getVarName(mb,
798 : l));
799 : }
800 201083158 : } else if (!isVarInit(mb, l)) {
801 : /* is the block still active ? */
802 8397 : for (i = 0; i <= top; i++)
803 8397 : if (blks[i] == getVarScope(mb, l))
804 : break;
805 8397 : if (i > top || blks[i] != getVarScope(mb, l))
806 0 : throw(MAL, "chkFlow", "%s.%s '%s' used outside scope",
807 : getModuleId(sig), getFunctionId(sig), getVarName(mb,
808 : l));
809 : }
810 352673136 : if (blockCntrl(p) || blockStart(p))
811 207532 : setVarInit(mb, l);
812 : }
813 : /* define variables */
814 286948751 : for (k = 0; k < p->retc; k++) {
815 146231237 : l = getArg(p, k);
816 146231237 : if (isVarInit(mb, l) && getVarScope(mb, l) == 0) {
817 : /* first time we see this variable and it is already
818 : * initialized: assume it exists globally */
819 112030173 : setVarScope(mb, l, blks[0]);
820 : }
821 146231237 : setVarInit(mb, l);
822 146231237 : if (getVarScope(mb, l) == 0) {
823 : /* variable has not been defined yet */
824 : /* exceptions are always declared at level 1 */
825 33904960 : if (p->barrier == CATCHsymbol)
826 199 : setVarScope(mb, l, blks[0]);
827 : else
828 33904761 : setVarScope(mb, l, blks[top]);
829 : }
830 146231237 : if (blockCntrl(p) || blockStart(p))
831 267210 : setVarUsed(mb, l);
832 : }
833 140717514 : if (p->barrier && msg == MAL_SUCCEED) {
834 495826 : if (blockStart(p)) {
835 245165 : if (top == MAXDEPTH - 2)
836 0 : throw(MAL, "chkFlow",
837 : "%s.%s too deeply nested MAL program",
838 : getModuleId(sig), getFunctionId(sig));
839 245165 : blkId++;
840 245165 : if (getModuleId(p) && getFunctionId(p)
841 239723 : && strcmp(getModuleId(p), "language") == 0
842 238591 : && strcmp(getFunctionId(p), "dataflow") == 0) {
843 238590 : if (dflow != -1)
844 0 : throw(MAL, "chkFlow",
845 : "%s.%s setLifeSpan nested dataflow blocks not allowed",
846 : getModuleId(sig), getFunctionId(sig));
847 238590 : dflow = blkId;
848 : }
849 245165 : blks[++top] = blkId;
850 : }
851 495826 : if (blockExit(p) && top > 0) {
852 245167 : if (dflow == blkId) {
853 : dflow = -1;
854 : } else
855 : /*
856 : * At the end of the block we should reset the status of all variables
857 : * defined within the block. For, the block could have been skipped
858 : * leading to uninitialized variables.
859 : */
860 4121849 : for (l = 0; l < mb->vtop; l++)
861 4115271 : if (getVarScope(mb, l) == blks[top]) {
862 56435 : setVarScope(mb, l, 0);
863 56435 : clrVarInit(mb, l);
864 : }
865 245167 : top--;
866 : }
867 : }
868 : }
869 : return msg;
870 : }
|