summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/literal.cpp98
-rw-r--r--src/wasm/wasm-binary.cpp55
-rw-r--r--src/wasm/wasm-s-parser.cpp15
-rw-r--r--src/wasm/wasm-stack.cpp15
-rw-r--r--src/wasm/wasm-type.cpp118
-rw-r--r--src/wasm/wasm-validator.cpp28
-rw-r--r--src/wasm/wasm.cpp12
-rw-r--r--src/wasm/wat-parser.cpp5
8 files changed, 106 insertions, 240 deletions
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 6c7689990..85e93f912 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -46,11 +46,6 @@ Literal::Literal(Type type) : type(type) {
case Type::none:
return;
case Type::unreachable:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
break;
}
}
@@ -100,11 +95,6 @@ Literal::Literal(const Literal& other) : type(other.type) {
case Type::none:
return;
case Type::unreachable:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
break;
}
}
@@ -239,7 +229,7 @@ Literals Literal::makeNegOnes(Type type) {
Literal Literal::makeZero(Type type) {
assert(type.isSingle());
if (type.isRef()) {
- if (type == Type::i31ref) {
+ if (type.getHeapType() == HeapType::i31) {
return makeI31(0);
} else {
return makeNull(type.getHeapType());
@@ -355,11 +345,6 @@ void Literal::getBits(uint8_t (&buf)[16]) const {
break;
case Type::none:
case Type::unreachable:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
WASM_UNREACHABLE("invalid type");
}
}
@@ -373,43 +358,37 @@ bool Literal::operator==(const Literal& other) const {
if (type != other.type) {
return false;
}
- auto compareRef = [&]() {
- assert(type.isRef());
- // Note that we've already handled nulls earlier.
- if (type.isFunction()) {
- assert(func.is() && other.func.is());
- return func == other.func;
- }
- if (type.isData()) {
- return gcData == other.gcData;
- }
- // other non-null reference type literals cannot represent concrete values,
- // i.e. there is no concrete anyref or eqref other than null.
- WASM_UNREACHABLE("unexpected type");
- };
if (type.isBasic()) {
switch (type.getBasic()) {
case Type::none:
return true; // special voided literal
case Type::i32:
case Type::f32:
- case Type::i31ref:
return i32 == other.i32;
case Type::i64:
case Type::f64:
return i64 == other.i64;
case Type::v128:
return memcmp(v128, other.v128, 16) == 0;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::dataref:
- return compareRef();
case Type::unreachable:
break;
}
} else if (type.isRef()) {
- return compareRef();
+ assert(type.isRef());
+ // Note that we've already handled nulls earlier.
+ if (type.isFunction()) {
+ assert(func.is() && other.func.is());
+ return func == other.func;
+ }
+ if (type.isData()) {
+ return gcData == other.gcData;
+ }
+ if (type.getHeapType() == HeapType::i31) {
+ return i32 == other.i32;
+ }
+ // other non-null reference type literals cannot represent concrete values,
+ // i.e. there is no concrete anyref or eqref other than null.
+ WASM_UNREACHABLE("unexpected type");
} else if (type.isRtt()) {
return *rttSupers == *other.rttSupers;
}
@@ -578,11 +557,6 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
o << "i32x4 ";
literal.printVec128(o, literal.getv128());
break;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
}
@@ -801,11 +775,6 @@ Literal Literal::eqz() const {
case Type::f64:
return eq(Literal(double(0)));
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -824,11 +793,6 @@ Literal Literal::neg() const {
case Type::f64:
return Literal(int64_t(i64 ^ 0x8000000000000000ULL)).castToF64();
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -847,11 +811,6 @@ Literal Literal::abs() const {
case Type::f64:
return Literal(int64_t(i64 & 0x7fffffffffffffffULL)).castToF64();
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -987,11 +946,6 @@ Literal Literal::add(const Literal& other) const {
case Type::f64:
return standardizeNaN(getf64() + other.getf64());
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -1010,11 +964,6 @@ Literal Literal::sub(const Literal& other) const {
case Type::f64:
return standardizeNaN(getf64() - other.getf64());
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -1112,11 +1061,6 @@ Literal Literal::mul(const Literal& other) const {
case Type::f64:
return standardizeNaN(getf64() * other.getf64());
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -1347,11 +1291,6 @@ Literal Literal::eq(const Literal& other) const {
case Type::f64:
return Literal(getf64() == other.getf64());
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -1370,11 +1309,6 @@ Literal Literal::ne(const Literal& other) const {
case Type::f64:
return Literal(getf64() != other.getf64());
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index e2588422c..5427deea1 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -693,6 +693,7 @@ void WasmBinaryWriter::writeElementSegments() {
auto start = startSection(BinaryConsts::Section::Element);
o << U32LEB(elemCount);
+ Type funcref = Type(HeapType::func, Nullable);
for (auto& segment : wasm->elementSegments) {
Index tableIdx = 0;
@@ -708,7 +709,7 @@ void WasmBinaryWriter::writeElementSegments() {
if (!isPassive) {
tableIdx = getTableIndex(segment->table);
hasTableIndex =
- tableIdx > 0 || wasm->getTable(segment->table)->type != Type::funcref;
+ tableIdx > 0 || wasm->getTable(segment->table)->type != funcref;
}
uint32_t flags = 0;
@@ -1339,7 +1340,36 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) {
}
void WasmBinaryWriter::writeType(Type type) {
- if (type.isRef() && !type.isBasic()) {
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ if (heapType.isBasic()) {
+ if (type.isNullable()) {
+ switch (heapType.getBasic()) {
+ case HeapType::any:
+ o << S32LEB(BinaryConsts::EncodedType::anyref);
+ return;
+ case HeapType::func:
+ o << S32LEB(BinaryConsts::EncodedType::funcref);
+ return;
+ case HeapType::eq:
+ o << S32LEB(BinaryConsts::EncodedType::eqref);
+ return;
+ default:
+ break;
+ }
+ } else {
+ switch (heapType.getBasic()) {
+ case HeapType::i31:
+ o << S32LEB(BinaryConsts::EncodedType::i31ref);
+ return;
+ case HeapType::data:
+ o << S32LEB(BinaryConsts::EncodedType::dataref);
+ return;
+ default:
+ break;
+ }
+ }
+ }
if (type.isNullable()) {
o << S32LEB(BinaryConsts::EncodedType::nullable);
} else {
@@ -1381,21 +1411,6 @@ void WasmBinaryWriter::writeType(Type type) {
case Type::v128:
ret = BinaryConsts::EncodedType::v128;
break;
- case Type::funcref:
- ret = BinaryConsts::EncodedType::funcref;
- break;
- case Type::anyref:
- ret = BinaryConsts::EncodedType::anyref;
- break;
- case Type::eqref:
- ret = BinaryConsts::EncodedType::eqref;
- break;
- case Type::i31ref:
- ret = BinaryConsts::EncodedType::i31ref;
- break;
- case Type::dataref:
- ret = BinaryConsts::EncodedType::dataref;
- break;
default:
WASM_UNREACHABLE("unexpected type");
}
@@ -1774,13 +1789,13 @@ bool WasmBinaryBuilder::getBasicType(int32_t code, Type& out) {
out = Type::v128;
return true;
case BinaryConsts::EncodedType::funcref:
- out = Type::funcref;
+ out = Type(HeapType::func, Nullable);
return true;
case BinaryConsts::EncodedType::anyref:
- out = Type::anyref;
+ out = Type(HeapType::any, Nullable);
return true;
case BinaryConsts::EncodedType::eqref:
- out = Type::eqref;
+ out = Type(HeapType::eq, Nullable);
return true;
case BinaryConsts::EncodedType::i31ref:
out = Type(HeapType::i31, NonNullable);
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index bfe09280d..97a25d2c4 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -1169,20 +1169,20 @@ Type SExpressionWasmBuilder::stringToType(const char* str,
}
}
if (strncmp(str, "funcref", 7) == 0 && (prefix || str[7] == 0)) {
- return Type::funcref;
+ return Type(HeapType::func, Nullable);
}
if ((strncmp(str, "externref", 9) == 0 && (prefix || str[9] == 0)) ||
(strncmp(str, "anyref", 6) == 0 && (prefix || str[6] == 0))) {
- return Type::anyref;
+ return Type(HeapType::any, Nullable);
}
if (strncmp(str, "eqref", 5) == 0 && (prefix || str[5] == 0)) {
- return Type::eqref;
+ return Type(HeapType::eq, Nullable);
}
if (strncmp(str, "i31ref", 6) == 0 && (prefix || str[6] == 0)) {
- return Type::i31ref;
+ return Type(HeapType::i31, NonNullable);
}
if (strncmp(str, "dataref", 7) == 0 && (prefix || str[7] == 0)) {
- return Type::dataref;
+ return Type(HeapType::data, NonNullable);
}
if (strncmp(str, "stringref", 9) == 0 && (prefix || str[9] == 0)) {
return Type(HeapType::string, Nullable);
@@ -1763,11 +1763,6 @@ parseConst(cashew::IString s, Type type, MixedArena& allocator) {
break;
}
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
WASM_UNREACHABLE("unexpected const type");
case Type::none:
case Type::unreachable: {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 28472aebd..c980d896b 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -187,11 +187,6 @@ void BinaryInstWriter::visitLoad(Load* curr) {
// the pointer is unreachable, so we are never reached; just don't emit
// a load
return;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
WASM_UNREACHABLE("unexpected type");
}
@@ -290,11 +285,6 @@ void BinaryInstWriter::visitStore(Store* curr) {
o << int8_t(BinaryConsts::SIMDPrefix)
<< U32LEB(BinaryConsts::V128Store);
break;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -742,11 +732,6 @@ void BinaryInstWriter::visitConst(Const* curr) {
}
break;
}
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index b13b1ecd7..84b11000f 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -567,19 +567,6 @@ Type asCanonical(Type type) {
}
}
-// Given a HeapType that may or may not be backed by the simplest possible
-// representation, return the equivalent type that is definitely backed by the
-// simplest possible representation.
-HeapType asCanonical(HeapType type) {
- if (type.isBasic()) {
- return type;
- } else if (auto canon = getHeapTypeInfo(type)->getCanonical()) {
- return *canon;
- } else {
- return type;
- }
-}
-
HeapType::BasicHeapType getBasicHeapSupertype(HeapType type) {
if (type.isBasic()) {
return type.getBasic();
@@ -670,35 +657,6 @@ std::optional<Type> TypeInfo::getCanonical() const {
return tuple.types[0];
}
}
- if (isRef()) {
- HeapType basic = asCanonical(ref.heapType);
- if (basic.isBasic()) {
- if (ref.nullable) {
- switch (basic.getBasic()) {
- case HeapType::func:
- return Type::funcref;
- case HeapType::any:
- return Type::anyref;
- case HeapType::eq:
- return Type::eqref;
- case HeapType::i31:
- case HeapType::data:
- case HeapType::string:
- case HeapType::stringview_wtf8:
- case HeapType::stringview_wtf16:
- case HeapType::stringview_iter:
- break;
- }
- } else {
- if (basic == HeapType::i31) {
- return Type::i31ref;
- }
- if (basic == HeapType::data) {
- return Type::dataref;
- }
- }
- }
- }
return {};
}
@@ -1004,7 +962,7 @@ bool Type::isTuple() const {
bool Type::isRef() const {
if (isBasic()) {
- return id >= funcref && id <= _last_basic_type;
+ return false;
} else {
return getTypeInfo(*this)->isRef();
}
@@ -1012,7 +970,7 @@ bool Type::isRef() const {
bool Type::isFunction() const {
if (isBasic()) {
- return id == funcref;
+ return false;
} else {
auto* info = getTypeInfo(*this);
return info->isRef() && info->ref.heapType.isFunction();
@@ -1021,7 +979,7 @@ bool Type::isFunction() const {
bool Type::isData() const {
if (isBasic()) {
- return id == dataref;
+ return false;
} else {
auto* info = getTypeInfo(*this);
return info->isRef() && info->ref.heapType.isData();
@@ -1030,7 +988,7 @@ bool Type::isData() const {
bool Type::isNullable() const {
if (isBasic()) {
- return id >= funcref && id <= eqref; // except i31ref and dataref
+ return false;
} else {
return getTypeInfo(*this)->isNullable();
}
@@ -1099,11 +1057,6 @@ unsigned Type::getByteSize() const {
return 8;
case Type::v128:
return 16;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
break;
@@ -1217,16 +1170,6 @@ HeapType Type::getHeapType() const {
case Type::f64:
case Type::v128:
break;
- case Type::funcref:
- return HeapType::func;
- case Type::anyref:
- return HeapType::any;
- case Type::eqref:
- return HeapType::eq;
- case Type::i31ref:
- return HeapType::i31;
- case Type::dataref:
- return HeapType::data;
}
WASM_UNREACHABLE("Unexpected type");
} else {
@@ -2086,16 +2029,6 @@ std::ostream& TypePrinter::print(Type type) {
return os << "f64";
case Type::v128:
return os << "v128";
- case Type::funcref:
- return os << "funcref";
- case Type::anyref:
- return os << "anyref";
- case Type::eqref:
- return os << "eqref";
- case Type::i31ref:
- return os << "i31ref";
- case Type::dataref:
- return os << "dataref";
}
}
@@ -2109,19 +2042,36 @@ std::ostream& TypePrinter::print(Type type) {
print(type.getTuple());
} else if (type.isRef()) {
auto heapType = type.getHeapType();
- if (type.isNullable() && heapType.isBasic()) {
- // Print shorthands for certain nullable basic heap types.
- switch (heapType.getBasic()) {
- case HeapType::string:
- return os << "stringref";
- case HeapType::stringview_wtf8:
- return os << "stringview_wtf8";
- case HeapType::stringview_wtf16:
- return os << "stringview_wtf16";
- case HeapType::stringview_iter:
- return os << "stringview_iter";
- default:
- break;
+ if (heapType.isBasic()) {
+ // Print shorthands for certain basic heap types.
+ if (type.isNullable()) {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ return os << "funcref";
+ case HeapType::any:
+ return os << "anyref";
+ case HeapType::eq:
+ return os << "eqref";
+ case HeapType::string:
+ return os << "stringref";
+ case HeapType::stringview_wtf8:
+ return os << "stringview_wtf8";
+ case HeapType::stringview_wtf16:
+ return os << "stringview_wtf16";
+ case HeapType::stringview_iter:
+ return os << "stringview_iter";
+ default:
+ break;
+ }
+ } else {
+ switch (heapType.getBasic()) {
+ case HeapType::i31:
+ return os << "i31ref";
+ case HeapType::data:
+ return os << "dataref";
+ default:
+ break;
+ }
}
}
os << "(ref ";
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 4c6e8e929..65a910747 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -1399,11 +1399,6 @@ void FunctionValidator::validateMemBytes(uint8_t bytes,
break;
case Type::unreachable:
break;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
WASM_UNREACHABLE("unexpected type");
}
@@ -2044,14 +2039,15 @@ void FunctionValidator::visitRefFunc(RefFunc* curr) {
}
void FunctionValidator::visitRefEq(RefEq* curr) {
+ Type eqref = Type(HeapType::eq, Nullable);
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.eq requires gc to be enabled");
shouldBeSubType(curr->left->type,
- Type::eqref,
+ eqref,
curr->left,
"ref.eq's left argument should be a subtype of eqref");
shouldBeSubType(curr->right->type,
- Type::eqref,
+ eqref,
curr->right,
"ref.eq's right argument should be a subtype of eqref");
}
@@ -2330,7 +2326,7 @@ void FunctionValidator::visitI31Get(I31Get* curr) {
curr,
"i31.get_s/u requires gc to be enabled");
shouldBeSubType(curr->i31->type,
- Type::i31ref,
+ Type(HeapType::i31, Nullable),
curr->i31,
"i31.get_s/u's argument should be i31ref");
}
@@ -2853,11 +2849,6 @@ void FunctionValidator::validateAlignment(
case Type::v128:
case Type::unreachable:
break;
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
WASM_UNREACHABLE("invalid type");
}
@@ -3130,7 +3121,7 @@ static void validateTables(Module& module, ValidationInfo& info) {
"--enable-reference-types)");
if (!module.tables.empty()) {
auto& table = module.tables.front();
- info.shouldBeTrue(table->type == Type::funcref,
+ info.shouldBeTrue(table->type == Type(HeapType::func, Nullable),
"table",
"Only funcref is valid for table type (when reference "
"types are disabled)");
@@ -3150,6 +3141,8 @@ static void validateTables(Module& module, ValidationInfo& info) {
}
}
+ Type anyref = Type(HeapType::any, Nullable);
+ Type funcref = Type(HeapType::func, Nullable);
for (auto& table : module.tables) {
info.shouldBeTrue(table->initial <= table->max,
"table",
@@ -3159,14 +3152,13 @@ static void validateTables(Module& module, ValidationInfo& info) {
"table",
"Non-nullable reference types are not yet supported for tables");
if (!module.features.hasGC()) {
- info.shouldBeTrue(table->type.isFunction() || table->type == Type::anyref,
+ info.shouldBeTrue(table->type.isFunction() || table->type == anyref,
"table",
- "Only function reference types or anyref are valid "
+ "Only function reference types or externref are valid "
"for table type (when GC is disabled)");
}
if (!module.features.hasTypedFunctionReferences()) {
- info.shouldBeTrue(table->type == Type::funcref ||
- table->type == Type::anyref,
+ info.shouldBeTrue(table->type == funcref || table->type == anyref,
"table",
"Only funcref and anyref are valid for table type "
"(when typed-function references are disabled)");
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index eceb4585b..bd83d62ed 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -903,7 +903,7 @@ void I31New::finalize() {
if (value->type == Type::unreachable) {
type = Type::unreachable;
} else {
- type = Type::i31ref;
+ type = Type(HeapType::i31, NonNullable);
}
}
@@ -1024,11 +1024,11 @@ Type BrOn::getSentType() {
}
return Type(getIntendedType(), NonNullable);
case BrOnFunc:
- return Type::funcref;
+ return Type(HeapType::func, NonNullable);
case BrOnData:
- return Type::dataref;
+ return Type(HeapType::data, NonNullable);
case BrOnI31:
- return Type::i31ref;
+ return Type(HeapType::i31, NonNullable);
case BrOnCastFail:
case BrOnNonFunc:
case BrOnNonData:
@@ -1164,10 +1164,10 @@ void RefAs::finalize() {
type = Type(HeapType::func, NonNullable);
break;
case RefAsData:
- type = Type::dataref;
+ type = Type(HeapType::data, NonNullable);
break;
case RefAsI31:
- type = Type::i31ref;
+ type = Type(HeapType::i31, NonNullable);
break;
default:
WASM_UNREACHABLE("invalid ref.as_*");
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index e3a2ab588..8b86017ff 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -1488,11 +1488,6 @@ Result<typename Ctx::InstrT> makeConst(Ctx& ctx, ParseInput& in, Type type) {
return in.err("unimplemented instruction");
case Type::none:
case Type::unreachable:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
break;
}
WASM_UNREACHABLE("unexpected type");