4#include "backend/WebAssembly.hpp"
8#include <experimental/type_traits>
24#include <wasm-binary.h>
25#include <wasm-builder.h>
26#include <wasm-interpreter.h>
27#include <wasm-validator.h>
33#if !defined(NDEBUG) && defined(M_ENABLE_SANITY_FIELDS)
34namespace dsl_options {
38static bool insist_no_ternary_logic =
false;
42#define M_insist_no_ternary_logic() M_insist(not m::dsl_options::insist_no_ternary_logic, "ternary logic must not occur")
45#define M_insist_no_ternary_logic()
86template<
typename, std::
size_t = 1>
struct Expr;
87template<
typename, VariableKind,
bool, std::
size_t = 1>
struct Variable;
88template<
typename, std::
size_t = 1>
struct Parameter;
94template<dsl_primitive, std::
size_t,
bool>
struct the_reference;
98template<
typename T, std::
size_t L = 1>
100template<
typename T, std::
size_t L = 1>
119template<dsl_primitive T>
124template<dsl_po
inter_to_primitive T>
129template<
typename T, std::
size_t L>
134template<
typename T, VariableKind Kind, std::
size_t L>
139template<
typename T, std::
size_t L>
144template<
typename T, std::
size_t L,
bool IsConst>
169template<dsl_primitive T>
174template<dsl_po
inter_to_primitive T>
179template<
typename T, std::
size_t L>
184template<
typename T, std::
size_t L>
189template<
typename T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
194template<
typename T, std::
size_t L>
199template<
typename T, std::
size_t L,
bool IsConst>
200struct expr<detail::the_reference<T, L, IsConst>>
216template<
typename, std::
size_t>
221requires (std::is_void_v<T>)
224 ::wasm::Type
operator()()
const { return ::wasm::Type(::wasm::Type::none); }
228template<std::
integral T>
229struct wasm_type_helper<
T, 1>
233 if constexpr (
sizeof(
T) <= 4)
234 return ::wasm::Type(::wasm::Type::i32);
235 if constexpr (
sizeof(
T) == 8)
236 return ::wasm::Type(::wasm::Type::i64);
242template<std::
floating_po
int T>
243struct wasm_type_helper<
T, 1>
247 if constexpr (
sizeof(
T) <= 4)
248 return ::wasm::Type(::wasm::Type::f32);
249 if constexpr (
sizeof(
T) == 8)
250 return ::wasm::Type(::wasm::Type::f64);
256template<dsl_po
inter_to_primitive T, std::
size_t L>
259 ::wasm::Type
operator()() { return ::wasm::Type(::wasm::Type::i32); };
263template<dsl_primitive T, std::
size_t L>
265struct wasm_type_helper<T, L>
267 ::wasm::Type
operator()()
const { return ::wasm::Type(::wasm::Type::v128); }
272template<
typename T, std::
size_t L>
279template<
typename ReturnType,
typename... ParamTypes>
284 return ::wasm::Signature(
292template<
typename T, std::
size_t L>
297template<std::
size_t W>
316template<std::
size_t W>
320template<std::
size_t W>
339template<std::
size_t W>
352#ifdef M_ENABLE_SANITY_FIELDS
353#define WASM_INSIST2_(COND, MSG) ({ \
354 auto old = std::exchange(m::dsl_options::insist_no_ternary_logic, false); \
355 m::wasm::Module::Get().emit_insist((COND), __FILE__, __LINE__, (MSG)); \
356 m::dsl_options::insist_no_ternary_logic = old; \
359#define WASM_INSIST2_(COND, MSG) ({ \
360 m::wasm::Module::Get().emit_insist((COND), __FILE__, __LINE__, (MSG)); \
364#define WASM_INSIST1_(COND) WASM_INSIST2_((COND), nullptr)
367#define WASM_INSIST2_(COND, MSG) while (0) { ((void) (COND), (void) (MSG)); }
368#define WASM_INSIST1_(COND) while (0) { ((void) (COND)); }
372#define WASM_GET_INSIST_(XXX, _1, _2, NAME, ...) NAME
373#define Wasm_insist(...) WASM_GET_INSIST_(XXX, ##__VA_ARGS__, WASM_INSIST2_, WASM_INSIST1_)(__VA_ARGS__)
414template<
typename T,
typename...
Ts, std::size_t
L, std::size_t... Ls>
415requires (
sizeof...(Ts) ==
sizeof...(Ls))
418 friend std::ostream &
operator<<(std::ostream &out,
print_types) {
419 return out << wasm_type<T, L>() <<
", " <<
print_types<
Ts..., Ls...>{};
424template<
typename T, std::
size_t L>
428 return out << wasm_type<T, L>();
433inline std::string
unique(std::string prefix,
unsigned &counter)
435 static thread_local std::ostringstream oss;
437 oss << prefix <<
'<' << counter++ <<
'>';
442template<
typename T, std::
size_t L,
bool = false,
typename U>
443requires (L == 1)
and std::floating_point<T>
and std::floating_point<U>
446 return ::wasm::Literal(
T(
value));
450template<
typename T, std::
size_t L,
bool = false,
typename U>
454 return sizeof(
T) <= 4 ? ::wasm::Literal(int32_t(
value))
455 : ::wasm::Literal(int64_t(
value));
459template<
typename T, std::
size_t L,
bool = false,
typename U>
463 return sizeof(
T) <= 4 ? ::wasm::Literal(uint32_t(
value))
464 : ::wasm::Literal(uint64_t(
value));
468template<
typename T, std::
size_t L,
bool VecRepr = false,
typename U>
476template<
typename T, std::
size_t L,
bool = false>
477requires (L == 1)
and std::is_pointer_v<T>
480 return ::wasm::Literal(uint32_t(
value));
485template<
typename T, std::
size_t L,
bool = false,
typename U>
487requires (
U value) { make_literal<T, 1>(
value); }
490 std::array<::wasm::Literal, 16 /
sizeof(
T)> vec;
491 auto it = std::fill_n(vec.begin(),
L, make_literal<T, 1, true>(
value));
492 std::fill(it, vec.end(), make_literal<T, 1>(
T(0)));
493 return ::wasm::Literal(vec);
498template<
typename T, std::
size_t L,
bool = false,
typename U>
499requires (
L > 1)
and (L *
sizeof(T) > 16)
and ((L *
sizeof(T)) % 16 == 0)
and
500requires (
U value) { make_literal<T, 1>(
value); }
503 std::array<::wasm::Literal, 16 /
sizeof(
T)> vec;
504 vec.fill(make_literal<T, 1, true>(
value));
505 ::wasm::Literal elem(vec);
507 std::array<::wasm::Literal, (
L *
sizeof(
T)) / 16> literals;
514template<
typename T, std::size_t L,
bool =
false,
typename... Us>
515requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (L ==
sizeof...(Us))
and
516requires (Us... values) { (make_literal<T, 1>(values), ...); }
517inline ::wasm::Literal make_literal(Us... values)
519 std::array<::wasm::Literal, 16 /
sizeof(
T)> vec;
520 auto it = vec.begin();
521 ((*(it++) = make_literal<T, 1, true>(values)), ...);
522 std::fill(it, vec.end(), make_literal<T, 1>(
T(0)));
523 return ::wasm::Literal(vec);
528template<
typename T, std::size_t
L,
bool =
false,
typename... Us>
529requires (
L > 1)
and (L *
sizeof(T) > 16)
and ((L *
sizeof(T)) % 16 == 0)
and (L ==
sizeof...(Us))
and
530requires (Us... values) { (make_literal<T, 1>(values), ...); }
531inline std::array<::wasm::Literal, (L *
sizeof(
T)) / 16> make_literal(Us... values)
533 ::wasm::Literal vectors[L] = { make_literal<T, 1, true>(values)... };
535 std::array<::wasm::Literal, (L *
sizeof(T)) / 16> literals;
536 for (std::size_t idx = 0; idx < (L *
sizeof(T)) / 16; ++idx) {
537 auto vec = std::to_array(*
reinterpret_cast<::wasm::Literal(*)[16 / sizeof(T)]
>(vectors + idx * (16 /
sizeof(T))));
538 literals[idx] = ::wasm::Literal(vec);
548#define M_EXCEPTION_LIST(X) \
549 X(invalid_escape_sequence) \
551 X(failed_unittest_check)
555#define DECLARE_ENUM(TYPE) TYPE,
561#define DECLARE_NAMES(TYPE) #TYPE,
562 static constexpr const char *
const names_[] = {
581::wasm::Literals insist_interpreter(::wasm::Literals &
args);
585::wasm::Literals throw_interpreter(::wasm::Literals &
args);
587const std::map<::wasm::Name, std::function<::wasm::Literals(::wasm::Literals&)>>
callback_functions = {
588#define CALLBACK(NAME, FUNC) { NAME, FUNC },
635 static boolean_result_t EvalBoolean(const ::wasm::Expression *
expr);
657 static inline std::atomic_uint NEXT_MODULE_ID_ = 0;
662 unsigned next_block_id_ = 0;
664 unsigned next_function_id_ = 0;
666 unsigned next_global_id_ = 0;
668 unsigned next_if_id_ = 0;
670 unsigned next_loop_id_ = 0;
676 ::wasm::Block *active_block_ =
nullptr;
678 ::wasm::Function *active_function_ =
nullptr;
680 ::wasm::Memory *memory_ =
nullptr;
682 std::unique_ptr<std::pair<memory::AddressSpace, memory::Memory>>
vm_;
688 std::vector<std::tuple<const char*, unsigned, const char*>>
messages_;
690 std::unique_ptr<::wasm::ModuleRunner::ExternalInterface>
interface_;
707 M_insist(not the_module_,
"must not have a module yet");
708 the_module_ = std::unique_ptr<Module>(
new Module());
711 M_insist(
bool(the_module_),
"must have a module");
712 the_module_ =
nullptr;
715 M_insist(
bool(the_module_),
"must have a module");
721 static unsigned ID() {
return Get().id_; }
727 return unique(prefix, Get().next_function_id_);
731 return unique(prefix, Get().next_global_id_);
739 static ::wasm::Builder &
Builder() {
return Get().builder_; }
754 static bool Validate(
bool verbose =
true,
bool global =
true);
757 static void Optimize(
int optimization_level);
760 ::wasm::Block *
set_active_block(::wasm::Block *block) {
return std::exchange(active_block_, block); }
762 ::wasm::Function *
set_active_function(::wasm::Function *fn) {
return std::exchange(active_function_, fn); }
768 template<
typename T, std::
size_t L>
771 template<
typename T, std::
size_t L>
774 void emit_break(std::size_t level = 1);
777 void emit_continue(std::size_t level = 1);
780 template<
typename T, std::
size_t L>
782 template<
typename T, std::
size_t L>
784 template<
typename T, std::
size_t L>
787 template<
typename T, std::
size_t L>
789 Expr<T, L> emit_select(PrimitiveExpr<bool, L> cond, Expr<T, L> tru, Expr<T, L> fals);
796 template<
typename T, std::
size_t L, std::
size_t M>
797 requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
799 const
std::array<uint8_t, M> &indices);
803 template<typename T,
std::
size_t L,
std::
size_t M>
804 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (is_pow_2(M))
and (M * sizeof(T) <= 16)
806 const
std::array<uint8_t, M> &indices);
811 template<typename T,
std::
size_t L,
std::
size_t M>
812 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
and (sizeof(T) == 1)
813 Expr<T, M / sizeof(T)> emit_shuffle_bytes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices);
817 template<typename T,
std::
size_t L,
std::
size_t M>
818 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (is_pow_2(M))
and (M * sizeof(T) <= 16)
819 Expr<T, M> emit_shuffle_lanes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices);
823 requires (L * sizeof(T) <= 16)
and requires (Us... us) { make_literal<T, L>(us...); }
824 void emit_global(::wasm::Name name,
bool is_mutable, Us... inits) {
825 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
826 : ::wasm::Builder::Mutability::Immutable;
827 ::wasm::Const *_init = builder_.makeConst(make_literal<T, L>(inits...));
828 auto global = builder_.makeGlobal(name, wasm_type<T, L>(), _init, mut);
829 module_.addGlobal(std::move(global));
832 requires (
L *
sizeof(
T) <= 16)
and requires (Us... us) { make_literal<T, L>(us...); }
833 void emit_global(
const std::array<::wasm::Name, 1> &names,
bool is_mutable, Us... inits) {
834 emit_global<T, L>(names[0], is_mutable, inits...);
837 requires requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, N>>; }
838 void emit_global(
const std::array<::wasm::Name, N> &names,
bool is_mutable, Us... inits) {
839 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
840 : ::wasm::Builder::Mutability::Immutable;
841 auto literals = make_literal<T, L>(inits...);
842 for (std::size_t idx = 0; idx < N; ++idx) {
843 ::wasm::Const *_init = builder_.makeConst(literals[idx]);
844 auto global = builder_.makeGlobal(names[idx], wasm_type<T, L>(), _init, mut);
845 module_.addGlobal(std::move(global));
848 template<dsl_po
inter_to_primitive T, std::
size_t L = 1>
849 void emit_global(::wasm::Name name,
bool is_mutable, uint32_t init) {
850 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
851 : ::wasm::Builder::Mutability::Immutable;
852 ::wasm::Const *_init = builder_.makeConst(::wasm::Literal(
init));
853 auto global = builder_.makeGlobal(name, wasm_type<T, L>(), _init, mut);
854 module_.addGlobal(std::move(global));
857 template<
typename T, std::
size_t L = 1>
858 requires (
L *
sizeof(
T) <= 16)
862 template<
typename T, std::
size_t L = 1>
865 void emit_import(
const char *extern_name,
const char *intern_name =
nullptr)
867 ::wasm::Const *
value =
M_CONSTEXPR_COND(std::is_pointer_v<T>, builder_.makeConst(make_literal<T, L>(0)),
868 builder_.makeConst(make_literal<T, L>(
T())));
869 auto global = builder_.makeGlobal(intern_name ? intern_name : extern_name, wasm_type<T, L>(),
M_notnull(
value),
870 ::wasm::Builder::Mutability::Immutable);
871 global->module =
"imports";
872 global->base = extern_name;
873 module_.addGlobal(std::move(global));
878 requires std::is_function_v<T>
and requires { wasm_type<T, 1>(); }
880 auto func = module_.addFunction(builder_.makeFunction(name, wasm_type<T, 1>(), {}));
881 func->module =
"imports";
887 module_.addExport(builder_.makeExport(name, name, ::wasm::ExternalKind::Function));
891 template<
typename ReturnType,
typename... ParamTypes, std::size_t... ParamLs>
892 requires std::is_void_v<ReturnType>
895 template<
typename ReturnType, std::size_t
ReturnL = 1,
typename... ParamTypes, std::size_t... ParamLs>
900 template<std::
size_t L>
907 const std::tuple<const char*, unsigned, const char*> &
get_message(std::size_t idx)
const {
908 return messages_.at(idx);
915 template<
class C,
typename... Args>
917 auto it = garbage_collected_data_.template try_emplace(
921 return as<C>(*it->second);
928 ::wasm::ModuleRunner
instantiate(::wasm::GlobalValueSet imports = {}) {
929 return ::wasm::ModuleRunner(module_, get_mock_interface(std::move(imports)));
933 void set_feature(::wasm::FeatureSet feature,
bool value) { module_.features.set(feature,
value); }
937 std::pair<uint8_t*, std::size_t>
binary();
940 void create_local_bitmap_stack();
941 void create_local_bitvector_stack();
942 void dispose_local_bitmap_stack();
943 void dispose_local_bitvector_stack();
945 template<std::
size_t L = 1>
950 branch_target_stack_.emplace_back(brk, continu,
nullptr);
956 auto top = branch_target_stack_.back();
957 branch_target_stack_.pop_back();
968 out <<
" currently active block: ";
973 out <<
"<anonymous block>";
990 void dump(std::ostream &out)
const { out << *
this << std::endl; }
993 void dump_all(std::ostream &out) { out << module_ << std::endl; }
1014 ::wasm::Block *this_block_ =
nullptr;
1016 ::wasm::Block *parent_block_ =
nullptr;
1018 bool attach_to_parent_ =
false;
1032 Block(::wasm::Block *block,
bool attach_to_parent)
1034 , attach_to_parent_(attach_to_parent)
1036 if (attach_to_parent_) {
1037 parent_block_ = Module::Get().active_block_;
1038 M_insist(not attach_to_parent_ or parent_block_,
"can only attach to parent if there is a parent block");
1044 explicit Block(
bool attach_to_parent) :
Block(
Module::Builder().makeBlock(), attach_to_parent) { }
1046 explicit Block(std::string name,
bool attach_to_parent)
1047 :
Block(
Module::Builder().makeBlock(
Module::Unique_Block_Name(name)), attach_to_parent)
1050 explicit Block(
const char *name,
bool attach_to_parent) :
Block(
std::string(name), attach_to_parent) { }
1055 if (this_block_
and attach_to_parent_)
1066 other.list.push_back(this_block_);
1067 this_block_ =
nullptr;
1072 std::string
name()
const {
M_insist(has_name());
return get().name.toString(); }
1075 bool empty()
const {
return get().list.empty(); }
1079 M_insist(not attach_to_parent_,
"cannot explicitly attach if attach_to_parent is true");
1085 M_insist(not attach_to_parent_,
"cannot explicitly attach if attach_to_parent is true");
1086 attach_to(Module::Block());
1090 void go_to()
const { Module::Block().list.push_back(Module::Builder().makeBreak(get().name)); }
1095 out <<
"vvvvvvvvvv block";
1097 out <<
" \"" << B.
name() <<
'"';
1098 out <<
" starts here vvvvvvvvvv\n";
1100 for (
auto expr : B.
get().list)
1101 out << *
expr <<
'\n';
1103 out <<
"^^^^^^^^^^^ block";
1105 out <<
" \"" << B.
name() <<
'"';
1106 out <<
" ends here ^^^^^^^^^^^\n";
1111 void dump(std::ostream &out)
const { out << *
this; out.flush(); }
1121 ::wasm::Block *old_block_ =
nullptr;
1125 old_block_ = Module::Get().set_active_block(block_.
this_block_);
1141template<
typename ReturnType,
typename... ParamTypes, std::size_t
ReturnL, std::size_t... ParamLs>
1142requires ((std::is_void_v<ReturnType>
and (ReturnL == 1)) or
1144 (not dsl_primitive<ReturnType> or (ReturnL *
sizeof(ReturnType) <= 16))
and
1146 ((not dsl_primitive<ParamTypes> or (ParamLs *
sizeof(ParamTypes) <= 16))
and ...)
1152 using type = ReturnType(ParamTypes...);
1156 using return_type = ReturnType;
1158 static constexpr std::size_t PARAMETER_COUNT =
sizeof...(ParamTypes);
1164 template<
typename...
Ts, std::size_t... Ls, std::size_t... Is>
1165 requires (
sizeof...(Ts) ==
sizeof...(Ls))
and (
sizeof...(
Ts) ==
sizeof...(Is))
1166 std::tuple<Parameter<Ts, Ls>...> make_parameters_helper(std::index_sequence<Is...>) {
1167 return std::make_tuple<Parameter<Ts, Ls>...>(
1173 template<
typename...
Ts, std::size_t... Ls,
typename Indices = std::make_index_sequence<
sizeof...(Ts)>>
1174 requires (
sizeof...(
Ts) ==
sizeof...(Ls))
1175 std::tuple<Parameter<Ts, Ls>...> make_parameters() {
return make_parameters_helper<
Ts..., Ls...>(Indices{}); }
1178 template<std::size_t I,
typename...
Ts>
1179 struct parameter_type;
1180 template<std::size_t I,
typename T,
typename...
Ts>
1181 struct parameter_type<I,
T,
Ts...>
1183 static_assert(I <=
sizeof...(Ts),
"parameter index out of range");
1184 using type =
typename parameter_type<I - 1,
Ts...>::type;
1186 template<
typename T,
typename...
Ts>
1187 struct parameter_type<0,
T,
Ts...>
1192 template<std::
size_t I>
1193 using parameter_type_t =
typename parameter_type<I, ParamTypes...>::type;
1196 template<std::size_t I, std::size_t... Ls>
1197 struct parameter_num_simd_lanes;
1198 template<std::size_t I, std::size_t
L, std::size_t... Ls>
1199 struct parameter_num_simd_lanes<I,
L, Ls...>
1201 static_assert(I <=
sizeof...(Ls),
"parameter index out of range");
1202 static constexpr std::size_t
value = parameter_num_simd_lanes<I - 1, Ls...>::value;
1204 template<std::size_t
L, std::size_t... Ls>
1205 struct parameter_num_simd_lanes<0,
L, Ls...>
1207 static constexpr std::size_t
value =
L;
1210 template<std::
size_t I>
1211 static constexpr std::size_t parameter_num_simd_lanes_v = parameter_num_simd_lanes<I, ParamLs...>::value;
1217 ::wasm::Function *this_function_ =
nullptr;
1219 ::wasm::Function *previous_function_ =
nullptr;
1224 swap(first.name_, second.name_);
1225 swap(first.body_, second.body_);
1226 swap(first.this_function_, second.this_function_);
1227 swap(first.previous_function_, second.previous_function_);
1237 , body_(name +
".body",
false)
1240 if constexpr (not std::is_void_v<ReturnType>)
1241 body_.
get().type = wasm_type<ReturnType, ReturnL>();
1244 auto fn = Module::Builder().makeFunction(
1246 wasm_type<dsl_type, 1>(),
1247 std::vector<::wasm::Type>{}
1249 fn->body = &body_.
get();
1250 this_function_ = Module::Get().module_.addFunction(std::move(fn));
1251 M_insist(this_function_->getNumParams() == PARAMETER_COUNT);
1252 Module::Get().create_local_bitmap_stack();
1253 Module::Get().create_local_bitvector_stack();
1256 previous_function_ = Module::Get().set_active_function(this_function_);
1263 if constexpr (not std::is_void_v<ReturnType>)
1264 body_.
get().list.push_back(Module::Builder().makeUnreachable());
1265 Module::Get().dispose_local_bitmap_stack();
1266 Module::Get().dispose_local_bitvector_stack();
1268 Module::Get().set_active_function(previous_function_);
1275 Block & body() {
return body_; }
1277 const Block & body()
const {
return body_; }
1280 std::string name()
const {
return name_.toString(); }
1283 std::tuple<Parameter<ParamTypes, ParamLs>...> parameters() {
return make_parameters<ParamTypes..., ParamLs...>(); }
1286 template<std::
size_t I>
1292 void emit_return()
requires std::is_void_v<ReturnType> {
1293 Module::Block().list.push_back(Module::Builder().makeReturn());
1297 template<primitive_convertible T>
1298 requires (not std::is_void_v<ReturnType>)
and
1300 void emit_return(
T &&t) {
1302 Module::Get().emit_return(
value);
1307 template<expr_convertible T>
1310 void emit_return(
T &&t)
1313 Module::Get().emit_return(
expr);
1317 ::wasm::Function & get()
const {
return *
M_notnull(this_function_); }
1321 out <<
"function \"" << Fn.name() <<
"\" : ";
1322 if constexpr (PARAMETER_COUNT)
1325 out <<
typeid(void).name();
1328 if (not Fn.get().vars.empty()) {
1329 out <<
" " << Fn.get().getNumVars() <<
" local variables:";
1330 for (::wasm::Index i = 0, end = Fn.get().getNumVars(); i != end; ++i)
1331 out <<
" [" << i <<
"] " << Fn.get().vars[i];
1339 void dump(std::ostream &out)
const { out << *
this; out.flush(); }
1340 void dump()
const { dump(std::cerr); }
1343template<
typename ReturnType,
typename... ParamTypes>
1344requires (std::is_void_v<ReturnType> or dsl_primitive<ReturnType> or dsl_pointer_to_primitive<ReturnType>)
and
1345 ((dsl_primitive<ParamTypes> or dsl_pointer_to_primitive<ParamTypes>)
and ...)
1346struct Function<ReturnType(ParamTypes...)> : Function<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>
1348 using Function<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>::Function;
1351template<
typename... ParamTypes, std::size_t... ParamLs>
1353 :
Function<PrimitiveExpr<void, 1>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1369template<
typename ReturnType,
typename... ParamTypes, std::size_t
ReturnL, std::size_t... ParamLs>
1370requires ((std::is_void_v<ReturnType>
and (ReturnL == 1)) or
1374 ((not dsl_primitive<ParamTypes> or (ParamLs * sizeof(ParamTypes) <= 16))
and ...)
1375struct FunctionProxy<PrimitiveExpr<ReturnType, ReturnL>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1377 using type = ReturnType(ParamTypes...);
1378 using dsl_type = PrimitiveExpr<ReturnType, ReturnL>(PrimitiveExpr<ParamTypes, ParamLs>...);
1384 FunctionProxy() =
delete;
1385 FunctionProxy(std::string name) : name_(Module::Unique_Function_Name(name)) { }
1386 FunctionProxy(
const char *name) : FunctionProxy(
std::string(name)) { }
1392 const std::string & name()
const {
return name_; }
1393 const char * c_name()
const {
return name_.c_str(); }
1395 Function<dsl_type> make_function()
const {
return Function<dsl_type>(name_); }
1399 template<
typename... Args>
1400 requires std::is_void_v<ReturnType>
and
1401 requires (Args&&...
args) { (PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args)), ...); }
1402 void operator()(Args&&...
args)
const {
1403 operator()(PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args))...);
1407 void operator()(PrimitiveExpr<ParamTypes, ParamLs>...
args)
const requires std::is_void_v<ReturnType> {
1408 Module::Block().list.push_back(
1409 Module::Builder().makeCall(name_, {
args.expr()... }, wasm_type<ReturnType, ReturnL>())
1414 template<
typename... Args>
1415 requires (not std::is_void_v<ReturnType>)
and
1416 requires (Args&&...
args) { (PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args)), ...); }
1417 PrimitiveExpr<ReturnType, ReturnL> operator()(Args&&...
args)
const {
1418 return operator()(PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args))...);
1422 PrimitiveExpr<ReturnType, ReturnL>
1423 operator()(PrimitiveExpr<ParamTypes, ParamLs>...
args)
const requires (not std::is_void_v<ReturnType>) {
1424 return PrimitiveExpr<ReturnType, ReturnL>(
1425 Module::Builder().makeCall(name_, {
args.expr()... }, wasm_type<ReturnType, ReturnL>())
1430template<
typename ReturnType,
typename... ParamTypes>
1431requires (std::is_void_v<ReturnType> or dsl_primitive<ReturnType> or dsl_pointer_to_primitive<ReturnType>)
and
1432 ((dsl_primitive<ParamTypes> or dsl_pointer_to_primitive<ParamTypes>)
and ...)
1434 :
FunctionProxy<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>
1436 using FunctionProxy<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>::FunctionProxy;
1439template<
typename... ParamTypes, std::size_t... ParamLs>
1441 :
FunctionProxy<PrimitiveExpr<void, 1>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1451template<
typename T,
typename U, std::
size_t L>
1453requires (PrimitiveExpr<T, L> e) { PrimitiveExpr<common_type_t<T, U>,
L>(e); }
and
1459template<dsl_primitive T, std::
size_t L>
1470 template<
typename, std::
size_t>
1472 template<
typename, VariableKind,
bool, std::
size_t>
1481 template<
typename, std::
size_t>
friend struct std::array;
1494 explicit PrimitiveExpr(::wasm::Expression *
expr, std::list<std::shared_ptr<Bit>> referenced_bits = {})
1496 , referenced_bits_(std::move(referenced_bits))
1507 :
PrimitiveExpr(Module::Builder().makeConst(::wasm::Literal(bytes.data())))
1518 template<dsl_primitive... Us>
1519 requires (
sizeof...(Us) > 0)
and requires (Us... us) { make_literal<T, L>(us...); }
1527 requires (Us... us) {
PrimitiveExpr(std::decay_t<Us>(us)...); }
1528 explicit PrimitiveExpr(Us... value)
1529 : PrimitiveExpr(
std::decay_t<Us>(value)...)
1548 swap(this->expr_, other.expr_);
1549 swap(this->referenced_bits_, other.referenced_bits_);
1559 M_insist(
expr_,
"cannot access an already moved or discarded expression of a `PrimitiveExpr`");
1560 return std::exchange(
expr_,
nullptr);
1566 template<dsl_primitive U = T, std::
size_t M = L>
1567 std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>>
move() {
1578 M_insist(
expr_,
"cannot clone an already moved or discarded `PrimitiveExpr`");
1580 ::wasm::ExpressionManipulator::copy(
expr_, Module::Get().module_),
1589 M_insist(
expr_,
"cannot discard an already moved or discarded `PrimitiveExpr`");
1590 if (
expr_->is<::wasm::Call>())
1591 Module::Block().list.push_back(Module::Builder().makeDrop(
expr_));
1606 template<dsl_primitive ResultType, std::
size_t ResultL>
1609 Module::Builder().makeUnary(
op,
expr()),
1616 template<dsl_primitive ResultType, std::
size_t ResultL, dsl_primitive OperandType, std::
size_t OperandL>
1621 Module::Builder().makeBinary(
op,
1622 this->
template to<OperandType, OperandL>().
expr(),
1634 template<dsl_primitive U, std::
size_t M>
1638 constexpr std::size_t FromL =
L;
1639 constexpr std::size_t
ToL = M;
1641 if constexpr (std::same_as<From, To>
and FromL ==
ToL)
1644 sizeof(From) ==
sizeof(
To)
and FromL ==
ToL)
1649 if constexpr (FromL == 1
and ToL == 1) {
1650 if constexpr (
sizeof(
To) <= 4)
1652 if constexpr (
sizeof(
To) == 8)
1653 return unary<To, ToL>(::wasm::ExtendUInt32);
1655 if constexpr (FromL > 1
and FromL ==
ToL) {
1656 if constexpr (
sizeof(
To) == 1)
1658 if constexpr (std::is_signed_v<To>) {
1659 if constexpr (
sizeof(
To) == 2)
1660 return to<int8_t, ToL>().template to<To, ToL>();
1661 if constexpr (
sizeof(
To) == 4)
1662 return to<int8_t, ToL>().template to<To, ToL>();
1663 if constexpr (
sizeof(
To) == 8)
1664 return to<int8_t, ToL>().template to<To, ToL>();
1666 if constexpr (
sizeof(
To) == 2)
1667 return to<uint8_t, ToL>().template to<To, ToL>();
1668 if constexpr (
sizeof(
To) == 4)
1669 return to<uint8_t, ToL>().template to<To, ToL>();
1670 if constexpr (
sizeof(
To) == 8)
1671 return to<uint8_t, ToL>().template to<To, ToL>();
1675 if constexpr (std::floating_point<To>) {
1676 if constexpr (FromL == 1
and ToL == 1) {
1677 if constexpr (
sizeof(
To) == 4)
1678 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat32);
1679 if constexpr (
sizeof(
To) == 8)
1680 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat64);
1682 if constexpr (FromL > 1
and FromL ==
ToL) {
1683 if constexpr (
sizeof(
To) == 4)
1684 return to<uint32_t, ToL>().template convert<To, ToL>();
1685 if constexpr (
sizeof(
To) == 8)
1686 return to<uint32_t, ToL>().template convert<To, ToL>();
1696 if constexpr (FromL == 1
and ToL == 1) {
1697 if constexpr (std::is_signed_v<From>) {
1698 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1699 return unary<To, ToL>(::wasm::ExtendSInt32);
1700 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1701 return unary<To, ToL>(::wasm::WrapInt64);
1703 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1704 return unary<To, ToL>(::wasm::ExtendUInt32);
1705 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1706 return unary<To, ToL>(::wasm::WrapInt64);
1708 if constexpr (
sizeof(
To) <= 4
and sizeof(From) <
sizeof(
To))
1710 if constexpr (
sizeof(From) <= 4
and sizeof(
To) <
sizeof(From)) {
1711 constexpr From MASK = (uint64_t(1) << (8 *
sizeof(
To))) - uint64_t(1);
1714 if constexpr (
sizeof(From) == 8
and sizeof(
To) < 4) {
1715 if constexpr (std::is_signed_v<To>) {
1716 auto wrapped = unary<int32_t, ToL>(::wasm::WrapInt64);
1717 constexpr int32_t MASK = (int64_t(1) << (8 *
sizeof(
To))) - int64_t(1);
1722 auto wrapped = unary<uint32_t, ToL>(::wasm::WrapInt64);
1723 constexpr uint32_t MASK = (uint64_t(1) << (8 *
sizeof(
To))) - uint64_t(1);
1730 if constexpr (FromL > 1
and FromL ==
ToL) {
1731 if constexpr (std::is_signed_v<From>) {
1732 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL <= 8)
1733 return unary<To, ToL>(::wasm::ExtendLowSVecI8x16ToVecI16x8);
1734 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL == 16) {
1737 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI8x16ToVecI16x8);
1741 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1742 return to<int16_t, ToL>().template to<To, ToL>();
1743 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1744 return to<int32_t, ToL>().template to<To, ToL>();
1745 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL <= 4)
1746 return unary<To, ToL>(::wasm::ExtendLowSVecI16x8ToVecI32x4);
1747 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL == 8) {
1750 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI16x8ToVecI32x4);
1754 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1755 return to<int32_t, ToL>().template to<To, ToL>();
1756 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1757 return unary<To, ToL>(::wasm::ExtendLowSVecI32x4ToVecI64x2);
1758 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1761 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI32x4ToVecI64x2);
1766 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL <= 8)
1767 return unary<To, ToL>(::wasm::ExtendLowUVecI8x16ToVecI16x8);
1768 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL == 16) {
1771 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI8x16ToVecI16x8);
1775 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1776 return to<uint16_t, ToL>().template to<To, ToL>();
1777 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1778 return to<uint32_t, ToL>().template to<To, ToL>();
1779 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL <= 4)
1780 return unary<To, ToL>(::wasm::ExtendLowUVecI16x8ToVecI32x4);
1781 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL == 8) {
1784 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI16x8ToVecI32x4);
1788 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1789 return to<uint32_t, ToL>().template to<To, ToL>();
1790 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1791 return unary<To, ToL>(::wasm::ExtendLowUVecI32x4ToVecI64x2);
1792 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1795 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI32x4ToVecI64x2);
1802 if constexpr (std::floating_point<To>) {
1803 if constexpr (FromL == 1
and ToL == 1) {
1804 if constexpr (std::is_signed_v<From>) {
1805 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 4)
1806 return unary<To, ToL>(::wasm::ConvertSInt32ToFloat32);
1807 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1808 return unary<To, ToL>(::wasm::ConvertSInt32ToFloat64);
1809 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1810 return unary<To, ToL>(::wasm::ConvertSInt64ToFloat32);
1811 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1812 return unary<To, ToL>(::wasm::ConvertSInt64ToFloat64);
1814 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 4)
1815 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat32);
1816 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1817 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat64);
1818 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1819 return unary<To, ToL>(::wasm::ConvertUInt64ToFloat32);
1820 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1821 return unary<To, ToL>(::wasm::ConvertUInt64ToFloat64);
1824 if constexpr (FromL > 1
and FromL ==
ToL) {
1825 if constexpr (std::is_signed_v<From>) {
1826 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1827 return to<int32_t, ToL>().template to<To, ToL>();
1828 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1829 return to<int32_t, ToL>().template to<To, ToL>();
1830 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
1831 return to<int32_t, ToL>().template to<To, ToL>();
1832 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1833 return to<int32_t, ToL>().template to<To, ToL>();
1834 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1835 return unary<To, ToL>(::wasm::ConvertSVecI32x4ToVecF32x4);
1836 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1837 return unary<To, ToL>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1838 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1841 clone().template
unary<
To,
ToL / 2>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1842 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1844 high_to_low.template
unary<
To,
ToL / 2>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1848 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1849 return to<uint32_t, ToL>().template convert<To, ToL>();
1850 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1851 return to<uint32_t, ToL>().template convert<To, ToL>();
1852 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
1853 return to<uint32_t, ToL>().template convert<To, ToL>();
1854 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1855 return to<uint32_t, ToL>().template convert<To, ToL>();
1856 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1857 return unary<To, ToL>(::wasm::ConvertUVecI32x4ToVecF32x4);
1858 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1859 return unary<To, ToL>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1860 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1863 clone().template
unary<
To,
ToL / 2>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1864 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1866 high_to_low.template
unary<
To,
ToL / 2>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1874 if constexpr (std::floating_point<From>) {
1876 if constexpr (FromL == 1
and ToL == 1) {
1877 if constexpr (std::is_signed_v<To>) {
1878 if constexpr (
sizeof(From) == 4
and sizeof(
To) <= 4)
1879 return unary<int32_t, ToL>(::wasm::TruncSFloat32ToInt32).template to<To, ToL>();
1880 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1881 return unary<To, ToL>(::wasm::TruncSFloat32ToInt64);
1882 if constexpr (
sizeof(From) == 8
and sizeof(
To) <= 4)
1883 return unary<int32_t, ToL>(::wasm::TruncSFloat64ToInt32).template to<To, ToL>();
1884 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1885 return unary<To, ToL>(::wasm::TruncSFloat64ToInt64);
1887 if constexpr (
sizeof(From) == 4
and sizeof(
To) <= 4)
1888 return unary<uint32_t, ToL>(::wasm::TruncUFloat32ToInt32).template to<To, ToL>();
1889 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1890 return unary<To, ToL>(::wasm::TruncUFloat32ToInt64);
1891 if constexpr (
sizeof(From) == 8
and sizeof(
To) <= 4)
1892 return unary<uint32_t, ToL>(::wasm::TruncUFloat64ToInt32).template to<To, ToL>();
1893 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1894 return unary<To, ToL>(::wasm::TruncUFloat64ToInt64);
1897 if constexpr (FromL > 1
and FromL ==
ToL) {
1898 if constexpr (std::is_signed_v<To>) {
1899 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1900 return unary<To, ToL>(::wasm::TruncSatSVecF32x4ToVecI32x4);
1901 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1902 return unary<To, ToL>(::wasm::TruncSatZeroSVecF64x2ToVecI32x4);
1903 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1904 return to<int32_t, ToL>().template to<To, ToL>();
1905 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1906 return to<int32_t, ToL>().template to<To, ToL>();
1908 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1909 return unary<To, ToL>(::wasm::TruncSatUVecF32x4ToVecI32x4);
1910 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1911 return unary<To, ToL>(::wasm::TruncSatZeroUVecF64x2ToVecI32x4);
1912 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1913 return convert<uint32_t, ToL>().template to<To, ToL>();
1914 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1915 return convert<uint32_t, ToL>().template to<To, ToL>();
1919 if constexpr (std::floating_point<To>) {
1920 if constexpr (FromL == 1
and ToL == 1) {
1921 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1922 return unary<To, ToL>(::wasm::PromoteFloat32);
1923 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1924 return unary<To, ToL>(::wasm::DemoteFloat64);
1926 if constexpr (FromL > 1
and FromL ==
ToL) {
1927 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1928 return unary<To, ToL>(::wasm::PromoteLowVecF32x4ToVecF64x2);
1929 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1932 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1934 high_to_low.template
unary<
To,
ToL / 2>(::wasm::PromoteLowVecF32x4ToVecF64x2);
1937 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1938 return unary<To, ToL>(::wasm::DemoteZeroVecF64x2ToVecF32x4);
1955 template<dsl_primitive To, std::
size_t ToL = L>
1956 requires (L == ToL)
and
1959 (
sizeof(
T) <=
sizeof(
To))
1973 template<dsl_primitive To, std::
size_t ToL = L>
1974 requires (L == ToL)
and
1979 std::is_convertible_v<T, To>)
and
1981 (std::is_convertible_v<T, To>
and
1993 template<dsl_po
inter_to_primitive To, std::
size_t ToL = L>
2002 auto make_signed()
requires unsigned_integral<T> {
return PrimitiveExpr<std::make_signed_t<T>, L>(move()); }
2008 auto make_unsigned()
requires signed_integral<T> {
return PrimitiveExpr<std::make_unsigned_t<T>, L>(move()); }
2016 template<dsl_primitive To, std::
size_t ToL = L>
2020 (
sizeof(T) ==
sizeof(
To))
2023 constexpr std::size_t FromL =
L;
2026 if constexpr (std::floating_point<To>) {
2027 if constexpr (FromL == 1) {
2028 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
2029 return unary<To, ToL>(::wasm::ReinterpretInt32);
2030 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
2031 return unary<To, ToL>(::wasm::ReinterpretInt64);
2036 if constexpr (std::floating_point<From>) {
2038 if constexpr (FromL == 1) {
2039 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
2040 return unary<To, ToL>(::wasm::ReinterpretFloat32);
2041 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
2042 return unary<To, ToL>(::wasm::ReinterpretFloat64);
2051 template<std::
size_t ToL>
2052 requires (
L == 1)
and (ToL > 1)
and (ToL *
sizeof(T) <= 16)
2055 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI8x16);
2058 if constexpr (
sizeof(
T) == 1)
2059 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI8x16);
2060 if constexpr (
sizeof(
T) == 2)
2061 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI16x8);
2062 if constexpr (
sizeof(
T) == 4)
2063 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI32x4);
2064 if constexpr (
sizeof(
T) == 8)
2065 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI64x2);
2068 if constexpr (std::floating_point<T>) {
2069 if constexpr (
sizeof(
T) == 4)
2070 return unary<T, ToL>(::wasm::UnaryOp::SplatVecF32x4);
2071 if constexpr (
sizeof(
T) == 8)
2072 return unary<T, ToL>(::wasm::UnaryOp::SplatVecF64x2);
2078 template<std::
size_t ToL>
2079 requires (L == 1)
and (ToL > 1)
and (ToL *
sizeof(T) > 16)
2080 PrimitiveExpr<T, ToL> broadcast() {
2081 using ResT = PrimitiveExpr<T, ToL>;
2082 std::array<typename ResT::vector_type, ResT::num_vectors> vectors;
2083 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx)
2084 vectors[idx] = clone().template broadcast<ResT::vector_type::num_simd_lanes>();
2085 return ResT(std::move(vectors));
2093#define UNOP_(NAME, TYPE) (::wasm::UnaryOp::NAME##TYPE)
2094#define UNIOP_(NAME) [] { \
2095 if constexpr (sizeof(T) == 8) \
2096 return UNOP_(NAME,Int64); \
2097 else if constexpr (sizeof(T) <= 4) \
2098 return UNOP_(NAME,Int32); \
2100 M_unreachable("unsupported operation"); \
2102#define UNFOP_(NAME) [] { \
2103 if constexpr (sizeof(T) == 8) \
2104 return UNOP_(NAME,Float64); \
2105 else if constexpr (sizeof(T) == 4) \
2106 return UNOP_(NAME,Float32); \
2108 M_unreachable("unsupported operation"); \
2110#define UNVOP_(NAME, TYPE) (::wasm::UnaryOp::NAME##Vec##TYPE)
2111#define UNIVOP_(NAME) [] { \
2112 if constexpr (sizeof(T) == 8) \
2113 return UNVOP_(NAME,I64x2); \
2114 else if constexpr (sizeof(T) == 4) \
2115 return UNVOP_(NAME,I32x4); \
2116 else if constexpr (sizeof(T) == 2) \
2117 return UNVOP_(NAME,I16x8); \
2118 else if constexpr (sizeof(T) == 1) \
2119 return UNVOP_(NAME,I8x16); \
2121 M_unreachable("unsupported operation"); \
2123#define UNFVOP_(NAME) [] { \
2124 if constexpr (sizeof(T) == 8) \
2125 return UNVOP_(NAME,F64x2); \
2126 else if constexpr (sizeof(T) == 4) \
2127 return UNVOP_(NAME,F32x4); \
2129 M_unreachable("unsupported operation"); \
2131#define UNARY_VOP(NAME) [] { \
2132 if constexpr (std::integral<T>) \
2133 return UNIVOP_(NAME); \
2134 else if constexpr (std::floating_point<T>) \
2135 return UNFVOP_(NAME); \
2137 M_unreachable("unsupported operation"); \
2145 PrimitiveExpr
operator-()
requires std::floating_point<T>
and (L == 1) {
return unary<T, L>(
UNFOP_(Neg)); }
2146 PrimitiveExpr operator-()
requires arithmetic<T>
and (L > 1) {
return unary<T, L>(
UNARY_VOP(Neg)); }
2161 static_assert(
L % 2 == 0,
"must mask this expression first");
2162 auto vec =
unary<int16_t,
L / 2>(
UNVOP_(ExtAddPairwiseS, I8x16ToI16x8));
2164 vec.template extract_unsafe<0>(),
2167 PrimitiveExpr<uint16_t, L / 2> add_pairwise()
requires unsigned_integral<T>
and (sizeof(T) == 1)
and (L > 1) {
2168 static_assert(L % 2 == 0,
"must mask this expression first");
2169 auto vec = unary<uint16_t, L / 2>(
UNVOP_(ExtAddPairwiseU, I8x16ToI16x8));
2171 vec.template extract_unsafe<0>(),
2174 PrimitiveExpr<int32_t, L / 2> add_pairwise()
requires signed_integral<T>
and (sizeof(T) == 2)
and (L > 1) {
2175 static_assert(L % 2 == 0,
"must mask this expression first");
2176 auto vec = unary<int32_t, L / 2>(
UNVOP_(ExtAddPairwiseS, I16x8ToI32x4));
2178 vec.template extract_unsafe<0>(),
2182 static_assert(
L % 2 == 0,
"must mask this expression first");
2183 auto vec =
unary<uint32_t,
L / 2>(
UNVOP_(ExtAddPairwiseU, I16x8ToI32x4));
2185 vec.template extract_unsafe<0>(),
2192 PrimitiveExpr operator~() requires integral<T>
and (L > 1) {
return unary<T, L>(
UNVOP_(Not, 128)); }
2195 return unary<T, L>(
UNIOP_(Clz));
2204 PrimitiveExpr popcnt()
requires unsigned_integral<T>
and (L == 1) {
return unary<T, L>(
UNIOP_(Popcnt)); }
2206 return unary<T, L>(
UNVOP_(Popcnt, I8x16));
2209 auto popcnt_on_I8x16 = this->unary<uint8_t, L * 2>(
UNVOP_(Popcnt, I8x16));
2210 return popcnt_on_I8x16.add_pairwise();
2213 auto popcnt_on_I8x16 = this->unary<uint8_t, L * 4>(
UNVOP_(Popcnt, I8x16));
2214 return popcnt_on_I8x16.add_pairwise().add_pairwise();
2223 PrimitiveExpr<uint32_t, 1> bitmask()
requires integral<T>
and (L > 1) {
2224 auto bitmask = unary<uint32_t, 1>(
UNIVOP_(Bitmask));
2225 return M_CONSTEXPR_COND(L *
sizeof(T) == 16, bitmask, bitmask bitand uint32_t((1U << L) - 1U));
2234 PrimitiveExpr
operator not()
requires boolean<T>
and (L == 1) {
return unary<T, L>(
UNIOP_(EqZ)); }
2235 PrimitiveExpr
operator not()
requires boolean<T>
and (L > 1) {
return unary<T, L>(
UNVOP_(Not, 128)); }
2241 return masked.template unary<bool, 1>(
UNVOP_(AnyTrue, 128));
2244 PrimitiveExpr<bool, 1> any_true()
requires integral<T>
and (L > 1) {
2246 M_CONSTEXPR_COND(L *
sizeof(T) == 16, *
this, PrimitiveExpr(
T(-1)) bitand *
this);
2247 return masked.template unary<bool, 1>(
UNVOP_(AnyTrue, 128));
2251 std::array<uint8_t, 16> bytes;
2252 auto it = std::fill_n(bytes.begin(),
L, 0);
2253 std::fill(it, bytes.end(), 0xff);
2256 return masked.template unary<bool, 1>(
UNVOP_(AllTrue, I8x16));
2259 PrimitiveExpr<bool, 1> all_true()
requires integral<T>
and (L > 1) {
2260 std::array<uint8_t, 16> bytes;
2261 auto it = std::fill_n(bytes.begin(), L, 0);
2262 std::fill(it, bytes.end(), 1);
2264 M_CONSTEXPR_COND(L *
sizeof(T) == 16, *
this, PrimitiveExpr(bytes) bitor *
this);
2265 return masked.template unary<bool, 1>(
UNIVOP_(AllTrue));
2271 PrimitiveExpr<uint64_t, L> hash()
requires signed_integral<T>
and (L == 1) {
return make_unsigned(); }
2272 PrimitiveExpr<uint64_t, L> hash()
requires std::floating_point<T>
and (sizeof(T) == 4)
and (L == 1) {
2273 return reinterpret<int32_t>().make_unsigned();
2275 PrimitiveExpr<uint64_t, L>
hash()
requires std::floating_point<T>
and (sizeof(T) == 8)
and (L == 1) {
2276 return reinterpret<int64_t>().make_unsigned();
2278 PrimitiveExpr<uint64_t, L>
hash()
requires std::same_as<T,
bool>
and (L == 1) {
return to<uint64_t>(); }
2293#define BINOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##TYPE)
2294#define BINIOP_(NAME, SIGN) [] { \
2295 if constexpr (sizeof(To) == 8) \
2296 return BINOP_(NAME,SIGN,Int64); \
2297 else if constexpr (sizeof(To) <= 4) \
2298 return BINOP_(NAME,SIGN,Int32); \
2300 M_unreachable("unsupported operation"); \
2302#define BINFOP_(NAME) [] { \
2303 if constexpr (sizeof(To) == 8) \
2304 return BINOP_(NAME,,Float64); \
2305 else if constexpr (sizeof(To) == 4) \
2306 return BINOP_(NAME,,Float32); \
2308 M_unreachable("unsupported operation"); \
2310#define BINARY_OP(NAME, SIGN) [] { \
2311 if constexpr (std::integral<To>) \
2312 return BINIOP_(NAME, SIGN); \
2313 else if constexpr (std::floating_point<To>) \
2314 return BINFOP_(NAME); \
2316 M_unreachable("unsupported operation"); \
2318#define BINVOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##Vec##TYPE)
2319#define BINIVOP_(NAME, SIGN) [] { \
2320 if constexpr (sizeof(To) == 8) \
2321 return BINVOP_(NAME,SIGN,I64x2); \
2322 else if constexpr (sizeof(To) == 4) \
2323 return BINVOP_(NAME,SIGN,I32x4); \
2324 else if constexpr (sizeof(To) == 2) \
2325 return BINVOP_(NAME,SIGN,I16x8); \
2326 else if constexpr (sizeof(To) == 1) \
2327 return BINVOP_(NAME,SIGN,I8x16); \
2329 M_unreachable("unsupported operation"); \
2331#define BINFVOP_(NAME) [] { \
2332 if constexpr (sizeof(To) == 8) \
2333 return BINVOP_(NAME,,F64x2); \
2334 else if constexpr (sizeof(To) == 4) \
2335 return BINVOP_(NAME,,F32x4); \
2337 M_unreachable("unsupported operation"); \
2339#define BINARY_VOP(NAME, SIGN) [] { \
2340 if constexpr (std::integral<To>) \
2341 return BINIVOP_(NAME, SIGN); \
2342 else if constexpr (std::floating_point<To>) \
2343 return BINFVOP_(NAME); \
2345 M_unreachable("unsupported operation"); \
2351 template<arithmetic U>
2355 if constexpr (
L *
sizeof(
To) <= 16)
2358 return this->
template to<To, L>().operator+(other.template to<To, L>());
2362 template<arithmetic U>
2366 if constexpr (
L *
sizeof(
To) <= 16)
2369 return this->
template to<To, L>().operator-(other.template to<To, L>());
2373 template<arithmetic U>
2374 requires arithmetically_combinable<T, U, L>
2377 return binary<To, L, To, L>(
BINARY_OP(Mul,), other);
2380 template<arithmetic U>
2385 if constexpr (std::integral<To>) {
2386 if constexpr (
sizeof(
To) == 8)
2388 else if constexpr (
sizeof(
To) == 4)
2390 else if constexpr (
sizeof(
To) == 2)
2392 }
else if (std::floating_point<To>) {
2397 if constexpr (
L *
sizeof(
To) <= 16)
2398 return binary<To, L, To, L>(op, other);
2400 return this->
template to<To, L>().operator*(other.template to<To, L>());
2403 template<arithmetic U>
2407 static_assert(
L == 16);
2408 using To = std::conditional_t<std::is_signed_v<T>, int16_t, uint16_t>;
2421 std::move(referenced_bits_low)
2424 Module::Builder().makeBinary(op_high, this->expr(), other.expr()),
2425 std::move(referenced_bits_high)
2429 auto indices = std::to_array<uint8_t>({ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 });
2434 template<arithmetic U>
2442 template<std::
floating_po
int U>
2443 requires arithmetically_combinable<T, U, L>
2445 requires std::floating_point<T>
and (L > 1) {
2447 if constexpr (
L *
sizeof(
To) <= 16)
2448 return binary<To, L, To, L>(
BINFVOP_(Div), other);
2450 return this->
template to<To, L>().operator/(other.template to<To, L>());
2454 template<
integral U>
2463 template<std::
floating_po
int U>
2464 requires arithmetically_combinable<T, U, L>
2466 requires std::floating_point<T>
and (L == 1) {
2468 return binary<To, L, To, L>(
BINFOP_(CopySign), other);
2472 template<std::
floating_po
int U>
2473 requires arithmetically_combinable<T, U, L>
2476 if constexpr (
L *
sizeof(
To) <= 16)
2479 return this->
template to<To, L>().min(other.template to<To, L>());
2482 template<
integral U>
2487 if constexpr (
sizeof(
To) == 4)
2489 else if constexpr (
sizeof(
To) == 2)
2491 else if constexpr (
sizeof(
To) == 1)
2495 if constexpr (
L *
sizeof(
To) <= 16)
2496 return binary<To, L, To, L>(
op, other);
2498 return this->
template to<To, L>().min(other.template to<To, L>());
2502 template<std::
floating_po
int U>
2503 requires arithmetically_combinable<T, U, L>
2506 if constexpr (
L *
sizeof(
To) <= 16)
2509 return this->
template to<To, L>().max(other.template to<To, L>());
2512 template<
integral U>
2517 if constexpr (
sizeof(To) == 4)
2519 else if constexpr (
sizeof(To) == 2)
2521 else if constexpr (
sizeof(To) == 1)
2525 if constexpr (L *
sizeof(To) <= 16)
2526 return binary<To, L, To, L>(op, other);
2528 return this->
template to<To, L>().max(other.template to<To, L>());
2532 template<
unsigned_
integral U>
2538 if constexpr (
sizeof(
To) == 2)
2540 else if constexpr (
sizeof(
To) == 1)
2544 if constexpr (
L *
sizeof(
To) <= 16)
2545 return binary<To, L, To, L>(
op, other);
2547 return this->
template to<To, L>().avg(other.template to<To, L>());
2553 template<std::
integral U>
2554 requires arithmetically_combinable<T, U, L>
2557 if constexpr (
L *
sizeof(
To) <= 16)
2560 return this->
template to<To, L>().operator bitand(other.template to<To, L>());
2564 template<std::
integral U>
2565 requires arithmetically_combinable<T, U, L>
2566 auto operator bitor(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>, L>
requires std::integral<T> {
2568 if constexpr (L *
sizeof(To) <= 16)
2571 return this->
template to<To, L>().operator bitor(other.template to<To, L>());
2575 template<std::
integral U>
2576 requires arithmetically_combinable<T, U, L>
2577 auto operator xor(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>, L>
requires std::integral<T> {
2578 using To = common_type_t<T, U>;
2579 if constexpr (L *
sizeof(To) <= 16)
2582 return this->
template to<To, L>().operator xor(other.template to<To, L>());
2586 template<
integral U>
2587 requires arithmetically_combinable<T, U, L>
2588 auto operator<<(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>,
L>
2589 requires integral<T>
and (L == 1) {
2590 using To = common_type_t<T, U>;
2591 if constexpr (
sizeof(
To) >= 4)
2592 return binary<To, L, To, L>(
BINIOP_(Shl,), other);
2593 else if constexpr (
sizeof(
To) == 2)
2594 return binary<To, L, To, L>(
BINOP_(Shl,, Int32), other) bitand PrimitiveExpr<To, 1>(0xffff);
2595 else if constexpr (
sizeof(
To) == 1)
2596 return binary<To, L, To, L>(
BINOP_(Shl,, Int32), other) bitand PrimitiveExpr<To, 1>(0xff);
2601 template<
integral U>
2602 requires requires (PrimitiveExpr<U, 1> e) {
2603 PrimitiveExpr<std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>, 1>(e);
2607 using Op = std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>;
2609 if constexpr (
sizeof(
T) == 8)
2610 return ::wasm::SIMDShiftOp::ShlVecI64x2;
2611 else if constexpr (
sizeof(
T) == 4)
2612 return ::wasm::SIMDShiftOp::ShlVecI32x4;
2613 else if constexpr (
sizeof(
T) == 2)
2614 return ::wasm::SIMDShiftOp::ShlVecI16x8;
2615 else if constexpr (
sizeof(
T) == 1)
2616 return ::wasm::SIMDShiftOp::ShlVecI8x16;
2629 template<
integral U>
2630 requires arithmetically_combinable<T, U, L>
2637 template<
integral U>
2638 requires requires (PrimitiveExpr<U, 1> e) {
2639 PrimitiveExpr<std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>, 1>(e);
2641 PrimitiveExpr operator>>(PrimitiveExpr<U, 1> other)
2642 requires integral<T>
and (L > 1) {
2643 using Op = std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>;
2645 if constexpr (
sizeof(T) == 8)
2646 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI64x2,
2647 ::wasm::SIMDShiftOp::ShrUVecI64x2);
2648 else if constexpr (
sizeof(T) == 4)
2649 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI32x4,
2650 ::wasm::SIMDShiftOp::ShrUVecI32x4);
2651 else if constexpr (
sizeof(T) == 2)
2652 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI16x8,
2653 ::wasm::SIMDShiftOp::ShrUVecI16x8);
2654 else if constexpr (
sizeof(T) == 1)
2655 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI8x16,
2656 ::wasm::SIMDShiftOp::ShrUVecI8x16);
2663 Module::Builder().makeSIMDShift(op, this->expr(), PrimitiveExpr<Op, 1>(other).
expr()),
2664 std::move(referenced_bits)
2669 template<
integral U>
2673 return binary<To, L, To, L>(
BINIOP_(RotL,), other);
2677 template<
integral U>
2681 return binary<To, L, To, L>(
BINIOP_(RotR,), other);
2687 template<dsl_primitive U>
2691 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2692 if constexpr (
L *
sizeof(
To) <= 16) {
2694 std::array<uint8_t, L>
indices;
2695 for (std::size_t idx = 0; idx <
L; ++idx)
2699 return this->
template to<To, L>().operator==(other.template to<To, L>());
2704 template<dsl_primitive U>
2708 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2709 if constexpr (
L *
sizeof(
To) <= 16) {
2711 std::array<uint8_t, L>
indices;
2712 for (std::size_t idx = 0; idx <
L; ++idx)
2716 return this->
template to<To, L>().operator!=(other.template to<To, L>());
2721 template<arithmetic U>
2725 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2727 if constexpr (
L == 1) {
2730 if constexpr (std::integral<To>) {
2731 if constexpr (
sizeof(
To) == 8)
2733 else if constexpr (
sizeof(
To) == 4)
2735 else if constexpr (
sizeof(
To) == 2)
2737 else if constexpr (
sizeof(
To) == 1)
2739 }
else if (std::floating_point<To>) {
2745 if constexpr (
L *
sizeof(
To) <= 16) {
2753 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2754 std::array<uint8_t, L>
indices;
2755 for (std::size_t idx = 0; idx <
L; ++idx)
2759 return this->
template to<To, L>().operator<(other.template to<To, L>());
2764 template<arithmetic U>
2768 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2770 if constexpr (
L == 1) {
2773 if constexpr (std::integral<To>) {
2774 if constexpr (
sizeof(
To) == 8)
2776 else if constexpr (
sizeof(
To) == 4)
2778 else if constexpr (
sizeof(
To) == 2)
2780 else if constexpr (
sizeof(
To) == 1)
2782 }
else if (std::floating_point<To>) {
2788 if constexpr (
L *
sizeof(
To) <= 16) {
2796 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2797 std::array<uint8_t, L>
indices;
2798 for (std::size_t idx = 0; idx <
L; ++idx)
2802 return this->
template to<To, L>().operator<=(other.template to<To, L>());
2807 template<arithmetic U>
2811 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2813 if constexpr (
L == 1) {
2816 if constexpr (std::integral<To>) {
2817 if constexpr (
sizeof(
To) == 8)
2819 else if constexpr (
sizeof(
To) == 4)
2821 else if constexpr (
sizeof(
To) == 2)
2823 else if constexpr (
sizeof(
To) == 1)
2825 }
else if (std::floating_point<To>) {
2831 if constexpr (
L *
sizeof(
To) <= 16) {
2839 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2840 std::array<uint8_t, L>
indices;
2841 for (std::size_t idx = 0; idx <
L; ++idx)
2845 return this->
template to<To, L>().operator>(other.template to<To, L>());
2850 template<arithmetic U>
2854 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2856 if constexpr (
L == 1) {
2859 if constexpr (std::integral<To>) {
2860 if constexpr (
sizeof(
To) == 8)
2862 else if constexpr (
sizeof(
To) == 4)
2864 else if constexpr (
sizeof(
To) == 2)
2866 else if constexpr (
sizeof(
To) == 1)
2868 }
else if (std::floating_point<To>) {
2874 if constexpr (
L *
sizeof(
To) <= 16) {
2882 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2883 std::array<uint8_t, L>
indices;
2884 for (std::size_t idx = 0; idx <
L; ++idx)
2888 return this->
template to<To, L>().operator>=(other.template to<To, L>());
2897 return binary<T, L, T, L>(
M_CONSTEXPR_COND(
L == 1,
BINOP_(And,,Int32),
BINVOP_(And,, 128)), other);
2903 return binary<T, L, T, L>(
BINVOP_(AndNot,, 128), other);
2909 return binary<T, L, T, L>(
M_CONSTEXPR_COND(
L == 1,
BINOP_(Or,,Int32),
BINVOP_(Or,, 128)), other);
2930 template<std::
size_t M>
2931 requires (M *
sizeof(
T) < 16)
2934 if constexpr (std::integral<T>) {
2935 if constexpr (
sizeof(
T) == 8)
2936 return ::wasm::SIMDExtractOp::ExtractLaneVecI64x2;
2937 else if constexpr (
sizeof(
T) == 4)
2938 return ::wasm::SIMDExtractOp::ExtractLaneVecI32x4;
2939 else if constexpr (
sizeof(
T) == 2)
2940 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDExtractOp::ExtractLaneSVecI16x8,
2941 ::wasm::SIMDExtractOp::ExtractLaneUVecI16x8);
2942 else if constexpr (
sizeof(
T) == 1)
2943 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDExtractOp::ExtractLaneSVecI8x16,
2944 ::wasm::SIMDExtractOp::ExtractLaneUVecI8x16);
2945 }
else if (std::floating_point<T>) {
2946 if constexpr (
sizeof(
T) == 8)
2947 return ::wasm::SIMDExtractOp::ExtractLaneVecF64x2;
2948 else if constexpr (
sizeof(
T) == 4)
2949 return ::wasm::SIMDExtractOp::ExtractLaneVecF32x4;
2954 Module::Builder().makeSIMDExtract(
op,
expr(), M),
2961 template<std::
size_t M>
2966 template<std::
size_t M, primitive_convertible U>
2967 requires (M < L)
and
2968 requires (primitive_expr_t<U> u) { PrimitiveExpr<T, 1>(u); }
2971 if constexpr (std::integral<T>) {
2972 if constexpr (
sizeof(
T) == 8)
2973 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI64x2;
2974 else if constexpr (
sizeof(
T) == 4)
2975 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI32x4;
2976 else if constexpr (
sizeof(
T) == 2)
2977 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI16x8;
2978 else if constexpr (
sizeof(
T) == 1)
2979 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI8x16;
2980 }
else if (std::floating_point<T>) {
2981 if constexpr (
sizeof(
T) == 8)
2982 return ::wasm::SIMDReplaceOp::ReplaceLaneVecF64x2;
2983 else if constexpr (
sizeof(
T) == 4)
2984 return ::wasm::SIMDReplaceOp::ReplaceLaneVecF32x4;
2994 Module::Builder().makeSIMDReplace(
op, this->expr(), M, replacement.expr()),
3005 Module::Builder().makeBinary(::wasm::BinaryOp::SwizzleVecI8x16,
3013 template<std::
size_t M>
3014 requires (M > 0)
and (M <= 16)
and (M %
sizeof(T) == 0)
3015 PrimitiveExpr<T, M / sizeof(T)> swizzle_bytes(const
std::array<uint8_t, M> &_indices) requires (L > 1) {
3016 std::array<uint8_t, 16>
indices;
3017 for (std::size_t idx = 0; idx < M; ++idx)
3018 indices[idx] = _indices[idx] <
L *
sizeof(
T) ? _indices[idx] : 16;
3021 Module::Builder().makeConst(::wasm::Literal(
indices.data()))
3025 vec.template extract_unsafe<0>(),
3031 template<std::
size_t M>
3034 std::array<uint8_t, 16>
indices;
3035 for (std::size_t idx = 0; idx < M; ++idx) {
3036 for (std::size_t
byte = 0;
byte <
sizeof(
T); ++byte)
3037 indices[idx *
sizeof(
T) + byte] = _indices[idx] <
L ? _indices[idx] *
sizeof(
T) +
byte : 16;
3041 Module::Builder().makeConst(::wasm::Literal(
indices.data()))
3045 vec.template extract_unsafe<0>(),
3055 out <<
"PrimitiveExpr<" <<
typeid(type).name() <<
"," <<
num_simd_lanes <<
">: ";
3056 if (P.expr_) out << *P.expr_;
3061 void dump(std::ostream &out)
const { out << *
this << std::endl; }
3068template<dsl_primitive T, std::
size_t L>
3083 template<
typename, std::
size_t>
friend struct PrimitiveExpr;
3084 template<
typename, std::
size_t>
3086 template<
typename, VariableKind,
bool, std::
size_t>
3089 friend struct Block;
3114 template<dsl_primitive... Us>
3115 requires (
sizeof...(Us) > 0)
and
3116 requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, num_vectors>>; }
3119 std::array<vector_type, num_vectors>
vectors;
3121 for (
auto literal : make_literal<T, L>(
value...))
3122 *(it++) =
vector_type(Module::Builder().makeConst(literal));
3124 return std::move(vectors);
3129 template<decayable... Us>
3130 requires (
sizeof...(Us) > 0)
and (dsl_primitive<std::decay_t<Us>>
and ...)
and
3131 requires (Us... us) {
PrimitiveExpr(std::decay_t<Us>(us)...); }
3150 template<dsl_primitive U, std::
size_t M>
3151 requires ((M *
sizeof(
U)) / 16 == num_vectors)
3154 std::array<ToVecT, num_vectors>
vectors;
3155 for (std::size_t idx = 0; idx <
num_vectors; ++idx)
3163 explicit operator bool()
const {
3164 return std::all_of(vectors_.cbegin(), vectors_.cend(), [](
const auto &expr){ return bool(expr); });
3168 PrimitiveExpr clone()
const {
3169 M_insist(
bool(*
this),
"cannot clone an already moved or discarded `PrimitiveExpr`");
3170 std::array<vector_type, num_vectors> vectors_cpy;
3171 for (std::size_t idx = 0; idx < num_vectors; ++idx)
3172 vectors_cpy[idx] = vectors_[idx].clone();
3173 return PrimitiveExpr(
3174 std::move(vectors_cpy)
3182 M_insist(
bool(*
this),
"cannot discard an already moved or discarded `PrimitiveExpr`");
3183 std::for_each(
vectors_.begin(),
vectors_.end(), [](
auto &expr){ expr.discard(); });
3192 template<dsl_primitive U, std::
size_t M>
3193 requires ((M *
sizeof(
U)) % 16 == 0)
3197 constexpr std::size_t FromL =
L;
3198 constexpr std::size_t
ToL = M;
3201 constexpr std::size_t FromVecL = (
L *
sizeof(
T)) / 16;
3202 constexpr std::size_t ToVecL = (M *
sizeof(
U)) / 16;
3204 if constexpr (std::same_as<From, To>
and FromL ==
ToL)
3207 sizeof(From) ==
sizeof(
To)
and FromL ==
ToL)
3212 if constexpr (FromL ==
ToL) {
3213 if constexpr (
sizeof(
To) == 1)
3215 if constexpr (std::is_signed_v<To>) {
3216 if constexpr (
sizeof(
To) == 2)
3217 return to<int8_t, ToL>().template to<To, ToL>();
3218 if constexpr (
sizeof(
To) == 4)
3219 return to<int8_t, ToL>().template to<To, ToL>();
3220 if constexpr (
sizeof(
To) == 8)
3221 return to<int8_t, ToL>().template to<To, ToL>();
3223 if constexpr (
sizeof(
To) == 2)
3224 return to<uint8_t, ToL>().template to<To, ToL>();
3225 if constexpr (
sizeof(
To) == 4)
3226 return to<uint8_t, ToL>().template to<To, ToL>();
3227 if constexpr (
sizeof(
To) == 8)
3228 return to<uint8_t, ToL>().template to<To, ToL>();
3232 if constexpr (std::floating_point<To>) {
3233 if constexpr (FromL ==
ToL) {
3234 if constexpr (
sizeof(
To) == 4)
3235 return to<uint32_t, ToL>().template convert<To, ToL>();
3236 if constexpr (
sizeof(
To) == 8)
3237 return to<uint32_t, ToL>().template convert<To, ToL>();
3244 if constexpr (FromL ==
ToL) {
3245 if constexpr (
sizeof(From) == 1)
3247 if constexpr (std::is_signed_v<From>) {
3248 if constexpr (
sizeof(From) == 2)
3249 return to<int8_t, ToL>().template to<To, ToL>();
3250 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3251 return to<int8_t, ToL>().template to<To, ToL>();
3252 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3253 return to<int8_t, ToL>().template to<To, ToL>();
3255 if constexpr (
sizeof(From) == 2)
3256 return to<uint8_t, ToL>().template to<To, ToL>();
3257 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3258 return to<uint8_t, ToL>().template to<To, ToL>();
3259 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3260 return to<uint8_t, ToL>().template to<To, ToL>();
3264 if constexpr (std::floating_point<From>) {
3265 if constexpr (FromL ==
ToL) {
3266 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3268 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3276 if constexpr (FromL ==
ToL) {
3277 if constexpr (std::is_signed_v<From>) {
3278 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2) {
3279 std::array<ToVecT, ToVecL>
vectors;
3280 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3281 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3282 ::wasm::ExtendLowSVecI8x16ToVecI16x8
3284 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3285 ::wasm::ExtendHighSVecI8x16ToVecI16x8
3290 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 1) {
3291 std::array<ToVecT, ToVecL>
vectors;
3292 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3293 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3294 ::wasm::NarrowSVecI16x8ToVecI8x16,
vectors_[2 * idx + 1]
3298 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3299 return to<int16_t, ToL>().template to<To, ToL>();
3300 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3301 return to<int16_t, ToL>().template to<To, ToL>();
3302 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3303 return to<int32_t, ToL>().template to<To, ToL>();
3304 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3305 return to<int16_t, ToL>().template to<To, ToL>();
3306 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4) {
3307 std::array<ToVecT, ToVecL>
vectors;
3308 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3309 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3310 ::wasm::ExtendLowSVecI16x8ToVecI32x4
3312 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3313 ::wasm::ExtendHighSVecI16x8ToVecI32x4
3318 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2) {
3319 std::array<ToVecT, ToVecL>
vectors;
3320 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3321 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3322 ::wasm::NarrowSVecI32x4ToVecI16x8,
vectors_[2 * idx + 1]
3326 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3327 return to<int32_t, ToL>().template to<To, ToL>();
3328 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3329 return to<int32_t, ToL>().template to<To, ToL>();
3330 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3331 std::array<ToVecT, ToVecL>
vectors;
3332 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3333 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3334 ::wasm::ExtendLowSVecI32x4ToVecI64x2
3336 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3337 ::wasm::ExtendHighSVecI32x4ToVecI64x2
3342 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3343 std::array<ToVecT, ToVecL>
vectors;
3344 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3345 std::array<uint8_t, 16>
indices =
3346 { 0, 1, 2, 3, 8, 9 , 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 };
3353 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2) {
3354 std::array<ToVecT, ToVecL>
vectors;
3355 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3356 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3357 ::wasm::ExtendLowUVecI8x16ToVecI16x8
3359 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3360 ::wasm::ExtendHighUVecI8x16ToVecI16x8
3365 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 1) {
3366 std::array<ToVecT, ToVecL>
vectors;
3367 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3368 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3369 ::wasm::NarrowSVecI16x8ToVecI8x16,
vectors_[2 * idx + 1]
3373 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3374 return to<uint16_t, ToL>().template to<To, ToL>();
3375 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3376 return to<uint16_t, ToL>().template to<To, ToL>();
3377 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3378 return to<uint32_t, ToL>().template to<To, ToL>();
3379 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3380 return to<uint16_t, ToL>().template to<To, ToL>();
3381 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4) {
3382 std::array<ToVecT, ToVecL>
vectors;
3383 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3384 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3385 ::wasm::ExtendLowUVecI16x8ToVecI32x4
3387 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3388 ::wasm::ExtendHighUVecI16x8ToVecI32x4
3393 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2) {
3394 std::array<ToVecT, ToVecL>
vectors;
3395 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3396 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3397 ::wasm::NarrowSVecI32x4ToVecI16x8,
vectors_[2 * idx + 1]
3401 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3402 return to<uint32_t, ToL>().template to<To, ToL>();
3403 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3404 return to<uint32_t, ToL>().template to<To, ToL>();
3405 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3406 std::array<ToVecT, ToVecL>
vectors;
3407 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3408 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3409 ::wasm::ExtendLowUVecI32x4ToVecI64x2
3411 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3412 ::wasm::ExtendHighUVecI32x4ToVecI64x2
3417 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3418 std::array<ToVecT, ToVecL>
vectors;
3419 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3420 std::array<uint8_t, 16>
indices =
3421 { 0, 1, 2, 3, 8, 9 , 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 };
3430 if constexpr (std::floating_point<To>) {
3431 if constexpr (FromL ==
ToL) {
3432 if constexpr (std::is_signed_v<From>) {
3433 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3434 return to<int32_t, ToL>().template to<To, ToL>();
3435 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3436 return to<int32_t, ToL>().template to<To, ToL>();
3437 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
3438 return to<int32_t, ToL>().template to<To, ToL>();
3439 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3440 return to<int32_t, ToL>().template to<To, ToL>();
3441 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3442 std::array<ToVecT, ToVecL>
vectors;
3443 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3444 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3445 ::wasm::ConvertSVecI32x4ToVecF32x4
3449 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3450 std::array<ToVecT, ToVecL>
vectors;
3451 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3452 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3453 ::wasm::ConvertLowSVecI32x4ToVecF64x2
3455 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3456 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3457 ::wasm::ConvertLowSVecI32x4ToVecF64x2
3462 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
3463 return to<int32_t, ToL>().template to<To, ToL>();
3464 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3465 return to<int32_t, ToL>().template to<To, ToL>();
3467 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3468 return to<uint32_t, ToL>().template convert<To, ToL>();
3469 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3470 return to<uint32_t, ToL>().template convert<To, ToL>();
3471 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
3472 return to<uint32_t, ToL>().template convert<To, ToL>();
3473 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3474 return to<uint32_t, ToL>().template convert<To, ToL>();
3475 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3476 std::array<ToVecT, ToVecL>
vectors;
3477 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3478 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3479 ::wasm::ConvertUVecI32x4ToVecF32x4
3483 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3484 std::array<ToVecT, ToVecL>
vectors;
3485 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3486 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3487 ::wasm::ConvertLowUVecI32x4ToVecF64x2
3489 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3490 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3491 ::wasm::ConvertLowUVecI32x4ToVecF64x2
3496 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
3497 return to<uint32_t, ToL>().template convert<To, ToL>();
3498 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3499 return to<uint32_t, ToL>().template convert<To, ToL>();
3505 if constexpr (std::floating_point<From>) {
3507 if constexpr (FromL ==
ToL) {
3508 if constexpr (std::is_signed_v<To>) {
3509 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3510 return to<int32_t, ToL>().template to<To, ToL>();
3511 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3512 return to<int32_t, ToL>().template to<To, ToL>();
3513 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2)
3514 return to<int32_t, ToL>().template to<To, ToL>();
3515 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3516 return to<int32_t, ToL>().template to<To, ToL>();
3517 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3518 std::array<ToVecT, ToVecL>
vectors;
3519 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3520 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3521 ::wasm::TruncSatSVecF32x4ToVecI32x4
3525 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3526 std::array<ToVecT, ToVecL>
vectors;
3527 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3528 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3529 ::wasm::TruncSatZeroSVecF64x2ToVecI32x4
3531 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3532 ::wasm::TruncSatZeroSVecF64x2ToVecI32x4
3538 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
3539 return to<int32_t, ToL>().template to<To, ToL>();
3540 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3541 return to<int32_t, ToL>().template to<To, ToL>();
3543 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3544 return convert<uint32_t, ToL>().template to<To, ToL>();
3545 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3546 return convert<uint32_t, ToL>().template to<To, ToL>();
3547 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2)
3548 return convert<uint32_t, ToL>().template to<To, ToL>();
3549 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3550 return convert<uint32_t, ToL>().template to<To, ToL>();
3551 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3552 std::array<ToVecT, ToVecL>
vectors;
3553 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3554 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3555 ::wasm::TruncSatUVecF32x4ToVecI32x4
3559 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3560 std::array<ToVecT, ToVecL>
vectors;
3561 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3562 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3563 ::wasm::TruncSatZeroUVecF64x2ToVecI32x4
3565 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3566 ::wasm::TruncSatZeroUVecF64x2ToVecI32x4
3572 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
3573 return convert<uint32_t, ToL>().template to<To, ToL>();
3574 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3575 return convert<uint32_t, ToL>().template to<To, ToL>();
3579 if constexpr (std::floating_point<To>) {
3580 if constexpr (FromL ==
ToL) {
3581 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3582 std::array<ToVecT, ToVecL>
vectors;
3583 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3584 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3585 ::wasm::PromoteLowVecF32x4ToVecF64x2
3587 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3588 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3589 ::wasm::PromoteLowVecF32x4ToVecF64x2
3594 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3595 std::array<ToVecT, ToVecL>
vectors;
3596 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3597 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3598 ::wasm::DemoteZeroVecF64x2ToVecF32x4
3600 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3601 ::wasm::DemoteZeroVecF64x2ToVecF32x4
3623 template<dsl_primitive To, std::
size_t ToL = L>
3624 requires (L == ToL)
and
3627 (
sizeof(
T) <=
sizeof(To))
3628 operator PrimitiveExpr<To, ToL>() {
return convert<To, ToL>(); }
3637 template<dsl_primitive To, std::
size_t ToL = L>
3642 std::is_convertible_v<T, To>
and
3668 auto OP() requires requires (vector_type v) { v.OP(); } { \
3669 using ResVecT = decltype(std::declval<vector_type>().OP()); \
3670 static_assert(ResVecT::num_simd_lanes * sizeof(typename ResVecT::type) == 16, \
3671 "result vectors must be fully utilized"); \
3672 std::array<ResVecT, num_vectors> vectors; \
3673 for (std::size_t idx = 0; idx < num_vectors; ++idx) \
3674 vectors[idx] = vectors_[idx].OP(); \
3675 return PrimitiveExpr<typename ResVecT::type, ResVecT::num_simd_lanes * num_vectors>(std::move(vectors)); \
3695 std::optional<PrimitiveExpr<uint32_t, 1>> res = vectors_[0].bitmask();
3696 for (std::size_t idx = 1; idx < num_vectors; ++idx)
3697 res.emplace((vectors_[idx].bitmask() << uint32_t(idx * vector_type::num_simd_lanes)) bitor *res);
3702 PrimitiveExpr<uint64_t, 1> bitmask()
requires (L > 32)
and (L <= 64)
and requires (vector_type v) { v.bitmask(); } {
3703 std::optional<PrimitiveExpr<uint64_t, 1>> res = vectors_[0].bitmask();
3704 for (std::size_t idx = 1; idx < num_vectors; ++idx)
3705 res.emplace((vectors_[idx].bitmask() << uint64_t(idx * vector_type::num_simd_lanes)) bitor *res);
3711 std::optional<PrimitiveExpr<bool, 1>>
res =
vectors_[0].any_true();
3712 for (std::size_t idx = 1; idx <
num_vectors; ++idx)
3713 res.emplace(vectors_[idx].any_true() or *
res);
3719 std::optional<PrimitiveExpr<bool, 1>>
res =
vectors_[0].all_true();
3720 for (std::size_t idx = 1; idx <
num_vectors; ++idx)
3721 res.emplace(vectors_[idx].all_true()
and *
res);
3731 template<dsl_primitive U> \
3732 requires arithmetically_combinable<T, U, L> and \
3733 requires (typename PrimitiveExpr<common_type_t<T, U>, L>::vector_type left, \
3734 typename PrimitiveExpr<common_type_t<T, U>, L>::vector_type right) \
3735 { left.OP(right); } \
3736 auto OP(PrimitiveExpr<U, L> other) { \
3737 using To = common_type_t<T, U>; \
3738 using OpT = decltype(to<To, L>()); \
3740 decltype(std::declval<typename OpT::vector_type>().OP(std::declval<typename OpT::vector_type>())); \
3741 static_assert(ResVecT::num_simd_lanes * sizeof(typename ResVecT::type) == 16, \
3742 "result vectors must be fully utilized"); \
3743 auto this_converted = this->template to<To, L>(); \
3744 auto other_converted = other.template to<To, L>(); \
3745 std::array<ResVecT, OpT::num_vectors> vectors; \
3746 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx) \
3747 vectors[idx] = this_converted.vectors_[idx].OP(other_converted.vectors_[idx]); \
3748 return PrimitiveExpr<typename ResVecT::type, ResVecT::num_simd_lanes * OpT::num_vectors>(std::move(vectors)); \
3767 template<dsl_primitive U> \
3768 PrimitiveExpr OP(PrimitiveExpr<U, 1> other) requires requires (vector_type v) { v.OP(other); } { \
3769 std::array<vector_type, num_vectors> vectors; \
3770 for (std::size_t idx = 0; idx < num_vectors; ++idx) \
3771 vectors[idx] = vectors_[idx].OP(other.clone()); \
3773 return PrimitiveExpr(std::move(vectors)); \
3780#define BINVOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##Vec##TYPE)
3781#define BINIVOP_(NAME, SIGN) [] { \
3782 if constexpr (sizeof(To) == 8) \
3783 return BINVOP_(NAME,SIGN,I64x2); \
3784 else if constexpr (sizeof(To) == 4) \
3785 return BINVOP_(NAME,SIGN,I32x4); \
3786 else if constexpr (sizeof(To) == 2) \
3787 return BINVOP_(NAME,SIGN,I16x8); \
3788 else if constexpr (sizeof(To) == 1) \
3789 return BINVOP_(NAME,SIGN,I8x16); \
3791 M_unreachable("unsupported operation"); \
3793#define BINFVOP_(NAME) [] { \
3794 if constexpr (sizeof(To) == 8) \
3795 return BINVOP_(NAME,,F64x2); \
3796 else if constexpr (sizeof(To) == 4) \
3797 return BINVOP_(NAME,,F32x4); \
3799 M_unreachable("unsupported operation"); \
3801#define BINARY_VOP(NAME, SIGN) [] { \
3802 if constexpr (std::integral<To>) \
3803 return BINIVOP_(NAME, SIGN); \
3804 else if constexpr (std::floating_point<To>) \
3805 return BINFVOP_(NAME); \
3807 M_unreachable("unsupported operation"); \
3813 if constexpr (
L > 16) {
3814 auto narrowed = to<uint8_t, L>();
3816 }
else if constexpr (
L == 16) {
3817 auto narrowed = to<uint8_t, L>();
3821 std::array<uint8_t, L>
indices;
3822 for (std::size_t idx = 0; idx <
L; ++idx)
3824 return cmp.swizzle_bytes(
indices);
3826 auto vectors = move<bool, L * sizeof(T)>();
3827 static_assert(
vectors.size() == 4);
3828 std::array<uint8_t,
L / 2>
indices;
3829 for (std::size_t idx = 0; idx <
L / 2; ++idx)
3833 std::array<uint8_t, L> lanes;
3834 std::iota(lanes.begin(), lanes.end(), 0);
3841 template<dsl_primitive U>
3843 PrimitiveExpr<bool, L> operator==(PrimitiveExpr<U, L> other) {
3845 using OpT =
decltype(to<To, L>());
3846 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3847 auto this_converted = this->
template to<To, L>();
3848 auto other_converted = other.template to<To, L>();
3849 std::array<PrimitiveExpr<uint_t<
sizeof(To)>, lanes>, OpT::num_vectors> vectors;
3850 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3851 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3852 BINARY_VOP(Eq,), other_converted.vectors_[idx]
3854 return PrimitiveExpr<uint_t<
sizeof(To)>, L>(std::move(vectors)).cmp_helper();
3858 template<dsl_primitive U>
3859 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3860 PrimitiveExpr<bool, L> operator!=(PrimitiveExpr<U, L> other) {
3861 using To = common_type_t<T, U>;
3862 using OpT =
decltype(to<To, L>());
3863 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3864 auto this_converted = this->
template to<To, L>();
3865 auto other_converted = other.template to<To, L>();
3866 std::array<PrimitiveExpr<uint_t<
sizeof(To)>, lanes>, OpT::num_vectors> vectors;
3867 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3868 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3869 BINARY_VOP(Ne,), other_converted.vectors_[idx]
3871 return PrimitiveExpr<uint_t<
sizeof(To)>, L>(std::move(vectors)).cmp_helper();
3875 template<arithmetic U>
3876 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3877 PrimitiveExpr<bool, L>
operator<(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3878 using To = common_type_t<T, U>;
3879 using OpT =
decltype(to<To, L>());
3880 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3882 if constexpr (std::integral<To>) {
3883 if constexpr (
sizeof(
To) == 8)
3885 else if constexpr (
sizeof(
To) == 4)
3887 else if constexpr (
sizeof(
To) == 2)
3889 else if constexpr (
sizeof(
To) == 1)
3891 }
else if (std::floating_point<To>) {
3898 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3900 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3902 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3903 auto this_converted = _this.template to<To, L>();
3904 auto other_converted = _other.template to<To, L>();
3906 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3907 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3908 op, other_converted.vectors_[idx]
3914 template<arithmetic U>
3915 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3916 PrimitiveExpr<bool, L>
operator<=(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3917 using To = common_type_t<T, U>;
3918 using OpT =
decltype(to<To, L>());
3919 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3921 if constexpr (std::integral<To>) {
3922 if constexpr (
sizeof(
To) == 8)
3924 else if constexpr (
sizeof(
To) == 4)
3926 else if constexpr (
sizeof(
To) == 2)
3928 else if constexpr (
sizeof(
To) == 1)
3930 }
else if (std::floating_point<To>) {
3937 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3939 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3941 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3942 auto this_converted = _this.template to<To, L>();
3943 auto other_converted = _other.template to<To, L>();
3945 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3946 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3947 op, other_converted.vectors_[idx]
3953 template<arithmetic U>
3954 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3955 PrimitiveExpr<bool, L>
operator>(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3956 using To = common_type_t<T, U>;
3957 using OpT =
decltype(to<To, L>());
3958 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3960 if constexpr (std::integral<To>) {
3961 if constexpr (
sizeof(
To) == 8)
3963 else if constexpr (
sizeof(
To) == 4)
3965 else if constexpr (
sizeof(
To) == 2)
3967 else if constexpr (
sizeof(
To) == 1)
3969 }
else if (std::floating_point<To>) {
3976 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3978 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3980 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3981 auto this_converted = _this.template to<To, L>();
3982 auto other_converted = _other.template to<To, L>();
3984 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3985 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3986 op, other_converted.vectors_[idx]
3992 template<arithmetic U>
3993 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3994 PrimitiveExpr<bool, L>
operator>=(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3995 using To = common_type_t<T, U>;
3996 using OpT =
decltype(to<To, L>());
3997 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3999 if constexpr (std::integral<To>) {
4000 if constexpr (
sizeof(
To) == 8)
4002 else if constexpr (
sizeof(
To) == 4)
4004 else if constexpr (
sizeof(
To) == 2)
4006 else if constexpr (
sizeof(
To) == 1)
4008 }
else if (std::floating_point<To>) {
4015 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
4017 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
4019 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
4020 auto this_converted = _this.template to<To, L>();
4021 auto other_converted = _other.template to<To, L>();
4023 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
4024 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
4025 op, other_converted.vectors_[idx]
4041 template<std::
size_t M>
4044 auto res =
vectors_[M / vector_type::num_simd_lanes].clone().template extract<M % vector_type::num_simd_lanes>();
4050 template<std::
size_t M, primitive_convertible U>
4053 static constexpr std::size_t lanes = vector_type::num_simd_lanes;
4054 vectors_[M / lanes] = vectors_[M / lanes].template replace<M % lanes>(std::forward<U>(value));
4060 template<std::
size_t M>
4061 requires (M > 0)
and (M <= 16)
and (M %
sizeof(T) == 0)
4062 PrimitiveExpr<T, M / sizeof(T)> swizzle_bytes(const
std::array<uint8_t, M> &indices) requires (num_vectors == 2) {
4063 return Module::Get().emit_shuffle_bytes(vectors_[0], vectors_[1], indices);
4068 template<std::
size_t M>
4071 return Module::Get().emit_shuffle_lanes(vectors_[0], vectors_[1], indices);
4079 friend std::ostream &
operator<<(std::ostream &out,
const PrimitiveExpr &P) {
4080 out <<
"PrimitiveExpr<" <<
typeid(
T).name() <<
"," <<
L <<
">: [";
4081 for (
auto it = P.vectors_.cbegin(); it != P.vectors_.cend(); ++it) {
4082 if (it != P.vectors_.cbegin())
4090 void dump(std::ostream &out)
const { out << *
this << std::endl; }
4091 void dump()
const {
dump(std::cerr); }
4100#define BINARY_LIST(X) \
4106 X(operator bitand) \
4128#define MAKE_BINARY(OP) \
4129 template<primitive_convertible T, primitive_convertible U> \
4130 requires requires (primitive_expr_t<T> t, primitive_expr_t<U> u) { t.OP(u); } \
4131 auto OP(T &&t, U &&u) \
4133 return primitive_expr_t<T>(std::forward<T>(t)).OP(primitive_expr_t<U>(std::forward<U>(u))); \
4140template<dsl_po
inter_to_primitive T, std::
size_t L>
4143struct PrimitiveExpr<T, L>
4146 static constexpr std::size_t num_simd_lanes = L;
4147 using pointed_type = std::decay_t<std::remove_pointer_t<T>>;
4148 using offset_t = int32_t;
4151 template<
typename, std::
size_t>
friend struct PrimitiveExpr;
4152 template<
typename, VariableKind,
bool, std::
size_t>
4153 friend class detail::variable_storage;
4154 friend struct Module;
4155 template<
typename>
friend struct FunctionProxy;
4156 template<dsl_primitive, std::
size_t,
bool>
friend struct detail::the_reference;
4157 template<
typename>
friend struct invoke_interpreter;
4160 PrimitiveExpr<uint32_t, 1> addr_;
4161 offset_t offset_ = 0;
4165 explicit PrimitiveExpr(PrimitiveExpr<uint32_t, 1> addr, offset_t offset = 0) : addr_(addr), offset_(offset) { }
4170 explicit PrimitiveExpr(::wasm::Expression *addr, std::list<std::shared_ptr<Bit>> referenced_bits = {},
4171 offset_t offset = 0)
4172 : addr_(addr, std::move(referenced_bits))
4177 explicit PrimitiveExpr(std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>> addr, offset_t offset = 0)
4184 , offset_([&raw_ptr](){
4185 auto &memory = Module::Memory();
4186 const auto offset =
reinterpret_cast<uint8_t*
>(raw_ptr) -
static_cast<uint8_t*
>(memory.addr());
4187 M_insist(offset >= 0
and offset < memory.size(),
"invalid raw pointer");
4194 PrimitiveExpr(PrimitiveExpr &other) : addr_(other.addr_), offset_(other.offset_) { }
4197 PrimitiveExpr(PrimitiveExpr &&other) : addr_(other.addr_), offset_(other.offset_) { }
4207 ::wasm::Expression *
expr() {
return to<uint32_t>().expr(); }
4209 std::list<std::shared_ptr<Bit>>
referenced_bits() {
return addr_.referenced_bits(); }
4211 std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>>
move() {
return addr_.move(); }
4216 explicit operator bool()
const {
return bool(addr_); }
4224 void discard() { addr_.discard(); }
4234 template<dsl_po
inter_to_primitive To, std::
size_t ToL = L>
4235 requires (not std::is_void_v<std::remove_pointer_t<To>>)
4236 PrimitiveExpr<To, ToL>
to()
requires std::is_void_v<pointed_type>
and (L == 1) {
4237 Wasm_insist((
clone().
template to<uint32_t>() % uint32_t(
alignof(std::remove_pointer_t<To>))).
eqz(),
4238 "cannot convert to type whose alignment requirement is not fulfilled");
4239 return PrimitiveExpr<To, ToL>(addr_.move(), offset_);
4244 template<
typename To, std::
size_t ToL = 1>
4245 requires std::same_as<To, uint32_t>
and (ToL == 1)
4246 PrimitiveExpr<uint32_t, 1> to() {
4247 return offset_ ? (offset_ > 0 ? addr_ + uint32_t(offset_) : addr_ - uint32_t(-offset_)) : addr_;
4251 template<
typename To, std::
size_t ToL = 1>
4252 requires (not std::same_as<To, T>)
and std::same_as<To, void*>
and (ToL == 1)
4253 PrimitiveExpr<void*, 1>
to() {
return PrimitiveExpr<void*, 1>(addr_.move(), offset_); }
4257 template<
typename To, std::
size_t ToL = L>
4258 requires std::same_as<To, T>
and (L == ToL)
4266 PrimitiveExpr<uint64_t, L>
hash() {
return to<uint32_t>().hash(); }
4275 PrimitiveExpr<bool, 1> is_nullptr() {
return to<uint32_t>() == 0
U; }
4280 PrimitiveExpr<bool, 1>
is_null() {
4282 return to<uint32_t>() == 0
U;
4288 PrimitiveExpr<bool, 1>
not_null() {
4290 return to<uint32_t>() != 0
U;
4294 std::pair<PrimitiveExpr, PrimitiveExpr<bool, 1>> split() {
auto cpy =
clone();
return { cpy, is_nullptr() }; }
4297 auto operator*()
requires dsl_primitive<pointed_type> {
4299 return Reference<pointed_type, L>(*
this);
4303 auto operator*() const requires dsl_primitive<pointed_type> {
4305 return ConstReference<pointed_type, L>(*
this);
4309 PrimitiveExpr<pointed_type, L> operator->() const requires dsl_primitive<pointed_type> {
4320 if constexpr (std::is_void_v<pointed_type>) {
4321 return PrimitiveExpr(addr_ + delta.make_unsigned(), offset_);
4323 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4324 return PrimitiveExpr(addr_ + (delta.make_unsigned() << log_size), offset_);
4330 if constexpr (std::is_void_v<pointed_type>) {
4333 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4334 offset_ += delta << log_size;
4341 if constexpr (std::is_void_v<pointed_type>) {
4342 return PrimitiveExpr(addr_ - delta.make_unsigned(), offset_);
4344 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4345 return PrimitiveExpr(addr_ - (delta.make_unsigned() << log_size), offset_);
4351 if constexpr (std::is_void_v<pointed_type>) {
4354 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4355 offset_ -= delta << log_size;
4361 PrimitiveExpr<offset_t, 1>
operator-(PrimitiveExpr other) {
4362 if constexpr (std::is_void_v<pointed_type>) {
4363 PrimitiveExpr<offset_t, 1> delta_addr = (this->addr_ - other.addr_).
make_signed();
4364 offset_t delta_offset = this->offset_ - other.offset_;
4365 return (delta_offset ? (delta_addr + delta_offset) : delta_addr);
4367 const int32_t log_size = std::countr_zero(
sizeof(pointed_type));
4368 PrimitiveExpr<offset_t, 1> delta_addr = (this->addr_ - other.addr_).
make_signed() >> log_size;
4369 offset_t delta_offset = (this->offset_ - other.offset_) >> log_size;
4370 return (delta_offset ? (delta_addr + delta_offset) : delta_addr);
4375#define CMP_OP(SYMBOL) \
4377 PrimitiveExpr<bool, 1> operator SYMBOL(PrimitiveExpr other) { \
4378 return this->to<uint32_t>() SYMBOL other.to<uint32_t>(); \
4394 PrimitiveExpr<pointed_type, L> load()
requires dsl_primitive<pointed_type> {
4395 M_insist(
bool(addr_),
"address already moved or discarded");
4396 if constexpr (
L *
sizeof(pointed_type) <= 16) {
4397 auto value = Module::Builder().makeLoad(
4399 std::is_signed_v<pointed_type>,
4400 offset_ >= 0 ? offset_ : 0,
4401 alignof(pointed_type),
4402 offset_ >= 0 ? addr_.expr() : (addr_ - uint32_t(-offset_)).
expr(),
4403 wasm_type<pointed_type, L>(),
4404 Module::Get().memory_->name
4406 return PrimitiveExpr<pointed_type, L>(value, addr_.referenced_bits());
4408 using ResT = PrimitiveExpr<pointed_type, L>;
4409 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
4410 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
4411 auto addr_cpy = addr_.clone();
4412 auto offset = offset_ + offset_t(idx * 16);
4413 auto value = Module::Builder().makeLoad(
4415 std::is_signed_v<pointed_type>,
4416 offset >= 0 ? offset : 0,
4417 alignof(pointed_type),
4418 offset >= 0 ? addr_cpy.expr() : (addr_cpy - uint32_t(-offset)).
expr(),
4419 ::wasm::Type(::wasm::Type::v128),
4420 Module::Get().memory_->name
4422 vectors[idx] =
typename ResT::vector_type(value, addr_cpy.referenced_bits());
4425 return ResT(std::move(vectors));
4429 void store(PrimitiveExpr<pointed_type, L> value)
requires dsl_primitive<pointed_type> {
4430 M_insist(
bool(addr_),
"address already moved or discarded");
4431 M_insist(
bool(value),
"value already moved or discarded");
4432 if constexpr (
L *
sizeof(pointed_type) <= 16) {
4433 auto e = Module::Builder().makeStore(
4435 offset_ >= 0 ? offset_ : 0,
4436 alignof(pointed_type),
4437 offset_ >= 0 ? addr_.expr() : (addr_ - uint32_t(-offset_)).
expr(),
4439 wasm_type<pointed_type, L>(),
4440 Module::Get().memory_->name
4442 Module::Block().list.push_back(e);
4445 for (std::size_t idx = 0; idx < PrimitiveExpr<pointed_type, L>::num_vectors; ++idx) {
4446 auto addr_cpy = addr_.clone();
4447 auto offset = offset_ + offset_t(idx * 16);
4448 auto e = Module::Builder().makeStore(
4450 offset >= 0 ? offset : 0,
4451 alignof(pointed_type),
4452 offset >= 0 ? addr_cpy.expr() : (addr_cpy - uint32_t(-offset)).
expr(),
4453 vectors[idx].
expr(),
4454 ::wasm::Type(::wasm::Type::v128),
4455 Module::Get().memory_->name
4457 Module::Block().list.push_back(e);
4469 friend std::ostream &
operator<<(std::ostream &out,
const PrimitiveExpr &P) {
4470 out <<
"PrimitiveExpr<" <<
typeid(
T).name() <<
"*, " <<
L <<
">: " << P.addr_ <<
" [" << P.offset_ <<
"]";
4474 void dump(std::ostream &out)
const { out << *
this << std::endl; }
4475 void dump()
const {
dump(std::cerr); }
4489template<
typename T, std::
size_t L>
4512template<dsl_primitive T, std::
size_t L>
4521 template<
typename, std::
size_t>
friend struct Expr;
4534 M_insist(
bool(value_),
"value must be present");
4542 M_insist(
bool(value_),
"value must be present");
4562 requires (Us... us) {
Expr(std::decay_t<Us>(us)...); }
4576 M_insist(not
bool(value_),
"value must be used or explicitly discarded");
4577 M_insist(not
bool(is_null_),
"NULL flag must be used or explicitly discarded");
4585 M_insist(
bool(value_),
"`Expr` has already been moved");
4586 return { value_, is_null_ };
4592 explicit operator bool()
const {
return bool(value_); }
4596 M_insist(
bool(value_),
"`Expr` has already been moved");
4605 M_insist(
bool(value_),
"`Expr` has already been moved");
4615 M_insist(
bool(value_),
"`Expr` has already been moved`");
4654 return not is_null_;
4664 return value_
and not is_null_;
4673 return not value_
and not is_null_;
4695 template<dsl_primitive To, std::
size_t ToL = L>
4701 template<dsl_primitive To, std::
size_t ToL = L>
4702 requires requires { value_.template to<To, ToL>(); }
4707 template<dsl_primitive To, std::
size_t ToL = L>
4708 requires requires { value_.template reinterpret<To, ToL>(); }
4713 template<std::
size_t ToL>
4714 requires requires { value_.template broadcast<ToL>(); is_null_.template broadcast<ToL>(); }
4716 return Expr<T, ToL>(value_.template broadcast<ToL>(), is_null_.template broadcast<ToL>());
4726#define UNARY_LIST(X) \
4745 auto OP() requires requires { value_.OP(); } { \
4746 using PrimExprT = decltype(value_.OP()); \
4747 using ExprT = expr_t<PrimExprT>; \
4748 return ExprT(value_.OP(), is_null_); \
4755 auto add_pairwise()
requires requires { value_.add_pairwise(); } {
4756 using PrimExprT =
decltype(value_.add_pairwise());
4759 return ExprT(value_.add_pairwise(), is_null_.template to<uint8_t>().add_pairwise().template to<bool>());
4761 return ExprT(value_.add_pairwise());
4777 return Expr<bool, 1>(value_.any_true(), is_null_.any_true());
4784 return Expr<bool, 1>(value_.all_true(), is_null_.any_true());
4795 return value_.hash();
4805 template<dsl_primitive U> \
4806 auto OP(Expr<U, L> other) requires requires { this->value_.OP(other.value_); } { \
4807 const unsigned idx = (other.can_be_null() << 1U) | this->can_be_null(); \
4808 auto result = this->value_.OP(other.value_); \
4809 using ReturnType = typename decltype(result)::type; \
4810 constexpr std::size_t ReturnLength = decltype(result)::num_simd_lanes; \
4812 default: M_unreachable("invalid index"); \
4814 return Expr<ReturnType, ReturnLength>(result); \
4816 return Expr<ReturnType, ReturnLength>(result, this->is_null_); \
4818 return Expr<ReturnType, ReturnLength>(result, other.is_null_); \
4820 return Expr<ReturnType, ReturnLength>(result, this->is_null_ or other.is_null_); \
4850 template<dsl_primitive U>
4852 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4858 return Expr(result);
4861 return Expr(result, this->is_null_);
4864 PrimitiveExpr<bool, L> is_null = other.is_null_.template broadcast<L>();
4865 return Expr(result, is_null);
4868 PrimitiveExpr<bool, L>
is_null = this->is_null_ or other.is_null_.template broadcast<L>();
4869 return Expr(result, is_null);
4874 template<dsl_primitive U>
4875 Expr operator>>(Expr<U, 1> other)
requires requires { this->value_ >> other.value_; }
and (L > 1) {
4876 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4882 return Expr(result);
4885 return Expr(result, this->is_null_);
4888 PrimitiveExpr<bool, L>
is_null = other.is_null_.template broadcast<L>();
4889 return Expr(result, is_null);
4892 PrimitiveExpr<bool, L>
is_null = this->is_null_ or other.is_null_.template broadcast<L>();
4893 return Expr(result, is_null);
4903 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4912 PrimitiveExpr<bool, L> result = this->value_
and other.value_.clone();
4913 PrimitiveExpr<bool, L> is_null =
4916 return Expr<bool, L>(result, is_null);
4919 PrimitiveExpr<bool, L> result = this->value_.clone()
and other.value_;
4920 PrimitiveExpr<bool, L> is_null =
4923 return Expr<bool, L>(result, is_null);
4926 auto this_is_null = this->is_null_.clone();
4927 auto other_is_null = other.is_null_.clone();
4928 PrimitiveExpr<bool, L> result = this->value_.clone()
and other.value_.clone();
4929 PrimitiveExpr<bool, L>
is_null =
4930 (this_is_null or other_is_null)
and
4931 (this->value_ or this->is_null_)
and
4932 (other.value_ or other.is_null_);
4933 return Expr<bool, L>(result, is_null);
4941 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4950 PrimitiveExpr<bool, L> result = this->value_.and_not(other.value_.clone());
4951 PrimitiveExpr<bool, L> is_null =
4952 this->is_null_.and_not(
4955 return Expr<bool, L>(result, is_null);
4958 PrimitiveExpr<bool, L> result = this->value_.clone().and_not(other.value_);
4959 PrimitiveExpr<bool, L> is_null =
4962 return Expr<bool, L>(result, is_null);
4965 auto this_is_null = this->is_null_.clone();
4966 auto other_is_null = other.is_null_.clone();
4967 PrimitiveExpr<bool, L> result = this->value_.clone().and_not(other.value_.clone());
4968 PrimitiveExpr<bool, L>
is_null =
4969 (this_is_null or other_is_null)
and
4970 (this->value_ or this->is_null_)
and
4971 (not other.value_ or other.is_null_);
4972 return Expr<bool, L>(result, is_null);
4980 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4989 PrimitiveExpr<bool, L> result = this->value_ or other.value_.clone();
4990 PrimitiveExpr<bool, L> is_null =
4993 return Expr<bool, L>(result, is_null);
4996 PrimitiveExpr<bool, L> result = this->value_.clone() or other.value_;
4997 PrimitiveExpr<bool, L> is_null =
5000 return Expr<bool, L>(result, is_null);
5003 auto this_is_null = this->is_null_.clone();
5004 auto other_is_null = other.is_null_.clone();
5005 PrimitiveExpr<bool, L> result = this->value_.clone() or other.value_.clone();
5006 PrimitiveExpr<bool, L>
is_null =
5007 (this_is_null or other_is_null)
and
5008 (not this->value_ or this->is_null_)
and
5009 (not other.value_ or other.is_null_);
5010 return Expr<bool, L>(result, is_null);
5021 template<std::
size_t M>
5024 return Expr<T, 1>(value_.template extract<M>(), is_null_.template extract<M>());
5026 return Expr<T, 1>(value_.template extract<M>());
5030 template<std::
size_t M, expr_convertible U>
5031 requires requires (expr_t<U> u) { Expr<T, 1>(u); }
5035 auto [value_val, value_is_null] =
value.split();
5036 return Expr(value_.template replace<M>(value_val), is_null_.template replace<M>(value_is_null));
5038 M_insist(not value.can_be_null(),
"cannot replace a non-nullable value with a nullable one");
5039 return Expr(value_.template replace<M>(value.value_));
5046 requires (
sizeof(
T) == 1)
and requires { value_.swizzle_bytes(
indices); } {
5048 auto indices_cpy =
indices.clone();
5049 return Expr(value_.swizzle_bytes(indices), is_null_.swizzle_bytes(indices_cpy));
5051 return Expr(value_.swizzle_bytes(indices));
5056 template<std::
size_t M>
5058 requires (
sizeof(
T) == 1)
and requires { value_.swizzle_bytes(indices); } {
5060 return Expr<T, M /
sizeof(T)>(value_.swizzle_bytes(indices), is_null_.swizzle_bytes(indices));
5062 return Expr<
T, M /
sizeof(
T)>(value_.swizzle_bytes(indices));
5067 template<std::
size_t M>
5068 Expr<T, M>
swizzle_lanes(
const std::array<uint8_t, M> &indices)
5069 requires requires { value_.swizzle_lanes(indices); } {
5071 return Expr<T, M>(value_.swizzle_lanes(indices), is_null_.swizzle_lanes(indices));
5073 return Expr<T, M>(value_.swizzle_lanes(indices));
5083 friend std::ostream &
operator<<(std::ostream &out,
const Expr &E) {
5084 out <<
"Expr<" <<
typeid(type).name() <<
"," <<
num_simd_lanes <<
">: value_=" << E.value_
5085 <<
", is_null_=" << E.is_null_;
5089 void dump(std::ostream &out)
const { out << *
this << std::endl; }
5090 void dump()
const {
dump(std::cerr); }
5094template<
typename T, std::
size_t L>
5098#define MAKE_BINARY(OP) \
5099 template<expr_convertible T, expr_convertible U> \
5100 requires (not primitive_convertible<T> or not primitive_convertible<U>) and \
5101 requires (expr_t<T> t, expr_t<U> u) { t.OP(u); } \
5102 auto OP(T &&t, U &&u) \
5104 return expr_t<T>(std::forward<T>(t)).OP(expr_t<U>(std::forward<U>(u))); \
5110#define USING_N(TYPE, LENGTH, NAME) \
5111 using NAME = PrimitiveExpr<TYPE, LENGTH>; \
5112 using _ ## NAME = Expr<TYPE, LENGTH>;
5113#define USING(TYPE, NAME) \
5114 template<std::size_t L> using NAME = PrimitiveExpr<TYPE, L>; \
5115 template<std::size_t L> using _ ## NAME = Expr<TYPE, L>; \
5116 USING_N(TYPE, 1, NAME ## x1) \
5117 USING_N(TYPE, 2, NAME ## x2) \
5118 USING_N(TYPE, 4, NAME ## x4) \
5119 USING_N(TYPE, 8, NAME ## x8) \
5120 USING_N(TYPE, 16, NAME ## x16) \
5121 USING_N(TYPE, 32, NAME ## x32)
5127 USING(uint16_t, U16)
5129 USING(uint32_t, U32)
5131 USING(uint64_t, U64)
5133 USING(
double, Double)
5149template<dsl_primitive T, std::
size_t L>
5150::wasm::Index allocate_local()
5152 ::wasm::Function &fn = Module::Function();
5153 const ::wasm::Index index = fn.getNumParams() + fn.vars.size();
5154 const ::wasm::Type type = wasm_type<T, L>();
5155 fn.vars.emplace_back(type);
5157 M_insist(fn.getLocalType(index) == type);
5164template<
typename T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5165class variable_storage;
5168template<dsl_primitive T, VariableKind Kind, std::
size_t L>
5170class variable_storage<
T, Kind, false,
L>
5173 static constexpr std::size_t num_locals = ((
L *
sizeof(
T)) + 15) / 16;
5174 static_assert(Kind != VariableKind::Param or num_locals == 1,
"parameters must fit in a single Wasm local");
5176 template<
typename, VariableKind,
bool, std::
size_t>
5177 friend class variable_storage;
5180 std::array<::wasm::Index, num_locals> indices_;
5186 std::array<::wasm::Index, num_locals>
indices;
5187 for (std::size_t idx = 0; idx < num_locals; ++idx)
5188 indices[idx] = allocate_local<T, L>();
5191 , type_(wasm_type<T, L>())
5194 variable_storage(
const variable_storage&) =
delete;
5195 variable_storage(variable_storage&&) =
default;
5196 variable_storage &
operator=(variable_storage&&) =
default;
5199 variable_storage(::wasm::Index idx,
tag<int>)
5200 requires (num_locals == 1)
5201 : indices_(std::to_array({ idx })), type_(wasm_type<T, L>())
5204 ::wasm::Function &fn = Module::Function();
5206 M_insist(fn.getLocalType(indices_[0]) == type_);
5211 template<
typename... Us>
5216 template<primitive_convertible U>
5218 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5221 template<primitive_convertible U>
5223 void operator=(
U &&_value)
requires (num_locals == 1) {
5225 Module::Block().list.push_back(Module::Builder().makeLocalSet(indices_[0],
value.expr()));
5228 template<primitive_convertible U>
5230 void operator=(
U &&_value)
requires (num_locals > 1) {
5232 static_assert(num_locals ==
decltype(
value)::num_vectors);
5234 for (std::size_t idx = 0; idx < num_locals; ++idx)
5235 Module::Block().list.push_back(Module::Builder().makeLocalSet(indices_[idx],
vectors[idx].
expr()));
5245 std::array<typename PrimitiveExpr<T, L>::vector_type, num_locals>
vectors;
5246 for (std::size_t idx = 0; idx < num_locals; ++idx)
5248 Module::Builder().makeLocalGet(indices_[idx], type_)
5255template<std::
size_t L>
5259 static constexpr std::size_t bit_num_simd_lanes = std::min<std::size_t>(
L, 16);
5261 static constexpr std::size_t num_locals = (
L + 15) / 16;
5263 template<
typename, VariableKind,
bool, std::
size_t>
5264 friend class variable_storage;
5268 std::array<std::shared_ptr<LocalBit<bit_num_simd_lanes>>, num_locals> values_;
5273 variable_storage(const variable_storage&) = delete;
5274 variable_storage(variable_storage&&) = default;
5275 variable_storage & operator=(variable_storage&&) = default;
5278 template<typename... Us>
5283 template<primitive_convertible U>
5285 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5288 template<primitive_convertible U>
5303template<dsl_primitive T, std::
size_t L>
5308 variable_storage<T, VariableKind::Local, false, L> value_;
5309 variable_storage<bool, VariableKind::Local, false, L> is_null_;
5315 template<
typename... Us>
5316 requires (
sizeof...(Us) > 0)
and requires (Us... us) {
Expr<T, L>(us...); }
5320 template<expr_convertible U>
5322 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5325 template<expr_convertible U>
5339 is_null_.set_false();
5347 is_null_.set_false();
5351 void set_null() { is_null_.set_true(); }
5358template<dsl_po
inter_to_primitive T, VariableKind Kind, std::
size_t L>
5359requires (Kind != VariableKind::Global)
5360class variable_storage<T, Kind, /* CanBeNull= */ false, L>
5365 variable_storage<uint32_t, Kind, false, 1> addr_;
5368 variable_storage() =
default;
5371 explicit variable_storage(::wasm::Index idx,
tag<int> tag) : addr_(idx,
tag) { }
5374 template<primitive_convertible U>
5376 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5379 template<primitive_convertible U>
5391template<
typename T, std::
size_t L>
5396 static constexpr std::size_t num_globals =
dsl_primitive<T> ? ((
L *
sizeof(
T)) + 15) / 16 : 1;
5400 std::array<::wasm::Name, num_globals> names_;
5406 : variable_storage(T())
5409 variable_storage(
const variable_storage&) =
delete;
5410 variable_storage(variable_storage&&) =
default;
5411 variable_storage &
operator=(variable_storage&&) =
default;
5414 template<
typename... Us>
5415 requires (
sizeof...(Us) > 0)
and requires (Us... us) { Module::Get().emit_global<
T,
L>(names_,
true, us...); }
5416 explicit variable_storage(Us...
init)
5419 std::array<::wasm::Name, num_globals> names;
5420 for (std::size_t idx = 0; idx < num_globals; ++idx)
5421 names[idx] = Module::Unique_Global_Name();
5424 , type_(wasm_type<T, L>())
5426 Module::Get().emit_global<
T,
L>(names_,
true,
init...);
5429 explicit variable_storage(uint32_t
init = 0)
5431 : names_(std::to_array<::wasm::Name>({ Module::Unique_Global_Name() }))
5432 , type_(wasm_type<T, L>())
5434 Module::Get().emit_global<
T,
L>(names_[0],
true,
init);
5439 requires (
sizeof...(Us) > 0)
and requires (Us... us) { make_literal<T, L>(us...); }
5441 Module::Get().module_.getGlobal(names_[0])->init = Module::Builder().makeConst(make_literal<T, L>(
init...));
5445 requires (
sizeof...(Us) > 0)
and
5446 requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, num_globals>>; }
5448 auto literals = make_literal<T, L>(
init...);
5449 for (std::size_t idx = 0; idx < num_globals; ++idx)
5450 Module::Get().module_.getGlobal(names_[idx])->init = Module::Builder().makeConst(literals[idx]);
5454 Module::Get().module_.getGlobal(names_[0])->init = Module::Builder().makeConst(::wasm::Literal(
init));
5457 template<primitive_convertible U>
5459 void init(
U &&_init)
requires (num_globals == 1) {
5461 Module::Get().module_.getGlobal(names_[0])->init =
init.expr();
5465 template<primitive_convertible U>
5467 void operator=(
U &&_value)
requires (num_globals == 1) {
5469 Module::Block().list.push_back(Module::Builder().makeGlobalSet(names_[0],
value.expr()));
5472 template<primitive_convertible U>
5474 void operator=(
U &&_value)
requires (num_globals > 1) {
5476 static_assert(num_globals ==
decltype(
value)::num_vectors);
5478 for (std::size_t idx = 0; idx < num_globals; ++idx)
5479 Module::Block().list.push_back(Module::Builder().makeGlobalSet(names_[idx],
vectors[idx].
expr()));
5489 std::array<typename PrimitiveExpr<T, L>::vector_type, num_globals>
vectors;
5490 for (std::size_t idx = 0; idx < num_globals; ++idx)
5492 Module::Builder().makeGlobalGet(names_[idx], type_)
5500template<
typename T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5502 (not (Kind == VariableKind::Global
and CanBeNull))
and
5508 template<
typename X>
5517#ifdef M_ENABLE_SANITY_FIELDS
5519 mutable bool used_ =
false;
5520#define REGISTER_USE(VAR) (VAR).used_ = true
5522#define REGISTER_USE(VAR)
5528 swap(first.storage_, second.storage_);
5529#ifdef M_ENABLE_SANITY_FIELDS
5530 swap(first.used_, second.used_);
5540#ifdef M_ENABLE_SANITY_FIELDS
5541 , used_(other.used_)
5551#ifdef M_ENABLE_SANITY_FIELDS
5552 M_insist(used_,
"variable must be used at least once");
5557 template<
typename... Us>
5558 requires requires (Us&&... us) { storage_type(std::forward<Us>(us)...); }
5565 requires (Kind == VariableKind::Param)
5566 : storage_(idx,
tag)
5575 if constexpr (CanBeNull)
5590 template<
typename U>
5591 requires requires (dependent_expr_type v) { dependent_expr_t<U>(v); }
5594 template<
typename To, std::
size_t ToL = L>
5595 requires requires (dependent_expr_type v) { v.template to<To, ToL>(); }
5598 template<
typename To, std::
size_t ToL = L>
5599 requires requires (dependent_expr_type v) { v.template reinterpret<To, ToL>(); }
5604 template<std::
size_t ToL>
5605 requires requires (dependent_expr_type v) { v.template broadcast<ToL>(); }
5610 template<
typename... Us>
5611 requires requires (Us&&... us) { storage_.init(std::forward<Us>(us)...); }
5612 void init(Us&&... init) { storage_.init(std::forward<Us>(
init)...); }
5614 template<
typename U>
5615 requires requires (
U &&u) { storage_ = std::forward<U>(u); }
5619 requires requires (
storage_type s) { { s.set_true() } -> std::same_as<void>; }
5621 storage_.set_true();
5625 requires requires (
storage_type s) { { s.set_false() } -> std::same_as<void>; }
5627 storage_.set_false();
5631 requires requires (
storage_type s) { { s.set_null() } -> std::same_as<void>; }
5633 storage_.set_null();
5643 auto OP() const requires requires (dependent_expr_type e) { e.OP(); } { return dependent_expr_type(*this).OP(); }
5656 UNARY(is_true_and_not_null)
5657 UNARY(is_false_and_not_null)
5661#define ASSIGNOP_LIST(X) \
5673#define ASSIGNOP(SYMBOL) \
5674 template<typename U> \
5675 requires requires { typename dependent_expr_t<U>; } and \
5676 requires (U &&u) { dependent_expr_t<U>(std::forward<U>(u)); } and \
5677 requires (dependent_expr_type var_value, dependent_expr_t<U> other_value) \
5678 { var_value SYMBOL other_value; } and \
5679 requires (Variable var, \
5680 decltype(std::declval<dependent_expr_type>() SYMBOL std::declval<dependent_expr_t<U>>()) value) \
5682 Variable & operator SYMBOL##= (U &&value) { \
5683 dependent_expr_t<U> _value(std::forward<U>(value)); \
5684 this->operator=(dependent_expr_type(*this) SYMBOL _value); \
5692 template<std::
size_t M>
5694 return dependent_expr_type(*this).template extract<M>();
5698 template<std::
size_t M,
typename U>
5701 this->
operator=(dependent_expr_type(*this).template replace<M>(std::forward<U>(value)));
5709 return dependent_expr_type(*this).swizzle_bytes(indices);
5714 template<std::
size_t M>
5717 return dependent_expr_type(*this).swizzle_lanes(indices);
5724template<dsl_po
inter_to_primitive T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5725requires requires (
const Variable<T, Kind, CanBeNull, L> &var,
typename PrimitiveExpr<T, L>::offset_t delta)
5726 { var.val().operator+(delta); }
5729 return var.val().operator+(delta);
5732template<dsl_po
inter_to_primitive T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5733requires requires (
const Variable<T, Kind, CanBeNull, L> &var,
typename PrimitiveExpr<T, L>::offset_t delta)
5734 { var.val().operator-(delta); }
5737 return var.val().operator-(delta);
5746template<
typename T, std::
size_t L>
5750template<
typename T, std::
size_t L>
5758template<
typename T, std::
size_t L>
5762template<
typename T, std::
size_t L>
5770template<
typename T, std::
size_t L>
5799template<
typename T, std::
size_t L>
5800requires (
L *
sizeof(
T) <= 16)
5807 using base_type::operator=;
5817 ::wasm::Function &fn = Module::Function();
5818 M_insist(index < fn.getNumLocals(),
"index out of bounds");
5819 M_insist(fn.isParam(index),
"not a parameter");
5820 M_insist(fn.getLocalType(index) == (wasm_type<T, L>()),
"type mismatch");
5831template<dsl_primitive T, std::
size_t L,
bool IsConst>
5835 friend struct Variable<
T*, VariableKind::Local,
false,
L>;
5836 friend struct Variable<
T*, VariableKind::Global,
false,
L>;
5837 friend struct Variable<
T*, VariableKind::Param,
false,
L>;
5839 static constexpr bool is_const = IsConst;
5848 M_insist(
bool(ptr_),
"must not be moved or discarded");
5852 template<
typename U>
5862#define ASSIGNOP(SYMBOL) \
5863 template<typename U> \
5864 requires requires (the_reference ref, U &&u) { ref SYMBOL std::forward<U>(u); } and \
5865 requires (the_reference ref, decltype(ref SYMBOL std::declval<U>()) value) \
5867 void operator SYMBOL##= (U &&value) { \
5868 this->operator=(the_reference(ptr_.clone()) SYMBOL std::forward<U>(value)); \
5904 bitmask_per_offset.fill(uint16_t(-1U));
5917template<std::
size_t L>
5937 : bitvector_(&bitvector)
5938 , bit_offset_(bit_offset)
5939 , starting_lane_(starting_lane)
5941 M_insist(bit_offset_ < 8,
"offset out of bounds");
5942 M_insist(starting_lane_ +
L <= 16,
"starting lane out of bounds");
5954 swap(first.bitmap_, second.bitmap_);
5965 M_insist(bit_offset_ < CHAR_BIT *
sizeof(uint64_t),
"offset out of bounds");
5977template<std::
size_t L>
5978requires (L > 0)
and (L <= 16)
5979struct LocalBit<L> : Bit
5981 friend void swap(LocalBit &first, LocalBit &second) {
5983 swap(first.storage_, second.storage_);
5986 friend struct Module;
5989 using storage_type = detail::local_bit_storage<L>;
5990 storage_type storage_;
5992 LocalBit() =
default;
5994 template<
typename... Us>
5995 requires requires (Us&&... us) { storage_type(std::forward<Us>(us)...); }
5996 LocalBit(Us&&... us) : storage_(
std::forward<Us>(us)...) { }
5999 LocalBit(
const LocalBit&) =
delete;
6004 if constexpr (
L == 1) {
6005 if (storage_.bitmap_) {
6006 M_insist((storage_.bitmap_->bitmask bitand
mask()) == 0,
"bit must still be allocated");
6008 if (storage_.bitmap_->bitmask == 0)
6009 Module::Get().local_bitmaps_stack_.back().emplace_back(storage_.bitmap_);
6011 storage_.bitmap_->bitmask |=
mask();
6014 if (storage_.bitvector_) {
6015 constexpr uint16_t MASK = (1U <<
L) - 1U;
6016 M_insist((storage_.bitvector_->bitmask_per_offset[offset()] bitand (MASK << starting_lane())) == 0,
6017 "bits must still be allocated");
6019 const auto &bitmasks = storage_.bitvector_->bitmask_per_offset;
6021 if (std::all_of(bitmasks.cbegin(), bitmasks.cend(), pred))
6022 Module::Get().local_bitvectors_stack_.back().emplace_back(storage_.bitvector_);
6024 storage_.bitvector_->bitmask_per_offset[offset()] |= MASK << starting_lane();
6033 std::conditional_t<L == 1, uint64_t, uint8_t> offset()
const {
return storage_.bit_offset_; }
6035 uint64_t
mask() const requires (L == 1) {
return 1UL << offset(); }
6037 U8x16
mask() const requires (L > 1) {
return U8x16(1U << offset()); }
6039 U8x16 mask_inverted() const requires (L > 1) {
return U8x16(~(1U << offset())); }
6041 uint8_t starting_lane() const requires (L > 1) {
return storage_.starting_lane_; }
6045 PrimitiveExpr<bool, L> is_set()
const {
6046 if constexpr (
L == 1) {
6047 return (storage_.bitmap_->u64 bitand
mask()).
template to<bool>();
6049 if constexpr (
L == 16) {
6051 return (storage_.bitvector_->u8x16 bitand
mask()).template to<bool>();
6052 }
else if (starting_lane()) {
6053 std::array<uint8_t, L>
indices;
6055 return (storage_.bitvector_->u8x16 bitand
mask()).swizzle_bytes(indices).template to<bool>();
6057 return PrimitiveExpr<bool, L>((storage_.bitvector_->u8x16 bitand
mask()).
template to<bool>().
move());
6064 if constexpr (
L == 1)
6065 storage_.bitmap_->u64 |=
mask();
6067 storage_.bitvector_->u8x16 |=
mask();
6071 if constexpr (
L == 1)
6072 storage_.bitmap_->u64 &= ~
mask();
6074 storage_.bitvector_->u8x16 &= mask_inverted();
6078 void set(PrimitiveExpr<bool, L> value) {
6079 if constexpr (
L == 1) {
6080 storage_.bitmap_->u64 =
6081 (storage_.bitmap_->u64 bitand ~mask()) bitor (
value.template to<uint64_t>() << offset());
6083 if constexpr (
L == 16) {
6085 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6086 (
value.template to<uint8_t>() << offset());
6087 }
else if (starting_lane()) {
6088 std::array<uint8_t, 16>
indices;
6089 auto it = std::fill_n(
indices.begin(), starting_lane(), L);
6090 std::iota(it, it + L, 0);
6091 std::fill(it + L,
indices.end(), L);
6092 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6093 (
value.swizzle_bytes(indices).template to<uint8_t>() << offset());
6095 Boolx16 value_masked((PrimitiveExpr<bool, L>(
true)
and value).
move());
6096 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6097 (value_masked.template to<uint8_t>() << offset());
6105 if constexpr (
L == 1) {
6106 auto other_bit = other.storage_.bitmap_->u64 bitand other.mask();
6107 Var<U64x1> this_bit;
6109 if (this->offset() > other.offset()) {
6110 const auto shift_width = this->offset() - other.offset();
6111 this_bit = other_bit << shift_width;
6112 }
else if (other.offset() > this->offset()) {
6113 const auto shift_width = other.offset() - this->offset();
6114 this_bit = other_bit >> shift_width;
6116 this_bit = other_bit;
6119 this->storage_.bitmap_->u64 =
6120 (this->storage_.bitmap_->u64 bitand ~this->mask()) bitor this_bit;
6124 auto other_bits = other.storage_.bitvector_->u8x16 bitand other.mask();
6125 Var<U8x16> this_bits;
6127 if (this->starting_lane() != other.starting_lane()) {
6128 std::array<uint8_t, 16>
indices;
6129 auto it = std::fill_n(
indices.begin(), starting_lane(), L);
6130 std::iota(it, it + L, 0);
6131 std::fill(it + L,
indices.end(), L);
6132 this_bits = other_bits.swizzle_bytes(indices);
6134 this_bits = other_bits;
6137 if (this->offset() > other.offset()) {
6138 const auto shift_width = this->offset() - other.offset();
6139 this_bits = other_bits << shift_width;
6140 }
else if (other.offset() > this->offset()) {
6141 const auto shift_width = other.offset() - this->offset();
6142 this_bits = other_bits >> shift_width;
6144 this_bits = other_bits;
6147 this->storage_.bitvector_->u8x16 =
6148 (this->storage_.bitvector_->u8x16 bitand this->mask_inverted()) bitor this_bits;
6155 operator PrimitiveExpr<bool, L>()
const {
return is_set(); }
6167template<primitive_convertible T>
6170template<expr_convertible T>
6171requires (not primitive_convertible<T>)
6176inline void BREAK(std::size_t level = 1) { Module::Get().emit_break(level); }
6177template<primitive_convertible C>
6178requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6179inline void BREAK(C &&_cond, std::size_t level = 1)
6182 Module::Get().emit_break(cond, level);
6187inline void CONTINUE(std::size_t level = 1) { Module::Get().emit_continue(level); }
6188template<primitive_convertible C>
6189requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6193 Module::Get().emit_continue(cond, level);
6200template<primitive_convertible C>
6201requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6211template<primitive_convertible C, primitive_convertible T, primitive_convertible U>
6215inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6220 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6221 constexpr std::size_t
L =
decltype(tru)::num_simd_lanes;
6224 return Module::Get().emit_select<
To,
L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6227template<primitive_convertible C, expr_convertible T, expr_convertible U>
6228requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6232inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6234 expr_t<T> tru(std::forward<T>(_tru));
6235 expr_t<U> fals(std::forward<U>(_fals));
6237 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6238 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6240 PrimitiveExpr<bool, 1> cond(std::forward<C>(_cond));
6241 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6244template<primitive_convertible C, primitive_convertible T, primitive_convertible U>
6249inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6251 primitive_expr_t<T> tru(std::forward<T>(_tru));
6252 primitive_expr_t<U> fals(std::forward<U>(_fals));
6254 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6255 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6257 PrimitiveExpr<bool, L> cond(std::forward<C>(_cond));
6258 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6261template<primitive_convertible C, expr_convertible T, expr_convertible U>
6262requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6266inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6268 expr_t<T> tru(std::forward<T>(_tru));
6269 expr_t<U> fals(std::forward<U>(_fals));
6271 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6272 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6274 PrimitiveExpr<bool, L> cond(std::forward<C>(_cond));
6275 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6281template<primitive_convertible T, primitive_convertible U, std::
size_t M>
6286 const std::array<uint8_t, M> &a)
6287 { Module::Get().emit_shuffle_bytes(e, e, a); }
6288inline auto ShuffleBytes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6293 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6294 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6296 return Module::Get().emit_shuffle_bytes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6299template<primitive_convertible T, primitive_convertible U, std::
size_t M>
6301 (primitive_expr_t<T>::num_simd_lanes == primitive_expr_t<U>::num_simd_lanes)
and
6302requires (PrimitiveExpr<common_type_t<typename primitive_expr_t<T>::type,
typename primitive_expr_t<U>::type>,
6303 primitive_expr_t<T>::num_simd_lanes> e,
6304 const std::array<uint8_t, M> &a)
6305 { Module::Get().emit_shuffle_lanes(e, e, a); }
6306inline auto ShuffleLanes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6311 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6312 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6314 return Module::Get().emit_shuffle_lanes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6317template<expr_convertible T, expr_convertible U, std::
size_t M>
6318requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6322 const std::array<uint8_t, M> &a)
6323 { Module::Get().emit_shuffle_bytes(e, e, a); }
6324inline auto ShuffleBytes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6326 expr_t<T> first(std::forward<T>(_first));
6327 expr_t<U> second(std::forward<U>(_second));
6329 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6330 constexpr std::size_t L =
decltype(first)::num_simd_lanes;
6332 return Module::Get().emit_shuffle_bytes<To, L>(first.template to<To, L>(), second.template to<To, L>(), indices);
6335template<expr_convertible T, expr_convertible U, std::
size_t M>
6336requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6337 have_common_type<
typename expr_t<T>::type,
typename expr_t<U>::type>
and
6338 (expr_t<T>::num_simd_lanes == expr_t<U>::num_simd_lanes)
and
6339requires (Expr<common_type_t<typename expr_t<T>::type,
typename expr_t<U>::type>, expr_t<T>::num_simd_lanes> e,
6340 const std::array<uint8_t, M> &a)
6341 { Module::Get().emit_shuffle_lanes(e, e, a); }
6342inline auto ShuffleLanes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6344 expr_t<T> first(std::forward<T>(_first));
6345 expr_t<U> second(std::forward<U>(_second));
6347 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6348 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6350 return Module::Get().emit_shuffle_lanes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6367 template<primitive_convertible C>
6370 : cond_(
std::forward<C>(cond))
6371 , name_(
Module::Unique_If_Name())
6394 ::wasm::Loop *loop_ =
nullptr;
6399 : body_(name +
".body", false)
6402 Module::Get().push_branch_targets(
6417 Module::Get().pop_branch_targets();
6418 Module::Block().list.push_back(loop_);
6424 std::string
name()
const {
return loop_->name.toString(); }
6432 template<primitive_convertible C>
6440 auto branch_targets = Module::Get().pop_branch_targets();
6441 Module::Get().push_branch_targets(branch_targets.brk, branch_targets.continu, cond);
6444 template<primitive_convertible C>
6462 : cond_(cond.
clone())
6463 , do_while_(
std::make_unique<
DoWhile>(name +
".do-while", cond))
6466 template<primitive_convertible C>
6470 template<primitive_convertible C>
6523 template<dsl_primitive T>
6527 template<dsl_primitive T, std::
size_t L = 1>
6531 template<dsl_primitive T, std::
size_t L = 1>
6536 template<dsl_primitive T>
6537 T *
raw_malloc(uint32_t count) {
return static_cast<T*
>(raw_allocate(
sizeof(
T) * count,
alignof(
T))); }
6540 template<dsl_primitive T, std::
size_t L = 1>
6542 if constexpr (
L == 1)
6543 return pre_allocate(
sizeof(
T) * count,
alignof(
T)).template to<T*, L>();
6544 else if constexpr (
L *
sizeof(
T) <= 16)
6545 return pre_allocate(16 * count,
alignof(
T)).template to<T*, L>();
6551 template<dsl_primitive T, std::
size_t L = 1,
typename U>
6552 requires requires (
U &&u) { U32x1(std::forward<U>(u)); }
6554 if constexpr (
L == 1) {
6556 allocate(uint32_t(
sizeof(
T)) * std::forward<U>(count),
alignof(
T)).
template to<T*, L>()
6559 }
else if constexpr (
L *
sizeof(
T) <= 16) {
6561 allocate(16U * std::forward<U>(count),
alignof(
T)).
template to<T*, L>()
6568 ).
template to<T*, L>()
6575 template<primitive_convertible T>
6580 template<primitive_convertible T,
typename U>
6581 requires requires (
U &&u) { U32x1(std::forward<U>(u)); }
and
6585 using pointed_type =
typename decltype(_ptr)::pointed_type;
6586 constexpr std::size_t
L =
decltype(_ptr)::num_simd_lanes;
6587 if constexpr (
L == 1)
6588 deallocate(_ptr.template to<void*>(), uint32_t(
sizeof(pointed_type)) * std::forward<U>(count));
6589 else if constexpr (
L *
sizeof(
T) <= 16)
6590 deallocate(_ptr.template to<void*>(), 16U * std::forward<U>(count));
6592 deallocate(_ptr.template to<void*>(),
6606inline void Module::create_local_bitmap_stack()
6608 local_bitmaps_stack_.emplace_back();
6611inline void Module::create_local_bitvector_stack()
6613 local_bitvectors_stack_.emplace_back();
6616inline void Module::dispose_local_bitmap_stack()
6618 auto &local_bitmaps = local_bitmaps_stack_.back();
6620 M_insist(~bitmap->bitmask == 0,
"all bits must have been deallocated");
6623 local_bitmaps_stack_.pop_back();
6626inline void Module::dispose_local_bitvector_stack()
6628 auto &local_bitvectors = local_bitvectors_stack_.back();
6631 for (
auto bitmask : bitvector->bitmask_per_offset)
6632 M_insist(uint16_t(~
bitmask) == 0,
"all bits must have been deallocated");
6636 local_bitvectors_stack_.pop_back();
6639template<std::
size_t L>
6643 if constexpr (
L == 1) {
6644 auto &local_bitmaps = local_bitmaps_stack_.back();
6646 if (local_bitmaps.empty())
6650 M_insist(bitmap.
bitmask,
"bitmap must have at least one bit unoccupied");
6652 uint8_t bit_offset = std::countr_zero(bitmap.
bitmask);
6653 bitmap.
bitmask ^= 1UL << bit_offset;
6658 local_bitmaps.pop_back();
6662 bool fresh_bitvector =
false;
6664 auto &local_bitvectors = local_bitvectors_stack_.back();
6666 if (local_bitvectors.empty()) {
6669 fresh_bitvector =
true;
6675 uint8_t starting_lane = uint8_t(-1U);
6676 constexpr uint16_t MASK = (1U <<
L) - 1U;
6677 for (uint8_t lane = 0; lane <= 16 -
L; ++lane) {
6680 const uint16_t masked =
bitmask bitand (MASK << lane);
6681 if (masked == (MASK << lane)) {
6682 starting_lane = lane;
6689 if (starting_lane == uint8_t(-1U)) {
6690 M_insist(not fresh_bitvector,
"fresh bitvector must have at least L consecutive bits unoccupied");
6691 goto allocate_bitvector;
6696 LocalBit<L> bit(bitvector, bit_offset, starting_lane);
6700 if (std::all_of(bitmasks.cbegin(), bitmasks.cend(), pred))
6701 local_bitvectors.pop_back();
6707template<
typename T, std::
size_t L>
6708requires (L *
sizeof(
T) <= 16)
6714template<
typename ReturnType,
typename... ParamTypes, std::size_t... ParamLs>
6715requires std::is_void_v<ReturnType>
6718 active_block_->list.push_back(
6719 builder_.makeCall(fn, { args.expr()... }, wasm_type<ReturnType, 1>())
6723template<
typename ReturnType, std::size_t
ReturnL,
typename... ParamTypes, std::size_t... ParamLs>
6728 builder_.makeCall(fn, { args.expr()... }, wasm_type<ReturnType, ReturnL>())
6732inline void Module::emit_return()
6734 active_block_->list.push_back(builder_.makeReturn());
6737template<
typename T, std::
size_t L>
6740 active_block_->list.push_back(builder_.makeReturn(
value.expr()));
6743template<
typename T, std::
size_t L>
6746 emit_return(
value.insist_not_null());
6750inline void Module::emit_break(std::size_t level)
6753 M_insist(branch_target_stack_.size() >= level);
6754 auto &branch_targets = branch_target_stack_[branch_target_stack_.size() - level];
6755 active_block_->list.push_back(builder_.makeBreak(branch_targets.brk));
6762 M_insist(branch_target_stack_.size() >= level);
6763 auto &branch_targets = branch_target_stack_[branch_target_stack_.size() - level];
6764 active_block_->list.push_back(builder_.makeBreak(branch_targets.brk,
nullptr, cond.expr()));
6767template<
typename T, std::
size_t L>
6770 if constexpr (
L *
sizeof(
T) <= 16) {
6775 builder_.makeSelect(cond.expr(), tru.expr(), fals.expr()),
6780 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
6781 auto vectors_tru = tru.vectors();
6782 auto vectors_fals = fals.vectors();
6783 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
6784 auto cond_cpy = cond.clone();
6788 vectors[idx] =
typename ResT::vector_type(
6789 builder_.makeSelect(cond_cpy.expr(), vectors_tru[idx].expr(),
6790 vectors_fals[idx].expr()),
6795 return ResT(std::move(
vectors));
6799template<
typename T, std::
size_t L>
6802 if (tru.can_be_null() or fals.can_be_null()) {
6803 auto [tru_val, tru_is_null] = tru.split();
6804 auto [fals_val, fals_is_null] = fals.split();
6805 auto cond_cpy = cond.clone();
6807 emit_select(cond, tru_val, fals_val),
6808 emit_select(cond_cpy, tru_is_null, fals_is_null)
6812 emit_select(cond, tru.insist_not_null(), fals.insist_not_null())
6817template<
typename T, std::
size_t L>
6826 if constexpr (
L *
sizeof(
T) <= 16) {
6831 builder_.makeSIMDTernary(::wasm::SIMDTernaryOp::Bitselect, tru.expr(), fals.expr(),
6837 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
6838 auto vectors_mask =
mask.vectors();
6839 static_assert(ResT::num_vectors == vectors_mask.size());
6840 auto vectors_tru = tru.vectors();
6841 auto vectors_fals = fals.vectors();
6842 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
6846 vectors[idx] =
typename ResT::vector_type(
6847 builder_.makeSIMDTernary(::wasm::SIMDTernaryOp::Bitselect,
6848 vectors_tru[idx].expr(), vectors_fals[idx].expr(),
6849 vectors_mask[idx].expr()),
6853 return ResT(std::move(
vectors));
6857template<
typename T, std::
size_t L>
6861 if (tru.can_be_null() or fals.can_be_null()) {
6862 auto [tru_val, tru_is_null] = tru.split();
6863 auto [fals_val, fals_is_null] = fals.split();
6864 auto cond_cpy = cond.clone();
6866 emit_select(cond, tru_val, fals_val),
6867 emit_select(cond_cpy, tru_is_null, fals_is_null)
6871 emit_select(cond, tru.insist_not_null(), fals.insist_not_null())
6876template<
typename T, std::
size_t L, std::
size_t M>
6877requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
6880 const
std::array<uint8_t, M> &_indices)
6882 std::array<uint8_t, 16>
indices;
6883 for (std::size_t idx = 0; idx < M; ++idx) {
6884 if (_indices[idx] <
L *
sizeof(
T))
6886 else if (_indices[idx] < 2 *
L *
sizeof(
T))
6887 indices[idx] = _indices[idx] + (16 -
L *
sizeof(
T));
6896 builder_.makeSIMDShuffle(first.expr(), second.expr(),
indices),
6900 vec.template extract_unsafe<0>(),
6904template<
typename T, std::
size_t L, std::
size_t M>
6905requires (L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (
is_pow_2(M))
and (M *
sizeof(T) <= 16)
6906inline PrimitiveExpr<T, M>
6908 const
std::array<uint8_t, M> &_indices)
6910 std::array<uint8_t, 16>
indices;
6911 for (std::size_t idx = 0; idx < M; ++idx) {
6912 for (std::size_t
byte = 0;
byte <
sizeof(
T); ++byte) {
6913 if (_indices[idx] <
L)
6914 indices[idx *
sizeof(
T) +
byte] = _indices[idx] *
sizeof(
T) + byte;
6915 else if (_indices[idx] < 2 *
L)
6917 _indices[idx] *
sizeof(
T) +
byte + (16 -
L *
sizeof(
T));
6919 indices[idx *
sizeof(
T) +
byte] = 15;
6927 builder_.makeSIMDShuffle(first.expr(), second.expr(),
indices),
6931 vec.template extract_unsafe<0>(),
6935template<
typename T, std::
size_t L, std::
size_t M>
6936requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
and (sizeof(T) == 1)
6937inline
Expr<T, M / sizeof(T)>
6938Module::emit_shuffle_bytes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices)
6940 if (first.can_be_null() or second.can_be_null()) {
6941 auto [first_val, first_is_null] = first.split();
6942 auto [second_val, second_is_null] = second.split();
6943 return Expr<
T, M /
sizeof(
T)>(
6944 emit_shuffle_bytes(first_val, second_val, indices),
6945 emit_shuffle_bytes(first_is_null, second_is_null, indices)
6948 return Expr<
T, M /
sizeof(
T)>(
6949 emit_shuffle_bytes(first.insist_not_null(), second.insist_not_null(), indices)
6954template<
typename T, std::
size_t L, std::
size_t M>
6957Module::emit_shuffle_lanes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices)
6959 if (first.can_be_null() or second.can_be_null()) {
6960 auto [first_val, first_is_null] = first.split();
6961 auto [second_val, second_is_null] = second.split();
6963 emit_shuffle_lanes(first_val, second_val,
indices),
6964 emit_shuffle_lanes(first_is_null, second_is_null,
indices)
6968 emit_shuffle_lanes(first.insist_not_null(), second.insist_not_null(),
indices)
6975 branch_target_stack_.emplace_back(brk, continu, condition.expr());
6985 Module::Block().list.push_back(Module::Builder().makeBreak(get().name,
nullptr, cond.expr()));
6997template<std::
size_t L>
6998inline variable_storage<bool, VariableKind::Local, false, L>::variable_storage()
7000 std::array<std::shared_ptr<LocalBit<bit_num_simd_lanes>>, num_locals> values;
7001 for (std::size_t idx = 0; idx < num_locals; ++idx)
7009template<std::
size_t L>
7010void variable_storage<bool, VariableKind::Local, false, L>::set_true()
7012 for (
auto &local_bit : values_)
7016template<std::
size_t L>
7017void variable_storage<bool, VariableKind::Local, false, L>::set_false()
7019 for (
auto &local_bit : values_)
7023template<std::
size_t L>
7024template<primitive_convertible U>
7025requires requires (
U &&u) { PrimitiveExpr<bool, L>(primitive_expr_t<U>(std::forward<U>(u))); }
7026void variable_storage<bool, VariableKind::Local, false, L>::operator=(
U &&_value)
7028 if constexpr (num_locals == 1) {
7029 PrimitiveExpr<bool, L>
value(primitive_expr_t<U>(std::forward<U>(_value)));
7030 values_[0]->set(
value);
7032 PrimitiveExpr<bool, L>
value(primitive_expr_t<U>(std::forward<U>(_value)));
7035 for (std::size_t idx = 0; idx < num_locals; ++idx)
7036 values_[idx]->set(
vectors[idx]);
7040template<std::
size_t L>
7041inline variable_storage<bool, VariableKind::Local, false, L>::operator PrimitiveExpr<bool, L>()
const
7043 if constexpr (num_locals == 1) {
7044 return PrimitiveExpr<bool, L>( values_[0]->is_set().
expr(), { values_[0] });
7046 static_assert(num_locals == PrimitiveExpr<bool, L>::num_vectors);
7047 std::array<typename PrimitiveExpr<bool, L>::vector_type, num_locals>
vectors;
7048 for (std::size_t idx = 0; idx < num_locals; ++idx)
7049 vectors[idx] =
typename PrimitiveExpr<bool, L>::vector_type(
7050 values_[idx]->is_set().expr(),
7053 return PrimitiveExpr<bool, L>(std::move(
vectors));
7068extern template void Module::emit_insist(PrimitiveExpr<bool, 2>,
const char*,
unsigned,
const char*);
7069extern template void Module::emit_insist(PrimitiveExpr<bool, 4>,
const char*,
unsigned,
const char*);
7070extern template void Module::emit_insist(PrimitiveExpr<bool, 8>,
const char*,
unsigned,
const char*);
7071extern template void Module::emit_insist(PrimitiveExpr<bool, 16>,
const char*,
unsigned,
const char*);
7072extern template void Module::emit_insist(PrimitiveExpr<bool, 32>,
const char*,
unsigned,
const char*);
and(sizeof(T)==4) U64x1 reinterpret_to_U64(m
#define REGISTER_USE(VAR)
#define M_EXCEPTION_LIST(X)
#define BINVOP_(NAME, SIGN, TYPE)
#define BINARY_OP(NAME, SIGN)
#define BINIOP_(NAME, SIGN)
#define USING(TYPE, NAME)
#define CALLBACK(NAME, FUNC)
#define BINARY_LIST(X)
List of supported binary operators on PrimitiveExpr, Expr, Variable, etc.
#define DECLARE_ENUM(TYPE)
#define M_insist_no_ternary_logic()
#define DECLARE_NAMES(TYPE)
#define BINARY_VOP(NAME, SIGN)
#define UNVOP_(NAME, TYPE)
#define BINOP_(NAME, SIGN, TYPE)
#define USING_N(TYPE, LENGTH, NAME)
friend void swap(local_bit_storage &first, local_bit_storage &second)
local_bit_storage()=default
local_bit_storage(LocalBitmap &bitmap, uint8_t bit_offset)
Creates a single bit with storage allocated in bitmap.
uint8_t bit_offset_
the offset of the single bit
Helper class to select appropriate storage for a LocalBit.
uint8_t starting_lane_
the lane index at which the L bits start
uint8_t bit_offset_
the offset of each bit in every lane
local_bit_storage()=default
friend void swap(local_bit_storage &first, local_bit_storage &second)
LocalBitvector * bitvector_
the bitvector in which the multiple bits are contained
local_bit_storage(LocalBitvector &bitvector, uint8_t bit_offset, uint8_t starting_lane)
Creates multiple bits with storage allocated in bitvector.
#define M_unreachable(MSG)
#define M_CONSTEXPR_COND(COND, IF_TRUE, IF_FALSE)
#define M_CONSTEXPR_COND_UNCAPTURED(COND, IF_TRUE, IF_FALSE)
and arithmetically_combinable< T, U, L > auto L auto copy_sign(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L > and(L
typename expr< T >::type expr_t
Convenience alias for expr.
PrimitiveExpr< To, ToL > std::size_t ToL
VariableKind
Declares the kind of a variable: local, parameter, or global.
friend struct PrimitiveExpr
Constructs an empty PrimitiveExpr, for which operator bool() returns false.
::wasm::Expression * expr()
Moves the underlying Binaryen ::wasm::Expression out of this.
auto ShuffleBytes(T &&_first, U &&_second, const std::array< uint8_t, M > &indices)
and(L<=16) struct LocalBit< L > void RETURN_UNSAFE()
A scalar bit or a vector of bits that is managed by the current function's stack.
friend struct FunctionProxy
bool can_be_null(const SQL_t &variant)
Bool< L > not_null(SQL_t &variant)
typename detail::_var_helper< T >::type _Var
Local variable that can always be NULL.
PrimitiveExpr & operator=(PrimitiveExpr other)
Assigns other to this.
PrimitiveExpr< bool, L > and_not(PrimitiveExpr< U, L > other) and(L > 1)
Computes the logical conjunction (and) of this and the logical negation (not) of other.
typename detail::var_helper< T >::type Var
Local variable.
PrimitiveExpr replace(U &&_value)
and arithmetically_combinable< T, U, L > auto operator/(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L >
Divides this by other.
auto make_unsigned()
Conversion of a PrimitiveExpr<T, L> to a PrimitiveExpr<std::make_unsigned_t<T>, L>.
PrimitiveExpr< T, 16/sizeof(T)> vector_type
the type of a single fully utilized vector
std::list< std::shared_ptr< Bit > > referenced_bits()
Moves the referenced bits out of this.
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.
PrimitiveExpr< bool, L > any_true() and(L > 1)
Returns true iff any value is non-zero.
::wasm::Literals insist_interpreter(::wasm::Literals &args)
Reports a runtime error.
and
Constructs a new PrimitiveExpr from a constant value.
PrimitiveExpr< U, M > convert()
PrimitiveExpr< To, 8 > low(Module::Builder().makeBinary(op_low, this_cpy.expr(), other_cpy.expr()), std::move(referenced_bits_low))
typename detail::global_helper< T >::type Global
Global variable.
Bool< L > is_null(SQL_t &variant)
auto Select(C &&_cond, T &&_tru, U &&_fals)
PrimitiveExpr< ResultType, ResultL > unary(::wasm::UnaryOp op)
Helper function to implement unary operations.
auto operator*(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L >
Multiplies this and other.
and ReturnL(PrimitiveExpr< ParamTypes, ParamLs >...)>
std::array< vector_type, num_vectors > vectors()
Moves the underlying PrimitiveExpr<T, 16 / sizeof(T)>s out of this.
and arithmetically_combinable< T, U, L > PrimitiveExpr< bool, L > operator>=(PrimitiveExpr< U, L > other) arithmetic< T >
Checks whether this greater than or equals to other.
and arithmetically_combinable< T, U, L > auto operator%(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L > integral< T > and(L
Computes the remainder of dividing this by other.
PrimitiveExpr L add_pairwise() and(sizeof(T)
auto max(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L > std
Computes the maximum of this and other.
void discard()
Discards this.
and arithmetically_combinable< T, U, L > PrimitiveExpr< bool, L > operator!=(PrimitiveExpr< U, L > other)
Checks whether this unequal to other.
std::array< vector_type, num_vectors > vectors_
the fully utilized SIMD vectors represented as PrimitiveExprs
PrimitiveExpr< uint64_t, L > L L L L U
PrimitiveExpr swizzle_bytes(PrimitiveExpr< uint8_t, 16 > indices)
Selects lanes of this in byte granularity depending on the indices specified by indices.
::wasm::Expression * expr_
the referenced Binaryen expression (AST)
for(std::size_t idx=1;idx< num_vectors;++idx) res.emplace((vectors_[idx].bitmask()<< uint32_t(idx *vector_type return * res
PrimitiveExpr< To, 8 > high(Module::Builder().makeBinary(op_high, this->expr(), other.expr()), std::move(referenced_bits_high))
PrimitiveExpr< T, 1 > extract()
Extracts the.
PrimitiveExpr< T, 1 > extract_unsafe()
Extracts the.
typename primitive_expr< T >::type primitive_expr_t
Convenience alias for primitive_expr.
PrimitiveExpr< bool, 1 > all_true() and(L > 1)
Returns true iff all values are true.
PrimitiveExpr< ResultType, ResultL > binary(::wasm::BinaryOp op, PrimitiveExpr< OperandType, OperandL > other)
Helper function to implement binary operations.
std::list< std::shared_ptr< Bit > > referenced_bits_
a list of referenced Bits
and std::floating_point< T > and std::floating_point< U >inline ::wasm::Literal make_literal(U value)
Creates a ::wasm::Literal of type.
std::string unique(std::string prefix, unsigned &counter)
Creates a unique name from a given prefix and a counter.
::wasm::Literals throw_interpreter(::wasm::Literals &args)
Throws an exception.
void CONTINUE(std::size_t level=1)
M swizzle_lanes(const std::array< uint8_t, M > &_indices)
static constexpr std::size_t num_vectors
the number of SIMD vectors needed for the represented expression
const std::map<::wasm::Name, std::function<::wasm::Literals(::wasm::Literals &)> > callback_functions
typename _int< W >::type int_t
and arithmetically_combinable< T, U, L > PrimitiveExpr< bool, L > operator<(PrimitiveExpr< U, L > other) arithmetic< T >
Checks whether this less than other.
std::conditional_t< std::is_signed_v< T >, int16_t, uint16_t > To
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
auto operator>>(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L > integral< T > and(L
Shifts this right by other.
PrimitiveExpr< uint64_t, L > hash() and(L
PrimitiveExpr clone() const
Creates and returns a deep copy of this.
and arithmetically_combinable< T, U, L > PrimitiveExpr< bool, L > operator>(PrimitiveExpr< U, L > other) arithmetic< T >
Checks whether this greater than to other.
void dump(std::ostream &out) const
and arithmetically_combinable< T, U, L > PrimitiveExpr< bool, L > operator<=(PrimitiveExpr< U, L > other) arithmetic< T >
Checks whether this less than or equals to other.
and arithmetically_combinable< T, U, L > auto L auto L auto min(PrimitiveExpr< U, L > other) -> PrimitiveExpr< common_type_t< T, U >, L >
auto ShuffleLanes(T &&_first, U &&_second, const std::array< uint8_t, M > &indices)
void BREAK(std::size_t level=1)
auto referenced_bits_high
PrimitiveExpr L L L L bitmask() and(L > 1)
Concatenates the most significant bit of each value of this into a single mask.
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)
Schema operator+(const Schema &left, const Schema &right)
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
typename conditional_one< Cond, TrueType, FalseType >::template type< Arg > conditional_one_t
constexpr T operator~(T t)
typename common_type< T, U >::type common_type_t
Convenience alias for common_type<T, U>::type.
constexpr T operator-(T left, T right)
M_LCOV_EXCL_START std::ostream & operator<<(std::ostream &out, const PlanTableBase< Actual > &PT)
and arithmetic< U > and same_signedness< T, U > U
bool M_EXPORT init(void)
Initializes the mu*t*able library.
bool operator==(std::reference_wrapper< T > left, std::reference_wrapper< T > right)
This class represents a reserved address space in virtual memory.
Helper struct for parameter packs.
void free(T &&ptr, U &&count)
virtual void * raw_allocate(uint32_t bytes, uint32_t align=1)=0
Pre-allocates memory for bytes consecutive bytes with alignment requirement align and returns a raw p...
void deallocate(Ptr< void > ptr, uint32_t bytes)
virtual uint32_t perform_pre_allocations()=0
Performs the actual pre-allocations.
void free(T &&ptr)
Frees exactly one value of type.
T * raw_malloc(uint32_t count)
Pre-allocates memory for an array of count consecutive values of type.
virtual U32x1 allocated_memory_consumption() const =0
Returns the allocated memory overall consumption.
T * raw_malloc()
Pre-allocates memory for exactly one value of type.
Var< Ptr< void > > allocate(uint32_t bytes, uint32_t align=1)
Ptr< PrimitiveExpr< T, L > > pre_malloc()
Pre-allocates memory for exactly one value of type.
virtual U32x1 allocated_memory_peak() const =0
Returns the allocated memory peak consumption.
virtual Var< Ptr< void > > allocate(U32x1 bytes, uint32_t align=1)=0
Allocates memory for bytes consecutive bytes with alignment requirement align and returns a pointer t...
virtual uint32_t pre_allocated_memory_consumption() const =0
Returns the pre-allocated memory overall consumption.
Var< Ptr< PrimitiveExpr< T, L > > > malloc()
Allocates memory for exactly one value of type.
virtual void deallocate(Ptr< void > ptr, U32x1 bytes)=0
Deallocates the bytes consecutive bytes of allocated memory at address ptr.
virtual Ptr< void > pre_allocate(uint32_t bytes, uint32_t align=1)=0
Pre-allocates memory for bytes consecutive bytes with alignment requirement align and returns a point...
Ptr< PrimitiveExpr< T, L > > pre_malloc(uint32_t count)
Pre-allocates memory for an array of count consecutive values of type.
Var< Ptr< PrimitiveExpr< T, L > > > malloc(U &&count)
Allocates memory for an array of count consecutive values of type.
A helper class to use a Block, thereby setting the Block active for code generation.
Block & block_
the block to use (for code gen)
Represents a code block, i.e.
void attach_to(::wasm::Block &other)
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.
friend void swap(Block &first, Block &second)
Block & operator=(Block &&other)
Block(::wasm::Block *block, bool attach_to_parent)
Create a new Block for a given ::wasm::Block.
::wasm::Block & get() const
void go_to() const
Emits a jump to the end of this Block.
bool attach_to_parent_
whether this block attaches itself to its parent block
Block(bool attach_to_parent)
Create an anonymous Block.
void dump(std::ostream &out) const
::wasm::Block * this_block_
this block, can be nullptr if default-constructed or the block has already been attached
void attach_to(Block &other)
Attaches this Block to the given Block other.
Block(std::string name, bool attach_to_parent)
Create a named Block and set it active in the current Module.
::wasm::Block & previous() const
::wasm::Block * parent_block_
the parent block, before this block was created
friend std::ostream & operator<<(std::ostream &out, const Block &B)
Block(const char *name, bool attach_to_parent)
Create a named Block and set it active in the current Module.
Helper struct to perform constant folding at compile time.
DoWhile(const DoWhile &)=delete
DoWhile(DoWhile &&)=default
DoWhile(const char *name, C &&cond)
DoWhile(std::string name, C &&_cond)
An Expr<T, L> combines a PrimitiveExpr<T, L> value with a PrimitiveExpr<bool, L>, called NULL informa...
Expr< T, ToL > broadcast()
Broadcasts a PrimitiveExpr<T, 1> to a PrimitiveExpr<T, ToL>.
Expr(PrimitiveExpr< T, L > value, PrimitiveExpr< bool, L > is_null)
Constructs an Expr from a value and NULL information is_null.
bool can_be_null() const
Returns true if this may be NULL, false otherwise.
Expr< To, ToL > reinterpret()
Reinterpret an Expr<T, L> to an Expr<To, ToL>.
PrimitiveExpr< T, L > value_
the referenced value expression
Expr swizzle_bytes(PrimitiveExpr< uint8_t, 16 > indices) and
Selects lanes of this in byte granularity depending on the indices specified by indices.
Expr< uint32_t, 1 > bitmask()
PrimitiveExpr< uint64_t, L > hash()
void discard()
Discards this.
UNARY_LIST(UNARY) auto add_pairwise()
Expr(Expr &other)
Constructs a new Expr by moving the underlying value_ and is_null_ of other to this.
Expr< bool, L > and_not(Expr< bool, L > other)
Implements logical and not according to 3VL of Kleene and Priest's logic.
Expr & operator=(Expr &&)=delete
Expr< T, 1 > extract()
Extracts the.
Expr(PrimitiveExpr< T, L > value)
Implicitly constructs an Expr from a value.
Expr(Expr &&other)
Constructs a new Expr by moving the underlying value_ and is_null_ of other to this.
PrimitiveExpr< bool, L > is_false_and_not_null()
Returns true if the value is false and NOT NULL.
PrimitiveExpr< bool, L > is_null()
Returns true if this is NULL, false otherwise.
and(dsl_primitive< std::decay_t< Us > > and ...) and
Constructs an Expr from a decayable constant value.
PrimitiveExpr< bool, L > is_true_and_not_null()
Returns true if the value is true and NOT NULL.
PrimitiveExpr< bool, L > not_null()
Returns true if this is NOT NULL, false otherwise.
BINARY(operator+)BINARY(operator-)BINARY(operator*)BINARY(operator/)BINARY(operator%)BINARY(operator bitand) BINARY(operator bitor) BINARY(operator xor) BINARY(operator<<) BINARY(operator>>) BINARY(operator
Expr< bool, 1 > any_true()
std::pair< PrimitiveExpr< T, L >, PrimitiveExpr< bool, L > > split()
Splits this Expr into a PrimitiveExpr<T, L> with the value and a PrimitiveExpr<bool,...
Expr< bool, 1 > all_true()
Expr(std::pair< PrimitiveExpr< T, L >, PrimitiveExpr< bool, L > > value)
Constructs an Expr from a std::pair value of value and NULL info.
Expr clone() const
Returns a deep copy of this.
static Expr Null()
Returns an Expr that is NULL.
std::pair< PrimitiveExpr< T, L >, PrimitiveExpr< bool, L > > split_unsafe()
Splits this Expr into a PrimitiveExpr<T, L> with the value and a PrimitiveExpr<bool,...
Expr replace(U &&_value)
Replaces the.
PrimitiveExpr< T, L > insist_not_null()
Moves the current value_ out of this.
Expr(const Expr &)=delete
Expr< To, ToL > to()
Explicitly converts an Expr<T, L> to an Expr<To, ToL>.
A handle to create a Function and to create invocations of that function.
Represents a Wasm function.
Helper struct for garbage collection done by the Module.
GarbageCollectedData()=default
virtual ~GarbageCollectedData()
GarbageCollectedData(GarbageCollectedData &&)=default
std::function< void(void)> continuation_t
PrimitiveExpr< bool, 1 > cond_
LocalBitmap(const LocalBitmap &)=delete
LocalBitvector(const LocalBitvector &)=delete
std::array< uint16_t, 8 > bitmask_per_offset
entry at index i is a bitmask for the 16 lanes using the constant bit offset i
Implements a loop which iterates exactly once unless explicitly continue-ed.
const Block & body() const
::wasm::Loop * loop_
the Binaryen loop
Loop(std::string name, tag< int >)
Convenience c'tor accessible via tag-dispatching.
Loop & operator=(Loop &&other)
Loop(const Loop &)=delete
friend void swap(Loop &first, Loop &second)
::wasm::Builder & Builder()
Returns the expression builder of the current module.
C & add_garbage_collected_data(void *handle, Args... args)
Adds and returns an instance of.
std::unique_ptr<::wasm::ModuleRunner::ExternalInterface > interface_
this module's interface, if any
::wasm::ModuleRunner instantiate(::wasm::GlobalValueSet imports={})
Create an instance of this module.
std::vector< branch_target_t > branch_target_stack_
stack of Binaryen branch targets
static std::string Unique_Global_Name(std::string prefix="global")
Returns a unique global name in the current module.
::wasm::Block * active_block_
the currently active Binaryen block
void dump_all(std::ostream &out)
std::unique_ptr< std::pair< memory::AddressSpace, memory::Memory > > vm_
the virtual address space and its backed memory; only set if no WasmContext was created
std::unordered_map< void *, std::unique_ptr< GarbageCollectedData > > garbage_collected_data_
mapping from handles to garbage collected data
void push_branch_targets(::wasm::Name brk, ::wasm::Name continu)
void emit_import(const char *extern_name, const char *intern_name=nullptr)
static thread_local std::unique_ptr< Module > the_module_
std::vector< std::vector< LocalBitmap * > > local_bitmaps_stack_
the per-function stacks of local bitmaps; used for local scalar boolean variables and NULL bits
static std::string Unique_Function_Name(std::string prefix="function")
Returns a unique function name in the current module.
void emit_global(const std::array<::wasm::Name, N > &names, bool is_mutable, Us... inits)
void emit_global(::wasm::Name name, bool is_mutable, Us... inits)
friend std::ostream & operator<<(std::ostream &out, const Module &M)
unsigned id_
the unique ID for this Module
void emit_global(const std::array<::wasm::Name, 1 > &names, bool is_mutable, Us... inits)
void emit_function_import(const char *name)
static std::string Unique_Block_Name(std::string prefix="block")
Returns a unique block name in the current module.
static std::string Unique_Loop_Name(std::string prefix="loop")
Returns a unique loop name in the current module.
const branch_target_t & current_branch_targets() const
void dump(std::ostream &out) const
static unsigned ID()
Returns the ID of the current module.
void emit_function_export(const char *name)
Add function name as export.
Module(const Module &)=delete
void set_feature(::wasm::FeatureSet feature, bool value)
::wasm::Block * set_active_block(::wasm::Block *block)
Sets the new active ::wasm::Block and returns the previously active ::wasm::Block.
::wasm::Function & Function()
Returns the currently active function.
std::vector< std::vector< LocalBitvector * > > local_bitvectors_stack_
the per-function stacks of local bitvectors; used for local vectorial boolean variables and NULL bit...
std::vector< std::tuple< const char *, unsigned, const char * > > messages_
filename, line, and an optional message for each emitted insist or exception throw
void emit_global(::wasm::Name name, bool is_mutable, uint32_t init)
::wasm::Function * set_active_function(::wasm::Function *fn)
Sets the new active ::wasm::Function and returns the previously active ::wasm::Function.
std::unique_ptr< Allocator > allocator_
the allocator
branch_target_t pop_branch_targets()
static std::string Unique_If_Name(std::string prefix="if")
Returns a unique if name in the current module.
::wasm::Module module_
the Binaryen Wasm module
static Allocator & Allocator()
Returns the allocator.
and(L *sizeof(T)<=16) and(M > 0) and(M<
Selects lanes of first and second in byte granularity depending on the indices specified by indices.
::wasm::Block & Block()
Returns the currently active block.
::wasm::Builder builder_
the Binaryen expression builder for the module_
const std::tuple< const char *, unsigned, const char * > & get_message(std::size_t idx) const
Parameter(unsigned index)
Create a Parameter<T, L> for the existing parameter local of given index.
typename base_type::dependent_expr_type dependent_expr_type
Variable & operator=(Variable &&other)
Variable(const Variable &)=delete
auto swizzle_bytes(PrimitiveExpr< uint8_t, 16 > indices) const
Selects lanes of this in byte granularity depending on the indices specified by indices.
auto swizzle_lanes(const std::array< uint8_t, M > &indices) const
Selects lanes of this in lane granularity depending on the indices specified by indices.
storage_type storage_
storage of this Variable
dependent_expr_type val() const
Obtain a Variable<T, L>s value as a PrimitiveExpr<T, L> or Expr<T, L>, depending on CanBeNull.
dependent_expr_t< PrimitiveExpr< T, ToL > > broadcast() const
Variable & operator=(const Variable &other)
Variable(Us &&... value)
Constructs a new Variable and initializes it with value.
conditional_one_t< CanBeNull, expr_t, primitive_expr_t, X > dependent_expr_t
Variable(::wasm::Index idx, tag< int > tag)
Constructs a Variable instance from an already allocated local with the given index idx.
UNARY_LIST(UNARY) UNARY(add_pairwise) UNARY(bitmask) UNARY(any_true) UNARY(all_true) UNARY(hash) UNARY(operator*)UNARY(operator->) UNARY(is_nullptr) UNARY(is_null) UNARY(not_null) UNARY(is_true_and_not_null) UNARY(is_false_and_not_null) #define ASSIGNOP_LIST(X) #define ASSIGNOP(SYMBOL) ASSIGNOP_LIST(ASSIGNOP) template< std::size_t M > auto extract() const
bool can_be_null() const
Check whether the value of this Variable can be NULL.
dependent_expr_t< PrimitiveExpr< To, ToL > > reinterpret() const
Variable & replace(U &&value)
Replaces the.
dependent_expr_t< PrimitiveExpr< T, L > > dependent_expr_type
friend void swap(Variable &first, Variable &second)
Variable & operator=(U &&value)
Variable()=default
Default-constructs a new Variable.
constexpr bool has_null_bit() const
Check whether this Variable can be assigned to NULL, i.e.
Variable(Variable &&other)
dependent_expr_t< PrimitiveExpr< To, ToL > > to() const
std::unique_ptr< DoWhile > do_while_
While(std::string name, C &&cond)
While(const While &)=delete
While(std::string name, PrimitiveExpr< bool, 1 > cond)
PrimitiveExpr< bool, 1 > cond_
While(const char *name, C &&cond)
const Block & body() const
Helper type to deduce the signed integral type with a given byte width.
Stores the "branch targets" introduced by control flow structures, i.e.
::wasm::Expression * condition
the continue condition (may be nullptr if there is no condition)
::wasm::Name brk
the break target
branch_target_t(::wasm::Name brk, ::wasm::Name continu, ::wasm::Expression *condition)
::wasm::Name continu
the continue target
Deduces a suitable specialization of Variable that can be NULL for the given type.
Deduces a suitable specialization of Variable for global variables of the given type.
the_reference(PrimitiveExpr< T *, L > ptr)
PrimitiveExpr< T *, L > ptr_
void operator=(U &&_value)
Deduces a suitable specialization of Variable for the given type.
::wasm::Type operator()()
::wasm::Signature operator()()
::wasm::Type operator()()
::wasm::Type operator()() const
::wasm::Type operator()()
::wasm::Type operator()() const
Converts a compile-time type into a runtime-type ::wasm::Type.
exception(exception_t type, std::string message)
typename expr< std::decay_t< T > >::type type
Helper type to deduce the Expr<U> type given a.
typename primitive_expr< std::decay_t< T > >::type type
Helper type to deduce the PrimitiveExpr<U> type given a type.
friend std::ostream & operator<<(std::ostream &out, print_types)
A helper type to print Wasm types.
Helper type to deduce the unsigned integral type with a given byte width.