Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "opt_multiplex.h"
15 : #include "manifold.h"
16 : #include "mal_interpreter.h"
17 :
18 : /*
19 : * The generic solution to the multiplex operators is to translate
20 : * them to a MAL loop.
21 : * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code
22 : * structure:
23 : *
24 : * resB:= bat.new(restype, A1);
25 : * barrier (h,t1):= iterator.new(A1);
26 : * t2:= algebra.fetch(A2,h)
27 : * ...
28 : * cr:= MOD.FCN(t1,...,tn);
29 : * bat.append(resB,cr);
30 : * redo (h,t):= iterator.next(A1);
31 : * end h;
32 : *
33 : * The algorithm consists of two phases: phase one deals with
34 : * collecting the relevant information, phase two is the actual
35 : * code construction.
36 : */
37 : static str
38 3945 : OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
39 : {
40 3945 : int i = 2, iter = 0;
41 3945 : int hvar, tvar;
42 3945 : const char *mod, *fcn;
43 3945 : int *alias, *resB;
44 3945 : InstrPtr q;
45 3945 : int tt;
46 3945 : int bat = (getModuleId(pci) == batmalRef);
47 :
48 3945 : (void) cntxt;
49 3945 : (void) stk;
50 7923 : for (i = 0; i < pci->retc; i++) {
51 3978 : tt = getBatType(getArgType(mb, pci, i));
52 3978 : if (tt == TYPE_any)
53 0 : throw(MAL, "optimizer.multiplex",
54 : SQLSTATE(HY002) "Target tail type is missing");
55 3978 : if (isAnyExpression(getArgType(mb, pci, i)))
56 0 : throw(MAL, "optimizer.multiplex",
57 : SQLSTATE(HY002) "Target type is missing");
58 : }
59 3945 : int plus_one = getArgType(mb, pci, pci->retc) == TYPE_lng ? 1 : 0;
60 3945 : mod = VALget(&getVar(mb, getArg(pci, pci->retc + plus_one))->value);
61 3945 : mod = putName(mod);
62 3945 : fcn = VALget(&getVar(mb, getArg(pci, pci->retc + 1 + plus_one))->value);
63 3945 : fcn = putName(fcn);
64 3945 : if (mod == NULL || fcn == NULL)
65 0 : throw(MAL, "optimizer.multiplex", SQLSTATE(HY013) MAL_MALLOC_FAIL);
66 :
67 : #ifndef NDEBUG
68 : TRC_WARNING_IF(MAL_OPTIMIZER) {
69 3945 : char *ps = instruction2str(mb, stk, pci, LIST_MAL_DEBUG);
70 3945 : TRC_WARNING_ENDIF(MAL_OPTIMIZER,
71 : "To speedup %s.%s a bulk operator implementation is needed%s%s\n",
72 : mod, fcn, ps ? " for " : "", ps ? ps : "");
73 3945 : GDKfree(ps);
74 : }
75 : #endif
76 :
77 3945 : if (plus_one) {
78 0 : q = newFcnCallArgs(mb, batRef, putName("densebat"), 2);
79 0 : if (q == NULL) {
80 0 : throw(MAL, "optimizer.multiplex", SQLSTATE(HY013) MAL_MALLOC_FAIL);
81 : }
82 0 : q = pushArgument(mb, q, getArg(pci, pci->retc));
83 0 : pushInstruction(mb, q);
84 0 : iter = getArg(q, 0);
85 : } else /* search the iterator bat */
86 3957 : for (i = pci->retc + 2; i < pci->argc; i++)
87 3957 : if (isaBatType(getArgType(mb, pci, i))) {
88 : iter = getArg(pci, i);
89 : break;
90 : }
91 3945 : if (i == pci->argc)
92 0 : throw(MAL, "optimizer.multiplex",
93 : SQLSTATE(HY002) "Iterator BAT type is missing");
94 :
95 : /*
96 : * Beware, the operator constant (arg=1) is passed along as well,
97 : * because in the end we issue a recursive function call that should
98 : * find the actual arguments at the proper place of the callee.
99 : */
100 :
101 3945 : alias = (int *) GDKmalloc(sizeof(int) * pci->maxarg);
102 3945 : resB = (int *) GDKmalloc(sizeof(int) * pci->retc);
103 3945 : if (alias == NULL || resB == NULL) {
104 0 : goto nomem;
105 : }
106 :
107 : /* resB := new(refBat) */
108 7923 : for (i = 0; i < pci->retc; i++) {
109 3978 : q = newFcnCallArgs(mb, batRef, newRef, 3);
110 3978 : if (q == NULL) {
111 0 : goto nomem;
112 : }
113 3978 : resB[i] = getArg(q, 0);
114 :
115 3978 : tt = getBatType(getArgType(mb, pci, i));
116 :
117 3978 : setVarType(mb, getArg(q, 0), newBatType(tt));
118 3978 : q = pushType(mb, q, tt);
119 3978 : q = pushArgument(mb, q, iter);
120 3978 : pushInstruction(mb, q);
121 3978 : assert(q->argc == 3);
122 : }
123 :
124 : /* barrier (h,r) := iterator.new(refBat); */
125 3945 : q = newFcnCall(mb, iteratorRef, newRef);
126 3945 : if (q == NULL) {
127 0 : goto nomem;
128 : }
129 3945 : q->barrier = BARRIERsymbol;
130 3945 : hvar = newTmpVariable(mb, TYPE_any);
131 3945 : getArg(q, 0) = hvar;
132 3945 : tvar = newTmpVariable(mb, TYPE_any);
133 3945 : q = pushReturn(mb, q, tvar);
134 3945 : q = pushArgument(mb, q, iter);
135 3945 : pushInstruction(mb, q);
136 :
137 : /* $1:= algebra.fetch(Ai,h) or constant */
138 11827 : for (i = pci->retc + 2 + plus_one; i < pci->argc; i++) {
139 7882 : if (getArg(pci, i) != iter &&isaBatType(getArgType(mb, pci, i))) {
140 3740 : q = newFcnCall(mb, algebraRef, "fetch");
141 3740 : if (q == NULL) {
142 0 : goto nomem;
143 : }
144 3740 : alias[i] = newTmpVariable(mb, getBatType(getArgType(mb, pci, i)));
145 3740 : getArg(q, 0) = alias[i];
146 3740 : q = pushArgument(mb, q, getArg(pci, i));
147 3740 : q = pushArgument(mb, q, hvar);
148 3740 : pushInstruction(mb, q);
149 : }
150 : }
151 :
152 : /* cr:= mod.CMD($1,...,$n); */
153 3945 : q = newFcnCallArgs(mb, mod, fcn, pci->argc - 2 - plus_one);
154 3945 : if (q == NULL) {
155 0 : goto nomem;
156 : }
157 7923 : for (i = 0; i < pci->retc; i++) {
158 3978 : int nvar = 0;
159 3978 : if (bat) {
160 39 : tt = getBatType(getArgType(mb, pci, i));
161 39 : nvar = newTmpVariable(mb, newBatType(tt));
162 : } else {
163 3939 : nvar = newTmpVariable(mb, TYPE_any);
164 : }
165 3978 : if (i)
166 33 : q = pushReturn(mb, q, nvar);
167 : else
168 3945 : getArg(q, 0) = nvar;
169 : }
170 :
171 11827 : for (i = pci->retc + 2 + plus_one; i < pci->argc; i++) {
172 7882 : if (getArg(pci, i) == iter) {
173 3948 : q = pushArgument(mb, q, tvar);
174 3934 : } else if (isaBatType(getArgType(mb, pci, i))) {
175 3740 : q = pushArgument(mb, q, alias[i]);
176 : } else {
177 194 : q = pushArgument(mb, q, getArg(pci, i));
178 : }
179 : }
180 3945 : pushInstruction(mb, q);
181 :
182 11868 : for (i = 0; i < pci->retc; i++) {
183 3978 : InstrPtr a = newFcnCall(mb, batRef, appendRef);
184 3978 : if (a == NULL) {
185 0 : goto nomem;
186 : }
187 3978 : a = pushArgument(mb, a, resB[i]);
188 3978 : a = pushArgument(mb, a, getArg(q, i));
189 3978 : getArg(a, 0) = resB[i];
190 3978 : pushInstruction(mb, a);
191 : }
192 :
193 : /* redo (h,r):= iterator.next(refBat); */
194 3945 : q = newFcnCall(mb, iteratorRef, nextRef);
195 3945 : if (q == NULL) {
196 0 : goto nomem;
197 : }
198 3945 : q->barrier = REDOsymbol;
199 3945 : getArg(q, 0) = hvar;
200 3945 : q = pushReturn(mb, q, tvar);
201 3945 : q = pushArgument(mb, q, iter);
202 3945 : pushInstruction(mb, q);
203 :
204 3945 : q = newAssignment(mb);
205 3945 : if (q == NULL) {
206 0 : goto nomem;
207 : }
208 3945 : q->barrier = EXITsymbol;
209 3945 : getArg(q, 0) = hvar;
210 3945 : q = pushReturn(mb, q, tvar);
211 3945 : pushInstruction(mb, q);
212 :
213 11868 : for (i = 0; i < pci->retc; i++) {
214 3978 : q = newAssignment(mb);
215 3978 : if (q == NULL) {
216 0 : goto nomem;
217 : }
218 3978 : getArg(q, 0) = getArg(pci, i);
219 3978 : q = pushArgument(mb, q, resB[i]);
220 3978 : pushInstruction(mb, q);
221 : }
222 3945 : GDKfree(alias);
223 3945 : GDKfree(resB);
224 3945 : return MAL_SUCCEED;
225 :
226 0 : nomem:
227 0 : GDKfree(alias);
228 0 : GDKfree(resB);
229 0 : throw(MAL, "optimizer.multiplex", SQLSTATE(HY013) MAL_MALLOC_FAIL);
230 : }
231 :
232 : /*
233 : * The multiplexSimple is called by the MAL scenario. It bypasses
234 : * the optimizer infrastructure, to avoid excessive space allocation
235 : * and interpretation overhead.
236 : */
237 : str
238 7 : OPTmultiplexSimple(Client cntxt, MalBlkPtr mb)
239 : {
240 7 : int i, doit = 0;
241 7 : InstrPtr p;
242 7 : str msg = MAL_SUCCEED;
243 :
244 7 : if (mb)
245 1289 : for (i = 0; i < mb->stop; i++) {
246 1282 : p = getInstrPtr(mb, i);
247 1282 : if (isMultiplex(p)) {
248 0 : p->typeresolved = false;
249 0 : doit++;
250 : }
251 : }
252 7 : if (doit) {
253 0 : msg = OPTmultiplexImplementation(cntxt, mb, 0, 0);
254 0 : if (!msg)
255 0 : msg = chkTypes(cntxt->usermodule, mb, TRUE);
256 0 : if (!msg)
257 0 : msg = chkFlow(mb);
258 0 : if (!msg)
259 0 : msg = chkDeclarations(mb);
260 : }
261 7 : return msg;
262 : }
263 :
264 : str
265 465055 : OPTmultiplexImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk,
266 : InstrPtr pci)
267 : {
268 465055 : InstrPtr *old = 0, p;
269 465055 : int i, limit, slimit, actions = 0;
270 465055 : str msg = MAL_SUCCEED;
271 :
272 465055 : (void) stk;
273 27817986 : for (i = 0; i < mb->stop; i++) {
274 27353819 : p = getInstrPtr(mb, i);
275 27353819 : if (isMultiplex(p)) {
276 : break;
277 : }
278 : }
279 465067 : if (i == mb->stop) {
280 464167 : goto wrapup;
281 : }
282 :
283 900 : old = mb->stmt;
284 900 : limit = mb->stop;
285 900 : slimit = mb->ssize;
286 900 : if (newMalBlkStmt(mb, mb->ssize) < 0)
287 0 : throw(MAL, "optimizer.multiplex", SQLSTATE(HY013) MAL_MALLOC_FAIL);
288 :
289 303945 : for (i = 0; i < limit; i++) {
290 303045 : p = old[i];
291 303045 : if (msg == MAL_SUCCEED && isMultiplex(p)) {
292 4705 : if (MANIFOLDtypecheck(cntxt, mb, p, 0) != NULL) {
293 760 : setFunctionId(p, manifoldRef);
294 760 : p->typeresolved = false;
295 760 : pushInstruction(mb, p);
296 760 : actions++;
297 760 : continue;
298 : }
299 3945 : msg = OPTexpandMultiplex(cntxt, mb, stk, p);
300 3945 : if (msg == MAL_SUCCEED) {
301 3945 : freeInstruction(p);
302 3945 : old[i] = 0;
303 3945 : actions++;
304 3945 : continue;
305 : }
306 :
307 0 : pushInstruction(mb, p);
308 0 : actions++;
309 298340 : } else if (old[i])
310 298340 : pushInstruction(mb, p);
311 : }
312 99263 : for (; i < slimit; i++)
313 98363 : if (old[i])
314 0 : pushInstruction(mb, old[i]);
315 900 : GDKfree(old);
316 :
317 : /* Defense line against incorrect plans */
318 900 : if (msg == MAL_SUCCEED && actions > 0) {
319 900 : msg = chkTypes(cntxt->usermodule, mb, FALSE);
320 900 : if (!msg)
321 900 : msg = chkFlow(mb);
322 900 : if (!msg)
323 900 : msg = chkDeclarations(mb);
324 : }
325 0 : wrapup:
326 : /* keep actions taken as a fake argument */
327 465067 : (void) pushInt(mb, pci, actions);
328 :
329 465067 : return msg;
330 : }
|