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 | |
20 | static int |
21 | OPTremapDirect(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 | */ |
112 | static int |
113 | OPTmultiplexInline(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){ |
287 | terminateMX: |
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 | */ |
331 | static 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 | |
343 | static int |
344 | OPTremapSwitched(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 | |
378 | str |
379 | OPTremapImplementation(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 | |