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 : * The first attempt of the multiplex optimizer is to locate
15 : * a properly typed multi-plexed implementation.
16 : * The policy is to search for bat<mod>.<fcn> before going
17 : * into the iterator code generation.
18 : */
19 : #include "monetdb_config.h"
20 : #include "opt_remap.h"
21 : #include "opt_inline.h"
22 : #include "opt_multiplex.h"
23 :
24 : static int
25 146704 : OPTremapDirect(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int idx,
26 : Module scope)
27 : {
28 146704 : str mod, fcn;
29 146704 : char buf[1024];
30 146704 : int i, retc = pci->retc;
31 146704 : InstrPtr p;
32 146704 : const char *bufName, *fcnName;
33 :
34 146704 : (void) cntxt;
35 146704 : (void) stk;
36 146704 : int plus_one = getArgType(mb, pci, pci->retc) == TYPE_lng ? 1 : 0;
37 146704 : mod = VALget(&getVar(mb, getArg(pci, retc + 0 + plus_one))->value);
38 146704 : fcn = VALget(&getVar(mb, getArg(pci, retc + 1 + plus_one))->value);
39 :
40 146704 : if (strncmp(mod, "bat", 3) == 0)
41 0 : mod += 3;
42 :
43 :
44 146704 : snprintf(buf, 1024, "bat%s", mod);
45 146704 : bufName = putName(buf);
46 146704 : fcnName = putName(fcn);
47 146704 : if (bufName == NULL || fcnName == NULL)
48 : return 0;
49 :
50 146704 : p = newInstructionArgs(mb, bufName, fcnName, pci->argc + 2);
51 146704 : if (p == NULL)
52 : return 0;
53 :
54 293450 : for (i = 0; i < pci->retc; i++)
55 146746 : if (i < 1)
56 146704 : getArg(p, i) = getArg(pci, i);
57 : else
58 42 : p = pushReturn(mb, p, getArg(pci, i));
59 146704 : p->retc = p->argc = pci->retc;
60 :
61 :
62 :
63 146704 : if (plus_one) {
64 101 : p = pushArgument(mb, p, getArg(pci, pci->retc)); // cardinality argument
65 : }
66 :
67 470015 : for (i = pci->retc + 2 + plus_one; i < pci->argc; i++)
68 323311 : p = pushArgument(mb, p, getArg(pci, i));
69 146704 : if (p->retc == 1 &&
70 146696 : ((bufName == batcalcRef
71 126485 : && (fcnName == mulRef
72 113300 : || fcnName == divRef
73 112018 : || fcnName == plusRef
74 78700 : || fcnName == minusRef
75 66880 : || fcnName == modRef))
76 86989 : || bufName == batmtimeRef
77 86571 : || bufName == batstrRef)) {
78 62993 : if (p->argc == 3 &&
79 : /* these two filter out unary batcalc.- with a candidate list */
80 44329 : getBatType(getArgType(mb, p, 1)) != TYPE_oid
81 44329 : && (getBatType(getArgType(mb, p, 2)) != TYPE_oid
82 44195 : && !(isVarConstant(mb, getArg(p, 2))
83 : && getArgType(mb, p, 2) == TYPE_bat))) {
84 : /* add candidate lists */
85 44186 : if (isaBatType(getArgType(mb, p, 1)))
86 43965 : p = pushNil(mb, p, TYPE_bat);
87 44186 : if (isaBatType(getArgType(mb, p, 2)))
88 24683 : p = pushNil(mb, p, TYPE_bat);
89 : }
90 : }
91 :
92 : /* now see if we can resolve the instruction */
93 146704 : typeChecker(scope, mb, p, idx, TRUE);
94 146704 : if (p->typechk == TYPE_UNKNOWN) {
95 746 : freeInstruction(p);
96 746 : return 0;
97 : }
98 145958 : pushInstruction(mb, p);
99 145958 : return 1;
100 : }
101 :
102 : /*
103 : * Multiplex inline functions should be done with care.
104 : * The approach taken is to make a temporary copy of the function to be inlined.
105 : * To change all the statements to reflect the new situation
106 : * and, if no error occurs, replaces the target instruction
107 : * with this new block.
108 : *
109 : * By the time we get here, we know that function is
110 : * side-effect free.
111 : *
112 : * The multiplex upgrade is targeted at all function
113 : * arguments whose actual is a BAT and its formal
114 : * is a scalar.
115 : * This seems sufficient for the SQL generated PSM code,
116 : * but does in general not hold.
117 : * For example,
118 : *
119 : * function foo(b:int,c:bat[:oid,:int])
120 : * ... d:= batcalc.+(b,c)
121 : * and
122 : * multiplex("user","foo",ba:bat[:oid,:int],ca:bat[:oid,:int])
123 : * upgrades the first argument. The naive upgrade of
124 : * the statement that would fail. The code below catches
125 : * most of them by simple prepending "bat" to the MAL function
126 : * name and leave it to the type resolver to generate the
127 : * error.
128 : *
129 : * The process terminates as soon as we
130 : * find an instruction that does not have a multiplex
131 : * counterpart.
132 : */
133 : static int
134 227 : OPTmultiplexInline(Client cntxt, MalBlkPtr mb, InstrPtr p, int pc)
135 : {
136 227 : MalBlkPtr mq;
137 227 : InstrPtr q = NULL, sig;
138 227 : char buf[1024];
139 227 : int i, j, k, m;
140 227 : int refbat = 0, retc = p->retc;
141 227 : bit *upgrade;
142 227 : str msg;
143 :
144 :
145 227 : str mod = VALget(&getVar(mb, getArg(p, retc + 0))->value);
146 227 : str fcn = VALget(&getVar(mb, getArg(p, retc + 1))->value);
147 : //Symbol s = findSymbol(cntxt->usermodule, mod,fcn);
148 227 : Symbol s = findSymbolInModule(getModule(putName(mod)), putName(fcn));
149 :
150 227 : if (s == NULL || !isSideEffectFree(s->def)
151 218 : || getInstrPtr(s->def, 0)->retc != p->retc) {
152 9 : return 0;
153 : }
154 : /*
155 : * Determine the variables to be upgraded and adjust their type
156 : */
157 218 : if ((mq = copyMalBlk(s->def)) == NULL) {
158 : return 0;
159 : }
160 218 : sig = getInstrPtr(mq, 0);
161 :
162 218 : upgrade = (bit *) GDKzalloc(sizeof(bit) * mq->vtop);
163 218 : if (upgrade == NULL) {
164 0 : freeMalBlk(mq);
165 0 : return 0;
166 : }
167 :
168 218 : setVarType(mq, 0, newBatType(getArgType(mb, p, 0)));
169 218 : clrVarFixed(mq, getArg(getInstrPtr(mq, 0), 0)); /* for typing */
170 218 : upgrade[getArg(getInstrPtr(mq, 0), 0)] = TRUE;
171 :
172 562 : for (i = 3; i < p->argc; i++) {
173 344 : if (!isaBatType(getArgType(mq, sig, i - 2))
174 344 : && isaBatType(getArgType(mb, p, i))) {
175 :
176 334 : if (getBatType(getArgType(mb, p, i)) != getArgType(mq, sig, i - 2)) {
177 0 : goto terminateMX;
178 : }
179 :
180 334 : setVarType(mq, i - 2, newBatType(getArgType(mb, p, i)));
181 334 : upgrade[getArg(sig, i - 2)] = TRUE;
182 334 : refbat = getArg(sig, i - 2);
183 : }
184 : }
185 : /*
186 : * The next step is to check each instruction of the
187 : * to-be-inlined function for arguments that require
188 : * an upgrade and resolve it afterwards.
189 : */
190 2416419 : for (i = 1; i < mq->stop; i++) {
191 2416419 : int fnd = 0;
192 :
193 2416419 : q = getInstrPtr(mq, i);
194 2416419 : if (q->token == ENDsymbol)
195 : break;
196 5459844 : for (j = 0; j < q->argc && !fnd; j++)
197 3043643 : if (upgrade[getArg(q, j)]) {
198 3738466 : for (k = 0; k < q->retc; k++) {
199 1869233 : setVarType(mq, getArg(q, j),
200 : newBatType(getArgType(mq, q, j)));
201 : /* for typing */
202 1869233 : clrVarFixed(mq, getArg(q, k));
203 1869233 : if (!upgrade[getArg(q, k)]) {
204 5864 : upgrade[getArg(q, k)] = TRUE;
205 : /* lets restart */
206 5864 : i = 0;
207 : }
208 : }
209 : fnd = 1;
210 : }
211 : /* nil:type -> nil:bat[:oid,:type] */
212 2416201 : if (!getModuleId(q) && q->token == ASSIGNsymbol && q->argc == 2
213 1094101 : && isVarConstant(mq, getArg(q, 1)) && upgrade[getArg(q, 0)]
214 45947 : && getArgType(mq, q, 0) == TYPE_void
215 0 : && !isaBatType(getArgType(mq, q, 1))) {
216 : /* handle nil assignment */
217 0 : if (ATOMcmp(getArgGDKType(mq, q, 1),
218 : VALptr(&getVar(mq, getArg(q, 1))->value),
219 : ATOMnilptr(getArgType(mq, q, 1))) == 0) {
220 0 : ValRecord cst;
221 0 : int tpe = newBatType(getArgType(mq, q, 1));
222 :
223 0 : setVarType(mq, getArg(q, 0), tpe);
224 0 : cst.vtype = TYPE_bat;
225 0 : cst.val.bval = bat_nil;
226 0 : cst.len = 0;
227 0 : m = defConstant(mq, tpe, &cst);
228 0 : if (m >= 0) {
229 0 : getArg(q, 1) = m;
230 0 : setVarType(mq, getArg(q, 1), tpe);
231 : }
232 : } else {
233 : /* handle constant tail setting */
234 0 : int tpe = newBatType(getArgType(mq, q, 1));
235 :
236 0 : setVarType(mq, getArg(q, 0), tpe);
237 0 : setModuleId(q, algebraRef);
238 0 : setFunctionId(q, projectRef);
239 0 : q = pushArgument(mb, q, getArg(q, 1));
240 0 : mq->stmt[i] = q;
241 0 : getArg(q, 1) = refbat;
242 : }
243 : }
244 : }
245 :
246 : /* now upgrade the statements */
247 2908 : for (i = 1; i < mq->stop; i++) {
248 2908 : q = getInstrPtr(mq, i);
249 2908 : if (q->token == ENDsymbol)
250 : break;
251 6067 : for (j = 0; j < q->argc; j++)
252 4287 : if (upgrade[getArg(q, j)]) {
253 2012 : if (blockStart(q) || q->barrier == REDOsymbol
254 2002 : || q->barrier == LEAVEsymbol)
255 10 : goto terminateMX;
256 2002 : if (getModuleId(q)) {
257 892 : snprintf(buf, 1024, "bat%s", getModuleId(q));
258 892 : setModuleId(q, putName(buf));
259 892 : q->typechk = TYPE_UNKNOWN;
260 892 : if (q->retc == 1 &&
261 892 : ((getModuleId(q) == batcalcRef
262 613 : && (getFunctionId(q) == mulRef
263 590 : || getFunctionId(q) == divRef
264 589 : || getFunctionId(q) == plusRef
265 67 : || getFunctionId(q) == minusRef
266 59 : || getFunctionId(q) == modRef))
267 338 : || getModuleId(q) == batmtimeRef
268 337 : || getModuleId(q) == batstrRef)) {
269 815 : if (q->argc == 3 &&
270 : /* these two filter out unary batcalc.- with a candidate list */
271 554 : getBatType(getArgType(mq, q, 1)) != TYPE_oid
272 554 : && getBatType(getArgType(mq, q, 2)) != TYPE_oid) {
273 : /* add candidate lists */
274 554 : if (isaBatType(getArgType(mq, q, 1)))
275 381 : q = pushNil(mq, q, TYPE_bat);
276 554 : if (isaBatType(getArgType(mq, q, 2)))
277 285 : q = pushNil(mq, q, TYPE_bat);
278 261 : } else if (q->argc == 4
279 260 : && getBatType(getArgType(mq, q, 3)) == TYPE_bit
280 : /* these two filter out unary
281 : * batcalc.- with a candidate
282 : * list */
283 0 : && getBatType(getArgType(mq, q, 1)) != TYPE_oid
284 0 : && getBatType(getArgType(mq, q, 2)) != TYPE_oid) {
285 0 : int a = getArg(q, 3);
286 0 : q->argc--;
287 : /* add candidate lists */
288 0 : if (isaBatType(getArgType(mq, q, 1)))
289 0 : q = pushNil(mq, q, TYPE_bat);
290 0 : if (isaBatType(getArgType(mq, q, 2)))
291 0 : q = pushNil(mq, q, TYPE_bat);
292 0 : q = pushArgument(mq, q, a);
293 : }
294 : }
295 :
296 : /* now see if we can resolve the instruction */
297 892 : typeChecker(cntxt->usermodule, mq, q, i, TRUE);
298 892 : if (q->typechk == TYPE_UNKNOWN)
299 2 : goto terminateMX;
300 : break;
301 : }
302 : /* handle simple upgraded assignments as well */
303 1110 : if (q->token == ASSIGNsymbol && q->argc == 2
304 1110 : && !(isaBatType(getArgType(mq, q, 1)))) {
305 20 : setModuleId(q, algebraRef);
306 20 : setFunctionId(q, projectRef);
307 20 : q = pushArgument(mq, q, getArg(q, 1));
308 20 : mq->stmt[i] = q;
309 20 : getArg(q, 1) = refbat;
310 :
311 20 : q->typechk = TYPE_UNKNOWN;
312 20 : typeChecker(cntxt->usermodule, mq, q, i, TRUE);
313 20 : if (q->typechk == TYPE_UNKNOWN)
314 0 : goto terminateMX;
315 : break;
316 : }
317 : }
318 : }
319 :
320 :
321 206 : if (mq->errors) {
322 0 : terminateMX:
323 :
324 12 : freeMalBlk(mq);
325 12 : GDKfree(upgrade);
326 :
327 : /* ugh ugh, fallback to non inline, but optimized code */
328 12 : msg = OPTmultiplexSimple(cntxt, s->def);
329 12 : if (msg)
330 0 : freeException(msg);
331 12 : s->def->inlineProp = 0;
332 12 : return 0;
333 : }
334 : /*
335 : * We have successfully constructed a variant
336 : * of the to-be-inlined function. Put it in place
337 : * of the original multiplex.
338 : * But first, shift the arguments of the multiplex.
339 : */
340 206 : delArgument(p, 2);
341 206 : delArgument(p, 1);
342 206 : inlineMALblock(mb, pc, mq);
343 :
344 206 : freeMalBlk(mq);
345 206 : GDKfree(upgrade);
346 206 : return 1;
347 : }
348 :
349 : /*
350 : * The comparison multiplex operations with a constant head may be supported
351 : * by reverse of the operation.
352 : */
353 : static const struct {
354 : const char *src, *dst;
355 : const int len;
356 : } OperatorMap[] = {
357 : {"<", ">", 1},
358 : {">", "<", 1},
359 : {">=", "<=", 2},
360 : {"<=", ">=", 2},
361 : {"==", "==", 2},
362 : {"!=", "!=", 2},
363 : {0, 0, 0}
364 : };
365 :
366 : static int
367 746 : OPTremapSwitched(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci,
368 : int idx, Module scope)
369 : {
370 746 : char *fcn;
371 746 : int r, i;
372 746 : (void) stk;
373 746 : (void) scope;
374 :
375 746 : if (!isMultiplex(pci) && getArgType(mb, pci, pci->retc) != TYPE_lng
376 0 : && !isVarConstant(mb, getArg(pci, 1))
377 0 : && !isVarConstant(mb, getArg(pci, 2))
378 0 : && !isVarConstant(mb, getArg(pci, 4)) && pci->argc != 5)
379 : return 0;
380 746 : fcn = VALget(&getVar(mb, getArg(pci, 2))->value);
381 5968 : for (i = 0; OperatorMap[i].src; i++)
382 4476 : if (strcmp(fcn, OperatorMap[i].src) == 0) {
383 : /* found a candidate for a switch */
384 0 : getVarConstant(mb, getArg(pci, 2)).val.sval = (char *) putNameLen(OperatorMap[i].dst, OperatorMap[i].len);
385 0 : getVarConstant(mb, getArg(pci, 2)).len = OperatorMap[i].len;
386 0 : r = getArg(pci, 3);
387 0 : getArg(pci, 3) = getArg(pci, 4);
388 0 : getArg(pci, 4) = r;
389 0 : r = OPTremapDirect(cntxt, mb, stk, pci, idx, scope);
390 :
391 : /* always restore the allocated function name */
392 0 : getVarConstant(mb, getArg(pci, 2)).val.sval = fcn;
393 0 : getVarConstant(mb, getArg(pci, 2)).len = strlen(fcn);
394 :
395 0 : if (r)
396 : return 1;
397 :
398 : /* restore the arguments */
399 0 : r = getArg(pci, 3);
400 0 : getArg(pci, 3) = getArg(pci, 4);
401 0 : getArg(pci, 4) = r;
402 : }
403 : return 0;
404 : }
405 :
406 : str
407 538703 : OPTremapImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
408 : {
409 538703 : InstrPtr *old, p;
410 538703 : int i, limit, slimit, actions = 0;
411 538703 : Module scope = cntxt->usermodule;
412 538703 : str msg = MAL_SUCCEED;
413 :
414 24468020 : for (i = 0; i < mb->stop; i++) {
415 23947547 : p = getInstrPtr(mb, i);
416 23947547 : if (isMultiplex(p)
417 23929317 : || (p->argc == 4 && getModuleId(p) == aggrRef
418 0 : && getFunctionId(p) == avgRef)) {
419 : break;
420 : }
421 : }
422 538693 : if (i == mb->stop) {
423 520473 : goto wrapup;
424 : }
425 :
426 18220 : old = mb->stmt;
427 18220 : limit = mb->stop;
428 18220 : slimit = mb->ssize;
429 18220 : if (newMalBlkStmt(mb, mb->ssize) < 0)
430 0 : throw(MAL, "optmizer.remap", SQLSTATE(HY013) MAL_MALLOC_FAIL);
431 :
432 3309314 : for (i = 0; i < limit; i++) {
433 3291094 : p = old[i];
434 3291094 : if (isMultiplex(p)) {
435 : /*
436 : * The next step considered is to handle inlined functions.
437 : * It means we have already skipped the most obvious ones,
438 : * such as the calculator functions. It is particularly
439 : * geared at handling the PSM code.
440 : */
441 146931 : int plus_one = getArgType(mb, p, p->retc) == TYPE_lng ? 1 : 0;
442 146931 : str mod = VALget(&getVar(mb, getArg(p, p->retc + 0 + plus_one))-> value);
443 146931 : str fcn = VALget(&getVar(mb, getArg(p, p->retc + 1 + plus_one))-> value);
444 : //Symbol s = findSymbol(cntxt->usermodule, mod,fcn);
445 146931 : Symbol s = findSymbolInModule(getModule(putName(mod)), putName(fcn));
446 :
447 146931 : if (s && s->def->inlineProp) {
448 227 : pushInstruction(mb, p);
449 227 : if (OPTmultiplexInline(cntxt, mb, p, mb->stop - 1)) {
450 206 : actions++;
451 : }
452 146704 : } else if (OPTremapDirect(cntxt, mb, stk, p, i, scope)
453 746 : || OPTremapSwitched(cntxt, mb, stk, p, i, scope)) {
454 145958 : freeInstruction(p);
455 145958 : actions++;
456 : } else {
457 746 : pushInstruction(mb, p);
458 : }
459 3144163 : } else if (p->argc == 4 && getModuleId(p) == aggrRef
460 0 : && getFunctionId(p) == avgRef) {
461 : /* group aggr.avg -> aggr.sum/aggr.count */
462 0 : InstrPtr sum, avg, t, iszero;
463 0 : InstrPtr cnt;
464 0 : sum = copyInstruction(p);
465 0 : if (sum == NULL) {
466 0 : msg = createException(MAL, "optimizer.remap",
467 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
468 0 : break;
469 : }
470 0 : cnt = copyInstruction(p);
471 0 : if (cnt == NULL) {
472 0 : freeInstruction(sum);
473 0 : msg = createException(MAL, "optimizer.remap",
474 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
475 0 : break;
476 : }
477 0 : setFunctionId(sum, sumRef);
478 0 : setFunctionId(cnt, countRef);
479 0 : getArg(sum, 0) = newTmpVariable(mb, getArgType(mb, p, 1));
480 0 : getArg(cnt, 0) = newTmpVariable(mb, newBatType(TYPE_lng));
481 0 : pushInstruction(mb, sum);
482 0 : pushInstruction(mb, cnt);
483 :
484 0 : t = newInstruction(mb, batcalcRef, eqRef);
485 0 : if (t == NULL) {
486 0 : msg = createException(MAL, "optimizer.remap",
487 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
488 0 : break;
489 : }
490 0 : getArg(t, 0) = newTmpVariable(mb, newBatType(TYPE_bit));
491 0 : t = pushArgument(mb, t, getDestVar(cnt));
492 0 : t = pushLng(mb, t, 0);
493 0 : pushInstruction(mb, t);
494 0 : iszero = t;
495 :
496 0 : t = newInstruction(mb, batcalcRef, dblRef);
497 0 : if (t == NULL) {
498 0 : msg = createException(MAL, "optimizer.remap",
499 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
500 0 : break;
501 : }
502 0 : getArg(t, 0) = newTmpVariable(mb, getArgType(mb, p, 0));
503 0 : t = pushArgument(mb, t, getDestVar(sum));
504 0 : pushInstruction(mb, t);
505 0 : sum = t;
506 :
507 0 : t = newInstruction(mb, batcalcRef, ifthenelseRef);
508 0 : if (t == NULL) {
509 0 : msg = createException(MAL, "optimizer.remap",
510 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
511 0 : break;
512 : }
513 0 : getArg(t, 0) = newTmpVariable(mb, getArgType(mb, p, 0));
514 0 : t = pushArgument(mb, t, getDestVar(iszero));
515 0 : t = pushNil(mb, t, TYPE_dbl);
516 0 : t = pushArgument(mb, t, getDestVar(sum));
517 0 : pushInstruction(mb, t);
518 0 : sum = t;
519 :
520 0 : t = newInstruction(mb, batcalcRef, dblRef);
521 0 : if (t == NULL) {
522 0 : msg = createException(MAL, "optimizer.remap",
523 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
524 0 : break;
525 : }
526 0 : getArg(t, 0) = newTmpVariable(mb, getArgType(mb, p, 0));
527 0 : t = pushArgument(mb, t, getDestVar(cnt));
528 0 : pushInstruction(mb, t);
529 0 : cnt = t;
530 :
531 0 : avg = newInstruction(mb, batcalcRef, divRef);
532 0 : if (avg == NULL) {
533 0 : msg = createException(MAL, "optimizer.remap",
534 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
535 0 : break;
536 : }
537 0 : getArg(avg, 0) = getArg(p, 0);
538 0 : avg = pushArgument(mb, avg, getDestVar(sum));
539 0 : avg = pushArgument(mb, avg, getDestVar(cnt));
540 0 : avg = pushNil(mb, avg, TYPE_bat);
541 0 : avg = pushNil(mb, avg, TYPE_bat);
542 0 : freeInstruction(p);
543 0 : pushInstruction(mb, avg);
544 : } else {
545 3144163 : pushInstruction(mb, p);
546 : }
547 : }
548 2478824 : for (; i < slimit; i++)
549 2460604 : if (old[i])
550 0 : pushInstruction(mb, old[i]);
551 18220 : GDKfree(old);
552 :
553 : /* Defense line against incorrect plans */
554 18220 : if (msg == MAL_SUCCEED && actions > 0) {
555 17889 : msg = chkTypes(cntxt->usermodule, mb, FALSE);
556 17889 : if (!msg)
557 17889 : msg = chkFlow(mb);
558 17889 : if (!msg)
559 17889 : msg = chkDeclarations(mb);
560 : }
561 331 : wrapup:
562 : /* keep actions taken as a fake argument */
563 538693 : (void) pushInt(mb, pci, actions);
564 538693 : return msg;
565 : }
|