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#include "monetdb_config.h"
10#include "opt_multiplex.h"
11#include "manifold.h"
12#include "mal_interpreter.h"
13
14/*
15 * The generic solution to the multiplex operators is to translate
16 * them to a MAL loop.
17 * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code
18 * structure:
19 *
20 * resB:= bat.new(A1);
21 * barrier (h,t1):= iterator.new(A1);
22 * t2:= algebra.fetch(A2,h)
23 * ...
24 * cr:= MOD.FCN(t1,...,tn);
25 * bat.append(resB,cr);
26 * redo (h,t):= iterator.next(A1);
27 * end h;
28 *
29 * The algorithm consists of two phases: phase one deals with
30 * collecting the relevant information, phase two is the actual
31 * code construction.
32 */
33static str
34OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
35{
36 int i = 2, iter = 0;
37 int hvar, tvar;
38 str mod, fcn;
39 int *alias, *resB;
40 InstrPtr q;
41 int tt;
42 int bat = (getModuleId(pci) == batmalRef) ;
43
44 //if ( optimizerIsApplied(mb,"multiplex"))
45 //return 0;
46 (void) cntxt;
47 (void) stk;
48 for (i = 0; i < pci->retc; i++) {
49 tt = getBatType(getArgType(mb, pci, i));
50 if (tt== TYPE_any)
51 throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target tail type is missing");
52 if (isAnyExpression(getArgType(mb, pci, i)))
53 throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target type is missing");
54 }
55
56 mod = VALget(&getVar(mb, getArg(pci, pci->retc))->value);
57 mod = putName(mod);
58 fcn = VALget(&getVar(mb, getArg(pci, pci->retc+1))->value);
59 fcn = putName(fcn);
60 if(mod == NULL || fcn == NULL)
61 throw(MAL, "optimizer.multiplex", SQLSTATE(HY001) MAL_MALLOC_FAIL);
62 fprintf(stderr,"#WARNING To speedup %s.%s a bulk operator implementation is needed\n#", mod,fcn);
63 fprintInstruction(stderr, mb, stk, pci, LIST_MAL_ALL);
64
65 /* search the iterator bat */
66 for (i = pci->retc+2; i < pci->argc; i++)
67 if (isaBatType(getArgType(mb, pci, i))) {
68 iter = getArg(pci, i);
69 break;
70 }
71 if( i == pci->argc)
72 throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Iterator BAT type is missing");
73
74 if( OPTdebug & OPTmultiplex)
75 { char *tpenme;
76 fprintf(stderr,"#calling the optimize multiplex script routine\n");
77 fprintFunction(stderr,mb, 0, LIST_MAL_ALL );
78 tpenme = getTypeName(getVarType(mb,iter));
79 fprintf(stderr,"#multiplex against operator %d %s\n",iter, tpenme);
80 GDKfree(tpenme);
81 fprintInstruction(stderr,mb, 0, pci,LIST_MAL_ALL);
82 }
83
84 /*
85 * Beware, the operator constant (arg=1) is passed along as well,
86 * because in the end we issue a recursive function call that should
87 * find the actual arguments at the proper place of the callee.
88 */
89
90 alias= (int*) GDKmalloc(sizeof(int) * pci->maxarg);
91 resB = (int*) GDKmalloc(sizeof(int) * pci->retc);
92 if (alias == NULL || resB == NULL) {
93 GDKfree(alias);
94 GDKfree(resB);
95 throw(MAL, "optimizer.multiplex", SQLSTATE(HY001) MAL_MALLOC_FAIL);
96 }
97
98 /* resB := new(refBat) */
99 for (i = 0; i < pci->retc; i++) {
100 q = newFcnCall(mb, batRef, newRef);
101 resB[i] = getArg(q, 0);
102
103 tt = getBatType(getArgType(mb, pci, i));
104
105 setVarType(mb, getArg(q, 0), newBatType(tt));
106 q = pushType(mb, q, tt);
107 }
108
109 /* barrier (h,r) := iterator.new(refBat); */
110 q = newFcnCall(mb, iteratorRef, newRef);
111 q->barrier = BARRIERsymbol;
112 hvar = newTmpVariable(mb, TYPE_any);
113 getArg(q,0) = hvar;
114 tvar = newTmpVariable(mb, TYPE_any);
115 q= pushReturn(mb, q, tvar);
116 (void) pushArgument(mb,q,iter);
117
118 /* $1:= algebra.fetch(Ai,h) or constant */
119 for (i = pci->retc+2; i < pci->argc; i++) {
120 if (getArg(pci, i) != iter && isaBatType(getArgType(mb, pci, i))) {
121 q = newFcnCall(mb, algebraRef, "fetch");
122 alias[i] = newTmpVariable(mb, getBatType(getArgType(mb, pci, i)));
123 getArg(q, 0) = alias[i];
124 q= pushArgument(mb, q, getArg(pci, i));
125 (void) pushArgument(mb, q, hvar);
126 }
127 }
128
129 /* cr:= mod.CMD($1,...,$n); */
130 q = newFcnCall(mb, mod, fcn);
131 for (i = 0; i < pci->retc; i++) {
132 int nvar = 0;
133 if (bat) {
134 tt = getBatType(getArgType(mb, pci, i));
135 nvar = newTmpVariable(mb, newBatType(tt));
136 } else {
137 nvar = newTmpVariable(mb, TYPE_any);
138 }
139 if (i)
140 q = pushReturn(mb, q, nvar);
141 else
142 getArg(q, 0) = nvar;
143 }
144
145 for (i = pci->retc+2; i < pci->argc; i++) {
146 if (getArg(pci, i) == iter) {
147 q = pushArgument(mb, q, tvar);
148 } else if (isaBatType(getArgType(mb, pci, i))) {
149 q = pushArgument(mb, q, alias[i]);
150 } else {
151 q = pushArgument(mb, q, getArg(pci, i));
152 }
153 }
154
155 for (i = 0; i < pci->retc; i++) {
156 InstrPtr a = newFcnCall(mb, batRef, appendRef);
157 a = pushArgument(mb, a, resB[i]);
158 a = pushArgument(mb, a, getArg(q,i));
159 getArg(a,0) = resB[i];
160 }
161
162/* redo (h,r):= iterator.next(refBat); */
163 q = newFcnCall(mb, iteratorRef, nextRef);
164 q->barrier = REDOsymbol;
165 getArg(q,0) = hvar;
166 q= pushReturn(mb, q, tvar);
167 (void) pushArgument(mb,q,iter);
168
169 q = newAssignment(mb);
170 q->barrier = EXITsymbol;
171 getArg(q,0) = hvar;
172 (void) pushReturn(mb, q, tvar);
173
174 for (i = 0; i < pci->retc; i++) {
175 q = newAssignment(mb);
176 getArg(q, 0) = getArg(pci, i);
177 (void) pushArgument(mb, q, resB[i]);
178 }
179 GDKfree(alias);
180 GDKfree(resB);
181 return MAL_SUCCEED;
182}
183
184/*
185 * The multiplexSimple is called by the MAL scenario. It bypasses
186 * the optimizer infrastructure, to avoid excessive space allocation
187 * and interpretation overhead.
188 */
189str
190OPTmultiplexSimple(Client cntxt, MalBlkPtr mb)
191{
192 //MalBlkPtr mb= cntxt->curprg->def;
193 int i, doit=0;
194 InstrPtr p;
195 str msg = MAL_SUCCEED;
196
197 if(mb)
198 for( i=0; i<mb->stop; i++){
199 p= getInstrPtr(mb,i);
200 if(isMultiplex(p)) {
201 p->typechk = TYPE_UNKNOWN;
202 doit++;
203 }
204 }
205 if( doit) {
206 msg = OPTmultiplexImplementation(cntxt, mb, 0, 0);
207 chkTypes(cntxt->usermodule, mb,TRUE);
208 chkFlow(mb);
209 chkDeclarations(mb);
210 }
211 return msg;
212}
213
214str
215OPTmultiplexImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
216{
217 InstrPtr *old = 0, p;
218 int i, limit, slimit, actions= 0;
219 str msg= MAL_SUCCEED;
220 char buf[256];
221 lng usec = GDKusec();
222
223 (void) stk;
224 (void) pci;
225
226 old = mb->stmt;
227 limit = mb->stop;
228 slimit = mb->ssize;
229 if ( newMalBlkStmt(mb, mb->ssize) < 0 )
230 throw(MAL,"optimizer.mergetable", SQLSTATE(HY001) MAL_MALLOC_FAIL);
231
232 for (i = 0; i < limit; i++) {
233 p = old[i];
234 if (msg == MAL_SUCCEED && isMultiplex(p)) {
235 if ( MANIFOLDtypecheck(cntxt,mb,p,0) != NULL){
236 setFunctionId(p, manifoldRef);
237 p->typechk = TYPE_UNKNOWN;
238 pushInstruction(mb, p);
239 actions++;
240 continue;
241 }
242 msg = OPTexpandMultiplex(cntxt, mb, stk, p);
243 if( msg== MAL_SUCCEED){
244 freeInstruction(p);
245 old[i]=0;
246 actions++;
247 continue;
248 }
249
250 pushInstruction(mb, p);
251 actions++;
252 } else if( old[i])
253 pushInstruction(mb, p);
254 }
255 for(;i<slimit; i++)
256 if( old[i])
257 freeInstruction(old[i]);
258 GDKfree(old);
259
260 /* Defense line against incorrect plans */
261 if( msg == MAL_SUCCEED && actions > 0){
262 chkTypes(cntxt->usermodule, mb, FALSE);
263 chkFlow(mb);
264 chkDeclarations(mb);
265 }
266 /* keep all actions taken as a post block comment */
267 usec = GDKusec()- usec;
268 snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec","multiplex",actions, usec);
269 newComment(mb,buf);
270 if( actions >= 0)
271 addtoMalBlkHistory(mb);
272
273 if( OPTdebug & OPTmultiplex){
274 fprintf(stderr, "#MULTIPLEX optimizer exit\n");
275 fprintFunction(stderr, mb, 0, LIST_MAL_ALL);
276 }
277 return msg;
278}
279