/* * The generic solution to the multiplex operators is to translate * them to a MAL loop. * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code * structure: * * @verbatim * A1rev:=bat.reverse(A1); * resB:= bat.new(A1); * barrier (h,t):= iterator.new(A1); * $1:= algebra.fetch(A1,h); * $2:= A2; # in case of constant? * ... * cr:= MOD.FCN($1,...,$n); * y:=algebra.fetch(A1rev,h); * bat.insert(resB,y,cr); * redo (h,t):= iterator.next(A1); * end h; * @end verbatim * * The algorithm consists of two phases: phase one deals with * collecting the relevant information, phase two is the actual * code construction. */ static str OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i = 2, resB, iter = 0, cr; int hvar, tvar; int x, y; str mod, fcn; int *alias; InstrPtr q; int ht, tt; (void) cntxt; (void) stk; ht = getHeadType(getArgType(mb, pci, 0)); if (ht != TYPE_oid) throw(MAL, "optimizer.multiplex", "Target head type is missing"); tt = getTailType(getArgType(mb, pci, 0)); if (tt== TYPE_any) throw(MAL, "optimizer.multiplex", "Target tail type is missing"); if (isAnyExpression(getArgType(mb, pci, 0))) throw(MAL, "optimizer.multiplex", "Target type is missing"); mod = VALget(&getVar(mb, getArg(pci, 1))->value); mod = putName(mod,strlen(mod)); fcn = VALget(&getVar(mb, getArg(pci, 2))->value); fcn = putName(fcn,strlen(fcn)); /* search the iterator bat */ for (i = 3; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { iter = getArg(pci, i); if (getHeadType(getVarType(mb,iter)) != TYPE_oid) throw(MAL, "optimizer.multiplex", "Iterator BAT is not OID-headed"); break; } if( i == pci->argc) throw(MAL, "optimizer.multiplex", "Iterator BAT type is missing"); OPTDEBUGmultiplex { mnstr_printf(cntxt->fdout,"#calling the optimize multiplex script routine\n"); printFunction(cntxt->fdout,mb, 0, LIST_MAL_ALL ); mnstr_printf(cntxt->fdout,"#multiplex against operator %d %s\n",iter, getTypeName(getVarType(mb,iter))); printInstruction(cntxt->fdout,mb, 0, pci,LIST_MAL_ALL); } /* * Beware, the operator constant (arg=1) is passed along as well, * because in the end we issue a recursive function call that should * find the actual arguments at the proper place of the callee. */ alias= (int*) GDKmalloc(sizeof(int) * pci->maxarg); if (alias == NULL) return NULL; /* x := bat.reverse(A1); */ x = newTmpVariable(mb, newBatType(getTailType(getVarType(mb,iter)), getHeadType(getVarType(mb,iter)))); q = newFcnCall(mb, batRef, reverseRef); getArg(q, 0) = x; q = pushArgument(mb, q, iter); /* resB := new(refBat) */ q = newFcnCall(mb, batRef, newRef); resB = getArg(q, 0); setVarType(mb, getArg(q, 0), newBatType(ht, tt)); q = pushType(mb, q, ht); q = pushType(mb, q, tt); /* barrier (h,r) := iterator.new(refBat); */ q = newFcnCall(mb, iteratorRef, newRef); q->barrier = BARRIERsymbol; hvar = newTmpVariable(mb, TYPE_any); getArg(q,0) = hvar; tvar = newTmpVariable(mb, TYPE_any); q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); /* $1:= algebra.fetch(Ai,h) or constant */ alias[i] = tvar; for (i++; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { q = newFcnCall(mb, algebraRef, "fetch"); alias[i] = newTmpVariable(mb, getTailType(getArgType(mb, pci, i))); getArg(q, 0) = alias[i]; q= pushArgument(mb, q, getArg(pci, i)); (void) pushArgument(mb, q, hvar); } /* cr:= mod.CMD($1,...,$n); */ q = newFcnCall(mb, mod, fcn); cr = getArg(q, 0) = newTmpVariable(mb, TYPE_any); for (i = 3; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { q= pushArgument(mb, q, alias[i]); } else { q = pushArgument(mb, q, getArg(pci, i)); } /* y := algebra.fetch(x,h); */ y = newTmpVariable(mb, getHeadType(getVarType(mb,iter))); q = newFcnCall(mb, algebraRef, "fetch"); getArg(q, 0) = y; q = pushArgument(mb, q, x); q = pushArgument(mb, q, hvar); /* insert(resB,h,cr); not append(resB, cr); the head type (oid) may dynamically change */ q = newFcnCall(mb, batRef, insertRef); q= pushArgument(mb, q, resB); q= pushArgument(mb, q, y); (void) pushArgument(mb, q, cr); /* redo (h,r):= iterator.next(refBat); */ q = newFcnCall(mb, iteratorRef, nextRef); q->barrier = REDOsymbol; getArg(q,0) = hvar; q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); q = newAssignment(mb); q->barrier = EXITsymbol; getArg(q,0) = hvar; (void) pushReturn(mb, q, tvar); q = newAssignment(mb); getArg(q, 0) = getArg(pci, 0); (void) pushArgument(mb, q, resB); GDKfree(alias); return MAL_SUCCEED; }
int OPTquerylogImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, limit, slimit; InstrPtr p = 0, *old= mb->stmt, q,r; int argc, io, user,nice,sys,idle,iowait,load, arg, start,finish, name; int xtime=0, rtime = 0, tuples=0; InstrPtr defineQuery = NULL; // query log needed? if ( !QLOGisset() ) return 0; (void) pci; (void) stk; /* to fool compilers */ (void) cntxt; /* gather information */ for (i = 1; i < mb->stop; i++) { p = getInstrPtr(mb,i); if ( getModuleId(p) && idcmp(getModuleId(p), "querylog") == 0 && idcmp(getFunctionId(p),"define")==0){ defineQuery= p; getVarConstant(mb,getArg(p,3)).val.lval = GDKusec()-getVarConstant(mb,getArg(p,3)).val.lval ; } } if ( defineQuery == NULL) /* nothing to do */ return 0; limit= mb->stop; slimit= mb->ssize; if ( newMalBlkStmt(mb, mb->ssize) < 0) return 0; pushInstruction(mb, old[0]); /* run the querylog.define operation */ defineQuery = copyInstruction(defineQuery); setFunctionId(defineQuery, insertRef); getArg(defineQuery,0) = newTmpVariable(mb,TYPE_any); defineQuery->token = ASSIGNsymbol; setModuleId(defineQuery,querylogRef); /* collect the initial statistics */ q = newStmt(mb, "clients", "getUsername"); name= getArg(q,0)= newVariable(mb,GDKstrdup("name"),TYPE_str); defineQuery = pushArgument(mb,defineQuery,name); q = newStmt(mb, "mtime", "current_timestamp"); start= getArg(q,0)= newVariable(mb,GDKstrdup("start"),TYPE_timestamp); defineQuery = pushArgument(mb,defineQuery,start); pushInstruction(mb, defineQuery); q = newStmt1(mb, sqlRef, "argRecord"); for ( argc=1; argc < old[0]->argc; argc++) q = pushArgument(mb, q, getArg(old[0],argc)); arg= getArg(q,0)= newVariable(mb,GDKstrdup("args"),TYPE_str); q = newStmt(mb, "alarm", "usec"); xtime = getArg(q,0)= newVariable(mb,GDKstrdup("xtime"),TYPE_lng); user = newVariable(mb,GDKstrdup("user"),TYPE_lng); nice = newVariable(mb,GDKstrdup("nice"),TYPE_lng); sys = newVariable(mb,GDKstrdup("sys"),TYPE_lng); idle = newVariable(mb,GDKstrdup("idle"),TYPE_lng); iowait = newVariable(mb,GDKstrdup("iowait"),TYPE_lng); q = newStmt(mb, "profiler", "cpustats"); q->retc= q->argc =0; q = pushReturn(mb,q,user); q = pushReturn(mb,q,nice); q = pushReturn(mb,q,sys); q = pushReturn(mb,q,idle); q = pushReturn(mb,q,iowait); q = newAssignment(mb); tuples= getArg(q,0) = newVariable(mb,GDKstrdup("tuples"),TYPE_wrd); (void) pushWrd(mb,q,1); for (i = 1; i < limit; i++) { p = old[i]; if (getModuleId(p)==sqlRef && (idcmp(getFunctionId(p),"exportValue")==0 || idcmp(getFunctionId(p),"exportResult")==0 ) ) { q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, xtime); getArg(r,0)=xtime; q = newStmt(mb, "alarm", "usec"); rtime= getArg(q,0)= newVariable(mb,GDKstrdup("rtime"),TYPE_lng); pushInstruction(mb,p); continue; } if ( getModuleId(p) == sqlRef && idcmp(getFunctionId(p),"resultSet")==0 && isaBatType(getVarType(mb,getArg(p,3)))){ q = newStmt(mb, "aggr", "count"); getArg(q,0) = tuples; (void) pushArgument(mb,q, getArg(p,3)); pushInstruction(mb,p); continue; } if ( p->token== ENDsymbol || p->barrier == RETURNsymbol || p->barrier == YIELDsymbol){ if ( rtime == 0){ q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, xtime); getArg(r,0)=xtime; q = newStmt(mb, "alarm", "usec"); rtime= getArg(q,0)= newVariable(mb,GDKstrdup("rtime"),TYPE_lng); } q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, rtime); getArg(r,0)=rtime; /* * Post execution statistics gathering */ q = newStmt(mb, "mtime", "current_timestamp"); finish= getArg(q,0)= newVariable(mb,GDKstrdup("finish"),TYPE_any); q = newStmt(mb, "profiler", "cpuload"); load = newVariable(mb,GDKstrdup("load"),TYPE_int); getArg(q,0)= load; io = newVariable(mb,GDKstrdup("io"),TYPE_int); q= pushReturn(mb,q,io); q = pushArgument(mb,q,user); q = pushArgument(mb,q,nice); q = pushArgument(mb,q,sys); q = pushArgument(mb,q,idle); q = pushArgument(mb,q,iowait); q = newStmt(mb, querylogRef, "call"); q = pushArgument(mb, q, start); q = pushArgument(mb, q, finish); q = pushArgument(mb, q, arg); q = pushArgument(mb, q, tuples); q = pushArgument(mb, q, xtime); q = pushArgument(mb, q, rtime); q = pushArgument(mb, q, load); q = pushArgument(mb, q, io); pushInstruction(mb,p); continue; } pushInstruction(mb,p); if (p->barrier == YIELDsymbol){ /* the factory yield may return */ q = newStmt(mb, "mtime", "current_timestamp"); start= getArg(q,0)= newVariable(mb,GDKstrdup("start"),TYPE_any); q = newStmt1(mb, sqlRef, "argRecord"); for ( argc=1; argc < old[0]->argc; argc++) q = pushArgument(mb, q, getArg(old[0],argc)); arg= getArg(q,0)= newVariable(mb,GDKstrdup("args"),TYPE_str); q = newAssignment(mb); q = pushLng(mb,q,0); q = newAssignment(mb); q = pushWrd(mb,q,0); tuples= getArg(q,0)= newVariable(mb,GDKstrdup("tuples"),TYPE_wrd); newFcnCall(mb,"profiler","setMemoryFlag"); q->argc--; pushWrd(mb,q,1); q = newStmt(mb, "alarm", "usec"); xtime = getArg(q,0)= newVariable(mb,GDKstrdup("xtime"),TYPE_lng); } } for( ; i<slimit; i++) if(old[i]) freeInstruction(old[i]); GDKfree(old); return 1; }
int OPTrecyclerImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) { int i, j, cnt, tp, c, actions = 0, marks = 0, delta = 0; Lifespan span; InstrPtr *old, q; int limit, updstmt = 0; char *recycled; short app_sc = -1, in = 0; ValRecord cst; (void) cntxt; (void) stk; limit = mb->stop; old = mb->stmt; for (i = 1; i < limit; i++) { p = old[i]; if (getModuleId(p) == sqlRef && (getFunctionId(p) == affectedRowsRef || getFunctionId(p) == exportOperationRef || getFunctionId(p) == appendRef || getFunctionId(p) == updateRef || getFunctionId(p) == deleteRef)) updstmt = 1; } span = setLifespan(mb); if (span == NULL) return 0; /* watch out, newly created instructions may introduce new variables */ recycled = GDKzalloc(sizeof(char) * mb->vtop * 2); if (recycled == NULL) return 0; if (newMalBlkStmt(mb, mb->ssize) < 0) { GDKfree(recycled); return 0; } pushInstruction(mb, old[0]); mb->recid = recycleSeq++; /* create a handle for recycler */ (void) newFcnCall(mb, "recycle", "prelude"); in = 1; for (i = 1; i < limit; i++) { p = old[i]; if (hasSideEffects(p, TRUE) || isUpdateInstruction(p) || isUnsafeFunction(p)) { if (getModuleId(p) == recycleRef) { /*don't inline recycle instr. */ freeInstruction(p); continue; } pushInstruction(mb, p); /* update instructions are not recycled but monitored*/ if (isUpdateInstruction(p)) { if (getModuleId(p) == batRef && (getArgType(mb, p, 1) == TYPE_bat || isaBatType(getArgType(mb, p, 1)))) { recycled[getArg(p, 1)] = 0; q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, getArg(p, 1)); actions++; } if (getModuleId(p) == sqlRef) { if (getFunctionId(p) == appendRef) { if (app_sc >= 0) continue; else app_sc = getArg(p, 2); } VALset(&cst, TYPE_int, &delta); c = defConstant(mb, TYPE_int, &cst); q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, c); pushArgument(mb, q, getArg(p, 2)); pushArgument(mb, q, getArg(p, 3)); if (getFunctionId(p) == updateRef) pushArgument(mb, q, getArg(p, 4)); actions++; } } /* take care of SQL catalog update instructions */ if (getModuleId(p) == sqlRef && getFunctionId(p) == catalogRef) { tp = *(int *) getVarValue(mb, getArg(p, 1)); if (tp == 22 || tp == 25) { delta = 2; VALset(&cst, TYPE_int, &delta); c = defConstant(mb, TYPE_int, &cst); q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, c); pushArgument(mb, q, getArg(p, 2)); if (tp == 25) pushArgument(mb, q, getArg(p, 3)); actions++; } } continue; } if (p->token == ENDsymbol || p->barrier == RETURNsymbol) { if (in) { /* if (updstmt && app_sc >= 0) { q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, app_sc); pushArgument(mb, q, app_tbl); } */ (void) newFcnCall(mb, "recycle", "epilogue"); in = 0; } pushInstruction(mb, p); continue; } if (p->barrier && p->token != CMDcall) { /* never save a barrier unless it is a command and side-effect free */ pushInstruction(mb, p); continue; } /* don't change instructions in update statements */ if (updstmt) { pushInstruction(mb, p); continue; } /* skip simple assignments */ if (p->token == ASSIGNsymbol) { pushInstruction(mb, p); continue; } if (getModuleId(p) == octopusRef && (getFunctionId(p) == bindRef || getFunctionId(p) == bindidxRef)) { recycled[getArg(p, 0)] = 1; p->recycle = recycleMaxInterest; marks++; } /* During base table recycling skip marking instructions other than octopus.bind */ if (baseTableMode) { pushInstruction(mb, p); continue; } /* general rule: all arguments are constants or recycled, ignore C pointer arguments from mvc */ cnt = 0; for (j = p->retc; j < p->argc; j++) if (recycled[getArg(p, j)] || isVarConstant(mb, getArg(p, j)) || ignoreVar(mb, getArg(p, j))) cnt++; if (cnt == p->argc - p->retc) { OPTDEBUGrecycle { mnstr_printf(cntxt->fdout, "#recycle instruction\n"); printInstruction(cntxt->fdout, mb, 0, p, LIST_MAL_ALL); } marks++; p->recycle = recycleMaxInterest; /* this instruction is to be monitored */ for (j = 0; j < p->retc; j++) if (getLastUpdate(span, getArg(p, j)) == i) recycled[getArg(p, j)] = 1; } /* * The expected gain is largest if we can re-use selections * on the base tables in SQL. These, however, are marked as * uselect() calls, which only produce the oid head. * For cheap types we preselect using select() and re-map uselect() back * over this temporary. * For the time being for all possible selects encountered * are marked for re-use. */ /* take care of semantic driven recyling */ /* for selections check the bat argument only the range is often template parameter*/ if ((getFunctionId(p) == selectRef || getFunctionId(p) == antiuselectRef || getFunctionId(p) == likeselectRef || getFunctionId(p) == likeRef || getFunctionId(p) == thetaselectRef) && recycled[getArg(p, 1)]) { p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } if ((getFunctionId(p) == uselectRef || getFunctionId(p) == thetauselectRef) && recycled[getArg(p, 1)]) { if (!ATOMvarsized(getGDKType(getArgType(mb, p, 2)))) { q = copyInstruction(p); getArg(q, 0) = newTmpVariable(mb, TYPE_any); if (getFunctionId(p) == uselectRef) setFunctionId(q, selectRef); else setFunctionId(q, thetaselectRef); q->recycle = recycleMaxInterest; marks++; recycled[getArg(q, 0)] = 1; pushInstruction(mb, q); getArg(p, 1) = getArg(q, 0); setFunctionId(p, projectRef); p->argc = 2; } p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } if (getModuleId(p) == pcreRef) { if ((getFunctionId(p) == selectRef && recycled[getArg(p, 2)]) || (getFunctionId(p) == uselectRef && recycled[getArg(p, 2)])) { p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } else if (getFunctionId(p) == likeuselectRef && recycled[getArg(p, 1)]) { q = copyInstruction(p); getArg(q, 0) = newTmpVariable(mb, TYPE_any); setFunctionId(q, likeselectRef); q->recycle = recycleMaxInterest; recycled[getArg(q, 0)] = 1; pushInstruction(mb, q); getArg(p, 1) = getArg(q, 0); setFunctionId(p, projectRef); setModuleId(p, algebraRef); p->argc = 2; p->recycle = recycleMaxInterest; marks += 2; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } } /* * The sql.bind instructions should be handled carefully * The delete and update BATs should not be recycled, * because they may lead to view dependencies that later interferes * with the transaction commits. */ /* enable recycling of delta-bats if (getModuleId(p) == sqlRef && (((getFunctionId(p) == bindRef || getFunctionId(p) == putName("bind_idxbat", 11)) && getVarConstant(mb, getArg(p, 5)).val.ival != 0) || getFunctionId(p) == binddbatRef)) { recycled[getArg(p, 0)] = 0; p->recycle = REC_NO_INTEREST; } */ /* * The sql.bind instructions should be handled carefully * The delete and update BATs should not be recycled, * because they may lead to view dependencies that later interferes * with the transaction commits. */ /* enable recycling of delta-bats if (getModuleId(p)== sqlRef && (((getFunctionId(p)==bindRef || getFunctionId(p) == putName("bind_idxbat",11)) && getVarConstant(mb, getArg(p,5)).val.ival != 0) || getFunctionId(p)== binddbatRef) ) { recycled[getArg(p,0)]=0; p->recycle = REC_NO_INTEREST; } */ pushInstruction(mb, p); }
/* * The generic solution to the multiplex operators is to translate * them to a MAL loop. * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code * structure: * * resB:= bat.new(A1); * barrier (h,t1):= iterator.new(A1); * t2:= algebra.fetch(A2,h) * ... * cr:= MOD.FCN(t1,...,tn); * bat.append(resB,cr); * redo (h,t):= iterator.next(A1); * end h; * * The algorithm consists of two phases: phase one deals with * collecting the relevant information, phase two is the actual * code construction. */ static str OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i = 2, iter = 0; int hvar, tvar; str mod, fcn; int *alias, *resB; InstrPtr q; int tt; int bat = (getModuleId(pci) == batmalRef) ; //if ( optimizerIsApplied(mb,"multiplex")) //return 0; (void) cntxt; (void) stk; for (i = 0; i < pci->retc; i++) { tt = getBatType(getArgType(mb, pci, i)); if (tt== TYPE_any) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target tail type is missing"); if (isAnyExpression(getArgType(mb, pci, i))) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target type is missing"); } mod = VALget(&getVar(mb, getArg(pci, pci->retc))->value); mod = putName(mod); fcn = VALget(&getVar(mb, getArg(pci, pci->retc+1))->value); fcn = putName(fcn); if(mod == NULL || fcn == NULL) throw(MAL, "optimizer.multiplex", SQLSTATE(HY001) MAL_MALLOC_FAIL); #ifndef NDEBUG fprintf(stderr,"#WARNING To speedup %s.%s a bulk operator implementation is needed\n#", mod,fcn); fprintInstruction(stderr, mb, stk, pci, LIST_MAL_DEBUG); #endif /* search the iterator bat */ for (i = pci->retc+2; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { iter = getArg(pci, i); break; } if( i == pci->argc) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Iterator BAT type is missing"); #ifdef DEBUG_OPT_MULTIPLEX { char *tpenme; fprintf(stderr,"#calling the optimize multiplex script routine\n"); fprintFunction(stderr,mb, 0, LIST_MAL_ALL ); tpenme = getTypeName(getVarType(mb,iter)); fprintf(stderr,"#multiplex against operator %d %s\n",iter, tpenme); GDKfree(tpenme); fprintInstruction(stderr,mb, 0, pci,LIST_MAL_ALL); } #endif /* * Beware, the operator constant (arg=1) is passed along as well, * because in the end we issue a recursive function call that should * find the actual arguments at the proper place of the callee. */ alias= (int*) GDKmalloc(sizeof(int) * pci->maxarg); resB = (int*) GDKmalloc(sizeof(int) * pci->retc); if (alias == NULL || resB == NULL) { GDKfree(alias); GDKfree(resB); return NULL; } /* resB := new(refBat) */ for (i = 0; i < pci->retc; i++) { q = newFcnCall(mb, batRef, newRef); resB[i] = getArg(q, 0); tt = getBatType(getArgType(mb, pci, i)); setVarType(mb, getArg(q, 0), newBatType(tt)); q = pushType(mb, q, tt); } /* barrier (h,r) := iterator.new(refBat); */ q = newFcnCall(mb, iteratorRef, newRef); q->barrier = BARRIERsymbol; hvar = newTmpVariable(mb, TYPE_any); getArg(q,0) = hvar; tvar = newTmpVariable(mb, TYPE_any); q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); /* $1:= algebra.fetch(Ai,h) or constant */ for (i = pci->retc+2; i < pci->argc; i++) { if (getArg(pci, i) != iter && isaBatType(getArgType(mb, pci, i))) { q = newFcnCall(mb, algebraRef, "fetch"); alias[i] = newTmpVariable(mb, getBatType(getArgType(mb, pci, i))); getArg(q, 0) = alias[i]; q= pushArgument(mb, q, getArg(pci, i)); (void) pushArgument(mb, q, hvar); } } /* cr:= mod.CMD($1,...,$n); */ q = newFcnCall(mb, mod, fcn); for (i = 0; i < pci->retc; i++) { int nvar = 0; if (bat) { tt = getBatType(getArgType(mb, pci, i)); nvar = newTmpVariable(mb, newBatType(tt)); } else { nvar = newTmpVariable(mb, TYPE_any); } if (i) q = pushReturn(mb, q, nvar); else getArg(q, 0) = nvar; } for (i = pci->retc+2; i < pci->argc; i++) { if (getArg(pci, i) == iter) { q = pushArgument(mb, q, tvar); } else if (isaBatType(getArgType(mb, pci, i))) { q = pushArgument(mb, q, alias[i]); } else { q = pushArgument(mb, q, getArg(pci, i)); } } for (i = 0; i < pci->retc; i++) { InstrPtr a = newFcnCall(mb, batRef, appendRef); a = pushArgument(mb, a, resB[i]); (void) pushArgument(mb, a, getArg(q,i)); } /* redo (h,r):= iterator.next(refBat); */ q = newFcnCall(mb, iteratorRef, nextRef); q->barrier = REDOsymbol; getArg(q,0) = hvar; q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); q = newAssignment(mb); q->barrier = EXITsymbol; getArg(q,0) = hvar; (void) pushReturn(mb, q, tvar); for (i = 0; i < pci->retc; i++) { q = newAssignment(mb); getArg(q, 0) = getArg(pci, i); (void) pushArgument(mb, q, resB[i]); } GDKfree(alias); GDKfree(resB); return MAL_SUCCEED; }
int OPTrecyclerImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, j, cnt, cand, actions = 1, marks = 0; InstrPtr *old, q,p; int limit; char *recycled; (void) cntxt; (void) stk; (void) pci; limit = mb->stop; old = mb->stmt; /* watch out, newly created instructions may introduce new variables */ recycled = GDKzalloc(sizeof(char) * mb->vtop * 2); if (recycled == NULL) return 0; if (newMalBlkStmt(mb, mb->ssize) < 0) { GDKfree(recycled); return 0; } pushInstruction(mb, old[0]); for (i = 1; i < limit; i++) { p = old[i]; if (p->token == ENDsymbol ) break; /* the first non-dataflow barrier breaks the recycler code*/ if (blockStart(p) && !(getFunctionId(p) && getFunctionId(p) == dataflowRef) ) break; if ( isUpdateInstruction(p) || hasSideEffects(p,TRUE)){ /* update instructions are not recycled but monitored*/ pushInstruction(mb, p); if (isUpdateInstruction(p)) { if (getModuleId(p) == batRef && isaBatType(getArgType(mb, p, 1))) { q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, getArg(p, 1)); actions++; } if (getModuleId(p) == sqlRef) { q= copyInstruction(p); getModuleId(q) = recycleRef; actions++; } } continue; } // Not all instruction may be recycled. In particular, we should avoid // MAL function with implicit/recursive side effects. // This can not always be detected easily. Likewise, we ignore cheap operations // Therefore, we use a safe subset to start with if ( ! (getModuleId(p) == sqlRef || getModuleId(p)== batRef || getModuleId(p) == algebraRef || getModuleId(p)==batcalcRef || getModuleId(p)== aggrRef || getModuleId(p)== groupRef || getModuleId(p)== batstrRef || getModuleId(p)== batmmathRef || getModuleId(p)== arrayRef || getModuleId(p)== batmtimeRef || getModuleId(p)== batcalcRef || getModuleId(p)== pcreRef || getModuleId(p)== mtimeRef || getModuleId(p) == calcRef || getModuleId(p)== dateRef || getModuleId(p) == timestampRef || getModuleId(p)== matRef ) ){ pushInstruction(mb,p); continue; } /* general rule: all arguments should be constants or recycled*/ cnt = 0; for (j = p->retc; j < p->argc; j++) if (recycled[getArg(p, j)] || isVarConstant(mb, getArg(p, j)) || isFunctionArgument(mb,getArg(p,j)) ) cnt++; cand = 0; for (j =0; j< p->retc; j++) if (recycled[getArg(p, j)] ==0) cand++; if (cnt == p->argc - p->retc && cand == p->retc) { marks++; p->recycle = RECYCLING; /* this instruction is to be monitored */ for (j = 0; j < p->retc; j++) recycled[getArg(p, j)] = 1; } pushInstruction(mb, p); } for (; i < limit; i++) pushInstruction(mb, old[i]); GDKfree(old); GDKfree(recycled); mb->recycle = marks > 0; OPTDEBUGrecycle { mnstr_printf(cntxt->fdout, "#recycle optimizer: "); printFunction(cntxt->fdout,mb, 0, LIST_MAL_ALL); } return actions + marks; }