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 % uint32_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<uint32_t>();
663 tuple_id += uint32_t(
L);
668 tuple_id += uint32_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 = [&](U32x1 tuple_id) -> U64x1 {
689 auto rec = [&](U32x1 curr_tuple_id,
decltype(levels.cbegin()) curr,
const decltype(levels.cend()) end,
693 Wasm_insist(curr_tuple_id == tuple_id % uint32_t(levels.back().num_tuples));
698 U32x1 child_iter = curr_tuple_id.clone() >> uint32_t(__builtin_ctzl(curr->num_tuples));
699 U32x1 inner_tuple_id = curr_tuple_id bitand uint32_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 U32x1 child_iter = curr_tuple_id.clone() / uint32_t(curr->num_tuples);
705 U32x1 inner_tuple_id = curr_tuple_id % uint32_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<I32x1>> inode_byte_offset;
714 std::optional<const Var<U32x1>> inode_iter;
716 M_insist(inode_offset_in_bits % 8 == 0,
"INode offset must be byte aligned");
717 inode_byte_offset.emplace(
718 int32_t(inode_offset_in_bits / 8)
719 + compute_additional_inode_byte_offset(tuple_id).
make_signed().
template to<int32_t>()
721 M_insist(levels.back().num_tuples != 0,
"INode must be large enough for at least one tuple");
722 if (levels.back().num_tuples != 1) {
724 is_pow_2(levels.back().num_tuples) ? tuple_id bitand uint32_t(levels.back().num_tuples - 1U)
725 : tuple_id % uint32_t(levels.back().num_tuples)
733 for (
auto &leaf_info : leaves) {
734 const uint8_t bit_stride = leaf_info.stride_in_bits % 8;
737 if (not needs_null_bitmap)
741 M_insist(not has_null_bitmap,
"at most one bitmap may be specified");
742 has_null_bitmap =
true;
744 M_insist(
L == 1,
"SIMDfied loading of NULL bitmap with bit stride currently not supported");
746 M_insist(
bool(inode_iter),
"stride requires repetition");
747 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
748 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
749 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
751 null_bitmap_bit_offset = leaf_info.offset_in_bits % 8;
752 null_bitmap_stride_in_bits = leaf_info.stride_in_bits;
755 null_bitmap_ptr.emplace();
756 *null_bitmap_ptr = base_address.clone() + *inode_byte_offset + leaf_byte_offset;
757 null_bitmap_mask.emplace();
758 *null_bitmap_mask = 1U << leaf_bit_offset;
762 std::size_t prev_layout_idx = 0;
765 if (layout_entry.nullable()) {
766 auto tuple_it = tuple_value_schema.
find(layout_entry.id);
767 if (tuple_it == tuple_value_schema.end())
769 M_insist(prev_layout_idx == 0 or layout_idx > prev_layout_idx,
770 "layout entries not processed in ascending order");
771 M_insist(*tuple_it->type == *layout_entry.type);
772 const auto delta = layout_idx - prev_layout_idx;
773 const uint8_t bit_delta = delta % 8;
774 const int32_t byte_delta = delta / 8;
776 auto advance_to_next_bit = [&]() {
780 *null_bitmap_mask <<=
781 Select(*pred, bit_delta, uint8_t(0));
783 *null_bitmap_mask <<= bit_delta;
786 *null_bitmap_ptr += (*null_bitmap_mask bitand 0xffU).
eqz().template to<int32_t>();
788 *null_bitmap_mask =
Select((*null_bitmap_mask bitand 0xffU).
eqz(),
789 *null_bitmap_mask >> 8U, *null_bitmap_mask);
795 Select(*pred, byte_delta, 0);
797 *null_bitmap_ptr += byte_delta;
802 if constexpr (IsStore) {
804 auto store = [&]<
typename T>() {
806 advance_to_next_bit();
811 null_bitmap_mask->template to<uint8_t>());
815 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
819 case Numeric::N_Decimal:
822 case 8: store.template operator()<_I8x1 >();
break;
823 case 16: store.template operator()<_I16x1>();
break;
824 case 32: store.template operator()<_I32x1>();
break;
825 case 64: store.template operator()<_I64x1>();
break;
828 case Numeric::N_Float:
830 store.template
operator()<_Floatx1>();
832 store.template operator()<_Doublex1>();
837 advance_to_next_bit();
840 setbit(null_bitmap_ptr->template to<uint8_t*>(),
value.is_null(),
841 null_bitmap_mask->template to<uint8_t>());
844 [&](
const Date&) { store.template operator()<_I32x1>(); },
845 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
849 const auto tuple_idx = std::distance(tuple_value_schema.begin(), tuple_it);
851 advance_to_next_bit();
853 U8x1
byte = *null_bitmap_ptr->template to<uint8_t*>();
855 (
byte bitand *null_bitmap_mask).
template to<bool>()
857 new (&null_bits[tuple_idx]) Boolx1(
value);
862 prev_layout_idx = layout_idx;
865 if constexpr (IsStore) {
871 "value of non-nullable entry must not be nullable");
875 M_unreachable(
"invalid type for given number of SIMD lanes");
879 [&](
const Boolean&) { check.template operator()<_Bool<L>>(); },
883 case Numeric::N_Decimal:
886 case 8: check.template operator()<_I8 <L>>();
break;
887 case 16: check.template operator()<_I16<L>>();
break;
888 case 32: check.template operator()<_I32<L>>();
break;
889 case 64: check.template operator()<_I64<L>>();
break;
892 case Numeric::N_Float:
894 check.template
operator()<_Float<L>>();
896 check.template operator()<_Double<L>>();
900 [&](
const Date&) { check.template operator()<_I32<L>>(); },
901 [&](
const DateTime&) { check.template operator()<_I64<L>>(); },
903 }, *layout_entry.type);
912 const auto delta = leaf_info.stride_in_bits - prev_layout_idx;
913 const uint8_t bit_delta = delta % 8;
914 const int32_t byte_delta = delta / 8;
919 *null_bitmap_mask <<=
Select(*pred, bit_delta, uint8_t(0));
921 *null_bitmap_mask <<= bit_delta;
924 *null_bitmap_ptr += (*null_bitmap_mask bitand 0xffU).
eqz().template to<int32_t>();
926 *null_bitmap_mask =
Select((*null_bitmap_mask bitand 0xffU).
eqz(),
927 *null_bitmap_mask >> 8U, *null_bitmap_mask);
934 *null_bitmap_ptr +=
Select(*pred, byte_delta, 0);
936 *null_bitmap_ptr += byte_delta;
942 "NULL bits must fill at least an entire SIMD vector when loading SIMDfied");
943 M_insist(
L == 1 or tuple_value_schema.num_entries() <= 64,
944 "bytes containing a NULL bitmap must fit into scalar value when loading SIMDfied");
946 std::max(ceil_to_pow_2(tuple_value_schema.num_entries()), 8UL) == leaf_info.stride_in_bits,
947 "NULL bitmaps must be packed s.t. the distance between two NULL bits of a single "
948 "attribute is a power of 2 when loading SIMDfied");
949 M_insist(
L == 1 or leaf_info.offset_in_bits % 8 == 0,
950 "NULL bitmaps must not start with bit offset when loading SIMDfied");
952 auto byte_offset = [&]() -> I32x1 {
953 if (inode_iter
and leaf_info.stride_in_bits) {
957 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
958 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
959 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
961 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
963 return *inode_byte_offset + leaf_byte_offset;
965 return *inode_byte_offset;
969 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
970 auto [it, inserted] =
972 loading_context.try_emplace(std::move(key)),
973 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
976 it->second.ptr = base_address.clone() + byte_offset;
979 byte_offset.discard();
981 const auto &ptr = it->second.ptr;
984 std::unordered_map<int32_t, Var<U8x1>> loaded_bytes;
989 if constexpr (not IsStore
and L > 1) {
990 auto emplace = [&]<
typename T>() {
991 using type =
typename T::type;
992 static constexpr std::size_t lanes = T::num_simd_lanes;
994 bytes.template emplace<Var<T>>(
995 *(ptr + leaf_info.offset_in_bits / 8).
template to<type*, lanes>()
999 switch (ceil_to_pow_2(tuple_value_schema.num_entries())) {
1000 default: emplace.template operator()<U8 <L>>();
break;
1001 case 16: emplace.template operator()<U16<L>>();
break;
1002 case 32: emplace.template operator()<U32<L>>();
break;
1003 case 64: emplace.template operator()<U64<L>>();
break;
1008 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
1009 auto &tuple_entry = tuple_value_schema[tuple_idx];
1010 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
1011 M_insist(*tuple_entry.type == *layout_entry.type);
1012 if (layout_entry.nullable()) {
1013 const uint8_t static_bit_offset = (leaf_info.offset_in_bits + layout_idx) % 8;
1014 const int32_t static_byte_offset = (leaf_info.offset_in_bits + layout_idx) / 8;
1015 if constexpr (IsStore) {
1022 if constexpr (
L == 1) {
1024 (ptr + static_byte_offset).
template to<uint8_t*>();
1025 setbit<U8x1>(byte_ptr,
is_null, static_bit_offset);
1027 auto store = [&,
is_null, layout_idx]<
typename U>() {
1028 using type =
typename U::type;
1029 static constexpr std::size_t lanes = U::num_simd_lanes;
1031 (ptr + leaf_info.offset_in_bits / 8).
template to<type*, lanes>();
1032 setbit<U>(bytes_ptr,
is_null, layout_idx);
1034 switch (ceil_to_pow_2(tuple_value_schema.num_entries())) {
1035 default: store.template operator()<U8 <L>>();
break;
1036 case 16: store.template operator()<U16<L>>();
break;
1037 case 32: store.template operator()<U32<L>>();
break;
1038 case 64: store.template operator()<U64<L>>();
break;
1044 M_unreachable(
"invalid type for given number of SIMD lanes");
1048 [&](
const Boolean&) { store.template operator()<_Bool<L>>(); },
1051 case Numeric::N_Int:
1052 case Numeric::N_Decimal:
1055 case 8: store.template operator()<_I8 <L>>();
break;
1056 case 16: store.template operator()<_I16<L>>();
break;
1057 case 32: store.template operator()<_I32<L>>();
break;
1058 case 64: store.template operator()<_I64<L>>();
break;
1061 case Numeric::N_Float:
1063 store.template
operator()<_Float<L>>();
1065 store.template operator()<_Double<L>>();
1069 M_insist(
L == 1,
"string SIMDfication currently not supported");
1073 (ptr + static_byte_offset).
template to<uint8_t*>();
1074 setbit<U8x1>(byte_ptr,
value.is_null(), static_bit_offset);
1077 [&](
const Date&) { store.template operator()<_I32<L>>(); },
1078 [&](
const DateTime&) { store.template operator()<_I64<L>>(); },
1080 }, *tuple_entry.type);
1084 if constexpr (
L == 1) {
1085 auto [it, inserted] = loaded_bytes.try_emplace(static_byte_offset);
1087 it->second = *(ptr + static_byte_offset).
template to<uint8_t*>();
1088 const auto &
byte = it->second;
1089 const uint8_t static_mask = 1U << static_bit_offset;
1091 new (&null_bits[tuple_idx]) Boolx1(
value);
1095 [&, layout_idx]<
typename T>
1097 requires (
L >= 16) {
1100 (_bytes bitand static_mask).
template to<bool>()
1102 new (&null_bits[tuple_idx]) Bool<L>(
value);
1105 [](
auto&) {
M_unreachable(
"invalid number of SIMD lanes"); },
1107 },
const_cast<const bytes_t&
>(bytes));
1113 if constexpr (IsStore) {
1116 [&, layout_entry]<sql_type
T>() {
1119 "value of non-nullable entry must not be nullable");
1123 M_unreachable(
"invalid type for given number of SIMD lanes");
1127 [&](
const Boolean&) { check.template operator()<_Bool<L>>(); },
1130 case Numeric::N_Int:
1131 case Numeric::N_Decimal:
1134 case 8: check.template operator()<_I8 <L>>();
break;
1135 case 16: check.template operator()<_I16<L>>();
break;
1136 case 32: check.template operator()<_I32<L>>();
break;
1137 case 64: check.template operator()<_I64<L>>();
break;
1140 case Numeric::N_Float:
1142 check.template
operator()<_Float<L>>();
1144 check.template operator()<_Double<L>>();
1148 [&](
const Date&) { check.template operator()<_I32<L>>(); },
1149 [&](
const DateTime&) { check.template operator()<_I64<L>>(); },
1151 }, *tuple_entry.type);
1159 M_insist(*layout_entry.type == *leaf_info.leaf.type());
1160 auto tuple_value_it = tuple_value_schema.find(layout_entry.id);
1161 auto tuple_addr_it = tuple_addr_schema.find(layout_entry.id);
1162 if (tuple_value_it == tuple_value_schema.end()
and tuple_addr_it == tuple_addr_schema.end())
1164 auto tuple_it = tuple_value_it != tuple_value_schema.end() ? tuple_value_it : tuple_addr_it;
1165 M_insist(*tuple_it->type == *layout_entry.type);
1166 const auto tuple_value_idx = std::distance(tuple_value_schema.begin(), tuple_value_it);
1167 const auto tuple_addr_idx = std::distance(tuple_addr_schema.begin(), tuple_addr_it);
1170 M_insist(tuple_it->type->is_boolean(),
1171 "leaf bit stride currently only for `Boolean` supported");
1173 "booleans must fill at least an entire SIMD vector when loading SIMDfied");
1174 M_insist(
L <= 64,
"bytes containing booleans must fit into scalar value when loading SIMDfied");
1175 M_insist(
L == 1 or leaf_info.stride_in_bits == 1,
1176 "booleans must be packed consecutively when loading SIMDfied");
1178 M_insist(
bool(inode_iter),
"stride requires repetition");
1179 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
1180 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
1181 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
1183 if constexpr (
L > 1) {
1186 "booleans must not start with bit offset when loading SIMDfied");
1190 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
1191 auto [it, inserted] =
1193 loading_context.try_emplace(std::move(key)),
1194 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
1195 M_insist(inserted == not it->second.mask);
1199 it->second.ptr = base_address.clone() + *inode_byte_offset;
1200 it->second.mask.emplace();
1201 if constexpr (
L == 1)
1202 *it->second.mask = 1U << leaf_bit_offset;
1206 leaf_bit_offset.discard();
1208 const auto &ptr = it->second.ptr;
1210 if constexpr (IsStore) {
1211 if constexpr (sql_type<_Bool<L>>) {
1214 auto [
value,
is_null] = env.get<_Bool<L>>(tuple_it->id).split();
1216 if constexpr (
L == 1) {
1218 (ptr + leaf_byte_offset).
template to<uint8_t*>();
1219 const auto &
mask = *it->second.mask;
1222 using bytes_t =
uint_t<
L / 8>;
1224 (ptr + leaf_byte_offset).
template to<bytes_t*>();
1225 *bytes_ptr =
value.bitmask().template to<bytes_t>();
1229 M_unreachable(
"invalid type for given number of SIMD lanes");
1232 if constexpr (sql_type<_Bool<L>>) {
1235 if constexpr (
L == 1) {
1236 U8x1
byte = *(ptr + leaf_byte_offset).
template to<uint8_t*>();
1237 const auto &
mask = *it->second.mask;
1238 if (tuple_value_it != tuple_value_schema.end()) {
1240 (
byte.
clone() bitand
mask.template to<uint8_t>()).template to<bool>()
1242 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
1247 using bytes_t =
uint_t<
L / 8>;
1249 *(ptr + leaf_byte_offset).
template to<bytes_t*>()
1251 auto create_mask = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
1254 auto static_mask = create_mask(std::make_index_sequence<L>());
1255 if (tuple_value_it != tuple_value_schema.end()) {
1257 (bytes.template broadcast<L>() bitand static_mask).template to<bool>()
1259 new (&values[tuple_value_idx])
SQL_t(_Bool<L>(
value));
1261 bytes.val().discard();
1267 M_unreachable(
"invalid type for given number of SIMD lanes");
1271 auto byte_offset = [&]() -> I32x1 {
1272 if (inode_iter
and leaf_info.stride_in_bits) {
1276 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
1277 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
1278 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
1280 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
1282 return *inode_byte_offset + leaf_byte_offset;
1284 return *inode_byte_offset;
1288 const uint8_t static_bit_offset = leaf_info.offset_in_bits % 8;
1289 const int32_t static_byte_offset = leaf_info.offset_in_bits / 8;
1291 key_t key(leaf_info.offset_in_bits % 8, leaf_info.stride_in_bits);
1292 auto [it, inserted] =
1294 loading_context.try_emplace(std::move(key)),
1295 std::make_pair(loading_context.emplace(loading_context.end(), std::move(key), value_t()),
true));
1298 it->second.ptr = base_address.clone() + byte_offset;
1301 byte_offset.discard();
1303 const auto &ptr = it->second.ptr;
1308 using type =
typename T::type;
1309 static constexpr std::size_t lanes = T::num_simd_lanes;
1311 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
1315 *(ptr + static_byte_offset).
template to<type*, lanes>() =
value;
1319 M_unreachable(
"invalid type for given number of SIMD lanes");
1325 using type =
typename T::type;
1326 static constexpr std::size_t lanes = T::num_simd_lanes;
1328 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
1330 if (tuple_value_it != tuple_value_schema.end()) {
1332 *(ptr + static_byte_offset).
template to<type*, lanes>()
1336 if (tuple_addr_it != tuple_addr_schema.end())
1338 (ptr + static_byte_offset).
template to<type*, lanes>()
1343 M_unreachable(
"invalid type for given number of SIMD lanes");
1347#define CALL(TYPE) if constexpr (IsStore) store.template operator()<TYPE>(); else load.template operator()<TYPE>()
1350 M_insist(
L == 1 or leaf_info.stride_in_bits == 8,
1351 "booleans must be packed consecutively in bytes when loading SIMDfied");
1352 if constexpr (sql_type<_Bool<L>>) {
1353 if constexpr (IsStore) {
1356 auto [
value,
is_null] = env.get<_Bool<L>>(tuple_it->id).split();
1359 (ptr + static_byte_offset).
template to<uint8_t*, L>();
1360 setbit<U8<L>>(byte_ptr,
value, static_bit_offset);
1366 *(ptr + static_byte_offset).
template to<uint8_t*, L>();
1367 U8<L> static_mask(1U << static_bit_offset);
1369 if (tuple_value_it != tuple_value_schema.end()) {
1371 (
byte.
clone() bitand static_mask.clone()).template to<bool>()
1373 new (&values[tuple_value_idx])
SQL_t(_Bool<L>(
value));
1377 static_mask.discard();
1381 M_unreachable(
"invalid type for given number of SIMD lanes");
1386 case Numeric::N_Int:
1387 case Numeric::N_Decimal:
1390 case 8:
CALL(_I8 <L>);
break;
1391 case 16:
CALL(_I16<L>);
break;
1392 case 32:
CALL(_I32<L>);
break;
1393 case 64:
CALL(_I64<L>);
break;
1396 case Numeric::N_Float:
1404 M_insist(
L == 1,
"string SIMDfication currently not supported");
1405 M_insist(static_bit_offset == 0,
"leaf offset of `CharacterSequence` must be byte aligned");
1406 if constexpr (IsStore) {
1410 IF (
value.clone().not_null()) {
1411 Ptr<Charx1> address((ptr + static_byte_offset).
template to<char*>());
1412 strncpy(address,
value, U32x1(cs.size() / 8)).discard();
1418 Ptr<Charx1> address((ptr + static_byte_offset).
template to<char*>());
1419 new (&values[tuple_value_idx])
SQL_t(
1420 NChar(address, layout_entry.nullable(), cs.length, cs.is_varying)
1426 [&](
const Date&) {
CALL(_I32<L>); },
1429 }, *tuple_it->type);
1436 auto emit_stride_jumps = [&](
decltype(levels.crbegin()) curr,
const decltype(levels.crend()) end) ->
void {
1437 auto rec = [&](
decltype(levels.crbegin()) curr,
const decltype(levels.crend()) end,
auto rec) ->
void {
1438 if (curr == end)
return;
1440 const auto inner = std::prev(curr);
1441 M_insist(curr->num_tuples % inner->num_tuples == 0,
"curr must be whole multiple of inner");
1444 const auto num_repetition_inner = curr->num_tuples / inner->num_tuples;
1445 const auto stride_remaining_in_bits = curr->stride_in_bits -
1446 num_repetition_inner * inner->stride_in_bits;
1447 M_insist(stride_remaining_in_bits % 8 == 0,
1448 "remaining stride of INodes must be whole multiple of a byte");
1451 if (
const int32_t remaining_stride_in_bytes = stride_remaining_in_bits / 8) [[likely]] {
1453 if (curr->num_tuples != 1U) {
1454 Boolx1 cond_mod = (tuple_id % uint32_t(curr->num_tuples)).
eqz();
1455 Boolx1 cond_and = (tuple_id bitand uint32_t(curr->num_tuples - 1U)).
eqz();
1456 const bool use_and =
is_pow_2(curr->num_tuples)
and options::remainder_removal;
1457 Boolx1 cond = use_and ? cond_and : cond_mod;
1458 (use_and ? cond_mod : cond_and).
discard();
1462 for (
auto &[_,
value] : loading_context) {
1463 if (is_predicated) {
1465 value.ptr +=
Select(*pred, remaining_stride_in_bytes, 0);
1467 value.ptr += remaining_stride_in_bytes;
1470 if (null_bitmap_ptr) {
1471 if (is_predicated) {
1474 Select(*pred, remaining_stride_in_bytes, 0);
1476 *null_bitmap_ptr += remaining_stride_in_bytes;
1481 rec(std::next(curr), end, rec);
1484 for (
auto &[_,
value] : loading_context) {
1485 if (is_predicated) {
1487 value.ptr +=
Select(*pred, remaining_stride_in_bytes, 0);
1489 value.ptr += remaining_stride_in_bytes;
1492 if (null_bitmap_ptr) {
1493 if (is_predicated) {
1496 Select(*pred, remaining_stride_in_bytes, 0);
1498 *null_bitmap_ptr += remaining_stride_in_bytes;
1503 rec(std::next(curr), end, rec);
1507 rec(std::next(curr), end, rec);
1511 rec(curr, end, rec);
1517 for (
auto &[key,
value] : loading_context) {
1518 const uint8_t bit_stride = key.second % 8;
1519 const int32_t byte_stride = key.second / 8;
1523 if (is_predicated) {
1525 *
value.mask <<=
Select(*pred, bit_stride, uint8_t(0));
1527 *
value.mask <<= bit_stride;
1530 value.ptr += (*
value.mask bitand 0xffU).
eqz().template to<int32_t>();
1534 if (byte_stride) [[likely]] {
1535 if (is_predicated) {
1540 value.ptr += int32_t(
L) * byte_stride;
1546 if (not levels.empty()) {
1548 Block lowest_inode_jumps(
false);
1549 for (
auto &[key,
value] : loading_context) {
1550 M_insist(levels.back().stride_in_bits % 8 == 0,
1551 "stride of INodes must be multiples of a whole byte");
1552 const auto stride_remaining_in_bits = levels.back().stride_in_bits -
1553 levels.back().num_tuples * key.second;
1554 const uint8_t remaining_bit_stride = stride_remaining_in_bits % 8;
1555 const int32_t remaining_byte_stride = stride_remaining_in_bits / 8;
1556 if (remaining_bit_stride) {
1560 const uint8_t end_bit_offset = (key.first + levels.back().num_tuples * key.second) % 8;
1561 M_insist(end_bit_offset != key.first);
1563 if (is_predicated) {
1566 "if the predicate is not fulfilled, the mask should not be advanced");
1568 *
value.mask = 1U << key.first;
1570 if (is_predicated) {
1572 value.ptr +=
Select(*pred, int32_t(end_bit_offset > key.first), 0);
1574 value.ptr += int32_t(end_bit_offset > key.first);
1578 if (remaining_byte_stride) [[likely]] {
1580 if (is_predicated) {
1583 Select(*pred, remaining_byte_stride, 0);
1585 value.ptr += remaining_byte_stride;
1590 if (null_bitmap_ptr) {
1593 M_insist(levels.back().stride_in_bits % 8 == 0,
1594 "stride of INodes must be multiples of a whole byte");
1595 const auto stride_remaining_in_bits = levels.back().stride_in_bits -
1596 levels.back().num_tuples * null_bitmap_stride_in_bits;
1597 const uint8_t remaining_bit_stride = stride_remaining_in_bits % 8;
1598 const int32_t remaining_byte_stride = stride_remaining_in_bits / 8;
1599 if (remaining_bit_stride) {
1601 const uint8_t end_bit_offset =
1602 (null_bitmap_bit_offset + levels.back().num_tuples * null_bitmap_stride_in_bits) % 8;
1603 M_insist(end_bit_offset != null_bitmap_bit_offset);
1605 if (is_predicated) {
1607 Wasm_insist(*pred or *null_bitmap_mask == 1U << null_bitmap_bit_offset,
1608 "if the predicate is not fulfilled, the mask should not be advanced");
1610 *null_bitmap_mask = 1U << null_bitmap_bit_offset;
1612 if (is_predicated) {
1615 Select(*pred, int32_t(end_bit_offset > null_bitmap_bit_offset), 0);
1617 *null_bitmap_ptr += int32_t(end_bit_offset > null_bitmap_bit_offset);
1621 if (remaining_byte_stride) [[likely]] {
1623 if (is_predicated) {
1626 Select(*pred, remaining_byte_stride, 0);
1628 *null_bitmap_ptr += remaining_byte_stride;
1635 if (not lowest_inode_jumps.
empty()) [[likely]] {
1636 M_insist(levels.back().num_tuples > 0);
1637 if (levels.back().num_tuples != 1U) {
1638 Boolx1 cond_mod = (tuple_id % uint32_t(levels.back().num_tuples)).
eqz();
1639 Boolx1 cond_and = (tuple_id bitand uint32_t(levels.back().num_tuples - 1U)).
eqz();
1640 const bool use_and =
is_pow_2(levels.back().num_tuples)
and options::remainder_removal;
1641 Boolx1 cond = use_and ? cond_and : cond_mod;
1642 (use_and ? cond_mod : cond_and).
discard();
1649 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1655 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1659 emit_stride_jumps(std::next(levels.crbegin()), levels.crend());
1665 if constexpr (not IsStore) {
1667 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
1668 auto &tuple_entry = tuple_value_schema[idx];
1674 env.add(tuple_entry.id, combined);
1676 env.add(tuple_entry.id,
value);
1681 if constexpr (
L == 1) {
1686 env.add(tuple_entry.id,
NChar(combined,
true,
value.length(),
1687 value.guarantees_terminating_nul()));
1690 env.add(tuple_entry.id,
NChar(_value,
false,
value.length(),
1691 value.guarantees_terminating_nul()));
1695 M_unreachable(
"string SIMDfication currently not supported");
1698 [](
auto) {
M_unreachable(
"value must be loaded beforehand"); },
1704 for (std::size_t idx = 0; idx != tuple_addr_schema.num_entries(); ++idx) {
1706 auto &tuple_entry = tuple_addr_schema[idx];
1707 env.add_addr(tuple_entry.id, std::move(addrs[idx]));
1713 for (std::size_t idx = 0; idx < tuple_value_schema.num_entries(); ++idx)
1714 values[idx].~
SQL_t();
1715 for (std::size_t idx = 0; idx < tuple_addr_schema.num_entries(); ++idx)
1717 if constexpr (not IsStore) {
1719 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
1720 if (has_null_bitmap
and layout_schema[tuple_value_schema[idx].
id].second.nullable())
1721 null_bits[idx].~Bool<L>();
1724 base_address.discard();
1727 if constexpr (IsStore)
1733 if constexpr (IsStore)
1734 return std::make_tuple<Block, Block, Block>(std::move(inits), std::move(stores), std::move(jumps));
1736 return std::make_tuple<Block, Block, Block>(std::move(inits), std::move(loads), std::move(jumps));
1743template<VariableKind Kind>
1744std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1749 if (options::pointer_sharing) {
1752 case 1:
return compile_data_layout_sequential<true, 1, false, true>(tuple_value_schema, tuple_addr_schema,
1755 case 2:
return compile_data_layout_sequential<true, 2, false, true>(tuple_value_schema, tuple_addr_schema,
1758 case 4:
return compile_data_layout_sequential<true, 4, false, true>(tuple_value_schema, tuple_addr_schema,
1761 case 8:
return compile_data_layout_sequential<true, 8, false, true>(tuple_value_schema, tuple_addr_schema,
1764 case 16:
return compile_data_layout_sequential<true, 16, false, true>(tuple_value_schema, tuple_addr_schema,
1767 case 32:
return compile_data_layout_sequential<true, 32, false, true>(tuple_value_schema, tuple_addr_schema,
1774 case 1:
return compile_data_layout_sequential<true, 1, false, false>(tuple_value_schema,
1775 tuple_addr_schema, base_address,
1777 case 2:
return compile_data_layout_sequential<true, 2, false, false>(tuple_value_schema,
1778 tuple_addr_schema, base_address,
1780 case 4:
return compile_data_layout_sequential<true, 4, false, false>(tuple_value_schema,
1781 tuple_addr_schema, base_address,
1783 case 8:
return compile_data_layout_sequential<true, 8, false, false>(tuple_value_schema,
1784 tuple_addr_schema, base_address,
1786 case 16:
return compile_data_layout_sequential<true, 16, false, false>(tuple_value_schema,
1787 tuple_addr_schema, base_address,
1789 case 32:
return compile_data_layout_sequential<true, 32, false, false>(tuple_value_schema,
1790 tuple_addr_schema, base_address,
1796template<VariableKind Kind>
1797std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1803 if (options::pointer_sharing) {
1806 case 1:
return compile_data_layout_sequential<true, 1, true, true>(tuple_value_schema, tuple_addr_schema,
1809 case 2:
return compile_data_layout_sequential<true, 2, true, true>(tuple_value_schema, tuple_addr_schema,
1812 case 4:
return compile_data_layout_sequential<true, 4, true, true>(tuple_value_schema, tuple_addr_schema,
1815 case 8:
return compile_data_layout_sequential<true, 8, true, true>(tuple_value_schema, tuple_addr_schema,
1818 case 16:
return compile_data_layout_sequential<true, 16, true, true>(tuple_value_schema, tuple_addr_schema,
1821 case 32:
return compile_data_layout_sequential<true, 32, true, true>(tuple_value_schema, tuple_addr_schema,
1828 case 1:
return compile_data_layout_sequential<true, 1, true, false>(tuple_value_schema,
1829 tuple_addr_schema, base_address,
1831 case 2:
return compile_data_layout_sequential<true, 2, true, false>(tuple_value_schema,
1832 tuple_addr_schema, base_address,
1834 case 4:
return compile_data_layout_sequential<true, 4, true, false>(tuple_value_schema,
1835 tuple_addr_schema, base_address,
1837 case 8:
return compile_data_layout_sequential<true, 8, true, false>(tuple_value_schema,
1838 tuple_addr_schema, base_address,
1840 case 16:
return compile_data_layout_sequential<true, 16, true, false>(tuple_value_schema,
1841 tuple_addr_schema, base_address,
1843 case 32:
return compile_data_layout_sequential<true, 32, true, false>(tuple_value_schema,
1844 tuple_addr_schema, base_address,
1850template<VariableKind Kind>
1851std::tuple<m::wasm::Block, m::wasm::Block, m::wasm::Block>
1856 if (options::pointer_sharing) {
1859 case 1:
return compile_data_layout_sequential<false, 1, true, true>(tuple_value_schema, tuple_addr_schema,
1862 case 2:
return compile_data_layout_sequential<false, 2, true, true>(tuple_value_schema, tuple_addr_schema,
1865 case 4:
return compile_data_layout_sequential<false, 4, true, true>(tuple_value_schema, tuple_addr_schema,
1868 case 8:
return compile_data_layout_sequential<false, 8, true, true>(tuple_value_schema, tuple_addr_schema,
1871 case 16:
return compile_data_layout_sequential<false, 16, true, true>(tuple_value_schema, tuple_addr_schema,
1874 case 32:
return compile_data_layout_sequential<false, 32, true, true>(tuple_value_schema, tuple_addr_schema,
1881 case 1:
return compile_data_layout_sequential<false, 1, true, false>(tuple_value_schema,
1882 tuple_addr_schema, base_address,
1884 case 2:
return compile_data_layout_sequential<false, 2, true, false>(tuple_value_schema,
1885 tuple_addr_schema, base_address,
1887 case 4:
return compile_data_layout_sequential<false, 4, true, false>(tuple_value_schema,
1888 tuple_addr_schema, base_address,
1890 case 8:
return compile_data_layout_sequential<false, 8, true, false>(tuple_value_schema,
1891 tuple_addr_schema, base_address,
1893 case 16:
return compile_data_layout_sequential<false, 16, true, false>(tuple_value_schema,
1894 tuple_addr_schema, base_address,
1896 case 32:
return compile_data_layout_sequential<false, 32, true, false>(tuple_value_schema,
1897 tuple_addr_schema, base_address,
1945template<
bool IsStore>
1953 M_insist(tuple_value_schema.
num_entries() != 0,
"point access must access at least one tuple schema entry");
1954 M_insist(not IsStore or tuple_addr_schema.
num_entries() == 0,
"addresses are only computed for loads");
1956 for (
auto &e : tuple_value_schema)
1958 for (
auto &e : tuple_addr_schema) {
1961 M_insist(not it->nullable(),
"nullable tuple address schema entry not yet supported");
1962 M_insist(not it->type->is_boolean(),
"boolean tuple address schema entry not yet supported");
1963 M_insist(not it->type->is_character_sequence(),
"character sequence tuple address schema entry omitted");
1968 SQL_t values[tuple_value_schema.num_entries()];
1971 if (not tuple_addr_schema.empty())
1972 addrs =
static_cast<SQL_addr_t*
>(alloca(
sizeof(
SQL_addr_t) * tuple_addr_schema.num_entries()));
1975 if constexpr (not IsStore)
1976 null_bits =
static_cast<Boolx1*
>(alloca(
sizeof(Boolx1) * tuple_value_schema.num_entries()));
1981 const bool needs_null_bitmap = [&]() {
1982 for (
auto &tuple_entry : tuple_value_schema) {
1988 bool has_null_bitmap =
false;
1995 auto compute_additional_inode_byte_offset = [&](U32x1 tuple_id) -> U64x1 {
1996 auto rec = [&](U32x1 curr_tuple_id,
decltype(levels.cbegin()) curr,
const decltype(levels.cend()) end,
2000 Wasm_insist(curr_tuple_id == tuple_id % uint32_t(levels.back().num_tuples));
2005 U32x1 child_iter = curr_tuple_id.clone() >> uint32_t(__builtin_ctzl(curr->num_tuples));
2006 U32x1 inner_tuple_id = curr_tuple_id bitand uint32_t(curr->num_tuples - 1U);
2007 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
2008 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
2009 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
2011 U32x1 child_iter = curr_tuple_id.clone() / uint32_t(curr->num_tuples);
2012 U32x1 inner_tuple_id = curr_tuple_id % uint32_t(curr->num_tuples);
2013 M_insist(curr->stride_in_bits % 8 == 0,
"INode stride must be byte aligned");
2014 U64x1 offset_in_bytes = child_iter * uint64_t(curr->stride_in_bits / 8);
2015 return offset_in_bytes + rec(inner_tuple_id, std::next(curr), end, rec);
2018 return rec(tuple_id.clone(), levels.cbegin(), levels.cend(), rec);
2020 M_insist(inode_offset_in_bits % 8 == 0,
"INode offset must be byte aligned");
2022 base_address.clone()
2023 + int32_t(inode_offset_in_bits / 8)
2024 + compute_additional_inode_byte_offset(tuple_id.clone()).make_signed().template to<int32_t>()
2026 std::optional<const Var<U32x1>> inode_iter;
2027 M_insist(levels.back().num_tuples != 0,
"INode must be large enough for at least one tuple");
2028 if (levels.back().num_tuples != 1) {
2030 is_pow_2(levels.back().num_tuples) ? tuple_id bitand uint32_t(levels.back().num_tuples - 1U)
2031 : tuple_id % uint32_t(levels.back().num_tuples)
2039 for (
auto &leaf_info : leaves) {
2040 const uint8_t bit_stride = leaf_info.stride_in_bits % 8;
2043 if (not needs_null_bitmap)
2046 M_insist(not has_null_bitmap,
"at most one bitmap may be specified");
2047 has_null_bitmap =
true;
2049 M_insist(
bool(inode_iter),
"stride requires repetition");
2050 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
2052 (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>()
2054 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
2059 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
2060 auto &tuple_entry = tuple_value_schema[tuple_idx];
2061 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
2062 M_insist(*tuple_entry.type == *layout_entry.type);
2063 if (layout_entry.nullable()) {
2064 U64x1 offset_in_bits = leaf_bit_offset + layout_idx;
2065 U8x1 bit_offset = (offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
2066 I32x1 byte_offset = (offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
2067 if constexpr (IsStore) {
2069 auto store = [&]<
typename T>() {
2073 (ptr + byte_offset).
template to<uint8_t*>();
2074 setbit<U8x1>(byte_ptr,
is_null, uint8_t(1) << bit_offset);
2077 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
2080 case Numeric::N_Int:
2081 case Numeric::N_Decimal:
2084 case 8: store.template operator()<_I8x1 >();
break;
2085 case 16: store.template operator()<_I16x1>();
break;
2086 case 32: store.template operator()<_I32x1>();
break;
2087 case 64: store.template operator()<_I64x1>();
break;
2090 case Numeric::N_Float:
2092 store.template
operator()<_Floatx1>();
2094 store.template operator()<_Doublex1>();
2100 (ptr + byte_offset).
template to<uint8_t*>();
2101 setbit<U8x1>(byte_ptr,
value.is_null(), uint8_t(1) << bit_offset);
2103 [&](
const Date&) { store.template operator()<_I32x1>(); },
2104 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
2106 }, *tuple_entry.type);
2109 U8x1
byte = *(ptr + byte_offset).
template to<uint8_t*>();
2111 new (&null_bits[tuple_idx]) Boolx1(
value);
2116 if constexpr (IsStore) {
2118 auto check = [&]<
typename T>() {
2120 "value of non-nullable entry must not be nullable");
2123 [&](
const Boolean&) { check.template operator()<_Boolx1>(); },
2126 case Numeric::N_Int:
2127 case Numeric::N_Decimal:
2130 case 8: check.template operator()<_I8x1 >();
break;
2131 case 16: check.template operator()<_I16x1>();
break;
2132 case 32: check.template operator()<_I32x1>();
break;
2133 case 64: check.template operator()<_I64x1>();
break;
2136 case Numeric::N_Float:
2138 check.template
operator()<_Floatx1>();
2140 check.template operator()<_Doublex1>();
2144 [&](
const Date&) { check.template operator()<_I32x1>(); },
2145 [&](
const DateTime&) { check.template operator()<_I64x1>(); },
2147 }, *tuple_entry.type);
2154 if (inode_iter
and leaf_info.stride_in_bits) {
2158 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
2159 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
2160 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
2161 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
2170 for (std::size_t tuple_idx = 0; tuple_idx != tuple_value_schema.num_entries(); ++tuple_idx) {
2171 auto &tuple_entry = tuple_value_schema[tuple_idx];
2172 const auto &[layout_idx, layout_entry] =
layout_schema[tuple_entry.id];
2173 M_insist(*tuple_entry.type == *layout_entry.type);
2174 if (layout_entry.nullable()) {
2175 const uint8_t static_bit_offset = (leaf_info.offset_in_bits + layout_idx) % 8;
2176 const int32_t static_byte_offset = (leaf_info.offset_in_bits + layout_idx) / 8;
2177 if constexpr (IsStore) {
2179 auto store = [&]<
typename T>() {
2183 (ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2184 setbit<U8x1>(byte_ptr,
is_null, static_bit_offset);
2187 [&](
const Boolean&) { store.template operator()<_Boolx1>(); },
2190 case Numeric::N_Int:
2191 case Numeric::N_Decimal:
2194 case 8: store.template operator()<_I8x1 >();
break;
2195 case 16: store.template operator()<_I16x1>();
break;
2196 case 32: store.template operator()<_I32x1>();
break;
2197 case 64: store.template operator()<_I64x1>();
break;
2200 case Numeric::N_Float:
2202 store.template
operator()<_Floatx1>();
2204 store.template operator()<_Doublex1>();
2210 (ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2211 setbit<U8x1>(byte_ptr,
value.is_null(), static_bit_offset);
2213 [&](
const Date&) { store.template operator()<_I32x1>(); },
2214 [&](
const DateTime&) { store.template operator()<_I64x1>(); },
2216 }, *tuple_entry.type);
2219 U8x1
byte = *(ptr.clone() + static_byte_offset).
template to<uint8_t*>();
2220 const uint8_t static_mask = 1U << static_bit_offset;
2222 new (&null_bits[tuple_idx]) Boolx1(
value);
2227 if constexpr (IsStore) {
2229 auto check = [&]<
typename T>() {
2231 "value of non-nullable entry must not be nullable");
2234 [&](
const Boolean&) { check.template operator()<_Boolx1>(); },
2237 case Numeric::N_Int:
2238 case Numeric::N_Decimal:
2241 case 8: check.template operator()<_I8x1 >();
break;
2242 case 16: check.template operator()<_I16x1>();
break;
2243 case 32: check.template operator()<_I32x1>();
break;
2244 case 64: check.template operator()<_I64x1>();
break;
2247 case Numeric::N_Float:
2249 check.template
operator()<_Floatx1>();
2251 check.template operator()<_Doublex1>();
2255 [&](
const Date&) { check.template operator()<_I32x1>(); },
2256 [&](
const DateTime&) { check.template operator()<_I64x1>(); },
2258 }, *tuple_entry.type);
2267 M_insist(*layout_entry.type == *leaf_info.leaf.type());
2268 auto tuple_value_it = tuple_value_schema.find(layout_entry.id);
2269 auto tuple_addr_it = tuple_addr_schema.find(layout_entry.id);
2270 if (tuple_value_it == tuple_value_schema.end()
and tuple_addr_it == tuple_addr_schema.end())
2272 auto tuple_it = tuple_value_it != tuple_value_schema.end() ? tuple_value_it : tuple_addr_it;
2273 M_insist(*tuple_it->type == *layout_entry.type);
2274 const auto tuple_value_idx = std::distance(tuple_value_schema.begin(), tuple_value_it);
2275 const auto tuple_addr_idx = std::distance(tuple_addr_schema.begin(), tuple_addr_it);
2278 M_insist(tuple_it->type->is_boolean(),
"leaf bit stride currently only for `Boolean` supported");
2280 M_insist(
bool(inode_iter),
"stride requires repetition");
2281 U64x1 leaf_offset_in_bits = leaf_info.offset_in_bits + *inode_iter * leaf_info.stride_in_bits;
2282 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>() ;
2283 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
2285 Ptr<U8x1> byte_ptr = (inode_ptr + leaf_byte_offset).
template to<uint8_t*>();
2286 U8x1
mask = uint8_t(1) << leaf_bit_offset;
2288 if constexpr (IsStore) {
2290 auto [
value,
is_null] = env.get<_Boolx1>(tuple_it->id).split();
2296 if (tuple_value_it != tuple_value_schema.end()) {
2298 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
2306 if (inode_iter
and leaf_info.stride_in_bits) {
2310 U64x1 leaf_offset_in_bits = *inode_iter * leaf_info.stride_in_bits;
2311 U8x1 leaf_bit_offset = (leaf_offset_in_bits.clone() bitand uint64_t(7)).to<uint8_t>();
2312 I32x1 leaf_byte_offset = (leaf_offset_in_bits >> uint64_t(3)).
make_signed().to<int32_t>();
2313 Wasm_insist(leaf_bit_offset == 0
U,
"no leaf bit offset without bit stride");
2314 return inode_ptr + leaf_byte_offset;
2320 const uint8_t static_bit_offset = leaf_info.offset_in_bits % 8;
2321 const int32_t static_byte_offset = leaf_info.offset_in_bits / 8;
2324 auto store = [&]<
typename T>() {
2325 using type =
typename T::type;
2327 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
2330 *(ptr + static_byte_offset).
template to<type*>() =
value;
2333 auto load = [&]<
typename T>() {
2334 using type =
typename T::type;
2336 "leaf offset of `Numeric`, `Date`, or `DateTime` must be byte aligned");
2337 if (tuple_value_it != tuple_value_schema.end()) {
2341 if (tuple_addr_it != tuple_addr_schema.end())
2343 (ptr.clone() + static_byte_offset).template to<type*>()
2348#define CALL(TYPE) if constexpr (IsStore) store.template operator()<TYPE>(); else load.template operator()<TYPE>()
2351 Ptr<U8x1> byte_ptr = (ptr + static_byte_offset).
template to<uint8_t*>();
2352 if constexpr (IsStore) {
2354 auto [
value,
is_null] = env.get<_Boolx1>(tuple_it->id).split();
2356 setbit<U8x1>(byte_ptr,
value, static_bit_offset);
2360 const uint8_t static_mask = 1U << static_bit_offset;
2362 if (tuple_value_it != tuple_value_schema.end()) {
2364 new (&values[tuple_value_idx])
SQL_t(_Boolx1(
value));
2372 case Numeric::N_Int:
2373 case Numeric::N_Decimal:
2376 case 8:
CALL(_I8x1 );
break;
2377 case 16:
CALL(_I16x1);
break;
2378 case 32:
CALL(_I32x1);
break;
2379 case 64:
CALL(_I64x1);
break;
2382 case Numeric::N_Float:
2390 M_insist(static_bit_offset == 0,
"leaf offset of `CharacterSequence` must be byte aligned");
2391 Ptr<Charx1> addr = (ptr + static_byte_offset).
template to<char*>();
2392 if constexpr (IsStore) {
2395 IF (
value.clone().not_null()) {
2400 new (&values[tuple_value_idx])
SQL_t(
2401 NChar(addr, layout_entry.nullable(), cs.length, cs.is_varying)
2406 [&](
const Date&) {
CALL(_I32x1); },
2409 }, *tuple_it->type);
2416 if constexpr (not IsStore) {
2418 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
2419 auto &tuple_entry = tuple_value_schema[idx];
2423 Expr<T> combined(
value.insist_not_null(), null_bits[idx]);
2424 env.add(tuple_entry.id, combined);
2426 env.add(tuple_entry.id,
value);
2433 env.add(tuple_entry.id,
NChar(combined,
true,
value.length(),
2434 value.guarantees_terminating_nul()));
2437 env.add(tuple_entry.id,
NChar(_value,
false,
value.length(),
2438 value.guarantees_terminating_nul()));
2441 [](
auto) {
M_unreachable(
"SIMDfication currently not supported"); },
2442 [](std::monostate) {
M_unreachable(
"value must be loaded beforehand"); },
2447 for (std::size_t idx = 0; idx != tuple_addr_schema.num_entries(); ++idx) {
2448 auto &tuple_entry = tuple_addr_schema[idx];
2449 env.add_addr(tuple_entry.id, std::move(addrs[idx]));
2454 for (std::size_t idx = 0; idx < tuple_value_schema.num_entries(); ++idx)
2455 values[idx].~
SQL_t();
2456 for (std::size_t idx = 0; idx < tuple_addr_schema.num_entries(); ++idx)
2458 if constexpr (not IsStore) {
2460 for (std::size_t idx = 0; idx != tuple_value_schema.num_entries(); ++idx) {
2461 if (has_null_bitmap
and layout_schema[tuple_value_schema[idx].
id].second.nullable())
2462 null_bits[idx].~Boolx1();
2465 base_address.discard();
2476 return compile_data_layout_point_access<true>(tuple_value_schema, tuple_addr_schema, base_address, layout,
2484 return compile_data_layout_point_access<false>(tuple_value_schema, tuple_addr_schema, base_address, layout,
2493template<
bool IsGlobal>
2496 : schema_(
std::cref(schema))
2497 , layout_(factory.make(schema, num_tuples))
2498 , load_simdfied_(load_simdfied)
2500 , pipeline_(
std::
move(pipeline))
2501 , teardown_(
std::
move(teardown))
2505 if constexpr (IsGlobal) {
2509 const uint32_t num_children =
2519template<
bool IsGlobal>
2522 if constexpr (IsGlobal) {
2523 if (not layout_.is_finite()) {
2525 M_insist(
bool(storage_.capacity_));
2526 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2527 auto buffer_size_in_bytes =
2528 (*storage_.capacity_ / uint32_t(layout_.child().num_tuples())) * child_size_in_bytes;
2534template<
bool IsGlobal>
2536 param_t _tuple_addr_schema)
const
2539 if (_tuple_value_schema) {
2540 for (
auto &e : _tuple_value_schema->get())
2541 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2543 if (_tuple_addr_schema) {
2544 for (
auto &e : _tuple_addr_schema->get())
2545 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple address schema entry not found");
2549 static Schema empty_schema;
2550 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2551 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2556template<
bool IsGlobal>
2562 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple schema entry not found");
2569template<
bool IsGlobal>
2575 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple schema entry not found");
2582template<
bool IsGlobal>
2585 M_insist(not base_address_,
"must not call `setup()` twice");
2586 M_insist(not size_,
"must not call `setup()` twice");
2587 M_insist(not capacity_,
"must not call `setup()` twice");
2588 M_insist(not first_iteration_,
"must not call `setup()` twice");
2591 base_address_.emplace();
2593 if (not layout_.is_finite()) {
2594 capacity_.emplace();
2595 first_iteration_.emplace(
true);
2599 if constexpr (IsGlobal) {
2601 *size_ = storage_.size_;
2602 if (not layout_.is_finite()) {
2603 M_insist(
bool(storage_.capacity_));
2604 *capacity_ = *storage_.capacity_;
2608 if (layout_.is_finite()) {
2609 if constexpr (IsGlobal) {
2610 *base_address_ = storage_.base_address_;
2613 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2614 const uint32_t num_children =
2615 (layout_.num_tuples() + layout_.child().num_tuples() - 1) / layout_.child().num_tuples();
2616 *base_address_ =
Module::Allocator().pre_allocate(num_children * child_size_in_bytes, 8);
2619 if constexpr (IsGlobal) {
2620 IF (*capacity_ == 0
U) {
2622 *capacity_ = uint32_t(layout_.child().num_tuples());
2625 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2628 *base_address_ = storage_.base_address_;
2632 *capacity_ = uint32_t(layout_.child().num_tuples());
2635 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2641template<
bool IsGlobal>
2644 M_insist(
bool(base_address_),
"must call `setup()` before");
2645 M_insist(
bool(size_),
"must call `setup()` before");
2646 M_insist(not layout_.is_finite() ==
bool(capacity_),
"must call `setup()` before");
2647 M_insist(not layout_.is_finite() ==
bool(first_iteration_),
"must call `setup()` before");
2649 if constexpr (not IsGlobal) {
2650 if (not layout_.is_finite()) {
2652 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2653 auto buffer_size_in_bytes = (*capacity_ / uint32_t(layout_.child().num_tuples())) * child_size_in_bytes;
2659 if constexpr (IsGlobal) {
2660 storage_.base_address_ = *base_address_;
2661 storage_.size_ = *size_;
2662 if (not layout_.is_finite()) {
2663 M_insist(
bool(storage_.capacity_));
2664 *storage_.capacity_ = *capacity_;
2669 base_address_.reset();
2671 if (not layout_.is_finite()) {
2673 first_iteration_->val().discard();
2674 first_iteration_.reset();
2678template<
bool IsGlobal>
2684 static Schema empty_schema;
2685 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2686 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2689 for (
auto &e : tuple_value_schema)
2690 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2691 for (
auto &e : tuple_addr_schema)
2692 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2696 if (not resume_pipeline_) {
2698 FUNCTION(resume_pipeline,
void(
void*, uint32_t))
2707 const auto num_simd_lanes_preferred =
2710 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2712 tuple_addr_schema.empty() ? 0UL : 4UL })
2721 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2723 WHILE (load_tuple_id < size) {
2727 base_address.discard();
2730 auto [load_inits, loads, load_jumps] =
2735 load_inits.attach_to_current();
2736 WHILE (load_tuple_id < size) {
2737 loads.attach_to_current();
2739 load_jumps.attach_to_current();
2746 resume_pipeline_ = std::move(resume_pipeline);
2751 (*resume_pipeline_)(base_address(), size());
2754template<
bool IsGlobal>
2757 execute_pipeline_inline(setup_, pipeline_, teardown_, std::move(tuple_value_schema), std::move(tuple_addr_schema));
2760template<
bool IsGlobal>
2767 static Schema empty_schema;
2768 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2769 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2772 for (
auto &e : tuple_value_schema)
2773 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2774 for (
auto &e : tuple_addr_schema)
2775 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2779 FUNCTION(resume_pipeline,
void(
void*, uint32_t))
2788 const auto num_simd_lanes_preferred =
2791 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2793 tuple_addr_schema.empty() ? 0UL : 4UL })
2802 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2804 WHILE (load_tuple_id < size) {
2808 base_address.discard();
2811 auto [load_inits, loads, load_jumps] =
2813 schema_, load_tuple_id);
2816 load_inits.attach_to_current();
2817 WHILE (load_tuple_id < size) {
2818 loads.attach_to_current();
2820 load_jumps.attach_to_current();
2829 resume_pipeline(base_address(), size());
2832template<
bool IsGlobal>
2839 static Schema empty_schema;
2840 const auto &tuple_value_schema = _tuple_value_schema ? _tuple_value_schema->get() : schema_.get();
2841 const auto &tuple_addr_schema = _tuple_addr_schema ? _tuple_addr_schema->get() : empty_schema;
2844 for (
auto &e : tuple_value_schema)
2845 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple value schema entry not found");
2846 for (
auto &e : tuple_addr_schema)
2847 M_insist(schema_.get().find(e.id) != schema_.get().cend(),
"tuple addr schema entry not found");
2853 base_address_ ? base_address_->val() :
Var<
Ptr<void>>(storage_.base_address_.val()).val(),
2854 ({ M_insist(bool(base_address_)); base_address_->val(); }));
2857 size_ ? size_->val() :
Var<U32x1>(storage_.size_.val()).val(),
2858 ({ M_insist(bool(size_)); size_->val(); }));
2861 std::optional<Var<Boolx1>> pred;
2864 pred = env.extract_predicate<_Boolx1>().is_true_and_not_null();
2866 U32x1 num_tuples = pred ?
Select(*pred, size, 0
U) : size;
2869 const auto num_simd_lanes_preferred =
2872 load_simdfied_ ? std::max<std::size_t>({ num_simd_lanes_preferred,
2874 tuple_addr_schema.empty() ? 0UL : 4UL })
2883 if (tuple_value_schema.num_entries() == 0
and tuple_addr_schema.num_entries() == 0) {
2885 WHILE (load_tuple_id < num_tuples) {
2889 base_address.discard();
2892 auto [load_inits, loads, load_jumps] =
2897 load_inits.attach_to_current();
2898 WHILE (load_tuple_id < num_tuples) {
2899 loads.attach_to_current();
2901 load_jumps.attach_to_current();
2909template<
bool IsGlobal>
2912 M_insist(
bool(base_address_),
"must call `setup()` before");
2913 M_insist(
bool(size_),
"must call `setup()` before");
2914 M_insist(not layout_.is_finite() ==
bool(capacity_),
"must call `setup()` before");
2915 M_insist(not layout_.is_finite() ==
bool(first_iteration_),
"must call `setup()` before");
2921 static Schema empty_schema;
2922 auto [_store_inits, stores, _store_jumps] =
2925 Block store_inits(std::move(_store_inits)), store_jumps(std::move(_store_jumps));
2927 if (layout_.is_finite()) {
2930 store_inits.attach_to_current();
2933 IF (*size_ == *capacity_) {
2935 const uint32_t child_size_in_bytes = (layout_.stride_in_bits() + 7) / 8;
2936 auto buffer_size_in_bytes = (*capacity_ / uint32_t(layout_.child().num_tuples())) * child_size_in_bytes;
2938 Wasm_insist(ptr == *base_address_ + buffer_size_in_bytes.make_signed(),
2939 "buffer could not be resized sequentially in memory");
2943 IF (*first_iteration_) {
2945 store_inits.attach_to_current();
2947 *first_iteration_ =
false;
2952 stores.attach_to_current();
2954 if (layout_.is_finite()) {
2957 *size_ = uint32_t(layout_.num_tuples());
2979template<
bool IsGlobal>
2983 auto load = buffer_.get().create_load_proxy(schema_.get());
2986 auto env_first = [&](){
2988 load(first.clone());
2992 operator()(first, second, env_first);
2995template<
bool IsGlobal>
2999 auto load = buffer_.get().create_load_proxy(schema_.get());
3000 auto store = buffer_.get().create_store_proxy(schema_.get());
3004 for (
auto &e : schema_.get()) {
3008 IF (
value.clone().is_null()) {
3017 if (
value.can_be_null()) {
3019 _env_first.
add(e.id, var);
3025 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3026 [](std::monostate) ->
void {
M_unreachable(
"value must be loaded beforehand"); },
3027 }, env_first.
get(e.id));
3033 load(second.clone());
3044template<
bool IsGlobal>
3049 auto store = buffer_.get().create_store_proxy(schema_.get());
3053 for (
auto &e : schema_.get()) {
3057 IF (
value.clone().is_null()) {
3066 if (
value.can_be_null()) {
3068 _env_first.
add(e.id, var);
3074 [](
auto) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3075 [](std::monostate) ->
void {
M_unreachable(
"value must be loaded beforehand"); }
3076 }, env_first.
get(e.id));
3104 static thread_local struct {} _;
3108 using fn_t = int32_t(uint32_t, uint32_t,
char*,
char*, uint32_t);
3109 std::optional<FunctionProxy<fn_t>> strncmp_terminating_nul;
3110 std::optional<FunctionProxy<fn_t>> strncmp_no_terminating_nul;
3116 auto strncmp_non_null = [&d, &_left, &_right, &reverse](
Ptr<Charx1> left,
Ptr<Charx1> right, U32x1 len) -> I32x1 {
3117 Wasm_insist(left.clone().not_null(),
"left operand must not be NULL");
3118 Wasm_insist(right.clone().not_null(),
"right operand must not be NULL");
3119 Wasm_insist(len.clone() != 0
U,
"length to compare must not be 0");
3124 auto left_gt_right = *left.clone() > *right.clone();
3125 return left_gt_right.to<int32_t>() - (*left < *right).to<int32_t>();
3128 if (not d.strncmp_terminating_nul) {
3130 FUNCTION(strncmp_terminating_nul, data_t::fn_t)
3142 I32x1 len_left =
Select(len < len_ty_left, len, len_ty_left) .make_signed();
3143 I32x1 len_right =
Select(len < len_ty_right, len, len_ty_right).make_signed();
3149 result = (left != end_left).to<int32_t>() - (right != end_right).to<int32_t>();
3150 BREAK(result != 0 or left == end_left);
3154 result = (*left > *right).to<int32_t>() - (*left < *right).to<int32_t>();
3166 d.strncmp_terminating_nul = std::move(strncmp_terminating_nul);
3170 M_insist(
bool(d.strncmp_terminating_nul));
3171 return (*d.strncmp_terminating_nul)(_left.
length(), _right.
length(), left, right, len);
3173 if (not d.strncmp_no_terminating_nul) {
3175 FUNCTION(strncmp_no_terminating_nul, data_t::fn_t)
3187 I32x1 len_left =
Select(len < len_ty_left, len, len_ty_left) .make_signed();
3188 I32x1 len_right =
Select(len < len_ty_right, len, len_ty_right).make_signed();
3193 end_left = left + len_left;
3194 end_right = right + len_right;
3198 WHILE(*end_left != 0
and end_left != left + len_left) {
3202 WHILE(*end_right != 0
and end_right != right + len_right) {
3207 swap(left, end_left);
3208 swap(right, end_right);
3221 IF (left != end_left) {
3226 IF (right != end_right) {
3233 result = (val_left > val_right).to<int32_t>() - (val_left < val_right).to<int32_t>();
3235 BREAK(val_left == 0);
3238 left += reverse ? -1 : 1;
3239 right += reverse ? -1 : 1;
3245 d.strncmp_no_terminating_nul = std::move(strncmp_no_terminating_nul);
3249 M_insist(
bool(d.strncmp_no_terminating_nul));
3250 return (*d.strncmp_no_terminating_nul)(_left.
length(), _right.
length(), left, right, len);
3258 IF (left.is_null() or right.is_null()) {
3259 result = _I32x1::Null();
3261 result = strncmp_non_null(left, right, len);
3265 const Var<I32x1> result(strncmp_non_null(left, right, len));
3266 return _I32x1(result);
3274 U32x1 len(std::min<uint32_t>(left.
length(), right.
length()) + 1U);
3275 return strncmp(left, right, len, reverse);
3280 _I32x1
res =
strncmp(left, right, len, reverse);
3283 case EQ:
return res == 0;
3284 case NE:
return res != 0;
3285 case LT:
return res < 0;
3286 case LE:
return res <= 0;
3287 case GT:
return res > 0;
3288 case GE:
return res >= 0;
3294 _I32x1
res =
strcmp(left, right, reverse);
3297 case EQ:
return res == 0;
3298 case NE:
return res != 0;
3299 case LT:
return res < 0;
3300 case LE:
return res <= 0;
3301 case GT:
return res > 0;
3302 case GE:
return res >= 0;
3313 static thread_local struct {} _;
3323 if (not d.strncpy) {
3333 Wasm_insist(not src.is_nullptr(),
"source must not be nullptr");
3334 Wasm_insist(not dst.is_nullptr(),
"destination must not be nullptr");
3337 WHILE (src != src_end) {
3339 BREAK(*src ==
'\0');
3346 d.strncpy = std::move(
strncpy);
3362 static thread_local struct {} _;
3372 M_insist(
'_' != escape_char
and '%' != escape_char,
"illegal escape character");
3377 return _Boolx1(
true);
3380 auto like_non_null = [&d, &_str, &_pattern, &escape_char](
Ptr<Charx1> str,
Ptr<Charx1> pattern) -> Boolx1 {
3381 Wasm_insist(str.clone().not_null(),
"string operand must not be NULL");
3382 Wasm_insist(pattern.clone().not_null(),
"pattern operand must not be NULL");
3386 FUNCTION(
like,
bool(int32_t, int32_t,
char*,
char*,
char))
3391 const auto len_ty_pattern =
PARAMETER(1);
3399 I32x1 num_entries = (len_ty_str + 1) * (len_ty_pattern + 1);
3404 WHILE (entry < dp + num_entries.clone()) {
3427 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3429 entry += len_ty_str + 1;
3441 entry = dp + len_ty_str + 2;
3444 pattern = val_pattern;
3447 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3450 WHILE (byte_pattern !=
'\0') {
3454 IF (is_not_escaped
and byte_pattern == escape_char) {
3456 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3460 IF (byte_pattern !=
'_' and byte_pattern !=
'%' and byte_pattern != escape_char) {
3461 Throw(exception::invalid_escape_sequence);
3464 is_not_escaped =
false;
3471 byte_str =
Select(str < end_str, *str,
'\0');
3474 WHILE (byte_str !=
'\0') {
3478 IF (is_not_escaped
and byte_pattern ==
'%') {
3480 *entry = *(entry - (len_ty_str + 1)) or *(entry - 1);
3482 IF ((is_not_escaped
and byte_pattern ==
'_') or byte_pattern == byte_str) {
3484 *entry = *(entry - (len_ty_str + 2));
3492 byte_str =
Select(str < end_str, *str,
'\0');
3498 entry += len_ty_str + 1 - len_str;
3501 byte_pattern =
Select(pattern < end_pattern, *pattern,
'\0');
3502 is_not_escaped =
true;
3509 const Var<Boolx1> result(*(entry + len_str - (len_ty_str + 2)));
3517 d.like = std::move(
like);
3522 return (*d.like)(_str.
length(), _pattern.
length(), str, pattern, escape_char);
3526 auto [_val_str, is_null_str] = _str.split();
3527 auto [_val_pattern, is_null_pattern] = _pattern.split();
3528 Ptr<Charx1> val_str(_val_str), val_pattern(_val_pattern);
3531 IF (is_null_str or is_null_pattern) {
3532 result = _Boolx1::Null();
3534 result = like_non_null(val_str, val_pattern);
3538 const Var<Boolx1> result(like_non_null(_str, _pattern));
3539 return _Boolx1(result);
3545 static thread_local struct {} _;
3556 M_insist(std::regex_match(*_pattern, std::regex(
"%[^_%\\\\]+%")),
"invalid contains pattern");
3558 if (_str.
length() == 0) {
3560 return _Boolx1(
false);
3563 auto contains_non_null = [&d, &_str, &_pattern](
Ptr<Charx1> str) -> Boolx1 {
3564 Wasm_insist(str.clone().not_null(),
"string operand must not be NULL");
3566 auto it = d.contains_map.find(_pattern);
3567 if (it == d.contains_map.end()) {
3577 const int32_t len_pattern = strlen(*_pattern) - 2;
3579 for (std::size_t i = 0; i < len_pattern; ++i)
3580 pattern[i] = (*_pattern)[i + 1];
3584 int32_t len_prefix = -1;
3586 tbl[0] = len_prefix;
3587 for (std::size_t i = 1; i < len_pattern + 1; ++i) {
3588 while (len_prefix >= 0
and pattern[len_prefix] != pattern[i - 1])
3589 len_prefix = tbl[len_prefix];
3591 tbl[i] = len_prefix;
3597 WHILE (val_str < end_str
and *val_str !=
'\0') {
3600 pos_pattern = *(
Ptr<I32x1>(tbl) + pos_pattern);
3604 IF (pos_pattern == len_pattern) {
3610 it = d.contains_map.emplace_hint(it, _pattern, std::move(
contains));
3614 M_insist(it != d.contains_map.end());
3615 return (it->second)(_str.
length(), str);
3619 auto [_val_str, is_null_str] = _str.split();
3624 result = _Boolx1::Null();
3626 result = contains_non_null(val_str);
3630 const Var<Boolx1> result(contains_non_null(_str));
3631 return _Boolx1(result);
3637 M_insist(std::regex_match(*pattern, std::regex(
"[^_%\\\\]+%")),
"invalid prefix pattern");
3640 const int32_t len_pattern = strlen(*pattern) - 1;
3642 for (std::size_t i = 0; i < len_pattern; ++i)
3643 _lower_bound[i] = (*pattern)[i];
3644 _lower_bound[len_pattern] =
'\0';
3649 for (std::size_t i = 0; i < len_pattern - 1; ++i)
3650 _upper_bound[i] = (*pattern)[i];
3651 const char last_char = (*pattern)[len_pattern - 1];
3652 _upper_bound[len_pattern - 1] = last_char + 1;
3653 _upper_bound[len_pattern] =
'\0';
3657 auto str_cpy = str.
clone();
3663 M_insist(std::regex_match(*pattern, std::regex(
"%[^_%\\\\]+")),
"invalid suffix pattern");
3666 const int32_t len_pattern = strlen(*pattern) - 1;
3668 for (std::size_t i = 0; i < len_pattern; ++i)
3669 _lower_bound[i] = (*pattern)[i + 1];
3670 _lower_bound[len_pattern] =
'\0';
3675 const char first_char = (*pattern)[1];
3676 _upper_bound[0] = first_char + 1;
3677 for (std::size_t i = 1; i < len_pattern; ++i)
3678 _upper_bound[i] = (*pattern)[i + 1];
3679 _upper_bound[len_pattern] =
'\0';
3683 const auto max_length = std::max<uint32_t>(str.
length(), len_pattern);
3684 auto str_cpy = str.
clone();
3685 return strncmp(str_cpy, lower_bound, U32x1(max_length),
GE,
true)
and
3686 strncmp(str, upper_bound, U32x1(max_length),
LT,
true);
3694template<
bool Predicated>
3696 const std::vector<SortingOperator::order_type> &order)
3702 for (
auto &o : order) {
3704 SQL_t _val_left = env_left.template compile(o.first);
3707 [&]<
typename T>(
Expr<T> val_left) ->
void {
3709 Expr<T> val_right = env_right.template compile<Expr<T>>(o.first);
3711 M_insist(val_left.can_be_null() == val_right.can_be_null(),
3712 "either both or none of the value to compare must be nullable");
3713 if (val_left.can_be_null()) {
3714 using type = std::conditional_t<std::is_same_v<T, bool>, _I32x1,
Expr<T>>;
3716 if constexpr (std::is_same_v<T, bool>) {
3717 left = val_left.template to<int32_t>();
3718 right = val_right.template to<int32_t>();
3725 I32x1 cmp_null = right.is_null().template to<int32_t>() - left.is_null().template to<int32_t>();
3726 _I32x1 _val_lt = (left < right).
template to<int32_t>();
3727 _I32x1 _val_gt = (left > right).
template to<int32_t>();
3728 _I32x1 _cmp_val = o.second ? _val_gt - _val_lt : _val_lt - _val_gt;
3729 auto [cmp_val, cmp_is_null] = _cmp_val.split();
3730 cmp_is_null.discard();
3731 I32x1 cmp = (cmp_null << 1) + cmp_val;
3735 using type = std::conditional_t<std::is_same_v<T, bool>, I32x1,
PrimitiveExpr<T>>;
3737 if constexpr (std::is_same_v<T, bool>) {
3738 left = val_left.insist_not_null().template to<int32_t>();
3739 right = val_right.insist_not_null().template to<int32_t>();
3741 left = val_left.insist_not_null();
3742 right = val_right.insist_not_null();
3746 I32x1 val_lt = (left < right).
template to<int32_t>();
3747 I32x1 val_gt = (left > right).
template to<int32_t>();
3748 I32x1 cmp = o.second ? val_gt - val_lt : val_lt - val_gt;
3753 [&](
NChar val_left) ->
void {
3754 auto &cs = as<const CharacterSequence>(*o.first.get().type());
3757 NChar val_right = env_right.template compile<NChar>(o.first);
3760 NChar left(_left, val_left.can_be_null(), val_left.length(), val_left.guarantees_terminating_nul()),
3764 "either both or none of the value to compare must be nullable");
3765 if (val_left.can_be_null()) {
3767 I32x1 cmp_null = _right.is_null().to<int32_t>() - _left.is_null().to<int32_t>();
3768 _I32x1 _delta = o.second ?
strcmp(left, right) :
strcmp(right, left);
3769 auto [delta_val, delta_is_null] = _delta.split();
3771 "result of strcmp is assumed to be in [-1,1]");
3772 delta_is_null.discard();
3773 I32x1 cmp = (cmp_null << 1) + delta_val;
3778 I32x1 delta = o.second ?
strcmp(left, right).insist_not_null()
3779 :
strcmp(right, left).insist_not_null();
3781 "result of strcmp is assumed to be in [-1,1]");
3786 [](
auto&&) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3787 [](std::monostate) ->
void {
M_unreachable(
"invalid expression"); }
3797 auto emit_comparison_rec = [&](
decltype(order.cbegin()) curr,
const decltype(order.cend()) end,
3807 SQL_t _val_left = env_left.template compile(curr->first);
3810 [&]<
typename T>(
Expr<T> val_left) ->
void {
3812 Expr<T> val_right = env_right.template compile<Expr<T>>(curr->first);
3814 M_insist(val_left.can_be_null() == val_right.can_be_null(),
3815 "either both or none of the value to compare must be nullable");
3816 if (val_left.can_be_null()) {
3817 using type = std::conditional_t<std::is_same_v<T, bool>, _I32x1,
Expr<T>>;
3819 if constexpr (std::is_same_v<T, bool>) {
3820 _left = val_left.template to<int32_t>();
3821 _right = val_right.template to<int32_t>();
3828 IF (_left.not_null()) {
3829 IF (_right.is_null()) {
3833 auto left = _left.val().insist_not_null(),
3834 right = _right.val().insist_not_null();
3835 Boolx1 left_lt_right = curr->second ? left.clone() < right.clone()
3836 : left.clone() > right.clone();
3837 IF (left_lt_right) {
3841 Boolx1 left_gt_right = curr->second ? left > right : left < right;
3842 IF (left_gt_right) {
3847 IF (_right.not_null()) {
3853 using type = std::conditional_t<std::is_same_v<T, bool>, I32x1,
PrimitiveExpr<T>>;
3855 if constexpr (std::is_same_v<T, bool>) {
3856 left = val_left.insist_not_null().template to<int32_t>();
3857 right = val_right.insist_not_null().template to<int32_t>();
3859 left = val_left.insist_not_null();
3860 right = val_right.insist_not_null();
3864 Boolx1 left_lt_right = curr->second ? left < right : left > right;
3865 IF (left_lt_right) {
3869 Boolx1 left_gt_right = curr->second ? left > right : left < right;
3870 IF (left_gt_right) {
3876 [&](
NChar val_left) ->
void {
3877 auto &cs = as<const CharacterSequence>(*curr->first.get().type());
3880 NChar val_right = env_right.template compile<NChar>(curr->first);
3884 NChar left(_left,
false, val_left.length(), val_left.guarantees_terminating_nul()),
3888 "either both or none of the value to compare must be nullable");
3889 if (val_left.can_be_null()) {
3891 IF (_left.not_null()) {
3892 IF (_right.is_null()) {
3896 I32x1 cmp = curr->second ?
strcmp(left, right).insist_not_null()
3897 :
strcmp(right, left).insist_not_null();
3898 IF (cmp.clone() != 0) {
3903 IF (_right.not_null()) {
3910 I32x1 cmp = curr->second ?
strcmp(left, right).insist_not_null()
3911 :
strcmp(right, left).insist_not_null();
3912 IF (cmp.clone() != 0) {
3918 [](
auto&&) ->
void {
M_unreachable(
"SIMDfication currently not supported"); },
3919 [](std::monostate) ->
void {
M_unreachable(
"invalid expression"); }
3923 rec(std::next(curr), end, rec);
3925 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
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, U32x1 tuple_id)
Compiles the data layout layout starting at memory address base_address and containing tuples of sche...
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.
typename detail::var_helper< T >::type Var
Local variable.
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< uint32_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially s...
std::variant< std::monostate #define ADD_TYPE(TYPE) SQL_ADDR_TYPES(ADD_TYPE) > SQL_addr_t
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< uint32_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially s...
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.
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, U32x1 tuple_id)
Compiles the data layout layout starting at memory address base_address and containing tuples of sche...
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_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, U32x1 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)
auto Select(C &&_cond, T &&_tru, U &&_fals)
_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...
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< uint32_t, Kind, false > &tuple_id)
Compiles the data layout layout containing tuples of schema layout_schema such that it sequentially l...
void discard()
Discards this.
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.
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()(U32x1 first, U32x1 second)
Swaps tuples with IDs first and second.
Helper type to deduce the Expr<U> type given a.