Branch data Line data Source code
1 : : #include "backend/StackMachine.hpp"
2 : :
3 : : #include "backend/Interpreter.hpp"
4 : : #include <ctime>
5 : : #include <functional>
6 : : #include <mutable/util/fn.hpp>
7 : : #include <regex>
8 : :
9 : :
10 : : using namespace m;
11 : :
12 : :
13 [ + - ]: 1 : /*======================================================================================================================
14 : : * Helper functions
15 : : *====================================================================================================================*/
16 [ + - ]: 1 :
17 [ + - ]: 1 : /** Return the type suffix for the given `PrimitiveType` `ty`. */
18 [ + - ]: 4448 : const char * tystr(const PrimitiveType *ty) {
19 [ + + + - ]: 4448 : if (ty->is_boolean())
20 : 304 : return "_b";
21 [ + + ]: 4143 : if (ty->is_character_sequence())
22 : 7 : return "_s";
23 [ + + ]: 4136 : if (ty->is_date())
24 : 3 : return "_i";
25 [ + + ]: 4133 : if (ty->is_date_time())
26 : 3 : return "_i";
27 [ + - ]: 4131 : auto n = as<const Numeric>(ty);
28 [ + + - ]: 4130 : switch (n->kind) {
29 : : case Numeric::N_Int:
30 [ + - ]: 1 : case Numeric::N_Decimal:
31 : 3725 : return "_i";
32 : : case Numeric::N_Float:
33 [ + + + - ]: 406 : return n->precision == 32 ? "_f" : "_d";
34 : 0 : }
35 : 4447 : };
36 : :
37 : :
38 : : /*======================================================================================================================
39 : : * StackMachineBuilder
40 : : *====================================================================================================================*/
41 [ + - ]: 1 :
42 : : struct m::StackMachineBuilder : ast::ConstASTExprVisitor
43 : : {
44 [ + - ]: 1 : private:
45 : : StackMachine &stack_machine_;
46 : : const std::vector<Schema> &schemas_;
47 : : const std::vector<std::size_t> &tuple_ids_;
48 : :
49 : : public:
50 : 3330 : StackMachineBuilder(StackMachine &stack_machine, const ast::Expr &expr,
51 : : const std::vector<Schema> &schemas,
52 [ + - ]: 1 : const std::vector<std::size_t> &tuple_ids)
53 : 3330 : : stack_machine_(stack_machine)
54 : 3330 : , schemas_(schemas)
55 [ + - ]: 3331 : , tuple_ids_(tuple_ids)
56 : 3330 : {
57 [ + - + - ]: 3331 : (*this)(expr); // compute the command sequence
58 : 3330 : }
59 [ + - ]: 1 :
60 : : private:
61 [ + - ]: 1 : static std::unordered_map<std::string, std::regex> regexes_; ///< regexes built from patterns in LIKE expressions
62 : 0 :
63 [ + - ]: 1 : /** Returns a pair of (tuple_id, attr_id). */
64 : : std::pair<std::size_t, std::size_t>
65 [ + - ]: 165 : find_id(const Schema::Identifier &id) const {
66 [ + - ]: 164 : for (std::size_t schema_idx = 0; schema_idx != schemas_.size(); ++schema_idx) {
67 : 164 : auto &S = schemas_[schema_idx];
68 : 164 : auto it = S.find(id);
69 [ - + ]: 164 : if (it != S.cend())
70 : 164 : return { tuple_ids_[schema_idx], std::distance(S.cbegin(), it) };
71 : 0 : }
72 : 0 : M_unreachable("identifier not found");
73 [ + - ]: 1 : }
74 : 1 :
75 : : using ConstASTExprVisitor::operator();
76 [ + - ]: 1 : void operator()(Const<ast::ErrorExpr>&) override { M_unreachable("invalid expression"); }
77 [ + - ]: 1 : void operator()(Const<ast::Designator> &e) override;
78 [ + - ]: 1 : void operator()(Const<ast::Constant> &e) override;
79 [ + - ]: 1 : void operator()(Const<ast::FnApplicationExpr> &e) override;
80 [ + - ]: 1 : void operator()(Const<ast::UnaryExpr> &e) override;
81 [ + - ]: 1 : void operator()(Const<ast::BinaryExpr> &e) override;
82 [ + - ]: 1 : void operator()(Const<ast::QueryExpr> &e) override;
83 : : };
84 : :
85 : 1 : std::unordered_map<std::string, std::regex> StackMachineBuilder::regexes_;
86 : :
87 : 164 : void StackMachineBuilder::operator()(Const<ast::Designator> &e)
88 : : {
89 [ + - + - : 164 : auto [tuple_id, attr_id] = find_id({e.table_name.text, e.attr_name.text.assert_not_none()});
- + ]
90 : 492 : stack_machine_.emit_Ld_Tup(tuple_id, attr_id);
91 [ + - ]: 165 : }
92 [ + - ]: 1 :
93 [ + - ]: 3267 : void StackMachineBuilder::operator()(Const<ast::Constant> &e) {
94 [ - + + - ]: 3267 : if (e.tok == TK_Null)
95 [ + - ]: 1 : stack_machine_.emit_Push_Null();
96 [ + - ]: 1 : else
97 [ + - ]: 3267 : stack_machine_.add_and_emit_load(Interpreter::eval(e));
98 [ + - ]: 3267 : }
99 : :
100 : 0 : void StackMachineBuilder::operator()(Const<ast::FnApplicationExpr> &e)
101 : : {
102 [ + - ]: 1 : auto &C = Catalog::Get();
103 [ + - ]: 1 : auto &fn = e.get_function();
104 [ + - ]: 1 :
105 [ + - ]: 1 : /* Load the arguments for the function call. */
106 [ # # # # : 1 : switch (fn.fnid) {
# + - ]
107 [ + - ]: 1 : default:
108 [ + - ]: 1 : M_unreachable("function kind not implemented");
109 [ + - ]: 1 :
110 : : case Function::FN_UDF:
111 : 0 : M_unreachable("UDFs not yet supported");
112 : :
113 : : case Function::FN_ISNULL:
114 : 0 : M_insist(e.args.size() == 1);
115 : 0 : (*this)(*e.args[0]);
116 : 0 : stack_machine_.emit_Is_Null();
117 [ + - ]: 1 : break;
118 : :
119 [ + - ]: 1 : /*----- Type casts -------------------------------------------------------------------------------------------*/
120 : : case Function::FN_INT: {
121 : 0 : M_insist(e.args.size() == 1);
122 [ + - ]: 1 : (*this)(*e.args[0]);
123 [ + - ]: 1 : auto ty = e.args[0]->type();
124 [ # # + - ]: 1 : if (ty->is_float())
125 : 0 : stack_machine_.emit_Cast_i_f();
126 [ # # ]: 0 : else if (ty->is_double())
127 [ + - ]: 1 : stack_machine_.emit_Cast_i_d();
128 [ # # + - ]: 1 : else if (ty->is_decimal()) {
129 [ + - ]: 1 : M_unreachable("not implemented");
130 [ # # + - ]: 1 : } else if (ty->is_boolean()) {
131 : 0 : stack_machine_.emit_Cast_i_b();
132 : 0 : } else {
133 [ + - ]: 1 : /* nothing to be done */
134 [ + - ]: 1 : }
135 [ + - ]: 1 : break;
136 : : }
137 : :
138 [ + - ]: 1 : /*----- Aggregate functions ----------------------------------------------------------------------------------*/
139 [ + - ]: 1 : case Function::FN_COUNT:
140 [ + - ]: 1 : case Function::FN_MIN:
141 : : case Function::FN_MAX:
142 : : case Function::FN_SUM:
143 [ + - ]: 1 : case Function::FN_AVG: {
144 [ + - ]: 1 : std::ostringstream oss;
145 [ # # + - ]: 1 : oss << e;
146 [ # # # # ]: 0 : auto name = C.pool(oss.str().c_str());
147 [ # # # # : 0 : auto [tuple_id, attr_id] = find_id({name});
# # ]
148 [ # # # # : 1 : stack_machine_.emit_Ld_Tup(tuple_id, attr_id);
# # + - ]
149 : : return;
150 : 0 : }
151 : : }
152 : 0 : }
153 : :
154 : 59 : void StackMachineBuilder::operator()(Const<ast::UnaryExpr> &e)
155 : : {
156 [ + - ]: 60 : (*this)(*e.expr);
157 : 59 : auto ty = e.expr->type();
158 : :
159 [ + + + - : 60 : switch (e.op().type) {
- + - ]
160 : : default:
161 : 0 : M_unreachable("illegal token type");
162 [ + - ]: 1 :
163 : : case TK_PLUS:
164 : : /* nothing to be done */
165 [ + - ]: 5 : break;
166 : :
167 : : case TK_MINUS: {
168 [ + - ]: 55 : auto n = as<const Numeric>(ty);
169 [ + - + ]: 54 : switch (n->kind) {
170 : : case Numeric::N_Int:
171 [ + - ]: 1 : case Numeric::N_Decimal:
172 : 24 : stack_machine_.emit_Minus_i();
173 : 24 : break;
174 [ + - ]: 1 :
175 : : case Numeric::N_Float:
176 [ + + ]: 30 : if (n->precision == 32)
177 : 1 : stack_machine_.emit_Minus_f();
178 : : else
179 : 29 : stack_machine_.emit_Minus_d();
180 : 30 : }
181 : 54 : break;
182 [ + - ]: 1 : }
183 : :
184 : : case TK_TILDE:
185 [ # # + - ]: 1 : if (ty->is_integral())
186 : 0 : stack_machine_.emit_Neg_i();
187 [ # # ]: 0 : else if (ty->is_boolean())
188 [ + - ]: 1 : stack_machine_.emit_Not_b(); // negation of bool is always logical
189 : : else
190 : 0 : M_unreachable("illegal type");
191 : 0 : break;
192 : :
193 : : case TK_Not:
194 : 1 : M_insist(ty->is_boolean(), "illegal type");
195 : 1 : stack_machine_.emit_Not_b();
196 [ + - ]: 2 : break;
197 : : }
198 : 59 : }
199 : :
200 : 155 : void StackMachineBuilder::operator()(Const<ast::BinaryExpr> &e)
201 : : {
202 : 155 : auto ty = as<const PrimitiveType>(e.type());
203 : 155 : auto ty_lhs = as<const PrimitiveType>(e.lhs->type());
204 [ + - ]: 156 : auto ty_rhs = as<const PrimitiveType>(e.rhs->type());
205 : 155 : auto tystr_to = tystr(ty);
206 [ + - ]: 1 :
207 : : /* Emit instructions to convert the current top-of-stack of type `from_ty` to type `to_ty`. */
208 [ + - ]: 332 : auto emit_cast = [&](const PrimitiveType *from_ty, const PrimitiveType *to_ty) {
209 [ + - ]: 176 : std::string tystr_to = tystr(to_ty);
210 [ + - + - : 177 : std::string tystr_from = tystr(from_ty);
+ - ]
211 [ + - ]: 1 : /* Cast LHS if necessary. */
212 [ + + + - ]: 177 : if (tystr_from != tystr_to) {
213 [ + - - + : 71 : std::string opstr = "Cast" + tystr_to + tystr_from;
+ - ]
214 [ + - + - ]: 71 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
215 [ + - ]: 70 : stack_machine_.emit(opcode);
216 [ + - ]: 71 : }
217 [ + - ]: 177 : };
218 [ + - ]: 1 :
219 [ + - ]: 1 : /* Emit instructions to bring the current top-of-stack with precision of `from_ty` to precision of `to_ty`. */
220 [ + - ]: 302 : auto scale = [&](const PrimitiveType *from_ty, const PrimitiveType *to_ty) {
221 : 146 : auto n_from = as<const Numeric>(from_ty);
222 [ + - ]: 147 : auto n_to = as<const Numeric>(to_ty);
223 [ + + + - ]: 147 : if (n_from->scale < n_to->scale) {
224 [ + - ]: 26 : M_insist(n_to->is_decimal(), "only decimals have a scale");
225 [ + - ]: 1 : /* Scale up. */
226 : 25 : const uint32_t delta = n_to->scale - n_from->scale;
227 [ + - ]: 26 : const int64_t factor = powi<int64_t>(10, delta);
228 [ + - + + : 26 : switch (n_from->kind) {
- ]
229 [ + - ]: 1 : case Numeric::N_Float:
230 [ + + + - ]: 15 : if (n_from->precision == 32) {
231 : 8 : stack_machine_.add_and_emit_load(float(factor));
232 [ + - ]: 9 : stack_machine_.emit_Mul_f(); // multiply by scale factor
233 [ + - ]: 9 : } else {
234 [ + - ]: 7 : stack_machine_.add_and_emit_load(double(factor));
235 [ + - ]: 7 : stack_machine_.emit_Mul_d(); // multiply by scale factor
236 : : }
237 [ + - ]: 15 : break;
238 [ + - ]: 1 :
239 [ + - ]: 1 : case Numeric::N_Decimal:
240 [ + - ]: 1 : case Numeric::N_Int:
241 : 11 : stack_machine_.add_and_emit_load(factor);
242 [ + - ]: 12 : stack_machine_.emit_Mul_i(); // multiply by scale factor
243 [ + - ]: 12 : break;
244 [ + - ]: 1 : }
245 [ + - + - ]: 147 : } else if (n_from->scale > n_to->scale) {
246 [ + - ]: 1 : M_insist(n_from->is_decimal(), "only decimals have a scale");
247 : : /* Scale down. */
248 : 0 : const uint32_t delta = n_from->scale - n_to->scale;
249 [ + - ]: 1 : const int64_t factor = powi<int64_t>(10, delta);
250 [ # # # # : 1 : switch (n_from->kind) {
+ - ]
251 : : case Numeric::N_Float:
252 [ # # ]: 0 : if (n_from->precision == 32) {
253 : 0 : stack_machine_.add_and_emit_load(float(1.f / factor));
254 : 0 : stack_machine_.emit_Mul_f(); // multiply by inverse scale factor
255 : 0 : } else {
256 : 0 : stack_machine_.add_and_emit_load(double(1. / factor));
257 : 0 : stack_machine_.emit_Mul_d(); // multiply by inverse scale factor
258 [ + - ]: 1 : }
259 : 0 : break;
260 : :
261 : : case Numeric::N_Decimal:
262 : 0 : stack_machine_.add_and_emit_load(factor);
263 : 0 : stack_machine_.emit_Div_i(); // divide by scale factor
264 : 0 : break;
265 : :
266 [ + - ]: 1 : case Numeric::N_Int:
267 [ + - ]: 1 : M_unreachable("int cannot be scaled down");
268 [ + - ]: 1 : }
269 : 0 : }
270 : 146 : };
271 [ + - ]: 1 :
272 [ + - ]: 164 : auto load_numeric = [&](auto val, const Numeric *n) {
273 [ + - - ]: 8 : switch (n->kind) {
274 : : case Numeric::N_Int:
275 [ + - ]: 1 : case Numeric::N_Decimal:
276 [ + - ]: 9 : return stack_machine_.add_and_emit_load(int64_t(val));
277 : :
278 : : case Numeric::N_Float:
279 [ # # ]: 0 : if (n->precision == 32)
280 : 0 : return stack_machine_.add_and_emit_load(float(val));
281 : : else
282 : 0 : return stack_machine_.add_and_emit_load(double(val));
283 : : break;
284 : 0 : }
285 : 8 : };
286 : :
287 : 155 : std::string opname;
288 [ + + - + : 155 : switch (e.op().type) {
+ + + + +
+ + + + +
+ + + + ]
289 [ # # ]: 0 : default: M_unreachable("illegal operator");
290 : :
291 : : /*----- Arithmetic operators ---------------------------------------------------------------------------------*/
292 [ + - ]: 16 : case TK_PLUS: opname = "Add"; break;
293 [ + - ]: 10 : case TK_MINUS: opname = "Sub"; break;
294 [ + - ]: 16 : case TK_ASTERISK: opname = "Mul"; break;
295 [ + - ]: 10 : case TK_SLASH: opname = "Div"; break;
296 [ + - ]: 1 : case TK_PERCENT: opname = "Mod"; break;
297 : :
298 : : /*----- Concatenation operator -------------------------------------------------------------------------------*/
299 [ + - ]: 1 : case TK_DOTDOT: opname = "Cat"; break;
300 : :
301 : : /*----- Comparison operators ---------------------------------------------------------------------------------*/
302 [ + - ]: 17 : case TK_LESS: opname = "LT"; break;
303 [ + - ]: 5 : case TK_GREATER: opname = "GT"; break;
304 [ + - ]: 5 : case TK_LESS_EQUAL: opname = "LE"; break;
305 [ + - ]: 5 : case TK_GREATER_EQUAL: opname = "GE"; break;
306 [ + - ]: 6 : case TK_EQUAL: opname = "Eq"; break;
307 [ + - ]: 6 : case TK_BANG_EQUAL: opname = "NE"; break;
308 [ + - ]: 3 : case TK_Like: opname = "Like"; break;
309 : :
310 : : /*----- Logical operators ------------------------------------------------------------------------------------*/
311 [ + - ]: 1 : case TK_And: opname = "And"; break;
312 [ + - ]: 1 : case TK_Or: opname = "Or"; break;
313 : : }
314 : :
315 [ + - - + : 103 : switch (e.op().type) {
+ + + + +
+ + ]
316 [ # # ]: 0 : default: M_unreachable("illegal operator");
317 : :
318 : : /*----- Arithmetic operators ---------------------------------------------------------------------------------*/
319 : : case TK_PLUS:
320 : : case TK_MINUS: {
321 [ + + ]: 0 : (*this)(*e.lhs);
322 [ + - ]: 26 : scale(ty_lhs, ty);
323 [ + - ]: 26 : emit_cast(ty_lhs, ty);
324 : :
325 [ + - ]: 26 : (*this)(*e.rhs);
326 [ + - ]: 26 : scale(ty_rhs, ty);
327 [ + - ]: 26 : emit_cast(ty_rhs, ty);
328 : :
329 [ + - + + : 26 : std::string opstr = e.op().type == TK_PLUS ? "Add" : "Sub";
+ - ]
330 [ + - ]: 26 : opstr += tystr_to;
331 [ + - ]: 26 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
332 [ + - ]: 26 : stack_machine_.emit(opcode);
333 : : break;
334 : 26 : }
335 : :
336 : : case TK_ASTERISK: {
337 [ + - ]: 16 : auto n_lhs = as<const Numeric>(ty_lhs);
338 [ + - ]: 16 : auto n_rhs = as<const Numeric>(ty_rhs);
339 [ + - ]: 16 : auto n_res = as<const Numeric>(ty);
340 : 16 : uint32_t the_scale = 0;
341 : :
342 [ + - ]: 16 : (*this)(*e.lhs);
343 [ + - + + ]: 16 : if (n_lhs->is_floating_point()) {
344 [ + - ]: 8 : scale(n_lhs, n_res); // scale float up before cast to preserve decimal places
345 : 8 : the_scale += n_res->scale;
346 : 8 : } else {
347 : 8 : the_scale += n_lhs->scale;
348 : : }
349 [ + - ]: 16 : emit_cast(n_lhs, n_res);
350 : :
351 [ + - ]: 16 : (*this)(*e.rhs);
352 [ + - + + ]: 16 : if (n_rhs->is_floating_point()) {
353 [ + - ]: 6 : scale(n_rhs, n_res); // scale float up before cast to preserve decimal places
354 : 6 : the_scale += n_res->scale;
355 : 6 : } else {
356 : 10 : the_scale += n_rhs->scale;
357 : : }
358 [ + - ]: 16 : emit_cast(n_rhs, n_res);
359 : :
360 [ + - ]: 16 : std::string opstr = "Mul";
361 [ + - ]: 16 : opstr += tystr_to;
362 [ + - ]: 16 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
363 [ + - ]: 16 : stack_machine_.emit(opcode); // Mul_x
364 : :
365 : : /* Scale down again, if necessary. */
366 : 16 : the_scale -= n_res->scale;
367 [ + - ]: 16 : M_insist(the_scale >= 0);
368 [ + + ]: 16 : if (the_scale != 0) {
369 [ + - + - ]: 5 : M_insist(n_res->is_decimal());
370 [ + - ]: 5 : const int64_t factor = powi<int64_t>(10, the_scale);
371 [ + - ]: 5 : load_numeric(factor, n_res);
372 [ + - ]: 5 : stack_machine_.emit_Div_i();
373 : 5 : }
374 : :
375 : : break;
376 : 16 : }
377 : :
378 : : case TK_SLASH: {
379 : : /* Division with potentially different numeric types is a tricky thing. Not only must we convert the values
380 : : * from their original data type to the type of the result, but we also must scale them correctly.
381 : : *
382 : : * The effective scale of the result is computed as `scale_lhs - scale_rhs`.
383 : : *
384 : : * (1) If the effective scale of the result is *less* than the expected scale of the result, the LHS must be
385 : : * scaled up.
386 : : *
387 : : * (2) If the effective scale of the result is *greater* than the expected scale of the result, the result
388 : : * must be scaled down.
389 : : *
390 : : * With these rules we can achieve maximum precision within the rules of the type system.
391 : : */
392 : :
393 [ + - ]: 10 : auto n_lhs = as<const Numeric>(ty_lhs);
394 [ + - ]: 10 : auto n_rhs = as<const Numeric>(ty_rhs);
395 [ + - ]: 10 : auto n_res = as<const Numeric>(ty);
396 : 10 : int32_t the_scale = 0;
397 : :
398 [ + - ]: 10 : (*this)(*e.lhs);
399 [ + - + + ]: 10 : if (n_lhs->is_floating_point()) {
400 [ + - ]: 5 : scale(n_lhs, n_res); // scale float up before cast to preserve decimal places
401 : 5 : the_scale += n_res->scale;
402 : 5 : } else {
403 : 5 : the_scale += n_lhs->scale;
404 : : }
405 [ + - ]: 10 : emit_cast(n_lhs, n_res);
406 : :
407 [ + - + + ]: 10 : if (n_rhs->is_floating_point())
408 : 3 : the_scale -= n_res->scale;
409 : : else
410 : 7 : the_scale -= n_rhs->scale;
411 : :
412 [ + + ]: 10 : if (the_scale < int32_t(n_res->scale)) {
413 [ + - ]: 3 : const int64_t factor = powi(10L, n_res->scale - the_scale); // scale up
414 [ + - ]: 3 : load_numeric(factor, n_res);
415 [ + - ]: 3 : stack_machine_.emit_Mul_i();
416 : 3 : }
417 : :
418 [ + - ]: 10 : (*this)(*e.rhs);
419 [ + - + + ]: 10 : if (n_rhs->is_floating_point())
420 [ + - ]: 3 : scale(n_rhs, n_res); // scale float up before cast to preserve decimal places
421 [ + - ]: 10 : emit_cast(n_rhs, n_res);
422 : :
423 [ + - ]: 10 : std::string opstr = "Div";
424 [ + - ]: 10 : opstr += tystr_to;
425 [ + - ]: 10 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
426 [ + - ]: 10 : stack_machine_.emit(opcode); // Div_x
427 : :
428 [ - + ]: 10 : if (the_scale > int32_t(n_res->scale)) {
429 [ # # ]: 0 : const int64_t factor = powi(10L, the_scale - n_res->scale); // scale down
430 [ # # ]: 0 : load_numeric(factor, n_res);
431 [ # # ]: 0 : stack_machine_.emit_Div_i();
432 : 0 : }
433 : :
434 : : break;
435 : 10 : }
436 : :
437 : : case TK_PERCENT:
438 [ + - ]: 1 : (*this)(*e.lhs);
439 [ + - ]: 1 : (*this)(*e.rhs);
440 [ + - ]: 1 : stack_machine_.emit_Mod_i();
441 : 1 : break;
442 : :
443 : : /*----- Concatenation operator -------------------------------------------------------------------------------*/
444 : : case TK_DOTDOT:
445 [ + - ]: 1 : (*this)(*e.lhs);
446 [ + - ]: 1 : (*this)(*e.rhs);
447 [ + - ]: 1 : stack_machine_.emit_Cat_s();
448 : 1 : break;
449 : :
450 : : /*----- Comparison operators ---------------------------------------------------------------------------------*/
451 : : case TK_LESS:
452 : : case TK_GREATER:
453 : : case TK_LESS_EQUAL:
454 : : case TK_GREATER_EQUAL:
455 : : case TK_EQUAL:
456 : : case TK_BANG_EQUAL:
457 [ + + + + ]: 70 : if (ty_lhs->is_numeric()) {
458 [ + - + - ]: 36 : M_insist(ty_rhs->is_numeric());
459 [ + - ]: 36 : auto n_lhs = as<const Numeric>(ty_lhs);
460 [ + - ]: 36 : auto n_rhs = as<const Numeric>(ty_rhs);
461 [ + - ]: 36 : auto n_res = arithmetic_join(n_lhs, n_rhs);
462 : :
463 [ + - ]: 36 : (*this)(*e.lhs);
464 [ + - ]: 36 : scale(n_lhs, n_res);
465 [ + - ]: 36 : emit_cast(n_lhs, n_res);
466 : :
467 [ + - ]: 36 : (*this)(*e.rhs);
468 [ + - ]: 36 : scale(n_rhs, n_res);
469 [ + - ]: 36 : emit_cast(n_rhs, n_res);
470 : :
471 [ + - + - ]: 36 : std::string opstr = opname + tystr(n_res);
472 [ + - ]: 36 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
473 [ + - ]: 36 : stack_machine_.emit(opcode);
474 : 36 : } else {
475 [ + - ]: 8 : (*this)(*e.lhs);
476 [ + - ]: 8 : (*this)(*e.rhs);
477 [ + - + - ]: 8 : std::string opstr = opname + tystr(ty_lhs);
478 [ + - ]: 8 : auto opcode = StackMachine::STR_TO_OPCODE.at(opstr);
479 [ + - ]: 8 : stack_machine_.emit(opcode);
480 : 8 : }
481 : 44 : break;
482 : :
483 : : case TK_Like: {
484 [ + - + - ]: 3 : if (auto rhs = cast<const ast::Constant>(e.rhs.get())) {
485 [ + - ]: 3 : (*this)(*e.lhs);
486 [ + - + - : 3 : auto pattern = interpret(*rhs->tok.text);
+ - ]
487 [ + - ]: 3 : auto it = regexes_.find(pattern);
488 [ + - ]: 3 : if (it == regexes_.end())
489 [ + - + - : 3 : it = StackMachineBuilder::regexes_.insert({pattern, pattern_to_regex(pattern.c_str(), true)}).first;
+ - ]
490 [ + - + - ]: 3 : stack_machine_.add_and_emit_load(&(it->second));
491 [ + - ]: 3 : stack_machine_.emit_Like_const();
492 : 3 : } else {
493 [ # # ]: 0 : (*this)(*e.lhs);
494 [ # # ]: 0 : (*this)(*e.rhs);
495 [ # # ]: 0 : stack_machine_.emit_Like_expr();
496 : : }
497 : 3 : break;
498 : : }
499 : :
500 : : /*----- Logical operators ------------------------------------------------------------------------------------*/
501 : : case TK_And:
502 [ + - ]: 1 : (*this)(*e.lhs);
503 [ + - ]: 1 : (*this)(*e.rhs);
504 [ + - ]: 1 : stack_machine_.emit_And_b();
505 : 1 : break;
506 : :
507 : : case TK_Or:
508 [ + - ]: 1 : (*this)(*e.lhs);
509 [ + - ]: 1 : (*this)(*e.rhs);
510 [ + - ]: 1 : stack_machine_.emit_Or_b();
511 : 1 : break;
512 : : }
513 : 207 : }
514 : :
515 : 0 : void StackMachineBuilder::operator()(Const<ast::QueryExpr> &e) {
516 : : /* Given the query expression, identify the position of its value in the tuple. */
517 : 0 : Catalog &C = Catalog::Get();
518 [ # # # # : 0 : auto [tuple_id, attr_id] = find_id({e.alias(), C.pool("$res")});
# # ]
519 : 0 : stack_machine_.emit_Ld_Tup(tuple_id, attr_id);
520 : 0 : }
521 : :
522 : :
523 : : /*======================================================================================================================
524 : : * Stack Machine
525 : : *====================================================================================================================*/
526 : :
527 [ + - ]: 1 : const std::unordered_map<std::string, StackMachine::Opcode> StackMachine::STR_TO_OPCODE = {
528 : : #define M_OPCODE(CODE, ...) { #CODE, StackMachine::Opcode:: CODE },
529 : : #include "tables/Opcodes.tbl"
530 : : #undef M_OPCODE
531 : : };
532 : :
533 : 0 : StackMachine::StackMachine(Schema in_schema, const ast::Expr &expr)
534 : 0 : : in_schema(in_schema)
535 : : {
536 [ # # ]: 0 : emit(expr, 1);
537 : : // TODO emit St
538 : 0 : }
539 : :
540 : 0 : StackMachine::StackMachine(Schema in_schema, const cnf::CNF &cnf)
541 : 0 : : in_schema(in_schema)
542 : : {
543 [ # # ]: 0 : emit(cnf);
544 : : // TODO emit St
545 : 0 : }
546 : :
547 : 3622 : void StackMachine::emit(const ast::Expr &expr, std::size_t tuple_id)
548 : : {
549 [ + - + + ]: 3622 : if (auto it = in_schema.find(Schema::Identifier(expr)); it != in_schema.end()) { // expression already computed
550 : : /* Given the expression, identify the position of its value in the tuple. */
551 : 292 : const unsigned idx = std::distance(in_schema.begin(), it);
552 : 292 : M_insist(idx < in_schema.num_entries(), "index out of bounds");
553 : 292 : emit_Ld_Tup(tuple_id, idx);
554 : 292 : } else { // expression has to be computed
555 [ + - + - ]: 3330 : std::vector<Schema> svec({in_schema});
556 [ + - ]: 3330 : std::vector<std::size_t> tuple_ids({tuple_id});
557 [ - + ]: 3330 : StackMachineBuilder(*this, expr, svec, tuple_ids); // compute the command sequence for this stack machine
558 : 3330 : }
559 : 3622 : }
560 : :
561 : 0 : void StackMachine::emit(const ast::Expr &expr, const Schema &schema, std::size_t tuple_id)
562 : : {
563 [ # # # # ]: 0 : if (auto it = in_schema.find(Schema::Identifier(expr)); it != in_schema.end()) { // expression already computed
564 : : /* Given the expression, identify the position of its value in the tuple. */
565 : 0 : const unsigned idx = std::distance(in_schema.begin(), it);
566 : 0 : M_insist(idx < in_schema.num_entries(), "index out of bounds");
567 : 0 : emit_Ld_Tup(tuple_id, idx);
568 : 0 : } else { // expression has to be computed
569 [ # # # # ]: 0 : std::vector<Schema> svec({schema});
570 [ # # ]: 0 : std::vector<std::size_t> tuple_ids({tuple_id});
571 [ # # ]: 0 : StackMachineBuilder(*this, expr, svec, tuple_ids); // compute the command sequence for this stack machine
572 : 0 : }
573 : 0 : }
574 : :
575 : 0 : void StackMachine::emit(const ast::Expr &expr,
576 : : std::vector<Schema> &schemas,
577 : : std::vector<std::size_t> &tuple_ids)
578 : : {
579 : 0 : StackMachineBuilder(*this, expr, schemas, tuple_ids); // compute the command sequence for this stack machine
580 : 0 : }
581 : :
582 : 5 : void StackMachine::emit(const cnf::CNF &cnf, std::size_t tuple_id)
583 : : {
584 : : /* Compile filter into stack machine. */
585 [ + + ]: 13 : for (auto clause_it = cnf.cbegin(); clause_it != cnf.cend(); ++clause_it) {
586 : 8 : auto &C = *clause_it;
587 [ + + ]: 19 : for (auto pred_it = C.cbegin(); pred_it != C.cend(); ++pred_it) {
588 : 11 : auto &P = *pred_it;
589 : 11 : emit(*P, tuple_id); // emit code for predicate
590 [ + + ]: 11 : if (P.negative())
591 : 3 : ops.push_back(StackMachine::Opcode::Not_b); // negate if negative
592 [ + + ]: 11 : if (pred_it != C.cbegin())
593 : 3 : ops.push_back(StackMachine::Opcode::Or_b);
594 : 11 : }
595 [ + + ]: 8 : if (clause_it != cnf.cbegin())
596 : 3 : ops.push_back(StackMachine::Opcode::And_b);
597 : 8 : }
598 : 5 : }
599 : :
600 : 0 : void StackMachine::emit(const cnf::CNF &cnf,
601 : : std::vector<Schema> &schemas,
602 : : std::vector<std::size_t> &tuple_ids)
603 : : {
604 : : /* Compile filter into stack machine. */
605 [ # # ]: 0 : for (auto clause_it = cnf.cbegin(); clause_it != cnf.cend(); ++clause_it) {
606 : 0 : auto &C = *clause_it;
607 [ # # ]: 0 : for (auto pred_it = C.cbegin(); pred_it != C.cend(); ++pred_it) {
608 : 0 : auto &P = *pred_it;
609 : 0 : emit(*P, schemas, tuple_ids); // emit code for predicate
610 [ # # ]: 0 : if (P.negative())
611 : 0 : ops.push_back(StackMachine::Opcode::Not_b); // negate if negative
612 [ # # ]: 0 : if (pred_it != C.cbegin())
613 : 0 : ops.push_back(StackMachine::Opcode::Or_b);
614 : 0 : }
615 [ # # ]: 0 : if (clause_it != cnf.cbegin())
616 : 0 : ops.push_back(StackMachine::Opcode::And_b);
617 : 0 : }
618 : 0 : }
619 : :
620 : 387 : void StackMachine::emit_Ld(const Type *ty)
621 : : {
622 : 387 : M_insist(not ty->is_boolean(), "to access a boolean use `emit_Ld_b(offset)`");
623 : :
624 [ + + ]: 387 : if (auto n = cast<const Numeric>(ty)) {
625 [ + - + ]: 344 : switch (n->kind) {
626 : : case Numeric::N_Int:
627 : : case Numeric::N_Decimal: {
628 [ + + + + : 280 : switch (n->size()) {
- ]
629 : 0 : default: M_unreachable("illegal type");
630 : 2 : case 8: emit_Ld_i8(); break;
631 : 27 : case 16: emit_Ld_i16(); break;
632 : 241 : case 32: emit_Ld_i32(); break;
633 : 10 : case 64: emit_Ld_i64(); break;
634 : : }
635 : 280 : break;
636 : : }
637 : :
638 : : case Numeric::N_Float: {
639 [ + + ]: 64 : if (n->size() == 32)
640 : 42 : emit_Ld_f();
641 : : else
642 : 22 : emit_Ld_d();
643 : 64 : break;
644 : : }
645 : : }
646 [ + + ]: 387 : } else if (auto cs = cast<const CharacterSequence>(ty)) {
647 : 39 : add_and_emit_load(cs->length);
648 : 39 : emit_Ld_s();
649 [ + + ]: 43 : } else if (auto d = cast<const Date>(ty)) {
650 : 2 : emit_Ld_i32();
651 [ + - ]: 4 : } else if (auto dt = cast<const DateTime>(ty)) {
652 : 2 : emit_Ld_i64();
653 : 2 : } else {
654 : 0 : M_unreachable("illegal type");
655 : : }
656 : 387 : }
657 : :
658 : 3050 : void StackMachine::emit_St(const Type *ty)
659 : : {
660 : 3050 : M_insist(not ty->is_boolean(), "to access a boolean use `emit_St_b(offset)`");
661 : :
662 [ + + ]: 3050 : if (auto n = cast<const Numeric>(ty)) {
663 [ + - + ]: 3007 : switch (n->kind) {
664 : : case Numeric::N_Int:
665 : : case Numeric::N_Decimal: {
666 [ + + + + : 2941 : switch (n->size()) {
- ]
667 : 0 : default: M_unreachable("illegal type");
668 : 3 : case 8: emit_St_i8(); break;
669 : 24 : case 16: emit_St_i16(); break;
670 : 2897 : case 32: emit_St_i32(); break;
671 : 17 : case 64: emit_St_i64(); break;
672 : : }
673 : 2941 : break;
674 : : }
675 : :
676 : : case Numeric::N_Float: {
677 [ + + ]: 66 : if (n->size() == 32)
678 : 43 : emit_St_f();
679 : : else
680 : 23 : emit_St_d();
681 : 66 : break;
682 : : }
683 : : }
684 [ + + ]: 3050 : } else if (auto cs = cast<const CharacterSequence>(ty)) {
685 : 39 : add_and_emit_load(cs->length + cs->is_varying);
686 : 39 : emit_St_s();
687 [ + + ]: 43 : } else if (auto d = cast<const Date>(ty)) {
688 : 2 : emit_St_i32();
689 [ + - ]: 4 : } else if (auto dt = cast<const DateTime>(ty)) {
690 : 2 : emit_St_i64();
691 : 2 : } else {
692 : 0 : M_unreachable("illegal type");
693 : : }
694 : 3050 : }
695 : :
696 : 4042 : void StackMachine::emit_St_Tup(std::size_t tuple_id, std::size_t index, const Type *ty)
697 : : {
698 [ + + ]: 4042 : if (ty->is_none()) {
699 : 1 : emit_St_Tup_Null(tuple_id, index);
700 [ + + ]: 4042 : } else if (auto cs = cast<const CharacterSequence>(ty)) {
701 : 96 : add_and_emit_load(cs->length);
702 : 96 : emit_St_Tup_s(tuple_id, index);
703 : 96 : } else {
704 : 3945 : std::ostringstream oss;
705 [ + - + - : 3945 : oss << "St_Tup" << tystr(as<const PrimitiveType>(ty));
+ - + - ]
706 [ + - + - ]: 3945 : auto opcode = StackMachine::STR_TO_OPCODE.at(oss.str());
707 [ + - ]: 3945 : emit(opcode);
708 [ + - ]: 3945 : emit(static_cast<Opcode>(tuple_id));
709 [ + - ]: 3945 : emit(static_cast<Opcode>(index));
710 : 3945 : }
711 : 4042 : }
712 : :
713 : 4 : void StackMachine::emit_Print(std::size_t ostream_index, const Type *ty)
714 : : {
715 [ + + ]: 4 : if (ty->is_none()) {
716 : 1 : emit_Push_Null();
717 : 1 : emit_Print_i(ostream_index);
718 [ - + ]: 4 : } else if (ty->is_date()) {
719 [ # # # # : 0 : emit(StackMachine::STR_TO_OPCODE.at("Print_date"));
# # ]
720 : 0 : emit(static_cast<Opcode>(ostream_index));
721 [ - + ]: 3 : } else if (ty->is_date_time()) {
722 [ # # # # : 0 : emit(StackMachine::STR_TO_OPCODE.at("Print_datetime"));
# # ]
723 : 0 : emit(static_cast<Opcode>(ostream_index));
724 : 0 : } else {
725 : 3 : std::ostringstream oss;
726 [ + - + - : 3 : oss << "Print" << tystr(as<const PrimitiveType>(ty));
+ - + - ]
727 [ + - + - ]: 3 : auto opcode = StackMachine::STR_TO_OPCODE.at(oss.str());
728 [ + - ]: 3 : emit(opcode);
729 [ + - ]: 3 : emit(static_cast<Opcode>(ostream_index));
730 : 3 : }
731 : 4 : }
732 : :
733 : 3221 : void StackMachine::emit_Cast(const Type *to_ty, const Type *from_ty)
734 : : {
735 : 3221 : auto to = as<const PrimitiveType>(to_ty);
736 : 3221 : auto from = as<const PrimitiveType>(from_ty);
737 [ + + ]: 3221 : if (from->as_vectorial() == to->as_vectorial()) return; // nothing to be done
738 : :
739 [ + + ]: 160 : if (auto n_from = cast<const Numeric>(from)) {
740 : 117 : auto n_to = as<const Numeric>(to);
741 : :
742 [ + + + - ]: 117 : switch (n_from->kind) {
743 : : case Numeric::N_Int:
744 [ + + + - ]: 72 : switch (n_to->kind) {
745 : : case Numeric::N_Int: /* int -> int */
746 : : /* nothing to be done */
747 : 53 : return;
748 : :
749 : : case Numeric::N_Decimal: /* int -> decimal */
750 : 1 : add_and_emit_load(powi(10L, n_to->scale));
751 : 1 : emit_Mul_i();
752 : 1 : return;
753 : :
754 : : case Numeric::N_Float:
755 [ + + ]: 18 : if (n_to->size() == 32) /* int -> float */
756 : 14 : emit_Cast_f_i();
757 : : else /* int -> double */
758 : 4 : emit_Cast_d_i();
759 : 18 : return;
760 : : }
761 : 0 : break;
762 : :
763 : : case Numeric::N_Float:
764 [ + + ]: 40 : if (n_from->size() == 32) {
765 [ - + + + ]: 4 : switch (n_to->kind) {
766 : : case Numeric::N_Int: /* float -> int */
767 : 2 : emit_Cast_i_f();
768 : 2 : return;
769 : :
770 : : case Numeric::N_Decimal: /* float -> decimal */
771 : 1 : add_and_emit_load(float(powi(10L, n_to->scale)));
772 : 1 : emit_Mul_f();
773 : 1 : emit_Cast_i_f();
774 : 1 : return;
775 : :
776 : : case Numeric::N_Float: /* float -> double */
777 : 1 : M_insist(n_to->size() == 64, "float to float");
778 : 1 : emit_Cast_d_f();
779 : 1 : return;
780 : : }
781 : 0 : } else {
782 [ - + + + ]: 36 : switch (n_to->kind) {
783 : : case Numeric::N_Int: /* double -> int */
784 : 2 : emit_Cast_i_d();
785 : 2 : return;
786 : :
787 : : case Numeric::N_Decimal: /* double -> decimal */
788 : 1 : add_and_emit_load(double(powi(10L, n_to->scale)));
789 : 1 : emit_Mul_d();
790 : 1 : emit_Cast_i_d();
791 : 1 : return;
792 : :
793 : : case Numeric::N_Float:
794 : 33 : M_insist(n_to->size() == 32, "double to double");
795 : 33 : emit_Cast_f_d(); /* double -> float */
796 : 33 : return;
797 : : }
798 : : }
799 : 0 : break;
800 : :
801 : : case Numeric::N_Decimal:
802 [ + + + - ]: 5 : switch (n_to->kind) {
803 : : case Numeric::N_Int: /* decimal -> int */
804 : 1 : add_and_emit_load(powi(10L, n_from->scale));
805 : 1 : emit_Div_i();
806 : 1 : return;
807 : :
808 : : case Numeric::N_Decimal: { /* decimal -> decimal */
809 [ - + ]: 2 : if (n_to->scale != n_from->scale) {
810 [ + + ]: 2 : if (n_to->scale > n_from->scale) {
811 : 1 : add_and_emit_load(powi(10L, n_to->scale - n_from->scale));
812 : 1 : emit_Mul_i();
813 : 1 : } else {
814 : 1 : add_and_emit_load(powi(10L, n_from->scale - n_to->scale));
815 : 1 : emit_Div_i();
816 : : }
817 : 2 : }
818 : 2 : return;
819 : : }
820 : :
821 : : case Numeric::N_Float:
822 [ + + ]: 2 : if (n_to->size() == 32) { /* decimal -> float */
823 : 1 : emit_Cast_f_i();
824 : 1 : add_and_emit_load(float(powi(10L, n_from->scale)));
825 : 1 : emit_Div_f();
826 : 1 : } else { /* decimal -> double */
827 : 1 : emit_Cast_d_i();
828 : 1 : add_and_emit_load(double(powi(10L, n_from->scale)));
829 : 1 : emit_Div_d();
830 : : }
831 : 2 : return;
832 : : }
833 : 0 : break;
834 : : }
835 [ + + ]: 43 : } else if (auto cs_from = cast<const CharacterSequence>(from_ty)) {
836 : 42 : M_insist(to_ty->is_character_sequence()); // XXX any checks necessary?
837 : 42 : return; // nothing to be done
838 [ + - ]: 1 : } else if (auto b_from = cast<const Boolean>(from_ty)) {
839 : 1 : auto n_to = as<const Numeric>(to);
840 : :
841 : 1 : M_insist(to_ty->is_numeric());
842 [ + - - ]: 1 : switch (n_to->kind) {
843 : : case Numeric::N_Int: /* bool -> int */
844 : 1 : emit_Cast_i_b();
845 : 1 : return;
846 : :
847 : : case Numeric::N_Float:
848 : : case Numeric::N_Decimal:
849 : 0 : M_unreachable("unsupported conversion");
850 : : }
851 : 0 : }
852 : :
853 : 0 : M_unreachable("unsupported conversion");
854 : 3221 : }
855 : :
856 : 10868 : void StackMachine::operator()(Tuple **tuples) const
857 : : {
858 : : static const void *labels[] = {
859 : : #define M_OPCODE(CODE, ...) && CODE,
860 : : #include "tables/Opcodes.tbl"
861 : : #undef M_OPCODE
862 : : };
863 : :
864 : 10868 : const_cast<StackMachine*>(this)->emit_Stop();
865 [ + + ]: 10868 : if (not values_) {
866 [ + + + - : 8362 : values_ = new Value[required_stack_size()];
+ + ]
867 : 2136 : null_bits_ = new bool[required_stack_size()]();
868 : 2136 : }
869 : 10868 : top_ = 0; // points to the top of the stack, i.e. the top-most entry
870 : 10868 : op_ = ops.cbegin();
871 : 10868 : auto p_mem = memory_; // pointer to free memory; used like a linear allocator
872 : :
873 : : #define NEXT goto *labels[std::size_t(*op_++)]
874 : :
875 : : #define PUSH(VAL, NUL) { \
876 : : M_insist(top_ < required_stack_size(), "index out of bounds"); \
877 : : values_[top_] = (VAL); \
878 : : null_bits_[top_] = (NUL); \
879 : : ++top_; \
880 : : }
881 : : #define POP() --top_
882 : : #define TOP_IS_NULL (null_bits_[top_ - 1UL])
883 : : #define TOP (values_[top_ - 1UL])
884 : :
885 : 10868 : NEXT;
886 : :
887 : :
888 : : /*======================================================================================================================
889 : : * Control flow operations
890 : : *====================================================================================================================*/
891 : :
892 : : Stop_Z: {
893 : 2 : M_insist(top_ >= 1);
894 [ + + ]: 2 : if (TOP.as_i() == 0) goto Stop; // stop evaluation on ZERO
895 : : }
896 : 1 : NEXT;
897 : :
898 : : Stop_NZ: {
899 : 2 : M_insist(top_ >= 1);
900 [ + + ]: 2 : if (TOP.as_i() != 0) goto Stop; // stop evaluation on NOT ZERO
901 : : }
902 : 1 : NEXT;
903 : :
904 : : Stop_False: {
905 : 2 : M_insist(top_ >= 1);
906 [ + + ]: 2 : if (not TOP.as_b()) goto Stop; // stop evaluation on FALSE
907 : : }
908 : 1 : NEXT;
909 : :
910 : : Stop_True: {
911 : 2 : M_insist(top_ >= 1);
912 [ + + ]: 2 : if (TOP.as_b()) goto Stop; // stop evaluation on TRUE
913 : : }
914 : 1 : NEXT;
915 : :
916 : :
917 : : /*======================================================================================================================
918 : : * Stack manipulation operations
919 : : *====================================================================================================================*/
920 : :
921 : : Pop:
922 : 91068 : POP();
923 : 91068 : NEXT;
924 : :
925 : : Push_Null:
926 : 17641 : PUSH(Value(), true);
927 : 17641 : NEXT;
928 : :
929 : : Dup:
930 : 10641 : PUSH(TOP, TOP_IS_NULL);
931 : 10641 : NEXT;
932 : :
933 : :
934 : : /*======================================================================================================================
935 : : * Context Access Operations
936 : : *====================================================================================================================*/
937 : :
938 : : /* Load a value from the context to the top of the value_stack_. */
939 : : Ld_Ctx: {
940 : 388905 : std::size_t idx = std::size_t(*op_++);
941 : 388905 : M_insist(idx < context_.size(), "index out of bounds");
942 : 388905 : PUSH(context_[idx], false);
943 : : }
944 : 388905 : NEXT;
945 : :
946 : : Upd_Ctx: {
947 : 90637 : M_insist(top_ >= 1);
948 : 90637 : std::size_t idx = static_cast<std::size_t>(*op_++);
949 : 90637 : M_insist(idx < context_.size(), "index out of bounds");
950 : : #ifdef M_ENABLE_SANITY_FIELDS
951 : : M_insist(context_[idx].type == TOP.type, "update must not change the type of a context entry");
952 : : #endif
953 : 90637 : const_cast<StackMachine*>(this)->context_[idx] = TOP;
954 : : }
955 : 90637 : NEXT;
956 : :
957 : :
958 : : /*======================================================================================================================
959 : : * Tuple Access Operations
960 : : *====================================================================================================================*/
961 : :
962 : : Ld_Tup: {
963 : 24760 : std::size_t tuple_id = std::size_t(*op_++);
964 : 24760 : std::size_t index = std::size_t(*op_++);
965 : 24760 : auto &t = *tuples[tuple_id];
966 : 24760 : PUSH(t[index], t.is_null(index));
967 : : }
968 : 24760 : NEXT;
969 : :
970 : : St_Tup_Null: {
971 : 182 : std::size_t tuple_id = std::size_t(*op_++);
972 : 182 : std::size_t index = std::size_t(*op_++);
973 : 182 : auto &t = *tuples[tuple_id];
974 : 182 : t.null(index);
975 : : }
976 : 76936 : NEXT;
977 : :
978 : : St_Tup_b:
979 : 37933 : St_Tup_i:
980 : 38182 : St_Tup_f:
981 : 38340 : St_Tup_d: {
982 : 38340 : std::size_t tuple_id = std::size_t(*op_++);
983 : 38340 : std::size_t index = std::size_t(*op_++);
984 : 38340 : auto &t = *tuples[tuple_id];
985 : 38340 : t.set(index, TOP, TOP_IS_NULL);
986 : : }
987 : 38340 : NEXT;
988 : :
989 : : St_Tup_s: {
990 : 219 : std::size_t tuple_id = std::size_t(*op_++);
991 : 219 : std::size_t index = std::size_t(*op_++);
992 : 219 : std::size_t length = TOP.as_i();
993 : 219 : POP();
994 : 219 : auto &t = *tuples[tuple_id];
995 [ + + ]: 219 : if (TOP_IS_NULL)
996 : 46 : t.null(index);
997 : : else {
998 : 173 : t.not_null(index);
999 : 173 : char *dst = reinterpret_cast<char*>(t[index].as_p());
1000 : 173 : char *src = reinterpret_cast<char*>(TOP.as_p());
1001 : 173 : strncpy(dst, reinterpret_cast<char*>(src), length);
1002 : 173 : dst[length] = 0; // always add terminating NUL byte, no matter whether this is a CHAR or VARCHAR
1003 : : }
1004 : : }
1005 : 219 : NEXT;
1006 : :
1007 : :
1008 : : /*======================================================================================================================
1009 : : * I/O Operations
1010 : : *====================================================================================================================*/
1011 : :
1012 : : Putc: {
1013 : 2 : std::size_t index = std::size_t(*op_++);
1014 : 2 : unsigned char chr = (unsigned char)(*op_++);
1015 : 2 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1016 : 2 : out << chr;
1017 : : }
1018 : 2 : NEXT;
1019 : :
1020 : : Print_i: {
1021 : 4 : M_insist(top_ >= 1);
1022 : 4 : std::size_t index = std::size_t(*op_++);
1023 : 4 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1024 [ + + ]: 4 : if (TOP_IS_NULL)
1025 : 2 : out << "NULL";
1026 : : else
1027 : 2 : out << TOP.as_i();
1028 : : }
1029 : 4 : NEXT;
1030 : :
1031 : : Print_f: {
1032 : 3 : M_insist(top_ >= 1);
1033 : 3 : std::size_t index = std::size_t(*op_++);
1034 : 3 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1035 [ + + ]: 3 : if (TOP_IS_NULL) {
1036 : 1 : out << "NULL";
1037 : 1 : } else {
1038 : 2 : const auto old_precision = out.precision(std::numeric_limits<float>::max_digits10 - 1);
1039 : 2 : out << TOP.as_f();
1040 : 2 : out.precision(old_precision);
1041 : : }
1042 : : }
1043 : 3 : NEXT;
1044 : :
1045 : : Print_d: {
1046 : 2 : M_insist(top_ >= 1);
1047 : 2 : std::size_t index = std::size_t(*op_++);
1048 : 2 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1049 [ + + ]: 2 : if (TOP_IS_NULL) {
1050 : 1 : out << "NULL";
1051 : 1 : } else {
1052 : 1 : const auto old_precision = out.precision(std::numeric_limits<double>::max_digits10 - 1);
1053 : 1 : out << TOP.as_d();
1054 : 1 : out.precision(old_precision);
1055 : : }
1056 : : }
1057 : 2 : NEXT;
1058 : :
1059 : : Print_s: {
1060 : 10 : M_insist(top_ >= 1);
1061 : 10 : std::size_t index = std::size_t(*op_++);
1062 : 10 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1063 [ + + ]: 10 : if (TOP_IS_NULL) {
1064 : 1 : out << "NULL";
1065 : 1 : } else {
1066 : 9 : const char *str = reinterpret_cast<char*>(TOP.as_p());
1067 : 9 : out << '"' << str << '"';
1068 : : }
1069 : : }
1070 : 10 : NEXT;
1071 : :
1072 : : Print_b: {
1073 : 3 : M_insist(top_ >= 1);
1074 : 3 : std::size_t index = std::size_t(*op_++);
1075 : 3 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1076 [ + + ]: 3 : if (TOP_IS_NULL)
1077 : 1 : out << "NULL";
1078 : : else
1079 : 2 : out << (TOP.as_b() ? "TRUE" : "FALSE");
1080 : : }
1081 : 3 : NEXT;
1082 : :
1083 : : Print_date: {
1084 : 3 : std::size_t index = std::size_t(*op_++);
1085 : 3 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1086 [ + + ]: 3 : if (TOP_IS_NULL) {
1087 : 1 : out << "NULL";
1088 : 1 : } else {
1089 : 2 : const int32_t date = TOP.as_i(); // signed because year is signed
1090 : 2 : const auto oldfill = out.fill('0');
1091 : 2 : const auto oldfmt = out.flags();
1092 : 2 : out << std::internal
1093 : 2 : << std::setw(date >> 9 > 0 ? 4 : 5) << (date >> 9) << '-'
1094 : 2 : << std::setw(2) << ((date >> 5) & 0xF) << '-'
1095 : 2 : << std::setw(2) << (date & 0x1F);
1096 : 2 : out.fill(oldfill);
1097 : 2 : out.flags(oldfmt);
1098 : : }
1099 : : }
1100 : 3 : NEXT;
1101 : :
1102 : : Print_datetime: {
1103 : 3 : std::size_t index = std::size_t(*op_++);
1104 : 3 : std::ostream &out = *reinterpret_cast<std::ostream*>(context_[index].as_p());
1105 [ + + ]: 3 : if (TOP_IS_NULL) {
1106 : 1 : out << "NULL";
1107 : 1 : } else {
1108 : 2 : const time_t time = TOP.as_i();
1109 : : std::tm tm;
1110 : 2 : gmtime_r(&time, &tm);
1111 : 2 : out << put_tm(tm);
1112 : : }
1113 : : }
1114 : 3 : NEXT;
1115 : :
1116 : :
1117 : : /*======================================================================================================================
1118 : : * Storage Access Operations
1119 : : *====================================================================================================================*/
1120 : :
1121 : : /*----- Load from memory ---------------------------------------------------------------------------------------------*/
1122 : :
1123 : : #define LOAD(TO_TYPE, FROM_TYPE) { \
1124 : : M_insist(top_ >= 1); \
1125 : : const void *ptr = TOP.as_p(); \
1126 : : TOP = (TO_TYPE)(*reinterpret_cast<const FROM_TYPE*>(ptr)); \
1127 : : } \
1128 : : NEXT
1129 : :
1130 : 24076 : Ld_i8: LOAD(int64_t, int8_t);
1131 : 80 : Ld_i16: LOAD(int64_t, int16_t);
1132 : 17043 : Ld_i32: LOAD(int64_t, int32_t);
1133 : 41 : Ld_i64: LOAD(int64_t, int64_t);
1134 : 117 : Ld_f: LOAD(float, float);
1135 : 46 : Ld_d: LOAD(double, double);
1136 : :
1137 : : Ld_s: {
1138 : 110 : M_insist(top_ >= 1);
1139 : 110 : uint64_t length = TOP.as_i();
1140 : 110 : POP();
1141 : 110 : void *ptr = TOP.as_p();
1142 : 110 : strncpy(reinterpret_cast<char*>(p_mem), reinterpret_cast<char*>(ptr), length);
1143 : 110 : p_mem[length] = 0; // always add terminating NUL byte, no matter whether this is a CHAR or VARCHAR
1144 : 110 : TOP = p_mem; // a pointer
1145 : 110 : p_mem += length + 1;
1146 : : }
1147 : 110 : NEXT;
1148 : :
1149 : : Ld_b: {
1150 : 432 : M_insist(top_ >= 1);
1151 : 432 : uint64_t mask = uint64_t(*op_++);
1152 : 432 : void *ptr = TOP.as_p();
1153 : 432 : TOP = bool(*reinterpret_cast<uint8_t*>(ptr) & mask);
1154 : : }
1155 : 432 : NEXT;
1156 : :
1157 : : #undef LOAD
1158 : :
1159 : : /*----- Store to memory ----------------------------------------------------------------------------------------------*/
1160 : :
1161 : : #define STORE(TO_TYPE, FROM_TYPE) { \
1162 : : M_insist(top_ >= 2); \
1163 : : if (TOP_IS_NULL) { POP(); POP(); NEXT; } \
1164 : : TO_TYPE val = TOP.as<FROM_TYPE>(); \
1165 : : POP(); \
1166 : : void *ptr = TOP.as_p(); \
1167 : : *reinterpret_cast<TO_TYPE*>(ptr) = val; \
1168 : : POP(); \
1169 : : } \
1170 : : NEXT
1171 : :
1172 [ + + ]: 3349 : St_i8: STORE(int8_t, int64_t);
1173 [ + + ]: 80 : St_i16: STORE(int16_t, int64_t);
1174 [ + + ]: 3002 : St_i32: STORE(int32_t, int64_t);
1175 [ + + ]: 41 : St_i64: STORE(int64_t, int64_t);
1176 [ + + ]: 117 : St_f: STORE(float, float);
1177 [ + + ]: 46 : St_d: STORE(double, double);
1178 : :
1179 : : St_s: {
1180 : 110 : M_insist(top_ >= 2);
1181 : 110 : uint64_t length = TOP.as_i();
1182 : 110 : POP();
1183 [ + + ]: 110 : if (TOP_IS_NULL) { POP(); POP(); NEXT; }
1184 : :
1185 : 87 : char *str = reinterpret_cast<char*>(TOP.as_p());
1186 : 87 : POP();
1187 : 87 : char *dst = reinterpret_cast<char*>(TOP.as_p());
1188 : 87 : POP();
1189 : 87 : strncpy(dst, str, length);
1190 : : }
1191 : 87 : NEXT;
1192 : :
1193 : : St_b: {
1194 : 432 : M_insist(top_ >= 2);
1195 : 432 : uint64_t bit_offset = uint64_t(*op_++);
1196 [ + + ]: 432 : if (TOP_IS_NULL) { POP(); POP(); NEXT; }
1197 : :
1198 : 411 : bool val = TOP.as_b();
1199 : 411 : POP();
1200 : 411 : void *ptr = TOP.as_p();
1201 : 411 : setbit(reinterpret_cast<uint8_t*>(ptr), val, bit_offset);
1202 : 411 : POP();
1203 : : }
1204 : 411 : NEXT;
1205 : :
1206 : : #undef STORE
1207 : :
1208 : :
1209 : : /*======================================================================================================================
1210 : : * Arithmetical operations
1211 : : *====================================================================================================================*/
1212 : :
1213 : : #define UNARY(OP, TYPE) { \
1214 : : M_insist(top_ >= 1); \
1215 : : TYPE val = TOP.as<TYPE>(); \
1216 : : TOP = OP(val); \
1217 : : } \
1218 : : NEXT;
1219 : :
1220 : : #define BINARY(OP, TYPE) { \
1221 : : M_insist(top_ >= 2); \
1222 : : TYPE rhs = TOP.as<TYPE>(); \
1223 : : bool is_rhs_null = TOP_IS_NULL; \
1224 : : POP(); \
1225 : : TYPE lhs = TOP.as<TYPE>(); \
1226 : : TOP = OP(lhs, rhs); \
1227 : : TOP_IS_NULL = TOP_IS_NULL or is_rhs_null; \
1228 : : } \
1229 : : NEXT;
1230 : :
1231 : : /* Integral increment. */
1232 : 10508 : Inc: UNARY(++, int64_t);
1233 : :
1234 : : /* Integral decrement. */
1235 : 2 : Dec: UNARY(--, int64_t);
1236 : :
1237 : : /* Arithmetic negation */
1238 : 26 : Minus_i: UNARY(-, int64_t);
1239 : 3 : Minus_f: UNARY(-, float);
1240 : 31 : Minus_d: UNARY(-, double);
1241 : :
1242 : : /* Add two values. */
1243 [ - + ]: 46138 : Add_i: BINARY(std::plus{}, int64_t);
1244 [ - + ]: 2 : Add_f: BINARY(std::plus{}, float);
1245 [ - + ]: 8 : Add_d: BINARY(std::plus{}, double);
1246 : : Add_p: {
1247 : 72869 : M_insist(top_ >= 2);
1248 : 72869 : void *rhs = TOP.as_p();
1249 : 72869 : POP();
1250 : 72869 : const uint64_t lhs = TOP.as<int64_t>();
1251 : 72869 : bool is_lhs_null = TOP_IS_NULL;
1252 : 72869 : TOP = lhs + reinterpret_cast<uint8_t*>(rhs);
1253 [ - + ]: 72869 : TOP_IS_NULL = TOP_IS_NULL or is_lhs_null;
1254 : : }
1255 : 72869 : NEXT;
1256 : :
1257 : : /* Subtract two values. */
1258 [ - + ]: 5 : Sub_i: BINARY(std::minus{}, int64_t);
1259 [ - + ]: 2 : Sub_f: BINARY(std::minus{}, float);
1260 [ - + ]: 6 : Sub_d: BINARY(std::minus{}, double);
1261 : :
1262 : : /* Multiply two values. */
1263 [ - + ]: 46449 : Mul_i: BINARY(std::multiplies{}, int64_t);
1264 [ - + ]: 11 : Mul_f: BINARY(std::multiplies{}, float);
1265 [ - + ]: 15 : Mul_d: BINARY(std::multiplies{}, double);
1266 : :
1267 : : /* Divide two values. */
1268 [ - + ]: 12 : Div_i: BINARY(std::divides{}, int64_t);
1269 [ - + ]: 3 : Div_f: BINARY(std::divides{}, float);
1270 [ - + ]: 7 : Div_d: BINARY(std::divides{}, double);
1271 : :
1272 : : /* Modulo divide two values. */
1273 [ - + ]: 3 : Mod_i: BINARY(std::modulus{}, int64_t);
1274 : :
1275 : : /* Concatenate two strings. */
1276 : : Cat_s: {
1277 : 5 : M_insist(top_ >= 2);
1278 : :
1279 : 5 : bool rhs_is_null = TOP_IS_NULL;
1280 : 5 : Value rhs = TOP;
1281 : 5 : POP();
1282 : 5 : bool lhs_is_null = TOP_IS_NULL;
1283 : 5 : Value lhs = TOP;
1284 : :
1285 [ + + ]: 5 : if (rhs_is_null)
1286 : 1 : NEXT; // nothing to be done
1287 [ + + ]: 4 : if (lhs_is_null) {
1288 : 1 : TOP = rhs;
1289 : 1 : TOP_IS_NULL = rhs_is_null;
1290 : 1 : NEXT;
1291 : : }
1292 : :
1293 [ - + ]: 3 : M_insist(not rhs_is_null and not lhs_is_null);
1294 : 3 : char *dest = reinterpret_cast<char*>(p_mem);
1295 : 3 : TOP = dest;
1296 : : /* Append LHS. */
1297 [ + + ]: 31 : for (auto src = lhs.as<char*>(); *src; ++src, ++dest)
1298 : 28 : *dest = *src;
1299 : : /* Append RHS. */
1300 [ + + ]: 10 : for (auto src = rhs.as<char*>(); *src; ++src, ++dest)
1301 : 7 : *dest = *src;
1302 : 3 : *dest++ = 0; // terminating NUL byte
1303 : 3 : p_mem = reinterpret_cast<uint8_t*>(dest);
1304 : : }
1305 : 3 : NEXT;
1306 : :
1307 : :
1308 : : /*======================================================================================================================
1309 : : * Bitwise operations
1310 : : *====================================================================================================================*/
1311 : :
1312 : : /* Bitwise negation */
1313 : 3346 : Neg_i: UNARY(~, int64_t);
1314 : :
1315 : : /* Bitwise And */
1316 [ - + ]: 41200 : And_i: BINARY(std::bit_and{}, int64_t);
1317 : :
1318 : : /* Bitwise Or */
1319 [ - + ]: 3347 : Or_i: BINARY(std::bit_or{}, int64_t);
1320 : :
1321 : : /* Bitwise Xor */
1322 [ - + ]: 4 : Xor_i: BINARY(std::bit_xor{}, int64_t);
1323 : :
1324 : : /* Shift left - with operand on stack. */
1325 : : ShL_i: {
1326 : 20470 : M_insist(top_ >= 2);
1327 : 20470 : uint64_t count = TOP.as<int64_t>();
1328 : 20470 : POP();
1329 : 20470 : uint64_t val = TOP.as<int64_t>();
1330 : 20470 : TOP = uint64_t(val << count);
1331 : : }
1332 : 20470 : NEXT;
1333 : :
1334 : : /* Shift left immediate - with operand as argument. */
1335 : : ShLi_i: {
1336 : 259 : M_insist(top_ >= 1);
1337 : 259 : std::size_t count = std::size_t(*op_++);
1338 : 259 : uint64_t val = TOP.as<int64_t>();
1339 : 259 : TOP = uint64_t(val << count);
1340 : : }
1341 : 259 : NEXT;
1342 : :
1343 : : /* Shift arithmetical right - with operand as argument. */
1344 : : SARi_i: {
1345 : 25662 : M_insist(top_ >= 1);
1346 : 25662 : std::size_t count = std::size_t(*op_++);
1347 : 25662 : int64_t val = TOP.as<int64_t>(); // signed integer for arithmetical shift
1348 : 25662 : TOP = int64_t(val >> count);
1349 : : }
1350 : 25662 : NEXT;
1351 : :
1352 : :
1353 : : /*======================================================================================================================
1354 : : * Logical operations
1355 : : *====================================================================================================================*/
1356 : :
1357 : : /* Logical not */
1358 : 5197 : Not_b: UNARY(not, bool);
1359 : :
1360 : : /* Logical and with three-valued logic (https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics). */
1361 : : And_b: {
1362 : 7 : M_insist(top_ >= 2);
1363 : 7 : bool rhs = TOP.as<bool>();
1364 : 7 : bool is_rhs_null = TOP_IS_NULL;
1365 : 7 : POP();
1366 : 7 : bool lhs = TOP.as<bool>();
1367 : 7 : bool is_lhs_null = TOP_IS_NULL;
1368 [ + + ]: 7 : TOP = lhs and rhs;
1369 [ + + + + : 10 : TOP_IS_NULL = (lhs or is_lhs_null) and (rhs or is_rhs_null) and (is_lhs_null or is_rhs_null);
+ + - + ]
1370 : : }
1371 : 5 : NEXT;
1372 : :
1373 : : /* Logical or with three-valued logic (https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics). */
1374 : : Or_b: {
1375 : 7 : M_insist(top_ >= 2);
1376 : 7 : bool rhs = TOP.as<bool>();
1377 : 7 : bool is_rhs_null = TOP_IS_NULL;
1378 : 7 : POP();
1379 : 7 : bool lhs = TOP.as<bool>();
1380 : 7 : bool is_lhs_null = TOP_IS_NULL;
1381 [ + + ]: 7 : TOP = lhs or rhs;
1382 [ + + - + : 8 : TOP_IS_NULL = (not lhs or is_lhs_null) and (not rhs or is_rhs_null) and (is_lhs_null or is_rhs_null);
+ + - + ]
1383 : : }
1384 : 5 : NEXT;
1385 : :
1386 : :
1387 : : /*======================================================================================================================
1388 : : * Comparison operations
1389 : : *====================================================================================================================*/
1390 : :
1391 : : EqZ_i: {
1392 : 2 : M_insist(top_ >= 1);
1393 : 2 : uint64_t val = TOP.as<int64_t>();
1394 : 2 : TOP = val == 0;
1395 : : }
1396 : 2 : NEXT;
1397 : :
1398 : : NEZ_i: {
1399 : 17386 : M_insist(top_ >= 1);
1400 : 17386 : uint64_t val = TOP.as<int64_t>();
1401 : 17386 : TOP = val != 0;
1402 : : }
1403 : 17386 : NEXT;
1404 : :
1405 [ - + ]: 26042 : Eq_i: BINARY(std::equal_to{}, int64_t);
1406 [ - + ]: 3 : Eq_f: BINARY(std::equal_to{}, float);
1407 [ - + ]: 3 : Eq_d: BINARY(std::equal_to{}, double);
1408 [ - + ]: 3 : Eq_b: BINARY(std::equal_to{}, bool);
1409 [ - + ]: 3 : Eq_s: BINARY(streq, char*);
1410 : :
1411 [ - + ]: 10386 : NE_i: BINARY(std::not_equal_to{}, int64_t);
1412 [ - + ]: 3 : NE_f: BINARY(std::not_equal_to{}, float);
1413 [ - + ]: 3 : NE_d: BINARY(std::not_equal_to{}, double);
1414 [ - + ]: 3 : NE_b: BINARY(std::not_equal_to{}, bool);
1415 [ - + ]: 3 : NE_s: BINARY(not streq, char*);
1416 : :
1417 [ - + ]: 10 : LT_i: BINARY(std::less{}, int64_t);
1418 [ - + ]: 3 : LT_f: BINARY(std::less{}, float);
1419 [ - + ]: 9 : LT_d: BINARY(std::less{}, double);
1420 [ - + ]: 3 : LT_s: BINARY(0 > strcmp, char*)
1421 : :
1422 [ - + ]: 4 : GT_i: BINARY(std::greater{}, int64_t);
1423 [ - + ]: 3 : GT_f: BINARY(std::greater{}, float);
1424 [ - + ]: 3 : GT_d: BINARY(std::greater{}, double);
1425 [ - + ]: 3 : GT_s: BINARY(0 < strcmp, char*);
1426 : :
1427 [ - + ]: 4 : LE_i: BINARY(std::less_equal{}, int64_t);
1428 [ - + ]: 3 : LE_f: BINARY(std::less_equal{}, float);
1429 [ - + ]: 3 : LE_d: BINARY(std::less_equal{}, double);
1430 [ - + ]: 3 : LE_s: BINARY(0 >= strcmp, char*);
1431 : :
1432 [ - + ]: 4 : GE_i: BINARY(std::greater_equal{}, int64_t);
1433 [ - + ]: 3 : GE_f: BINARY(std::greater_equal{}, float);
1434 [ - + ]: 3 : GE_d: BINARY(std::greater_equal{}, double);
1435 [ - + ]: 3 : GE_s: BINARY(0 <= strcmp, char*);
1436 : :
1437 : : #define CMP(TYPE) { \
1438 : : M_insist(top_ >= 2); \
1439 : : TYPE rhs = TOP.as<TYPE>(); \
1440 : : bool is_rhs_null = TOP_IS_NULL; \
1441 : : POP(); \
1442 : : TYPE lhs = TOP.as<TYPE>(); \
1443 : : bool is_lhs_null = TOP_IS_NULL; \
1444 : : TOP = int64_t(lhs >= rhs) - int64_t(lhs <= rhs); \
1445 : : TOP_IS_NULL = is_lhs_null or is_rhs_null; \
1446 : : } \
1447 : : NEXT;
1448 : :
1449 [ - + ]: 2 : Cmp_i: CMP(int64_t);
1450 [ - + ]: 2 : Cmp_f: CMP(float);
1451 [ - + ]: 2 : Cmp_d: CMP(double);
1452 [ - + ]: 2 : Cmp_b: CMP(bool);
1453 [ - + ]: 3 : Cmp_s: BINARY(strcmp, char*);
1454 : :
1455 : : #undef CMP
1456 : :
1457 : : Like_const: {
1458 : 3 : M_insist(top_ >= 2);
1459 : 3 : std::regex *re = TOP.as<std::regex*>();
1460 : 3 : POP();
1461 [ - + ]: 3 : if (not TOP_IS_NULL) {
1462 : 3 : char *str = TOP.as<char*>();
1463 : 3 : TOP = std::regex_match(str, *re);
1464 : 3 : }
1465 : : }
1466 : 3 : NEXT;
1467 : :
1468 : : Like_expr: {
1469 : 6 : M_insist(top_ >= 2);
1470 [ + + ]: 6 : if (TOP_IS_NULL) {
1471 : 1 : POP();
1472 : 1 : TOP_IS_NULL = true;
1473 : 1 : } else {
1474 : 5 : char *pattern = TOP.as<char*>();
1475 : 5 : POP();
1476 [ + + ]: 5 : if (not TOP_IS_NULL) {
1477 : 4 : char *str = TOP.as<char*>();
1478 [ - + - + : 4 : TOP = like(str, pattern);
+ - + - ]
1479 : 4 : }
1480 : : }
1481 : : }
1482 : 6 : NEXT;
1483 : :
1484 : :
1485 : : /*======================================================================================================================
1486 : : * Selection operation
1487 : : *====================================================================================================================*/
1488 : :
1489 : : Sel: {
1490 : 21348 : M_insist(top_ >= 3);
1491 : 21348 : bool cond_is_null = null_bits_[top_ - 3UL];
1492 : :
1493 [ + + ]: 21348 : if (cond_is_null) {
1494 : 45 : values_[top_ - 3UL] = values_[top_ - 2UL]; // pick any value
1495 : 45 : null_bits_[top_ - 3UL] = true;
1496 : 45 : POP();
1497 : 45 : POP();
1498 : 45 : NEXT;
1499 : : }
1500 : :
1501 : 21303 : bool cond = values_[top_ - 3UL].as_b();
1502 [ + + ]: 21303 : if (cond) {
1503 : 399 : values_[top_ - 3UL] = values_[top_ - 2UL];
1504 : 399 : null_bits_[top_ - 3UL] = null_bits_[top_ - 2UL];
1505 : 399 : } else {
1506 : 20904 : values_[top_ - 3UL] = TOP;
1507 : 20904 : null_bits_[top_ - 3UL] = TOP_IS_NULL;
1508 : : }
1509 : 21303 : POP();
1510 : 21303 : POP();
1511 : : }
1512 : 21303 : NEXT;
1513 : :
1514 : :
1515 : : /*======================================================================================================================
1516 : : * Intrinsic functions
1517 : : *====================================================================================================================*/
1518 : :
1519 : : Is_Null:
1520 : 3587 : TOP = bool(TOP_IS_NULL);
1521 : 3587 : TOP_IS_NULL = false;
1522 : 3587 : NEXT;
1523 : :
1524 : : /* Cast to int. */
1525 : 11 : Cast_i_f: UNARY((int64_t), float);
1526 : 9 : Cast_i_d: UNARY((int64_t), double);
1527 : 46685 : Cast_i_b: UNARY((int64_t), bool);
1528 : :
1529 : : /* Cast to float. */
1530 : 26 : Cast_f_i: UNARY((float), int64_t);
1531 : 34 : Cast_f_d: UNARY((float), double);
1532 : :
1533 : : /* Cast to double. */
1534 : 34 : Cast_d_i: UNARY((double), int64_t);
1535 : 20 : Cast_d_f: UNARY((double), float);
1536 : :
1537 : : #undef BINARY
1538 : : #undef UNARY
1539 : :
1540 : 10864 : Stop:
1541 : 10864 : const_cast<StackMachine*>(this)->ops.pop_back(); // terminating Stop
1542 : :
1543 : 10864 : op_ = ops.cbegin();
1544 : 10864 : top_ = 0;
1545 : 10864 : }
1546 : :
1547 : : M_LCOV_EXCL_START
1548 : : void StackMachine::dump(std::ostream &out) const
1549 : : {
1550 : : out << "StackMachine\n Context: [";
1551 : : for (auto it = context_.cbegin(); it != context_.cend(); ++it) {
1552 : : if (it != context_.cbegin()) out << ", ";
1553 : : out << *it;
1554 : : }
1555 : : out << ']'
1556 : : << "\n Input Schema: " << in_schema
1557 : : << "\n Output Schema: {[";
1558 : : for (auto it = out_schema.begin(), end = out_schema.end(); it != end; ++it) {
1559 : : if (it != out_schema.begin()) out << ',';
1560 : : out << ' ' << **it;
1561 : : }
1562 : : out << " ]}"
1563 : : << "\n Opcode Sequence:\n";
1564 : : const std::size_t current_op = op_ - ops.begin();
1565 : : for (std::size_t i = 0; i != ops.size(); ++i) {
1566 : : auto opc = ops[i];
1567 : : if (i == current_op)
1568 : : out << " --> ";
1569 : : else
1570 : : out << " ";
1571 : : out << "[0x" << std::hex << std::setfill('0') << std::setw(4) << i << std::dec << "]: "
1572 : : << StackMachine::OPCODE_TO_STR[static_cast<std::size_t>(opc)];
1573 : : switch (opc) {
1574 : : /* Opcodes with *three* operands. */
1575 : : case Opcode::St_Tup_s:
1576 : : ++i;
1577 : : out << ' ' << static_cast<int64_t>(ops[i]);
1578 : : /* fall through */
1579 : :
1580 : : /* Opcodes with *two* operands. */
1581 : : case Opcode::Ld_Tup:
1582 : : case Opcode::St_Tup_Null:
1583 : : case Opcode::St_Tup_i:
1584 : : case Opcode::St_Tup_f:
1585 : : case Opcode::St_Tup_d:
1586 : : case Opcode::St_Tup_b:
1587 : : case Opcode::Putc:
1588 : : ++i;
1589 : : out << ' ' << static_cast<int64_t>(ops[i]);
1590 : : /* fall through */
1591 : :
1592 : : /* Opcodes with *one* operand. */
1593 : : case Opcode::Ld_Ctx:
1594 : : case Opcode::Upd_Ctx:
1595 : : case Opcode::Print_i:
1596 : : case Opcode::Print_f:
1597 : : case Opcode::Print_d:
1598 : : case Opcode::Print_s:
1599 : : case Opcode::Print_b:
1600 : : case Opcode::Ld_s:
1601 : : case Opcode::Ld_b:
1602 : : case Opcode::St_s:
1603 : : case Opcode::St_b:
1604 : : ++i;
1605 : : out << ' ' << static_cast<int64_t>(ops[i]);
1606 : : /* fall through */
1607 : :
1608 : : default:;
1609 : : }
1610 : : out << '\n';
1611 : : }
1612 : : out << " Stack:\n";
1613 : : for (std::size_t i = top_; i --> 0; ) {
1614 : : if (null_bits_[i])
1615 : : out << " NULL\n";
1616 : : else
1617 : : out << " " << values_[i] << '\n';
1618 : : }
1619 : : out.flush();
1620 : : }
1621 : :
1622 : : void StackMachine::dump() const { dump(std::cerr); }
1623 : : M_LCOV_EXCL_STOP
|