| 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 | */ |
| 33 | static str |
| 34 | OPTexpandMultiplex(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 | */ |
| 189 | str |
| 190 | OPTmultiplexSimple(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 | |
| 214 | str |
| 215 | OPTmultiplexImplementation(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 | |