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_evaluate.h"
11#include "opt_aliases.h"
12
13static int
14OPTallConstant(Client cntxt, MalBlkPtr mb, InstrPtr p)
15{
16 int i;
17 (void)cntxt;
18
19 if ( !(p->token == ASSIGNsymbol ||
20 getModuleId(p) == calcRef ||
21 getModuleId(p) == strRef ||
22 getModuleId(p) == mtimeRef ||
23 getModuleId(p) == mmathRef))
24 return FALSE;
25 if (getModuleId(p) == mmathRef && strcmp(getFunctionId(p), "rand") == 0)
26 return FALSE;
27
28 for (i = p->retc; i < p->argc; i++)
29 if (isVarConstant(mb, getArg(p, i)) == FALSE)
30 return FALSE;
31 for (i = 0; i < p->retc; i++) {
32 if (isaBatType(getArgType(mb, p, i)))
33 return FALSE;
34 if ( mb->unsafeProp )
35 return FALSE;
36 }
37 return TRUE;
38}
39
40static int OPTsimpleflow(MalBlkPtr mb, int pc)
41{
42 int i, block =0, simple= TRUE;
43 InstrPtr p;
44
45 for ( i= pc; i< mb->stop; i++){
46 p =getInstrPtr(mb,i);
47 if (blockStart(p))
48 block++;
49 if ( blockExit(p))
50 block--;
51 if ( blockCntrl(p))
52 simple= FALSE;
53 if ( block == 0){
54 return simple;
55 }
56 }
57 return FALSE;
58}
59
60/* barrier blocks can only be dropped when they are fully excluded. */
61static str
62OPTremoveUnusedBlocks(Client cntxt, MalBlkPtr mb)
63{
64 /* catch and remove constant bounded blocks */
65 int i, j = 0, action = 0, block = -1, skip = 0, multipass = 1;
66 InstrPtr p;
67 str msg = MAL_SUCCEED;
68
69 while(multipass--){
70 block = -1;
71 skip = 0;
72 j = 0;
73 for (i = 0; i < mb->stop; i++) {
74 p = mb->stmt[i];
75 if (blockExit(p) && block == getArg(p,0) ){
76 block = -1;
77 skip = 0;
78 freeInstruction(p);
79 mb->stmt[i]= 0;
80 continue;
81 }
82 if (p->argc == 2 && blockStart(p) && block < 0 && isVarConstant(mb, getArg(p, 1)) && getArgType(mb, p, 1) == TYPE_bit ){
83 if( getVarConstant(mb, getArg(p, 1)).val.btval == 0)
84 {
85 block = getArg(p,0);
86 skip ++;
87 action++;
88 }
89 // Try to remove the barrier statement itself (when true).
90 if ( getVarConstant(mb, getArg(p, 1)).val.btval == 1 && OPTsimpleflow(mb,i))
91 {
92 block = getArg(p,0);
93 skip = 0;
94 action++;
95 freeInstruction(p);
96 mb->stmt[i]= 0;
97 continue;
98 }
99 } else
100 if( p->argc == 2 && blockStart(p) && block >= 0 && skip == 0 && isVarConstant(mb, getArg(p, 1)) && getArgType(mb, p, 1) == TYPE_bit && multipass == 0)
101 multipass++;
102 if (skip){
103 freeInstruction(p);
104 mb->stmt[i]= 0;
105 } else
106 mb->stmt[j++] = p;
107 }
108 mb->stop = j;
109 for (; j < i; j++)
110 mb->stmt[j] = NULL;
111 }
112 if (action)
113 chkTypes(cntxt->usermodule, mb, TRUE);
114 return msg;
115}
116
117str
118OPTevaluateImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
119{
120 InstrPtr p;
121 int i, k, limit, *alias = 0, barrier;
122 MalStkPtr env = NULL;
123 int profiler, sqlprofiler;
124 int debugstate = cntxt->itrace, actions = 0, constantblock = 0;
125 int *assigned = 0, use;
126 char buf[256];
127 lng usec = GDKusec();
128 str msg = MAL_SUCCEED;
129
130 (void)stk;
131 (void)pci;
132
133 if ( mb->inlineProp )
134 return MAL_SUCCEED;
135
136 cntxt->itrace = 0;
137
138 if( OPTdebug & OPTevaluate){
139 fprintf(stderr, "Constant expression optimizer started\n");
140 }
141
142 assigned = (int*) GDKzalloc(sizeof(int) * mb->vtop);
143 if (assigned == NULL)
144 throw(MAL,"optimzier.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL);
145
146 alias = (int*)GDKzalloc(mb->vsize * sizeof(int) * 2); /* we introduce more */
147 if (alias == NULL){
148 GDKfree(assigned);
149 throw(MAL,"optimzier.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL);
150 }
151
152 // arguments are implicitly assigned by context
153 p = getInstrPtr(mb, 0);
154 for ( k =p->retc; k < p->argc; k++)
155 assigned[getArg(p,k)]++;
156 limit = mb->stop;
157 for (i = 1; i < limit; i++) {
158 p = getInstrPtr(mb, i);
159 // The double count emerging from a barrier exit is ignored.
160 if (! blockExit(p) || (blockExit(p) && p->retc != p->argc))
161 for ( k =0; k < p->retc; k++)
162 if ( p->retc != p->argc || p->token != ASSIGNsymbol )
163 assigned[getArg(p,k)]++;
164 }
165
166 for (i = 1; i < limit && cntxt->mode != FINISHCLIENT; i++) {
167 p = getInstrPtr(mb, i);
168 // to avoid management of duplicate assignments over multiple blocks
169 // we limit ourselves to evaluation of the first assignment only.
170 use = assigned[getArg(p,0)] == 1 && !(p->argc == p->retc && blockExit(p));
171 for (k = p->retc; k < p->argc; k++)
172 if (alias[getArg(p, k)])
173 getArg(p, k) = alias[getArg(p, k)];
174 if( OPTdebug & OPTevaluate){
175 fprintInstruction(stderr , mb, 0, p, LIST_MAL_ALL);
176 }
177 /* be aware that you only assign once to a variable */
178 if (use && p->retc == 1 && OPTallConstant(cntxt, mb, p) && !isUnsafeFunction(p)) {
179 barrier = p->barrier;
180 p->barrier = 0;
181 profiler = malProfileMode; /* we don't trace it */
182 sqlprofiler = cntxt->sqlprofiler;
183 malProfileMode = 0;
184 if ( env == NULL) {
185 env = prepareMALstack(mb, 2 * mb->vsize);
186 if (!env) {
187 msg = createException(MAL,"optimizer.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL);
188 goto wrapup;
189 }
190 env->keepAlive = TRUE;
191 }
192 msg = reenterMAL(cntxt, mb, i, i + 1, env);
193 malProfileMode= profiler;
194 cntxt->sqlprofiler = sqlprofiler;
195 p->barrier = barrier;
196 if( OPTdebug & OPTevaluate){
197 fprintf(stderr, "#retc var %s\n", getVarName(mb, getArg(p, 0)));
198 fprintf(stderr, "#result:%s\n", msg == MAL_SUCCEED ? "ok" : msg);
199 }
200 if (msg == MAL_SUCCEED) {
201 int nvar;
202 ValRecord cst;
203
204 actions++;
205 cst.vtype = 0;
206 VALcopy(&cst, &env->stk[getArg(p, 0)]);
207 /* You may not overwrite constants. They may be used by
208 * other instructions */
209 nvar = getArg(p, 1) = defConstant(mb, getArgType(mb, p, 0), &cst);
210 if (nvar >= env->stktop) {
211 VALcopy(&env->stk[getArg(p, 1)], &getVarConstant(mb, getArg(p, 1)));
212 env->stktop = getArg(p, 1) + 1;
213 }
214 alias[getArg(p, 0)] = getArg(p, 1);
215 p->argc = 2;
216 p->token = ASSIGNsymbol;
217 clrFunction(p);
218 p->barrier = barrier;
219 /* freeze the type */
220 setVarFixed(mb,getArg(p,1));
221 setVarUDFtype(mb,getArg(p,1));
222 if( OPTdebug & OPTevaluate){
223 str tpename;
224 fprintf(stderr, "Evaluated new constant=%d -> %d:%s\n",
225 getArg(p, 0), getArg(p, 1), tpename = getTypeName(getArgType(mb, p, 1)));
226 GDKfree(tpename);
227 }
228 } else {
229 /* if there is an error, we should postpone message handling,
230 as the actual error (eg. division by zero ) may not happen) */
231 if( OPTdebug & OPTevaluate){
232 fprintf(stderr, "Evaluated %s\n", msg);
233 }
234 freeException(msg);
235 msg= MAL_SUCCEED;
236 mb->errors = 0;
237 }
238 }
239 constantblock += blockStart(p) && OPTallConstant(cntxt, mb, p); /* default */
240 }
241 // produces errors in SQL when enabled
242 if ( constantblock)
243 msg = OPTremoveUnusedBlocks(cntxt, mb);
244 cntxt->itrace = debugstate;
245
246 /* Defense line against incorrect plans */
247 /* Plan is unaffected */
248 chkTypes(cntxt->usermodule, mb, FALSE);
249 chkFlow(mb);
250 chkDeclarations(mb);
251
252 /* keep all actions taken as a post block comment */
253 usec = GDKusec()- usec;
254 snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec","evaluate",actions,usec);
255 newComment(mb,buf);
256 if( actions >= 0)
257 addtoMalBlkHistory(mb);
258
259wrapup:
260 if ( env) freeStack(env);
261 if(assigned) GDKfree(assigned);
262 if(alias) GDKfree(alias);
263 if( OPTdebug & OPTevaluate){
264 fprintf(stderr, "#EVALUATE optimizer exit\n");
265 fprintFunction(stderr, mb, 0, LIST_MAL_ALL);
266 }
267 return msg;
268}
269