LCOV - code coverage report
Current view: top level - src/backend - StackMachine.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 837 944 88.7 %
Date: 2025-03-25 01:19:55 Functions: 22 32 68.8 %
Branches: 558 1012 55.1 %

           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

Generated by: LCOV version 1.16