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