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