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) \
552 X(failed_unittest_check)
556#define DECLARE_ENUM(TYPE) TYPE,
562#define DECLARE_NAMES(TYPE) #TYPE,
563 static constexpr const char *
const names_[] = {
582::wasm::Literals insist_interpreter(::wasm::Literals &
args);
586::wasm::Literals throw_interpreter(::wasm::Literals &
args);
588const std::map<::wasm::Name, std::function<::wasm::Literals(::wasm::Literals&)>>
callback_functions = {
589#define CALLBACK(NAME, FUNC) { NAME, FUNC },
636 static boolean_result_t EvalBoolean(const ::wasm::Expression *
expr);
658 static inline std::atomic_uint NEXT_MODULE_ID_ = 0;
663 unsigned next_block_id_ = 0;
665 unsigned next_function_id_ = 0;
667 unsigned next_global_id_ = 0;
669 unsigned next_if_id_ = 0;
671 unsigned next_loop_id_ = 0;
677 ::wasm::Block *active_block_ =
nullptr;
679 ::wasm::Function *active_function_ =
nullptr;
681 ::wasm::Memory *memory_ =
nullptr;
683 std::unique_ptr<std::pair<memory::AddressSpace, memory::Memory>>
vm_;
689 std::vector<std::tuple<const char*, unsigned, const char*>>
messages_;
691 std::unique_ptr<::wasm::ModuleRunner::ExternalInterface>
interface_;
708 M_insist(not the_module_,
"must not have a module yet");
709 the_module_ = std::unique_ptr<Module>(
new Module());
712 M_insist(
bool(the_module_),
"must have a module");
713 the_module_ =
nullptr;
716 M_insist(
bool(the_module_),
"must have a module");
722 static unsigned ID() {
return Get().id_; }
728 return unique(prefix, Get().next_function_id_);
732 return unique(prefix, Get().next_global_id_);
740 static ::wasm::Builder &
Builder() {
return Get().builder_; }
755 static bool Validate(
bool verbose =
true,
bool global =
true);
758 static void Optimize(
int optimization_level);
761 ::wasm::Block *
set_active_block(::wasm::Block *block) {
return std::exchange(active_block_, block); }
763 ::wasm::Function *
set_active_function(::wasm::Function *fn) {
return std::exchange(active_function_, fn); }
769 template<
typename T, std::
size_t L>
772 template<
typename T, std::
size_t L>
775 void emit_break(std::size_t level = 1);
778 void emit_continue(std::size_t level = 1);
781 template<
typename T, std::
size_t L>
783 template<
typename T, std::
size_t L>
785 template<
typename T, std::
size_t L>
788 template<
typename T, std::
size_t L>
790 Expr<T, L> emit_select(PrimitiveExpr<bool, L> cond, Expr<T, L> tru, Expr<T, L> fals);
797 template<
typename T, std::
size_t L, std::
size_t M>
798 requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
800 const
std::array<uint8_t, M> &indices);
804 template<typename T,
std::
size_t L,
std::
size_t M>
805 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (is_pow_2(M))
and (M * sizeof(T) <= 16)
807 const
std::array<uint8_t, M> &indices);
812 template<typename T,
std::
size_t L,
std::
size_t M>
813 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
and (sizeof(T) == 1)
814 Expr<T, M / sizeof(T)> emit_shuffle_bytes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices);
818 template<typename T,
std::
size_t L,
std::
size_t M>
819 requires (L > 1)
and (L * sizeof(T) <= 16)
and (M > 0)
and (is_pow_2(M))
and (M * sizeof(T) <= 16)
820 Expr<T, M> emit_shuffle_lanes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices);
824 requires (L * sizeof(T) <= 16)
and requires (Us... us) { make_literal<T, L>(us...); }
825 void emit_global(::wasm::Name name,
bool is_mutable, Us... inits) {
826 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
827 : ::wasm::Builder::Mutability::Immutable;
828 ::wasm::Const *_init = builder_.makeConst(make_literal<T, L>(inits...));
829 auto global = builder_.makeGlobal(name, wasm_type<T, L>(), _init, mut);
830 module_.addGlobal(std::move(global));
833 requires (
L *
sizeof(
T) <= 16)
and requires (Us... us) { make_literal<T, L>(us...); }
834 void emit_global(
const std::array<::wasm::Name, 1> &names,
bool is_mutable, Us... inits) {
835 emit_global<T, L>(names[0], is_mutable, inits...);
838 requires requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, N>>; }
839 void emit_global(
const std::array<::wasm::Name, N> &names,
bool is_mutable, Us... inits) {
840 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
841 : ::wasm::Builder::Mutability::Immutable;
842 auto literals = make_literal<T, L>(inits...);
843 for (std::size_t idx = 0; idx < N; ++idx) {
844 ::wasm::Const *_init = builder_.makeConst(literals[idx]);
845 auto global = builder_.makeGlobal(names[idx], wasm_type<T, L>(), _init, mut);
846 module_.addGlobal(std::move(global));
849 template<dsl_po
inter_to_primitive T, std::
size_t L = 1>
850 void emit_global(::wasm::Name name,
bool is_mutable, uint32_t init) {
851 ::wasm::Builder::Mutability mut = is_mutable ? ::wasm::Builder::Mutability::Mutable
852 : ::wasm::Builder::Mutability::Immutable;
853 ::wasm::Const *_init = builder_.makeConst(::wasm::Literal(
init));
854 auto global = builder_.makeGlobal(name, wasm_type<T, L>(), _init, mut);
855 module_.addGlobal(std::move(global));
858 template<
typename T, std::
size_t L = 1>
859 requires (
L *
sizeof(
T) <= 16)
863 template<
typename T, std::
size_t L = 1>
866 void emit_import(
const char *extern_name,
const char *intern_name =
nullptr)
868 ::wasm::Const *
value =
M_CONSTEXPR_COND(std::is_pointer_v<T>, builder_.makeConst(make_literal<T, L>(0)),
869 builder_.makeConst(make_literal<T, L>(
T())));
870 auto global = builder_.makeGlobal(intern_name ? intern_name : extern_name, wasm_type<T, L>(),
M_notnull(
value),
871 ::wasm::Builder::Mutability::Immutable);
872 global->module =
"imports";
873 global->base = extern_name;
874 module_.addGlobal(std::move(global));
879 requires std::is_function_v<T>
and requires { wasm_type<T, 1>(); }
881 auto func = module_.addFunction(builder_.makeFunction(name, wasm_type<T, 1>(), {}));
882 func->module =
"imports";
888 module_.addExport(builder_.makeExport(name, name, ::wasm::ExternalKind::Function));
892 template<
typename ReturnType,
typename... ParamTypes, std::size_t... ParamLs>
893 requires std::is_void_v<ReturnType>
896 template<
typename ReturnType, std::size_t
ReturnL = 1,
typename... ParamTypes, std::size_t... ParamLs>
901 template<std::
size_t L>
908 const std::tuple<const char*, unsigned, const char*> &
get_message(std::size_t idx)
const {
909 return messages_.at(idx);
916 template<
class C,
typename... Args>
918 auto it = garbage_collected_data_.try_emplace(
922 return as<C>(*it->second);
929 ::wasm::ModuleRunner
instantiate(::wasm::GlobalValueSet imports = {}) {
930 return ::wasm::ModuleRunner(module_, get_mock_interface(std::move(imports)));
934 void set_feature(::wasm::FeatureSet feature,
bool value) { module_.features.set(feature,
value); }
938 std::pair<uint8_t*, std::size_t>
binary();
941 void create_local_bitmap_stack();
942 void create_local_bitvector_stack();
943 void dispose_local_bitmap_stack();
944 void dispose_local_bitvector_stack();
946 template<std::
size_t L = 1>
951 branch_target_stack_.emplace_back(brk, continu,
nullptr);
957 auto top = branch_target_stack_.back();
958 branch_target_stack_.pop_back();
969 out <<
" currently active block: ";
974 out <<
"<anonymous block>";
991 void dump(std::ostream &out)
const { out << *
this << std::endl; }
994 void dump_all(std::ostream &out) { out << module_ << std::endl; }
1015 ::wasm::Block *this_block_ =
nullptr;
1017 ::wasm::Block *parent_block_ =
nullptr;
1019 bool attach_to_parent_ =
false;
1033 Block(::wasm::Block *block,
bool attach_to_parent)
1035 , attach_to_parent_(attach_to_parent)
1037 if (attach_to_parent_) {
1038 parent_block_ = Module::Get().active_block_;
1039 M_insist(not attach_to_parent_ or parent_block_,
"can only attach to parent if there is a parent block");
1045 explicit Block(
bool attach_to_parent) :
Block(
Module::Builder().makeBlock(), attach_to_parent) { }
1047 explicit Block(std::string name,
bool attach_to_parent)
1048 :
Block(
Module::Builder().makeBlock(
Module::Unique_Block_Name(name)), attach_to_parent)
1051 explicit Block(
const char *name,
bool attach_to_parent) :
Block(
std::string(name), attach_to_parent) { }
1056 if (this_block_
and attach_to_parent_)
1067 other.list.push_back(this_block_);
1068 this_block_ =
nullptr;
1073 std::string
name()
const {
M_insist(has_name());
return get().name.toString(); }
1076 bool empty()
const {
return get().list.empty(); }
1080 M_insist(not attach_to_parent_,
"cannot explicitly attach if attach_to_parent is true");
1086 M_insist(not attach_to_parent_,
"cannot explicitly attach if attach_to_parent is true");
1087 attach_to(Module::Block());
1091 void go_to()
const { Module::Block().list.push_back(Module::Builder().makeBreak(get().name)); }
1096 out <<
"vvvvvvvvvv block";
1098 out <<
" \"" << B.
name() <<
'"';
1099 out <<
" starts here vvvvvvvvvv\n";
1101 for (
auto expr : B.
get().list)
1102 out << *
expr <<
'\n';
1104 out <<
"^^^^^^^^^^^ block";
1106 out <<
" \"" << B.
name() <<
'"';
1107 out <<
" ends here ^^^^^^^^^^^\n";
1112 void dump(std::ostream &out)
const { out << *
this; out.flush(); }
1122 ::wasm::Block *old_block_ =
nullptr;
1126 old_block_ = Module::Get().set_active_block(block_.
this_block_);
1142template<
typename ReturnType,
typename... ParamTypes, std::size_t
ReturnL, std::size_t... ParamLs>
1143requires ((std::is_void_v<ReturnType>
and (ReturnL == 1)) or
1145 (not dsl_primitive<ReturnType> or (ReturnL *
sizeof(ReturnType) <= 16))
and
1147 ((not dsl_primitive<ParamTypes> or (ParamLs *
sizeof(ParamTypes) <= 16))
and ...)
1153 using type = ReturnType(ParamTypes...);
1157 using return_type = ReturnType;
1159 static constexpr std::size_t PARAMETER_COUNT =
sizeof...(ParamTypes);
1165 template<
typename...
Ts, std::size_t... Ls, std::size_t... Is>
1166 requires (
sizeof...(Ts) ==
sizeof...(Ls))
and (
sizeof...(
Ts) ==
sizeof...(Is))
1167 std::tuple<Parameter<Ts, Ls>...> make_parameters_helper(std::index_sequence<Is...>) {
1168 return std::make_tuple<Parameter<Ts, Ls>...>(
1174 template<
typename...
Ts, std::size_t... Ls,
typename Indices = std::make_index_sequence<
sizeof...(Ts)>>
1175 requires (
sizeof...(
Ts) ==
sizeof...(Ls))
1176 std::tuple<Parameter<Ts, Ls>...> make_parameters() {
return make_parameters_helper<
Ts..., Ls...>(Indices{}); }
1179 template<std::size_t I,
typename...
Ts>
1180 struct parameter_type;
1181 template<std::size_t I,
typename T,
typename...
Ts>
1182 struct parameter_type<I,
T,
Ts...>
1184 static_assert(I <=
sizeof...(Ts),
"parameter index out of range");
1185 using type =
typename parameter_type<I - 1,
Ts...>::type;
1187 template<
typename T,
typename...
Ts>
1188 struct parameter_type<0,
T,
Ts...>
1193 template<std::
size_t I>
1194 using parameter_type_t =
typename parameter_type<I, ParamTypes...>::type;
1197 template<std::size_t I, std::size_t... Ls>
1198 struct parameter_num_simd_lanes;
1199 template<std::size_t I, std::size_t
L, std::size_t... Ls>
1200 struct parameter_num_simd_lanes<I,
L, Ls...>
1202 static_assert(I <=
sizeof...(Ls),
"parameter index out of range");
1203 static constexpr std::size_t
value = parameter_num_simd_lanes<I - 1, Ls...>::value;
1205 template<std::size_t
L, std::size_t... Ls>
1206 struct parameter_num_simd_lanes<0,
L, Ls...>
1208 static constexpr std::size_t
value =
L;
1211 template<std::
size_t I>
1212 static constexpr std::size_t parameter_num_simd_lanes_v = parameter_num_simd_lanes<I, ParamLs...>::value;
1218 ::wasm::Function *this_function_ =
nullptr;
1220 ::wasm::Function *previous_function_ =
nullptr;
1225 swap(first.name_, second.name_);
1226 swap(first.body_, second.body_);
1227 swap(first.this_function_, second.this_function_);
1228 swap(first.previous_function_, second.previous_function_);
1238 , body_(name +
".body",
false)
1241 if constexpr (not std::is_void_v<ReturnType>)
1242 body_.
get().type = wasm_type<ReturnType, ReturnL>();
1245 auto fn = Module::Builder().makeFunction(
1247 wasm_type<dsl_type, 1>(),
1248 std::vector<::wasm::Type>{}
1250 fn->body = &body_.
get();
1251 this_function_ = Module::Get().module_.addFunction(std::move(fn));
1252 M_insist(this_function_->getNumParams() == PARAMETER_COUNT);
1253 Module::Get().create_local_bitmap_stack();
1254 Module::Get().create_local_bitvector_stack();
1257 previous_function_ = Module::Get().set_active_function(this_function_);
1264 if constexpr (not std::is_void_v<ReturnType>)
1265 body_.
get().list.push_back(Module::Builder().makeUnreachable());
1266 Module::Get().dispose_local_bitmap_stack();
1267 Module::Get().dispose_local_bitvector_stack();
1269 Module::Get().set_active_function(previous_function_);
1276 Block & body() {
return body_; }
1278 const Block & body()
const {
return body_; }
1281 std::string name()
const {
return name_.toString(); }
1284 std::tuple<Parameter<ParamTypes, ParamLs>...> parameters() {
return make_parameters<ParamTypes..., ParamLs...>(); }
1287 template<std::
size_t I>
1293 void emit_return()
requires std::is_void_v<ReturnType> {
1294 Module::Block().list.push_back(Module::Builder().makeReturn());
1298 template<primitive_convertible T>
1299 requires (not std::is_void_v<ReturnType>)
and
1301 void emit_return(
T &&t) {
1303 Module::Get().emit_return(
value);
1308 template<expr_convertible T>
1311 void emit_return(
T &&t)
1314 Module::Get().emit_return(
expr);
1318 ::wasm::Function & get()
const {
return *
M_notnull(this_function_); }
1322 out <<
"function \"" << Fn.name() <<
"\" : ";
1323 if constexpr (PARAMETER_COUNT)
1326 out <<
typeid(void).name();
1329 if (not Fn.get().vars.empty()) {
1330 out <<
" " << Fn.get().getNumVars() <<
" local variables:";
1331 for (::wasm::Index i = 0, end = Fn.get().getNumVars(); i != end; ++i)
1332 out <<
" [" << i <<
"] " << Fn.get().vars[i];
1340 void dump(std::ostream &out)
const { out << *
this; out.flush(); }
1341 void dump()
const { dump(std::cerr); }
1344template<
typename ReturnType,
typename... ParamTypes>
1345requires (std::is_void_v<ReturnType> or dsl_primitive<ReturnType> or dsl_pointer_to_primitive<ReturnType>)
and
1346 ((dsl_primitive<ParamTypes> or dsl_pointer_to_primitive<ParamTypes>)
and ...)
1347struct Function<ReturnType(ParamTypes...)> : Function<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>
1349 using Function<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>::Function;
1352template<
typename... ParamTypes, std::size_t... ParamLs>
1354 :
Function<PrimitiveExpr<void, 1>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1370template<
typename ReturnType,
typename... ParamTypes, std::size_t
ReturnL, std::size_t... ParamLs>
1371requires ((std::is_void_v<ReturnType>
and (ReturnL == 1)) or
1375 ((not dsl_primitive<ParamTypes> or (ParamLs * sizeof(ParamTypes) <= 16))
and ...)
1376struct FunctionProxy<PrimitiveExpr<ReturnType, ReturnL>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1378 using type = ReturnType(ParamTypes...);
1379 using dsl_type = PrimitiveExpr<ReturnType, ReturnL>(PrimitiveExpr<ParamTypes, ParamLs>...);
1385 FunctionProxy() =
delete;
1386 FunctionProxy(std::string name) : name_(Module::Unique_Function_Name(name)) { }
1387 FunctionProxy(
const char *name) : FunctionProxy(
std::string(name)) { }
1393 const std::string & name()
const {
return name_; }
1394 const char * c_name()
const {
return name_.c_str(); }
1396 Function<dsl_type> make_function()
const {
return Function<dsl_type>(name_); }
1400 template<
typename... Args>
1401 requires std::is_void_v<ReturnType>
and
1402 requires (Args&&...
args) { (PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args)), ...); }
1403 void operator()(Args&&...
args)
const {
1404 operator()(PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args))...);
1408 void operator()(PrimitiveExpr<ParamTypes, ParamLs>...
args)
const requires std::is_void_v<ReturnType> {
1409 Module::Block().list.push_back(
1410 Module::Builder().makeCall(name_, {
args.expr()... }, wasm_type<ReturnType, ReturnL>())
1415 template<
typename... Args>
1416 requires (not std::is_void_v<ReturnType>)
and
1417 requires (Args&&...
args) { (PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args)), ...); }
1418 PrimitiveExpr<ReturnType, ReturnL> operator()(Args&&...
args)
const {
1419 return operator()(PrimitiveExpr<ParamTypes, ParamLs>(std::forward<Args>(
args))...);
1423 PrimitiveExpr<ReturnType, ReturnL>
1424 operator()(PrimitiveExpr<ParamTypes, ParamLs>...
args)
const requires (not std::is_void_v<ReturnType>) {
1425 return PrimitiveExpr<ReturnType, ReturnL>(
1426 Module::Builder().makeCall(name_, {
args.expr()... }, wasm_type<ReturnType, ReturnL>())
1431template<
typename ReturnType,
typename... ParamTypes>
1432requires (std::is_void_v<ReturnType> or dsl_primitive<ReturnType> or dsl_pointer_to_primitive<ReturnType>)
and
1433 ((dsl_primitive<ParamTypes> or dsl_pointer_to_primitive<ParamTypes>)
and ...)
1435 :
FunctionProxy<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>
1437 using FunctionProxy<PrimitiveExpr<ReturnType, 1>(PrimitiveExpr<ParamTypes, 1>...)>::FunctionProxy;
1440template<
typename... ParamTypes, std::size_t... ParamLs>
1442 :
FunctionProxy<PrimitiveExpr<void, 1>(PrimitiveExpr<ParamTypes, ParamLs>...)>
1452template<
typename T,
typename U, std::
size_t L>
1454requires (PrimitiveExpr<T, L> e) { PrimitiveExpr<common_type_t<T, U>,
L>(e); }
and
1460template<dsl_primitive T, std::
size_t L>
1471 template<
typename, std::
size_t>
1473 template<
typename, VariableKind,
bool, std::
size_t>
1482 template<
typename, std::
size_t>
friend struct std::array;
1495 explicit PrimitiveExpr(::wasm::Expression *
expr, std::list<std::shared_ptr<Bit>> referenced_bits = {})
1497 , referenced_bits_(std::move(referenced_bits))
1508 :
PrimitiveExpr(Module::Builder().makeConst(::wasm::Literal(bytes.data())))
1519 template<dsl_primitive... Us>
1520 requires (
sizeof...(Us) > 0)
and requires (Us... us) { make_literal<T, L>(us...); }
1528 requires (Us... us) {
PrimitiveExpr(std::decay_t<Us>(us)...); }
1529 explicit PrimitiveExpr(Us... value)
1530 : PrimitiveExpr(
std::decay_t<Us>(value)...)
1549 swap(this->expr_, other.expr_);
1550 swap(this->referenced_bits_, other.referenced_bits_);
1560 M_insist(
expr_,
"cannot access an already moved or discarded expression of a `PrimitiveExpr`");
1561 return std::exchange(
expr_,
nullptr);
1567 template<dsl_primitive U = T, std::
size_t M = L>
1568 std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>>
move() {
1579 M_insist(
expr_,
"cannot clone an already moved or discarded `PrimitiveExpr`");
1581 ::wasm::ExpressionManipulator::copy(
expr_, Module::Get().module_),
1590 M_insist(
expr_,
"cannot discard an already moved or discarded `PrimitiveExpr`");
1591 if (
expr_->is<::wasm::Call>())
1592 Module::Block().list.push_back(Module::Builder().makeDrop(
expr_));
1607 template<dsl_primitive ResultType, std::
size_t ResultL>
1610 Module::Builder().makeUnary(
op,
expr()),
1617 template<dsl_primitive ResultType, std::
size_t ResultL, dsl_primitive OperandType, std::
size_t OperandL>
1622 Module::Builder().makeBinary(
op,
1623 this->
template to<OperandType, OperandL>().
expr(),
1635 template<dsl_primitive U, std::
size_t M>
1639 constexpr std::size_t FromL =
L;
1640 constexpr std::size_t
ToL = M;
1642 if constexpr (std::same_as<From, To>
and FromL ==
ToL)
1645 sizeof(From) ==
sizeof(
To)
and FromL ==
ToL)
1650 if constexpr (FromL == 1
and ToL == 1) {
1651 if constexpr (
sizeof(
To) <= 4)
1653 if constexpr (
sizeof(
To) == 8)
1654 return unary<To, ToL>(::wasm::ExtendUInt32);
1656 if constexpr (FromL > 1
and FromL ==
ToL) {
1657 if constexpr (
sizeof(
To) == 1)
1659 if constexpr (std::is_signed_v<To>) {
1660 if constexpr (
sizeof(
To) == 2)
1661 return to<int8_t, ToL>().template to<To, ToL>();
1662 if constexpr (
sizeof(
To) == 4)
1663 return to<int8_t, ToL>().template to<To, ToL>();
1664 if constexpr (
sizeof(
To) == 8)
1665 return to<int8_t, ToL>().template to<To, ToL>();
1667 if constexpr (
sizeof(
To) == 2)
1668 return to<uint8_t, ToL>().template to<To, ToL>();
1669 if constexpr (
sizeof(
To) == 4)
1670 return to<uint8_t, ToL>().template to<To, ToL>();
1671 if constexpr (
sizeof(
To) == 8)
1672 return to<uint8_t, ToL>().template to<To, ToL>();
1676 if constexpr (std::floating_point<To>) {
1677 if constexpr (FromL == 1
and ToL == 1) {
1678 if constexpr (
sizeof(
To) == 4)
1679 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat32);
1680 if constexpr (
sizeof(
To) == 8)
1681 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat64);
1683 if constexpr (FromL > 1
and FromL ==
ToL) {
1684 if constexpr (
sizeof(
To) == 4)
1685 return to<uint32_t, ToL>().template convert<To, ToL>();
1686 if constexpr (
sizeof(
To) == 8)
1687 return to<uint32_t, ToL>().template convert<To, ToL>();
1697 if constexpr (FromL == 1
and ToL == 1) {
1698 if constexpr (std::is_signed_v<From>) {
1699 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1700 return unary<To, ToL>(::wasm::ExtendSInt32);
1701 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1702 return unary<To, ToL>(::wasm::WrapInt64);
1704 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1705 return unary<To, ToL>(::wasm::ExtendUInt32);
1706 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1707 return unary<To, ToL>(::wasm::WrapInt64);
1709 if constexpr (
sizeof(
To) <= 4
and sizeof(From) <
sizeof(
To))
1711 if constexpr (
sizeof(From) <= 4
and sizeof(
To) <
sizeof(From)) {
1712 constexpr From MASK = (uint64_t(1) << (8 *
sizeof(
To))) - uint64_t(1);
1715 if constexpr (
sizeof(From) == 8
and sizeof(
To) < 4) {
1716 if constexpr (std::is_signed_v<To>) {
1717 auto wrapped = unary<int32_t, ToL>(::wasm::WrapInt64);
1718 constexpr int32_t MASK = (int64_t(1) << (8 *
sizeof(
To))) - int64_t(1);
1723 auto wrapped = unary<uint32_t, ToL>(::wasm::WrapInt64);
1724 constexpr uint32_t MASK = (uint64_t(1) << (8 *
sizeof(
To))) - uint64_t(1);
1731 if constexpr (FromL > 1
and FromL ==
ToL) {
1732 if constexpr (std::is_signed_v<From>) {
1733 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL <= 8)
1734 return unary<To, ToL>(::wasm::ExtendLowSVecI8x16ToVecI16x8);
1735 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL == 16) {
1738 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI8x16ToVecI16x8);
1742 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1743 return to<int16_t, ToL>().template to<To, ToL>();
1744 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1745 return to<int32_t, ToL>().template to<To, ToL>();
1746 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL <= 4)
1747 return unary<To, ToL>(::wasm::ExtendLowSVecI16x8ToVecI32x4);
1748 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL == 8) {
1751 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI16x8ToVecI32x4);
1755 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1756 return to<int32_t, ToL>().template to<To, ToL>();
1757 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1758 return unary<To, ToL>(::wasm::ExtendLowSVecI32x4ToVecI64x2);
1759 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1762 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowSVecI32x4ToVecI64x2);
1767 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL <= 8)
1768 return unary<To, ToL>(::wasm::ExtendLowUVecI8x16ToVecI16x8);
1769 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2
and FromL == 16) {
1772 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI8x16ToVecI16x8);
1776 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1777 return to<uint16_t, ToL>().template to<To, ToL>();
1778 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1779 return to<uint32_t, ToL>().template to<To, ToL>();
1780 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL <= 4)
1781 return unary<To, ToL>(::wasm::ExtendLowUVecI16x8ToVecI32x4);
1782 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4
and FromL == 8) {
1785 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI16x8ToVecI32x4);
1789 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1790 return to<uint32_t, ToL>().template to<To, ToL>();
1791 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1792 return unary<To, ToL>(::wasm::ExtendLowUVecI32x4ToVecI64x2);
1793 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1796 clone().template
unary<
To,
ToL / 2>(::wasm::ExtendLowUVecI32x4ToVecI64x2);
1803 if constexpr (std::floating_point<To>) {
1804 if constexpr (FromL == 1
and ToL == 1) {
1805 if constexpr (std::is_signed_v<From>) {
1806 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 4)
1807 return unary<To, ToL>(::wasm::ConvertSInt32ToFloat32);
1808 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1809 return unary<To, ToL>(::wasm::ConvertSInt32ToFloat64);
1810 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1811 return unary<To, ToL>(::wasm::ConvertSInt64ToFloat32);
1812 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1813 return unary<To, ToL>(::wasm::ConvertSInt64ToFloat64);
1815 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 4)
1816 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat32);
1817 if constexpr (
sizeof(From) <= 4
and sizeof(
To) == 8)
1818 return unary<To, ToL>(::wasm::ConvertUInt32ToFloat64);
1819 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1820 return unary<To, ToL>(::wasm::ConvertUInt64ToFloat32);
1821 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1822 return unary<To, ToL>(::wasm::ConvertUInt64ToFloat64);
1825 if constexpr (FromL > 1
and FromL ==
ToL) {
1826 if constexpr (std::is_signed_v<From>) {
1827 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1828 return to<int32_t, ToL>().template to<To, ToL>();
1829 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1830 return to<int32_t, ToL>().template to<To, ToL>();
1831 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
1832 return to<int32_t, ToL>().template to<To, ToL>();
1833 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1834 return to<int32_t, ToL>().template to<To, ToL>();
1835 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1836 return unary<To, ToL>(::wasm::ConvertSVecI32x4ToVecF32x4);
1837 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1838 return unary<To, ToL>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1839 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1842 clone().template
unary<
To,
ToL / 2>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1843 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1845 high_to_low.template
unary<
To,
ToL / 2>(::wasm::ConvertLowSVecI32x4ToVecF64x2);
1849 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
1850 return to<uint32_t, ToL>().template convert<To, ToL>();
1851 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
1852 return to<uint32_t, ToL>().template convert<To, ToL>();
1853 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
1854 return to<uint32_t, ToL>().template convert<To, ToL>();
1855 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
1856 return to<uint32_t, ToL>().template convert<To, ToL>();
1857 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1858 return unary<To, ToL>(::wasm::ConvertUVecI32x4ToVecF32x4);
1859 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1860 return unary<To, ToL>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1861 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1864 clone().template
unary<
To,
ToL / 2>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1865 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1867 high_to_low.template
unary<
To,
ToL / 2>(::wasm::ConvertLowUVecI32x4ToVecF64x2);
1875 if constexpr (std::floating_point<From>) {
1877 if constexpr (FromL == 1
and ToL == 1) {
1878 if constexpr (std::is_signed_v<To>) {
1879 if constexpr (
sizeof(From) == 4
and sizeof(
To) <= 4)
1880 return unary<int32_t, ToL>(::wasm::TruncSFloat32ToInt32).template to<To, ToL>();
1881 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1882 return unary<To, ToL>(::wasm::TruncSFloat32ToInt64);
1883 if constexpr (
sizeof(From) == 8
and sizeof(
To) <= 4)
1884 return unary<int32_t, ToL>(::wasm::TruncSFloat64ToInt32).template to<To, ToL>();
1885 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1886 return unary<To, ToL>(::wasm::TruncSFloat64ToInt64);
1888 if constexpr (
sizeof(From) == 4
and sizeof(
To) <= 4)
1889 return unary<uint32_t, ToL>(::wasm::TruncUFloat32ToInt32).template to<To, ToL>();
1890 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1891 return unary<To, ToL>(::wasm::TruncUFloat32ToInt64);
1892 if constexpr (
sizeof(From) == 8
and sizeof(
To) <= 4)
1893 return unary<uint32_t, ToL>(::wasm::TruncUFloat64ToInt32).template to<To, ToL>();
1894 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1895 return unary<To, ToL>(::wasm::TruncUFloat64ToInt64);
1898 if constexpr (FromL > 1
and FromL ==
ToL) {
1899 if constexpr (std::is_signed_v<To>) {
1900 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1901 return unary<To, ToL>(::wasm::TruncSatSVecF32x4ToVecI32x4);
1902 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1903 return unary<To, ToL>(::wasm::TruncSatZeroSVecF64x2ToVecI32x4);
1904 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1905 return to<int32_t, ToL>().template to<To, ToL>();
1906 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1907 return to<int32_t, ToL>().template to<To, ToL>();
1909 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
1910 return unary<To, ToL>(::wasm::TruncSatUVecF32x4ToVecI32x4);
1911 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1912 return unary<To, ToL>(::wasm::TruncSatZeroUVecF64x2ToVecI32x4);
1913 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1914 return convert<uint32_t, ToL>().template to<To, ToL>();
1915 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
1916 return convert<uint32_t, ToL>().template to<To, ToL>();
1920 if constexpr (std::floating_point<To>) {
1921 if constexpr (FromL == 1
and ToL == 1) {
1922 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
1923 return unary<To, ToL>(::wasm::PromoteFloat32);
1924 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1925 return unary<To, ToL>(::wasm::DemoteFloat64);
1927 if constexpr (FromL > 1
and FromL ==
ToL) {
1928 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL <= 2)
1929 return unary<To, ToL>(::wasm::PromoteLowVecF32x4ToVecF64x2);
1930 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8
and FromL == 4) {
1933 auto high_to_low =
swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
1935 high_to_low.template
unary<
To,
ToL / 2>(::wasm::PromoteLowVecF32x4ToVecF64x2);
1938 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
1939 return unary<To, ToL>(::wasm::DemoteZeroVecF64x2ToVecF32x4);
1956 template<dsl_primitive To, std::
size_t ToL = L>
1957 requires (L == ToL)
and
1960 (
sizeof(
T) <=
sizeof(
To))
1974 template<dsl_primitive To, std::
size_t ToL = L>
1975 requires (L == ToL)
and
1980 std::is_convertible_v<T, To>)
and
1982 (std::is_convertible_v<T, To>
and
1994 template<dsl_po
inter_to_primitive To, std::
size_t ToL = L>
2003 auto make_signed()
requires unsigned_integral<T> {
return PrimitiveExpr<std::make_signed_t<T>, L>(move()); }
2009 auto make_unsigned()
requires signed_integral<T> {
return PrimitiveExpr<std::make_unsigned_t<T>, L>(move()); }
2017 template<dsl_primitive To, std::
size_t ToL = L>
2021 (
sizeof(T) ==
sizeof(
To))
2024 constexpr std::size_t FromL =
L;
2027 if constexpr (std::floating_point<To>) {
2028 if constexpr (FromL == 1) {
2029 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
2030 return unary<To, ToL>(::wasm::ReinterpretInt32);
2031 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
2032 return unary<To, ToL>(::wasm::ReinterpretInt64);
2037 if constexpr (std::floating_point<From>) {
2039 if constexpr (FromL == 1) {
2040 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4)
2041 return unary<To, ToL>(::wasm::ReinterpretFloat32);
2042 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
2043 return unary<To, ToL>(::wasm::ReinterpretFloat64);
2052 template<std::
size_t ToL>
2053 requires (
L == 1)
and (ToL > 1)
and (ToL *
sizeof(T) <= 16)
2056 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI8x16);
2059 if constexpr (
sizeof(
T) == 1)
2060 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI8x16);
2061 if constexpr (
sizeof(
T) == 2)
2062 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI16x8);
2063 if constexpr (
sizeof(
T) == 4)
2064 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI32x4);
2065 if constexpr (
sizeof(
T) == 8)
2066 return unary<T, ToL>(::wasm::UnaryOp::SplatVecI64x2);
2069 if constexpr (std::floating_point<T>) {
2070 if constexpr (
sizeof(
T) == 4)
2071 return unary<T, ToL>(::wasm::UnaryOp::SplatVecF32x4);
2072 if constexpr (
sizeof(
T) == 8)
2073 return unary<T, ToL>(::wasm::UnaryOp::SplatVecF64x2);
2079 template<std::
size_t ToL>
2080 requires (L == 1)
and (ToL > 1)
and (ToL *
sizeof(T) > 16)
2081 PrimitiveExpr<T, ToL> broadcast() {
2082 using ResT = PrimitiveExpr<T, ToL>;
2083 std::array<typename ResT::vector_type, ResT::num_vectors> vectors;
2084 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx)
2085 vectors[idx] = clone().template broadcast<ResT::vector_type::num_simd_lanes>();
2086 return ResT(std::move(vectors));
2094#define UNOP_(NAME, TYPE) (::wasm::UnaryOp::NAME##TYPE)
2095#define UNIOP_(NAME) [] { \
2096 if constexpr (sizeof(T) == 8) \
2097 return UNOP_(NAME,Int64); \
2098 else if constexpr (sizeof(T) <= 4) \
2099 return UNOP_(NAME,Int32); \
2101 M_unreachable("unsupported operation"); \
2103#define UNFOP_(NAME) [] { \
2104 if constexpr (sizeof(T) == 8) \
2105 return UNOP_(NAME,Float64); \
2106 else if constexpr (sizeof(T) == 4) \
2107 return UNOP_(NAME,Float32); \
2109 M_unreachable("unsupported operation"); \
2111#define UNVOP_(NAME, TYPE) (::wasm::UnaryOp::NAME##Vec##TYPE)
2112#define UNIVOP_(NAME) [] { \
2113 if constexpr (sizeof(T) == 8) \
2114 return UNVOP_(NAME,I64x2); \
2115 else if constexpr (sizeof(T) == 4) \
2116 return UNVOP_(NAME,I32x4); \
2117 else if constexpr (sizeof(T) == 2) \
2118 return UNVOP_(NAME,I16x8); \
2119 else if constexpr (sizeof(T) == 1) \
2120 return UNVOP_(NAME,I8x16); \
2122 M_unreachable("unsupported operation"); \
2124#define UNFVOP_(NAME) [] { \
2125 if constexpr (sizeof(T) == 8) \
2126 return UNVOP_(NAME,F64x2); \
2127 else if constexpr (sizeof(T) == 4) \
2128 return UNVOP_(NAME,F32x4); \
2130 M_unreachable("unsupported operation"); \
2132#define UNARY_VOP(NAME) [] { \
2133 if constexpr (std::integral<T>) \
2134 return UNIVOP_(NAME); \
2135 else if constexpr (std::floating_point<T>) \
2136 return UNFVOP_(NAME); \
2138 M_unreachable("unsupported operation"); \
2146 PrimitiveExpr
operator-()
requires std::floating_point<T>
and (L == 1) {
return unary<T, L>(
UNFOP_(Neg)); }
2147 PrimitiveExpr operator-()
requires arithmetic<T>
and (L > 1) {
return unary<T, L>(
UNARY_VOP(Neg)); }
2162 static_assert(
L % 2 == 0,
"must mask this expression first");
2163 auto vec =
unary<int16_t,
L / 2>(
UNVOP_(ExtAddPairwiseS, I8x16ToI16x8));
2165 vec.template extract_unsafe<0>(),
2168 PrimitiveExpr<uint16_t, L / 2> add_pairwise()
requires unsigned_integral<T>
and (sizeof(T) == 1)
and (L > 1) {
2169 static_assert(L % 2 == 0,
"must mask this expression first");
2170 auto vec = unary<uint16_t, L / 2>(
UNVOP_(ExtAddPairwiseU, I8x16ToI16x8));
2172 vec.template extract_unsafe<0>(),
2175 PrimitiveExpr<int32_t, L / 2> add_pairwise()
requires signed_integral<T>
and (sizeof(T) == 2)
and (L > 1) {
2176 static_assert(L % 2 == 0,
"must mask this expression first");
2177 auto vec = unary<int32_t, L / 2>(
UNVOP_(ExtAddPairwiseS, I16x8ToI32x4));
2179 vec.template extract_unsafe<0>(),
2183 static_assert(
L % 2 == 0,
"must mask this expression first");
2184 auto vec =
unary<uint32_t,
L / 2>(
UNVOP_(ExtAddPairwiseU, I16x8ToI32x4));
2186 vec.template extract_unsafe<0>(),
2193 PrimitiveExpr operator~() requires integral<T>
and (L > 1) {
return unary<T, L>(
UNVOP_(Not, 128)); }
2196 return unary<T, L>(
UNIOP_(Clz));
2205 PrimitiveExpr popcnt()
requires unsigned_integral<T>
and (L == 1) {
return unary<T, L>(
UNIOP_(Popcnt)); }
2207 return unary<T, L>(
UNVOP_(Popcnt, I8x16));
2210 auto popcnt_on_I8x16 = this->unary<uint8_t, L * 2>(
UNVOP_(Popcnt, I8x16));
2211 return popcnt_on_I8x16.add_pairwise();
2214 auto popcnt_on_I8x16 = this->unary<uint8_t, L * 4>(
UNVOP_(Popcnt, I8x16));
2215 return popcnt_on_I8x16.add_pairwise().add_pairwise();
2224 PrimitiveExpr<uint32_t, 1> bitmask()
requires integral<T>
and (L > 1) {
2225 auto bitmask = unary<uint32_t, 1>(
UNIVOP_(Bitmask));
2226 return M_CONSTEXPR_COND(L *
sizeof(T) == 16, bitmask, bitmask bitand uint32_t((1U << L) - 1U));
2235 PrimitiveExpr
operator not()
requires boolean<T>
and (L == 1) {
return unary<T, L>(
UNIOP_(EqZ)); }
2236 PrimitiveExpr
operator not()
requires boolean<T>
and (L > 1) {
return unary<T, L>(
UNVOP_(Not, 128)); }
2242 return masked.template unary<bool, 1>(
UNVOP_(AnyTrue, 128));
2245 PrimitiveExpr<bool, 1> any_true()
requires integral<T>
and (L > 1) {
2247 M_CONSTEXPR_COND(L *
sizeof(T) == 16, *
this, PrimitiveExpr(
T(-1)) bitand *
this);
2248 return masked.template unary<bool, 1>(
UNVOP_(AnyTrue, 128));
2252 std::array<uint8_t, 16> bytes;
2253 auto it = std::fill_n(bytes.begin(),
L, 0);
2254 std::fill(it, bytes.end(), 0xff);
2257 return masked.template unary<bool, 1>(
UNVOP_(AllTrue, I8x16));
2260 PrimitiveExpr<bool, 1> all_true()
requires integral<T>
and (L > 1) {
2261 std::array<uint8_t, 16> bytes;
2262 auto it = std::fill_n(bytes.begin(), L, 0);
2263 std::fill(it, bytes.end(), 1);
2265 M_CONSTEXPR_COND(L *
sizeof(T) == 16, *
this, PrimitiveExpr(bytes) bitor *
this);
2266 return masked.template unary<bool, 1>(
UNIVOP_(AllTrue));
2272 PrimitiveExpr<uint64_t, L> hash()
requires signed_integral<T>
and (L == 1) {
return make_unsigned(); }
2273 PrimitiveExpr<uint64_t, L> hash()
requires std::floating_point<T>
and (sizeof(T) == 4)
and (L == 1) {
2274 return reinterpret<int32_t>().make_unsigned();
2276 PrimitiveExpr<uint64_t, L>
hash()
requires std::floating_point<T>
and (sizeof(T) == 8)
and (L == 1) {
2277 return reinterpret<int64_t>().make_unsigned();
2279 PrimitiveExpr<uint64_t, L>
hash()
requires std::same_as<T,
bool>
and (L == 1) {
return to<uint64_t>(); }
2294#define BINOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##TYPE)
2295#define BINIOP_(NAME, SIGN) [] { \
2296 if constexpr (sizeof(To) == 8) \
2297 return BINOP_(NAME,SIGN,Int64); \
2298 else if constexpr (sizeof(To) <= 4) \
2299 return BINOP_(NAME,SIGN,Int32); \
2301 M_unreachable("unsupported operation"); \
2303#define BINFOP_(NAME) [] { \
2304 if constexpr (sizeof(To) == 8) \
2305 return BINOP_(NAME,,Float64); \
2306 else if constexpr (sizeof(To) == 4) \
2307 return BINOP_(NAME,,Float32); \
2309 M_unreachable("unsupported operation"); \
2311#define BINARY_OP(NAME, SIGN) [] { \
2312 if constexpr (std::integral<To>) \
2313 return BINIOP_(NAME, SIGN); \
2314 else if constexpr (std::floating_point<To>) \
2315 return BINFOP_(NAME); \
2317 M_unreachable("unsupported operation"); \
2319#define BINVOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##Vec##TYPE)
2320#define BINIVOP_(NAME, SIGN) [] { \
2321 if constexpr (sizeof(To) == 8) \
2322 return BINVOP_(NAME,SIGN,I64x2); \
2323 else if constexpr (sizeof(To) == 4) \
2324 return BINVOP_(NAME,SIGN,I32x4); \
2325 else if constexpr (sizeof(To) == 2) \
2326 return BINVOP_(NAME,SIGN,I16x8); \
2327 else if constexpr (sizeof(To) == 1) \
2328 return BINVOP_(NAME,SIGN,I8x16); \
2330 M_unreachable("unsupported operation"); \
2332#define BINFVOP_(NAME) [] { \
2333 if constexpr (sizeof(To) == 8) \
2334 return BINVOP_(NAME,,F64x2); \
2335 else if constexpr (sizeof(To) == 4) \
2336 return BINVOP_(NAME,,F32x4); \
2338 M_unreachable("unsupported operation"); \
2340#define BINARY_VOP(NAME, SIGN) [] { \
2341 if constexpr (std::integral<To>) \
2342 return BINIVOP_(NAME, SIGN); \
2343 else if constexpr (std::floating_point<To>) \
2344 return BINFVOP_(NAME); \
2346 M_unreachable("unsupported operation"); \
2352 template<arithmetic U>
2356 if constexpr (
L *
sizeof(
To) <= 16)
2359 return this->
template to<To, L>().operator+(other.template to<To, L>());
2363 template<arithmetic U>
2367 if constexpr (
L *
sizeof(
To) <= 16)
2370 return this->
template to<To, L>().operator-(other.template to<To, L>());
2374 template<arithmetic U>
2375 requires arithmetically_combinable<T, U, L>
2378 return binary<To, L, To, L>(
BINARY_OP(Mul,), other);
2381 template<arithmetic U>
2386 if constexpr (std::integral<To>) {
2387 if constexpr (
sizeof(
To) == 8)
2389 else if constexpr (
sizeof(
To) == 4)
2391 else if constexpr (
sizeof(
To) == 2)
2393 }
else if (std::floating_point<To>) {
2398 if constexpr (
L *
sizeof(
To) <= 16)
2399 return binary<To, L, To, L>(op, other);
2401 return this->
template to<To, L>().operator*(other.template to<To, L>());
2404 template<arithmetic U>
2408 static_assert(
L == 16);
2409 using To = std::conditional_t<std::is_signed_v<T>, int16_t, uint16_t>;
2422 std::move(referenced_bits_low)
2425 Module::Builder().makeBinary(op_high, this->expr(), other.expr()),
2426 std::move(referenced_bits_high)
2430 auto indices = std::to_array<uint8_t>({ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 });
2435 template<arithmetic U>
2443 template<std::
floating_po
int U>
2444 requires arithmetically_combinable<T, U, L>
2446 requires std::floating_point<T>
and (L > 1) {
2448 if constexpr (
L *
sizeof(
To) <= 16)
2449 return binary<To, L, To, L>(
BINFVOP_(Div), other);
2451 return this->
template to<To, L>().operator/(other.template to<To, L>());
2455 template<
integral U>
2464 template<std::
floating_po
int U>
2465 requires arithmetically_combinable<T, U, L>
2467 requires std::floating_point<T>
and (L == 1) {
2469 return binary<To, L, To, L>(
BINFOP_(CopySign), other);
2473 template<std::
floating_po
int U>
2474 requires arithmetically_combinable<T, U, L>
2477 if constexpr (
L *
sizeof(
To) <= 16)
2480 return this->
template to<To, L>().min(other.template to<To, L>());
2483 template<
integral U>
2488 if constexpr (
sizeof(
To) == 4)
2490 else if constexpr (
sizeof(
To) == 2)
2492 else if constexpr (
sizeof(
To) == 1)
2496 if constexpr (
L *
sizeof(
To) <= 16)
2497 return binary<To, L, To, L>(
op, other);
2499 return this->
template to<To, L>().min(other.template to<To, L>());
2503 template<std::
floating_po
int U>
2504 requires arithmetically_combinable<T, U, L>
2507 if constexpr (
L *
sizeof(
To) <= 16)
2510 return this->
template to<To, L>().max(other.template to<To, L>());
2513 template<
integral U>
2518 if constexpr (
sizeof(To) == 4)
2520 else if constexpr (
sizeof(To) == 2)
2522 else if constexpr (
sizeof(To) == 1)
2526 if constexpr (L *
sizeof(To) <= 16)
2527 return binary<To, L, To, L>(op, other);
2529 return this->
template to<To, L>().max(other.template to<To, L>());
2533 template<
unsigned_
integral U>
2539 if constexpr (
sizeof(
To) == 2)
2541 else if constexpr (
sizeof(
To) == 1)
2545 if constexpr (
L *
sizeof(
To) <= 16)
2546 return binary<To, L, To, L>(
op, other);
2548 return this->
template to<To, L>().avg(other.template to<To, L>());
2554 template<std::
integral U>
2555 requires arithmetically_combinable<T, U, L>
2558 if constexpr (
L *
sizeof(
To) <= 16)
2561 return this->
template to<To, L>().operator bitand(other.template to<To, L>());
2565 template<std::
integral U>
2566 requires arithmetically_combinable<T, U, L>
2567 auto operator bitor(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>, L>
requires std::integral<T> {
2569 if constexpr (L *
sizeof(To) <= 16)
2572 return this->
template to<To, L>().operator bitor(other.template to<To, L>());
2576 template<std::
integral U>
2577 requires arithmetically_combinable<T, U, L>
2578 auto operator xor(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>, L>
requires std::integral<T> {
2579 using To = common_type_t<T, U>;
2580 if constexpr (L *
sizeof(To) <= 16)
2583 return this->
template to<To, L>().operator xor(other.template to<To, L>());
2587 template<
integral U>
2588 requires arithmetically_combinable<T, U, L>
2589 auto operator<<(PrimitiveExpr<U, L> other) -> PrimitiveExpr<common_type_t<T, U>,
L>
2590 requires integral<T>
and (L == 1) {
2591 using To = common_type_t<T, U>;
2592 if constexpr (
sizeof(
To) >= 4)
2593 return binary<To, L, To, L>(
BINIOP_(Shl,), other);
2594 else if constexpr (
sizeof(
To) == 2)
2595 return binary<To, L, To, L>(
BINOP_(Shl,, Int32), other) bitand PrimitiveExpr<To, 1>(0xffff);
2596 else if constexpr (
sizeof(
To) == 1)
2597 return binary<To, L, To, L>(
BINOP_(Shl,, Int32), other) bitand PrimitiveExpr<To, 1>(0xff);
2602 template<
integral U>
2603 requires requires (PrimitiveExpr<U, 1> e) {
2604 PrimitiveExpr<std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>, 1>(e);
2608 using Op = std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>;
2610 if constexpr (
sizeof(
T) == 8)
2611 return ::wasm::SIMDShiftOp::ShlVecI64x2;
2612 else if constexpr (
sizeof(
T) == 4)
2613 return ::wasm::SIMDShiftOp::ShlVecI32x4;
2614 else if constexpr (
sizeof(
T) == 2)
2615 return ::wasm::SIMDShiftOp::ShlVecI16x8;
2616 else if constexpr (
sizeof(
T) == 1)
2617 return ::wasm::SIMDShiftOp::ShlVecI8x16;
2630 template<
integral U>
2631 requires arithmetically_combinable<T, U, L>
2638 template<
integral U>
2639 requires requires (PrimitiveExpr<U, 1> e) {
2640 PrimitiveExpr<std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>, 1>(e);
2642 PrimitiveExpr operator>>(PrimitiveExpr<U, 1> other)
2643 requires integral<T>
and (L > 1) {
2644 using Op = std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>;
2646 if constexpr (
sizeof(T) == 8)
2647 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI64x2,
2648 ::wasm::SIMDShiftOp::ShrUVecI64x2);
2649 else if constexpr (
sizeof(T) == 4)
2650 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI32x4,
2651 ::wasm::SIMDShiftOp::ShrUVecI32x4);
2652 else if constexpr (
sizeof(T) == 2)
2653 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI16x8,
2654 ::wasm::SIMDShiftOp::ShrUVecI16x8);
2655 else if constexpr (
sizeof(T) == 1)
2656 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDShiftOp::ShrSVecI8x16,
2657 ::wasm::SIMDShiftOp::ShrUVecI8x16);
2664 Module::Builder().makeSIMDShift(op, this->expr(), PrimitiveExpr<Op, 1>(other).
expr()),
2665 std::move(referenced_bits)
2670 template<
integral U>
2674 return binary<To, L, To, L>(
BINIOP_(RotL,), other);
2678 template<
integral U>
2682 return binary<To, L, To, L>(
BINIOP_(RotR,), other);
2688 template<dsl_primitive U>
2692 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2693 if constexpr (
L *
sizeof(
To) <= 16) {
2695 std::array<uint8_t, L>
indices;
2696 for (std::size_t idx = 0; idx <
L; ++idx)
2700 return this->
template to<To, L>().operator==(other.template to<To, L>());
2705 template<dsl_primitive U>
2709 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2710 if constexpr (
L *
sizeof(
To) <= 16) {
2712 std::array<uint8_t, L>
indices;
2713 for (std::size_t idx = 0; idx <
L; ++idx)
2717 return this->
template to<To, L>().operator!=(other.template to<To, L>());
2722 template<arithmetic U>
2726 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2728 if constexpr (
L == 1) {
2731 if constexpr (std::integral<To>) {
2732 if constexpr (
sizeof(
To) == 8)
2734 else if constexpr (
sizeof(
To) == 4)
2736 else if constexpr (
sizeof(
To) == 2)
2738 else if constexpr (
sizeof(
To) == 1)
2740 }
else if (std::floating_point<To>) {
2746 if constexpr (
L *
sizeof(
To) <= 16) {
2754 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2755 std::array<uint8_t, L>
indices;
2756 for (std::size_t idx = 0; idx <
L; ++idx)
2760 return this->
template to<To, L>().operator<(other.template to<To, L>());
2765 template<arithmetic U>
2769 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2771 if constexpr (
L == 1) {
2774 if constexpr (std::integral<To>) {
2775 if constexpr (
sizeof(
To) == 8)
2777 else if constexpr (
sizeof(
To) == 4)
2779 else if constexpr (
sizeof(
To) == 2)
2781 else if constexpr (
sizeof(
To) == 1)
2783 }
else if (std::floating_point<To>) {
2789 if constexpr (
L *
sizeof(
To) <= 16) {
2797 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2798 std::array<uint8_t, L>
indices;
2799 for (std::size_t idx = 0; idx <
L; ++idx)
2803 return this->
template to<To, L>().operator<=(other.template to<To, L>());
2808 template<arithmetic U>
2812 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2814 if constexpr (
L == 1) {
2817 if constexpr (std::integral<To>) {
2818 if constexpr (
sizeof(
To) == 8)
2820 else if constexpr (
sizeof(
To) == 4)
2822 else if constexpr (
sizeof(
To) == 2)
2824 else if constexpr (
sizeof(
To) == 1)
2826 }
else if (std::floating_point<To>) {
2832 if constexpr (
L *
sizeof(
To) <= 16) {
2840 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2841 std::array<uint8_t, L>
indices;
2842 for (std::size_t idx = 0; idx <
L; ++idx)
2846 return this->
template to<To, L>().operator>(other.template to<To, L>());
2851 template<arithmetic U>
2855 constexpr std::size_t
ToL =
L == 1 ?
L :
L *
sizeof(
T);
2857 if constexpr (
L == 1) {
2860 if constexpr (std::integral<To>) {
2861 if constexpr (
sizeof(
To) == 8)
2863 else if constexpr (
sizeof(
To) == 4)
2865 else if constexpr (
sizeof(
To) == 2)
2867 else if constexpr (
sizeof(
To) == 1)
2869 }
else if (std::floating_point<To>) {
2875 if constexpr (
L *
sizeof(
To) <= 16) {
2883 auto cmp = _this.template binary<bool, ToL, To, L>(
op, _other);
2884 std::array<uint8_t, L>
indices;
2885 for (std::size_t idx = 0; idx <
L; ++idx)
2889 return this->
template to<To, L>().operator>=(other.template to<To, L>());
2898 return binary<T, L, T, L>(
M_CONSTEXPR_COND(
L == 1,
BINOP_(And,,Int32),
BINVOP_(And,, 128)), other);
2904 return binary<T, L, T, L>(
BINVOP_(AndNot,, 128), other);
2910 return binary<T, L, T, L>(
M_CONSTEXPR_COND(
L == 1,
BINOP_(Or,,Int32),
BINVOP_(Or,, 128)), other);
2931 template<std::
size_t M>
2932 requires (M *
sizeof(
T) < 16)
2935 if constexpr (std::integral<T>) {
2936 if constexpr (
sizeof(
T) == 8)
2937 return ::wasm::SIMDExtractOp::ExtractLaneVecI64x2;
2938 else if constexpr (
sizeof(
T) == 4)
2939 return ::wasm::SIMDExtractOp::ExtractLaneVecI32x4;
2940 else if constexpr (
sizeof(
T) == 2)
2941 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDExtractOp::ExtractLaneSVecI16x8,
2942 ::wasm::SIMDExtractOp::ExtractLaneUVecI16x8);
2943 else if constexpr (
sizeof(
T) == 1)
2944 return M_CONSTEXPR_COND(std::is_signed_v<T>, ::wasm::SIMDExtractOp::ExtractLaneSVecI8x16,
2945 ::wasm::SIMDExtractOp::ExtractLaneUVecI8x16);
2946 }
else if (std::floating_point<T>) {
2947 if constexpr (
sizeof(
T) == 8)
2948 return ::wasm::SIMDExtractOp::ExtractLaneVecF64x2;
2949 else if constexpr (
sizeof(
T) == 4)
2950 return ::wasm::SIMDExtractOp::ExtractLaneVecF32x4;
2955 Module::Builder().makeSIMDExtract(
op,
expr(), M),
2962 template<std::
size_t M>
2967 template<std::
size_t M, primitive_convertible U>
2968 requires (M < L)
and
2969 requires (primitive_expr_t<U> u) { PrimitiveExpr<T, 1>(u); }
2972 if constexpr (std::integral<T>) {
2973 if constexpr (
sizeof(
T) == 8)
2974 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI64x2;
2975 else if constexpr (
sizeof(
T) == 4)
2976 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI32x4;
2977 else if constexpr (
sizeof(
T) == 2)
2978 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI16x8;
2979 else if constexpr (
sizeof(
T) == 1)
2980 return ::wasm::SIMDReplaceOp::ReplaceLaneVecI8x16;
2981 }
else if (std::floating_point<T>) {
2982 if constexpr (
sizeof(
T) == 8)
2983 return ::wasm::SIMDReplaceOp::ReplaceLaneVecF64x2;
2984 else if constexpr (
sizeof(
T) == 4)
2985 return ::wasm::SIMDReplaceOp::ReplaceLaneVecF32x4;
2995 Module::Builder().makeSIMDReplace(
op, this->expr(), M, replacement.expr()),
3006 Module::Builder().makeBinary(::wasm::BinaryOp::SwizzleVecI8x16,
3014 template<std::
size_t M>
3015 requires (M > 0)
and (M <= 16)
and (M %
sizeof(T) == 0)
3016 PrimitiveExpr<T, M / sizeof(T)> swizzle_bytes(const
std::array<uint8_t, M> &_indices) requires (L > 1) {
3017 std::array<uint8_t, 16>
indices;
3018 for (std::size_t idx = 0; idx < M; ++idx)
3019 indices[idx] = _indices[idx] <
L *
sizeof(
T) ? _indices[idx] : 16;
3022 Module::Builder().makeConst(::wasm::Literal(
indices.data()))
3026 vec.template extract_unsafe<0>(),
3032 template<std::
size_t M>
3035 std::array<uint8_t, 16>
indices;
3036 for (std::size_t idx = 0; idx < M; ++idx) {
3037 for (std::size_t
byte = 0;
byte <
sizeof(
T); ++byte)
3038 indices[idx *
sizeof(
T) + byte] = _indices[idx] <
L ? _indices[idx] *
sizeof(
T) +
byte : 16;
3042 Module::Builder().makeConst(::wasm::Literal(
indices.data()))
3046 vec.template extract_unsafe<0>(),
3056 out <<
"PrimitiveExpr<" <<
typeid(type).name() <<
"," <<
num_simd_lanes <<
">: ";
3057 if (P.expr_) out << *P.expr_;
3062 void dump(std::ostream &out)
const { out << *
this << std::endl; }
3069template<dsl_primitive T, std::
size_t L>
3084 template<
typename, std::
size_t>
friend struct PrimitiveExpr;
3085 template<
typename, std::
size_t>
3087 template<
typename, VariableKind,
bool, std::
size_t>
3090 friend struct Block;
3115 template<dsl_primitive... Us>
3116 requires (
sizeof...(Us) > 0)
and
3117 requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, num_vectors>>; }
3120 std::array<vector_type, num_vectors>
vectors;
3122 for (
auto literal : make_literal<T, L>(
value...))
3123 *(it++) =
vector_type(Module::Builder().makeConst(literal));
3125 return std::move(vectors);
3130 template<decayable... Us>
3131 requires (
sizeof...(Us) > 0)
and (dsl_primitive<std::decay_t<Us>>
and ...)
and
3132 requires (Us... us) {
PrimitiveExpr(std::decay_t<Us>(us)...); }
3151 template<dsl_primitive U, std::
size_t M>
3152 requires ((M *
sizeof(
U)) / 16 == num_vectors)
3155 std::array<ToVecT, num_vectors>
vectors;
3156 for (std::size_t idx = 0; idx <
num_vectors; ++idx)
3164 explicit operator bool()
const {
3165 return std::all_of(vectors_.cbegin(), vectors_.cend(), [](
const auto &expr){ return bool(expr); });
3169 PrimitiveExpr clone()
const {
3170 M_insist(
bool(*
this),
"cannot clone an already moved or discarded `PrimitiveExpr`");
3171 std::array<vector_type, num_vectors> vectors_cpy;
3172 for (std::size_t idx = 0; idx < num_vectors; ++idx)
3173 vectors_cpy[idx] = vectors_[idx].clone();
3174 return PrimitiveExpr(
3175 std::move(vectors_cpy)
3183 M_insist(
bool(*
this),
"cannot discard an already moved or discarded `PrimitiveExpr`");
3184 std::for_each(
vectors_.begin(),
vectors_.end(), [](
auto &expr){ expr.discard(); });
3193 template<dsl_primitive U, std::
size_t M>
3194 requires ((M *
sizeof(
U)) % 16 == 0)
3198 constexpr std::size_t FromL =
L;
3199 constexpr std::size_t
ToL = M;
3202 constexpr std::size_t FromVecL = (
L *
sizeof(
T)) / 16;
3203 constexpr std::size_t ToVecL = (M *
sizeof(
U)) / 16;
3205 if constexpr (std::same_as<From, To>
and FromL ==
ToL)
3208 sizeof(From) ==
sizeof(
To)
and FromL ==
ToL)
3213 if constexpr (FromL ==
ToL) {
3214 if constexpr (
sizeof(
To) == 1)
3216 if constexpr (std::is_signed_v<To>) {
3217 if constexpr (
sizeof(
To) == 2)
3218 return to<int8_t, ToL>().template to<To, ToL>();
3219 if constexpr (
sizeof(
To) == 4)
3220 return to<int8_t, ToL>().template to<To, ToL>();
3221 if constexpr (
sizeof(
To) == 8)
3222 return to<int8_t, ToL>().template to<To, ToL>();
3224 if constexpr (
sizeof(
To) == 2)
3225 return to<uint8_t, ToL>().template to<To, ToL>();
3226 if constexpr (
sizeof(
To) == 4)
3227 return to<uint8_t, ToL>().template to<To, ToL>();
3228 if constexpr (
sizeof(
To) == 8)
3229 return to<uint8_t, ToL>().template to<To, ToL>();
3233 if constexpr (std::floating_point<To>) {
3234 if constexpr (FromL ==
ToL) {
3235 if constexpr (
sizeof(
To) == 4)
3236 return to<uint32_t, ToL>().template convert<To, ToL>();
3237 if constexpr (
sizeof(
To) == 8)
3238 return to<uint32_t, ToL>().template convert<To, ToL>();
3245 if constexpr (FromL ==
ToL) {
3246 if constexpr (
sizeof(From) == 1)
3248 if constexpr (std::is_signed_v<From>) {
3249 if constexpr (
sizeof(From) == 2)
3250 return to<int8_t, ToL>().template to<To, ToL>();
3251 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3252 return to<int8_t, ToL>().template to<To, ToL>();
3253 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3254 return to<int8_t, ToL>().template to<To, ToL>();
3256 if constexpr (
sizeof(From) == 2)
3257 return to<uint8_t, ToL>().template to<To, ToL>();
3258 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3259 return to<uint8_t, ToL>().template to<To, ToL>();
3260 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3261 return to<uint8_t, ToL>().template to<To, ToL>();
3265 if constexpr (std::floating_point<From>) {
3266 if constexpr (FromL ==
ToL) {
3267 if constexpr (
sizeof(From) == 4
and FromVecL >= 4)
3269 if constexpr (
sizeof(From) == 8
and FromVecL >= 8)
3277 if constexpr (FromL ==
ToL) {
3278 if constexpr (std::is_signed_v<From>) {
3279 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2) {
3280 std::array<ToVecT, ToVecL>
vectors;
3281 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3282 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3283 ::wasm::ExtendLowSVecI8x16ToVecI16x8
3285 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3286 ::wasm::ExtendHighSVecI8x16ToVecI16x8
3291 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 1) {
3292 std::array<ToVecT, ToVecL>
vectors;
3293 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3294 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3295 ::wasm::NarrowSVecI16x8ToVecI8x16,
vectors_[2 * idx + 1]
3299 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3300 return to<int16_t, ToL>().template to<To, ToL>();
3301 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3302 return to<int16_t, ToL>().template to<To, ToL>();
3303 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3304 return to<int32_t, ToL>().template to<To, ToL>();
3305 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3306 return to<int16_t, ToL>().template to<To, ToL>();
3307 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4) {
3308 std::array<ToVecT, ToVecL>
vectors;
3309 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3310 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3311 ::wasm::ExtendLowSVecI16x8ToVecI32x4
3313 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3314 ::wasm::ExtendHighSVecI16x8ToVecI32x4
3319 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2) {
3320 std::array<ToVecT, ToVecL>
vectors;
3321 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3322 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3323 ::wasm::NarrowSVecI32x4ToVecI16x8,
vectors_[2 * idx + 1]
3327 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3328 return to<int32_t, ToL>().template to<To, ToL>();
3329 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3330 return to<int32_t, ToL>().template to<To, ToL>();
3331 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3332 std::array<ToVecT, ToVecL>
vectors;
3333 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3334 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3335 ::wasm::ExtendLowSVecI32x4ToVecI64x2
3337 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3338 ::wasm::ExtendHighSVecI32x4ToVecI64x2
3343 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3344 std::array<ToVecT, ToVecL>
vectors;
3345 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3346 std::array<uint8_t, 16>
indices =
3347 { 0, 1, 2, 3, 8, 9 , 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 };
3354 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 2) {
3355 std::array<ToVecT, ToVecL>
vectors;
3356 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3357 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3358 ::wasm::ExtendLowUVecI8x16ToVecI16x8
3360 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3361 ::wasm::ExtendHighUVecI8x16ToVecI16x8
3366 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 1) {
3367 std::array<ToVecT, ToVecL>
vectors;
3368 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3369 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3370 ::wasm::NarrowSVecI16x8ToVecI8x16,
vectors_[2 * idx + 1]
3374 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3375 return to<uint16_t, ToL>().template to<To, ToL>();
3376 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3377 return to<uint16_t, ToL>().template to<To, ToL>();
3378 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3379 return to<uint32_t, ToL>().template to<To, ToL>();
3380 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3381 return to<uint16_t, ToL>().template to<To, ToL>();
3382 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4) {
3383 std::array<ToVecT, ToVecL>
vectors;
3384 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3385 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3386 ::wasm::ExtendLowUVecI16x8ToVecI32x4
3388 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3389 ::wasm::ExtendHighUVecI16x8ToVecI32x4
3394 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2) {
3395 std::array<ToVecT, ToVecL>
vectors;
3396 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3397 vectors[idx] =
vectors_[2 * idx].
template binary<To, ToVecT::num_simd_lanes>(
3398 ::wasm::NarrowSVecI32x4ToVecI16x8,
vectors_[2 * idx + 1]
3402 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3403 return to<uint32_t, ToL>().template to<To, ToL>();
3404 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3405 return to<uint32_t, ToL>().template to<To, ToL>();
3406 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3407 std::array<ToVecT, ToVecL>
vectors;
3408 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3409 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3410 ::wasm::ExtendLowUVecI32x4ToVecI64x2
3412 vectors[2 * idx + 1] =
vectors_[idx].template unary<To, ToVecT::num_simd_lanes>(
3413 ::wasm::ExtendHighUVecI32x4ToVecI64x2
3418 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3419 std::array<ToVecT, ToVecL>
vectors;
3420 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3421 std::array<uint8_t, 16>
indices =
3422 { 0, 1, 2, 3, 8, 9 , 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 };
3431 if constexpr (std::floating_point<To>) {
3432 if constexpr (FromL ==
ToL) {
3433 if constexpr (std::is_signed_v<From>) {
3434 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3435 return to<int32_t, ToL>().template to<To, ToL>();
3436 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3437 return to<int32_t, ToL>().template to<To, ToL>();
3438 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
3439 return to<int32_t, ToL>().template to<To, ToL>();
3440 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3441 return to<int32_t, ToL>().template to<To, ToL>();
3442 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3443 std::array<ToVecT, ToVecL>
vectors;
3444 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3445 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3446 ::wasm::ConvertSVecI32x4ToVecF32x4
3450 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3451 std::array<ToVecT, ToVecL>
vectors;
3452 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3453 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3454 ::wasm::ConvertLowSVecI32x4ToVecF64x2
3456 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3457 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3458 ::wasm::ConvertLowSVecI32x4ToVecF64x2
3463 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
3464 return to<int32_t, ToL>().template to<To, ToL>();
3465 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3466 return to<int32_t, ToL>().template to<To, ToL>();
3468 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 4)
3469 return to<uint32_t, ToL>().template convert<To, ToL>();
3470 if constexpr (
sizeof(From) == 1
and sizeof(
To) == 8)
3471 return to<uint32_t, ToL>().template convert<To, ToL>();
3472 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 4)
3473 return to<uint32_t, ToL>().template convert<To, ToL>();
3474 if constexpr (
sizeof(From) == 2
and sizeof(
To) == 8)
3475 return to<uint32_t, ToL>().template convert<To, ToL>();
3476 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3477 std::array<ToVecT, ToVecL>
vectors;
3478 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3479 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3480 ::wasm::ConvertUVecI32x4ToVecF32x4
3484 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3485 std::array<ToVecT, ToVecL>
vectors;
3486 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3487 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3488 ::wasm::ConvertLowUVecI32x4ToVecF64x2
3490 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3491 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3492 ::wasm::ConvertLowUVecI32x4ToVecF64x2
3497 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4)
3498 return to<uint32_t, ToL>().template convert<To, ToL>();
3499 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3500 return to<uint32_t, ToL>().template convert<To, ToL>();
3506 if constexpr (std::floating_point<From>) {
3508 if constexpr (FromL ==
ToL) {
3509 if constexpr (std::is_signed_v<To>) {
3510 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3511 return to<int32_t, ToL>().template to<To, ToL>();
3512 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3513 return to<int32_t, ToL>().template to<To, ToL>();
3514 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2)
3515 return to<int32_t, ToL>().template to<To, ToL>();
3516 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3517 return to<int32_t, ToL>().template to<To, ToL>();
3518 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3519 std::array<ToVecT, ToVecL>
vectors;
3520 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3521 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3522 ::wasm::TruncSatSVecF32x4ToVecI32x4
3526 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3527 std::array<ToVecT, ToVecL>
vectors;
3528 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3529 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3530 ::wasm::TruncSatZeroSVecF64x2ToVecI32x4
3532 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3533 ::wasm::TruncSatZeroSVecF64x2ToVecI32x4
3539 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
3540 return to<int32_t, ToL>().template to<To, ToL>();
3541 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3542 return to<int32_t, ToL>().template to<To, ToL>();
3544 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 1
and FromVecL >= 4)
3545 return convert<uint32_t, ToL>().template to<To, ToL>();
3546 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 1
and FromVecL >= 8)
3547 return convert<uint32_t, ToL>().template to<To, ToL>();
3548 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 2)
3549 return convert<uint32_t, ToL>().template to<To, ToL>();
3550 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 2
and FromVecL >= 4)
3551 return convert<uint32_t, ToL>().template to<To, ToL>();
3552 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 4) {
3553 std::array<ToVecT, ToVecL>
vectors;
3554 for (std::size_t idx = 0; idx < ToVecL; ++idx)
3555 vectors[idx] =
vectors_[idx].
template unary<To, ToVecT::num_simd_lanes>(
3556 ::wasm::TruncSatUVecF32x4ToVecI32x4
3560 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3561 std::array<ToVecT, ToVecL>
vectors;
3562 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3563 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3564 ::wasm::TruncSatZeroUVecF64x2ToVecI32x4
3566 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3567 ::wasm::TruncSatZeroUVecF64x2ToVecI32x4
3573 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8)
3574 return convert<uint32_t, ToL>().template to<To, ToL>();
3575 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 8)
3576 return convert<uint32_t, ToL>().template to<To, ToL>();
3580 if constexpr (std::floating_point<To>) {
3581 if constexpr (FromL ==
ToL) {
3582 if constexpr (
sizeof(From) == 4
and sizeof(
To) == 8) {
3583 std::array<ToVecT, ToVecL>
vectors;
3584 for (std::size_t idx = 0; idx < FromVecL; ++idx) {
3585 vectors[2 * idx] =
vectors_[idx].clone().template unary<To, ToVecT::num_simd_lanes>(
3586 ::wasm::PromoteLowVecF32x4ToVecF64x2
3588 auto high_to_low =
vectors_[idx].swizzle_lanes(std::to_array<uint8_t>({ 2, 3 }));
3589 vectors[2 * idx + 1] = high_to_low.template unary<To, ToVecT::num_simd_lanes>(
3590 ::wasm::PromoteLowVecF32x4ToVecF64x2
3595 if constexpr (
sizeof(From) == 8
and sizeof(
To) == 4) {
3596 std::array<ToVecT, ToVecL>
vectors;
3597 for (std::size_t idx = 0; idx < ToVecL; ++idx) {
3598 auto low =
vectors_[2 * idx].template unary<To, ToVecT::num_simd_lanes>(
3599 ::wasm::DemoteZeroVecF64x2ToVecF32x4
3601 auto high =
vectors_[2 * idx + 1].template unary<To, ToVecT::num_simd_lanes>(
3602 ::wasm::DemoteZeroVecF64x2ToVecF32x4
3624 template<dsl_primitive To, std::
size_t ToL = L>
3625 requires (L == ToL)
and
3628 (
sizeof(
T) <=
sizeof(To))
3629 operator PrimitiveExpr<To, ToL>() {
return convert<To, ToL>(); }
3638 template<dsl_primitive To, std::
size_t ToL = L>
3643 std::is_convertible_v<T, To>
and
3669 auto OP() requires requires (vector_type v) { v.OP(); } { \
3670 using ResVecT = decltype(std::declval<vector_type>().OP()); \
3671 static_assert(ResVecT::num_simd_lanes * sizeof(typename ResVecT::type) == 16, \
3672 "result vectors must be fully utilized"); \
3673 std::array<ResVecT, num_vectors> vectors; \
3674 for (std::size_t idx = 0; idx < num_vectors; ++idx) \
3675 vectors[idx] = vectors_[idx].OP(); \
3676 return PrimitiveExpr<typename ResVecT::type, ResVecT::num_simd_lanes * num_vectors>(std::move(vectors)); \
3696 std::optional<PrimitiveExpr<uint32_t, 1>> res = vectors_[0].bitmask();
3697 for (std::size_t idx = 1; idx < num_vectors; ++idx)
3698 res.emplace((vectors_[idx].bitmask() << uint32_t(idx * vector_type::num_simd_lanes)) bitor *res);
3703 PrimitiveExpr<uint64_t, 1> bitmask()
requires (L > 32)
and (L <= 64)
and requires (vector_type v) { v.bitmask(); } {
3704 std::optional<PrimitiveExpr<uint64_t, 1>> res = vectors_[0].bitmask();
3705 for (std::size_t idx = 1; idx < num_vectors; ++idx)
3706 res.emplace((vectors_[idx].bitmask() << uint64_t(idx * vector_type::num_simd_lanes)) bitor *res);
3712 std::optional<PrimitiveExpr<bool, 1>>
res =
vectors_[0].any_true();
3713 for (std::size_t idx = 1; idx <
num_vectors; ++idx)
3714 res.emplace(vectors_[idx].any_true() or *
res);
3720 std::optional<PrimitiveExpr<bool, 1>>
res =
vectors_[0].all_true();
3721 for (std::size_t idx = 1; idx <
num_vectors; ++idx)
3722 res.emplace(vectors_[idx].all_true()
and *
res);
3732 template<dsl_primitive U> \
3733 requires arithmetically_combinable<T, U, L> and \
3734 requires (typename PrimitiveExpr<common_type_t<T, U>, L>::vector_type left, \
3735 typename PrimitiveExpr<common_type_t<T, U>, L>::vector_type right) \
3736 { left.OP(right); } \
3737 auto OP(PrimitiveExpr<U, L> other) { \
3738 using To = common_type_t<T, U>; \
3739 using OpT = decltype(to<To, L>()); \
3741 decltype(std::declval<typename OpT::vector_type>().OP(std::declval<typename OpT::vector_type>())); \
3742 static_assert(ResVecT::num_simd_lanes * sizeof(typename ResVecT::type) == 16, \
3743 "result vectors must be fully utilized"); \
3744 auto this_converted = this->template to<To, L>(); \
3745 auto other_converted = other.template to<To, L>(); \
3746 std::array<ResVecT, OpT::num_vectors> vectors; \
3747 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx) \
3748 vectors[idx] = this_converted.vectors_[idx].OP(other_converted.vectors_[idx]); \
3749 return PrimitiveExpr<typename ResVecT::type, ResVecT::num_simd_lanes * OpT::num_vectors>(std::move(vectors)); \
3768 template<dsl_primitive U> \
3769 PrimitiveExpr OP(PrimitiveExpr<U, 1> other) requires requires (vector_type v) { v.OP(other); } { \
3770 std::array<vector_type, num_vectors> vectors; \
3771 for (std::size_t idx = 0; idx < num_vectors; ++idx) \
3772 vectors[idx] = vectors_[idx].OP(other.clone()); \
3774 return PrimitiveExpr(std::move(vectors)); \
3781#define BINVOP_(NAME, SIGN, TYPE) (::wasm::BinaryOp::NAME##SIGN##Vec##TYPE)
3782#define BINIVOP_(NAME, SIGN) [] { \
3783 if constexpr (sizeof(To) == 8) \
3784 return BINVOP_(NAME,SIGN,I64x2); \
3785 else if constexpr (sizeof(To) == 4) \
3786 return BINVOP_(NAME,SIGN,I32x4); \
3787 else if constexpr (sizeof(To) == 2) \
3788 return BINVOP_(NAME,SIGN,I16x8); \
3789 else if constexpr (sizeof(To) == 1) \
3790 return BINVOP_(NAME,SIGN,I8x16); \
3792 M_unreachable("unsupported operation"); \
3794#define BINFVOP_(NAME) [] { \
3795 if constexpr (sizeof(To) == 8) \
3796 return BINVOP_(NAME,,F64x2); \
3797 else if constexpr (sizeof(To) == 4) \
3798 return BINVOP_(NAME,,F32x4); \
3800 M_unreachable("unsupported operation"); \
3802#define BINARY_VOP(NAME, SIGN) [] { \
3803 if constexpr (std::integral<To>) \
3804 return BINIVOP_(NAME, SIGN); \
3805 else if constexpr (std::floating_point<To>) \
3806 return BINFVOP_(NAME); \
3808 M_unreachable("unsupported operation"); \
3814 if constexpr (
L > 16) {
3815 auto narrowed = to<uint8_t, L>();
3817 }
else if constexpr (
L == 16) {
3818 auto narrowed = to<uint8_t, L>();
3822 std::array<uint8_t, L>
indices;
3823 for (std::size_t idx = 0; idx <
L; ++idx)
3825 return cmp.swizzle_bytes(
indices);
3827 auto vectors = move<bool, L * sizeof(T)>();
3828 static_assert(
vectors.size() == 4);
3829 std::array<uint8_t,
L / 2>
indices;
3830 for (std::size_t idx = 0; idx <
L / 2; ++idx)
3834 std::array<uint8_t, L> lanes;
3835 std::iota(lanes.begin(), lanes.end(), 0);
3842 template<dsl_primitive U>
3844 PrimitiveExpr<bool, L> operator==(PrimitiveExpr<U, L> other) {
3846 using OpT =
decltype(to<To, L>());
3847 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3848 auto this_converted = this->
template to<To, L>();
3849 auto other_converted = other.template to<To, L>();
3850 std::array<PrimitiveExpr<uint_t<
sizeof(To)>, lanes>, OpT::num_vectors> vectors;
3851 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3852 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3853 BINARY_VOP(Eq,), other_converted.vectors_[idx]
3855 return PrimitiveExpr<uint_t<
sizeof(To)>, L>(std::move(vectors)).cmp_helper();
3859 template<dsl_primitive U>
3860 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3861 PrimitiveExpr<bool, L> operator!=(PrimitiveExpr<U, L> other) {
3862 using To = common_type_t<T, U>;
3863 using OpT =
decltype(to<To, L>());
3864 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3865 auto this_converted = this->
template to<To, L>();
3866 auto other_converted = other.template to<To, L>();
3867 std::array<PrimitiveExpr<uint_t<
sizeof(To)>, lanes>, OpT::num_vectors> vectors;
3868 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3869 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3870 BINARY_VOP(Ne,), other_converted.vectors_[idx]
3872 return PrimitiveExpr<uint_t<
sizeof(To)>, L>(std::move(vectors)).cmp_helper();
3876 template<arithmetic U>
3877 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3878 PrimitiveExpr<bool, L>
operator<(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3879 using To = common_type_t<T, U>;
3880 using OpT =
decltype(to<To, L>());
3881 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3883 if constexpr (std::integral<To>) {
3884 if constexpr (
sizeof(
To) == 8)
3886 else if constexpr (
sizeof(
To) == 4)
3888 else if constexpr (
sizeof(
To) == 2)
3890 else if constexpr (
sizeof(
To) == 1)
3892 }
else if (std::floating_point<To>) {
3899 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3901 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3903 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3904 auto this_converted = _this.template to<To, L>();
3905 auto other_converted = _other.template to<To, L>();
3907 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3908 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3909 op, other_converted.vectors_[idx]
3915 template<arithmetic U>
3916 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3917 PrimitiveExpr<bool, L>
operator<=(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3918 using To = common_type_t<T, U>;
3919 using OpT =
decltype(to<To, L>());
3920 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3922 if constexpr (std::integral<To>) {
3923 if constexpr (
sizeof(
To) == 8)
3925 else if constexpr (
sizeof(
To) == 4)
3927 else if constexpr (
sizeof(
To) == 2)
3929 else if constexpr (
sizeof(
To) == 1)
3931 }
else if (std::floating_point<To>) {
3938 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3940 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3942 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3943 auto this_converted = _this.template to<To, L>();
3944 auto other_converted = _other.template to<To, L>();
3946 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3947 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3948 op, other_converted.vectors_[idx]
3954 template<arithmetic U>
3955 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3956 PrimitiveExpr<bool, L>
operator>(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3957 using To = common_type_t<T, U>;
3958 using OpT =
decltype(to<To, L>());
3959 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
3961 if constexpr (std::integral<To>) {
3962 if constexpr (
sizeof(
To) == 8)
3964 else if constexpr (
sizeof(
To) == 4)
3966 else if constexpr (
sizeof(
To) == 2)
3968 else if constexpr (
sizeof(
To) == 1)
3970 }
else if (std::floating_point<To>) {
3977 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
3979 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
3981 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
3982 auto this_converted = _this.template to<To, L>();
3983 auto other_converted = _other.template to<To, L>();
3985 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
3986 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
3987 op, other_converted.vectors_[idx]
3993 template<arithmetic U>
3994 requires same_signedness<T, U>
and arithmetically_combinable<T, U, L>
3995 PrimitiveExpr<bool, L>
operator>=(PrimitiveExpr<U, L> other)
requires arithmetic<T> {
3996 using To = common_type_t<T, U>;
3997 using OpT =
decltype(to<To, L>());
3998 static constexpr std::size_t lanes = OpT::vector_type::num_simd_lanes;
4000 if constexpr (std::integral<To>) {
4001 if constexpr (
sizeof(
To) == 8)
4003 else if constexpr (
sizeof(
To) == 4)
4005 else if constexpr (
sizeof(
To) == 2)
4007 else if constexpr (
sizeof(
To) == 1)
4009 }
else if (std::floating_point<To>) {
4016 constexpr bool is_u64_vec = unsigned_integral<To>
and sizeof(
To) == 8
and L > 1;
4018 M_CONSTEXPR_COND(is_u64_vec, (*
this xor PrimitiveExpr<T, L>(
T(1) << (CHAR_BIT *
sizeof(T) - 1))), *
this);
4020 M_CONSTEXPR_COND(is_u64_vec, (other xor PrimitiveExpr<U, L>(
U(1) << (CHAR_BIT *
sizeof(U) - 1))), other);
4021 auto this_converted = _this.template to<To, L>();
4022 auto other_converted = _other.template to<To, L>();
4024 for (std::size_t idx = 0; idx < OpT::num_vectors; ++idx)
4025 vectors[idx] = this_converted.vectors_[idx].template binary<uint_t<
sizeof(To)>, lanes>(
4026 op, other_converted.vectors_[idx]
4042 template<std::
size_t M>
4045 auto res =
vectors_[M / vector_type::num_simd_lanes].clone().template extract<M % vector_type::num_simd_lanes>();
4051 template<std::
size_t M, primitive_convertible U>
4054 static constexpr std::size_t lanes = vector_type::num_simd_lanes;
4055 vectors_[M / lanes] = vectors_[M / lanes].template replace<M % lanes>(std::forward<U>(value));
4061 template<std::
size_t M>
4062 requires (M > 0)
and (M <= 16)
and (M %
sizeof(T) == 0)
4063 PrimitiveExpr<T, M / sizeof(T)> swizzle_bytes(const
std::array<uint8_t, M> &indices) requires (num_vectors == 2) {
4064 return Module::Get().emit_shuffle_bytes(vectors_[0], vectors_[1], indices);
4069 template<std::
size_t M>
4072 return Module::Get().emit_shuffle_lanes(vectors_[0], vectors_[1], indices);
4080 friend std::ostream &
operator<<(std::ostream &out,
const PrimitiveExpr &P) {
4081 out <<
"PrimitiveExpr<" <<
typeid(
T).name() <<
"," <<
L <<
">: [";
4082 for (
auto it = P.vectors_.cbegin(); it != P.vectors_.cend(); ++it) {
4083 if (it != P.vectors_.cbegin())
4091 void dump(std::ostream &out)
const { out << *
this << std::endl; }
4092 void dump()
const {
dump(std::cerr); }
4101#define BINARY_LIST(X) \
4107 X(operator bitand) \
4129#define MAKE_BINARY(OP) \
4130 template<primitive_convertible T, primitive_convertible U> \
4131 requires requires (primitive_expr_t<T> t, primitive_expr_t<U> u) { t.OP(u); } \
4132 auto OP(T &&t, U &&u) \
4134 return primitive_expr_t<T>(std::forward<T>(t)).OP(primitive_expr_t<U>(std::forward<U>(u))); \
4141template<dsl_po
inter_to_primitive T, std::
size_t L>
4144struct PrimitiveExpr<T, L>
4147 static constexpr std::size_t num_simd_lanes = L;
4148 using pointed_type = std::decay_t<std::remove_pointer_t<T>>;
4149 using offset_t = int32_t;
4152 template<
typename, std::
size_t>
friend struct PrimitiveExpr;
4153 template<
typename, VariableKind,
bool, std::
size_t>
4154 friend class detail::variable_storage;
4155 friend struct Module;
4156 template<
typename>
friend struct FunctionProxy;
4157 template<dsl_primitive, std::
size_t,
bool>
friend struct detail::the_reference;
4158 template<
typename>
friend struct invoke_interpreter;
4161 PrimitiveExpr<uint32_t, 1> addr_;
4162 offset_t offset_ = 0;
4166 explicit PrimitiveExpr(PrimitiveExpr<uint32_t, 1> addr, offset_t offset = 0) : addr_(addr), offset_(offset) { }
4171 explicit PrimitiveExpr(::wasm::Expression *addr, std::list<std::shared_ptr<Bit>> referenced_bits = {},
4172 offset_t offset = 0)
4173 : addr_(addr, std::move(referenced_bits))
4178 explicit PrimitiveExpr(std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>> addr, offset_t offset = 0)
4185 , offset_([&raw_ptr](){
4186 auto &memory = Module::Memory();
4187 const auto offset =
reinterpret_cast<uint8_t*
>(raw_ptr) -
static_cast<uint8_t*
>(memory.addr());
4188 M_insist(offset >= 0
and offset < memory.size(),
"invalid raw pointer");
4195 PrimitiveExpr(PrimitiveExpr &other) : addr_(other.addr_), offset_(other.offset_) { }
4198 PrimitiveExpr(PrimitiveExpr &&other) : addr_(other.addr_), offset_(other.offset_) { }
4208 ::wasm::Expression *
expr() {
return to<uint32_t>().expr(); }
4210 std::list<std::shared_ptr<Bit>>
referenced_bits() {
return addr_.referenced_bits(); }
4212 std::pair<::wasm::Expression*, std::list<std::shared_ptr<Bit>>>
move() {
return addr_.move(); }
4217 explicit operator bool()
const {
return bool(addr_); }
4225 void discard() { addr_.discard(); }
4235 template<dsl_po
inter_to_primitive To, std::
size_t ToL = L>
4236 requires (not std::is_void_v<std::remove_pointer_t<To>>)
4237 PrimitiveExpr<To, ToL>
to()
requires std::is_void_v<pointed_type>
and (L == 1) {
4238 Wasm_insist((
clone().
template to<uint32_t>() % uint32_t(
alignof(std::remove_pointer_t<To>))).
eqz(),
4239 "cannot convert to type whose alignment requirement is not fulfilled");
4240 return PrimitiveExpr<To, ToL>(addr_.move(), offset_);
4245 template<
typename To, std::
size_t ToL = 1>
4246 requires std::same_as<To, uint32_t>
and (ToL == 1)
4247 PrimitiveExpr<uint32_t, 1> to() {
4248 return offset_ ? (offset_ > 0 ? addr_ + uint32_t(offset_) : addr_ - uint32_t(-offset_)) : addr_;
4252 template<
typename To, std::
size_t ToL = 1>
4253 requires (not std::same_as<To, T>)
and std::same_as<To, void*>
and (ToL == 1)
4254 PrimitiveExpr<void*, 1>
to() {
return PrimitiveExpr<void*, 1>(addr_.move(), offset_); }
4258 template<
typename To, std::
size_t ToL = L>
4259 requires std::same_as<To, T>
and (L == ToL)
4267 PrimitiveExpr<uint64_t, L>
hash() {
return to<uint32_t>().hash(); }
4276 PrimitiveExpr<bool, 1> is_nullptr() {
return to<uint32_t>() == 0
U; }
4281 PrimitiveExpr<bool, 1>
is_null() {
4283 return to<uint32_t>() == 0
U;
4289 PrimitiveExpr<bool, 1>
not_null() {
4291 return to<uint32_t>() != 0
U;
4295 std::pair<PrimitiveExpr, PrimitiveExpr<bool, 1>> split() {
auto cpy =
clone();
return { cpy, is_nullptr() }; }
4298 auto operator*()
requires dsl_primitive<pointed_type> {
4300 return Reference<pointed_type, L>(*
this);
4304 auto operator*() const requires dsl_primitive<pointed_type> {
4306 return ConstReference<pointed_type, L>(*
this);
4310 PrimitiveExpr<pointed_type, L> operator->() const requires dsl_primitive<pointed_type> {
4321 if constexpr (std::is_void_v<pointed_type>) {
4322 return PrimitiveExpr(addr_ + delta.make_unsigned(), offset_);
4324 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4325 return PrimitiveExpr(addr_ + (delta.make_unsigned() << log_size), offset_);
4331 if constexpr (std::is_void_v<pointed_type>) {
4334 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4335 offset_ += delta << log_size;
4342 if constexpr (std::is_void_v<pointed_type>) {
4343 return PrimitiveExpr(addr_ - delta.make_unsigned(), offset_);
4345 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4346 return PrimitiveExpr(addr_ - (delta.make_unsigned() << log_size), offset_);
4352 if constexpr (std::is_void_v<pointed_type>) {
4355 const uint32_t log_size = std::countr_zero(
sizeof(pointed_type));
4356 offset_ -= delta << log_size;
4362 PrimitiveExpr<offset_t, 1>
operator-(PrimitiveExpr other) {
4363 if constexpr (std::is_void_v<pointed_type>) {
4364 PrimitiveExpr<offset_t, 1> delta_addr = (this->addr_ - other.addr_).
make_signed();
4365 offset_t delta_offset = this->offset_ - other.offset_;
4366 return (delta_offset ? (delta_addr + delta_offset) : delta_addr);
4368 const int32_t log_size = std::countr_zero(
sizeof(pointed_type));
4369 PrimitiveExpr<offset_t, 1> delta_addr = (this->addr_ - other.addr_).
make_signed() >> log_size;
4370 offset_t delta_offset = (this->offset_ - other.offset_) >> log_size;
4371 return (delta_offset ? (delta_addr + delta_offset) : delta_addr);
4376#define CMP_OP(SYMBOL) \
4378 PrimitiveExpr<bool, 1> operator SYMBOL(PrimitiveExpr other) { \
4379 return this->to<uint32_t>() SYMBOL other.to<uint32_t>(); \
4395 PrimitiveExpr<pointed_type, L> load()
requires dsl_primitive<pointed_type> {
4396 M_insist(
bool(addr_),
"address already moved or discarded");
4397 if constexpr (
L *
sizeof(pointed_type) <= 16) {
4398 auto value = Module::Builder().makeLoad(
4400 std::is_signed_v<pointed_type>,
4401 offset_ >= 0 ? offset_ : 0,
4402 alignof(pointed_type),
4403 offset_ >= 0 ? addr_.expr() : (addr_ - uint32_t(-offset_)).
expr(),
4404 wasm_type<pointed_type, L>(),
4405 Module::Get().memory_->name
4407 return PrimitiveExpr<pointed_type, L>(value, addr_.referenced_bits());
4409 using ResT = PrimitiveExpr<pointed_type, L>;
4410 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
4411 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
4412 auto addr_cpy = addr_.clone();
4413 auto offset = offset_ + offset_t(idx * 16);
4414 auto value = Module::Builder().makeLoad(
4416 std::is_signed_v<pointed_type>,
4417 offset >= 0 ? offset : 0,
4418 alignof(pointed_type),
4419 offset >= 0 ? addr_cpy.expr() : (addr_cpy - uint32_t(-offset)).
expr(),
4420 ::wasm::Type(::wasm::Type::v128),
4421 Module::Get().memory_->name
4423 vectors[idx] =
typename ResT::vector_type(value, addr_cpy.referenced_bits());
4426 return ResT(std::move(vectors));
4430 void store(PrimitiveExpr<pointed_type, L> value)
requires dsl_primitive<pointed_type> {
4431 M_insist(
bool(addr_),
"address already moved or discarded");
4432 M_insist(
bool(value),
"value already moved or discarded");
4433 if constexpr (
L *
sizeof(pointed_type) <= 16) {
4434 auto e = Module::Builder().makeStore(
4436 offset_ >= 0 ? offset_ : 0,
4437 alignof(pointed_type),
4438 offset_ >= 0 ? addr_.expr() : (addr_ - uint32_t(-offset_)).
expr(),
4440 wasm_type<pointed_type, L>(),
4441 Module::Get().memory_->name
4443 Module::Block().list.push_back(e);
4446 for (std::size_t idx = 0; idx < PrimitiveExpr<pointed_type, L>::num_vectors; ++idx) {
4447 auto addr_cpy = addr_.clone();
4448 auto offset = offset_ + offset_t(idx * 16);
4449 auto e = Module::Builder().makeStore(
4451 offset >= 0 ? offset : 0,
4452 alignof(pointed_type),
4453 offset >= 0 ? addr_cpy.expr() : (addr_cpy - uint32_t(-offset)).
expr(),
4454 vectors[idx].
expr(),
4455 ::wasm::Type(::wasm::Type::v128),
4456 Module::Get().memory_->name
4458 Module::Block().list.push_back(e);
4470 friend std::ostream &
operator<<(std::ostream &out,
const PrimitiveExpr &P) {
4471 out <<
"PrimitiveExpr<" <<
typeid(
T).name() <<
"*, " <<
L <<
">: " << P.addr_ <<
" [" << P.offset_ <<
"]";
4475 void dump(std::ostream &out)
const { out << *
this << std::endl; }
4476 void dump()
const {
dump(std::cerr); }
4490template<
typename T, std::
size_t L>
4513template<dsl_primitive T, std::
size_t L>
4522 template<
typename, std::
size_t>
friend struct Expr;
4535 M_insist(
bool(value_),
"value must be present");
4543 M_insist(
bool(value_),
"value must be present");
4563 requires (Us... us) {
Expr(std::decay_t<Us>(us)...); }
4577 M_insist(not
bool(value_),
"value must be used or explicitly discarded");
4578 M_insist(not
bool(is_null_),
"NULL flag must be used or explicitly discarded");
4586 M_insist(
bool(value_),
"`Expr` has already been moved");
4587 return { value_, is_null_ };
4593 explicit operator bool()
const {
return bool(value_); }
4597 M_insist(
bool(value_),
"`Expr` has already been moved");
4606 M_insist(
bool(value_),
"`Expr` has already been moved");
4616 M_insist(
bool(value_),
"`Expr` has already been moved`");
4655 return not is_null_;
4665 return value_
and not is_null_;
4674 return not value_
and not is_null_;
4696 template<dsl_primitive To, std::
size_t ToL = L>
4702 template<dsl_primitive To, std::
size_t ToL = L>
4703 requires requires { value_.template to<To, ToL>(); }
4708 template<dsl_primitive To, std::
size_t ToL = L>
4709 requires requires { value_.template reinterpret<To, ToL>(); }
4714 template<std::
size_t ToL>
4715 requires requires { value_.template broadcast<ToL>(); is_null_.template broadcast<ToL>(); }
4717 return Expr<T, ToL>(value_.template broadcast<ToL>(), is_null_.template broadcast<ToL>());
4727#define UNARY_LIST(X) \
4746 auto OP() requires requires { value_.OP(); } { \
4747 using PrimExprT = decltype(value_.OP()); \
4748 using ExprT = expr_t<PrimExprT>; \
4749 return ExprT(value_.OP(), is_null_); \
4756 auto add_pairwise()
requires requires { value_.add_pairwise(); } {
4757 using PrimExprT =
decltype(value_.add_pairwise());
4760 return ExprT(value_.add_pairwise(), is_null_.template to<uint8_t>().add_pairwise().template to<bool>());
4762 return ExprT(value_.add_pairwise());
4778 return Expr<bool, 1>(value_.any_true(), is_null_.any_true());
4785 return Expr<bool, 1>(value_.all_true(), is_null_.any_true());
4796 return value_.hash();
4806 template<dsl_primitive U> \
4807 auto OP(Expr<U, L> other) requires requires { this->value_.OP(other.value_); } { \
4808 const unsigned idx = (other.can_be_null() << 1U) | this->can_be_null(); \
4809 auto result = this->value_.OP(other.value_); \
4810 using ReturnType = typename decltype(result)::type; \
4811 constexpr std::size_t ReturnLength = decltype(result)::num_simd_lanes; \
4813 default: M_unreachable("invalid index"); \
4815 return Expr<ReturnType, ReturnLength>(result); \
4817 return Expr<ReturnType, ReturnLength>(result, this->is_null_); \
4819 return Expr<ReturnType, ReturnLength>(result, other.is_null_); \
4821 return Expr<ReturnType, ReturnLength>(result, this->is_null_ or other.is_null_); \
4851 template<dsl_primitive U>
4853 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4859 return Expr(result);
4862 return Expr(result, this->is_null_);
4865 PrimitiveExpr<bool, L> is_null = other.is_null_.template broadcast<L>();
4866 return Expr(result, is_null);
4869 PrimitiveExpr<bool, L>
is_null = this->is_null_ or other.is_null_.template broadcast<L>();
4870 return Expr(result, is_null);
4875 template<dsl_primitive U>
4876 Expr operator>>(Expr<U, 1> other)
requires requires { this->value_ >> other.value_; }
and (L > 1) {
4877 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4883 return Expr(result);
4886 return Expr(result, this->is_null_);
4889 PrimitiveExpr<bool, L>
is_null = other.is_null_.template broadcast<L>();
4890 return Expr(result, is_null);
4893 PrimitiveExpr<bool, L>
is_null = this->is_null_ or other.is_null_.template broadcast<L>();
4894 return Expr(result, is_null);
4904 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4913 PrimitiveExpr<bool, L> result = this->value_
and other.value_.clone();
4914 PrimitiveExpr<bool, L> is_null =
4917 return Expr<bool, L>(result, is_null);
4920 PrimitiveExpr<bool, L> result = this->value_.clone()
and other.value_;
4921 PrimitiveExpr<bool, L> is_null =
4924 return Expr<bool, L>(result, is_null);
4927 auto this_is_null = this->is_null_.clone();
4928 auto other_is_null = other.is_null_.clone();
4929 PrimitiveExpr<bool, L> result = this->value_.clone()
and other.value_.clone();
4930 PrimitiveExpr<bool, L>
is_null =
4931 (this_is_null or other_is_null)
and
4932 (this->value_ or this->is_null_)
and
4933 (other.value_ or other.is_null_);
4934 return Expr<bool, L>(result, is_null);
4942 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4951 PrimitiveExpr<bool, L> result = this->value_.and_not(other.value_.clone());
4952 PrimitiveExpr<bool, L> is_null =
4953 this->is_null_.and_not(
4956 return Expr<bool, L>(result, is_null);
4959 PrimitiveExpr<bool, L> result = this->value_.clone().and_not(other.value_);
4960 PrimitiveExpr<bool, L> is_null =
4963 return Expr<bool, L>(result, is_null);
4966 auto this_is_null = this->is_null_.clone();
4967 auto other_is_null = other.is_null_.clone();
4968 PrimitiveExpr<bool, L> result = this->value_.clone().and_not(other.value_.clone());
4969 PrimitiveExpr<bool, L>
is_null =
4970 (this_is_null or other_is_null)
and
4971 (this->value_ or this->is_null_)
and
4972 (not other.value_ or other.is_null_);
4973 return Expr<bool, L>(result, is_null);
4981 const unsigned idx = (
bool(other.is_null_) << 1U) |
bool(this->is_null_);
4990 PrimitiveExpr<bool, L> result = this->value_ or other.value_.clone();
4991 PrimitiveExpr<bool, L> is_null =
4994 return Expr<bool, L>(result, is_null);
4997 PrimitiveExpr<bool, L> result = this->value_.clone() or other.value_;
4998 PrimitiveExpr<bool, L> is_null =
5001 return Expr<bool, L>(result, is_null);
5004 auto this_is_null = this->is_null_.clone();
5005 auto other_is_null = other.is_null_.clone();
5006 PrimitiveExpr<bool, L> result = this->value_.clone() or other.value_.clone();
5007 PrimitiveExpr<bool, L>
is_null =
5008 (this_is_null or other_is_null)
and
5009 (not this->value_ or this->is_null_)
and
5010 (not other.value_ or other.is_null_);
5011 return Expr<bool, L>(result, is_null);
5022 template<std::
size_t M>
5025 return Expr<T, 1>(value_.template extract<M>(), is_null_.template extract<M>());
5027 return Expr<T, 1>(value_.template extract<M>());
5031 template<std::
size_t M, expr_convertible U>
5032 requires requires (expr_t<U> u) { Expr<T, 1>(u); }
5036 auto [value_val, value_is_null] =
value.split();
5037 return Expr(value_.template replace<M>(value_val), is_null_.template replace<M>(value_is_null));
5039 M_insist(not value.can_be_null(),
"cannot replace a non-nullable value with a nullable one");
5040 return Expr(value_.template replace<M>(value.value_));
5047 requires (
sizeof(
T) == 1)
and requires { value_.swizzle_bytes(
indices); } {
5049 auto indices_cpy =
indices.clone();
5050 return Expr(value_.swizzle_bytes(indices), is_null_.swizzle_bytes(indices_cpy));
5052 return Expr(value_.swizzle_bytes(indices));
5057 template<std::
size_t M>
5059 requires (
sizeof(
T) == 1)
and requires { value_.swizzle_bytes(indices); } {
5061 return Expr<T, M /
sizeof(T)>(value_.swizzle_bytes(indices), is_null_.swizzle_bytes(indices));
5063 return Expr<
T, M /
sizeof(
T)>(value_.swizzle_bytes(indices));
5068 template<std::
size_t M>
5069 Expr<T, M>
swizzle_lanes(
const std::array<uint8_t, M> &indices)
5070 requires requires { value_.swizzle_lanes(indices); } {
5072 return Expr<T, M>(value_.swizzle_lanes(indices), is_null_.swizzle_lanes(indices));
5074 return Expr<T, M>(value_.swizzle_lanes(indices));
5084 friend std::ostream &
operator<<(std::ostream &out,
const Expr &E) {
5085 out <<
"Expr<" <<
typeid(type).name() <<
"," <<
num_simd_lanes <<
">: value_=" << E.value_
5086 <<
", is_null_=" << E.is_null_;
5090 void dump(std::ostream &out)
const { out << *
this << std::endl; }
5091 void dump()
const {
dump(std::cerr); }
5095template<
typename T, std::
size_t L>
5099#define MAKE_BINARY(OP) \
5100 template<expr_convertible T, expr_convertible U> \
5101 requires (not primitive_convertible<T> or not primitive_convertible<U>) and \
5102 requires (expr_t<T> t, expr_t<U> u) { t.OP(u); } \
5103 auto OP(T &&t, U &&u) \
5105 return expr_t<T>(std::forward<T>(t)).OP(expr_t<U>(std::forward<U>(u))); \
5111#define USING_N(TYPE, LENGTH, NAME) \
5112 using NAME = PrimitiveExpr<TYPE, LENGTH>; \
5113 using _ ## NAME = Expr<TYPE, LENGTH>;
5114#define USING(TYPE, NAME) \
5115 template<std::size_t L> using NAME = PrimitiveExpr<TYPE, L>; \
5116 template<std::size_t L> using _ ## NAME = Expr<TYPE, L>; \
5117 USING_N(TYPE, 1, NAME ## x1) \
5118 USING_N(TYPE, 2, NAME ## x2) \
5119 USING_N(TYPE, 4, NAME ## x4) \
5120 USING_N(TYPE, 8, NAME ## x8) \
5121 USING_N(TYPE, 16, NAME ## x16) \
5122 USING_N(TYPE, 32, NAME ## x32)
5128 USING(uint16_t, U16)
5130 USING(uint32_t, U32)
5132 USING(uint64_t, U64)
5134 USING(
double, Double)
5150template<dsl_primitive T, std::
size_t L>
5151::wasm::Index allocate_local()
5153 ::wasm::Function &fn = Module::Function();
5154 const ::wasm::Index index = fn.getNumParams() + fn.vars.size();
5155 const ::wasm::Type type = wasm_type<T, L>();
5156 fn.vars.emplace_back(type);
5158 M_insist(fn.getLocalType(index) == type);
5165template<
typename T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5166class variable_storage;
5169template<dsl_primitive T, VariableKind Kind, std::
size_t L>
5171class variable_storage<
T, Kind, false,
L>
5174 static constexpr std::size_t num_locals = ((
L *
sizeof(
T)) + 15) / 16;
5175 static_assert(Kind != VariableKind::Param or num_locals == 1,
"parameters must fit in a single Wasm local");
5177 template<
typename, VariableKind,
bool, std::
size_t>
5178 friend class variable_storage;
5181 std::array<::wasm::Index, num_locals> indices_;
5187 std::array<::wasm::Index, num_locals>
indices;
5188 for (std::size_t idx = 0; idx < num_locals; ++idx)
5189 indices[idx] = allocate_local<T, L>();
5192 , type_(wasm_type<T, L>())
5195 variable_storage(
const variable_storage&) =
delete;
5196 variable_storage(variable_storage&&) =
default;
5197 variable_storage &
operator=(variable_storage&&) =
default;
5200 variable_storage(::wasm::Index idx,
tag<int>)
5201 requires (num_locals == 1)
5202 : indices_(std::to_array({ idx })), type_(wasm_type<T, L>())
5205 ::wasm::Function &fn = Module::Function();
5207 M_insist(fn.getLocalType(indices_[0]) == type_);
5212 template<
typename... Us>
5217 template<primitive_convertible U>
5219 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5222 template<primitive_convertible U>
5224 void operator=(
U &&_value)
requires (num_locals == 1) {
5226 Module::Block().list.push_back(Module::Builder().makeLocalSet(indices_[0],
value.expr()));
5229 template<primitive_convertible U>
5231 void operator=(
U &&_value)
requires (num_locals > 1) {
5233 static_assert(num_locals ==
decltype(
value)::num_vectors);
5235 for (std::size_t idx = 0; idx < num_locals; ++idx)
5236 Module::Block().list.push_back(Module::Builder().makeLocalSet(indices_[idx],
vectors[idx].
expr()));
5246 std::array<typename PrimitiveExpr<T, L>::vector_type, num_locals>
vectors;
5247 for (std::size_t idx = 0; idx < num_locals; ++idx)
5249 Module::Builder().makeLocalGet(indices_[idx], type_)
5256template<std::
size_t L>
5260 static constexpr std::size_t bit_num_simd_lanes = std::min<std::size_t>(
L, 16);
5262 static constexpr std::size_t num_locals = (
L + 15) / 16;
5264 template<
typename, VariableKind,
bool, std::
size_t>
5265 friend class variable_storage;
5269 std::array<std::shared_ptr<LocalBit<bit_num_simd_lanes>>, num_locals> values_;
5274 variable_storage(const variable_storage&) = delete;
5275 variable_storage(variable_storage&&) = default;
5276 variable_storage & operator=(variable_storage&&) = default;
5279 template<typename... Us>
5284 template<primitive_convertible U>
5286 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5289 template<primitive_convertible U>
5304template<dsl_primitive T, std::
size_t L>
5309 variable_storage<T, VariableKind::Local, false, L> value_;
5310 variable_storage<bool, VariableKind::Local, false, L> is_null_;
5316 template<
typename... Us>
5317 requires (
sizeof...(Us) > 0)
and requires (Us... us) {
Expr<T, L>(us...); }
5321 template<expr_convertible U>
5323 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5326 template<expr_convertible U>
5340 is_null_.set_false();
5348 is_null_.set_false();
5352 void set_null() { is_null_.set_true(); }
5359template<dsl_po
inter_to_primitive T, VariableKind Kind, std::
size_t L>
5360requires (Kind != VariableKind::Global)
5361class variable_storage<T, Kind, /* CanBeNull= */ false, L>
5366 variable_storage<uint32_t, Kind, false, 1> addr_;
5369 variable_storage() =
default;
5372 explicit variable_storage(::wasm::Index idx,
tag<int> tag) : addr_(idx,
tag) { }
5375 template<primitive_convertible U>
5377 explicit variable_storage(
U &&
value) : variable_storage() {
operator=(std::forward<U>(
value)); }
5380 template<primitive_convertible U>
5392template<
typename T, std::
size_t L>
5397 static constexpr std::size_t num_globals =
dsl_primitive<T> ? ((
L *
sizeof(
T)) + 15) / 16 : 1;
5401 std::array<::wasm::Name, num_globals> names_;
5407 : variable_storage(T())
5410 variable_storage(
const variable_storage&) =
delete;
5411 variable_storage(variable_storage&&) =
default;
5412 variable_storage &
operator=(variable_storage&&) =
default;
5415 template<
typename... Us>
5416 requires (
sizeof...(Us) > 0)
and requires (Us... us) { Module::Get().emit_global<
T,
L>(names_,
true, us...); }
5417 explicit variable_storage(Us...
init)
5420 std::array<::wasm::Name, num_globals> names;
5421 for (std::size_t idx = 0; idx < num_globals; ++idx)
5422 names[idx] = Module::Unique_Global_Name();
5425 , type_(wasm_type<T, L>())
5427 Module::Get().emit_global<
T,
L>(names_,
true,
init...);
5430 explicit variable_storage(uint32_t
init = 0)
5432 : names_(std::to_array<::wasm::Name>({ Module::Unique_Global_Name() }))
5433 , type_(wasm_type<T, L>())
5435 Module::Get().emit_global<
T,
L>(names_[0],
true,
init);
5440 requires (
sizeof...(Us) > 0)
and requires (Us... us) { make_literal<T, L>(us...); }
5442 Module::Get().module_.getGlobal(names_[0])->init = Module::Builder().makeConst(make_literal<T, L>(
init...));
5446 requires (
sizeof...(Us) > 0)
and
5447 requires (Us... us) { { make_literal<T, L>(us...) } -> std::same_as<std::array<::wasm::Literal, num_globals>>; }
5449 auto literals = make_literal<T, L>(
init...);
5450 for (std::size_t idx = 0; idx < num_globals; ++idx)
5451 Module::Get().module_.getGlobal(names_[idx])->init = Module::Builder().makeConst(literals[idx]);
5455 Module::Get().module_.getGlobal(names_[0])->init = Module::Builder().makeConst(::wasm::Literal(
init));
5458 template<primitive_convertible U>
5460 void init(
U &&_init)
requires (num_globals == 1) {
5462 Module::Get().module_.getGlobal(names_[0])->init =
init.expr();
5466 template<primitive_convertible U>
5468 void operator=(
U &&_value)
requires (num_globals == 1) {
5470 Module::Block().list.push_back(Module::Builder().makeGlobalSet(names_[0],
value.expr()));
5473 template<primitive_convertible U>
5475 void operator=(
U &&_value)
requires (num_globals > 1) {
5477 static_assert(num_globals ==
decltype(
value)::num_vectors);
5479 for (std::size_t idx = 0; idx < num_globals; ++idx)
5480 Module::Block().list.push_back(Module::Builder().makeGlobalSet(names_[idx],
vectors[idx].
expr()));
5490 std::array<typename PrimitiveExpr<T, L>::vector_type, num_globals>
vectors;
5491 for (std::size_t idx = 0; idx < num_globals; ++idx)
5493 Module::Builder().makeGlobalGet(names_[idx], type_)
5501template<
typename T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5503 (not (Kind == VariableKind::Global
and CanBeNull))
and
5509 template<
typename X>
5518#ifdef M_ENABLE_SANITY_FIELDS
5520 mutable bool used_ =
false;
5521#define REGISTER_USE(VAR) (VAR).used_ = true
5523#define REGISTER_USE(VAR)
5529 swap(first.storage_, second.storage_);
5530#ifdef M_ENABLE_SANITY_FIELDS
5531 swap(first.used_, second.used_);
5541#ifdef M_ENABLE_SANITY_FIELDS
5542 , used_(other.used_)
5552#ifdef M_ENABLE_SANITY_FIELDS
5553 M_insist(used_,
"variable must be used at least once");
5558 template<
typename... Us>
5559 requires requires (Us&&... us) { storage_type(std::forward<Us>(us)...); }
5566 requires (Kind == VariableKind::Param)
5567 : storage_(idx,
tag)
5576 if constexpr (CanBeNull)
5591 template<
typename U>
5592 requires requires (dependent_expr_type v) { dependent_expr_t<U>(v); }
5595 template<
typename To, std::
size_t ToL = L>
5596 requires requires (dependent_expr_type v) { v.template to<To, ToL>(); }
5599 template<
typename To, std::
size_t ToL = L>
5600 requires requires (dependent_expr_type v) { v.template reinterpret<To, ToL>(); }
5605 template<std::
size_t ToL>
5606 requires requires (dependent_expr_type v) { v.template broadcast<ToL>(); }
5611 template<
typename... Us>
5612 requires requires (Us&&... us) { storage_.init(std::forward<Us>(us)...); }
5613 void init(Us&&... init) { storage_.init(std::forward<Us>(
init)...); }
5615 template<
typename U>
5616 requires requires (
U &&u) { storage_ = std::forward<U>(u); }
5620 requires requires (
storage_type s) { { s.set_true() } -> std::same_as<void>; }
5622 storage_.set_true();
5626 requires requires (
storage_type s) { { s.set_false() } -> std::same_as<void>; }
5628 storage_.set_false();
5632 requires requires (
storage_type s) { { s.set_null() } -> std::same_as<void>; }
5634 storage_.set_null();
5644 auto OP() const requires requires (dependent_expr_type e) { e.OP(); } { return dependent_expr_type(*this).OP(); }
5657 UNARY(is_true_and_not_null)
5658 UNARY(is_false_and_not_null)
5662#define ASSIGNOP_LIST(X) \
5674#define ASSIGNOP(SYMBOL) \
5675 template<typename U> \
5676 requires requires { typename dependent_expr_t<U>; } and \
5677 requires (U &&u) { dependent_expr_t<U>(std::forward<U>(u)); } and \
5678 requires (dependent_expr_type var_value, dependent_expr_t<U> other_value) \
5679 { var_value SYMBOL other_value; } and \
5680 requires (Variable var, \
5681 decltype(std::declval<dependent_expr_type>() SYMBOL std::declval<dependent_expr_t<U>>()) value) \
5683 Variable & operator SYMBOL##= (U &&value) { \
5684 dependent_expr_t<U> _value(std::forward<U>(value)); \
5685 this->operator=(dependent_expr_type(*this) SYMBOL _value); \
5693 template<std::
size_t M>
5695 return dependent_expr_type(*this).template extract<M>();
5699 template<std::
size_t M,
typename U>
5702 this->
operator=(dependent_expr_type(*this).template replace<M>(std::forward<U>(value)));
5710 return dependent_expr_type(*this).swizzle_bytes(indices);
5715 template<std::
size_t M>
5718 return dependent_expr_type(*this).swizzle_lanes(indices);
5725template<dsl_po
inter_to_primitive T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5726requires requires (
const Variable<T, Kind, CanBeNull, L> &var,
typename PrimitiveExpr<T, L>::offset_t delta)
5727 { var.val().operator+(delta); }
5730 return var.val().operator+(delta);
5733template<dsl_po
inter_to_primitive T, VariableKind Kind,
bool CanBeNull, std::
size_t L>
5734requires requires (
const Variable<T, Kind, CanBeNull, L> &var,
typename PrimitiveExpr<T, L>::offset_t delta)
5735 { var.val().operator-(delta); }
5738 return var.val().operator-(delta);
5747template<
typename T, std::
size_t L>
5751template<
typename T, std::
size_t L>
5759template<
typename T, std::
size_t L>
5763template<
typename T, std::
size_t L>
5771template<
typename T, std::
size_t L>
5800template<
typename T, std::
size_t L>
5801requires (
L *
sizeof(
T) <= 16)
5808 using base_type::operator=;
5818 ::wasm::Function &fn = Module::Function();
5819 M_insist(index < fn.getNumLocals(),
"index out of bounds");
5820 M_insist(fn.isParam(index),
"not a parameter");
5821 M_insist(fn.getLocalType(index) == (wasm_type<T, L>()),
"type mismatch");
5832template<dsl_primitive T, std::
size_t L,
bool IsConst>
5836 friend struct Variable<
T*, VariableKind::Local,
false,
L>;
5837 friend struct Variable<
T*, VariableKind::Global,
false,
L>;
5838 friend struct Variable<
T*, VariableKind::Param,
false,
L>;
5840 static constexpr bool is_const = IsConst;
5849 M_insist(
bool(ptr_),
"must not be moved or discarded");
5853 template<
typename U>
5863#define ASSIGNOP(SYMBOL) \
5864 template<typename U> \
5865 requires requires (the_reference ref, U &&u) { ref SYMBOL std::forward<U>(u); } and \
5866 requires (the_reference ref, decltype(ref SYMBOL std::declval<U>()) value) \
5868 void operator SYMBOL##= (U &&value) { \
5869 this->operator=(the_reference(ptr_.clone()) SYMBOL std::forward<U>(value)); \
5905 bitmask_per_offset.fill(uint16_t(-1U));
5918template<std::
size_t L>
5938 : bitvector_(&bitvector)
5939 , bit_offset_(bit_offset)
5940 , starting_lane_(starting_lane)
5942 M_insist(bit_offset_ < 8,
"offset out of bounds");
5943 M_insist(starting_lane_ +
L <= 16,
"starting lane out of bounds");
5955 swap(first.bitmap_, second.bitmap_);
5966 M_insist(bit_offset_ < CHAR_BIT *
sizeof(uint64_t),
"offset out of bounds");
5978template<std::
size_t L>
5979requires (L > 0)
and (L <= 16)
5980struct LocalBit<L> : Bit
5982 friend void swap(LocalBit &first, LocalBit &second) {
5984 swap(first.storage_, second.storage_);
5987 friend struct Module;
5990 using storage_type = detail::local_bit_storage<L>;
5991 storage_type storage_;
5993 LocalBit() =
default;
5995 template<
typename... Us>
5996 requires requires (Us&&... us) { storage_type(std::forward<Us>(us)...); }
5997 LocalBit(Us&&... us) : storage_(
std::forward<Us>(us)...) { }
6000 LocalBit(
const LocalBit&) =
delete;
6005 if constexpr (
L == 1) {
6006 if (storage_.bitmap_) {
6007 M_insist((storage_.bitmap_->bitmask bitand
mask()) == 0,
"bit must still be allocated");
6009 if (storage_.bitmap_->bitmask == 0)
6010 Module::Get().local_bitmaps_stack_.back().emplace_back(storage_.bitmap_);
6012 storage_.bitmap_->bitmask |=
mask();
6015 if (storage_.bitvector_) {
6016 constexpr uint16_t MASK = (1U <<
L) - 1U;
6017 M_insist((storage_.bitvector_->bitmask_per_offset[offset()] bitand (MASK << starting_lane())) == 0,
6018 "bits must still be allocated");
6020 const auto &bitmasks = storage_.bitvector_->bitmask_per_offset;
6022 if (std::all_of(bitmasks.cbegin(), bitmasks.cend(), pred))
6023 Module::Get().local_bitvectors_stack_.back().emplace_back(storage_.bitvector_);
6025 storage_.bitvector_->bitmask_per_offset[offset()] |= MASK << starting_lane();
6034 std::conditional_t<L == 1, uint64_t, uint8_t> offset()
const {
return storage_.bit_offset_; }
6036 uint64_t
mask() const requires (L == 1) {
return 1UL << offset(); }
6038 U8x16
mask() const requires (L > 1) {
return U8x16(1U << offset()); }
6040 U8x16 mask_inverted() const requires (L > 1) {
return U8x16(~(1U << offset())); }
6042 uint8_t starting_lane() const requires (L > 1) {
return storage_.starting_lane_; }
6046 PrimitiveExpr<bool, L> is_set()
const {
6047 if constexpr (
L == 1) {
6048 return (storage_.bitmap_->u64 bitand
mask()).
template to<bool>();
6050 if constexpr (
L == 16) {
6052 return (storage_.bitvector_->u8x16 bitand
mask()).template to<bool>();
6053 }
else if (starting_lane()) {
6054 std::array<uint8_t, L>
indices;
6056 return (storage_.bitvector_->u8x16 bitand
mask()).swizzle_bytes(indices).template to<bool>();
6058 return PrimitiveExpr<bool, L>((storage_.bitvector_->u8x16 bitand
mask()).
template to<bool>().
move());
6065 if constexpr (
L == 1)
6066 storage_.bitmap_->u64 |=
mask();
6068 storage_.bitvector_->u8x16 |=
mask();
6072 if constexpr (
L == 1)
6073 storage_.bitmap_->u64 &= ~
mask();
6075 storage_.bitvector_->u8x16 &= mask_inverted();
6079 void set(PrimitiveExpr<bool, L> value) {
6080 if constexpr (
L == 1) {
6081 storage_.bitmap_->u64 =
6082 (storage_.bitmap_->u64 bitand ~mask()) bitor (
value.template to<uint64_t>() << offset());
6084 if constexpr (
L == 16) {
6086 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6087 (
value.template to<uint8_t>() << offset());
6088 }
else if (starting_lane()) {
6089 std::array<uint8_t, 16>
indices;
6090 auto it = std::fill_n(
indices.begin(), starting_lane(), L);
6091 std::iota(it, it + L, 0);
6092 std::fill(it + L,
indices.end(), L);
6093 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6094 (
value.swizzle_bytes(indices).template to<uint8_t>() << offset());
6096 Boolx16 value_masked((PrimitiveExpr<bool, L>(
true)
and value).
move());
6097 storage_.bitvector_->u8x16 = (storage_.bitvector_->u8x16 bitand mask_inverted()) bitor
6098 (value_masked.template to<uint8_t>() << offset());
6106 if constexpr (
L == 1) {
6107 auto other_bit = other.storage_.bitmap_->u64 bitand other.mask();
6108 Var<U64x1> this_bit;
6110 if (this->offset() > other.offset()) {
6111 const auto shift_width = this->offset() - other.offset();
6112 this_bit = other_bit << shift_width;
6113 }
else if (other.offset() > this->offset()) {
6114 const auto shift_width = other.offset() - this->offset();
6115 this_bit = other_bit >> shift_width;
6117 this_bit = other_bit;
6120 this->storage_.bitmap_->u64 =
6121 (this->storage_.bitmap_->u64 bitand ~this->mask()) bitor this_bit;
6125 auto other_bits = other.storage_.bitvector_->u8x16 bitand other.mask();
6126 Var<U8x16> this_bits;
6128 if (this->starting_lane() != other.starting_lane()) {
6129 std::array<uint8_t, 16>
indices;
6130 auto it = std::fill_n(
indices.begin(), starting_lane(), L);
6131 std::iota(it, it + L, 0);
6132 std::fill(it + L,
indices.end(), L);
6133 this_bits = other_bits.swizzle_bytes(indices);
6135 this_bits = other_bits;
6138 if (this->offset() > other.offset()) {
6139 const auto shift_width = this->offset() - other.offset();
6140 this_bits = other_bits << shift_width;
6141 }
else if (other.offset() > this->offset()) {
6142 const auto shift_width = other.offset() - this->offset();
6143 this_bits = other_bits >> shift_width;
6145 this_bits = other_bits;
6148 this->storage_.bitvector_->u8x16 =
6149 (this->storage_.bitvector_->u8x16 bitand this->mask_inverted()) bitor this_bits;
6156 operator PrimitiveExpr<bool, L>()
const {
return is_set(); }
6168template<primitive_convertible T>
6171template<expr_convertible T>
6172requires (not primitive_convertible<T>)
6177inline void BREAK(std::size_t level = 1) { Module::Get().emit_break(level); }
6178template<primitive_convertible C>
6179requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6180inline void BREAK(C &&_cond, std::size_t level = 1)
6183 Module::Get().emit_break(cond, level);
6188inline void CONTINUE(std::size_t level = 1) { Module::Get().emit_continue(level); }
6189template<primitive_convertible C>
6190requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6194 Module::Get().emit_continue(cond, level);
6201template<primitive_convertible C>
6202requires requires (C &&c) { PrimitiveExpr<bool, 1>(std::forward<C>(c)); }
6212template<primitive_convertible C, primitive_convertible T, primitive_convertible U>
6216inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6221 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6222 constexpr std::size_t
L =
decltype(tru)::num_simd_lanes;
6225 return Module::Get().emit_select<
To,
L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6228template<primitive_convertible C, expr_convertible T, expr_convertible U>
6229requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6233inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6235 expr_t<T> tru(std::forward<T>(_tru));
6236 expr_t<U> fals(std::forward<U>(_fals));
6238 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6239 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6241 PrimitiveExpr<bool, 1> cond(std::forward<C>(_cond));
6242 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6245template<primitive_convertible C, primitive_convertible T, primitive_convertible U>
6250inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6252 primitive_expr_t<T> tru(std::forward<T>(_tru));
6253 primitive_expr_t<U> fals(std::forward<U>(_fals));
6255 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6256 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6258 PrimitiveExpr<bool, L> cond(std::forward<C>(_cond));
6259 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6262template<primitive_convertible C, expr_convertible T, expr_convertible U>
6263requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6267inline auto Select(C &&_cond, T &&_tru, U &&_fals)
6269 expr_t<T> tru(std::forward<T>(_tru));
6270 expr_t<U> fals(std::forward<U>(_fals));
6272 using To =
common_type_t<
typename decltype(tru)::type,
typename decltype(fals)::type>;
6273 constexpr std::size_t L =
decltype(tru)::num_simd_lanes;
6275 PrimitiveExpr<bool, L> cond(std::forward<C>(_cond));
6276 return Module::Get().emit_select<To, L>(cond, tru.template to<To, L>(), fals.template to<To, L>());
6282template<primitive_convertible T, primitive_convertible U, std::
size_t M>
6287 const std::array<uint8_t, M> &a)
6288 { Module::Get().emit_shuffle_bytes(e, e, a); }
6289inline auto ShuffleBytes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6294 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6295 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6297 return Module::Get().emit_shuffle_bytes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6300template<primitive_convertible T, primitive_convertible U, std::
size_t M>
6302 (primitive_expr_t<T>::num_simd_lanes == primitive_expr_t<U>::num_simd_lanes)
and
6303requires (PrimitiveExpr<common_type_t<typename primitive_expr_t<T>::type,
typename primitive_expr_t<U>::type>,
6304 primitive_expr_t<T>::num_simd_lanes> e,
6305 const std::array<uint8_t, M> &a)
6306 { Module::Get().emit_shuffle_lanes(e, e, a); }
6307inline auto ShuffleLanes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6312 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6313 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6315 return Module::Get().emit_shuffle_lanes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6318template<expr_convertible T, expr_convertible U, std::
size_t M>
6319requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6323 const std::array<uint8_t, M> &a)
6324 { Module::Get().emit_shuffle_bytes(e, e, a); }
6325inline auto ShuffleBytes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6327 expr_t<T> first(std::forward<T>(_first));
6328 expr_t<U> second(std::forward<U>(_second));
6330 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6331 constexpr std::size_t L =
decltype(first)::num_simd_lanes;
6333 return Module::Get().emit_shuffle_bytes<To, L>(first.template to<To, L>(), second.template to<To, L>(), indices);
6336template<expr_convertible T, expr_convertible U, std::
size_t M>
6337requires (not primitive_convertible<T> or not primitive_convertible<U>)
and
6338 have_common_type<
typename expr_t<T>::type,
typename expr_t<U>::type>
and
6339 (expr_t<T>::num_simd_lanes == expr_t<U>::num_simd_lanes)
and
6340requires (Expr<common_type_t<typename expr_t<T>::type,
typename expr_t<U>::type>, expr_t<T>::num_simd_lanes> e,
6341 const std::array<uint8_t, M> &a)
6342 { Module::Get().emit_shuffle_lanes(e, e, a); }
6343inline auto ShuffleLanes(T &&_first, U &&_second,
const std::array<uint8_t, M> &indices)
6345 expr_t<T> first(std::forward<T>(_first));
6346 expr_t<U> second(std::forward<U>(_second));
6348 using To =
common_type_t<
typename decltype(first)::type,
typename decltype(second)::type>;
6349 constexpr std::size_t
L =
decltype(first)::num_simd_lanes;
6351 return Module::Get().emit_shuffle_lanes<
To,
L>(first.template to<To, L>(), second.template to<To, L>(),
indices);
6368 template<primitive_convertible C>
6371 : cond_(
std::forward<C>(cond))
6372 , name_(
Module::Unique_If_Name())
6395 ::wasm::Loop *loop_ =
nullptr;
6400 : body_(name +
".body", false)
6403 Module::Get().push_branch_targets(
6418 Module::Get().pop_branch_targets();
6419 Module::Block().list.push_back(loop_);
6425 std::string
name()
const {
return loop_->name.toString(); }
6433 template<primitive_convertible C>
6441 auto branch_targets = Module::Get().pop_branch_targets();
6442 Module::Get().push_branch_targets(branch_targets.brk, branch_targets.continu, cond);
6445 template<primitive_convertible C>
6463 : cond_(cond.
clone())
6464 , do_while_(
std::make_unique<
DoWhile>(name +
".do-while", cond))
6467 template<primitive_convertible C>
6471 template<primitive_convertible C>
6524 template<dsl_primitive T>
6528 template<dsl_primitive T, std::
size_t L = 1>
6532 template<dsl_primitive T, std::
size_t L = 1>
6537 template<dsl_primitive T>
6538 T *
raw_malloc(uint32_t count) {
return static_cast<T*
>(raw_allocate(
sizeof(
T) * count,
alignof(
T))); }
6541 template<dsl_primitive T, std::
size_t L = 1>
6543 if constexpr (
L == 1)
6544 return pre_allocate(
sizeof(
T) * count,
alignof(
T)).template to<T*, L>();
6545 else if constexpr (
L *
sizeof(
T) <= 16)
6546 return pre_allocate(16 * count,
alignof(
T)).template to<T*, L>();
6552 template<dsl_primitive T, std::
size_t L = 1,
typename U>
6553 requires requires (
U &&u) { U32x1(std::forward<U>(u)); }
6555 if constexpr (
L == 1) {
6557 allocate(uint32_t(
sizeof(
T)) * std::forward<U>(count),
alignof(
T)).
template to<T*, L>()
6560 }
else if constexpr (
L *
sizeof(
T) <= 16) {
6562 allocate(16U * std::forward<U>(count),
alignof(
T)).
template to<T*, L>()
6569 ).
template to<T*, L>()
6576 template<primitive_convertible T>
6581 template<primitive_convertible T,
typename U>
6582 requires requires (
U &&u) { U32x1(std::forward<U>(u)); }
and
6586 using pointed_type =
typename decltype(_ptr)::pointed_type;
6587 constexpr std::size_t
L =
decltype(_ptr)::num_simd_lanes;
6588 if constexpr (
L == 1)
6589 deallocate(_ptr.template to<void*>(), uint32_t(
sizeof(pointed_type)) * std::forward<U>(count));
6590 else if constexpr (
L *
sizeof(
T) <= 16)
6591 deallocate(_ptr.template to<void*>(), 16U * std::forward<U>(count));
6593 deallocate(_ptr.template to<void*>(),
6607inline void Module::create_local_bitmap_stack()
6609 local_bitmaps_stack_.emplace_back();
6612inline void Module::create_local_bitvector_stack()
6614 local_bitvectors_stack_.emplace_back();
6617inline void Module::dispose_local_bitmap_stack()
6619 auto &local_bitmaps = local_bitmaps_stack_.back();
6621 M_insist(~bitmap->bitmask == 0,
"all bits must have been deallocated");
6624 local_bitmaps_stack_.pop_back();
6627inline void Module::dispose_local_bitvector_stack()
6629 auto &local_bitvectors = local_bitvectors_stack_.back();
6632 for (
auto bitmask : bitvector->bitmask_per_offset)
6633 M_insist(uint16_t(~
bitmask) == 0,
"all bits must have been deallocated");
6637 local_bitvectors_stack_.pop_back();
6640template<std::
size_t L>
6644 if constexpr (
L == 1) {
6645 auto &local_bitmaps = local_bitmaps_stack_.back();
6647 if (local_bitmaps.empty())
6651 M_insist(bitmap.
bitmask,
"bitmap must have at least one bit unoccupied");
6653 uint8_t bit_offset = std::countr_zero(bitmap.
bitmask);
6654 bitmap.
bitmask ^= 1UL << bit_offset;
6659 local_bitmaps.pop_back();
6663 bool fresh_bitvector =
false;
6665 auto &local_bitvectors = local_bitvectors_stack_.back();
6667 if (local_bitvectors.empty()) {
6670 fresh_bitvector =
true;
6676 uint8_t starting_lane = uint8_t(-1U);
6677 constexpr uint16_t MASK = (1U <<
L) - 1U;
6678 for (uint8_t lane = 0; lane <= 16 -
L; ++lane) {
6681 const uint16_t masked =
bitmask bitand (MASK << lane);
6682 if (masked == (MASK << lane)) {
6683 starting_lane = lane;
6690 if (starting_lane == uint8_t(-1U)) {
6691 M_insist(not fresh_bitvector,
"fresh bitvector must have at least L consecutive bits unoccupied");
6692 goto allocate_bitvector;
6697 LocalBit<L> bit(bitvector, bit_offset, starting_lane);
6701 if (std::all_of(bitmasks.cbegin(), bitmasks.cend(), pred))
6702 local_bitvectors.pop_back();
6708template<
typename T, std::
size_t L>
6709requires (L *
sizeof(
T) <= 16)
6715template<
typename ReturnType,
typename... ParamTypes, std::size_t... ParamLs>
6716requires std::is_void_v<ReturnType>
6719 active_block_->list.push_back(
6720 builder_.makeCall(fn, { args.expr()... }, wasm_type<ReturnType, 1>())
6724template<
typename ReturnType, std::size_t
ReturnL,
typename... ParamTypes, std::size_t... ParamLs>
6729 builder_.makeCall(fn, { args.expr()... }, wasm_type<ReturnType, ReturnL>())
6733inline void Module::emit_return()
6735 active_block_->list.push_back(builder_.makeReturn());
6738template<
typename T, std::
size_t L>
6741 active_block_->list.push_back(builder_.makeReturn(
value.expr()));
6744template<
typename T, std::
size_t L>
6747 emit_return(
value.insist_not_null());
6751inline void Module::emit_break(std::size_t level)
6754 M_insist(branch_target_stack_.size() >= level);
6755 auto &branch_targets = branch_target_stack_[branch_target_stack_.size() - level];
6756 active_block_->list.push_back(builder_.makeBreak(branch_targets.brk));
6763 M_insist(branch_target_stack_.size() >= level);
6764 auto &branch_targets = branch_target_stack_[branch_target_stack_.size() - level];
6765 active_block_->list.push_back(builder_.makeBreak(branch_targets.brk,
nullptr, cond.expr()));
6768template<
typename T, std::
size_t L>
6771 if constexpr (
L *
sizeof(
T) <= 16) {
6776 builder_.makeSelect(cond.expr(), tru.expr(), fals.expr()),
6781 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
6782 auto vectors_tru = tru.vectors();
6783 auto vectors_fals = fals.vectors();
6784 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
6785 auto cond_cpy = cond.clone();
6789 vectors[idx] =
typename ResT::vector_type(
6790 builder_.makeSelect(cond_cpy.expr(), vectors_tru[idx].expr(),
6791 vectors_fals[idx].expr()),
6796 return ResT(std::move(
vectors));
6800template<
typename T, std::
size_t L>
6803 if (tru.can_be_null() or fals.can_be_null()) {
6804 auto [tru_val, tru_is_null] = tru.split();
6805 auto [fals_val, fals_is_null] = fals.split();
6806 auto cond_cpy = cond.clone();
6808 emit_select(cond, tru_val, fals_val),
6809 emit_select(cond_cpy, tru_is_null, fals_is_null)
6813 emit_select(cond, tru.insist_not_null(), fals.insist_not_null())
6818template<
typename T, std::
size_t L>
6827 if constexpr (
L *
sizeof(
T) <= 16) {
6832 builder_.makeSIMDTernary(::wasm::SIMDTernaryOp::Bitselect, tru.expr(), fals.expr(),
6838 std::array<typename ResT::vector_type, ResT::num_vectors>
vectors;
6839 auto vectors_mask =
mask.vectors();
6840 static_assert(ResT::num_vectors == vectors_mask.size());
6841 auto vectors_tru = tru.vectors();
6842 auto vectors_fals = fals.vectors();
6843 for (std::size_t idx = 0; idx < ResT::num_vectors; ++idx) {
6847 vectors[idx] =
typename ResT::vector_type(
6848 builder_.makeSIMDTernary(::wasm::SIMDTernaryOp::Bitselect,
6849 vectors_tru[idx].expr(), vectors_fals[idx].expr(),
6850 vectors_mask[idx].expr()),
6854 return ResT(std::move(
vectors));
6858template<
typename T, std::
size_t L>
6862 if (tru.can_be_null() or fals.can_be_null()) {
6863 auto [tru_val, tru_is_null] = tru.split();
6864 auto [fals_val, fals_is_null] = fals.split();
6865 auto cond_cpy = cond.clone();
6867 emit_select(cond, tru_val, fals_val),
6868 emit_select(cond_cpy, tru_is_null, fals_is_null)
6872 emit_select(cond, tru.insist_not_null(), fals.insist_not_null())
6877template<
typename T, std::
size_t L, std::
size_t M>
6878requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
6881 const
std::array<uint8_t, M> &_indices)
6883 std::array<uint8_t, 16>
indices;
6884 for (std::size_t idx = 0; idx < M; ++idx) {
6885 if (_indices[idx] <
L *
sizeof(
T))
6887 else if (_indices[idx] < 2 *
L *
sizeof(
T))
6888 indices[idx] = _indices[idx] + (16 -
L *
sizeof(
T));
6897 builder_.makeSIMDShuffle(first.expr(), second.expr(),
indices),
6901 vec.template extract_unsafe<0>(),
6905template<
typename T, std::
size_t L, std::
size_t M>
6906requires (L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (
is_pow_2(M))
and (M *
sizeof(T) <= 16)
6907inline PrimitiveExpr<T, M>
6909 const
std::array<uint8_t, M> &_indices)
6911 std::array<uint8_t, 16>
indices;
6912 for (std::size_t idx = 0; idx < M; ++idx) {
6913 for (std::size_t
byte = 0;
byte <
sizeof(
T); ++byte) {
6914 if (_indices[idx] <
L)
6915 indices[idx *
sizeof(
T) +
byte] = _indices[idx] *
sizeof(
T) + byte;
6916 else if (_indices[idx] < 2 *
L)
6918 _indices[idx] *
sizeof(
T) +
byte + (16 -
L *
sizeof(
T));
6920 indices[idx *
sizeof(
T) +
byte] = 15;
6928 builder_.makeSIMDShuffle(first.expr(), second.expr(),
indices),
6932 vec.template extract_unsafe<0>(),
6936template<
typename T, std::
size_t L, std::
size_t M>
6937requires (
L > 1)
and (L *
sizeof(T) <= 16)
and (M > 0)
and (M <= 16)
and (M % sizeof(T) == 0)
and (sizeof(T) == 1)
6938inline
Expr<T, M / sizeof(T)>
6939Module::emit_shuffle_bytes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices)
6941 if (first.can_be_null() or second.can_be_null()) {
6942 auto [first_val, first_is_null] = first.split();
6943 auto [second_val, second_is_null] = second.split();
6944 return Expr<
T, M /
sizeof(
T)>(
6945 emit_shuffle_bytes(first_val, second_val, indices),
6946 emit_shuffle_bytes(first_is_null, second_is_null, indices)
6949 return Expr<
T, M /
sizeof(
T)>(
6950 emit_shuffle_bytes(first.insist_not_null(), second.insist_not_null(), indices)
6955template<
typename T, std::
size_t L, std::
size_t M>
6958Module::emit_shuffle_lanes(
Expr<T, L> first,
Expr<T, L> second, const
std::array<uint8_t, M> &indices)
6960 if (first.can_be_null() or second.can_be_null()) {
6961 auto [first_val, first_is_null] = first.split();
6962 auto [second_val, second_is_null] = second.split();
6964 emit_shuffle_lanes(first_val, second_val,
indices),
6965 emit_shuffle_lanes(first_is_null, second_is_null,
indices)
6969 emit_shuffle_lanes(first.insist_not_null(), second.insist_not_null(),
indices)
6976 branch_target_stack_.emplace_back(brk, continu, condition.expr());
6986 Module::Block().list.push_back(Module::Builder().makeBreak(get().name,
nullptr, cond.expr()));
6998template<std::
size_t L>
6999inline variable_storage<bool, VariableKind::Local, false, L>::variable_storage()
7001 std::array<std::shared_ptr<LocalBit<bit_num_simd_lanes>>, num_locals> values;
7002 for (std::size_t idx = 0; idx < num_locals; ++idx)
7010template<std::
size_t L>
7011void variable_storage<bool, VariableKind::Local, false, L>::set_true()
7013 for (
auto &local_bit : values_)
7017template<std::
size_t L>
7018void variable_storage<bool, VariableKind::Local, false, L>::set_false()
7020 for (
auto &local_bit : values_)
7024template<std::
size_t L>
7025template<primitive_convertible U>
7026requires requires (
U &&u) { PrimitiveExpr<bool, L>(primitive_expr_t<U>(std::forward<U>(u))); }
7027void variable_storage<bool, VariableKind::Local, false, L>::operator=(
U &&_value)
7029 if constexpr (num_locals == 1) {
7030 PrimitiveExpr<bool, L>
value(primitive_expr_t<U>(std::forward<U>(_value)));
7031 values_[0]->set(
value);
7033 PrimitiveExpr<bool, L>
value(primitive_expr_t<U>(std::forward<U>(_value)));
7036 for (std::size_t idx = 0; idx < num_locals; ++idx)
7037 values_[idx]->set(
vectors[idx]);
7041template<std::
size_t L>
7042inline variable_storage<bool, VariableKind::Local, false, L>::operator PrimitiveExpr<bool, L>()
const
7044 if constexpr (num_locals == 1) {
7045 return PrimitiveExpr<bool, L>( values_[0]->is_set().
expr(), { values_[0] });
7047 static_assert(num_locals == PrimitiveExpr<bool, L>::num_vectors);
7048 std::array<typename PrimitiveExpr<bool, L>::vector_type, num_locals>
vectors;
7049 for (std::size_t idx = 0; idx < num_locals; ++idx)
7050 vectors[idx] =
typename PrimitiveExpr<bool, L>::vector_type(
7051 values_[idx]->is_set().expr(),
7054 return PrimitiveExpr<bool, L>(std::move(
vectors));
7069extern template void Module::emit_insist(PrimitiveExpr<bool, 2>,
const char*,
unsigned,
const char*);
7070extern template void Module::emit_insist(PrimitiveExpr<bool, 4>,
const char*,
unsigned,
const char*);
7071extern template void Module::emit_insist(PrimitiveExpr<bool, 8>,
const char*,
unsigned,
const char*);
7072extern template void Module::emit_insist(PrimitiveExpr<bool, 16>,
const char*,
unsigned,
const char*);
7073extern 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.