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 | |
19 | void |
20 | addMalException(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 | |
40 | Symbol |
41 | newSymbol(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 | |
65 | void |
66 | freeSymbol(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 | |
77 | void |
78 | freeSymbolList(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 | |
90 | int |
91 | newMalBlkStmt(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 | |
104 | MalBlkPtr |
105 | newMalBlk(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 */ |
153 | static 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 | |
165 | int |
166 | resizeMalBlk(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 */ |
201 | void |
202 | resetMalBlk(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 | |
212 | void |
213 | resetMalBlkAndFreeInstructions(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. */ |
226 | void |
227 | freeMalBlk(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. */ |
262 | MalBlkPtr |
263 | copyMalBlk(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 | |
353 | void |
354 | addtoMalBlkHistory(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 | |
372 | MalBlkPtr |
373 | getMalBlkHistory(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 |
383 | MalBlkPtr |
384 | getMalBlkOptimized(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 | */ |
423 | int |
424 | prepareMalBlk(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 | */ |
446 | InstrPtr |
447 | newInstructionArgs(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 | } |
476 | InstrPtr |
477 | newInstruction(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. */ |
507 | InstrPtr |
508 | copyInstruction(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 | |
517 | void |
518 | clrFunction(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 | |
528 | void |
529 | clrInstruction(InstrPtr p) |
530 | { |
531 | clrFunction(p); |
532 | memset((char *) p, 0, offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0])); |
533 | } |
534 | |
535 | void |
536 | freeInstruction(InstrPtr p) |
537 | { |
538 | GDKfree(p); |
539 | } |
540 | |
541 | /* Moving instructions around calls for care, because all dependent |
542 | * information should also be updated. */ |
543 | void |
544 | oldmoveInstruction(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. */ |
559 | void |
560 | removeInstruction(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 | |
581 | void |
582 | removeInstructionBlock(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 | |
601 | void |
602 | moveInstruction(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 | |
624 | int |
625 | findVariable(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. */ |
641 | int |
642 | findVariableLength(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. */ |
657 | malType |
658 | getType(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 | |
668 | str |
669 | getArgDefault(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 |
690 | int |
691 | isReserved(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. */ |
761 | static int |
762 | makeVarSpace(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*/ |
783 | int |
784 | newVariable(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. */ |
814 | int |
815 | cloneVariable(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 | |
840 | int |
841 | newTmpVariable(MalBlkPtr mb, malType type) |
842 | { |
843 | return newVariable(mb,0,0,type); |
844 | } |
845 | |
846 | int |
847 | newTypeVariable(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 | |
861 | void |
862 | clearVariable(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 | |
882 | void |
883 | freeVariable(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 | */ |
892 | void |
893 | trimMalVariables_(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 | |
963 | void |
964 | trimMalVariables(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. */ |
1014 | str |
1015 | convertConstant(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 | |
1054 | int |
1055 | fndConstant(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 | |
1077 | int |
1078 | cpyConstant(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 | |
1090 | int |
1091 | defConstant(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. */ |
1143 | InstrPtr |
1144 | pushArgument(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 | |
1195 | InstrPtr |
1196 | setArgument(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 | |
1211 | InstrPtr |
1212 | pushReturn(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 */ |
1230 | InstrPtr |
1231 | pushArgumentId(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. */ |
1250 | void |
1251 | delArgument(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 | |
1262 | void |
1263 | setArgType(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 | |
1269 | void |
1270 | setReturnArgument(InstrPtr p, int i) |
1271 | { |
1272 | setDestVar(p, i); |
1273 | } |
1274 | |
1275 | malType |
1276 | destinationType(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 | */ |
1289 | inline void |
1290 | setPolymorphic(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 | |
1311 | void |
1312 | pushInstruction(MalBlkPtr mb, InstrPtr p) |
1313 | { |
1314 | int i; |
1315 | int ; |
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 | |