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 : : }
|