summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2022-07-20 20:13:18 -0700
committerGitHub <noreply@github.com>2022-07-20 20:13:18 -0700
commitda5035f893ce9e046f99cf3ede92b576024aa9da (patch)
tree9db48e77502a646d74ef1a9d11f9b8f0967ff856 /src
parent1c53f7dd29e79bc1894959cad817b22f087689f7 (diff)
downloadbinaryen-da5035f893ce9e046f99cf3ede92b576024aa9da.tar.gz
binaryen-da5035f893ce9e046f99cf3ede92b576024aa9da.tar.bz2
binaryen-da5035f893ce9e046f99cf3ede92b576024aa9da.zip
Remove basic reference types (#4802)
Basic reference types like `Type::funcref`, `Type::anyref`, etc. made it easy to accidentally forget to handle reference types with the same basic HeapTypes but the opposite nullability. In principle there is nothing special about the types with shorthands except in the binary and text formats. Removing these shorthands from the internal type representation by removing all basic reference types makes some code more complicated locally, but simplifies code globally and encourages properly handling both nullable and non-nullable reference types.
Diffstat (limited to 'src')
-rw-r--r--src/asmjs/asm_v_wasm.cpp16
-rw-r--r--src/binaryen-c.cpp90
-rw-r--r--src/ir/abstract.h10
-rw-r--r--src/ir/module-splitting.cpp12
-rw-r--r--src/ir/table-utils.cpp3
-rw-r--r--src/literal.h43
-rw-r--r--src/passes/ConstHoisting.cpp8
-rw-r--r--src/passes/FuncCastEmulation.cpp14
-rw-r--r--src/passes/InstrumentLocals.cpp167
-rw-r--r--src/passes/OptimizeInstructions.cpp8
-rw-r--r--src/passes/Precompute.cpp3
-rw-r--r--src/passes/Print.cpp98
-rw-r--r--src/tools/fuzzing/fuzzing.cpp76
-rw-r--r--src/tools/fuzzing/heap-types.cpp8
-rw-r--r--src/tools/spec-wrapper.h12
-rw-r--r--src/tools/wasm-reduce.cpp40
-rw-r--r--src/tools/wasm-shell.cpp4
-rw-r--r--src/wasm-builder.h33
-rw-r--r--src/wasm-interpreter.h10
-rw-r--r--src/wasm-type.h8
-rw-r--r--src/wasm.h8
-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
29 files changed, 386 insertions, 631 deletions
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
index 38eb029f4..314c2494f 100644
--- a/src/asmjs/asm_v_wasm.cpp
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -33,12 +33,6 @@ JsType wasmToJsType(Type type) {
return JS_INT64;
case Type::v128:
WASM_UNREACHABLE("v128 not implemented yet");
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- WASM_UNREACHABLE("reference types are not supported by wasm2js");
case Type::none:
return JS_NONE;
case Type::unreachable:
@@ -60,16 +54,6 @@ char getSig(Type type) {
return 'd';
case Type::v128:
return 'V';
- case Type::funcref:
- return 'F';
- case Type::anyref:
- return 'A';
- case Type::eqref:
- return 'Q';
- case Type::i31ref:
- return 'I';
- case Type::dataref:
- return 'D';
case Type::none:
return 'v';
case Type::unreachable:
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index e2afe66dc..d0e99ac0b 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -51,6 +51,29 @@ static_assert(sizeof(BinaryenLiteral) == sizeof(Literal),
BinaryenLiteral toBinaryenLiteral(Literal x) {
BinaryenLiteral ret;
ret.type = x.type.getID();
+ if (x.type.isRef()) {
+ auto heapType = x.type.getHeapType();
+ if (heapType.isBasic()) {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ ret.func = x.isNull() ? nullptr : x.getFunc().c_str();
+ break;
+ case HeapType::any:
+ case HeapType::eq:
+ assert(x.isNull() && "unexpected non-null reference type literal");
+ break;
+ case HeapType::i31:
+ case HeapType::data:
+ case HeapType::string:
+ case HeapType::stringview_wtf8:
+ case HeapType::stringview_wtf16:
+ case HeapType::stringview_iter:
+ WASM_UNREACHABLE("TODO: reftypes");
+ }
+ return ret;
+ }
+ WASM_UNREACHABLE("TODO: reftypes");
+ }
TODO_SINGLE_COMPOUND(x.type);
switch (x.type.getBasic()) {
case Type::i32:
@@ -68,16 +91,6 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
case Type::v128:
memcpy(&ret.v128, x.getv128Ptr(), 16);
break;
- case Type::funcref:
- ret.func = x.isNull() ? nullptr : x.getFunc().c_str();
- break;
- case Type::anyref:
- case Type::eqref:
- assert(x.isNull() && "unexpected non-null reference type literal");
- break;
- case Type::i31ref:
- case Type::dataref:
- WASM_UNREACHABLE("TODO: reftypes");
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -86,7 +99,30 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
}
Literal fromBinaryenLiteral(BinaryenLiteral x) {
- switch (x.type) {
+ auto type = Type(x.type);
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ if (type.isNullable()) {
+ return Literal::makeNull(heapType);
+ }
+ if (heapType.isBasic()) {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ return Literal::makeFunc(x.func);
+ case HeapType::any:
+ case HeapType::eq:
+ case HeapType::i31:
+ case HeapType::data:
+ case HeapType::string:
+ case HeapType::stringview_wtf8:
+ case HeapType::stringview_wtf16:
+ case HeapType::stringview_iter:
+ WASM_UNREACHABLE("TODO: reftypes");
+ }
+ }
+ }
+ assert(type.isBasic());
+ switch (type.getBasic()) {
case Type::i32:
return Literal(x.i32);
case Type::i64:
@@ -97,14 +133,6 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) {
return Literal(x.i64).castToF64();
case Type::v128:
return Literal(x.v128);
- case Type::funcref:
- return Literal::makeFunc(x.func);
- case Type::anyref:
- case Type::eqref:
- return Literal::makeNull(Type(x.type).getHeapType());
- case Type::i31ref:
- case Type::dataref:
- WASM_UNREACHABLE("TODO: reftypes");
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -137,12 +165,24 @@ BinaryenType BinaryenTypeInt64(void) { return Type::i64; }
BinaryenType BinaryenTypeFloat32(void) { return Type::f32; }
BinaryenType BinaryenTypeFloat64(void) { return Type::f64; }
BinaryenType BinaryenTypeVec128(void) { return Type::v128; }
-BinaryenType BinaryenTypeFuncref(void) { return Type::funcref; }
-BinaryenType BinaryenTypeExternref(void) { return Type::anyref; }
-BinaryenType BinaryenTypeAnyref(void) { return Type::anyref; }
-BinaryenType BinaryenTypeEqref(void) { return Type::eqref; }
-BinaryenType BinaryenTypeI31ref(void) { return Type::i31ref; }
-BinaryenType BinaryenTypeDataref(void) { return Type::dataref; }
+BinaryenType BinaryenTypeFuncref(void) {
+ return Type(HeapType::func, Nullable).getID();
+}
+BinaryenType BinaryenTypeExternref(void) {
+ return Type(HeapType::any, Nullable).getID();
+}
+BinaryenType BinaryenTypeAnyref(void) {
+ return Type(HeapType::any, Nullable).getID();
+}
+BinaryenType BinaryenTypeEqref(void) {
+ return Type(HeapType::eq, Nullable).getID();
+}
+BinaryenType BinaryenTypeI31ref(void) {
+ return Type(HeapType::i31, NonNullable).getID();
+}
+BinaryenType BinaryenTypeDataref(void) {
+ return Type(HeapType::data, NonNullable).getID();
+}
BinaryenType BinaryenTypeUnreachable(void) { return Type::unreachable; }
BinaryenType BinaryenTypeAuto(void) { return uintptr_t(-1); }
diff --git a/src/ir/abstract.h b/src/ir/abstract.h
index 63057a975..feac9b50d 100644
--- a/src/ir/abstract.h
+++ b/src/ir/abstract.h
@@ -121,11 +121,6 @@ inline UnaryOp getUnary(Type type, Op op) {
break;
}
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable: {
return InvalidUnary;
@@ -293,11 +288,6 @@ inline BinaryOp getBinary(Type type, Op op) {
break;
}
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable: {
return InvalidBinary;
diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp
index dc04cdba2..e24dd6452 100644
--- a/src/ir/module-splitting.cpp
+++ b/src/ir/module-splitting.cpp
@@ -143,11 +143,11 @@ void TableSlotManager::addSlot(Name func, Slot slot) {
TableSlotManager::TableSlotManager(Module& module) : module(module) {
// TODO: Reject or handle passive element segments
- auto it = std::find_if(module.tables.begin(),
- module.tables.end(),
- [&](std::unique_ptr<Table>& table) {
- return table->type == Type::funcref;
- });
+ auto funcref = Type(HeapType::func, Nullable);
+ auto it = std::find_if(
+ module.tables.begin(),
+ module.tables.end(),
+ [&](std::unique_ptr<Table>& table) { return table->type == funcref; });
if (it == module.tables.end()) {
return;
}
@@ -163,7 +163,7 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) {
// append new items at constant offsets after all existing items at constant
// offsets.
if (activeTableSegments.size() == 1 &&
- activeTableSegments[0]->type == Type::funcref &&
+ activeTableSegments[0]->type == funcref &&
!activeTableSegments[0]->offset->is<Const>()) {
assert(activeTableSegments[0]->offset->is<GlobalGet>() &&
"Unexpected initializer instruction");
diff --git a/src/ir/table-utils.cpp b/src/ir/table-utils.cpp
index 0d47f155b..fb9285319 100644
--- a/src/ir/table-utils.cpp
+++ b/src/ir/table-utils.cpp
@@ -76,7 +76,8 @@ bool usesExpressions(ElementSegment* curr, Module* module) {
// declare a type that is a subtype of that, so it must use the post-MVP form
// of using expressions.
bool hasTableOfSpecializedType =
- curr->table.is() && module->getTable(curr->table)->type != Type::funcref;
+ curr->table.is() &&
+ module->getTable(curr->table)->type != Type(HeapType::func, Nullable);
return !allElementsRefFunc || hasTableOfSpecializedType;
}
diff --git a/src/literal.h b/src/literal.h
index 4caeab9d4..c37f74499 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -251,11 +251,12 @@ public:
static Literal makeNull(HeapType type) {
return Literal(Type(type, Nullable));
}
- static Literal makeFunc(Name func, Type type = Type::funcref) {
+ static Literal makeFunc(Name func,
+ Type type = Type(HeapType::func, Nullable)) {
return Literal(func, type);
}
static Literal makeI31(int32_t value) {
- auto lit = Literal(Type::i31ref);
+ auto lit = Literal(Type(HeapType::i31, NonNullable));
lit.i32 = value & 0x7fffffff;
return lit;
}
@@ -753,20 +754,7 @@ struct RttSupers : std::vector<RttSuper> {};
namespace std {
template<> struct hash<wasm::Literal> {
size_t operator()(const wasm::Literal& a) const {
- auto digest = wasm::hash(a.type.getID());
- auto hashRef = [&]() {
- assert(a.type.isRef());
- if (a.isNull()) {
- return digest;
- }
- if (a.type.isFunction()) {
- wasm::rehash(digest, a.getFunc());
- return digest;
- }
- // 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");
- };
+ auto digest = wasm::hash(a.type);
if (a.type.isBasic()) {
switch (a.type.getBasic()) {
case wasm::Type::i32:
@@ -787,20 +775,25 @@ template<> struct hash<wasm::Literal> {
wasm::rehash(digest, chunks[0]);
wasm::rehash(digest, chunks[1]);
return digest;
- case wasm::Type::funcref:
- case wasm::Type::anyref:
- case wasm::Type::eqref:
- case wasm::Type::dataref:
- return hashRef();
- case wasm::Type::i31ref:
- wasm::rehash(digest, a.geti31(true));
- return digest;
case wasm::Type::none:
case wasm::Type::unreachable:
break;
}
} else if (a.type.isRef()) {
- return hashRef();
+ if (a.isNull()) {
+ return digest;
+ }
+ if (a.type.isFunction()) {
+ wasm::rehash(digest, a.getFunc());
+ return digest;
+ }
+ if (a.type.getHeapType() == wasm::HeapType::i31) {
+ wasm::rehash(digest, a.geti31(true));
+ return digest;
+ }
+ // 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 (a.type.isRtt()) {
const auto& supers = a.getRttSupers();
wasm::rehash(digest, supers.size());
diff --git a/src/passes/ConstHoisting.cpp b/src/passes/ConstHoisting.cpp
index 3320c129b..3d9ac5e90 100644
--- a/src/passes/ConstHoisting.cpp
+++ b/src/passes/ConstHoisting.cpp
@@ -89,15 +89,9 @@ private:
size = value.type.getByteSize();
break;
}
- // not implemented yet
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref: {
return false;
- }
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
diff --git a/src/passes/FuncCastEmulation.cpp b/src/passes/FuncCastEmulation.cpp
index 704f44e03..5e62ec9fc 100644
--- a/src/passes/FuncCastEmulation.cpp
+++ b/src/passes/FuncCastEmulation.cpp
@@ -62,13 +62,6 @@ static Expression* toABI(Expression* value, Module* module) {
case Type::v128: {
WASM_UNREACHABLE("v128 not implemented yet");
}
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref: {
- WASM_UNREACHABLE("reference types cannot be converted to i64");
- }
case Type::none: {
// the value is none, but we need a value here
value =
@@ -107,13 +100,6 @@ static Expression* fromABI(Expression* value, Type type, Module* module) {
case Type::v128: {
WASM_UNREACHABLE("v128 not implemented yet");
}
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref: {
- WASM_UNREACHABLE("reference types cannot be converted from i64");
- }
case Type::none: {
value = builder.makeDrop(value);
break;
diff --git a/src/passes/InstrumentLocals.cpp b/src/passes/InstrumentLocals.cpp
index 3bf23b61c..761aac2a9 100644
--- a/src/passes/InstrumentLocals.cpp
+++ b/src/passes/InstrumentLocals.cpp
@@ -58,9 +58,6 @@ Name get_f64("get_f64");
Name get_v128("get_v128");
Name get_funcref("get_funcref");
Name get_anyref("get_anyref");
-Name get_eqref("get_eqref");
-Name get_i31ref("get_i31ref");
-Name get_dataref("get_dataref");
Name set_i32("set_i32");
Name set_i64("set_i64");
@@ -69,48 +66,41 @@ Name set_f64("set_f64");
Name set_v128("set_v128");
Name set_funcref("set_funcref");
Name set_anyref("set_anyref");
-Name set_eqref("set_eqref");
-Name set_i31ref("set_i31ref");
-Name set_dataref("set_dataref");
struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
void visitLocalGet(LocalGet* curr) {
Builder builder(*getModule());
Name import;
- TODO_SINGLE_COMPOUND(curr->type);
- switch (curr->type.getBasic()) {
- case Type::i32:
- import = get_i32;
- break;
- case Type::i64:
- return; // TODO
- case Type::f32:
- import = get_f32;
- break;
- case Type::f64:
- import = get_f64;
- break;
- case Type::v128:
- import = get_v128;
- break;
- case Type::funcref:
+ if (curr->type.isRef()) {
+ auto heapType = curr->type.getHeapType();
+ if (heapType == HeapType::func && curr->type.isNullable()) {
import = get_funcref;
- break;
- case Type::anyref:
+ } else if (heapType == HeapType::any && curr->type.isNullable()) {
import = get_anyref;
- break;
- case Type::eqref:
- import = get_eqref;
- break;
- case Type::i31ref:
- import = get_i31ref;
- break;
- case Type::dataref:
- import = get_dataref;
- break;
- case Type::none:
- case Type::unreachable:
- WASM_UNREACHABLE("unexpected type");
+ } else {
+ WASM_UNREACHABLE("TODO: general reference types");
+ }
+ } else {
+ TODO_SINGLE_COMPOUND(curr->type);
+ switch (curr->type.getBasic()) {
+ case Type::i32:
+ import = get_i32;
+ break;
+ case Type::i64:
+ return; // TODO
+ case Type::f32:
+ import = get_f32;
+ break;
+ case Type::f64:
+ import = get_f64;
+ break;
+ case Type::v128:
+ import = get_v128;
+ break;
+ case Type::none:
+ case Type::unreachable:
+ WASM_UNREACHABLE("unexpected type");
+ }
}
replaceCurrent(builder.makeCall(import,
{builder.makeConst(int32_t(id++)),
@@ -130,45 +120,41 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
Builder builder(*getModule());
Name import;
auto type = curr->value->type;
- if (type.isFunction() && type != Type::funcref) {
+ if (type.isFunction() && type.getHeapType() != HeapType::func) {
// FIXME: support typed function references
return;
}
- TODO_SINGLE_COMPOUND(curr->value->type);
- switch (type.getBasic()) {
- case Type::i32:
- import = set_i32;
- break;
- case Type::i64:
- return; // TODO
- case Type::f32:
- import = set_f32;
- break;
- case Type::f64:
- import = set_f64;
- break;
- case Type::v128:
- import = set_v128;
- break;
- case Type::funcref:
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ if (heapType == HeapType::func && type.isNullable()) {
import = set_funcref;
- break;
- case Type::anyref:
+ } else if (heapType == HeapType::any && type.isNullable()) {
import = set_anyref;
- break;
- case Type::eqref:
- import = set_eqref;
- break;
- case Type::i31ref:
- import = set_i31ref;
- break;
- case Type::dataref:
- import = set_dataref;
- break;
- case Type::unreachable:
- return; // nothing to do here
- default:
- WASM_UNREACHABLE("unexpected type");
+ } else {
+ WASM_UNREACHABLE("TODO: general reference types");
+ }
+ } else {
+ TODO_SINGLE_COMPOUND(curr->value->type);
+ switch (type.getBasic()) {
+ case Type::i32:
+ import = set_i32;
+ break;
+ case Type::i64:
+ return; // TODO
+ case Type::f32:
+ import = set_f32;
+ break;
+ case Type::f64:
+ import = set_f64;
+ break;
+ case Type::v128:
+ import = set_v128;
+ break;
+ case Type::unreachable:
+ return; // nothing to do here
+ case Type::none:
+ WASM_UNREACHABLE("unexpected type");
+ }
}
curr->value = builder.makeCall(import,
{builder.makeConst(int32_t(id++)),
@@ -188,36 +174,13 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
addImport(curr, set_f64, {Type::i32, Type::i32, Type::f64}, Type::f64);
if (curr->features.hasReferenceTypes()) {
- addImport(curr,
- get_funcref,
- {Type::i32, Type::i32, Type::funcref},
- Type::funcref);
- addImport(curr,
- set_funcref,
- {Type::i32, Type::i32, Type::funcref},
- Type::funcref);
- addImport(
- curr, get_anyref, {Type::i32, Type::i32, Type::anyref}, Type::anyref);
- addImport(
- curr, set_anyref, {Type::i32, Type::i32, Type::anyref}, Type::anyref);
- if (curr->features.hasGC()) {
- addImport(
- curr, get_eqref, {Type::i32, Type::i32, Type::eqref}, Type::eqref);
- addImport(
- curr, set_eqref, {Type::i32, Type::i32, Type::eqref}, Type::eqref);
- addImport(
- curr, get_i31ref, {Type::i32, Type::i32, Type::i31ref}, Type::i31ref);
- addImport(
- curr, set_i31ref, {Type::i32, Type::i32, Type::i31ref}, Type::i31ref);
- addImport(curr,
- get_dataref,
- {Type::i32, Type::i32, Type::dataref},
- Type::dataref);
- addImport(curr,
- set_dataref,
- {Type::i32, Type::i32, Type::dataref},
- Type::dataref);
- }
+ Type func = Type(HeapType::func, Nullable);
+ Type any = Type(HeapType::any, Nullable);
+
+ addImport(curr, get_funcref, {Type::i32, Type::i32, func}, func);
+ addImport(curr, set_funcref, {Type::i32, Type::i32, func}, func);
+ addImport(curr, get_anyref, {Type::i32, Type::i32, any}, any);
+ addImport(curr, set_anyref, {Type::i32, Type::i32, any}, any);
}
if (curr->features.hasSIMD()) {
addImport(curr, get_v128, {Type::i32, Type::i32, Type::v128}, Type::v128);
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 9d50275b9..9ab8e20d9 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1472,7 +1472,8 @@ struct OptimizeInstructions
// there.
//
// See "notes on removing casts", above, for when this is safe to do.
- void skipCast(Expression*& input, Type requiredType = Type::anyref) {
+ void skipCast(Expression*& input,
+ Type requiredType = Type(HeapType::any, Nullable)) {
// Traps-never-happen mode is a requirement for us to optimize here.
if (!getPassOptions().trapsNeverHappen) {
return;
@@ -1523,8 +1524,9 @@ struct OptimizeInstructions
// This is safe to do first because nothing farther down cares about the
// type, and we consume the two input references, so removing a cast could
// not help our parents (see "notes on removing casts").
- skipCast(curr->left, Type::eqref);
- skipCast(curr->right, Type::eqref);
+ Type nullableEq = Type(HeapType::eq, Nullable);
+ skipCast(curr->left, nullableEq);
+ skipCast(curr->right, nullableEq);
// Identical references compare equal.
// (Technically we do not need to check if the inputs are also foldable into
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index f599990a0..156c33c3f 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -249,7 +249,8 @@ struct Precompute
curr->finalize();
return;
}
- } else if (singleValue.type == Type::funcref) {
+ } else if (singleValue.type.isRef() &&
+ singleValue.type.getHeapType() == HeapType::func) {
if (auto* r = curr->value->template dynCast<RefFunc>()) {
r->func = singleValue.getFunc();
r->finalize();
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index ca2985240..440ff114f 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -72,6 +72,62 @@ static std::ostream& printLocal(Index index, Function* func, std::ostream& o) {
namespace {
+static bool maybePrintRefShorthand(std::ostream& o, Type type) {
+ if (!type.isRef()) {
+ return false;
+ }
+ auto heapType = type.getHeapType();
+ if (heapType.isBasic()) {
+ if (type.isNullable()) {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ o << "funcref";
+ return true;
+ case HeapType::any:
+ o << "anyref";
+ return true;
+ case HeapType::eq:
+ o << "eqref";
+ return true;
+ case HeapType::i31:
+ case HeapType::data:
+ break;
+ case HeapType::string:
+ o << "stringref";
+ return true;
+ case HeapType::stringview_wtf8:
+ o << "stringview_wtf8";
+ return true;
+ case HeapType::stringview_wtf16:
+ o << "stringview_wtf16";
+ return true;
+ case HeapType::stringview_iter:
+ o << "stringview_iter";
+ return true;
+ }
+ } else {
+ switch (heapType.getBasic()) {
+ case HeapType::func:
+ case HeapType::any:
+ case HeapType::eq:
+ break;
+ case HeapType::i31:
+ o << "i31ref";
+ return true;
+ case HeapType::data:
+ o << "dataref";
+ return true;
+ case HeapType::string:
+ case HeapType::stringview_wtf8:
+ case HeapType::stringview_wtf16:
+ case HeapType::stringview_iter:
+ break;
+ }
+ }
+ }
+ return false;
+}
+
// Helper for printing the name of a type. This output is guaranteed to not
// contain spaces.
struct TypeNamePrinter {
@@ -126,13 +182,15 @@ void TypeNamePrinter::print(Type type) {
} else if (type.isRtt()) {
print(type.getRtt());
} else if (type.isRef()) {
- os << "ref";
- if (type.isNullable()) {
- os << "?";
+ if (!maybePrintRefShorthand(os, type)) {
+ os << "ref";
+ if (type.isNullable()) {
+ os << "?";
+ }
+ os << '|';
+ print(type.getHeapType());
+ os << '|';
}
- os << '|';
- print(type.getHeapType());
- os << '|';
} else {
WASM_UNREACHABLE("unexpected type");
}
@@ -273,29 +331,15 @@ static std::ostream& printType(std::ostream& o, Type type, Module* wasm) {
}
TypeNamePrinter(o, wasm).print(rtt.heapType);
o << ')';
- } else if (type.isRef() && !type.isBasic()) {
- auto heapType = type.getHeapType();
- if (type.isNullable() && heapType.isBasic()) {
- // Print shorthands for certain nullable basic heap types.
- switch (heapType.getBasic()) {
- case HeapType::string:
- return o << "stringref";
- case HeapType::stringview_wtf8:
- return o << "stringview_wtf8";
- case HeapType::stringview_wtf16:
- return o << "stringview_wtf16";
- case HeapType::stringview_iter:
- return o << "stringview_iter";
- default:
- break;
+ } else if (type.isRef()) {
+ if (!maybePrintRefShorthand(o, type)) {
+ o << "(ref ";
+ if (type.isNullable()) {
+ o << "null ";
}
+ TypeNamePrinter(o, wasm).print(type.getHeapType());
+ o << ')';
}
- o << "(ref ";
- if (type.isNullable()) {
- o << "null ";
- }
- TypeNamePrinter(o, wasm).print(type.getHeapType());
- o << ')';
} else {
WASM_UNREACHABLE("unexpected type");
}
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 5489e7638..59c9dca45 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -260,15 +260,15 @@ void TranslateToFuzzReader::setupTables() {
// Ensure a funcref element segment and table exist. Segments with more
// specific function types may have a smaller chance of getting functions.
Table* table = nullptr;
- auto iter =
- std::find_if(wasm.tables.begin(), wasm.tables.end(), [&](auto& table) {
- return table->type == Type::funcref;
- });
+ Type funcref = Type(HeapType::func, Nullable);
+ auto iter = std::find_if(wasm.tables.begin(),
+ wasm.tables.end(),
+ [&](auto& table) { return table->type == funcref; });
if (iter != wasm.tables.end()) {
table = iter->get();
} else {
auto tablePtr = builder.makeTable(
- Names::getValidTableName(wasm, "fuzzing_table"), Type::funcref, 0, 0);
+ Names::getValidTableName(wasm, "fuzzing_table"), funcref, 0, 0);
tablePtr->hasExplicitName = true;
table = wasm.addTable(std::move(tablePtr));
}
@@ -277,7 +277,7 @@ void TranslateToFuzzReader::setupTables() {
std::any_of(wasm.elementSegments.begin(),
wasm.elementSegments.end(),
[&](auto& segment) {
- return segment->table.is() && segment->type == Type::funcref;
+ return segment->table.is() && segment->type == funcref;
});
if (!hasFuncrefElemSegment) {
// TODO: use a random table
@@ -934,7 +934,7 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
if (type.isTuple()) {
options.add(FeatureSet::Multivalue, &Self::makeTupleMake);
}
- if (type == Type::i31ref) {
+ if (type.isRef() && type.getHeapType() == HeapType::i31) {
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeI31New);
}
// TODO: struct.get and other GC things
@@ -1463,11 +1463,6 @@ Expression* TranslateToFuzzReader::makeNonAtomicLoad(Type type) {
return builder.makeLoad(
16, false, offset, pick(1, 2, 4, 8, 16), ptr, type);
}
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("invalid type");
@@ -1566,11 +1561,6 @@ Expression* TranslateToFuzzReader::makeNonAtomicStore(Type type) {
return builder.makeStore(
16, offset, pick(1, 2, 4, 8, 16), ptr, value, type);
}
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("invalid type");
@@ -1701,11 +1691,6 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {
case Type::f64:
return Literal(getDouble());
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("invalid type");
@@ -1747,11 +1732,6 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {
case Type::f64:
return Literal(double(small));
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");
@@ -1816,11 +1796,6 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {
std::numeric_limits<uint64_t>::max()));
break;
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");
@@ -1844,11 +1819,6 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {
value = Literal(double(int64_t(1) << upTo(64)));
break;
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");
@@ -2115,11 +2085,6 @@ Expression* TranslateToFuzzReader::makeUnary(Type type) {
AllTrueVecI32x4),
make(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");
@@ -2255,11 +2220,6 @@ Expression* TranslateToFuzzReader::makeUnary(Type type) {
}
WASM_UNREACHABLE("invalid value");
}
- 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");
@@ -2493,11 +2453,6 @@ Expression* TranslateToFuzzReader::makeBinary(Type type) {
make(Type::v128),
make(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");
@@ -2700,11 +2655,6 @@ Expression* TranslateToFuzzReader::makeSIMDExtract(Type type) {
op = ExtractLaneVecF64x2;
break;
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");
@@ -2892,7 +2842,7 @@ Expression* TranslateToFuzzReader::makeRefEq(Type type) {
}
Expression* TranslateToFuzzReader::makeI31New(Type type) {
- assert(type == Type::i31ref);
+ assert(type.isRef() && type.getHeapType() == HeapType::i31);
assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
auto* value = make(Type::i32);
return builder.makeI31New(value);
@@ -2901,7 +2851,9 @@ Expression* TranslateToFuzzReader::makeI31New(Type type) {
Expression* TranslateToFuzzReader::makeI31Get(Type type) {
assert(type == Type::i32);
assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
- auto* i31 = make(Type::i31ref);
+ // TODO: Maybe this should be nullable?
+ // https://github.com/WebAssembly/gc/issues/312
+ auto* i31 = make(Type(HeapType::i31, NonNullable));
return builder.makeI31Get(i31, bool(oneIn(2)));
}
@@ -2958,7 +2910,9 @@ Type TranslateToFuzzReader::getSingleConcreteType() {
WeightedOption{Type::f32, VeryImportant},
WeightedOption{Type::f64, VeryImportant})
.add(FeatureSet::SIMD, WeightedOption{Type::v128, Important})
- .add(FeatureSet::ReferenceTypes, Type::funcref, Type::anyref)
+ .add(FeatureSet::ReferenceTypes,
+ Type(HeapType::func, Nullable),
+ Type(HeapType::any, Nullable))
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
// Type(HeapType::func, NonNullable),
// Type(HeapType::any, NonNullable),
@@ -2974,7 +2928,7 @@ Type TranslateToFuzzReader::getReferenceType() {
return pick(FeatureOptions<Type>()
// Avoid Type::anyref without GC enabled, see
// TranslateToFuzzReader::getSingleConcreteType.
- .add(FeatureSet::ReferenceTypes, Type::funcref)
+ .add(FeatureSet::ReferenceTypes, Type(HeapType::func, Nullable))
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
Type(HeapType::func, NonNullable),
Type(HeapType::any, NonNullable),
diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp
index 77891ce53..edb96977d 100644
--- a/src/tools/fuzzing/heap-types.cpp
+++ b/src/tools/fuzzing/heap-types.cpp
@@ -163,13 +163,7 @@ struct HeapTypeGeneratorImpl {
return rand.pick(
Random::FeatureOptions<Type::BasicType>{}
.add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
- .add(FeatureSet::SIMD, Type::v128)
- .add(FeatureSet::ReferenceTypes | FeatureSet::GC,
- Type::funcref,
- Type::anyref,
- Type::eqref,
- Type::i31ref,
- Type::dataref));
+ .add(FeatureSet::SIMD, Type::v128));
}
HeapType generateHeapType() {
diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h
index c2a49c598..366e8d4fc 100644
--- a/src/tools/spec-wrapper.h
+++ b/src/tools/spec-wrapper.h
@@ -52,18 +52,6 @@ inline std::string generateSpecWrapper(Module& wasm) {
case Type::v128:
ret += "(v128.const i32x4 0 0 0 0)";
break;
- case Type::funcref:
- ret += "(ref.null func)";
- break;
- case Type::anyref:
- ret += "(ref.null any)";
- break;
- case Type::eqref:
- ret += "(ref.null eq)";
- break;
- case Type::i31ref:
- case Type::dataref:
- WASM_UNREACHABLE("TODO: reftypes");
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index a4d326426..28cc77e95 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -626,13 +626,9 @@ struct Reducer
case Type::f64:
fixed = builder->makeUnary(TruncSFloat64ToInt32, child);
break;
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- continue; // not implemented yet
+ continue;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -653,13 +649,9 @@ struct Reducer
case Type::f64:
fixed = builder->makeUnary(TruncSFloat64ToInt64, child);
break;
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- continue; // not implemented yet
+ continue;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -680,13 +672,9 @@ struct Reducer
case Type::f64:
fixed = builder->makeUnary(DemoteFloat64, child);
break;
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- continue; // not implemented yet
+ continue;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
@@ -707,26 +695,18 @@ struct Reducer
break;
case Type::f64:
WASM_UNREACHABLE("unexpected type");
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- continue; // not implemented yet
+ continue;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
}
break;
}
+ // not implemented yet
case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::eqref:
- case Type::i31ref:
- case Type::dataref:
- continue; // not implemented yet
+ continue;
case Type::none:
case Type::unreachable:
WASM_UNREACHABLE("unexpected type");
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 617d56313..51eafb652 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -347,8 +347,8 @@ protected:
spectest->addExport(
builder.makeExport("global_f64", Name::fromInt(3), ExternalKind::Global));
- spectest->addTable(
- builder.makeTable(Name::fromInt(0), Type::funcref, 10, 20));
+ spectest->addTable(builder.makeTable(
+ Name::fromInt(0), Type(HeapType::func, Nullable), 10, 20));
spectest->addExport(
builder.makeExport("table", Name::fromInt(0), ExternalKind::Table));
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index eec86bc50..d76d24b21 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -82,7 +82,8 @@ public:
}
static std::unique_ptr<Table> makeTable(Name name,
- Type type = Type::funcref,
+ Type type = Type(HeapType::func,
+ Nullable),
Address initial = 0,
Address max = Table::kMaxSize) {
auto table = std::make_unique<Table>();
@@ -97,7 +98,7 @@ public:
makeElementSegment(Name name,
Name table,
Expression* offset = nullptr,
- Type type = Type::funcref) {
+ Type type = Type(HeapType::func, Nullable)) {
auto seg = std::make_unique<ElementSegment>();
seg->name = name;
seg->table = table;
@@ -1114,20 +1115,14 @@ public:
if (type.isFunction()) {
return makeRefFunc(value.getFunc(), type.getHeapType());
}
+ if (type.isRef() && type.getHeapType() == HeapType::i31) {
+ return makeI31New(makeConst(value.geti31()));
+ }
if (type.isRtt()) {
return makeRtt(value.type);
}
TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- case Type::anyref:
- case Type::eqref:
- assert(value.isNull() && "unexpected non-null reference type literal");
- return makeRefNull(type);
- case Type::i31ref:
- return makeI31New(makeConst(value.geti31()));
- default:
- WASM_UNREACHABLE("invalid constant expression");
- }
+ WASM_UNREACHABLE("unsupported constant expression");
}
Expression* makeConstantExpression(Literals values) {
@@ -1273,7 +1268,10 @@ public:
if (curr->type.isNullable()) {
return ExpressionManipulator::refNull(curr, curr->type);
}
- if (curr->type.isFunction() || !curr->type.isBasic()) {
+ if (curr->type.isRef() && curr->type.getHeapType() == HeapType::i31) {
+ return makeI31New(makeConst(0));
+ }
+ if (!curr->type.isBasic()) {
// We can't do any better, keep the original.
return curr;
}
@@ -1298,15 +1296,6 @@ public:
value = Literal(bytes.data());
break;
}
- case Type::funcref:
- WASM_UNREACHABLE("handled above");
- case Type::anyref:
- case Type::eqref:
- return ExpressionManipulator::refNull(curr, curr->type);
- case Type::i31ref:
- return makeI31New(makeConst(0));
- case Type::dataref:
- return curr;
case Type::none:
return ExpressionManipulator::nop(curr);
case Type::unreachable:
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 478b33bc4..eb5dcd5a7 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -2396,11 +2396,6 @@ public:
return Literal(load64u(addr)).castToF64();
case Type::v128:
return Literal(load128(addr).data());
- 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");
@@ -2454,11 +2449,6 @@ public:
case Type::v128:
store128(addr, value.getv128());
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-type.h b/src/wasm-type.h
index 23e559aad..4d87ac68d 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -106,14 +106,8 @@ public:
f32,
f64,
v128,
- funcref,
- anyref,
- eqref,
- // From here types are non-nullable.
- i31ref,
- dataref,
};
- static constexpr BasicType _last_basic_type = dataref;
+ static constexpr BasicType _last_basic_type = v128;
Type() : id(none) {}
diff --git a/src/wasm.h b/src/wasm.h
index b5d51b84b..99a2d9f94 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2036,11 +2036,13 @@ class ElementSegment : public Named {
public:
Name table;
Expression* offset;
- Type type = Type::funcref;
+ Type type = Type(HeapType::func, Nullable);
std::vector<Expression*> data;
ElementSegment() = default;
- ElementSegment(Name table, Expression* offset, Type type = Type::funcref)
+ ElementSegment(Name table,
+ Expression* offset,
+ Type type = Type(HeapType::func, Nullable))
: table(table), offset(offset), type(type) {}
ElementSegment(Name table,
Expression* offset,
@@ -2060,7 +2062,7 @@ public:
Address initial = 0;
Address max = kMaxSize;
- Type type = Type::funcref;
+ Type type = Type(HeapType::func, Nullable);
bool hasMax() { return max != kUnlimitedSize; }
void clear() {
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");