22bool pointer_sharing =
true;
25bool remainder_removal =
true;
30static
void add_wasm_util_args()
38 "--no-pointer-sharing",
39 "do not use pointer sharing optimization for data layout compilation",
40 [](
bool){ options::pointer_sharing =
false; }
45 "--no-remainder-removal",
46 "do not use remainder removal optimization for data layout compilation",
47 [](
bool){ options::remainder_removal =
false; }
64 [&operand](
auto &&actual) ->
void requires requires { { actual.template to<T>() } -> sql_type; } {
65 auto v = actual.template to<T>();
67 new (&operand)
SQL_t(v);
69 [](
auto &actual) ->
void requires (not
requires { { actual.template to<T>() } -> sql_type; }) {
72 [](std::monostate) ->
void {
M_unreachable(
"invalid variant"); },
80 switch (to_type->kind) {
81 case Numeric::N_Decimal:
85 switch (to_type->
size()) {
89 convert_in_place<int8_t>(operand);
92 convert_in_place<int16_t>(operand);
95 convert_in_place<int32_t>(operand);
98 convert_in_place<int64_t>(operand);
102 case Numeric::N_Float:
103 if (to_type->
size() <= 32)
104 convert_in_place<float>(operand);
106 convert_in_place<double>(operand);
111template<
bool CanBeNull, std::
size_t L>
114 using result_t = std::conditional_t<CanBeNull, _Bool<L>, Bool<L>>;
117 return result_t(
true);
119 std::optional<result_t> wasm_cnf, wasm_clause;
120 for (
auto &clause : cnf) {
122 for (
auto &pred : clause) {
124 M_insist(pred.expr().type()->is_boolean());
126 C.
compile<_Bool<L>>(pred.expr()).insist_not_null());
127 auto wasm_pred = pred.negative() ? not compiled : compiled;
131 wasm_clause.emplace(*wasm_clause or wasm_pred);
133 wasm_clause.emplace(wasm_pred);
135 M_insist(
bool(wasm_clause),
"empty clause?");
139 wasm_cnf.emplace(*wasm_cnf
and *wasm_clause);
141 wasm_cnf.emplace(*wasm_clause);
143 M_insist(
bool(wasm_cnf),
"empty CNF?");
160 case 1:
set(_I32x1::Null());
break;
161 case 2:
set(_I32x2::Null());
break;
162 case 4:
set(_I32x4::Null());
break;
163 case 8:
set(_I32x8::Null());
break;
164 case 16:
set(_I32x16::Null());
break;
165 case 32:
set(_I32x32::Null());
break;
180 case 1:
set(_I32x1::Null());
break;
181 case 2:
set(_I32x2::Null());
break;
182 case 4:
set(_I32x4::Null());
break;
183 case 8:
set(_I32x8::Null());
break;
184 case 16:
set(_I32x16::Null());
break;
185 case 32:
set(_I32x32::Null());
break;
191 auto value = Interpreter::eval(e);
193 auto set_constant = [
this, &e, &
value]<std::size_t
L>(){
195 [
this]<sql_type
T>(
T &&actual) { this->
set(std::forward<T>(actual)); },
204 case Numeric::N_Decimal:
209 set_helper(_I8<L>(
value.as_i()));
212 set_helper(_I16<L>(
value.as_i()));
215 set_helper(_I32<L>(
value.as_i()));
218 set_helper(_I64<L>(
value.as_i()));
222 case Numeric::N_Float:
224 set_helper(_Float<L>(
value.as_f()));
226 set_helper(_Double<L>(
value.as_d()));
230 M_insist(
L == 1,
"string SIMDfication currently not supported");
233 [&
value, &set_helper](
const Date&) { set_helper(_I32<L>(
value.as_i())); },
236 [](
auto&&) {
M_unreachable(
"invalid type for given number of SIMD lanes"); },
241 case 1: set_constant.operator()<1>();
break;
242 case 2: set_constant.operator()<2>();
break;
243 case 4: set_constant.operator()<4>();
break;
244 case 8: set_constant.operator()<8>();
break;
245 case 16: set_constant.operator()<16>();
break;
246 case 32: set_constant.operator()<32>();
break;
254 auto apply_unop = [
this, &e](
auto unop) {
257 [](std::monostate&&) ->
void {
M_unreachable(
"illegal value"); },
258 [
this, &unop](
auto &&
expr) ->
void requires requires { { unop(
expr) } -> sql_type; } {
261 [](
auto &&
expr) ->
void requires (not
requires { { unop(
expr) } -> sql_type; }) {
267#define UNOP(OP) apply_unop(overloaded { \
268 [](auto &&expr) -> decltype(expr.operator OP()) { return expr.operator OP(); }, \
276 case TK_PLUS:
UNOP(+);
277 case TK_MINUS:
UNOP(-);
278 case TK_TILDE:
UNOP(~);
279 case TK_Not:
UNOP(not);
288 auto apply_binop = [
this, &e](
auto binop) {
301 [](std::monostate&&) ->
void {
M_unreachable(
"illegal value"); },
302 [
this, &binop, &rhs](
auto &&expr_lhs) ->
void {
304 [](std::monostate&&) ->
void {
M_unreachable(
"illegal value"); },
305 [
this, expr_lhs, &binop](
auto &&expr_rhs)
mutable ->
void
306 requires requires { { binop(expr_lhs, expr_rhs) } -> sql_type; } {
307 this->
set(binop(expr_lhs, expr_rhs));
309 [](
auto &&expr_rhs) ->
void
310 requires (not
requires { { binop(expr_lhs, expr_rhs) } -> sql_type; }) {
318#define BINOP(OP) apply_binop( \
319 [](auto lhs, auto rhs) -> decltype(lhs.operator OP(rhs)) { return lhs.operator OP(rhs); } \
321#define CMPOP(OP, STRCMP_OP) { \
322 if (e.lhs->type()->is_character_sequence()) { \
323 M_insist(e.rhs->type()->is_character_sequence()); \
324 M_insist(CodeGenContext::Get().num_simd_lanes() == 1, "invalid number of SIMD lanes"); \
326 [](NChar lhs, NChar rhs) -> _Boolx1 { \
327 return strcmp(lhs, rhs, STRCMP_OP); \
340 case TK_PLUS:
BINOP(+);
341 case TK_MINUS:
BINOP(-);
342 case TK_ASTERISK:
BINOP(*);
343 case TK_SLASH:
BINOP(/);
344 case TK_PERCENT:
BINOP(%);
348 case TK_BANG_EQUAL:
CMPOP(!=,
NE);
350 case TK_LESS_EQUAL:
CMPOP(<=,
LE);
352 case TK_GREATER_EQUAL:
CMPOP(>=,
GE);
360 NChar str = get<NChar>();
361 if (
auto static_pattern = cast<ast::Constant>(e.
rhs.get())) {
363 interpret(*static_pattern->tok.text.assert_not_none())
365 if (std::regex_match(*pattern, std::regex(
"%[^_%\\\\]+%"))) {
369 if (std::regex_match(*pattern, std::regex(
"[^_%\\\\]+%"))) {
373 if (std::regex_match(*pattern, std::regex(
"%[^_%\\\\]+"))) {
380 NChar pattern = get<NChar>();
390 NChar lhs = get<NChar>();
392 NChar rhs = get<NChar>();
394 M_insist(e.
lhs->can_be_null() == lhs.can_be_null());
395 M_insist(e.
rhs->can_be_null() == rhs.can_be_null());
398 bool res_can_be_null = lhs.can_be_null() or rhs.can_be_null();
399 std::size_t res_length = lhs.length() + rhs.length() + 1;
401 if (res_can_be_null) {
402 auto [_ptr_lhs, is_nullptr_lhs] = lhs.split();
403 auto [_ptr_rhs, is_nullptr_rhs] = rhs.split();
406 IF (is_nullptr_lhs or is_nullptr_rhs) {
411 strncpy(ptr, ptr_rhs, U32x1(rhs.size_in_bytes())).discard();
412 if (not rhs.guarantees_terminating_nul())
418 strncpy(ptr, rhs, U32x1(rhs.size_in_bytes())).discard();
419 if (not rhs.guarantees_terminating_nul())
434 _Boolx1 lhs = get<_Boolx1>();
436 _Boolx1 rhs = get<_Boolx1>();
438 if (e.
op().
type == TK_And)
460 case m::Function::FN_ISNULL: {
464 [
this]<sql_type
T>(
T actual) ->
void requires requires {
SQL_t(actual.is_null()); } {
465 set(actual.is_null());
467 []<sql_type
T>(
T actual) ->
void requires (not
requires {
SQL_t(actual.is_null()); }) {
470 [](std::monostate) ->
void {
M_unreachable(
"invalid variant"); },
476 case m::Function::FN_INT: {
479 convert_in_place<int32_t>(arg);
485 case m::Function::FN_COUNT:
486 case m::Function::FN_MIN:
487 case m::Function::FN_MAX:
488 case m::Function::FN_SUM:
489 case m::Function::FN_AVG: {
490 std::ostringstream oss;
509 case 1:
return cnf.
can_be_null() ? compile_cnf<true, 1>(*
this, cnf) : compile_cnf<false, 1>(*
this, cnf);
510 case 16:
return cnf.
can_be_null() ? compile_cnf<true, 16>(*
this, cnf) : compile_cnf<false, 16>(*
this, cnf);
511 case 32:
return cnf.
can_be_null() ? compile_cnf<true, 32>(*
this, cnf) : compile_cnf<false, 32>(*
this, cnf);
524 out <<
"WasmEnvironment\n` entries: { ";
525 for (
auto it =
exprs_.begin(), end =
exprs_.end(); it != end; ++it) {
526 if (it !=
exprs_.begin()) out <<
", ";
529 out <<
" }" << std::endl;
531 out <<
"WasmEnvironment\n` address entries: { ";
536 out <<
" }" << std::endl;
572template<
bool IsStore, std::
size_t L,
bool SinglePass,
bool Po
interSharing, VariableKind Kind>
574std::tuple<Block, Block, Block>
575compile_data_layout_sequential(
const Schema &_tuple_value_schema,
const Schema &_tuple_addr_schema,
582 M_insist(tuple_value_schema.num_entries() != 0,
"sequential access must access at least one tuple schema entry");
583 M_insist(not IsStore or tuple_addr_schema.num_entries() == 0,
"addresses are only computed for loads");
585 for (
auto &e : tuple_value_schema)
587 for (
auto &e : tuple_addr_schema) {
590 M_insist(not it->nullable(),
"nullable tuple address schema entry not yet supported");
591 M_insist(not it->type->is_boolean(),
"boolean tuple address schema entry not yet supported");
592 M_insist(not it->type->is_character_sequence(),
"character sequence tuple address schema entry omitted");
598 Block inits(
"inits",
false), stores(
"stores",
false), loads(
"loads",
false), jumps(
"jumps",
false);
600 SQL_t values[tuple_value_schema.num_entries()];
603 if (not tuple_addr_schema.empty())
607 if constexpr (not IsStore)
608 null_bits =
static_cast<Bool<L>*
>(alloca(
sizeof(Bool<L>) * tuple_value_schema.num_entries()));
610 using key_t = std::pair<uint8_t, uint64_t>;
612 using ptr_t = std::conditional_t<SinglePass, Var<Ptr<void>>,
Global<Ptr<void>>>;
614 using mask_t = std::conditional_t<SinglePass, Var<U32x1>,
Global<U32x1>>;
618 std::optional<mask_t>
mask;
624 PointerSharing, std::unordered_map<key_t, value_t>, std::vector<std::pair<key_t, value_t>>
629 if constexpr (
L > 1) {
631 Wasm_insist(tuple_id % uint64_t(
L) == 0
U,
"must start at a tuple ID beginning a SIMD batch");
636 const bool needs_null_bitmap = [&]() {
637 for (
auto &tuple_entry : tuple_value_schema) {
643 bool has_null_bitmap =
false;
646 const bool is_predicated = env.predicated();
647 M_insist(not is_predicated or (IsStore
and L == 1),
"predication only supported for storing scalar tuples");
648 std::optional<Var<Boolx1>> pred;
651 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
656 if constexpr (IsStore) {
661 tuple_id += pred->to<uint64_t>();
663 tuple_id += uint64_t(
L);
668 tuple_id += uint64_t(
L);
674 [&, &inits=inits, &jumps=jumps, &stores=stores, &loads=loads]
676 uint64_t inode_offset_in_bits)
679 loading_context.clear();
682 std::optional<ptr_t> null_bitmap_ptr;
683 std::optional<mask_t> null_bitmap_mask;
684 uint8_t null_bitmap_bit_offset;
685 uint64_t null_bitmap_stride_in_bits;
688 auto compute_additional_inode_byte_offset = [&](U64x1 tuple_id) -> U64x1 {
689 auto rec = [&](U64x1 curr_tuple_id,
decltype(levels.cbegin()) curr,
const decltype(levels.cend()) end,
693 Wasm_insist(curr_tuple_id == tuple_id % uint64_t(levels.back().num_tuples));
698 U64x1 child_iter = curr_tuple_id.clone() >> uint64_t(__builtin_ctzl(curr->num_tuples));
699 U64x1 inner_tuple_id = curr_tuple_id bitand uint64_t(curr->num_tuples - 1U);
700 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
701 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
702 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
704 U64x1 child_iter = curr_tuple_id.clone() / uint64_t(curr->num_tuples);
705 U64x1 inner_tuple_id = curr_tuple_id % uint64_t(curr->num_tuples);
706 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
707 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
708 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
711 return rec(tuple_id.clone(), levels.cbegin(), levels.cend(), rec);
713 std::optional<const Var<I64x1>> inode_byte_offset;
714 std::optional<const Var<U64x1>> inode_iter;
716 M_insist(inode_offset_in_bits % 8 == 0,
"INode offset must be byte aligned");
717 inode_byte_offset.emplace(
718 int64_t(inode_offset_in_bits / 8) + compute_additional_inode_byte_offset(tuple_id).
make_signed()
720 M_insist(levels.back().num_tuples != 0,
"INode must be large enough for at least one tuple");
721 if (levels.back().num_tuples != 1) {
723 is_pow_2(levels.back().num_tuples) ? tuple_id bitand uint64_t(levels.back().num_tuples - 1U)
724 : tuple_id % uint64_t(levels.back().num_tuples)
732 for (
auto &leaf_info : leaves) {
733 const uint8_t bit_stride = leaf_info.stride_in_bits % 8;
736 if (not needs_null_bitmap)
740 M_insist(not has_null_bitmap,
"at most one bitmap may be specified");
741 has_null_bitmap =
true;
743 M_insist(
L == 1,
"SIMDfied loading of NULL bitmap with bit stride currently not supported");
745 M_insist(
bool(inode_iter),
"stride requires repetition");
746 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
747 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
748 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
750 null_bitmap_bit_offset = leaf_info.offset_in_bits % 8;
751 null_bitmap_stride_in_bits = leaf_info.stride_in_bits;
754 null_bitmap_ptr.emplace();
755 *null_bitmap_ptr = base_address.clone() + *inode_byte_offset + leaf_byte_offset;
756 null_bitmap_mask.emplace();
757 *null_bitmap_mask = 1U << leaf_bit_offset;
761 std::size_t prev_layout_idx = 0;
764 if (layout_entry.nullable()) {
765 auto tuple_it = tuple_value_schema.
find(layout_entry.id);
766 if (tuple_it == tuple_value_schema.end())
768 M_insist(prev_layout_idx == 0 or layout_idx > prev_layout_idx,
769 "layout entries not processed in ascending order");
770 M_insist(*tuple_it->type == *layout_entry.type);
771 const auto delta = layout_idx - prev_layout_idx;
772 const uint8_t bit_delta = delta % 8;
773 const int64_t byte_delta = delta / 8;
775 auto advance_to_next_bit = [&]() {
779 *null_bitmap_mask <<=
780 Select(*pred, bit_delta, uint8_t(0));
782 *null_bitmap_mask <<= bit_delta;
785 *null_bitmap_ptr += (*null_bitmap_mask bitand 0xffU).
eqz().template to<int64_t>();
787 *null_bitmap_mask =
Select((*null_bitmap_mask bitand 0xffU).
eqz(),
788 *null_bitmap_mask >> 8U, *null_bitmap_mask);
794 Select(*pred, byte_delta, 0);
796 *null_bitmap_ptr += byte_delta;
801 if constexpr (IsStore) {
803 auto store = [&]<
typename T>() {
805 advance_to_next_bit();
810 null_bitmap_mask->template to<uint8_t>());
814 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
818 case Numeric::N_Decimal:
821 case 8: store.template operator()<_I8x1 >();
break;
822 case 16: store.template operator()<_I16x1>();
break;
823 case 32: store.template operator()<_I32x1>();
break;
824 case 64: store.template operator()<_I64x1>();
break;
827 case Numeric::N_Float:
829 store.template
operator()<_Floatx1>();
831 store.template operator()<_Doublex1>();
836 advance_to_next_bit();
839 setbit(null_bitmap_ptr->template to<uint8_t*>(),
value.is_null(),
840 null_bitmap_mask->template to<uint8_t>());
843 [&](
const Date&) { store.template operator()<_I32x1>(); },
844 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
848 const auto tuple_idx = std::distance(tuple_value_schema.begin(), tuple_it);
850 advance_to_next_bit();
852 U8x1
byte = *null_bitmap_ptr->template to<uint8_t*>();
854 (
byte bitand *null_bitmap_mask).
template to<bool>()
856 new (&null_bits[tuple_idx]) Boolx1(
value);
861 prev_layout_idx = layout_idx;
864 if constexpr (IsStore) {
870 "value of non-nullable entry must not be nullable");
874 M_unreachable(
"invalid type for given number of SIMD lanes");
878 [&](
const Boolean&) { check.template operator()<_Bool<L>>(); },
882 case Numeric::N_Decimal:
885 case 8: check.template operator()<_I8 <L>>();
break;
886 case 16: check.template operator()<_I16<L>>();
break;
887 case 32: check.template operator()<_I32<L>>();
break;
888 case 64: check.template operator()<_I64<L>>();
break;
891 case Numeric::N_Float:
893 check.template
operator()<_Float<L>>();
895 check.template operator()<_Double<L>>();
899 [&](
const Date&) { check.template operator()<_I32<L>>(); },
900 [&](
const DateTime&) { check.template operator()<_I64<L>>(); },
902 }, *layout_entry.type);
911 const auto delta = leaf_info.stride_in_bits - prev_layout_idx;
912 const uint8_t bit_delta = delta % 8;
913 const int64_t byte_delta = delta / 8;
918 *null_bitmap_mask <<=
Select(*pred, bit_delta, uint8_t(0));
920 *null_bitmap_mask <<= bit_delta;
923 *null_bitmap_ptr += (*null_bitmap_mask bitand 0xffU).
eqz().template to<int64_t>();
925 *null_bitmap_mask =
Select((*null_bitmap_mask bitand 0xffU).
eqz(),
926 *null_bitmap_mask >> 8U, *null_bitmap_mask);
933 *null_bitmap_ptr +=
Select(*pred, byte_delta, 0);
935 *null_bitmap_ptr += byte_delta;
941 "NULL bits must fill at least an entire SIMD vector when loading SIMDfied");
942 M_insist(
L == 1 or tuple_value_schema.num_entries() <= 64,
943 "bytes containing a NULL bitmap must fit into scalar value when loading SIMDfied");
945 std::max(ceil_to_pow_2(tuple_value_schema.num_entries()), 8UL) == leaf_info.stride_in_bits,
946 "NULL bitmaps must be packed s.t. the distance between two NULL bits of a single "
947 "attribute is a power of 2 when loading SIMDfied");
948 M_insist(
L == 1 or leaf_info.offset_in_bits % 8 == 0,
949 "NULL bitmaps must not start with bit offset when loading SIMDfied");
951 auto byte_offset = [&]() -> I64x1 {
952 if (inode_iter
and leaf_info.stride_in_bits) {
956 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
957 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
958 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
960 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
962 return *inode_byte_offset + leaf_byte_offset;
964 return *inode_byte_offset;
968 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
969 auto [it, inserted] =
971 loading_context.try_emplace(std::move(key)),
972 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
975 it->second.ptr = base_address.clone() + byte_offset;
978 byte_offset.discard();
980 const auto &ptr = it->second.ptr;
983 std::unordered_map<int64_t, Var<U8x1>> loaded_bytes;
988 if constexpr (not IsStore
and L > 1) {
989 auto emplace = [&]<
typename T>() {
990 using type =
typename T::type;
991 static constexpr std::size_t lanes = T::num_simd_lanes;
993 bytes.template emplace<Var<T>>(
994 *(ptr + leaf_info.offset_in_bits / 8).
template to<type*, lanes>()
998 switch (ceil_to_pow_2(tuple_value_schema.num_entries())) {
999 default: emplace.template operator()<U8 <L>>();
break;
1000 case 16: emplace.template operator()<U16<L>>();
break;
1001 case 32: emplace.template operator()<U32<L>>();
break;
1002 case 64: emplace.template operator()<U64<L>>();
break;
1007 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
1008 auto &tuple_entry = tuple_value_schema[tuple_idx];
1009 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
1010 M_insist(*tuple_entry.type == *layout_entry.type);
1011 if (layout_entry.nullable()) {
1012 const uint8_t static_bit_offset = (leaf_info.offset_in_bits + layout_idx) % 8;
1013 const int64_t static_byte_offset = (leaf_info.offset_in_bits + layout_idx) / 8;
1014 if constexpr (IsStore) {
1021 if constexpr (
L == 1) {
1023 (ptr + static_byte_offset).
template to<uint8_t*>();
1024 setbit<U8x1>(byte_ptr,
is_null, static_bit_offset);
1026 auto store = [&,
is_null, layout_idx]<
typename U>() {
1027 using type =
typename U::type;
1028 static constexpr std::size_t lanes = U::num_simd_lanes;
1030 (ptr + leaf_info.offset_in_bits / 8).
template to<type*, lanes>();
1031 setbit<U>(bytes_ptr,
is_null, layout_idx);
1033 switch (ceil_to_pow_2(tuple_value_schema.num_entries())) {
1034 default: store.template operator()<U8 <L>>();
break;
1035 case 16: store.template operator()<U16<L>>();
break;
1036 case 32: store.template operator()<U32<L>>();
break;
1037 case 64: store.template operator()<U64<L>>();
break;
1043 M_unreachable(
"invalid type for given number of SIMD lanes");
1047 [&](
const Boolean&) { store.template operator()<_Bool<L>>(); },
1050 case Numeric::N_Int:
1051 case Numeric::N_Decimal:
1054 case 8: store.template operator()<_I8 <L>>();
break;
1055 case 16: store.template operator()<_I16<L>>();
break;
1056 case 32: store.template operator()<_I32<L>>();
break;
1057 case 64: store.template operator()<_I64<L>>();
break;
1060 case Numeric::N_Float:
1062 store.template
operator()<_Float<L>>();
1064 store.template operator()<_Double<L>>();
1068 M_insist(
L == 1,
"string SIMDfication currently not supported");
1072 (ptr + static_byte_offset).
template to<uint8_t*>();
1073 setbit<U8x1>(byte_ptr,
value.is_null(), static_bit_offset);
1076 [&](
const Date&) { store.template operator()<_I32<L>>(); },
1077 [&](
const DateTime&) { store.template operator()<_I64<L>>(); },
1079 }, *tuple_entry.type);
1083 if constexpr (
L == 1) {
1084 auto [it, inserted] = loaded_bytes.try_emplace(static_byte_offset);
1086 it->second = *(ptr + static_byte_offset).
template to<uint8_t*>();
1087 const auto &
byte = it->second;
1088 const uint8_t static_mask = 1U << static_bit_offset;
1090 new (&null_bits[tuple_idx]) Boolx1(
value);
1094 [&, layout_idx]<
typename T>
1096 requires (
L >= 16) {
1099 (_bytes bitand static_mask).
template to<bool>()
1101 new (&null_bits[tuple_idx]) Bool<L>(
value);
1104 [](
auto&) {
M_unreachable(
"invalid number of SIMD lanes"); },
1106 },
const_cast<const bytes_t&
>(bytes));
1112 if constexpr (IsStore) {
1115 [&, layout_entry]<sql_type
T>() {
1118 "value of non-nullable entry must not be nullable");
1122 M_unreachable(
"invalid type for given number of SIMD lanes");
1126 [&](
const Boolean&) { check.template operator()<_Bool<L>>(); },
1129 case Numeric::N_Int:
1130 case Numeric::N_Decimal:
1133 case 8: check.template operator()<_I8 <L>>();
break;
1134 case 16: check.template operator()<_I16<L>>();
break;
1135 case 32: check.template operator()<_I32<L>>();
break;
1136 case 64: check.template operator()<_I64<L>>();
break;
1139 case Numeric::N_Float:
1141 check.template
operator()<_Float<L>>();
1143 check.template operator()<_Double<L>>();
1147 [&](
const Date&) { check.template operator()<_I32<L>>(); },
1148 [&](
const DateTime&) { check.template operator()<_I64<L>>(); },
1150 }, *tuple_entry.type);
1158 M_insist(*layout_entry.type == *leaf_info.leaf.type());
1159 auto tuple_value_it = tuple_value_schema.find(layout_entry.id);
1160 auto tuple_addr_it = tuple_addr_schema.find(layout_entry.id);
1161 if (tuple_value_it == tuple_value_schema.end()
and tuple_addr_it == tuple_addr_schema.end())
1163 auto tuple_it = tuple_value_it != tuple_value_schema.end() ? tuple_value_it : tuple_addr_it;
1164 M_insist(*tuple_it->type == *layout_entry.type);
1165 const auto tuple_value_idx = std::distance(tuple_value_schema.begin(), tuple_value_it);
1166 const auto tuple_addr_idx = std::distance(tuple_addr_schema.begin(), tuple_addr_it);
1169 M_insist(tuple_it->type->is_boolean(),
1170 "leaf bit stride currently only for `Boolean` supported");
1172 "booleans must fill at least an entire SIMD vector when loading SIMDfied");
1173 M_insist(
L <= 64,
"bytes containing booleans must fit into scalar value when loading SIMDfied");
1174 M_insist(
L == 1 or leaf_info.stride_in_bits == 1,
1175 "booleans must be packed consecutively when loading SIMDfied");
1177 M_insist(
bool(inode_iter),
"stride requires repetition");
1178 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
1179 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
1180 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
1182 if constexpr (
L > 1) {
1185 "booleans must not start with bit offset when loading SIMDfied");
1189 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
1190 auto [it, inserted] =
1192 loading_context.try_emplace(std::move(key)),
1193 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
1194 M_insist(inserted == not it->second.mask);
1198 it->second.ptr = base_address.clone() + *inode_byte_offset;
1199 it->second.mask.emplace();
1200 if constexpr (
L == 1)
1201 *it->second.mask = 1U << leaf_bit_offset;
1205 leaf_bit_offset.discard();
1207 const auto &ptr = it->second.ptr;
1209 if constexpr (IsStore) {
1210 if constexpr (sql_type<_Bool<L>>) {
1213 auto [
value,
is_null] = env.get<_Bool<L>>(tuple_it->id).split();
1215 if constexpr (
L == 1) {
1217 (ptr + leaf_byte_offset).
template to<uint8_t*>();
1218 const auto &
mask = *it->second.mask;
1221 using bytes_t =
uint_t<
L / 8>;
1223 (ptr + leaf_byte_offset).
template to<bytes_t*>();
1224 *bytes_ptr =
value.bitmask().template to<bytes_t>();
1228 M_unreachable(
"invalid type for given number of SIMD lanes");
1231 if constexpr (sql_type<_Bool<L>>) {
1234 if constexpr (
L == 1) {
1235 U8x1
byte = *(ptr + leaf_byte_offset).
template to<uint8_t*>();
1236 const auto &
mask = *it->second.mask;
1237 if (tuple_value_it != tuple_value_schema.end()) {
1239 (
byte.
clone() bitand
mask.template to<uint8_t>()).template to<bool>()
1241 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
1246 using bytes_t =
uint_t<
L / 8>;
1248 *(ptr + leaf_byte_offset).
template to<bytes_t*>()
1250 auto create_mask = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
1253 auto static_mask = create_mask(std::make_index_sequence<L>());
1254 if (tuple_value_it != tuple_value_schema.end()) {
1256 (bytes.template broadcast<L>() bitand static_mask).template to<bool>()
1258 new (&values[tuple_value_idx])
SQL_t(_Bool<L>(
value));
1260 bytes.val().discard();
1266 M_unreachable(
"invalid type for given number of SIMD lanes");
1270 auto byte_offset = [&]() -> I64x1 {
1271 if (inode_iter
and leaf_info.stride_in_bits) {
1275 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
1276 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
1277 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
1279 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
1281 return *inode_byte_offset + leaf_byte_offset;
1283 return *inode_byte_offset;
1287 const uint8_t static_bit_offset = leaf_info.offset_in_bits % 8;
1288 const int64_t static_byte_offset = leaf_info.offset_in_bits / 8;
1290 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
1291 auto [it, inserted] =
1293 loading_context.try_emplace(std::move(key)),
1294 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
1297 it->second.ptr = base_address.clone() + byte_offset;
1300 byte_offset.discard();
1302 const auto &ptr = it->second.ptr;
1307 using type =
typename T::type;
1308 static constexpr std::size_t lanes = T::num_simd_lanes;
1310 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
1314 *(ptr + static_byte_offset).
template to<type*, lanes>() =
value;
1318 M_unreachable(
"invalid type for given number of SIMD lanes");
1324 using type =
typename T::type;
1325 static constexpr std::size_t lanes = T::num_simd_lanes;
1327 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
1329 if (tuple_value_it != tuple_value_schema.end()) {
1331 *(ptr + static_byte_offset).
template to<type*, lanes>()
1335 if (tuple_addr_it != tuple_addr_schema.end())
1337 (ptr + static_byte_offset).
template to<type*, lanes>()
1342 M_unreachable(
"invalid type for given number of SIMD lanes");
1346#define CALL(TYPE) if constexpr (IsStore) store.template operator()<TYPE>(); else load.template operator()<TYPE>()
1349 M_insist(
L == 1 or leaf_info.stride_in_bits == 8,
1350 "booleans must be packed consecutively in bytes when loading SIMDfied");
1351 if constexpr (sql_type<_Bool<L>>) {
1352 if constexpr (IsStore) {
1355 auto [
value,
is_null] = env.get<_Bool<L>>(tuple_it->id).split();
1358 (ptr + static_byte_offset).
template to<uint8_t*, L>();
1359 setbit<U8<L>>(byte_ptr,
value, static_bit_offset);
1365 *(ptr + static_byte_offset).
template to<uint8_t*, L>();
1366 U8<L> static_mask(1U << static_bit_offset);
1368 if (tuple_value_it != tuple_value_schema.end()) {
1370 (
byte.
clone() bitand static_mask.clone()).template to<bool>()
1372 new (&values[tuple_value_idx])
SQL_t(_Bool<L>(
value));
1376 static_mask.discard();
1380 M_unreachable(
"invalid type for given number of SIMD lanes");
1385 case Numeric::N_Int:
1386 case Numeric::N_Decimal:
1389 case 8:
CALL(_I8 <L>);
break;
1390 case 16:
CALL(_I16<L>);
break;
1391 case 32:
CALL(_I32<L>);
break;
1392 case 64:
CALL(_I64<L>);
break;
1395 case Numeric::N_Float:
1403 M_insist(
L == 1,
"string SIMDfication currently not supported");
1404 M_insist(static_bit_offset == 0,
"leaf offset of `CharacterSequence` must be byte aligned");
1405 if constexpr (IsStore) {
1409 IF (
value.clone().not_null()) {
1410 Ptr<Charx1> address((ptr + static_byte_offset).
template to<char*>());
1411 strncpy(address,
value, U32x1(cs.size() / 8)).discard();
1417 Ptr<Charx1> address((ptr + static_byte_offset).
template to<char*>());
1418 new (&values[tuple_value_idx])
SQL_t(
1419 NChar(address, layout_entry.nullable(), cs.length, cs.is_varying)
1425 [&](
const Date&) {
CALL(_I32<L>); },
1428 }, *tuple_it->type);
1435 auto emit_stride_jumps = [&](
decltype(levels.crbegin()) curr,
const decltype(levels.crend()) end) ->
void {
1436 auto rec = [&](
decltype(levels.crbegin()) curr,
const decltype(levels.crend()) end,
auto rec) ->
void {
1437 if (curr == end)
return;
1439 const auto inner = std::prev(curr);
1440 M_insist(curr->num_tuples % inner->num_tuples == 0,
"curr must be whole multiple of inner");
1443 const auto num_repetition_inner = curr->num_tuples / inner->num_tuples;
1444 const auto stride_remaining_in_bits = curr->stride_in_bits -
1445 num_repetition_inner * inner->stride_in_bits;
1446 M_insist(stride_remaining_in_bits % 8 == 0,
1447 "remaining stride of INodes must be whole multiple of a byte");
1450 if (
const int64_t remaining_stride_in_bytes = stride_remaining_in_bits / 8) [[likely]] {
1452 if (curr->num_tuples != 1U) {
1453 Boolx1 cond_mod = (tuple_id % uint64_t(curr->num_tuples)).
eqz();
1454 Boolx1 cond_and = (tuple_id bitand uint64_t(curr->num_tuples - 1U)).
eqz();
1455 const bool use_and =
is_pow_2(curr->num_tuples)
and options::remainder_removal;
1456 Boolx1 cond = use_and ? cond_and : cond_mod;
1457 (use_and ? cond_mod : cond_and).
discard();
1461 for (
auto &[_,
value] : loading_context) {
1462 if (is_predicated) {
1464 value.ptr +=
Select(*pred, remaining_stride_in_bytes, 0);
1466 value.ptr += remaining_stride_in_bytes;
1469 if (null_bitmap_ptr) {
1470 if (is_predicated) {
1473 Select(*pred, remaining_stride_in_bytes, 0);
1475 *null_bitmap_ptr += remaining_stride_in_bytes;
1480 rec(std::next(curr), end, rec);
1483 for (
auto &[_,
value] : loading_context) {
1484 if (is_predicated) {
1486 value.ptr +=
Select(*pred, remaining_stride_in_bytes, 0);
1488 value.ptr += remaining_stride_in_bytes;
1491 if (null_bitmap_ptr) {
1492 if (is_predicated) {
1495 Select(*pred, remaining_stride_in_bytes, 0);
1497 *null_bitmap_ptr += remaining_stride_in_bytes;
1502 rec(std::next(curr), end, rec);
1506 rec(std::next(curr), end, rec);
1510 rec(curr, end, rec);
1516 for (
auto &[key,
value] : loading_context) {
1517 const uint8_t bit_stride = key.second % 8;
1518 const int64_t byte_stride = key.second / 8;
1522 if (is_predicated) {
1524 *
value.mask <<=
Select(*pred, bit_stride, uint8_t(0));
1526 *
value.mask <<= bit_stride;
1529 value.ptr += (*
value.mask bitand 0xffU).
eqz().template to<int64_t>();
1533 if (byte_stride) [[likely]] {
1534 if (is_predicated) {
1539 value.ptr += int64_t(
L) * byte_stride;
1545 if (not levels.empty()) {
1547 Block lowest_inode_jumps(
false);
1548 for (
auto &[key,
value] : loading_context) {
1549 M_insist(levels.back().stride_in_bits % 8 == 0,
1550 "stride of INodes must be multiples of a whole byte");
1551 const auto stride_remaining_in_bits = levels.back().stride_in_bits -
1552 levels.back().num_tuples * key.second;
1553 const uint8_t remaining_bit_stride = stride_remaining_in_bits % 8;
1554 const int64_t remaining_byte_stride = stride_remaining_in_bits / 8;
1555 if (remaining_bit_stride) {
1559 const uint8_t end_bit_offset = (key.first + levels.back().num_tuples * key.second) % 8;
1560 M_insist(end_bit_offset != key.first);
1562 if (is_predicated) {
1565 "if the predicate is not fulfilled, the mask should not be advanced");
1567 *
value.mask = 1U << key.first;
1569 if (is_predicated) {
1571 value.ptr +=
Select(*pred, int64_t(end_bit_offset > key.first), 0);
1573 value.ptr += int64_t(end_bit_offset > key.first);
1577 if (remaining_byte_stride) [[likely]] {
1579 if (is_predicated) {
1582 Select(*pred, remaining_byte_stride, 0);
1584 value.ptr += remaining_byte_stride;
1589 if (null_bitmap_ptr) {
1592 M_insist(levels.back().stride_in_bits % 8 == 0,
1593 "stride of INodes must be multiples of a whole byte");
1594 const auto stride_remaining_in_bits = levels.back().stride_in_bits -
1595 levels.back().num_tuples * null_bitmap_stride_in_bits;
1596 const uint8_t remaining_bit_stride = stride_remaining_in_bits % 8;
1597 const int64_t remaining_byte_stride = stride_remaining_in_bits / 8;
1598 if (remaining_bit_stride) {
1600 const uint8_t end_bit_offset =
1601 (null_bitmap_bit_offset + levels.back().num_tuples * null_bitmap_stride_in_bits) % 8;
1602 M_insist(end_bit_offset != null_bitmap_bit_offset);
1604 if (is_predicated) {
1606 Wasm_insist(*pred or *null_bitmap_mask == 1U << null_bitmap_bit_offset,
1607 "if the predicate is not fulfilled, the mask should not be advanced");
1609 *null_bitmap_mask = 1U << null_bitmap_bit_offset;
1611 if (is_predicated) {
1614 Select(*pred, int64_t(end_bit_offset > null_bitmap_bit_offset), 0);
1616 *null_bitmap_ptr += int64_t(end_bit_offset > null_bitmap_bit_offset);
1620 if (remaining_byte_stride) [[likely]] {
1622 if (is_predicated) {
1625 Select(*pred, remaining_byte_stride, 0);
1627 *null_bitmap_ptr += remaining_byte_stride;
1634 if (not lowest_inode_jumps.
empty()) [[likely]] {
1635 M_insist(levels.back().num_tuples > 0);
1636 if (levels.back().num_tuples != 1U) {
1637 Boolx1 cond_mod = (tuple_id % uint64_t(levels.back().num_tuples)).
eqz();
1638 Boolx1 cond_and = (tuple_id bitand uint64_t(levels.back().num_tuples - 1U)).
eqz();
1639 const bool use_and =
is_pow_2(levels.back().num_tuples)
and options::remainder_removal;
1640 Boolx1 cond = use_and ? cond_and : cond_mod;
1641 (use_and ? cond_mod : cond_and).
discard();
1648 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1654 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1658 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1664 if constexpr (not IsStore) {
1666 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
1667 auto &tuple_entry = tuple_value_schema[idx];
1673 env.add(tuple_entry.id, combined);
1675 env.add(tuple_entry.id,
value);
1680 if constexpr (
L == 1) {
1685 env.add(tuple_entry.id,
NChar(combined,
true,
value.length(),
1686 value.guarantees_terminating_nul()));
1689 env.add(tuple_entry.id,
NChar(_value,
false,
value.length(),
1690 value.guarantees_terminating_nul()));
1694 M_unreachable(
"string SIMDfication currently not supported");
1697 [](
auto) {
M_unreachable(
"value must be loaded beforehand"); },
1703 for (std::size_t idx = 0; idx != tuple_addr_schema.num_entries(); ++idx) {
1705 auto &tuple_entry = tuple_addr_schema[idx];
1706 env.add_addr(tuple_entry.id, std::move(addrs[idx]));
1712 for (std::size_t idx = 0; idx < tuple_value_schema.num_entries(); ++idx)
1713 values[idx].~
SQL_t();
1714 for (std::size_t idx = 0; idx < tuple_addr_schema.num_entries(); ++idx)
1716 if constexpr (not IsStore) {
1718 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
1719 if (has_null_bitmap
and layout_schema[tuple_value_schema[idx].
id].second.nullable())
1720 null_bits[idx].~Bool<L>();
1723 base_address.discard();
1726 if constexpr (IsStore)
1732 if constexpr (IsStore)
1733 return std::make_tuple<Block, Block, Block>(std::move(inits), std::move(stores), std::move(jumps));
1735 return std::make_tuple<Block, Block, Block>(std::move(inits), std::move(loads), std::move(jumps));
1742template<VariableKind Kind>
1743std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1748 if (options::pointer_sharing) {
1751 case 1:
return compile_data_layout_sequential<true, 1, false, true>(tuple_value_schema, tuple_addr_schema,
1754 case 2:
return compile_data_layout_sequential<true, 2, false, true>(tuple_value_schema, tuple_addr_schema,
1757 case 4:
return compile_data_layout_sequential<true, 4, false, true>(tuple_value_schema, tuple_addr_schema,
1760 case 8:
return compile_data_layout_sequential<true, 8, false, true>(tuple_value_schema, tuple_addr_schema,
1763 case 16:
return compile_data_layout_sequential<true, 16, false, true>(tuple_value_schema, tuple_addr_schema,
1766 case 32:
return compile_data_layout_sequential<true, 32, false, true>(tuple_value_schema, tuple_addr_schema,
1773 case 1:
return compile_data_layout_sequential<true, 1, false, false>(tuple_value_schema,
1774 tuple_addr_schema, base_address,
1776 case 2:
return compile_data_layout_sequential<true, 2, false, false>(tuple_value_schema,
1777 tuple_addr_schema, base_address,
1779 case 4:
return compile_data_layout_sequential<true, 4, false, false>(tuple_value_schema,
1780 tuple_addr_schema, base_address,
1782 case 8:
return compile_data_layout_sequential<true, 8, false, false>(tuple_value_schema,
1783 tuple_addr_schema, base_address,
1785 case 16:
return compile_data_layout_sequential<true, 16, false, false>(tuple_value_schema,
1786 tuple_addr_schema, base_address,
1788 case 32:
return compile_data_layout_sequential<true, 32, false, false>(tuple_value_schema,
1789 tuple_addr_schema, base_address,
1795template<VariableKind Kind>
1796std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1802 if (options::pointer_sharing) {
1805 case 1:
return compile_data_layout_sequential<true, 1, true, true>(tuple_value_schema, tuple_addr_schema,
1808 case 2:
return compile_data_layout_sequential<true, 2, true, true>(tuple_value_schema, tuple_addr_schema,
1811 case 4:
return compile_data_layout_sequential<true, 4, true, true>(tuple_value_schema, tuple_addr_schema,
1814 case 8:
return compile_data_layout_sequential<true, 8, true, true>(tuple_value_schema, tuple_addr_schema,
1817 case 16:
return compile_data_layout_sequential<true, 16, true, true>(tuple_value_schema, tuple_addr_schema,
1820 case 32:
return compile_data_layout_sequential<true, 32, true, true>(tuple_value_schema, tuple_addr_schema,
1827 case 1:
return compile_data_layout_sequential<true, 1, true, false>(tuple_value_schema,
1828 tuple_addr_schema, base_address,
1830 case 2:
return compile_data_layout_sequential<true, 2, true, false>(tuple_value_schema,
1831 tuple_addr_schema, base_address,
1833 case 4:
return compile_data_layout_sequential<true, 4, true, false>(tuple_value_schema,
1834 tuple_addr_schema, base_address,
1836 case 8:
return compile_data_layout_sequential<true, 8, true, false>(tuple_value_schema,
1837 tuple_addr_schema, base_address,
1839 case 16:
return compile_data_layout_sequential<true, 16, true, false>(tuple_value_schema,
1840 tuple_addr_schema, base_address,
1842 case 32:
return compile_data_layout_sequential<true, 32, true, false>(tuple_value_schema,
1843 tuple_addr_schema, base_address,
1849template<VariableKind Kind>
1850std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1855 if (options::pointer_sharing) {
1858 case 1:
return compile_data_layout_sequential<false, 1, true, true>(tuple_value_schema, tuple_addr_schema,
1861 case 2:
return compile_data_layout_sequential<false, 2, true, true>(tuple_value_schema, tuple_addr_schema,
1864 case 4:
return compile_data_layout_sequential<false, 4, true, true>(tuple_value_schema, tuple_addr_schema,
1867 case 8:
return compile_data_layout_sequential<false, 8, true, true>(tuple_value_schema, tuple_addr_schema,
1870 case 16:
return compile_data_layout_sequential<false, 16, true, true>(tuple_value_schema, tuple_addr_schema,
1873 case 32:
return compile_data_layout_sequential<false, 32, true, true>(tuple_value_schema, tuple_addr_schema,
1880 case 1:
return compile_data_layout_sequential<false, 1, true, false>(tuple_value_schema,
1881 tuple_addr_schema, base_address,
1883 case 2:
return compile_data_layout_sequential<false, 2, true, false>(tuple_value_schema,
1884 tuple_addr_schema, base_address,
1886 case 4:
return compile_data_layout_sequential<false, 4, true, false>(tuple_value_schema,
1887 tuple_addr_schema, base_address,
1889 case 8:
return compile_data_layout_sequential<false, 8, true, false>(tuple_value_schema,
1890 tuple_addr_schema, base_address,
1892 case 16:
return compile_data_layout_sequential<false, 16, true, false>(tuple_value_schema,
1893 tuple_addr_schema, base_address,
1895 case 32:
return compile_data_layout_sequential<false, 32, true, false>(tuple_value_schema,
1896 tuple_addr_schema, base_address,
1944template<
bool IsStore>
1952 M_insist(tuple_value_schema.
num_entries() != 0,
"point access must access at least one tuple schema entry");
1953 M_insist(not IsStore or tuple_addr_schema.
num_entries() == 0,
"addresses are only computed for loads");
1955 for (
auto &e : tuple_value_schema)
1957 for (
auto &e : tuple_addr_schema) {
1960 M_insist(not it->nullable(),
"nullable tuple address schema entry not yet supported");
1961 M_insist(not it->type->is_boolean(),
"boolean tuple address schema entry not yet supported");
1962 M_insist(not it->type->is_character_sequence(),
"character sequence tuple address schema entry omitted");
1967 SQL_t values[tuple_value_schema.num_entries()];
1970 if (not tuple_addr_schema.empty())
1971 addrs =
static_cast<SQL_addr_t*
>(alloca(
sizeof(
SQL_addr_t) * tuple_addr_schema.num_entries()));
1974 if constexpr (not IsStore)
1975 null_bits =
static_cast<Boolx1*
>(alloca(
sizeof(Boolx1) * tuple_value_schema.num_entries()));
1980 const bool needs_null_bitmap = [&]() {
1981 for (
auto &tuple_entry : tuple_value_schema) {
1987 bool has_null_bitmap =
false;
1994 auto compute_additional_inode_byte_offset = [&](U64x1 tuple_id) -> U64x1 {
1995 auto rec = [&](U64x1 curr_tuple_id,
decltype(levels.cbegin()) curr,
const decltype(levels.cend()) end,
1999 Wasm_insist(curr_tuple_id == tuple_id % uint64_t(levels.back().num_tuples));
2004 U64x1 child_iter = curr_tuple_id.clone() >> uint64_t(__builtin_ctzl(curr->num_tuples));
2005 U64x1 inner_tuple_id = curr_tuple_id bitand uint64_t(curr->num_tuples - 1U);
2006 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
2007 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
2008 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
2010 U64x1 child_iter = curr_tuple_id.clone() / uint64_t(curr->num_tuples);
2011 U64x1 inner_tuple_id = curr_tuple_id % uint64_t(curr->num_tuples);
2012 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
2013 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
2014 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
2017 return rec(tuple_id.clone(), levels.cbegin(), levels.cend(), rec);
2019 M_insist(inode_offset_in_bits % 8 == 0,
"INode offset must be byte aligned");
2021 base_address.clone()
2022 + int64_t(inode_offset_in_bits / 8)
2023 + compute_additional_inode_byte_offset(tuple_id.clone()).make_signed()
2025 std::optional<const Var<U64x1>> inode_iter;
2026 M_insist(levels.back().num_tuples != 0,
"INode must be large enough for at least one tuple");
2027 if (levels.back().num_tuples != 1) {
2029 is_pow_2(levels.back().num_tuples) ? tuple_id bitand uint64_t(levels.back().num_tuples - 1U)
2030 : tuple_id % uint64_t(levels.back().num_tuples)
2038 for (
auto &leaf_info : leaves) {
2039 const uint8_t bit_stride = leaf_info.stride_in_bits % 8;
2042 if (not needs_null_bitmap)
2045 M_insist(not has_null_bitmap,
"at most one bitmap may be specified");
2046 has_null_bitmap =
true;
2048 M_insist(
bool(inode_iter),
"stride requires repetition");
2049 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
2051 (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>()
2053 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
2058 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
2059 auto &tuple_entry = tuple_value_schema[tuple_idx];
2060 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
2061 M_insist(*tuple_entry.type == *layout_entry.type);
2062 if (layout_entry.nullable()) {
2063 U64x1 offset_in_bits = leaf_bit_offset + layout_idx;
2064 U8x1 bit_offset = (offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
2065 I64x1 byte_offset = (offset_in_bits >> uint64_t(3)).
make_signed();
2066 if constexpr (IsStore) {
2068 auto store = [&]<
typename T>() {
2072 (ptr + byte_offset).
template to<uint8_t*>();
2073 setbit<U8x1>(byte_ptr,
is_null, uint8_t(1) << bit_offset);
2076 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
2079 case Numeric::N_Int:
2080 case Numeric::N_Decimal:
2083 case 8: store.template operator()<_I8x1 >();
break;
2084 case 16: store.template operator()<_I16x1>();
break;
2085 case 32: store.template operator()<_I32x1>();
break;
2086 case 64: store.template operator()<_I64x1>();
break;
2089 case Numeric::N_Float:
2091 store.template
operator()<_Floatx1>();
2093 store.template operator()<_Doublex1>();
2099 (ptr + byte_offset).
template to<uint8_t*>();
2100 setbit<U8x1>(byte_ptr,
value.is_null(), uint8_t(1) << bit_offset);
2102 [&](
const Date&) { store.template operator()<_I32x1>(); },
2103 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
2105 }, *tuple_entry.type);
2108 U8x1
byte = *(ptr + byte_offset).
template to<uint8_t*>();
2110 new (&null_bits[tuple_idx]) Boolx1(
value);
2115 if constexpr (IsStore) {
2117 auto check = [&]<
typename T>() {
2119 "value of non-nullable entry must not be nullable");
2122 [&](
const Boolean&) { check.template operator()<_Boolx1>(); },
2125 case Numeric::N_Int:
2126 case Numeric::N_Decimal:
2129 case 8: check.template operator()<_I8x1 >();
break;
2130 case 16: check.template operator()<_I16x1>();
break;
2131 case 32: check.template operator()<_I32x1>();
break;
2132 case 64: check.template operator()<_I64x1>();
break;
2135 case Numeric::N_Float:
2137 check.template
operator()<_Floatx1>();
2139 check.template operator()<_Doublex1>();
2143 [&](
const Date&) { check.template operator()<_I32x1>(); },
2144 [&](
const DateTime&) { check.template operator()<_I64x1>(); },
2146 }, *tuple_entry.type);
2153 if (inode_iter
and leaf_info.stride_in_bits) {
2157 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
2158 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
2159 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
2160 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
2169 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
2170 auto &tuple_entry = tuple_value_schema[tuple_idx];
2171 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
2172 M_insist(*tuple_entry.type == *layout_entry.type);
2173 if (layout_entry.nullable()) {
2174 const uint8_t static_bit_offset = (leaf_info.offset_in_bits + layout_idx) % 8;
2175 const int64_t static_byte_offset = (leaf_info.offset_in_bits + layout_idx) / 8;
2176 if constexpr (IsStore) {
2178 auto store = [&]<
typename T>() {
2182 (ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2183 setbit<U8x1>(byte_ptr,
is_null, static_bit_offset);
2186 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
2189 case Numeric::N_Int:
2190 case Numeric::N_Decimal:
2193 case 8: store.template operator()<_I8x1 >();
break;
2194 case 16: store.template operator()<_I16x1>();
break;
2195 case 32: store.template operator()<_I32x1>();
break;
2196 case 64: store.template operator()<_I64x1>();
break;
2199 case Numeric::N_Float:
2201 store.template
operator()<_Floatx1>();
2203 store.template operator()<_Doublex1>();
2209 (ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2210 setbit<U8x1>(byte_ptr,
value.is_null(), static_bit_offset);
2212 [&](
const Date&) { store.template operator()<_I32x1>(); },
2213 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
2215 }, *tuple_entry.type);
2218 U8x1
byte = *(ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2219 const uint8_t static_mask = 1U << static_bit_offset;
2221 new (&null_bits[tuple_idx]) Boolx1(
value);
2226 if constexpr (IsStore) {
2228 auto check = [&]<
typename T>() {
2230 "value of non-nullable entry must not be nullable");
2233 [&](
const Boolean&) { check.template operator()<_Boolx1>(); },
2236 case Numeric::N_Int:
2237 case Numeric::N_Decimal:
2240 case 8: check.template operator()<_I8x1 >();
break;
2241 case 16: check.template operator()<_I16x1>();
break;
2242 case 32: check.template operator()<_I32x1>();
break;
2243 case 64: check.template operator()<_I64x1>();
break;
2246 case Numeric::N_Float:
2248 check.template
operator()<_Floatx1>();
2250 check.template operator()<_Doublex1>();
2254 [&](
const Date&) { check.template operator()<_I32x1>(); },
2255 [&](
const DateTime&) { check.template operator()<_I64x1>(); },
2257 }, *tuple_entry.type);
2266 M_insist(*layout_entry.type == *leaf_info.leaf.type());
2267 auto tuple_value_it = tuple_value_schema.find(layout_entry.id);
2268 auto tuple_addr_it = tuple_addr_schema.find(layout_entry.id);
2269 if (tuple_value_it == tuple_value_schema.end()
and tuple_addr_it == tuple_addr_schema.end())
2271 auto tuple_it = tuple_value_it != tuple_value_schema.end() ? tuple_value_it : tuple_addr_it;
2272 M_insist(*tuple_it->type == *layout_entry.type);
2273 const auto tuple_value_idx = std::distance(tuple_value_schema.begin(), tuple_value_it);
2274 const auto tuple_addr_idx = std::distance(tuple_addr_schema.begin(), tuple_addr_it);
2277 M_insist(tuple_it->type->is_boolean(),
"leaf bit stride currently only for `Boolean` supported");
2279 M_insist(
bool(inode_iter),
"stride requires repetition");
2280 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
2281 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
2282 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
2284 Ptr<U8x1> byte_ptr = (inode_ptr + leaf_byte_offset).
template to<uint8_t*>();
2285 U8x1
mask = uint8_t(1) << leaf_bit_offset;
2287 if constexpr (IsStore) {
2289 auto [
value,
is_null] = env.get<_Boolx1>(tuple_it->id).split();
2295 if (tuple_value_it != tuple_value_schema.end()) {
2297 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
2305 if (inode_iter
and leaf_info.stride_in_bits) {
2309 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
2310 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
2311 I64x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed();
2312 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
2313 return inode_ptr + leaf_byte_offset;
2319 const uint8_t static_bit_offset = leaf_info.offset_in_bits % 8;
2320 const int64_t static_byte_offset = leaf_info.offset_in_bits / 8;
2323 auto store = [&]<
typename T>() {
2324 using type =
typename T::type;
2326 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
2329 *(ptr + static_byte_offset).
template to<type*>() =
value;
2332 auto load = [&]<
typename T>() {
2333 using type =
typename T::type;
2335 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
2336 if (tuple_value_it != tuple_value_schema.end()) {
2340 if (tuple_addr_it != tuple_addr_schema.end())
2342 (ptr.clone() + static_byte_offset).template to<type*>()
2347#define CALL(TYPE) if constexpr (IsStore) store.template operator()<TYPE>(); else load.template operator()<TYPE>()
2350 Ptr<U8x1> byte_ptr = (ptr + static_byte_offset).
template to<uint8_t*>();
2351 if constexpr (IsStore) {
2353 auto [
value,
is_null] = env.get<_Boolx1>(tuple_it->id).split();
2355 setbit<U8x1>(byte_ptr,
value, static_bit_offset);
2359 const uint8_t static_mask = 1U << static_bit_offset;
2361 if (tuple_value_it != tuple_value_schema.end()) {
2363 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
2371 case Numeric::N_Int:
2372 case Numeric::N_Decimal:
2375 case 8:
CALL(_I8x1 );
break;
2376 case 16:
CALL(_I16x1);
break;
2377 case 32:
CALL(_I32x1);
break;
2378 case 64:
CALL(_I64x1);
break;
2381 case Numeric::N_Float:
2389 M_insist(static_bit_offset == 0,
"leaf offset of `CharacterSequence` must be byte aligned");
2390 Ptr<Charx1> addr = (ptr + static_byte_offset).
template to<char*>();
2391 if constexpr (IsStore) {
2394 IF (
value.clone().not_null()) {
2399 new (&values[tuple_value_idx])
SQL_t(
2400 NChar(addr, layout_entry.nullable(), cs.length, cs.is_varying)
2405 [&](
const Date&) {
CALL(_I32x1); },
2408 }, *tuple_it->type);
2415 if constexpr (not IsStore) {
2417 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
2418 auto &tuple_entry = tuple_value_schema[idx];
2422 Expr<T> combined(
value.insist_not_null(), null_bits[idx]);
2423 env.add(tuple_entry.id, combined);
2425 env.add(tuple_entry.id,
value);
2432 env.add(tuple_entry.id,
NChar(combined,
true,
value.length(),
2433 value.guarantees_terminating_nul()));
2436 env.add(tuple_entry.id,
NChar(_value,
false,
value.length(),
2437 value.guarantees_terminating_nul()));
2440 [](
auto) {
M_unreachable(
"SIMDfication currently not supported"); },
2441 [](std::monostate) {
M_unreachable(
"value must be loaded beforehand"); },
2446 for (std::size_t idx = 0; idx != tuple_addr_schema.num_entries(); ++idx) {
2447 auto &tuple_entry = tuple_addr_schema[idx];
2448 env.add_addr(tuple_entry.id, std::move(addrs[idx]));
2453 for (std::size_t idx = 0; idx < tuple_value_schema.num_entries(); ++idx)
2454 values[idx].~
SQL_t();
2455 for (std::size_t idx = 0; idx < tuple_addr_schema.num_entries(); ++idx)
2457 if constexpr (not IsStore) {
2459 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
2460 if (has_null_bitmap
and layout_schema[tuple_value_schema[idx].
id].second.nullable())
2461 null_bits[idx].~Boolx1();
2464 base_address.discard();
2475 return compile_data_layout_point_access<true>(tuple_value_schema, tuple_addr_schema, base_address, layout,
2483 return compile_data_layout_point_access<false>(tuple_value_schema, tuple_addr_schema, base_address, layout,
2492template<
bool IsGlobal>
2495 : schema_(
std::cref(schema))
2496 , layout_(factory.make(schema, num_tuples))
2497 , load_simdfied_(load_simdfied)
2499 , pipeline_(
std::
move(pipeline))
2500 , teardown_(
std::
move(teardown))
2504 if constexpr (IsGlobal) {
2508 const uint64_t num_children =
2518template<
bool IsGlobal>
2521 if constexpr (IsGlobal) {
2522 if (not layout_.is_finite()) {
2524 M_insist(
bool(storage_.capacity_));
2525 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2526 auto buffer_size_in_bytes =
2527 (*storage_.capacity_ / uint64_t(layout_.child().num_tuples())) * child_size_in_bytes;
2533template<
bool IsGlobal>
2535 param_t _tuple_addr_schema)
const
2538 if (_tuple_value_schema) {
2539 for (
auto &e : _tuple_value_schema->get())
2540 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2542 if (_tuple_addr_schema) {
2543 for (
auto &e : _tuple_addr_schema->get())
2544 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple address schema entry not found");
2548 static Schema empty_schema;
2549 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2550 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2555template<
bool IsGlobal>
2561 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple schema entry not found");
2568template<
bool IsGlobal>
2574 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple schema entry not found");
2581template<
bool IsGlobal>
2584 M_insist(not base_address_,
"must not call `setup()` twice");
2585 M_insist(not size_,
"must not call `setup()` twice");
2586 M_insist(not capacity_,
"must not call `setup()` twice");
2587 M_insist(not first_iteration_,
"must not call `setup()` twice");
2590 base_address_.emplace();
2592 if (not layout_.is_finite()) {
2593 capacity_.emplace();
2594 first_iteration_.emplace(
true);
2598 if constexpr (IsGlobal) {
2600 *size_ = storage_.size_;
2601 if (not layout_.is_finite()) {
2602 M_insist(
bool(storage_.capacity_));
2603 *capacity_ = *storage_.capacity_;
2607 if (layout_.is_finite()) {
2608 if constexpr (IsGlobal) {
2609 *base_address_ = storage_.base_address_;
2612 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2613 const uint64_t num_children =
2614 (layout_.num_tuples() + layout_.child().num_tuples() - 1) / layout_.child().num_tuples();
2615 *base_address_ =
Module::Allocator().pre_allocate(num_children * child_size_in_bytes, 8);
2618 if constexpr (IsGlobal) {
2619 IF (*capacity_ == 0
U) {
2621 *capacity_ = uint64_t(layout_.child().num_tuples());
2624 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2627 *base_address_ = storage_.base_address_;
2631 *capacity_ = uint64_t(layout_.child().num_tuples());
2634 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2640template<
bool IsGlobal>
2643 M_insist(
bool(base_address_),
"must call `setup()` before");
2644 M_insist(
bool(size_),
"must call `setup()` before");
2645 M_insist(not layout_.is_finite() ==
bool(capacity_),
"must call `setup()` before");
2646 M_insist(not layout_.is_finite() ==
bool(first_iteration_),
"must call `setup()` before");
2648 if constexpr (not IsGlobal) {
2649 if (not layout_.is_finite()) {
2651 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2652 auto buffer_size_in_bytes = (*capacity_ / uint64_t(layout_.child().num_tuples())) * child_size_in_bytes;
2658 if constexpr (IsGlobal) {
2659 storage_.base_address_ = *base_address_;
2660 storage_.size_ = *size_;
2661 if (not layout_.is_finite()) {
2662 M_insist(
bool(storage_.capacity_));
2663 *storage_.capacity_ = *capacity_;
2668 base_address_.reset();
2670 if (not layout_.is_finite()) {
2672 first_iteration_->val().discard();
2673 first_iteration_.reset();
2677template<
bool IsGlobal>
2683 static Schema empty_schema;
2684 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2685 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2688 for (
auto &e : tuple_value_schema)
2689 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2690 for (
auto &e : tuple_addr_schema)
2691 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2695 if (not resume_pipeline_) {
2697 FUNCTION(resume_pipeline,
void(
void*, uint64_t))
2706 const auto num_simd_lanes_preferred =
2709 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2711 tuple_addr_schema.empty() ? 0UL : 2UL })
2720 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2722 WHILE (load_tuple_id < size) {
2726 base_address.discard();
2729 auto [load_inits, loads, load_jumps] =
2734 load_inits.attach_to_current();
2735 WHILE (load_tuple_id < size) {
2736 loads.attach_to_current();
2738 load_jumps.attach_to_current();
2745 resume_pipeline_ = std::move(resume_pipeline);
2750 (*resume_pipeline_)(base_address(), size());
2753template<
bool IsGlobal>
2756 execute_pipeline_inline(setup_, pipeline_, teardown_, std::move(tuple_value_schema), std::move(tuple_addr_schema));
2759template<
bool IsGlobal>
2766 static Schema empty_schema;
2767 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2768 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2771 for (
auto &e : tuple_value_schema)
2772 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2773 for (
auto &e : tuple_addr_schema)
2774 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2778 FUNCTION(resume_pipeline,
void(
void*, uint64_t))
2787 const auto num_simd_lanes_preferred =
2790 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2792 tuple_addr_schema.empty() ? 0UL : 2UL })
2801 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2803 WHILE (load_tuple_id < size) {
2807 base_address.discard();
2810 auto [load_inits, loads, load_jumps] =
2812 schema_, load_tuple_id);
2815 load_inits.attach_to_current();
2816 WHILE (load_tuple_id < size) {
2817 loads.attach_to_current();
2819 load_jumps.attach_to_current();
2828 resume_pipeline(base_address(), size());
2831template<
bool IsGlobal>
2838 static Schema empty_schema;
2839 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2840 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2843 for (
auto &e : tuple_value_schema)
2844 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2845 for (
auto &e : tuple_addr_schema)
2846 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2852 base_address_ ? base_address_->val() :
Var<
Ptr<void>>(storage_.base_address_.val()).val(),
2853 ({ M_insist(bool(base_address_)); base_address_->val(); }));
2856 size_ ? size_->val() :
Var<U64x1>(storage_.size_.val()).val(),
2857 ({ M_insist(bool(size_)); size_->val(); }));
2860 std::optional<Var<Boolx1>> pred;
2863 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
2865 U64x1 num_tuples = pred ?
Select(*pred, size, 0
U) : size;
2868 const auto num_simd_lanes_preferred =
2871 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2873 tuple_addr_schema.empty() ? 0UL : 2UL })
2882 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2884 WHILE (load_tuple_id < num_tuples) {
2888 base_address.discard();
2891 auto [load_inits, loads, load_jumps] =
2896 load_inits.attach_to_current();
2897 WHILE (load_tuple_id < num_tuples) {
2898 loads.attach_to_current();
2900 load_jumps.attach_to_current();
2908template<
bool IsGlobal>
2911 M_insist(
bool(base_address_),
"must call `setup()` before");
2912 M_insist(
bool(size_),
"must call `setup()` before");
2913 M_insist(not layout_.is_finite() ==
bool(capacity_),
"must call `setup()` before");
2914 M_insist(not layout_.is_finite() ==
bool(first_iteration_),
"must call `setup()` before");
2920 static Schema empty_schema;
2921 auto [_store_inits, stores, _store_jumps] =
2924 Block store_inits(std::move(_store_inits)), store_jumps(std::move(_store_jumps));
2926 if (layout_.is_finite()) {
2929 store_inits.attach_to_current();
2932 IF (*size_ == *capacity_) {
2934 const uint64_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2935 auto buffer_size_in_bytes = (*capacity_ / uint64_t(layout_.child().num_tuples())) * child_size_in_bytes;
2937 Wasm_insist(ptr == *base_address_ + buffer_size_in_bytes.make_signed(),
2938 "buffer could not be resized sequentially in memory");
2942 IF (*first_iteration_) {
2944 store_inits.attach_to_current();
2946 *first_iteration_ =
false;
2951 stores.attach_to_current();
2953 if (layout_.is_finite()) {
2956 *size_ = uint64_t(layout_.num_tuples());
2978template<
bool IsGlobal>
2982 auto load = buffer_.get().create_load_proxy(schema_.get());
2985 auto env_first = [&](){
2987 load(first.clone());
2991 operator()(first, second, env_first);
2994template<
bool IsGlobal>
2998 auto load = buffer_.get().create_load_proxy(schema_.get());
2999 auto store = buffer_.get().create_store_proxy(schema_.get());
3003 for (
auto &e : schema_.get()) {
3007 IF (
value.clone().is_null()) {
3016 if (
value.can_be_null()) {
3018 _env_first.
add(e.id, var);
3024 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3025 [](std::monostate) ->
void {
M_unreachable(
"value must be loaded beforehand"); },
3026 }, env_first.
get(e.id));
3032 load(second.clone());
3043template<
bool IsGlobal>
3048 auto store = buffer_.get().create_store_proxy(schema_.get());
3052 for (
auto &e : schema_.get()) {
3056 IF (
value.clone().is_null()) {
3065 if (
value.can_be_null()) {
3067 _env_first.
add(e.id, var);
3073 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3074 [](std::monostate) ->
void {
M_unreachable(
"value must be loaded beforehand"); }
3075 }, env_first.
get(e.id));
3103 static thread_local struct {} _;
3107 using fn_t = int32_t(uint32_t, uint32_t,
char*,
char*, uint32_t);
3108 std::optional<FunctionProxy<fn_t>> strncmp_terminating_nul;
3109 std::optional<FunctionProxy<fn_t>> strncmp_no_terminating_nul;
3115 auto strncmp_non_null = [&d, &_left, &_right, &reverse](
Ptr<Charx1> left,
Ptr<Charx1> right, U32x1 len) -> I32x1 {
3116 Wasm_insist(left.clone().not_null(),
"left operand must not be NULL");
3117 Wasm_insist(right.clone().not_null(),
"right operand must not be NULL");
3118 Wasm_insist(len.clone() != 0
U,
"length to compare must not be 0");
3123 auto left_gt_right = *left.clone() > *right.clone();
3124 return left_gt_right.to<int32_t>() - (*left < *right).to<int32_t>();
3127 if (not d.strncmp_terminating_nul) {
3129 FUNCTION(strncmp_terminating_nul, data_t::fn_t)
3141 I32x1 len_left =
Select(len < len_ty_left, len, len_ty_left) .make_signed();
3142 I32x1 len_right =
Select(len < len_ty_right, len, len_ty_right).make_signed();
3148 result = (left != end_left).to<int32_t>() - (right != end_right).to<int32_t>();
3149 BREAK(result != 0 or left == end_left);
3153 result = (*left > *right).to<int32_t>() - (*left < *right).to<int32_t>();
3165 d.strncmp_terminating_nul = std::move(strncmp_terminating_nul);
3169 M_insist(
bool(d.strncmp_terminating_nul));
3170 return (*d.strncmp_terminating_nul)(_left.
length(), _right.
length(), left, right, len);
3172 if (not d.strncmp_no_terminating_nul) {
3174 FUNCTION(strncmp_no_terminating_nul, data_t::fn_t)
3186 I32x1 len_left =
Select(len < len_ty_left, len, len_ty_left) .make_signed();
3187 I32x1 len_right =
Select(len < len_ty_right, len, len_ty_right).make_signed();
3192 end_left = left + len_left;
3193 end_right = right + len_right;
3197 WHILE(*end_left != 0
and end_left != left + len_left) {
3201 WHILE(*end_right != 0
and end_right != right + len_right) {
3206 swap(left, end_left);
3207 swap(right, end_right);
3220 IF (left != end_left) {
3225 IF (right != end_right) {
3232 result = (val_left > val_right).to<int32_t>() - (val_left < val_right).to<int32_t>();
3234 BREAK(val_left == 0);
3237 left += reverse ? -1 : 1;
3238 right += reverse ? -1 : 1;
3244 d.strncmp_no_terminating_nul = std::move(strncmp_no_terminating_nul);
3248 M_insist(
bool(d.strncmp_no_terminating_nul));
3249 return (*d.strncmp_no_terminating_nul)(_left.
length(), _right.
length(), left, right, len);
3257 IF (left.is_null() or right.is_null()) {
3258 result = _I32x1::Null();
3260 result = strncmp_non_null(left, right, len);
3264 const Var<I32x1> result(strncmp_non_null(left, right, len));
3265 return _I32x1(result);
3273 U32x1 len(std::min<uint32_t>(left.
length(), right.
length()) + 1U);
3274 return strncmp(left, right, len, reverse);
3279 _I32x1
res =
strncmp(left, right, len, reverse);
3282 case EQ:
return res == 0;
3283 case NE:
return res != 0;
3284 case LT:
return res < 0;
3285 case LE:
return res <= 0;
3286 case GT:
return res > 0;
3287 case GE:
return res >= 0;
3293 _I32x1
res =
strcmp(left, right, reverse);
3296 case EQ:
return res == 0;
3297 case NE:
return res != 0;
3298 case LT:
return res < 0;
3299 case LE:
return res <= 0;
3300 case GT:
return res > 0;
3301 case GE:
return res >= 0;
3312 static thread_local struct {} _;
3322 if (not d.strncpy) {
3332 Wasm_insist(not src.is_nullptr(),
"source must not be nullptr");
3333 Wasm_insist(not dst.is_nullptr(),
"destination must not be nullptr");
3336 WHILE (src != src_end) {
3338 BREAK(*src ==
'\0');
3345 d.strncpy = std::move(
strncpy);
3361 static thread_local struct {} _;
3371 M_insist(
'_' != escape_char
and '%' != escape_char,
"illegal escape character");
3376 return _Boolx1(
true);
3379 auto like_non_null = [&d, &_str, &_pattern, &escape_char](
Ptr<Charx1> str,
Ptr<Charx1> pattern) -> Boolx1 {
3380 Wasm_insist(str.clone().not_null(),
"string operand must not be NULL");
3381 Wasm_insist(pattern.clone().not_null(),
"pattern operand must not be NULL");
3385 FUNCTION(
like,
bool(int32_t, int32_t,
char*,
char*,
char))
3390 const auto len_ty_pattern =
PARAMETER(1);
3398 I32x1 num_entries = (len_ty_str + 1) * (len_ty_pattern + 1);
3403 WHILE (entry < dp + num_entries.clone()) {
3426 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3428 entry += len_ty_str + 1;
3440 entry = dp + len_ty_str + 2;
3443 pattern = val_pattern;
3446 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3449 WHILE (byte_pattern !=
'\0') {
3453 IF (is_not_escaped
and byte_pattern == escape_char) {
3455 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3459 IF (byte_pattern !=
'_' and byte_pattern !=
'%' and byte_pattern != escape_char) {
3460 Throw(exception::invalid_escape_sequence);
3463 is_not_escaped =
false;
3470 byte_str =
Select(str < end_str, *str,
'\0');
3473 WHILE (byte_str !=
'\0') {
3477 IF (is_not_escaped
and byte_pattern ==
'%') {
3479 *entry = *(entry - (len_ty_str + 1)) or *(entry - 1);
3481 IF ((is_not_escaped
and byte_pattern ==
'_') or byte_pattern == byte_str) {
3483 *entry = *(entry - (len_ty_str + 2));
3491 byte_str =
Select(str < end_str, *str,
'\0');
3497 entry += len_ty_str + 1 - len_str;
3500 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3501 is_not_escaped =
true;
3508 const Var<Boolx1> result(*(entry + len_str - (len_ty_str + 2)));
3516 d.like = std::move(
like);
3521 return (*d.like)(_str.
length(), _pattern.
length(), str, pattern, escape_char);
3525 auto [_val_str, is_null_str] = _str.split();
3526 auto [_val_pattern, is_null_pattern] = _pattern.split();
3527 Ptr<Charx1> val_str(_val_str), val_pattern(_val_pattern);
3530 IF (is_null_str or is_null_pattern) {
3531 result = _Boolx1::Null();
3533 result = like_non_null(val_str, val_pattern);
3537 const Var<Boolx1> result(like_non_null(_str, _pattern));
3538 return _Boolx1(result);
3544 static thread_local struct {} _;
3555 M_insist(std::regex_match(*_pattern, std::regex(
"%[^_%\\\\]+%")),
"invalid contains pattern");
3557 if (_str.
length() == 0) {
3559 return _Boolx1(
false);
3562 auto contains_non_null = [&d, &_str, &_pattern](
Ptr<Charx1> str) -> Boolx1 {
3563 Wasm_insist(str.clone().not_null(),
"string operand must not be NULL");
3565 auto it = d.contains_map.find(_pattern);
3566 if (it == d.contains_map.end()) {
3576 const int64_t len_pattern = strlen(*_pattern) - 2;
3578 for (std::size_t i = 0; i < len_pattern; ++i)
3579 pattern[i] = (*_pattern)[i + 1];
3583 int64_t len_prefix = -1;
3585 tbl[0] = len_prefix;
3586 for (std::size_t i = 1; i < len_pattern + 1; ++i) {
3587 while (len_prefix >= 0
and pattern[len_prefix] != pattern[i - 1])
3588 len_prefix = tbl[len_prefix];
3590 tbl[i] = len_prefix;
3596 WHILE (val_str < end_str
and *val_str !=
'\0') {
3599 pos_pattern = *(
Ptr<I64x1>(tbl) + pos_pattern);
3600 IF (pos_pattern < 0) {
3606 IF (pos_pattern == len_pattern) {
3612 it = d.contains_map.emplace_hint(it, _pattern, std::move(
contains));
3616 M_insist(it != d.contains_map.end());
3617 return (it->second)(_str.
length(), str);
3621 auto [_val_str, is_null_str] = _str.split();
3626 result = _Boolx1::Null();
3628 result = contains_non_null(val_str);
3632 const Var<Boolx1> result(contains_non_null(_str));
3633 return _Boolx1(result);
3639 M_insist(std::regex_match(*pattern, std::regex(
"[^_%\\\\]+%")),
"invalid prefix pattern");
3642 const int32_t len_pattern = strlen(*pattern) - 1;
3644 for (std::size_t i = 0; i < len_pattern; ++i)
3645 _lower_bound[i] = (*pattern)[i];
3646 _lower_bound[len_pattern] =
'\0';
3651 for (std::size_t i = 0; i < len_pattern - 1; ++i)
3652 _upper_bound[i] = (*pattern)[i];
3653 const char last_char = (*pattern)[len_pattern - 1];
3654 _upper_bound[len_pattern - 1] = last_char + 1;
3655 _upper_bound[len_pattern] =
'\0';
3659 auto str_cpy = str.
clone();
3665 M_insist(std::regex_match(*pattern, std::regex(
"%[^_%\\\\]+")),
"invalid suffix pattern");
3668 const int32_t len_pattern = strlen(*pattern) - 1;
3670 for (std::size_t i = 0; i < len_pattern; ++i)
3671 _lower_bound[i] = (*pattern)[i + 1];
3672 _lower_bound[len_pattern] =
'\0';
3677 const char first_char = (*pattern)[1];
3678 _upper_bound[0] = first_char + 1;
3679 for (std::size_t i = 1; i < len_pattern; ++i)
3680 _upper_bound[i] = (*pattern)[i + 1];
3681 _upper_bound[len_pattern] =
'\0';
3685 const auto max_length = std::max<uint32_t>(str.
length(), len_pattern);
3686 auto str_cpy = str.
clone();
3687 return strncmp(str_cpy, lower_bound, U32x1(max_length),
GE,
true)
and
3688 strncmp(str, upper_bound, U32x1(max_length),
LT,
true);
3696template<
bool Predicated>
3698 const std::vector<SortingOperator::order_type> &order)
3704 for (
auto &o : order) {
3709 [&]<
typename T>(
Expr<T> val_left) ->
void {
3711 Expr<T> val_right = env_right.template compile<Expr<T>>(o.first);
3713 M_insist(val_left.can_be_null() == val_right.can_be_null(),
3714 "either both or none of the value to compare must be nullable");
3715 if (val_left.can_be_null()) {
3716 using type = std::conditional_t<std::is_same_v<T, bool>, _I32x1,
Expr<T>>;
3718 if constexpr (std::is_same_v<T, bool>) {
3719 left = val_left.template to<int32_t>();
3720 right = val_right.template to<int32_t>();
3727 I32x1 cmp_null = right.is_null().template to<int32_t>() - left.is_null().template to<int32_t>();
3728 _I32x1 _val_lt = (left < right).
template to<int32_t>();
3729 _I32x1 _val_gt = (left > right).
template to<int32_t>();
3730 _I32x1 _cmp_val = o.second ? _val_gt - _val_lt : _val_lt - _val_gt;
3731 auto [cmp_val, cmp_is_null] = _cmp_val.split();
3732 cmp_is_null.discard();
3733 I32x1 cmp = (cmp_null << 1) + cmp_val;
3737 using type = std::conditional_t<std::is_same_v<T, bool>, I32x1,
PrimitiveExpr<T>>;
3739 if constexpr (std::is_same_v<T, bool>) {
3740 left = val_left.insist_not_null().template to<int32_t>();
3741 right = val_right.insist_not_null().template to<int32_t>();
3743 left = val_left.insist_not_null();
3744 right = val_right.insist_not_null();
3748 I32x1 val_lt = (left < right).
template to<int32_t>();
3749 I32x1 val_gt = (left > right).
template to<int32_t>();
3750 I32x1 cmp = o.second ? val_gt - val_lt : val_lt - val_gt;
3755 [&](
NChar val_left) ->
void {
3756 auto &cs = as<const CharacterSequence>(*o.first.get().type());
3759 NChar val_right = env_right.template compile<NChar>(o.first);
3762 NChar left(_left, val_left.can_be_null(), val_left.length(), val_left.guarantees_terminating_nul()),
3766 "either both or none of the value to compare must be nullable");
3767 if (val_left.can_be_null()) {
3769 I32x1 cmp_null = _right.is_null().to<int32_t>() - _left.is_null().to<int32_t>();
3770 _I32x1 _delta = o.second ?
strcmp(left, right) :
strcmp(right, left);
3771 auto [delta_val, delta_is_null] = _delta.split();
3773 "result of strcmp is assumed to be in [-1,1]");
3774 delta_is_null.discard();
3775 I32x1 cmp = (cmp_null << 1) + delta_val;
3780 I32x1 delta = o.second ?
strcmp(left, right).insist_not_null()
3781 :
strcmp(right, left).insist_not_null();
3783 "result of strcmp is assumed to be in [-1,1]");
3788 [](
auto&&) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3789 [](std::monostate) ->
void {
M_unreachable(
"invalid expression"); }
3799 auto emit_comparison_rec = [&](
decltype(order.cbegin()) curr,
const decltype(order.cend()) end,
3812 [&]<
typename T>(
Expr<T> val_left) ->
void {
3814 Expr<T> val_right = env_right.template compile<Expr<T>>(curr->first);
3816 M_insist(val_left.can_be_null() == val_right.can_be_null(),
3817 "either both or none of the value to compare must be nullable");
3818 if (val_left.can_be_null()) {
3819 using type = std::conditional_t<std::is_same_v<T, bool>, _I32x1,
Expr<T>>;
3821 if constexpr (std::is_same_v<T, bool>) {
3822 _left = val_left.template to<int32_t>();
3823 _right = val_right.template to<int32_t>();
3830 IF (_left.not_null()) {
3831 IF (_right.is_null()) {
3835 auto left = _left.val().insist_not_null(),
3836 right = _right.val().insist_not_null();
3837 Boolx1 left_lt_right = curr->second ? left.clone() < right.clone()
3838 : left.clone() > right.clone();
3839 IF (left_lt_right) {
3843 Boolx1 left_gt_right = curr->second ? left > right : left < right;
3844 IF (left_gt_right) {
3849 IF (_right.not_null()) {
3855 using type = std::conditional_t<std::is_same_v<T, bool>, I32x1,
PrimitiveExpr<T>>;
3857 if constexpr (std::is_same_v<T, bool>) {
3858 left = val_left.insist_not_null().template to<int32_t>();
3859 right = val_right.insist_not_null().template to<int32_t>();
3861 left = val_left.insist_not_null();
3862 right = val_right.insist_not_null();
3866 Boolx1 left_lt_right = curr->second ? left < right : left > right;
3867 IF (left_lt_right) {
3871 Boolx1 left_gt_right = curr->second ? left > right : left < right;
3872 IF (left_gt_right) {
3878 [&](
NChar val_left) ->
void {
3879 auto &cs = as<const CharacterSequence>(*curr->first.get().type());
3882 NChar val_right = env_right.template compile<NChar>(curr->first);
3886 NChar left(_left,
false, val_left.length(), val_left.guarantees_terminating_nul()),
3890 "either both or none of the value to compare must be nullable");
3891 if (val_left.can_be_null()) {
3893 IF (_left.not_null()) {
3894 IF (_right.is_null()) {
3898 I32x1 cmp = curr->second ?
strcmp(left, right).insist_not_null()
3899 :
strcmp(right, left).insist_not_null();
3900 IF (cmp.clone() != 0) {
3905 IF (_right.not_null()) {
3912 I32x1 cmp = curr->second ?
strcmp(left, right).insist_not_null()
3913 :
strcmp(right, left).insist_not_null();
3914 IF (cmp.clone() != 0) {
3920 [](
auto&&) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3921 [](std::monostate) ->
void {
M_unreachable(
"invalid expression"); }
3925 rec(std::next(curr), end, rec);
3927 emit_comparison_rec(order.cbegin(), order.cend(), emit_comparison_rec);
__attribute__((constructor(202))) static void register_interpreter()
#define M_insist_no_ternary_logic()
#define FUNCTION(NAME, TYPE)
void convert_in_place(SQL_t &operand)
Convert operand of some SQL_t type to the target type.
#define CMPOP(OP, STRCMP_OP)
std::conditional_t< CanBeNull, _Bool< L >, Bool< L > > compile_cnf(ExprCompiler &C, const cnf::CNF &cnf)
void add(const char *group_name, const char *short_name, const char *long_name, const char *description, Callback &&callback)
Adds a new group option to the ArgParser.
#define M_unreachable(MSG)
#define M_CONSTEXPR_COND(COND, IF_TRUE, IF_FALSE)
const Schema const Schema & tuple_schema
std::size_t get_num_simd_lanes(const DataLayout &layout, const Schema &layout_schema, const Schema &tuple_schema)
Returns the number of SIMD lanes used for accessing tuples of schema tuple_schema in SIMDfied manner ...
const Schema & layout_schema
_I32x1 strcmp(NChar left, NChar right, bool reverse=false)
Compares two strings left and right.
_Boolx1 like_prefix(NChar str, const ThreadSafePooledString &pattern)
Checks whether the string str has the prefix pattern.
_Boolx1 like_contains(NChar str, const ThreadSafePooledString &pattern)
Checks whether the string str contains the pattern pattern.
_I32x1 strncmp(NChar left, NChar right, U32x1 len, bool reverse=false)
Compares two strings left and right.
std::variant< std::monostate #define ADD_TYPE(TYPE) SQL_TYPES(ADD_TYPE) > SQL_t
Ptr< Charx1 > strncpy(Ptr< Charx1 > dst, Ptr< Charx1 > src, U32x1 count)
Copies the contents of src to dst, but no more than count characters.
template I32x1 compare< true >(const Environment &, const Environment &, const std::vector< SortingOperator::order_type > &)
typename detail::_var_helper< T >::type _Var
Local variable that can always be NULL.
std::tuple< Block, Block, Block > compile_store_sequential(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, std::size_t num_simd_lanes, const Schema &layout_schema, Variable< uint64_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially s...
typename detail::var_helper< T >::type Var
Local variable.
void compile_store_point_access(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, const Schema &layout_schema, U64x1 tuple_id)
Compiles the data layout layout starting at memory address base_address and containing tuples of sche...
std::variant< std::monostate #define ADD_TYPE(TYPE) SQL_ADDR_TYPES(ADD_TYPE) > SQL_addr_t
PrimitiveExpr< bool, L > eqz() and(L
auto make_signed()
Conversion of a PrimitiveExpr<T, L> to a PrimitiveExpr<std::make_signed_t<T>, L>.
void GOTO(const Block &block)
Jumps to the end of block.
template I32x1 compare< false >(const Environment &, const Environment &, const std::vector< SortingOperator::order_type > &)
and
Constructs a new PrimitiveExpr from a constant value.
void compile_load_point_access(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, const Schema &layout_schema, U64x1 tuple_id)
Compiles the data layout layout starting at memory address base_address and containing tuples of sche...
typename detail::global_helper< T >::type Global
Global variable.
Bool< L > is_null(SQL_t &variant)
std::tuple< Block, Block, Block > compile_store_sequential_single_pass(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, std::size_t num_simd_lanes, const Schema &layout_schema, Variable< uint64_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially s...
auto Select(C &&_cond, T &&_tru, U &&_fals)
void compile_data_layout_point_access(const Schema &_tuple_value_schema, const Schema &_tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, const Schema &layout_schema, U64x1 tuple_id)
Compiles the data layout layout starting at memory address base_address and containing tuples of sche...
_Boolx1 like(NChar str, NChar pattern, const char escape_char='\\')
Checks whether the string str matches the pattern pattern regarding SQL LIKE semantics using escape c...
void discard()
Discards this.
std::tuple< Block, Block, Block > compile_load_sequential(const Schema &tuple_value_schema, const Schema &tuple_addr_schema, Ptr< void > base_address, const storage::DataLayout &layout, std::size_t num_simd_lanes, const Schema &layout_schema, Variable< uint64_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially l...
PrimitiveExpr< uint64_t, L > L L L L U
_Boolx1 like_suffix(NChar str, const ThreadSafePooledString &pattern)
Checks whether the string str has the suffix pattern.
for(std::size_t idx=1;idx< num_vectors;++idx) res.emplace((vectors_[idx].bitmask()<< uint32_t(idx *vector_type return * res
std::variant< std::monostate, _Boolx1, _Boolx16, _Boolx32 > SQL_boolean_t
I32x1 compare(const Environment &env_left, const Environment &env_right, const std::vector< SortingOperator::order_type > &order)
Compares two tuples, which must be already loaded into the environments env_left and env_right,...
void CONTINUE(std::size_t level=1)
std::pair<::wasm::Expression *, std::list< std::shared_ptr< Bit > > > move()
Moves the underlying Binaryen ::wasm::Expression and the referenced bits out of this.
typename uint< W >::type uint_t
PrimitiveExpr clone() const
Creates and returns a deep copy of this.
cmp_op
comparison operations, e.g. for string comparison
void BREAK(std::size_t level=1)
static constexpr std::size_t num_simd_lanes
the number of SIMD lanes of the represented expression, i.e. 1 for scalar and at least 2 for vectori...
M_EXPORT constexpr bool is_pow_2(T n)
std::function< void(void)> pipeline_t
bool M_EXPORT contains(const H &haystack, const N &needle)
Checks whether haystack contains needle.
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
ThreadSafeStringPool::proxy_type ThreadSafePooledString
bool M_EXPORT like(const std::string &str, const std::string &pattern, const char escape_char='\\')
Compares a SQL-style LIKE pattern with the given std::string.
and arithmetic< U > and same_signedness< T, U > U
std::string interpret(const std::string &str, char esc='\\', char quote='"')
void M_EXPORT setbit(T *bytes, bool value, uint32_t n)
auto visit(Callable &&callable, Base &obj, m::tag< Callable > &&=m::tag< Callable >())
Generic implementation to visit a class hierarchy, with similar syntax as std::visit.
command-line options for the HeuristicSearchPlanEnumerator
The catalog contains all Databases and keeps track of all meta information of the database system.
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
static Catalog & Get()
Return a reference to the single Catalog instance.
m::ArgParser & arg_parser()
The type of character strings, both fixed length and varying length.
fnid_t fnid
the function id
A Type that represents the absence of any other type.
The numeric type represents integer and floating-point types of different precision and scale.
uint64_t size() const override
Compute the size in bits of an instance of this type.
Pooled< T, Pool, false > assert_not_none() const
An Identifier is composed of a name and an optional prefix.
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
std::size_t num_entries() const
Returns the number of entries in this Schema.
const_iterator cend() const
Schema deduplicate() const
Returns a deduplicated version of this Schema, i.e.
iterator find(const Identifier &id)
Returns an iterator to the entry with the given Identifier id, or end() if no such entry exists.
Schema drop_constants() const
Returns a copy of this Schema where all constant entries are removed.
std::unique_ptr< Expr > lhs
const Numeric * common_operand_type
std::unique_ptr< Expr > rhs
A constant: a string literal or a numeric constant.
const Type * type() const
Returns the Type of this Expr.
std::vector< std::unique_ptr< Expr > > args
const Function & get_function() const
A query expression for nested queries.
const ThreadSafePooledString & alias() const
ThreadSafePooledOptionalString text
declared as optional for dummy tokens
A unary expression: "+e", "-e", "~e", "NOT e".
std::unique_ptr< Expr > expr
A CNF represents a conjunction of cnf::Clauses.
bool can_be_null() const
Returns true iff this CNF formula is nullable, i.e.
This is an interface for factories that compute particular DataLayouts for a given sequence of Types,...
virtual size_type num_tuples() const =0
returns the number of tuples represented by an instance of this node
Models how data is laid out in a linear address space.
bool is_finite() const
returns true iff this DataLayout lays out a finite sequence of tuples
uint64_t stride_in_bits() const
return the stride (in bits) of the single child of the DataLayout
const Node & child() const
returns a reference to the single child of this DataLayout
size_type num_tuples() const
returns the number of tuples laid out by this DataLayout; must not be called when not is_finite()
std::vector< level_info_t > level_info_stack_t
void for_sibling_leaves(callback_leaves_t callback) const
Represents a code block, i.e.
void attach_to_current()
Attaches this Block to the wasm::Block currently active in the Module.
bool empty() const
Returns whether this Block is empty, i.e.
Buffers tuples by materializing them into memory.
buffer_load_proxy_t< IsGlobal > create_load_proxy(param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Creates and returns a proxy object to load value tuples of schema tuple_value_schema (default: entire...
void execute_pipeline_inline(setup_t setup, pipeline_t pipeline, teardown_t teardown, param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Emits code inline to execute the given pipeline pipeline for each value tuple of schema tuple_value_s...
void resume_pipeline(param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Emits code into a separate function to resume the pipeline for each value tuple of schema tuple_value...
Buffer(const Schema &schema, const storage::DataLayoutFactory &factory, bool load_simdfied=false, std::size_t num_tuples=0, setup_t setup=setup_t::Make_Without_Parent(), pipeline_t pipeline=pipeline_t(), teardown_t teardown=teardown_t::Make_Without_Parent())
Creates a buffer for num_tuples tuples (0 means infinite) of schema schema using the data layout crea...
void execute_pipeline(setup_t setup, pipeline_t pipeline, teardown_t teardown, param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Emits code into a separate function to execute the give pipeline pipeline for each value tuple of sch...
const Schema & schema() const
Returns the schema of the buffer.
void consume()
Emits code to store the current tuple into the buffer.
buffer_swap_proxy_t< IsGlobal > create_swap_proxy(param_t tuple_schema=param_t()) const
Creates and returns a proxy object to swap tuples of schema tuple_schema (default: entire tuples) in ...
std::optional< std::reference_wrapper< const Schema > > param_t
parameter type for proxy creation and pipeline resuming methods
void setup()
Performs the setup of all local variables of this buffer (by reading them from the global backups iff...
storage::DataLayout layout_
data layout of buffer
buffer_storage< IsGlobal > storage_
if IsGlobal, contains backups for base address, capacity, and size
buffer_store_proxy_t< IsGlobal > create_store_proxy(param_t tuple_schema=param_t()) const
Creates and returns a proxy object to store tuples of schema tuple_schema (default: entire tuples) to...
void resume_pipeline_inline(param_t tuple_value_schema=param_t(), param_t tuple_addr_schema=param_t()) const
Emits code inline to resume the pipeline for each value tuple of schema tuple_value_schema (default: ...
void teardown()
Performs the teardown of all local variables of this buffer (by storing them into the global backups ...
std::size_t num_simd_lanes() const
Returns the number of SIMD lanes used.
std::size_t num_simd_lanes_preferred() const
Returns the number of SIMD lanes preferred by other operators.
static thread_local std::unique_ptr< CodeGenContext > the_context_
Environment & env()
Returns the current Environment.
void set_num_simd_lanes(std::size_t n)
Sets the number of SIMD lanes used to n.
static CodeGenContext & Get()
Scope scoped_environment()
Creates a new, scoped Environment.
Binds Schema::Identifiers to Expr<T>s.
auto compile(T &&t) const
Compile t by delegating compilation to an ExprCompiler for this Environment.
void add(Schema::Identifier id, T &&expr)
Adds a mapping from id to expr.
std::unordered_map< Schema::Identifier, SQL_t > exprs_
maps Schema::Identifiers to Expr<T>s that evaluate to the current expression
SQL_t get(const Schema::Identifier &id) const
Returns the copied entry for identifier id.
std::unordered_map< Schema::Identifier, SQL_addr_t > expr_addrs_
maps Schema::Identifiers to Ptr<Expr<T>>s that evaluate to the address of the current expression
Compiles AST expressions m::Expr to Wasm ASTs m::wasm::Expr<T>.
void operator()(const ast::ErrorExpr &) override
SQL_t compile(const m::ast::Expr &e)
Compiles a m::Expr e of statically unknown type to a SQL_t.
const Environment & env_
the environment to use for resolving designators to Expr<T>s
A handle to create a Function and to create invocations of that function.
Helper struct for garbage collection done by the Module.
C & add_garbage_collected_data(void *handle, Args... args)
Adds and returns an instance of.
std::size_t length() const
bool guarantees_terminating_nul() const
Proxy to implement loads from a buffer.
Proxy to implement stores to a buffer.
Proxy to implement swaps in a buffer.
void operator()(U64x1 first, U64x1 second)
Swaps tuples with IDs first and second.
Helper type to deduce the Expr<U> type given a.