LCOV - code coverage report
Current view: top level - src/backend - WasmUtil.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 136 367 37.1 %
Date: 2025-03-25 01:19:55 Functions: 86 1796 4.8 %
Branches: 184 1250 14.7 %

           Branch data     Line data    Source code
       1                 :            : #pragma once
       2                 :            : 
       3                 :            : #include "backend/WasmDSL.hpp"
       4                 :            : #include <functional>
       5                 :            : #include <mutable/catalog/Schema.hpp>
       6                 :            : #include <mutable/IR/PhysicalOptimizer.hpp>
       7                 :            : #include <mutable/parse/AST.hpp>
       8                 :            : #include <mutable/util/concepts.hpp>
       9                 :            : #include <optional>
      10                 :            : #include <variant>
      11                 :            : 
      12                 :            : #include <mutable/util/macro.hpp>
      13                 :            : 
      14                 :            : 
      15                 :            : namespace m {
      16                 :            : 
      17                 :            : namespace wasm {
      18                 :            : 
      19                 :            : // forward declarations
      20                 :            : struct Environment;
      21                 :            : template<bool IsGlobal> struct Buffer;
      22                 :            : template<bool IsGlobal> struct buffer_load_proxy_t;
      23                 :            : template<bool IsGlobal> struct buffer_store_proxy_t;
      24                 :            : template<bool IsGlobal> struct buffer_swap_proxy_t;
      25                 :            : 
      26                 :            : 
      27                 :            : /*======================================================================================================================
      28                 :            :  * Declare SQL helper type for character sequences
      29                 :            :  *====================================================================================================================*/
      30                 :            : 
      31                 :            : struct NChar : Ptr<Charx1>
      32                 :            : {
      33                 :            :     using primitive_type = NChar;
      34                 :            : 
      35                 :            :     private:
      36                 :            :     bool can_be_null_;
      37                 :            :     const CharacterSequence *type_;
      38                 :            : 
      39                 :            :     public:
      40                 :        154 :     NChar(Ptr<Charx1> ptr, bool can_be_null, const CharacterSequence *type)
      41                 :        154 :         : Ptr<Charx1>(ptr), can_be_null_(can_be_null), type_(type)
      42                 :        154 :     { }
      43                 :         20 :     NChar(Ptr<Charx1> ptr, bool can_be_null, std::size_t length, bool guarantees_terminating_nul)
      44                 :         20 :         : Ptr<Charx1>(ptr)
      45                 :         20 :         , can_be_null_(can_be_null)
      46   [ +  -  #  #  :         40 :         , type_(guarantees_terminating_nul ? Type::Get_Varchar(Type::TY_Scalar, length)
                   +  - ]
      47         [ +  - ]:         20 :                                            : Type::Get_Char(Type::TY_Scalar, length))
      48                 :         20 :     { }
      49                 :            : 
      50                 :         44 :     NChar(NChar&) = default;
      51                 :         20 :     NChar(NChar&&) = default;
      52                 :            : 
      53         [ +  - ]:         46 :     NChar clone() const { return NChar(Ptr<Charx1>::clone(), can_be_null_, type_); }
      54                 :            : 
      55                 :        118 :     Ptr<Charx1> val() { return *this; }
      56                 :            : 
      57                 :            :     Ptr<Charx1> insist_not_null() {
      58                 :            :         if (can_be_null())
      59                 :            :             Wasm_insist(clone().not_null(), "must not be NULL");
      60                 :            :         return val();
      61                 :            :     }
      62                 :            : 
      63                 :         10 :     Boolx1 is_null() {
      64         [ +  - ]:         10 :         if (can_be_null()) {
      65                 :         10 :             return Ptr<Charx1>::is_null();
      66                 :            :         } else {
      67                 :          0 :             discard();
      68                 :          0 :             return Boolx1(false);
      69                 :            :         }
      70                 :         10 :     }
      71                 :          0 :     Boolx1 not_null() {
      72         [ #  # ]:          0 :         if (can_be_null()) {
      73                 :          0 :             return Ptr<Charx1>::not_null();
      74                 :            :         } else {
      75                 :          0 :             discard();
      76                 :          0 :             return Boolx1(true);
      77                 :            :         }
      78                 :          0 :     }
      79                 :            : 
      80                 :        118 :     bool can_be_null() const { return can_be_null_; }
      81                 :        196 :     std::size_t length() const { return type_->length; }
      82                 :          0 :     uint64_t size_in_bytes() const { return type_->size() / 8; }
      83                 :         82 :     bool guarantees_terminating_nul() const { return type_->is_varying; }
      84                 :            : };
      85                 :            : 
      86                 :            : 
      87                 :            : /*======================================================================================================================
      88                 :            :  * Decimal
      89                 :            :  *====================================================================================================================*/
      90                 :            : 
      91                 :            : template<signed_integral Base>
      92                 :            : struct Decimal : Expr<Base>
      93                 :            : {
      94                 :            :     using expr_type = Expr<Base>;
      95                 :            :     using arithmetic_type = Base;
      96                 :            : 
      97                 :            :     private:
      98                 :            :     ///> the number of decimal digits right of the decimal point
      99                 :            :     uint32_t scale_;
     100                 :            : 
     101                 :            :     public:
     102                 :            :     /** Constructs a `Decimal` from a given \p value at the given \p scale.  For example, a \p value of `142` at a \p
     103                 :            :      * scale of `2` would represent `1.42`. */
     104                 :        480 :     Decimal(Base value, uint32_t scale) : expr_type(value), scale_(scale) {
     105   [ +  -  +  - ]:        480 :         M_insist(scale != 0);
     106                 :        480 :     }
     107                 :            : 
     108                 :            :     /** Constructs a `Decimal` from a given \p value at the given \p scale.  For example, a \p value of `142` at a \p
     109                 :            :      * scale of `2` would represent `1.42`. */
     110                 :        364 :     Decimal(expr_type value, uint32_t scale) : expr_type(value), scale_(scale) { }
     111                 :            : 
     112   [ +  -  +  - ]:          8 :     Decimal(Decimal &other) : Decimal(other.val(), other.scale_) { }
     113                 :            :     Decimal(Decimal &&other) : Decimal(other.val(), other.scale_) { }
     114                 :            : 
     115                 :            :     /** Constructs a `Decimal` from a given \p value at the given \p scale.  For example, a \p value of `142` at a \p
     116                 :            :      * scale of `2` would represent `142.00`. */
     117                 :         16 :     static Decimal Scaled(expr_type value, uint32_t scale) {
     118                 :         16 :         const arithmetic_type scaling_factor = powi(arithmetic_type(10), scale);
     119   [ +  -  +  - ]:         16 :         return Decimal(value * scaling_factor, scale);
     120                 :          0 :     }
     121                 :            : 
     122                 :            : 
     123                 :            :     public:
     124                 :        844 :     expr_type val() { return *this; }
     125                 :            : 
     126                 :        848 :     uint32_t scale() const { return scale_; }
     127                 :            : 
     128                 :            :     template<signed_integral To>
     129                 :            :     Expr<To> to() { return val().template to<To>() / powi(To(10), To(scale_)); }
     130                 :            : 
     131                 :            :     Decimal clone() const { return Decimal(expr_type::clone(), scale_); }
     132                 :            : 
     133                 :            : 
     134                 :            :     /*------------------------------------------------------------------------------------------------------------------
     135                 :            :      * Unary operations
     136                 :            :      *----------------------------------------------------------------------------------------------------------------*/
     137                 :            : 
     138                 :            :     /** Bitwise negation is not supported by `Decimal` type. */
     139                 :            :     Decimal operator~()
     140                 :            :     requires false
     141                 :            :     { M_unreachable("modulo division on decimals is not defined"); }
     142                 :            : 
     143                 :            : 
     144                 :            :     /*------------------------------------------------------------------------------------------------------------------
     145                 :            :      * Binary operations
     146                 :            :      *----------------------------------------------------------------------------------------------------------------*/
     147                 :            : 
     148                 :         64 :     Decimal operator+(Decimal other) {
     149   [ +  +  +  + ]:         64 :         if (this->scale_ == other.scale_)
     150   [ +  -  +  -  :         24 :             return Decimal(this->val() + other.val(), scale_); // fall back to regular integer arithmetic
          +  -  +  -  +  
                -  +  - ]
     151                 :            : 
     152   [ +  +  +  + ]:         40 :         if (this->scale() > other.scale()) {
     153                 :         20 :             const arithmetic_type scaling_factor = powi(arithmetic_type(10), this->scale() - other.scale());
     154   [ +  -  +  -  :         20 :             return Decimal(this->val() + (other * scaling_factor).val() , this->scale());
          -  +  +  -  +  
          -  +  -  +  -  
          -  +  +  -  +  
                      - ]
     155                 :            :         } else {
     156                 :         20 :             const arithmetic_type scaling_factor = powi(arithmetic_type(10), other.scale() - this->scale());
     157   [ +  -  +  -  :         20 :             return Decimal((*this * scaling_factor).val() + other.val() , other.scale());
          -  +  +  -  +  
          -  +  -  +  -  
          -  +  +  -  +  
                      - ]
     158                 :            :         }
     159                 :         64 :     }
     160                 :            : 
     161                 :            :     template<typename T>
     162                 :            :     requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
     163   [ +  -  +  -  :          8 :     Decimal operator+(T &&other) { return Decimal(*this + Scaled(expr_t<T>(other), scale_)); }
          -  +  +  -  +  
          -  -  +  +  -  
          +  -  -  +  +  
             -  +  -  -  
                      + ]
     164                 :            : 
     165                 :         64 :     Decimal operator-(Decimal other) {
     166   [ +  +  +  + ]:         64 :         if (this->scale_ == other.scale_)
     167   [ +  -  +  -  :         24 :             return Decimal(this->val() - other.val(), scale_); // fall back to regular integer arithmetic
          +  -  +  -  +  
                -  +  - ]
     168                 :            : 
     169   [ +  +  +  + ]:         40 :         if (this->scale() > other.scale()) {
     170                 :         20 :             const arithmetic_type scaling_factor = powi(arithmetic_type(10), this->scale() - other.scale());
     171   [ +  -  +  -  :         20 :             return Decimal(this->val() - (other * scaling_factor).val() , this->scale());
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     172                 :            :         } else {
     173                 :         20 :             const arithmetic_type scaling_factor = powi(arithmetic_type(10), other.scale() - this->scale());
     174   [ +  -  +  -  :         20 :             return Decimal((*this * scaling_factor).val() - other.val() , other.scale());
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     175                 :            :         }
     176                 :         64 :     }
     177                 :            : 
     178                 :            :     template<typename T>
     179                 :            :     requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
     180   [ +  -  +  -  :          4 :     Decimal operator-(T &&other) { return Decimal(*this - Scaled(expr_t<T>(other), scale_)); }
          -  +  +  -  +  
                -  -  + ]
     181                 :            : 
     182                 :         56 :     Decimal operator*(Decimal other) {
     183                 :         56 :         uint32_t smaller_scale = this->scale();
     184                 :         56 :         uint32_t higher_scale = other.scale();
     185   [ +  +  +  + ]:         56 :         if (smaller_scale > higher_scale)
     186                 :         20 :             std::swap(smaller_scale, higher_scale);
     187                 :         56 :         M_insist(smaller_scale <= higher_scale);
     188                 :         56 :         const arithmetic_type scaling_factor = powi(arithmetic_type(10), smaller_scale);
     189   [ +  -  +  -  :         56 :         return Decimal((this->val() * other.val()) / scaling_factor, higher_scale);
          +  -  +  -  +  
          -  +  -  +  -  
                   +  - ]
     190                 :          0 :     }
     191                 :            : 
     192                 :            :     template<typename T>
     193                 :            :     requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
     194   [ +  -  +  -  :         88 :     Decimal operator*(T &&other) { return Decimal(this->val() * expr_t<T>(other), scale()); }
          -  +  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  -  +  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          -  +  +  -  +  
             -  +  -  -  
                      + ]
     195                 :            : 
     196                 :         60 :     Decimal operator/(Decimal other) {
     197                 :         60 :         const uint32_t scale_res = std::max(this->scale(), other.scale());
     198                 :         60 :         const arithmetic_type scaling_factor = powi(arithmetic_type(10), scale_res + other.scale() - this->scale());
     199   [ +  -  +  -  :         60 :         return Decimal((this->val() * scaling_factor) / other.val(), scale_res);
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     200                 :          0 :     }
     201                 :            : 
     202                 :            :     template<typename T>
     203                 :            :     requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
     204   [ +  -  +  -  :          4 :     Decimal operator/(T &&other) { return Decimal(this->val() / expr_t<T>(other), scale()); }
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     205                 :            : 
     206                 :            :     /** Modulo division is not supported by `Decimal` type. */
     207                 :            :     template<typename T>
     208                 :            :     requires false
     209                 :            :     Decimal operator%(T&&) { M_unreachable("modulo division on decimals is not defined"); }
     210                 :            : 
     211                 :            :     /** Bitwise and is not supported by `Decimal` type. */
     212                 :            :     template<typename T>
     213                 :            :     requires false
     214                 :            :     Decimal operator bitand(T&&) { M_unreachable("bitwise and on decimals is not defined"); }
     215                 :            : 
     216                 :            :     /** Bitwise or is not supported by `Decimal` type. */
     217                 :            :     template<typename T>
     218                 :            :     requires false
     219                 :            :     Decimal operator bitor(T&&) { M_unreachable("bitwise or on decimals is not defined"); }
     220                 :            : 
     221                 :            :     /** Bitwise xor (exclusive or) is not supported by `Decimal` type. */
     222                 :            :     template<typename T>
     223                 :            :     requires false
     224                 :            :     Decimal operator xor(T&&) { M_unreachable("exclusive or on decimals is not defined"); }
     225                 :            : 
     226                 :            :     /** Shift left is not supported by `Decimal` type. */
     227                 :            :     template<typename T>
     228                 :            :     requires false
     229                 :            :     Decimal operator<<(T&&) { M_unreachable("shift left on decimals is not defined"); }
     230                 :            : 
     231                 :            :     /** Shift right is not supported by `Decimal` type. */
     232                 :            :     template<typename T>
     233                 :            :     requires false
     234                 :            :     Decimal operator>>(T&&) { M_unreachable("shift right on decimals is not defined"); }
     235                 :            : 
     236                 :            :     /** Shift right is not supported by `Decimal` type. */
     237                 :            :     template<typename T>
     238                 :            :     requires false
     239                 :            :     Decimal rotl(T&&) { M_unreachable("rotate left on decimals is not defined"); }
     240                 :            : 
     241                 :            :     /** Shift right is not supported by `Decimal` type. */
     242                 :            :     template<typename T>
     243                 :            :     requires false
     244                 :            :     Decimal rotr(T&&) { M_unreachable("rotate right on decimals is not defined"); }
     245                 :            : 
     246                 :            : 
     247                 :            :     friend std::ostream & operator<<(std::ostream &out, const Decimal &d) {
     248                 :            :         return out << "Decimal: " << static_cast<const expr_type&>(d) << ", scale = " << d.scale_;
     249                 :            :     }
     250                 :            : 
     251                 :            :     void dump(std::ostream &out) const { out << *this << std::endl; }
     252                 :            :     void dump() const { dump(std::cerr); }
     253                 :            : };
     254                 :            : 
     255                 :            : /*----------------------------------------------------------------------------------------------------------------------
     256                 :            :  * Binary operations
     257                 :            :  *--------------------------------------------------------------------------------------------------------------------*/
     258                 :            : 
     259                 :            : template<typename Base>
     260                 :            : requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
     261                 :          4 : Decimal<typename expr_t<Base>::type> operator+(Base &&left, Decimal<typename expr_t<Base>::type> right)
     262                 :            : {
     263                 :          4 :     return right + left;
     264                 :            : }
     265                 :            : 
     266                 :            : template<typename Base>
     267                 :            : requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
     268                 :          4 : Decimal<typename expr_t<Base>::type> operator-(Base &&left, Decimal<typename expr_t<Base>::type> right)
     269                 :            : {
     270   [ +  -  +  -  :          4 :     return Decimal<typename expr_t<Base>::type>::Scaled(expr_t<Base>(left), right.scale()) - right;
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     271                 :          0 : }
     272                 :            : 
     273                 :            : template<typename Base>
     274                 :            : requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
     275                 :          4 : Decimal<typename expr_t<Base>::type> operator*(Base &&left, Decimal<typename expr_t<Base>::type> right)
     276                 :            : {
     277                 :          4 :     return right * left;
     278                 :            : }
     279                 :            : 
     280                 :            : template<typename Base>
     281                 :            : requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
     282                 :          4 : Decimal<typename expr_t<Base>::type> operator/(Base &&left, Decimal<typename expr_t<Base>::type> right)
     283                 :            : {
     284   [ +  -  +  -  :          4 :     return Decimal<typename expr_t<Base>::type>(expr_t<Base>(left), 0) / right;
          +  -  -  +  +  
          -  +  -  +  -  
                   -  + ]
     285                 :          0 : }
     286                 :            : 
     287                 :            : using Decimal32 = Decimal<int32_t>;
     288                 :            : using Decimal64 = Decimal<int64_t>;
     289                 :            : 
     290                 :            : 
     291                 :            : 
     292                 :            : /*======================================================================================================================
     293                 :            :  * Declare valid SQL types
     294                 :            :  *====================================================================================================================*/
     295                 :            : 
     296                 :            : template<typename>
     297                 :            : struct is_sql_type;
     298                 :            : 
     299                 :            : template<typename>
     300                 :            : struct is_sql_addr_type;
     301                 :            : 
     302                 :            : #define SQL_TYPES_SCALAR(X) \
     303                 :            :     X(_Boolx1) \
     304                 :            :     X(_I8x1) \
     305                 :            :     X(_I16x1) \
     306                 :            :     X(_I32x1) \
     307                 :            :     X(_I64x1) \
     308                 :            :     X(_Floatx1) \
     309                 :            :     X(_Doublex1) \
     310                 :            :     X(NChar)
     311                 :            : 
     312                 :            : #define SQL_ADDR_TYPES_SCALAR(X) \
     313                 :            :     X(Ptr<Boolx1>) \
     314                 :            :     X(Ptr<I8x1>) \
     315                 :            :     X(Ptr<I16x1>) \
     316                 :            :     X(Ptr<I32x1>) \
     317                 :            :     X(Ptr<I64x1>) \
     318                 :            :     X(Ptr<Floatx1>) \
     319                 :            :     X(Ptr<Doublex1>) \
     320                 :            :     X(NChar)
     321                 :            : 
     322                 :            : #define SQL_TYPES(X) \
     323                 :            :     SQL_TYPES_SCALAR(X) \
     324                 :            :     X(_Boolx16) \
     325                 :            :     X(_Boolx32) \
     326                 :            :     X(_I8x16) \
     327                 :            :     X(_I8x32) \
     328                 :            :     X(_I16x8) \
     329                 :            :     X(_I16x16) \
     330                 :            :     X(_I16x32) \
     331                 :            :     X(_I32x4) \
     332                 :            :     X(_I32x8) \
     333                 :            :     X(_I32x16) \
     334                 :            :     X(_I32x32) \
     335                 :            :     X(_I64x2) \
     336                 :            :     X(_I64x4) \
     337                 :            :     X(_I64x8) \
     338                 :            :     X(_I64x16) \
     339                 :            :     X(_I64x32) \
     340                 :            :     X(_Floatx4) \
     341                 :            :     X(_Floatx8) \
     342                 :            :     X(_Floatx16) \
     343                 :            :     X(_Floatx32) \
     344                 :            :     X(_Doublex2) \
     345                 :            :     X(_Doublex4) \
     346                 :            :     X(_Doublex8) \
     347                 :            :     X(_Doublex16) \
     348                 :            :     X(_Doublex32)
     349                 :            : 
     350                 :            : #define SQL_ADDR_TYPES(X) \
     351                 :            :     SQL_ADDR_TYPES_SCALAR(X) \
     352                 :            :     X(Ptr<Boolx16>) \
     353                 :            :     X(Ptr<Boolx32>) \
     354                 :            :     X(Ptr<I8x16>) \
     355                 :            :     X(Ptr<I8x32>) \
     356                 :            :     X(Ptr<I16x8>) \
     357                 :            :     X(Ptr<I16x16>) \
     358                 :            :     X(Ptr<I16x32>) \
     359                 :            :     X(Ptr<I32x4>) \
     360                 :            :     X(Ptr<I32x8>) \
     361                 :            :     X(Ptr<I32x16>) \
     362                 :            :     X(Ptr<I32x32>) \
     363                 :            :     X(Ptr<I64x2>) \
     364                 :            :     X(Ptr<I64x4>) \
     365                 :            :     X(Ptr<I64x8>) \
     366                 :            :     X(Ptr<I64x16>) \
     367                 :            :     X(Ptr<I64x32>) \
     368                 :            :     X(Ptr<Floatx4>) \
     369                 :            :     X(Ptr<Floatx8>) \
     370                 :            :     X(Ptr<Floatx16>) \
     371                 :            :     X(Ptr<Floatx32>) \
     372                 :            :     X(Ptr<Doublex2>) \
     373                 :            :     X(Ptr<Doublex4>) \
     374                 :            :     X(Ptr<Doublex8>) \
     375                 :            :     X(Ptr<Doublex16>) \
     376                 :            :     X(Ptr<Doublex32>)
     377                 :            : 
     378                 :            : #define ADD_EXPR_SQL_TYPE(TYPE) template<> struct is_sql_type<TYPE>{};
     379                 :            : SQL_TYPES(ADD_EXPR_SQL_TYPE)
     380                 :            : #undef ADD_EXPR_SQL_TYPE
     381                 :            : 
     382                 :            : #define ADD_EXPR_SQL_ADDR_TYPE(TYPE) template<> struct is_sql_addr_type<TYPE>{};
     383                 :            : SQL_ADDR_TYPES(ADD_EXPR_SQL_ADDR_TYPE)
     384                 :            : #undef ADD_EXPR_SQL_ADDR_TYPE
     385                 :            : 
     386                 :            : template<typename T>
     387                 :            : concept sql_type = requires { is_sql_type<T>{}; };
     388                 :            : 
     389                 :            : template<typename T>
     390                 :            : concept sql_addr_type = requires { is_sql_addr_type<T>{}; };
     391                 :            : 
     392                 :            : using SQL_t = std::variant<
     393                 :            :     std::monostate
     394                 :            : #define ADD_TYPE(TYPE) , TYPE
     395                 :            :     SQL_TYPES(ADD_TYPE)
     396                 :            : #undef ADD_TYPE
     397                 :            : >;
     398                 :            : 
     399                 :            : using SQL_addr_t = std::variant<
     400                 :            :     std::monostate
     401                 :            : #define ADD_TYPE(TYPE) , TYPE
     402                 :            :     SQL_ADDR_TYPES(ADD_TYPE)
     403                 :            : #undef ADD_TYPE
     404                 :            : >;
     405                 :            : 
     406                 :            : template<typename T>
     407                 :            : concept sql_boolean_type = sql_type<T> and boolean<typename T::type>;
     408                 :            : 
     409                 :            : using SQL_boolean_t = std::variant<std::monostate, _Boolx1, _Boolx16, _Boolx32>;
     410                 :            : 
     411                 :            : 
     412                 :            : /*======================================================================================================================
     413                 :            :  * Helper functions for SQL types
     414                 :            :  *====================================================================================================================*/
     415                 :            : 
     416                 :         60 : inline void discard(SQL_t &variant)
     417                 :            : {
     418                 :         60 :     std::visit(overloaded {
     419                 :         60 :         []<sql_type T>(T actual) -> void { actual.discard(); },
     420                 :          0 :         [](std::monostate) -> void { M_unreachable("invalid variant"); },
     421                 :         60 :     }, variant);
     422                 :         60 : }
     423                 :            : 
     424                 :          0 : inline void discard(SQL_addr_t &variant)
     425                 :            : {
     426                 :          0 :     std::visit(overloaded {
     427                 :          0 :         []<sql_addr_type T>(T actual) -> void { actual.discard(); },
     428                 :          0 :         [](std::monostate) -> void { M_unreachable("invalid variant"); },
     429                 :          0 :     }, variant);
     430                 :          0 : }
     431                 :            : 
     432                 :            : template<sql_type To>
     433                 :          0 : inline To convert(SQL_t &variant)
     434                 :            : {
     435                 :            :     using type = typename To::type;
     436                 :            :     static constexpr std::size_t num_simd_lanes = To::num_simd_lanes;
     437                 :            : 
     438                 :          0 :     return std::visit(overloaded {
     439                 :          0 :         [](auto actual) -> To requires requires { actual.template to<type, num_simd_lanes>(); } {
     440                 :          0 :             return actual.template to<type, num_simd_lanes>();
     441                 :            :         },
     442                 :            :         [](NChar actual) -> NChar requires std::same_as<To, NChar> {
     443                 :            :             return actual;
     444                 :            :         },
     445                 :          0 :         [](auto actual) -> To requires (not requires { actual.template to<type, num_simd_lanes>(); }) {
     446                 :          0 :             M_unreachable("illegal conversion");
     447                 :            :         },
     448                 :          0 :         [](std::monostate) -> To { M_unreachable("invalid variant"); },
     449                 :          0 :     }, variant);
     450                 :            : }
     451                 :            : 
     452                 :          0 : inline bool can_be_null(const SQL_t &variant)
     453                 :            : {
     454                 :          0 :     return std::visit(overloaded {
     455                 :          0 :         []<sql_type T>(const T &actual) -> bool { return actual.can_be_null(); },
     456                 :          0 :         [](std::monostate) -> bool { M_unreachable("invalid variant"); },
     457                 :          0 :     }, variant);
     458                 :            : }
     459                 :            : 
     460                 :            : template<std::size_t L = 1>
     461                 :          0 : inline Bool<L> is_null(SQL_t &variant)
     462                 :            : {
     463                 :          0 :     return std::visit(overloaded {
     464                 :          0 :         []<sql_type T>(T actual) -> Bool<L> requires requires { { actual.is_null() } -> std::same_as<Bool<L>>; } {
     465                 :          0 :             return actual.is_null();
     466                 :            :         },
     467                 :          0 :         []<sql_type T>(T actual) -> Bool<L> requires (not requires { { actual.is_null() } -> std::same_as<Bool<L>>; }) {
     468                 :          0 :             M_unreachable("invalid type for given number of SIMD lanes");
     469                 :            :         },
     470                 :          0 :         [](std::monostate) -> Bool<L> { M_unreachable("invalid variant"); },
     471                 :          0 :     }, variant);
     472                 :            : }
     473                 :            : 
     474                 :            : template<std::size_t L = 1>
     475                 :          0 : inline Bool<L> not_null(SQL_t &variant)
     476                 :            : {
     477                 :          0 :     return std::visit(overloaded {
     478                 :          0 :         []<sql_type T>(T actual) -> Bool<L> requires requires { { actual.not_null() } -> std::same_as<Bool<L>>; } {
     479                 :          0 :             return actual.not_null();
     480                 :            :         },
     481                 :          0 :         []<sql_type T>(T actual) -> Bool<L> requires (not requires { { actual.not_null() } -> std::same_as<Bool<L>>; }) {
     482                 :          0 :             M_unreachable("invalid type for given number of SIMD lanes");
     483                 :            :         },
     484                 :          0 :         [](std::monostate) -> Bool<L> { M_unreachable("invalid variant"); },
     485                 :          0 :     }, variant);
     486                 :            : }
     487                 :            : 
     488                 :            : 
     489                 :            : /*======================================================================================================================
     490                 :            :  * ExprCompiler
     491                 :            :  *====================================================================================================================*/
     492                 :            : 
     493                 :            : /** Compiles AST expressions `m::Expr` to Wasm ASTs `m::wasm::Expr<T>`.  Also supports compiling `m::cnf::CNF`s. */
     494                 :            : struct ExprCompiler : ast::ConstASTExprVisitor
     495                 :            : {
     496                 :            :     private:
     497                 :            :     ///> current intermediate results during AST traversal and compilation
     498                 :            :     SQL_t intermediate_result_;
     499                 :            :     ///> the environment to use for resolving designators to `Expr<T>`s
     500                 :            :     const Environment &env_;
     501                 :            : 
     502                 :            :     public:
     503                 :          0 :     ExprCompiler(const Environment &env) : env_(env) { }
     504                 :            : 
     505                 :            :     ///> Compiles a `m::Expr` \p e of statically unknown type to a `SQL_t`.
     506                 :          0 :     SQL_t compile(const m::ast::Expr &e) {
     507                 :          0 :         (*this)(e);
     508                 :          0 :         return std::move(intermediate_result_);
     509                 :            :     }
     510                 :            :     ///> Compile a `m::Expr` \p e of statically known type to \tparam T.
     511                 :            :     template<sql_type T>
     512                 :          0 :     T compile(const m::ast::Expr &e) {
     513                 :          0 :         (*this)(e);
     514                 :          0 :         M_insist(std::holds_alternative<T>(intermediate_result_));
     515                 :          0 :         return *std::get_if<T>(&intermediate_result_);
     516                 :            :     }
     517                 :            : 
     518                 :            :     ///> Compile a `m::cnf::CNF` \p cnf of statically unknown type to a `SQL_t`.
     519                 :            :     SQL_boolean_t compile(const cnf::CNF &cnf);
     520                 :            : 
     521                 :            :     ///> Compile a `m::cnf::CNF` \p cnf of statically known type to \tparam T.
     522                 :            :     template<sql_boolean_type T>
     523                 :          0 :     T compile(const cnf::CNF &cnf) {
     524                 :          0 :         auto result = compile(cnf);
     525         [ #  # ]:          0 :         M_insist(std::holds_alternative<T>(result));
     526         [ #  # ]:          0 :         return *std::get_if<T>(&result);
     527                 :          0 :     }
     528                 :            : 
     529                 :            :     private:
     530                 :            :     using ConstASTExprVisitor::operator();
     531                 :            :     void operator()(const ast::ErrorExpr&) override;
     532                 :            :     void operator()(const ast::Designator &op) override;
     533                 :            :     void operator()(const ast::Constant &op) override;
     534                 :            :     void operator()(const ast::UnaryExpr &op) override;
     535                 :            :     void operator()(const ast::BinaryExpr &op) override;
     536                 :            :     void operator()(const ast::FnApplicationExpr &op) override;
     537                 :            :     void operator()(const ast::QueryExpr &op) override;
     538                 :            : 
     539                 :          0 :     SQL_t get() { return std::move(intermediate_result_); }
     540                 :            : 
     541                 :            :     template<sql_type T>
     542                 :          0 :     T get() {
     543                 :          0 :         M_insist(std::holds_alternative<T>(intermediate_result_));
     544                 :          0 :         return *std::get_if<T>(&intermediate_result_);
     545                 :            :     }
     546                 :            : 
     547                 :          0 :     void set(SQL_t &&value) {
     548                 :          0 :         intermediate_result_.~SQL_t(); // destroy old
     549                 :          0 :         new (&intermediate_result_) SQL_t(std::move(value)); // placement-new
     550                 :          0 :     }
     551                 :            : 
     552                 :            :     template<sql_type T>
     553   [ #  #  #  #  :          0 :     void set(T &&value) { set(SQL_t(std::forward<T>(value))); }
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     554                 :            : };
     555                 :            : 
     556                 :            : 
     557                 :            : /*======================================================================================================================
     558                 :            :  * Environment
     559                 :            :  *====================================================================================================================*/
     560                 :            : 
     561                 :            : /** Binds `Schema::Identifier`s to `Expr<T>`s. */
     562                 :            : struct Environment
     563                 :            : {
     564                 :            :     private:
     565                 :            :     ///> maps `Schema::Identifier`s to `Expr<T>`s that evaluate to the current expression
     566                 :            :     std::unordered_map<Schema::Identifier, SQL_t> exprs_;
     567                 :            :     ///> maps `Schema::Identifier`s to `Ptr<Expr<T>>`s that evaluate to the address of the current expression
     568                 :            :     std::unordered_map<Schema::Identifier, SQL_addr_t> expr_addrs_;
     569                 :            :     ///> optional predicate if predication is used
     570                 :            :     SQL_boolean_t predicate_;
     571                 :            : 
     572                 :            :     public:
     573                 :         72 :     Environment() = default;
     574                 :            :     Environment(const Environment&) = delete;
     575         [ +  - ]:         72 :     Environment(Environment&&) = default;
     576                 :            : 
     577                 :        144 :     ~Environment() {
     578         [ +  + ]:        204 :         for (auto &p : exprs_)
     579         [ +  - ]:         60 :             discard(p.second);
     580         [ -  + ]:        144 :         for (auto &p : expr_addrs_)
     581         [ #  # ]:          0 :             discard(p.second);
     582                 :            :         /* do not discard `predicate_` to make sure predication predicate is used if it was set */
     583                 :        144 :     }
     584                 :            : 
     585                 :            :     /*----- Access methods -------------------------------------------------------------------------------------------*/
     586                 :            :     /** Returns `true` iff this `Environment` contains \p id. */
     587                 :          0 :     bool has(const Schema::Identifier &id) const { return exprs_.find(id) != exprs_.end(); }
     588                 :            :     /** Returns `true` iff this `Environment` contains the address of \p id. */
     589                 :            :     bool has_addr(const Schema::Identifier &id) const { return expr_addrs_.find(id) != expr_addrs_.end(); }
     590                 :            :     /** Returns `true` iff the entry for identifier \p id has `sql_type` \tparam T. */
     591                 :            :     template<sql_type T>
     592                 :          0 :     bool is(const Schema::Identifier &id) const {
     593                 :          0 :         auto it = exprs_.find(id);
     594                 :          0 :         M_insist(it != exprs_.end(), "identifier not found");
     595                 :          0 :         return std::holds_alternative<T>(it->second);
     596                 :            :     }
     597                 :            :     /** Returns `true` iff the address entry for identifier \p id has `sql_addr_type` \tparam T. */
     598                 :            :     template<sql_addr_type T>
     599                 :            :     bool is_addr(Schema::Identifier id) const {
     600                 :            :         auto it = expr_addrs_.find(id);
     601                 :            :         M_insist(it != expr_addrs_.end(), "identifier not found");
     602                 :            :         return std::holds_alternative<T>(it->second);
     603                 :            :     }
     604                 :            :     /** Returns `true` iff this `Environment` is empty. */
     605         [ #  # ]:          0 :     bool empty() const { return exprs_.empty() and expr_addrs_.empty(); }
     606                 :            : 
     607                 :            :     /** Clears this `Environment`. */
     608                 :            :     void clear() {
     609                 :            :         for (auto &p : exprs_)
     610                 :            :             discard(p.second);
     611                 :            :         for (auto &p : expr_addrs_)
     612                 :            :             discard(p.second);
     613                 :            :         exprs_.clear();
     614                 :            :         expr_addrs_.clear();
     615                 :            :     }
     616                 :            : 
     617                 :            :     ///> Adds a mapping from \p id to \p expr.
     618                 :            :     template<sql_type T>
     619                 :         10 :     void add(Schema::Identifier id, T &&expr) {
     620                 :         10 :         auto res = exprs_.emplace(std::move(id), std::forward<T>(expr));
     621                 :         10 :         M_insist(res.second, "duplicate ID");
     622                 :         10 :     }
     623                 :            :     ///> Adds a mapping from \p id to \p expr.
     624                 :         50 :     void add(Schema::Identifier id, SQL_t &&expr) {
     625                 :         50 :         auto res = exprs_.emplace(std::move(id), std::move(expr));
     626                 :         50 :         M_insist(res.second, "duplicate ID");
     627                 :         50 :     }
     628                 :            : 
     629                 :            :     ///> Adds a address mapping from \p id to \p expr.
     630                 :            :     template<sql_addr_type T>
     631                 :          0 :     void add_addr(Schema::Identifier id, T &&expr) {
     632                 :          0 :         auto res = expr_addrs_.emplace(id, std::forward<T>(expr));
     633                 :          0 :         M_insist(res.second, "duplicate ID");
     634                 :          0 :     }
     635                 :            :     ///> Adds a address mapping from \p id to \p expr.
     636                 :          0 :     void add_addr(Schema::Identifier id, SQL_addr_t &&expr) {
     637                 :          0 :         auto res = expr_addrs_.emplace(id, std::move(expr));
     638                 :          0 :         M_insist(res.second, "duplicate ID");
     639                 :          0 :     }
     640                 :            : 
     641                 :            :     ///> **Copies** all entries of \p other into `this`.
     642                 :          0 :     void add(const Environment &other) {
     643         [ #  # ]:          0 :         for (auto &p : other.exprs_) {
     644                 :          0 :             std::visit(overloaded {
     645                 :          0 :                 [](std::monostate) -> void { M_unreachable("invalid expression"); },
     646   [ #  #  #  #  :          0 :                 [this, &p](auto &e) -> void { this->add(p.first, e.clone()); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     647                 :          0 :             }, p.second);
     648                 :            :         }
     649         [ #  # ]:          0 :         for (auto &p : other.expr_addrs_) {
     650                 :          0 :             std::visit(overloaded {
     651                 :          0 :                 [](std::monostate) -> void { M_unreachable("invalid expression"); },
     652   [ #  #  #  #  :          0 :                 [this, &p](auto &e) -> void { this->add_addr(p.first, e.clone()); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     653                 :          0 :             }, p.second);
     654                 :            :         }
     655                 :          0 :     }
     656                 :            :     ///> **Moves** all entries of \p other into `this`.
     657                 :            :     void add(Environment &&other) {
     658                 :            :         this->exprs_.merge(other.exprs_);
     659                 :            :         M_insist(other.exprs_.empty(), "duplicate ID not moved from other to this");
     660                 :            :         this->expr_addrs_.merge(other.expr_addrs_);
     661                 :            :         M_insist(other.expr_addrs_.empty(), "duplicate ID not moved from other to this");
     662                 :            :     }
     663                 :            : 
     664                 :            :     ///> Returns the **moved** entry for identifier \p id.
     665                 :          0 :     SQL_t extract(const Schema::Identifier &id) {
     666                 :          0 :         auto it = exprs_.find(id);
     667                 :          0 :         M_insist(it != exprs_.end(), "identifier not found");
     668                 :          0 :         auto nh = exprs_.extract(it);
     669         [ #  # ]:          0 :         return std::move(nh.mapped());
     670                 :          0 :     }
     671                 :            :     ///> Returns the **moved** entry for identifier \p id.
     672                 :            :     template<sql_type T>
     673                 :          0 :     T extract(const Schema::Identifier &id) {
     674                 :          0 :         auto it = exprs_.find(id);
     675                 :          0 :         M_insist(it != exprs_.end(), "identifier not found");
     676                 :          0 :         auto nh = exprs_.extract(it);
     677   [ #  #  #  #  :          0 :         M_insist(std::holds_alternative<T>(nh.mapped()));
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     678   [ #  #  #  #  :          0 :         return *std::get_if<T>(&nh.mapped());
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     679                 :          0 :     }
     680                 :            : 
     681                 :            :     ///> Returns the **moved** address entry for identifier \p id.
     682                 :            :     SQL_addr_t extract_addr(const Schema::Identifier &id) {
     683                 :            :         auto it = expr_addrs_.find(id);
     684                 :            :         M_insist(it != expr_addrs_.end(), "identifier not found");
     685                 :            :         auto nh = expr_addrs_.extract(it);
     686                 :            :         return std::move(nh.mapped());
     687                 :            :     }
     688                 :            :     ///> Returns the **moved** address entry for identifier \p id.
     689                 :            :     template<sql_addr_type T>
     690                 :            :     T extract_addr(const Schema::Identifier &id) {
     691                 :            :         auto it = expr_addrs_.find(id);
     692                 :            :         M_insist(it != expr_addrs_.end(), "identifier not found");
     693                 :            :         auto nh = expr_addrs_.extract(it);
     694                 :            :         M_insist(std::holds_alternative<T>(nh.mapped()));
     695                 :            :         return *std::get_if<T>(&nh.mapped());
     696                 :            :     }
     697                 :            : 
     698                 :            :     ///> Returns the **copied** entry for identifier \p id.
     699                 :          0 :     SQL_t get(const Schema::Identifier &id) const {
     700                 :          0 :         auto it = exprs_.find(id);
     701                 :          0 :         M_insist(it != exprs_.end(), "identifier not found");
     702                 :          0 :         return std::visit(overloaded {
     703   [ #  #  #  #  :          0 :             [](auto &e) -> SQL_t { return e.clone(); },
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     704                 :          0 :             [](std::monostate) -> SQL_t { M_unreachable("invalid expression"); },
     705                 :          0 :         }, it->second);
     706                 :            :     }
     707                 :            :     ///> Returns the **copied** entry for identifier \p id.
     708                 :            :     template<sql_type T>
     709                 :        276 :     T get(const Schema::Identifier &id) const {
     710                 :        276 :         auto it = exprs_.find(id);
     711                 :        276 :         M_insist(it != exprs_.end(), "identifier not found");
     712                 :        276 :         M_insist(std::holds_alternative<T>(it->second));
     713                 :        276 :         return std::get_if<T>(&it->second)->clone();
     714                 :            :     }
     715                 :            : 
     716                 :            :     ///> Returns the **copied** address entry for identifier \p id.
     717                 :            :     SQL_addr_t get_addr(const Schema::Identifier &id) const {
     718                 :            :         auto it = expr_addrs_.find(id);
     719                 :            :         M_insist(it != expr_addrs_.end(), "identifier not found");
     720                 :            :         return std::visit(overloaded {
     721                 :            :             [](auto &e) -> SQL_addr_t { return e.clone(); },
     722                 :            :             [](std::monostate) -> SQL_addr_t { M_unreachable("invalid expression"); },
     723                 :            :         }, it->second);
     724                 :            :     }
     725                 :            :     ///> Returns the **copied** address entry for identifier \p id.
     726                 :            :     template<sql_addr_type T>
     727                 :            :     T get_addr(const Schema::Identifier &id) const {
     728                 :            :         auto it = expr_addrs_.find(id);
     729                 :            :         M_insist(it != expr_addrs_.end(), "identifier not found");
     730                 :            :         M_insist(std::holds_alternative<T>(it->second));
     731                 :            :         return std::get_if<T>(&it->second)->clone();
     732                 :            :     }
     733                 :            : 
     734                 :            :     ///> Returns the **copied** entry for identifier \p id.
     735                 :            :     SQL_t operator[](const Schema::Identifier &id) const { return get(id); }
     736                 :            : 
     737                 :            : 
     738                 :            :     /*----- Expression and CNF compilation ---------------------------------------------------------------------------*/
     739                 :            :     ///> Compile \p t by delegating compilation to an `ExprCompiler` for `this` `Environment`.
     740                 :            :     template<typename T>
     741                 :            :     requires requires (ExprCompiler C, T &&t) { C.compile(std::forward<T>(t)); }
     742                 :          0 :     auto compile(T &&t) const {
     743                 :          0 :         ExprCompiler C(*this);
     744   [ #  #  #  #  :          0 :         return C.compile(std::forward<T>(t));
          #  #  #  #  #  
                      # ]
     745                 :          0 :     }
     746                 :            :      ///> Compile \p t by delegating compilation to an `ExprCompiler` for `this` `Environment`.
     747                 :            :     template<typename T, typename U>
     748                 :            :     requires requires (ExprCompiler C, U &&u) { C.compile<T>(std::forward<U>(u)); }
     749                 :          0 :     auto compile(U &&u) const {
     750                 :          0 :         ExprCompiler C(*this);
     751   [ #  #  #  #  :          0 :         return C.compile<T>(std::forward<U>(u));
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     752                 :          0 :     }
     753                 :            : 
     754                 :            :     /*----- Predication ----------------------------------------------------------------------------------------------*/
     755                 :            :     ///> Returns `true` iff `this` `Environment` uses predication.
     756                 :         10 :     bool predicated() const { return not std::holds_alternative<std::monostate>(predicate_); }
     757                 :            : 
     758                 :            :     ///> Adds the predicate \p pred to the predication predicate.
     759                 :          0 :     void add_predicate(SQL_boolean_t &&pred) {
     760                 :          0 :         std::visit(overloaded {
     761                 :          0 :             [](std::monostate) -> void { M_unreachable("invalid predicate"); },
     762                 :          0 :             [this]<sql_boolean_type T>(T &&e) -> void { this->add_predicate<T>(std::forward<T>(e)); },
     763                 :          0 :         }, std::forward<SQL_boolean_t>(pred));
     764                 :          0 :     }
     765                 :            :     ///> Adds the predicate \p pred to the predication predicate.
     766                 :            :     template<sql_boolean_type T>
     767                 :          0 :     void add_predicate(T &&pred) {
     768   [ #  #  #  #  :          0 :         if (predicated()) {
                   #  # ]
     769                 :          0 :             auto old = extract_predicate<T>();
     770   [ #  #  #  #  :          0 :             new (&predicate_) SQL_boolean_t(old and std::forward<T>(pred)); // placement-new
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     771                 :          0 :         } else {
     772                 :          0 :             new (&predicate_) SQL_boolean_t(std::forward<T>(pred)); // placement-new
     773                 :            :         }
     774                 :          0 :     }
     775                 :            :     ///> Adds the predicate compiled from the `cnf::CNF` \p cnf to the predication predicate.
     776         [ #  # ]:          0 :     void add_predicate(const cnf::CNF &cnf) { add_predicate(compile(cnf)); }
     777                 :            : 
     778                 :            :     ///> Returns the **moved** current predication predicate.
     779                 :          0 :     SQL_boolean_t extract_predicate() {
     780                 :          0 :         M_insist(predicated(), "cannot access an undefined or already extracted predicate");
     781                 :          0 :         auto tmp = std::move(predicate_);
     782                 :          0 :         new (&predicate_) SQL_boolean_t(std::monostate()); // placement-new
     783                 :          0 :         return tmp;
     784         [ #  # ]:          0 :     }
     785                 :            :     ///> Returns the **moved** current predication predicate.
     786                 :            :     template<sql_boolean_type T>
     787                 :          0 :     T extract_predicate() {
     788                 :          0 :         M_insist(predicated(), "cannot access an undefined or already extracted predicate");
     789                 :          0 :         M_insist(std::holds_alternative<T>(predicate_));
     790                 :          0 :         auto tmp = *std::get_if<T>(&predicate_);
     791                 :          0 :         predicate_.~SQL_boolean_t(); // destroy old
     792                 :          0 :         new (&predicate_) SQL_boolean_t(std::monostate()); // placement-new
     793                 :          0 :         return tmp;
     794   [ #  #  #  #  :          0 :     }
                   #  # ]
     795                 :            : 
     796                 :            :     ///> Returns the **copied** current predication predicate.
     797                 :            :     SQL_boolean_t get_predicate() const {
     798                 :            :         return std::visit(overloaded {
     799                 :            :             [](const std::monostate&) -> SQL_boolean_t {
     800                 :            :                 M_unreachable("cannot access an undefined or already extracted predicate");
     801                 :            :             },
     802                 :            :             [](const auto &e) -> SQL_boolean_t { return e.clone(); },
     803                 :            :         }, predicate_);
     804                 :            :     }
     805                 :            :     ///> Returns the **copied** current predication predicate.
     806                 :            :     template<sql_boolean_type T>
     807                 :          0 :     T get_predicate() const {
     808                 :          0 :         M_insist(predicated(), "cannot access an undefined or already extracted predicate");
     809                 :          0 :         M_insist(std::holds_alternative<T>(predicate_));
     810                 :          0 :         return std::get_if<T>(&predicate_)->clone();
     811                 :            :     }
     812                 :            : 
     813                 :            :     void dump(std::ostream &out) const;
     814                 :            :     void dump() const;
     815                 :            : };
     816                 :            : 
     817                 :            : 
     818                 :            : /*======================================================================================================================
     819                 :            :  * CodeGenContext
     820                 :            :  *====================================================================================================================*/
     821                 :            : 
     822                 :            : struct Scope
     823                 :            : {
     824                 :            :     friend struct CodeGenContext;
     825                 :            : 
     826                 :            :     private:
     827                 :            :     Environment inner_; ///< environment active during `this` `Scope`s lifetime
     828                 :            :     Environment *outer_; ///< environment active before and after `this` `Scope`s lifetime
     829                 :            : 
     830                 :            :     Scope() = delete;
     831                 :            :     Scope(Environment inner);
     832                 :            : 
     833                 :            :     Scope(const Scope&) = delete;
     834                 :            :     Scope(Scope&&) = default;
     835                 :            : 
     836                 :            :     public:
     837                 :            :     ~Scope();
     838                 :            : 
     839                 :            :     /** Ends `this` `Scope` by returning the currently active environment and setting the former one active again. */
     840                 :            :     Environment extract();
     841                 :            : };
     842                 :            : 
     843                 :            : /** The Wasm `CodeGenContext` provides context information necessary for code generation.
     844                 :            :  *
     845                 :            :  * The context contains:
     846                 :            :  * - an `Environment` of named values, e.g. SQL attribute values
     847                 :            :  * - an `ExprCompiler` to compile expressions within the current `Environment`
     848                 :            :  * - the number of tuples written to the result set
     849                 :            :  * / the number of SIMD lanes currently used
     850                 :            :  */
     851                 :            : struct CodeGenContext
     852                 :            : {
     853                 :            :     friend struct Scope;
     854                 :            : 
     855                 :            :     private:
     856                 :         80 :     Environment *env_ = nullptr; ///< environment for locally bound identifiers
     857                 :            :     Global<U32x1> num_tuples_; ///< variable to hold the number of result tuples produced
     858                 :            :     std::unordered_map<const char*, std::pair<uint32_t, NChar>> literals_; ///< maps each literal to its address at which it is stored
     859                 :            :     ///> number of SIMD lanes currently used, i.e. 1 for scalar and at least 2 for vectorial values
     860                 :         80 :     std::size_t num_simd_lanes_ = 1;
     861                 :            :     ///> number of SIMD lanes currently preferred, i.e. 1 for scalar and at least 2 for vectorial values
     862                 :         80 :     std::size_t num_simd_lanes_preferred_ = 1;
     863                 :            : 
     864                 :            :     public:
     865                 :        240 :     CodeGenContext() = default;
     866                 :            :     CodeGenContext(const CodeGenContext&) = delete;
     867                 :            : 
     868                 :         80 :     ~CodeGenContext() {
     869                 :            : #ifdef M_ENABLE_SANITY_FIELDS
     870                 :            :         num_tuples_.val().discard();  // artificial use of `num_tuples_` to silence diagnostics if unittests are executed
     871                 :            : #endif
     872         [ -  + ]:         80 :         for (auto &p : literals_)
     873         [ #  # ]:          0 :             p.second.second.discard();
     874                 :         80 :     }
     875                 :            : 
     876                 :            :     /*----- Thread-local instance ------------------------------------------------------------------------------------*/
     877                 :            :     private:
     878                 :            :     static thread_local std::unique_ptr<CodeGenContext> the_context_;
     879                 :            : 
     880                 :            :     public:
     881                 :         80 :     static void Init() {
     882                 :         80 :         M_insist(not the_context_, "must not have a context yet");
     883                 :         80 :         the_context_ = std::make_unique<CodeGenContext>();
     884                 :         80 :     }
     885                 :         80 :     static void Dispose() {
     886                 :         80 :         M_insist(bool(the_context_), "must have a context");
     887                 :         80 :         the_context_ = nullptr;
     888                 :         80 :     }
     889                 :        266 :     static CodeGenContext & Get() {
     890                 :        266 :         M_insist(bool(the_context_), "must have a context");
     891                 :        266 :         return *the_context_;
     892                 :            :     }
     893                 :            : 
     894                 :            :     /** Creates a new, *scoped* `Environment`.  The new `Environment` is immediately used by the `CodeGenContext`.  When
     895                 :            :      * the `Scope` is destroyed (i.e. when it goes out of scope or its method `extract()` is called), the *old*
     896                 :            :      * `Environment` is used again by the `CodeGenContext`. */
     897         [ +  - ]:         72 :     Scope scoped_environment() { return Scope(Environment()); }
     898                 :            :     /** Creates a new `Scope` using the `Environment` `env` which is immediately used by the `CodeGenContext`.  When
     899                 :            :      * the `Scope` is destroyed (i.e. when it goes out of scope or its method `extract()` is called), the *old*
     900                 :            :      * `Environment` is used again by the `CodeGenContext`. */
     901         [ #  # ]:          0 :     Scope scoped_environment(Environment env) { return Scope(std::move(env)); }
     902                 :            : 
     903                 :            :     /*----- Access methods -------------------------------------------------------------------------------------------*/
     904                 :            :     /** Returns the current `Environment`. */
     905                 :         20 :     Environment & env() { M_insist(bool(env_)); return *env_; }
     906                 :            :     /** Returns the current `Environment`. */
     907                 :            :     const Environment & env() const { M_insist(bool(env_)); return *env_; }
     908                 :            : 
     909                 :            :     /** Returns the number of result tuples produced. */
     910                 :          0 :     U32x1 num_tuples() const { return num_tuples_; }
     911                 :            :     /** Set the number of result tuples produced to `n`. */
     912                 :          0 :     void set_num_tuples(U32x1 n) { num_tuples_ = n; }
     913                 :            :     /** Increments the number of result tuples produced by `n`. */
     914                 :          0 :     void inc_num_tuples(U32x1 n = U32x1(1)) { num_tuples_ += n; }
     915                 :            : 
     916                 :            :     /** Adds the string literal `literal` located at pointer offset `ptr`. */
     917                 :          0 :     void add_literal(const char *literal, uint32_t ptr) {
     918         [ #  # ]:          0 :         auto [_, inserted] = literals_.emplace(
     919                 :            :             std::piecewise_construct,
     920                 :          0 :             std::forward_as_tuple(literal),
     921   [ #  #  #  # ]:          0 :             std::forward_as_tuple(ptr, NChar(Ptr<Charx1>(U32x1(ptr)), false, strlen(literal) + 1, true))
     922                 :            :         );
     923                 :          0 :         M_insist(inserted);
     924                 :          0 :     }
     925                 :            :     /** Returns the raw address at which `literal` is stored. */
     926                 :          0 :     uint32_t get_literal_raw_address(const char *literal) const {
     927                 :          0 :         auto it = literals_.find(literal);
     928                 :          0 :         M_insist(it != literals_.end(), "unknown literal");
     929                 :          0 :         return it->second.first;
     930                 :            :     }
     931                 :            :     /** Returns the address at which `literal` is stored. */
     932                 :          0 :     NChar get_literal_address(const char *literal) const {
     933                 :          0 :         auto it = literals_.find(literal);
     934                 :          0 :         M_insist(it != literals_.end(), "unknown literal");
     935                 :          0 :         return it->second.second.clone();
     936                 :            :     }
     937                 :            : 
     938                 :            :     /** Returns the number of SIMD lanes used. */
     939                 :          0 :     std::size_t num_simd_lanes() const { return num_simd_lanes_; }
     940                 :            :     /** Sets the number of SIMD lanes used to `n`. */
     941                 :         10 :     void set_num_simd_lanes(std::size_t n) { num_simd_lanes_ = n; }
     942                 :            : 
     943                 :            :     /** Returns the number of SIMD lanes preferred by other operators. */
     944                 :         10 :     std::size_t num_simd_lanes_preferred() const { return num_simd_lanes_preferred_; }
     945                 :            :     /** Updates the number of SIMD lanes preferred by `n`. */
     946                 :         10 :     void update_num_simd_lanes_preferred(std::size_t n) {
     947                 :         10 :         num_simd_lanes_preferred_ = std::max(num_simd_lanes_preferred_, n);
     948                 :         10 :     }
     949                 :            : };
     950                 :            : 
     951                 :         72 : inline Scope::Scope(Environment inner)
     952                 :         72 :     : inner_(std::move(inner))
     953                 :            : {
     954         [ +  - ]:         72 :     outer_ = std::exchange(CodeGenContext::Get().env_, &inner_);
     955                 :         72 : }
     956                 :            : 
     957                 :         72 : inline Scope::~Scope()
     958                 :            : {
     959         [ +  - ]:         72 :     CodeGenContext::Get().env_ = outer_;
     960                 :         72 : }
     961                 :            : 
     962                 :          0 : inline Environment Scope::extract()
     963                 :            : {
     964                 :          0 :     CodeGenContext::Get().env_ = outer_;
     965                 :          0 :     return std::move(inner_);
     966                 :            : }
     967                 :            : 
     968                 :            : 
     969                 :            : /*======================================================================================================================
     970                 :            :  * compile data layout
     971                 :            :  *====================================================================================================================*/
     972                 :            : 
     973                 :            : /** Compiles the data layout \p layout containing tuples of schema \p layout_schema such that it sequentially stores
     974                 :            :  * tuples of schema \p tuple_value_schema starting at memory address \p base_address and tuple ID \p tuple_id.  The store
     975                 :            :  * does *not* have to be done in a single pass, i.e. the returned code may be emitted into a function which can be
     976                 :            :  * called multiple times and each call starts storing at exactly the point where it has ended in the last call.  The
     977                 :            :  * given variable \p tuple_id will be incremented automatically before advancing to the next tuple (i.e. code for
     978                 :            :  * this will be emitted at the start of the block returned as third element).  Predication is supported and emitted
     979                 :            :  * respectively.  SIMDfication is supported and will be emitted iff \p num_simd_lanes is greater than 1.
     980                 :            :  *
     981                 :            :  * Does not emit any code but returns three `wasm::Block`s containing code: the first one initializes all needed
     982                 :            :  * variables, the second one stores one tuple, and the third one advances to the next tuple.  Additionally, if not
     983                 :            :  * \tparam IsStore, adds the addresses of the values of tuples with schema \p tuple_addr_schema into the current
     984                 :            :  * environment. */
     985                 :            : template<VariableKind Kind>
     986                 :            : std::tuple<Block, Block, Block>
     987                 :            : compile_store_sequential(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr<void> base_address,
     988                 :            :                          const storage::DataLayout &layout, std::size_t num_simd_lanes, const Schema &layout_schema,
     989                 :            :                          Variable<uint32_t, Kind, false> &tuple_id);
     990                 :            : 
     991                 :            : /** Compiles the data layout \p layout containing tuples of schema \p layout_schema such that it sequentially stores
     992                 :            :  * tuples of schema \p tuple_value_schema starting at memory address \p base_address and tuple ID \p tuple_id.  The store
     993                 :            :  * has to be done in a single pass, i.e. the execution of the returned code must *not* be split among multiple
     994                 :            :  * function calls.  The given variable \p tuple_id will be incremented automatically before advancing to the next
     995                 :            :  * tuple (i.e. code for this will be emitted at the start of the block returned as third element).  Predication is
     996                 :            :  * supported and emitted respectively.  SIMDfication is supported and will be emitted iff \p num_simd_lanes is greater
     997                 :            :  * than 1.
     998                 :            :  *
     999                 :            :  * Does not emit any code but returns three `wasm::Block`s containing code: the first one initializes all needed
    1000                 :            :  * variables, the second one stores one tuple, and the third one advances to the next tuple.  Additionally, if not
    1001                 :            :  * \tparam IsStore, adds the addresses of the values of tuples with schema \p tuple_addr_schema into the current
    1002                 :            :  * environment. */
    1003                 :            : template<VariableKind Kind>
    1004                 :            : std::tuple<Block, Block, Block>
    1005                 :            : compile_store_sequential_single_pass(const Schema &tuple_value_schema, const Schema &tuple_addr_schema,
    1006                 :            :                                      Ptr<void> base_address, const storage::DataLayout &layout,
    1007                 :            :                                      std::size_t num_simd_lanes, const Schema &layout_schema,
    1008                 :            :                                      Variable<uint32_t, Kind, false> &tuple_id);
    1009                 :            : 
    1010                 :            : /** Compiles the data layout \p layout containing tuples of schema \p layout_schema such that it sequentially loads
    1011                 :            :  * tuples of schema \p tuple_value_schema starting at memory address \p base_address and tuple ID \p tuple_id.  The given
    1012                 :            :  * variable \p tuple_id will be incremented automatically before advancing to the next tuple (i.e. code for this will
    1013                 :            :  * be emitted at the start of the block returned as third element).  SIMDfication is supported and will be emitted
    1014                 :            :  * iff \p num_simd_lanes is greater than 1.
    1015                 :            :  *
    1016                 :            :  * Does not emit any code but returns three `wasm::Block`s containing code: the first one initializes all needed
    1017                 :            :  * variables, the second one loads one tuple, and the third one advances to the next tuple.  Additionally, if not
    1018                 :            :  * \tparam IsStore, adds the addresses of the values of tuples with schema \p tuple_addr_schema into the current
    1019                 :            :  * environment. */
    1020                 :            : template<VariableKind Kind>
    1021                 :            : std::tuple<Block, Block, Block>
    1022                 :            : compile_load_sequential(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr<void> base_address,
    1023                 :            :                         const storage::DataLayout &layout, std::size_t num_simd_lanes, const Schema &layout_schema,
    1024                 :            :                         Variable<uint32_t, Kind, false> &tuple_id);
    1025                 :            : 
    1026                 :            : /** Compiles the data layout \p layout starting at memory address \p base_address and containing tuples of schema
    1027                 :            :  * \p layout_schema such that it stores the single tuple with schema \p tuple_value_schema and ID \p tuple_id.
    1028                 :            :  *
    1029                 :            :  * Emits the storing code into the current block.  Additionally, if not \tparam IsStore, adds the addresses of the
    1030                 :            :  * values of tuples with schema \p tuple_addr_schema into the current environment. */
    1031                 :            : void compile_store_point_access(const Schema &tuple_value_schema, const Schema &tuple_addr_schema,
    1032                 :            :                                 Ptr<void> base_address, const storage::DataLayout &layout, const Schema &layout_schema,
    1033                 :            :                                 U32x1 tuple_id);
    1034                 :            : 
    1035                 :            : /** Compiles the data layout \p layout starting at memory address \p base_address and containing tuples of schema
    1036                 :            :  * \p layout_schema such that it loads the single tuple with schema \p tuple_value_schema and ID \p tuple_id.
    1037                 :            :  *
    1038                 :            :  * Emits the loading code into the current block and adds the loaded values into the current environment.  Additionally,
    1039                 :            :  * if not \tparam IsStore, adds the addresses of the values of tuples with schema \p tuple_addr_schema into the current
    1040                 :            :  * environment. */
    1041                 :            : void compile_load_point_access(const Schema &tuple_value_schema, const Schema &tuple_addr_schema,
    1042                 :            :                                Ptr<void> base_address, const storage::DataLayout &layout, const Schema &layout_schema,
    1043                 :            :                                U32x1 tuple_id);
    1044                 :            : 
    1045                 :            : 
    1046                 :            : /*======================================================================================================================
    1047                 :            :  * Buffer
    1048                 :            :  *====================================================================================================================*/
    1049                 :            : 
    1050                 :            : template<bool IsGlobal>
    1051                 :            : class buffer_storage;
    1052                 :            : 
    1053                 :            : template<>
    1054                 :            : class buffer_storage<false> {};
    1055                 :            : 
    1056                 :            : template<>
    1057         [ #  # ]:          0 : class buffer_storage<true>
    1058                 :            : {
    1059                 :            :     friend struct Buffer<true>;
    1060                 :            : 
    1061                 :            :     Global<Ptr<void>> base_address_; ///< global backup for base address of buffer
    1062                 :            :     Global<U32x1> size_; ///< global backup for current size of buffer, default initialized to 0
    1063                 :            :     ///> global backup for dynamic capacity of infinite buffer, default initialized to 0
    1064                 :            :     std::optional<Global<U32x1>> capacity_;
    1065                 :            : };
    1066                 :            : 
    1067                 :            : /** Buffers tuples by materializing them into memory. */
    1068                 :            : template<bool IsGlobal>
    1069                 :            : struct Buffer
    1070                 :            : {
    1071                 :            :     private:
    1072                 :            :     ///> parameter type for proxy creation and pipeline resuming methods
    1073                 :            :     using param_t = std::optional<std::reference_wrapper<const Schema>>;
    1074                 :            : 
    1075                 :            :     std::reference_wrapper<const Schema> schema_; ///< schema of buffer
    1076                 :            :     storage::DataLayout layout_; ///< data layout of buffer
    1077                 :            :     bool load_simdfied_ = false; ///< flag whether to load from the buffer in SIMDfied manner
    1078                 :            :     std::optional<Var<Ptr<void>>> base_address_; ///< base address of buffer
    1079                 :            :     std::optional<Var<U32x1>> size_; ///< current size of buffer, default initialized to 0
    1080                 :            :     std::optional<Var<U32x1>> capacity_; ///< dynamic capacity of infinite buffer, default initialized to 0
    1081                 :            :     std::optional<Var<Boolx1>> first_iteration_; ///< flag to indicate first loop iteration for infinite buffer
    1082                 :            :     buffer_storage<IsGlobal> storage_; ///< if `IsGlobal`, contains backups for base address, capacity, and size
    1083                 :            :     setup_t setup_; ///< remaining pipeline initializations
    1084                 :            :     pipeline_t pipeline_; ///< remaining actual pipeline
    1085                 :            :     teardown_t teardown_; ///< remaining pipeline post-processing
    1086                 :            :     ///> function to resume pipeline for entire buffer; expects base address and size of buffer as parameters
    1087                 :            :     mutable std::optional<FunctionProxy<void(void*, uint32_t)>> resume_pipeline_;
    1088                 :            : 
    1089                 :            :     public:
    1090                 :            :     /** Creates a buffer for \p num_tuples tuples (0 means infinite) of schema \p schema using the data layout
    1091                 :            :      * created by \p factory to temporarily materialize tuples before resuming with the remaining pipeline
    1092                 :            :      * initializations \p setup, the actual pipeline \p pipeline, and the post-processing \p teardown.  For global
    1093                 :            :      * finite buffers, emits code to pre-allocate entire buffer into the **current** block. */
    1094                 :            :     Buffer(const Schema &schema, const storage::DataLayoutFactory &factory, bool load_simdfied = false,
    1095                 :            :            std::size_t num_tuples = 0, setup_t setup = setup_t::Make_Without_Parent(),
    1096                 :            :            pipeline_t pipeline = pipeline_t(), teardown_t teardown = teardown_t::Make_Without_Parent());
    1097                 :            : 
    1098                 :            :     Buffer(const Buffer&) = delete;
    1099   [ #  #  #  #  :          0 :     Buffer(Buffer&&) = default;
          #  #  #  #  #  
                      # ]
    1100                 :            : 
    1101                 :            :     ~Buffer();
    1102                 :            : 
    1103                 :            :     Buffer & operator=(Buffer&&) = default;
    1104                 :            : 
    1105                 :            :     /** Returns the schema of the buffer. */
    1106                 :          0 :     const Schema & schema() const { return schema_; }
    1107                 :            :     /** Returns the layout of the buffer. */
    1108                 :          0 :     const storage::DataLayout & layout() const { return layout_; }
    1109                 :            :     /** Returns the base address of the buffer. */
    1110                 :          0 :     Ptr<void> base_address() const {
    1111                 :            :         if constexpr (IsGlobal) {
    1112         [ #  # ]:          0 :             return base_address_ ? base_address_->val() : storage_.base_address_.val(); // since global may be outdated
    1113                 :            :         } else {
    1114                 :          0 :             M_insist(bool(base_address_));
    1115                 :          0 :             return *base_address_;
    1116                 :            :         }
    1117                 :            :     }
    1118                 :            :     /** Returns the current size of the buffer. */
    1119                 :          0 :     U32x1 size() const {
    1120                 :            :         if constexpr (IsGlobal) {
    1121         [ #  # ]:          0 :             return size_ ? size_->val() : storage_.size_.val(); // since global may be outdated
    1122                 :            :         } else {
    1123                 :          0 :             M_insist(bool(size_));
    1124                 :          0 :             return *size_;
    1125                 :            :         }
    1126                 :            :     }
    1127                 :            : 
    1128                 :            :     /** Creates and returns a proxy object to load value tuples of schema \p tuple_value_schema (default: entire tuples)
    1129                 :            :      * and address tuples of schema \p tuple_addr_schema (default: empty tuples) from the buffer. */
    1130                 :            :     buffer_load_proxy_t<IsGlobal> create_load_proxy(param_t tuple_value_schema = param_t(),
    1131                 :            :                                                     param_t tuple_addr_schema = param_t()) const;
    1132                 :            :     /** Creates and returns a proxy object to store tuples of schema \p tuple_schema (default: entire tuples) to the
    1133                 :            :      * buffer. */
    1134                 :            :     buffer_store_proxy_t<IsGlobal> create_store_proxy(param_t tuple_schema = param_t()) const;
    1135                 :            :     /** Creates and returns a proxy object to swap tuples of schema \p tuple_schema (default: entire tuples) in the
    1136                 :            :      * buffer. */
    1137                 :            :     buffer_swap_proxy_t<IsGlobal> create_swap_proxy(param_t tuple_schema = param_t()) const;
    1138                 :            : 
    1139                 :            :     /** Performs the setup of all local variables of this buffer (by reading them from the global backups iff
    1140                 :            :      * \tparam IsGlobal) for a write access.  Must be called before any call to `consume()`. */
    1141                 :            :     void setup();
    1142                 :            :     /** Performs the teardown of all local variables of this buffer (by storing them into the global backups iff
    1143                 :            :      * \tparam IsGlobal) for a write access.  Must be called after all calls to `consume()`. */
    1144                 :            :     void teardown();
    1145                 :            : 
    1146                 :            :     /** Performs the setup of the local base address of this buffer by reading it from the global backup. */
    1147                 :          0 :     void setup_base_address() requires IsGlobal {
    1148                 :          0 :         M_insist(not base_address_, "must not call `setup_base_address()` twice");
    1149         [ #  # ]:          0 :         if (not layout_.is_finite()) {
    1150                 :          0 :             M_insist(bool(storage_.capacity_));
    1151         [ #  # ]:          0 :             Wasm_insist(*storage_.capacity_ != 0U, "buffer must be already allocated");
    1152                 :          0 :         }
    1153                 :          0 :         base_address_.emplace(storage_.base_address_);
    1154                 :          0 :     }
    1155                 :            :     /** Performs the teardown of the local base address of this buffer by destroying it but *without* storing it into
    1156                 :            :      * the global backup. */
    1157                 :          0 :     void teardown_base_address() requires IsGlobal {
    1158                 :          0 :         M_insist(bool(base_address_), "must call `setup_base_address()` before");
    1159                 :          0 :         base_address_.reset();
    1160                 :          0 :     }
    1161                 :            : 
    1162                 :            :     /** Emits code into a separate function to resume the pipeline for each value tuple of schema \p tuple_value_schema
    1163                 :            :      * (default: entire tuples) and address tuple of schema \p tuple_addr_schema (default: empty tuples) in the buffer.
    1164                 :            :      * Used to explicitly resume pipeline for infinite or partially filled buffers. */
    1165                 :            :     void resume_pipeline(param_t tuple_value_schema = param_t(), param_t tuple_addr_schema = param_t()) const;
    1166                 :            :     /** Emits code inline to resume the pipeline for each value tuple of schema \p tuple_value_schema (default: entire
    1167                 :            :      * tuples) and address tuple of schema \p tuple_addr_schema (default: empty tuples) in the buffer.  Due to inlining
    1168                 :            :      * the current `Environment` must not be cleared and this method should be used for n-ary operators.  Used to
    1169                 :            :      * explicitly resume pipeline for infinite or partially filled buffers.  Predication is supported, i.e. if the
    1170                 :            :      * predication predicate is not fulfilled, no tuples will be loaded and thus the pipeline will not be resumed. */
    1171                 :            :     void resume_pipeline_inline(param_t tuple_value_schema = param_t(), param_t tuple_addr_schema = param_t()) const;
    1172                 :            : 
    1173                 :            :     /** Emits code into a separate function to execute the give pipeline \p pipeline for each value tuple of schema \p
    1174                 :            :      * tuple_value_schema (default: entire tuples) and address tuple of schema \p tuple_addr_schema (default: empty
    1175                 :            :      * tuples) in the buffer.  Used to explicitly execute a given pipeline. */
    1176                 :            :     void execute_pipeline(setup_t setup, pipeline_t pipeline, teardown_t teardown,
    1177                 :            :                           param_t tuple_value_schema = param_t(), param_t tuple_addr_schema = param_t()) const;
    1178                 :            :     /** Emits code inline to execute the given pipeline \p pipeline for each value tuple of schema \p tuple_value_schema
    1179                 :            :      * (default: entire tuples) and address tuple of schema \p tuple_addr_schema (default: empty tuples) in the buffer.
    1180                 :            :      * Due to inlining the current `Environment` must not be cleared and this method should be used for n-ary operators.
    1181                 :            :      * Used to explicitly execute a given pipeline. Predication is supported, i.e. if the predication predicate is not
    1182                 :            :      * fulfilled, no tuples will be loaded and thus the pipeline will not be executed. */
    1183                 :            :     void execute_pipeline_inline(setup_t setup, pipeline_t pipeline, teardown_t teardown,
    1184                 :            :                                  param_t tuple_value_schema = param_t(), param_t tuple_addr_schema = param_t()) const;
    1185                 :            : 
    1186                 :            :     /** Emits code to store the current tuple into the buffer.  The behaviour depends on whether the buffer is finite:
    1187                 :            :      * - **finite:** If the buffer is full, resumes the pipeline for each tuple in the buffer and clears the buffer
    1188                 :            :      *               afterwards.
    1189                 :            :      * - **infinite:**  Potentially resizes the buffer but never resumes the pipeline (must be done explicitly by
    1190                 :            :      *                  calling `resume_pipeline()`).
    1191                 :            :      * Predication is supported, i.e. the current tuple is always written in the buffer but can only loaded from it
    1192                 :            :      * later iff the predication predicate is fulfilled. */
    1193                 :            :     void consume();
    1194                 :            : };
    1195                 :            : 
    1196                 :            : using LocalBuffer = Buffer<false>;
    1197                 :            : using GlobalBuffer = Buffer<true>;
    1198                 :            : 
    1199                 :            : 
    1200                 :            : /*======================================================================================================================
    1201                 :            :  * buffer accesses
    1202                 :            :  *====================================================================================================================*/
    1203                 :            : 
    1204                 :            : /** Proxy to implement loads from a buffer. */
    1205                 :            : template<bool IsGlobal>
    1206                 :            : struct buffer_load_proxy_t
    1207                 :            : {
    1208                 :            :     friend struct Buffer<IsGlobal>;
    1209                 :            : 
    1210                 :            :     private:
    1211                 :            :     std::reference_wrapper<const Buffer<IsGlobal>> buffer_; ///< buffer to load from
    1212                 :            :     std::reference_wrapper<const Schema> value_schema_; ///< value entries to load
    1213                 :            :     std::reference_wrapper<const Schema> addr_schema_; ///< address entries to load
    1214                 :            : 
    1215                 :          0 :     buffer_load_proxy_t(const Buffer<IsGlobal> &buffer, const Schema &value_schema, const Schema &addr_schema)
    1216                 :          0 :         : buffer_(std::cref(buffer))
    1217                 :          0 :         , value_schema_(std::cref(value_schema))
    1218                 :          0 :         , addr_schema_(std::cref(addr_schema))
    1219                 :          0 :     { }
    1220                 :            : 
    1221                 :            :     public:
    1222                 :            :     buffer_load_proxy_t(const buffer_load_proxy_t&) = delete;
    1223                 :            :     buffer_load_proxy_t(buffer_load_proxy_t&&) = default;
    1224                 :            : 
    1225                 :            :     buffer_load_proxy_t & operator=(buffer_load_proxy_t&&) = default;
    1226                 :            : 
    1227                 :            :     /** Returns the value entries to load. */
    1228                 :            :     const Schema & value_schema() const { return value_schema_; }
    1229                 :            :     /** Returns the address entries to load. */
    1230                 :            :     const Schema & addr_schema() const { return addr_schema_; }
    1231                 :            : 
    1232                 :            :     /** Loads tuple with ID \p tuple_id into the current environment. */
    1233                 :          0 :     void operator()(U32x1 tuple_id) {
    1234   [ #  #  #  #  :          0 :         Wasm_insist(tuple_id.clone() < buffer_.get().size(), "tuple ID out of bounds");
          #  #  #  #  #  
                #  #  # ]
    1235   [ #  #  #  # ]:          0 :         compile_load_point_access(value_schema_, addr_schema_, buffer_.get().base_address(), buffer_.get().layout(),
    1236   [ #  #  #  # ]:          0 :                                   buffer_.get().schema(), tuple_id);
    1237                 :          0 :     }
    1238                 :            : };
    1239                 :            : 
    1240                 :            : /** Proxy to implement stores to a buffer. */
    1241                 :            : template<bool IsGlobal>
    1242                 :            : struct buffer_store_proxy_t
    1243                 :            : {
    1244                 :            :     friend struct Buffer<IsGlobal>;
    1245                 :            : 
    1246                 :            :     private:
    1247                 :            :     std::reference_wrapper<const Buffer<IsGlobal>> buffer_; ///< buffer to store to
    1248                 :            :     std::reference_wrapper<const Schema> schema_; ///< entries to store
    1249                 :            : 
    1250                 :          0 :     buffer_store_proxy_t(const Buffer<IsGlobal> &buffer, const Schema &schema)
    1251                 :          0 :         : buffer_(std::cref(buffer))
    1252                 :          0 :         , schema_(std::cref(schema))
    1253                 :          0 :     { }
    1254                 :            : 
    1255                 :            :     public:
    1256                 :            :     buffer_store_proxy_t(const buffer_store_proxy_t&) = delete;
    1257                 :            :     buffer_store_proxy_t(buffer_store_proxy_t&&) = default;
    1258                 :            : 
    1259                 :            :     buffer_store_proxy_t & operator=(buffer_store_proxy_t&&) = default;
    1260                 :            : 
    1261                 :            :     /** Returns the entries to store. */
    1262                 :            :     const Schema & schema() const { return schema_; }
    1263                 :            : 
    1264                 :            :     /** Stores values from the current environment to tuple with ID \p tuple_id. */
    1265                 :          0 :     void operator()(U32x1 tuple_id) {
    1266   [ #  #  #  #  :          0 :         static Schema empty_schema;
             #  #  #  # ]
    1267   [ #  #  #  #  :          0 :         Wasm_insist(tuple_id.clone() < buffer_.get().size(), "tuple ID out of bounds");
          #  #  #  #  #  
                #  #  # ]
    1268   [ #  #  #  # ]:          0 :         compile_store_point_access(schema_, empty_schema, buffer_.get().base_address(), buffer_.get().layout(),
    1269   [ #  #  #  # ]:          0 :                                    buffer_.get().schema(), tuple_id);
    1270                 :          0 :     }
    1271                 :            : };
    1272                 :            : 
    1273                 :            : /** Proxy to implement swaps in a buffer. */
    1274                 :            : template<bool IsGlobal>
    1275                 :            : struct buffer_swap_proxy_t
    1276                 :            : {
    1277                 :            :     friend struct Buffer<IsGlobal>;
    1278                 :            : 
    1279                 :            :     private:
    1280                 :            :     std::reference_wrapper<const Buffer<IsGlobal>> buffer_; ///< buffer in which swaps are performed
    1281                 :            :     std::reference_wrapper<const Schema> schema_; ///< entries to swap
    1282                 :            : 
    1283                 :          0 :     buffer_swap_proxy_t(const Buffer<IsGlobal> &buffer, const Schema &schema)
    1284                 :          0 :         : buffer_(std::cref(buffer))
    1285                 :          0 :         , schema_(std::cref(schema))
    1286                 :          0 :     { }
    1287                 :            : 
    1288                 :            :     public:
    1289                 :            :     /** Returns the entries to swap. */
    1290                 :          0 :     const Schema & schema() const { return schema_; }
    1291                 :            : 
    1292                 :            :     /** Swaps tuples with IDs \p first and \p second. */
    1293                 :            :     void operator()(U32x1 first, U32x1 second);
    1294                 :            :     /** Swaps tuples with IDs \p first and \p second where the first one is already loaded and accessible through
    1295                 :            :      * \p env_first.  Note that environments are also swapped afterwards, i.e. \p env_first contains still the values
    1296                 :            :      * of the former tuple with ID \p first which is located at ID \p second after the call, except for `NChar`s
    1297                 :            :      * since they are only pointers to the actual values, i.e. \p env_first contains still the addresses of the
    1298                 :            :      * former tuple with ID \p first where the values of tuple with ID \p second are stored after the call. */
    1299                 :            :     void operator()(U32x1 first, U32x1 second, const Environment &env_first);
    1300                 :            :     /** Swaps tuples with IDs \p first and \p second which are already loaded and accessible through \p env_first and
    1301                 :            :      * \p env_second.  Note that environments are also swapped afterwards, i.e. \p env_first contains still the values
    1302                 :            :      * of the former tuple with ID \p first which is located at ID \p second after the call and vice versa, except
    1303                 :            :      * for `NChar`s since they are only pointers to the actual values, i.e. \p env_first contains still the addresses
    1304                 :            :      * of the former tuple with ID \p first where the values of tuple with ID \p second are stored after the call and
    1305                 :            :      * vice versa. */
    1306                 :            :     void operator()(U32x1 first, U32x1 second, const Environment &env_first, const Environment &env_second);
    1307                 :            : };
    1308                 :            : 
    1309                 :            : 
    1310                 :            : /*======================================================================================================================
    1311                 :            :  * bit operations
    1312                 :            :  *====================================================================================================================*/
    1313                 :            : 
    1314                 :            : /** Sets the \p n -th bit of the value pointed to by \p bytes to \p value. */
    1315                 :            : template<typename T, std::size_t L>
    1316                 :            : requires integral<typename T::type> and (T::num_simd_lanes == L)
    1317                 :          4 : void setbit(Ptr<T> bytes, Bool<L> value, uint8_t n)
    1318                 :            : {
    1319   [ +  -  +  -  :          4 :     *bytes ^= (-value.template to<typename T::type>() xor *bytes.clone()) bitand T(1 << n);
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
    1320                 :          4 : }
    1321                 :            : /** Sets the bit masked by \p mask of the value pointed to by \p bytes to \p value. */
    1322                 :            : template<typename T, std::size_t L>
    1323                 :            : requires integral<typename T::type> and (T::num_simd_lanes == L)
    1324                 :          4 : void setbit(Ptr<T> bytes, Bool<L> value, T mask)
    1325                 :            : {
    1326   [ +  -  +  -  :          4 :     *bytes ^= (-value.template to<typename T::type>() xor *bytes.clone()) bitand mask;
          +  -  +  -  +  
          -  +  -  +  -  
                   -  + ]
    1327                 :          4 : }
    1328                 :            : 
    1329                 :            : 
    1330                 :            : /*======================================================================================================================
    1331                 :            :  * string comparison
    1332                 :            :  *====================================================================================================================*/
    1333                 :            : 
    1334                 :            : ///> comparison operations, e.g. for string comparison
    1335                 :            : enum cmp_op
    1336                 :            : {
    1337                 :            :     EQ, NE, LT, LE, GT, GE
    1338                 :            : };
    1339                 :            : 
    1340                 :            : /** Compares two strings \p left and \p right.  Has similar semantics to `strncmp` of libc. */
    1341                 :            : _I32x1 strncmp(NChar left, NChar right, U32x1 len, bool reverse = false);
    1342                 :            : /** Compares two strings \p left and \p right.  Has similar semantics to `strcmp` of libc. */
    1343                 :            : _I32x1 strcmp(NChar left, NChar right, bool reverse = false);
    1344                 :            : /** Compares two strings \p left and \p right.  Has similar semantics to `strncmp` of libc. */
    1345                 :            : _Boolx1 strncmp(NChar left, NChar right, U32x1 len, cmp_op op, bool reverse = false);
    1346                 :            : /** Compares two strings \p left and \p right.  Has similar semantics to `strcmp` of libc. */
    1347                 :            : _Boolx1 strcmp(NChar left, NChar right, cmp_op op, bool reverse = false);
    1348                 :            : 
    1349                 :            : 
    1350                 :            : /*======================================================================================================================
    1351                 :            :  * string copy
    1352                 :            :  *====================================================================================================================*/
    1353                 :            : 
    1354                 :            : /** Copies the contents of \p src to \p dst, but no more than \p count characters.  The function returns a `Ptr<Charx1>`
    1355                 :            :  * to the *end* of the copied sequence in \p dst, i.e. to the copied NUL-byte or to the character *after* the lastly
    1356                 :            :  * copied character.  If the first \p count characters of \p src are *not* NUL-terminated, \p dst will not be
    1357                 :            :  * NUL-terminated, too. */
    1358                 :            : Ptr<Charx1> strncpy(Ptr<Charx1> dst, Ptr<Charx1> src, U32x1 count);
    1359                 :            : 
    1360                 :            : 
    1361                 :            : /*======================================================================================================================
    1362                 :            :  * SQL LIKE
    1363                 :            :  *====================================================================================================================*/
    1364                 :            : 
    1365                 :            : /** Checks whether the string \p str matches the pattern \p pattern regarding SQL LIKE semantics using escape
    1366                 :            :  * character \p escape_char. */
    1367                 :            : _Boolx1 like(NChar str, NChar pattern, const char escape_char = '\\');
    1368                 :            : /** Checks whether the string \p str contains the pattern \p pattern.  The implementation is based on the
    1369                 :            :  * Knuth–Morris–Pratt algorithm and represents a special case of the SQL LIKE in which the pattern is known at query
    1370                 :            :  * compile time and has the form `%[^_%\\]+%`. */
    1371                 :            : _Boolx1 like_contains(NChar str, const ThreadSafePooledString &pattern);
    1372                 :            : /** Checks whether the string \p str has the prefix \p pattern.  The implementation is based on rewriting to string
    1373                 :            :  * comparisons and represents a special case of the SQL LIKE in which the pattern is known at query compile time and
    1374                 :            :  * has the form `[^_%\\]+%`. */
    1375                 :            : _Boolx1 like_prefix(NChar str, const ThreadSafePooledString &pattern);
    1376                 :            : /** Checks whether the string \p str has the suffix \p pattern.  The implementation is based on rewriting to string
    1377                 :            :  * comparisons and represents a special case of the SQL LIKE in which the pattern is known at query compile time and
    1378                 :            :  * has the form `%[^_%\\]+`. */
    1379                 :            : _Boolx1 like_suffix(NChar str, const ThreadSafePooledString &pattern);
    1380                 :            : 
    1381                 :            : 
    1382                 :            : /*======================================================================================================================
    1383                 :            :  * signum and comparator
    1384                 :            :  *====================================================================================================================*/
    1385                 :            : 
    1386                 :            : /** Returns the signum of \p value, i.e. -1 for negative values, 0 for zero, and 1 for positive values.. */
    1387                 :            : template<typename T>
    1388                 :            : requires arithmetic<typename T::type>
    1389                 :            : T signum(T value)
    1390                 :            : {
    1391                 :            :     using type = typename T::type;
    1392                 :            :     return (value.clone() > type(0)).template to<type>() - (value < type(0)).template to<type>();
    1393                 :            : }
    1394                 :            : 
    1395                 :            : /** Compares two tuples, which must be already loaded into the environments \p env_left and \p env_right, according to
    1396                 :            :  * the ordering \p order (the second element of each pair is `true` iff the corresponding sorting should be
    1397                 :            :  * ascending).  Note that the value NULL is always considered smaller regardless of the ordering.  Comparison is
    1398                 :            :  * performed branchless iff \tparam Predicated.
    1399                 :            :  *
    1400                 :            :  * Returns a negative number if \p left is smaller than \p right, 0 if both are equal, and a positive number if
    1401                 :            :  * \p left is greater than \p right, according to the ordering. */
    1402                 :            : template<bool Predicated>
    1403                 :            : I32x1 compare(const Environment &env_left, const Environment &env_right,
    1404                 :            :               const std::vector<SortingOperator::order_type> &order);
    1405                 :            : 
    1406                 :            : 
    1407                 :            : /*======================================================================================================================
    1408                 :            :  * explicit instantiation declarations
    1409                 :            :  *====================================================================================================================*/
    1410                 :            : 
    1411                 :            : template void Environment::add_predicate(_Boolx1 &&);
    1412                 :            : template void Environment::add_predicate(_Boolx16&&);
    1413                 :            : template void Environment::add_predicate(_Boolx32&&);
    1414                 :            : template _Boolx1  Environment::extract_predicate();
    1415                 :            : template _Boolx16 Environment::extract_predicate();
    1416                 :            : template _Boolx32 Environment::extract_predicate();
    1417                 :            : template _Boolx1  Environment::get_predicate() const;
    1418                 :            : template _Boolx16 Environment::get_predicate() const;
    1419                 :            : template _Boolx32 Environment::get_predicate() const;
    1420                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential(
    1421                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Var<U32x1>&
    1422                 :            : );
    1423                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential(
    1424                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Global<U32x1>&
    1425                 :            : );
    1426                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential(
    1427                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&,
    1428                 :            :     Variable<uint32_t, VariableKind::Param, false>&
    1429                 :            : );
    1430                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential_single_pass(
    1431                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Var<U32x1>&
    1432                 :            : );
    1433                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential_single_pass(
    1434                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Global<U32x1>&
    1435                 :            : );
    1436                 :            : extern template std::tuple<Block, Block, Block> compile_store_sequential_single_pass(
    1437                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&,
    1438                 :            :     Variable<uint32_t, VariableKind::Param, false>&
    1439                 :            : );
    1440                 :            : extern template std::tuple<Block, Block, Block> compile_load_sequential(
    1441                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Var<U32x1>&
    1442                 :            : );
    1443                 :            : extern template std::tuple<Block, Block, Block> compile_load_sequential(
    1444                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&, Global<U32x1>&
    1445                 :            : );
    1446                 :            : extern template std::tuple<Block, Block, Block> compile_load_sequential(
    1447                 :            :     const Schema&, const Schema&, Ptr<void>, const storage::DataLayout&, std::size_t, const Schema&,
    1448                 :            :     Variable<uint32_t, VariableKind::Param, false>&
    1449                 :            : );
    1450                 :            : extern template struct Buffer<false>;
    1451                 :            : extern template struct Buffer<true>;
    1452                 :            : extern template struct buffer_swap_proxy_t<false>;
    1453                 :            : extern template struct buffer_swap_proxy_t<true>;
    1454                 :            : extern template I32x1 compare<false>(
    1455                 :            :     const Environment&, const Environment&, const std::vector<SortingOperator::order_type>&
    1456                 :            : );
    1457                 :            : extern template I32x1 compare<true>(
    1458                 :            :     const Environment&, const Environment&, const std::vector<SortingOperator::order_type>&
    1459                 :            : );
    1460                 :            : 
    1461                 :            : }
    1462                 :            : 
    1463                 :            : }

Generated by: LCOV version 1.16