summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2024-06-18 18:20:49 -0700
committerGitHub <noreply@github.com>2024-06-19 01:20:49 +0000
commit2df678e4670517eaac40d1d2d9541d3b706b324b (patch)
tree3a47da6a20834dad1e43a8953ea428eb12fcb13c /src
parent829e228d4b2ccb314ea1e653fd16154ae3fd31b3 (diff)
downloadbinaryen-2df678e4670517eaac40d1d2d9541d3b706b324b.tar.gz
binaryen-2df678e4670517eaac40d1d2d9541d3b706b324b.tar.bz2
binaryen-2df678e4670517eaac40d1d2d9541d3b706b324b.zip
[threads] Shared basic heap types (#6667)
Implement binary and text parsing and printing of shared basic heap types and incorporate them into the type hierarchy. To avoid the massive amount of code duplication that would be necessary if we were to add separate enum variants for each of the shared basic heap types, use bit 0 to indicate whether the type is shared and replace `getBasic()` with `getBasic(Unshared)`, which clears that bit. Update all the use sites to record whether the original type was shared and produce shared or unshared output without code duplication.
Diffstat (limited to 'src')
-rw-r--r--src/analysis/lattices/shared.h18
-rw-r--r--src/binaryen-c.cpp4
-rw-r--r--src/ir/type-updating.cpp2
-rw-r--r--src/parser/contexts.h93
-rw-r--r--src/parser/parsers.h91
-rw-r--r--src/passes/TypeGeneralizing.cpp7
-rw-r--r--src/tools/fuzzing/fuzzing.cpp6
-rw-r--r--src/tools/fuzzing/heap-types.cpp6
-rw-r--r--src/tools/wasm-fuzz-lattices.cpp13
-rw-r--r--src/wasm-type.h64
-rw-r--r--src/wasm/literal.cpp14
-rw-r--r--src/wasm/wasm-binary.cpp23
-rw-r--r--src/wasm/wasm-type.cpp190
13 files changed, 322 insertions, 209 deletions
diff --git a/src/analysis/lattices/shared.h b/src/analysis/lattices/shared.h
index 54acf92b4..f345014b9 100644
--- a/src/analysis/lattices/shared.h
+++ b/src/analysis/lattices/shared.h
@@ -27,10 +27,10 @@ namespace wasm::analysis {
// A lattice whose elements are a single ascending chain in lattice `L`.
// Internally, there is only ever a single monotonically increasing element of L
-// materialized. Dereferencing any element of the Shared lattice will produce
-// the current value of that single element of L, which is generally safe
-// because the current value always overapproximates (i.e. is higher in the
-// lattice than) the value at the time of the Shared element's construction.
+// materialized. Dereferencing any element of the SharedPath lattice will
+// produce the current value of that single element of L, which is generally
+// safe because the current value always overapproximates (i.e. is higher in the
+// lattice than) the value at the time of the SharedPath element's construction.
//
// Each element of this lattice maintains a sequence number that corresponds to
// a value the shared underlying element has had at some point in time. Higher
@@ -38,7 +38,7 @@ namespace wasm::analysis {
// Elements of this lattice are compared and joined using these sequence
// numbers, so blocks will correctly be re-analyzed if the value has increased
// since the last time they were analyzed.
-template<Lattice L> struct Shared {
+template<Lattice L> struct SharedPath {
// If we ever have extremely long-running analyses, this may need to be
// changed to uint64_t.
using Seq = uint32_t;
@@ -70,7 +70,7 @@ template<Lattice L> struct Shared {
return !(*this == other);
}
- friend Shared;
+ friend SharedPath;
};
L lattice;
@@ -84,7 +84,7 @@ template<Lattice L> struct Shared {
mutable typename L::Element val;
mutable Seq seq = 0;
- Shared(L&& l) : lattice(std::move(l)), val(lattice.getBottom()) {}
+ SharedPath(L&& l) : lattice(std::move(l)), val(lattice.getBottom()) {}
// TODO: Delete the move constructor and the move assignment operator. This
// requires fixing the lattice fuzzer first, since it depends on lattices
@@ -119,10 +119,10 @@ template<Lattice L> struct Shared {
};
// Deduction guide.
-template<typename L> Shared(L&&) -> Shared<L>;
+template<typename L> SharedPath(L&&) -> SharedPath<L>;
#if __cplusplus >= 202002L
-static_assert(Lattice<Shared<Bool>>);
+static_assert(Lattice<SharedPath<Bool>>);
#endif // __cplusplus >= 202002L
} // namespace wasm::analysis
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 4e7fb0b61..841fa61ee 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -78,7 +78,7 @@ BinaryenLiteral toBinaryenLiteral(Literal x) {
assert(x.type.isRef());
auto heapType = x.type.getHeapType();
if (heapType.isBasic()) {
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::i31:
WASM_UNREACHABLE("TODO: i31");
case HeapType::ext:
@@ -132,7 +132,7 @@ Literal fromBinaryenLiteral(BinaryenLiteral x) {
assert(type.isRef());
auto heapType = type.getHeapType();
if (heapType.isBasic()) {
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::i31:
WASM_UNREACHABLE("TODO: i31");
case HeapType::ext:
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
index d9935ee32..ceb37300a 100644
--- a/src/ir/type-updating.cpp
+++ b/src/ir/type-updating.cpp
@@ -90,7 +90,7 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
i = 0;
for (auto [type, _] : typeIndices) {
typeBuilder[i].setOpen(type.isOpen());
- typeBuilder[i].setShared(type.isShared());
+ typeBuilder[i].setShared(type.getShared());
if (type.isSignature()) {
auto sig = type.getSignature();
TypeList newParams, newResults;
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index 6dabfca3d..f58275d71 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -105,24 +105,21 @@ struct NullTypeParserCtx {
using ElemListT = Ok;
using DataStringT = Ok;
- HeapTypeT makeFuncType() { return Ok{}; }
- HeapTypeT makeAnyType() { return Ok{}; }
- HeapTypeT makeExternType() { return Ok{}; }
- HeapTypeT makeEqType() { return Ok{}; }
- HeapTypeT makeI31Type() { return Ok{}; }
- HeapTypeT makeStructType() { return Ok{}; }
- HeapTypeT makeArrayType() { return Ok{}; }
- HeapTypeT makeExnType() { return Ok{}; }
- HeapTypeT makeStringType() { return Ok{}; }
- HeapTypeT makeStringViewWTF8Type() { return Ok{}; }
- HeapTypeT makeStringViewWTF16Type() { return Ok{}; }
- HeapTypeT makeStringViewIterType() { return Ok{}; }
- HeapTypeT makeContType() { return Ok{}; }
- HeapTypeT makeNoneType() { return Ok{}; }
- HeapTypeT makeNoextType() { return Ok{}; }
- HeapTypeT makeNofuncType() { return Ok{}; }
- HeapTypeT makeNoexnType() { return Ok{}; }
- HeapTypeT makeNocontType() { return Ok{}; }
+ HeapTypeT makeFuncType(Shareability) { return Ok{}; }
+ HeapTypeT makeAnyType(Shareability) { return Ok{}; }
+ HeapTypeT makeExternType(Shareability) { return Ok{}; }
+ HeapTypeT makeEqType(Shareability) { return Ok{}; }
+ HeapTypeT makeI31Type(Shareability) { return Ok{}; }
+ HeapTypeT makeStructType(Shareability) { return Ok{}; }
+ HeapTypeT makeArrayType(Shareability) { return Ok{}; }
+ HeapTypeT makeExnType(Shareability) { return Ok{}; }
+ HeapTypeT makeStringType(Shareability) { return Ok{}; }
+ HeapTypeT makeContType(Shareability) { return Ok{}; }
+ HeapTypeT makeNoneType(Shareability) { return Ok{}; }
+ HeapTypeT makeNoextType(Shareability) { return Ok{}; }
+ HeapTypeT makeNofuncType(Shareability) { return Ok{}; }
+ HeapTypeT makeNoexnType(Shareability) { return Ok{}; }
+ HeapTypeT makeNocontType(Shareability) { return Ok{}; }
TypeT makeI32() { return Ok{}; }
TypeT makeI64() { return Ok{}; }
@@ -208,21 +205,51 @@ template<typename Ctx> struct TypeParserCtx {
Ctx& self() { return *static_cast<Ctx*>(this); }
- HeapTypeT makeFuncType() { return HeapType::func; }
- HeapTypeT makeAnyType() { return HeapType::any; }
- HeapTypeT makeExternType() { return HeapType::ext; }
- HeapTypeT makeEqType() { return HeapType::eq; }
- HeapTypeT makeI31Type() { return HeapType::i31; }
- HeapTypeT makeStructType() { return HeapType::struct_; }
- HeapTypeT makeArrayType() { return HeapType::array; }
- HeapTypeT makeExnType() { return HeapType::exn; }
- HeapTypeT makeStringType() { return HeapType::string; }
- HeapTypeT makeContType() { return HeapType::cont; }
- HeapTypeT makeNoneType() { return HeapType::none; }
- HeapTypeT makeNoextType() { return HeapType::noext; }
- HeapTypeT makeNofuncType() { return HeapType::nofunc; }
- HeapTypeT makeNoexnType() { return HeapType::noexn; }
- HeapTypeT makeNocontType() { return HeapType::nocont; }
+ HeapTypeT makeFuncType(Shareability share) {
+ return HeapTypes::func.getBasic(share);
+ }
+ HeapTypeT makeAnyType(Shareability share) {
+ return HeapTypes::any.getBasic(share);
+ }
+ HeapTypeT makeExternType(Shareability share) {
+ return HeapTypes::ext.getBasic(share);
+ }
+ HeapTypeT makeEqType(Shareability share) {
+ return HeapTypes::eq.getBasic(share);
+ }
+ HeapTypeT makeI31Type(Shareability share) {
+ return HeapTypes::i31.getBasic(share);
+ }
+ HeapTypeT makeStructType(Shareability share) {
+ return HeapTypes::struct_.getBasic(share);
+ }
+ HeapTypeT makeArrayType(Shareability share) {
+ return HeapTypes::array.getBasic(share);
+ }
+ HeapTypeT makeExnType(Shareability share) {
+ return HeapTypes::exn.getBasic(share);
+ }
+ HeapTypeT makeStringType(Shareability share) {
+ return HeapTypes::string.getBasic(share);
+ }
+ HeapTypeT makeContType(Shareability share) {
+ return HeapTypes::cont.getBasic(share);
+ }
+ HeapTypeT makeNoneType(Shareability share) {
+ return HeapTypes::none.getBasic(share);
+ }
+ HeapTypeT makeNoextType(Shareability share) {
+ return HeapTypes::noext.getBasic(share);
+ }
+ HeapTypeT makeNofuncType(Shareability share) {
+ return HeapTypes::nofunc.getBasic(share);
+ }
+ HeapTypeT makeNoexnType(Shareability share) {
+ return HeapTypes::noexn.getBasic(share);
+ }
+ HeapTypeT makeNocontType(Shareability share) {
+ return HeapTypes::nocont.getBasic(share);
+ }
TypeT makeI32() { return Type::i32; }
TypeT makeI64() { return Type::i64; }
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 1c329aecb..db450e3c6 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -27,6 +27,8 @@ namespace wasm::WATParser {
using namespace std::string_view_literals;
// Types
+template<typename Ctx>
+Result<typename Ctx::HeapTypeT> absheaptype(Ctx&, Shareability);
template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::RefTypeT> maybeRefType(Ctx&);
template<typename Ctx> Result<typename Ctx::RefTypeT> reftype(Ctx&);
@@ -358,58 +360,73 @@ template<typename Ctx> Result<> module(Ctx&);
// Types
// =====
-// heaptype ::= x:typeidx => types[x]
-// | 'func' => func
-// | 'extern' => extern
-template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
+// absheaptype ::= 'func' | 'extern' | ...
+template<typename Ctx>
+Result<typename Ctx::HeapTypeT> absheaptype(Ctx& ctx, Shareability share) {
if (ctx.in.takeKeyword("func"sv)) {
- return ctx.makeFuncType();
+ return ctx.makeFuncType(share);
}
if (ctx.in.takeKeyword("any"sv)) {
- return ctx.makeAnyType();
+ return ctx.makeAnyType(share);
}
if (ctx.in.takeKeyword("extern"sv)) {
- return ctx.makeExternType();
+ return ctx.makeExternType(share);
}
if (ctx.in.takeKeyword("eq"sv)) {
- return ctx.makeEqType();
+ return ctx.makeEqType(share);
}
if (ctx.in.takeKeyword("i31"sv)) {
- return ctx.makeI31Type();
+ return ctx.makeI31Type(share);
}
if (ctx.in.takeKeyword("struct"sv)) {
- return ctx.makeStructType();
+ return ctx.makeStructType(share);
}
if (ctx.in.takeKeyword("array"sv)) {
- return ctx.makeArrayType();
+ return ctx.makeArrayType(share);
}
if (ctx.in.takeKeyword("exn"sv)) {
- return ctx.makeExnType();
+ return ctx.makeExnType(share);
}
if (ctx.in.takeKeyword("string"sv)) {
- return ctx.makeStringType();
+ return ctx.makeStringType(share);
}
if (ctx.in.takeKeyword("cont"sv)) {
- return ctx.makeContType();
+ return ctx.makeContType(share);
}
if (ctx.in.takeKeyword("none"sv)) {
- return ctx.makeNoneType();
+ return ctx.makeNoneType(share);
}
if (ctx.in.takeKeyword("noextern"sv)) {
- return ctx.makeNoextType();
+ return ctx.makeNoextType(share);
}
if (ctx.in.takeKeyword("nofunc"sv)) {
- return ctx.makeNofuncType();
+ return ctx.makeNofuncType(share);
}
if (ctx.in.takeKeyword("noexn"sv)) {
- return ctx.makeNoexnType();
+ return ctx.makeNoexnType(share);
}
if (ctx.in.takeKeyword("nocont"sv)) {
- return ctx.makeNocontType();
+ return ctx.makeNocontType(share);
}
- auto type = typeidx(ctx);
- CHECK_ERR(type);
- return *type;
+ return ctx.in.err("expected abstract heap type");
+}
+
+// heaptype ::= x:typeidx => types[x]
+// | t:absheaptype => unshared t
+// | '(' 'shared' t:absheaptype ')' => shared t
+template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
+ if (auto t = maybeTypeidx(ctx)) {
+ CHECK_ERR(t);
+ return *t;
+ }
+
+ auto share = ctx.in.takeSExprStart("shared"sv) ? Shared : Unshared;
+ auto t = absheaptype(ctx, share);
+ CHECK_ERR(t);
+ if (share == Shared && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of shared abstract heap type");
+ }
+ return *t;
}
// reftype ::= 'funcref' => funcref
@@ -422,49 +439,49 @@ template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
// | '(' ref null? t:heaptype ')' => ref null? t
template<typename Ctx> MaybeResult<typename Ctx::TypeT> maybeReftype(Ctx& ctx) {
if (ctx.in.takeKeyword("funcref"sv)) {
- return ctx.makeRefType(ctx.makeFuncType(), Nullable);
+ return ctx.makeRefType(ctx.makeFuncType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("externref"sv)) {
- return ctx.makeRefType(ctx.makeExternType(), Nullable);
+ return ctx.makeRefType(ctx.makeExternType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("anyref"sv)) {
- return ctx.makeRefType(ctx.makeAnyType(), Nullable);
+ return ctx.makeRefType(ctx.makeAnyType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("eqref"sv)) {
- return ctx.makeRefType(ctx.makeEqType(), Nullable);
+ return ctx.makeRefType(ctx.makeEqType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("i31ref"sv)) {
- return ctx.makeRefType(ctx.makeI31Type(), Nullable);
+ return ctx.makeRefType(ctx.makeI31Type(Unshared), Nullable);
}
if (ctx.in.takeKeyword("structref"sv)) {
- return ctx.makeRefType(ctx.makeStructType(), Nullable);
+ return ctx.makeRefType(ctx.makeStructType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("arrayref"sv)) {
- return ctx.makeRefType(ctx.makeArrayType(), Nullable);
+ return ctx.makeRefType(ctx.makeArrayType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("exnref"sv)) {
- return ctx.makeRefType(ctx.makeExnType(), Nullable);
+ return ctx.makeRefType(ctx.makeExnType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("stringref"sv)) {
- return ctx.makeRefType(ctx.makeStringType(), Nullable);
+ return ctx.makeRefType(ctx.makeStringType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("contref"sv)) {
- return ctx.makeRefType(ctx.makeContType(), Nullable);
+ return ctx.makeRefType(ctx.makeContType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("nullref"sv)) {
- return ctx.makeRefType(ctx.makeNoneType(), Nullable);
+ return ctx.makeRefType(ctx.makeNoneType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("nullexternref"sv)) {
- return ctx.makeRefType(ctx.makeNoextType(), Nullable);
+ return ctx.makeRefType(ctx.makeNoextType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("nullfuncref"sv)) {
- return ctx.makeRefType(ctx.makeNofuncType(), Nullable);
+ return ctx.makeRefType(ctx.makeNofuncType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("nullexnref"sv)) {
- return ctx.makeRefType(ctx.makeNoexnType(), Nullable);
+ return ctx.makeRefType(ctx.makeNoexnType(Unshared), Nullable);
}
if (ctx.in.takeKeyword("nullcontref"sv)) {
- return ctx.makeRefType(ctx.makeNocontType(), Nullable);
+ return ctx.makeRefType(ctx.makeNocontType(Unshared), Nullable);
}
if (!ctx.in.takeSExprStart("ref"sv)) {
diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp
index 81dd0a39e..720830400 100644
--- a/src/passes/TypeGeneralizing.cpp
+++ b/src/passes/TypeGeneralizing.cpp
@@ -58,7 +58,7 @@ using TypeRequirement = Inverted<ValType>;
// Record a type requirement for each local variable. Shared the requirements
// across basic blocks.
-using LocalTypeRequirements = Shared<Vector<TypeRequirement>>;
+using LocalTypeRequirements = SharedPath<Vector<TypeRequirement>>;
// The type requirements for each reference-typed value on the stack at a
// particular location.
@@ -75,7 +75,8 @@ struct State : StateLattice {
static constexpr int LocalsIndex = 0;
static constexpr int StackIndex = 1;
- State(Function* func) : StateLattice{Shared{initLocals(func)}, initStack()} {}
+ State(Function* func)
+ : StateLattice{SharedPath{initLocals(func)}, initStack()} {}
void push(Element& elem, Type type) const noexcept {
stackLattice().push(stack(elem), std::move(type));
@@ -109,7 +110,7 @@ struct State : StateLattice {
private:
static LocalTypeRequirements initLocals(Function* func) noexcept {
- return Shared{Vector{Inverted{ValType{}}, func->getNumLocals()}};
+ return SharedPath{Vector{Inverted{ValType{}}, func->getNumLocals()}};
}
static ValueStackTypeRequirements initStack() noexcept {
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 57c7ec6b0..6b54ac56d 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -2556,7 +2556,8 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
auto heapType = type.getHeapType();
assert(heapType.isBasic());
assert(wasm.features.hasReferenceTypes());
- switch (heapType.getBasic()) {
+ assert(!heapType.isShared() && "TODO: handle shared types");
+ switch (heapType.getBasic(Unshared)) {
case HeapType::ext: {
auto null = builder.makeRefNull(HeapType::ext);
// TODO: support actual non-nullable externrefs via imported globals or
@@ -4233,7 +4234,8 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
return type;
}
if (type.isBasic() && oneIn(2)) {
- switch (type.getBasic()) {
+ assert(!type.isShared() && "TODO: handle shared types");
+ switch (type.getBasic(Unshared)) {
case HeapType::func:
// TODO: Typed function references.
return pick(FeatureOptions<HeapType>()
diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp
index 08b4c4453..127d4f5ba 100644
--- a/src/tools/fuzzing/heap-types.cpp
+++ b/src/tools/fuzzing/heap-types.cpp
@@ -377,7 +377,8 @@ struct HeapTypeGeneratorImpl {
if (rand.oneIn(8)) {
return type.getBottom();
}
- switch (type.getBasic()) {
+ assert(!type.isShared() && "TODO: handle shared types");
+ switch (type.getBasic(Unshared)) {
case HeapType::func:
return pickSubFunc();
case HeapType::cont:
@@ -438,7 +439,8 @@ struct HeapTypeGeneratorImpl {
// This is not a constructed type, so it must be a basic type.
assert(type.isBasic());
candidates.push_back(type);
- switch (type.getBasic()) {
+ assert(!type.isShared() && "TODO: handle shared types");
+ switch (type.getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
case HeapType::exn:
diff --git a/src/tools/wasm-fuzz-lattices.cpp b/src/tools/wasm-fuzz-lattices.cpp
index d74bc5148..551ddbaf1 100644
--- a/src/tools/wasm-fuzz-lattices.cpp
+++ b/src/tools/wasm-fuzz-lattices.cpp
@@ -185,7 +185,7 @@ using LatticeVariant = std::variant<RandomFullLattice,
ArrayLattice,
Vector<RandomLattice>,
TupleLattice,
- Shared<RandomLattice>>;
+ SharedPath<RandomLattice>>;
struct RandomLattice::LatticeImpl : LatticeVariant {};
@@ -196,7 +196,7 @@ using LatticeElementVariant =
typename ArrayLattice::Element,
typename Vector<RandomLattice>::Element,
typename TupleLattice::Element,
- typename Shared<RandomLattice>::Element>;
+ typename SharedPath<RandomLattice>::Element>;
struct RandomLattice::ElementImpl : LatticeElementVariant {};
@@ -271,7 +271,7 @@ RandomLattice::RandomLattice(Random& rand, size_t depth) : rand(rand) {
return;
case FullLatticePicks + 5:
lattice = std::make_unique<LatticeImpl>(
- LatticeImpl{Shared{RandomLattice{rand, depth + 1}}});
+ LatticeImpl{SharedPath{RandomLattice{rand, depth + 1}}});
return;
}
WASM_UNREACHABLE("unexpected pick");
@@ -375,7 +375,7 @@ RandomLattice::Element RandomLattice::makeElement() const noexcept {
typename TupleLattice::Element{std::get<0>(l->lattices).makeElement(),
std::get<1>(l->lattices).makeElement()}};
}
- if (const auto* l = std::get_if<Shared<RandomLattice>>(lattice.get())) {
+ if (const auto* l = std::get_if<SharedPath<RandomLattice>>(lattice.get())) {
auto elem = l->getBottom();
l->join(elem, l->lattice.makeElement());
return ElementImpl{elem};
@@ -489,8 +489,9 @@ void printElement(std::ostream& os,
indent(os, depth);
os << ")\n";
} else if (const auto* e =
- std::get_if<typename Shared<RandomLattice>::Element>(&*elem)) {
- os << "Shared(\n";
+ std::get_if<typename SharedPath<RandomLattice>::Element>(
+ &*elem)) {
+ os << "SharedPath(\n";
printElement(os, **e, depth + 1);
indent(os, depth);
os << ")\n";
diff --git a/src/wasm-type.h b/src/wasm-type.h
index 2a74e4a60..95c3605a5 100644
--- a/src/wasm-type.h
+++ b/src/wasm-type.h
@@ -310,6 +310,8 @@ public:
const Type& operator[](size_t i) const { return *Iterator{{this, i}}; }
};
+enum Shareability { Shared, Unshared };
+
class HeapType {
// Unlike `Type`, which represents the types of values on the WebAssembly
// stack, `HeapType` is used to describe the structures that reference types
@@ -318,24 +320,26 @@ class HeapType {
uintptr_t id;
public:
+ // Bit zero indicates whether the type is `shared`, so we need to leave it
+ // free.
enum BasicHeapType : uint32_t {
- ext,
- func,
- cont,
- any,
- eq,
- i31,
- struct_,
- array,
- exn,
- string,
- none,
- noext,
- nofunc,
- nocont,
- noexn,
+ ext = 0 << 1,
+ func = 1 << 1,
+ cont = 2 << 1,
+ any = 3 << 1,
+ eq = 4 << 1,
+ i31 = 5 << 1,
+ struct_ = 6 << 1,
+ array = 7 << 1,
+ exn = 8 << 1,
+ string = 9 << 1,
+ none = 10 << 1,
+ noext = 11 << 1,
+ nofunc = 12 << 1,
+ nocont = 13 << 1,
+ noexn = 14 << 1,
};
- static constexpr BasicHeapType _last_basic_type = noexn;
+ static constexpr BasicHeapType _last_basic_type = BasicHeapType(noexn + 1);
// BasicHeapType can be implicitly upgraded to HeapType
constexpr HeapType(BasicHeapType id) : id(id) {}
@@ -377,7 +381,9 @@ public:
bool isString() const;
bool isBottom() const;
bool isOpen() const;
- bool isShared() const;
+ bool isShared() const { return getShared() == Shared; }
+
+ Shareability getShared() const;
Signature getSignature() const;
Continuation getContinuation() const;
@@ -399,19 +405,27 @@ public:
size_t getDepth() const;
// Get the bottom heap type for this heap type's hierarchy.
- BasicHeapType getBottom() const;
+ BasicHeapType getUnsharedBottom() const;
+ BasicHeapType getBottom() const {
+ return HeapType(getUnsharedBottom()).getBasic(getShared());
+ }
// Get the top heap type for this heap type's hierarchy.
- BasicHeapType getTop() const;
+ BasicHeapType getUnsharedTop() const;
+ BasicHeapType getTop() const {
+ return HeapType(getUnsharedTop()).getBasic(getShared());
+ }
// Get the recursion group for this non-basic type.
RecGroup getRecGroup() const;
size_t getRecGroupIndex() const;
constexpr TypeID getID() const { return id; }
- constexpr BasicHeapType getBasic() const {
- assert(isBasic() && "Basic heap type expected");
- return static_cast<BasicHeapType>(id);
+
+ // Get the shared or unshared version of this basic heap type.
+ constexpr BasicHeapType getBasic(Shareability share) const {
+ assert(isBasic());
+ return BasicHeapType(share == Shared ? (id | 1) : (id & ~1));
}
// (In)equality must be defined for both HeapType and BasicHeapType because it
@@ -644,7 +658,7 @@ struct TypeBuilder {
void createRecGroup(size_t i, size_t length);
void setOpen(size_t i, bool open = true);
- void setShared(size_t i, bool shared = true);
+ void setShared(size_t i, Shareability share = Shared);
enum class ErrorReason {
// There is a cycle in the supertype relation.
@@ -717,8 +731,8 @@ struct TypeBuilder {
builder.setOpen(index, open);
return *this;
}
- Entry& setShared(bool shared = true) {
- builder.setShared(index, shared);
+ Entry& setShared(Shareability share = Shared) {
+ builder.setShared(index, share);
return *this;
}
};
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index d4e1bfc45..f13ea504f 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -127,7 +127,7 @@ Literal::Literal(const Literal& other) : type(other.type) {
assert(!type.isNullable());
auto heapType = type.getHeapType();
if (heapType.isBasic()) {
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::i31:
i32 = other.i32;
return;
@@ -620,8 +620,11 @@ std::ostream& operator<<(std::ostream& o, Literal literal) {
} else {
assert(literal.type.isRef());
auto heapType = literal.type.getHeapType();
+ if (heapType.isShared()) {
+ o << "shared ";
+ }
if (heapType.isBasic()) {
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::i31:
o << "i31ref(" << literal.geti31() << ")";
break;
@@ -2686,11 +2689,12 @@ Literal Literal::externalize() const {
return Literal(std::shared_ptr<GCData>{}, HeapType::noext);
}
auto heapType = type.getHeapType();
+ auto extType = HeapTypes::ext.getBasic(heapType.getShared());
if (heapType.isBasic()) {
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::i31: {
return Literal(std::make_shared<GCData>(HeapType::i31, Literals{*this}),
- HeapType::ext);
+ extType);
}
case HeapType::string:
WASM_UNREACHABLE("TODO: string literals");
@@ -2698,7 +2702,7 @@ Literal Literal::externalize() const {
WASM_UNREACHABLE("unexpected type");
}
}
- return Literal(gcData, HeapType::ext);
+ return Literal(gcData, extType);
}
Literal Literal::internalize() const {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index b05dd5237..524f4b98c 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1538,8 +1538,8 @@ void WasmBinaryWriter::writeType(Type type) {
WASM_UNREACHABLE("bad type without GC");
}
auto heapType = type.getHeapType();
- if (heapType.isBasic() && type.isNullable()) {
- switch (heapType.getBasic()) {
+ if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::ext:
o << S32LEB(BinaryConsts::EncodedType::externref);
return;
@@ -1644,14 +1644,16 @@ void WasmBinaryWriter::writeHeapType(HeapType type) {
}
}
- if (type.isSignature() || type.isContinuation() || type.isStruct() ||
- type.isArray()) {
+ if (!type.isBasic()) {
o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
return;
}
+
int ret = 0;
- assert(type.isBasic());
- switch (type.getBasic()) {
+ if (type.isShared()) {
+ o << S32LEB(BinaryConsts::EncodedType::Shared);
+ }
+ switch (type.getBasic(Unshared)) {
case HeapType::ext:
ret = BinaryConsts::EncodedHeapType::ext;
break;
@@ -2083,7 +2085,7 @@ bool WasmBinaryReader::getBasicHeapType(int64_t code, HeapType& out) {
out = HeapType::func;
return true;
case BinaryConsts::EncodedHeapType::cont:
- out = HeapType::func;
+ out = HeapType::cont;
return true;
case BinaryConsts::EncodedHeapType::ext:
out = HeapType::ext;
@@ -2168,9 +2170,14 @@ HeapType WasmBinaryReader::getHeapType() {
}
return types[type];
}
+ auto share = Unshared;
+ if (type == BinaryConsts::EncodedType::Shared) {
+ share = Shared;
+ type = getS64LEB(); // TODO: Actually s33
+ }
HeapType ht;
if (getBasicHeapType(type, ht)) {
- return ht;
+ return ht.getBasic(share);
} else {
throwError("invalid wasm heap type: " + std::to_string(type));
}
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 532ebb913..ae6fd4b30 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -86,7 +86,7 @@ struct HeapTypeInfo {
// global store.
bool isTemp = false;
bool isOpen = false;
- bool isShared = false;
+ Shareability share = Unshared;
// The supertype of this HeapType, if it exists.
HeapTypeInfo* supertype = nullptr;
// The recursion group of this type or null if the recursion group is trivial
@@ -436,18 +436,18 @@ bool isTemp(HeapType type) {
HeapType::BasicHeapType getBasicHeapSupertype(HeapType type) {
if (type.isBasic()) {
- return type.getBasic();
+ return HeapType::BasicHeapType(type.getID());
}
auto* info = getHeapTypeInfo(type);
switch (info->kind) {
case HeapTypeInfo::SignatureKind:
- return HeapType::func;
+ return HeapTypes::func.getBasic(info->share);
case HeapTypeInfo::ContinuationKind:
- return HeapType::cont;
+ return HeapTypes::cont.getBasic(info->share);
case HeapTypeInfo::StructKind:
- return HeapType::struct_;
+ return HeapTypes::struct_.getBasic(info->share);
case HeapTypeInfo::ArrayKind:
- return HeapType::array;
+ return HeapTypes::array.getBasic(info->share);
}
WASM_UNREACHABLE("unexpected kind");
};
@@ -470,42 +470,53 @@ std::optional<HeapType> getBasicHeapTypeLUB(HeapType::BasicHeapType a,
if (unsigned(a) > unsigned(b)) {
std::swap(a, b);
}
- switch (a) {
+ auto bUnshared = HeapType(b).getBasic(Unshared);
+ HeapType lubUnshared;
+ switch (HeapType(a).getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
case HeapType::cont:
case HeapType::exn:
return std::nullopt;
case HeapType::any:
- return {HeapType::any};
+ lubUnshared = HeapType::any;
+ break;
case HeapType::eq:
- if (b == HeapType::i31 || b == HeapType::struct_ ||
- b == HeapType::array) {
- return {HeapType::eq};
+ if (bUnshared == HeapType::i31 || bUnshared == HeapType::struct_ ||
+ bUnshared == HeapType::array) {
+ lubUnshared = HeapType::eq;
+ } else {
+ lubUnshared = HeapType::any;
}
- return {HeapType::any};
+ break;
case HeapType::i31:
- if (b == HeapType::struct_ || b == HeapType::array) {
- return {HeapType::eq};
+ if (bUnshared == HeapType::struct_ || bUnshared == HeapType::array) {
+ lubUnshared = HeapType::eq;
+ } else {
+ lubUnshared = HeapType::any;
}
- return {HeapType::any};
+ break;
case HeapType::struct_:
- if (b == HeapType::array) {
- return {HeapType::eq};
+ if (bUnshared == HeapType::array) {
+ lubUnshared = HeapType::eq;
+ } else {
+ lubUnshared = HeapType::any;
}
- return {HeapType::any};
+ break;
case HeapType::array:
case HeapType::string:
- return {HeapType::any};
+ lubUnshared = HeapType::any;
+ break;
case HeapType::none:
case HeapType::noext:
case HeapType::nofunc:
case HeapType::nocont:
case HeapType::noexn:
// Bottom types already handled.
- break;
+ WASM_UNREACHABLE("unexpected basic type");
}
- WASM_UNREACHABLE("unexpected basic type");
+ auto share = HeapType(a).getShared();
+ return {lubUnshared.getBasic(share)};
}
TypeInfo::TypeInfo(const TypeInfo& other) {
@@ -898,7 +909,7 @@ FeatureSet Type::getFeatures() const {
void noteChild(HeapType* heapType) {
if (heapType->isBasic()) {
- switch (heapType->getBasic()) {
+ switch (heapType->getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
feats |= FeatureSet::ReferenceTypes;
@@ -1228,7 +1239,7 @@ bool HeapType::isString() const { return *this == HeapType::string; }
bool HeapType::isBottom() const {
if (isBasic()) {
- switch (getBasic()) {
+ switch (getBasic(Unshared)) {
case ext:
case func:
case cont:
@@ -1259,12 +1270,11 @@ bool HeapType::isOpen() const {
}
}
-bool HeapType::isShared() const {
+Shareability HeapType::getShared() const {
if (isBasic()) {
- // TODO: shared basic heap types
- return false;
+ return (id & 1) != 0 ? Shared : Unshared;
} else {
- return getHeapTypeInfo(*this)->isShared;
+ return getHeapTypeInfo(*this)->share;
}
}
@@ -1305,9 +1315,11 @@ std::optional<HeapType> HeapType::getSuperType() const {
return ret;
}
+ auto share = getShared();
+
// There may be a basic supertype.
if (isBasic()) {
- switch (getBasic()) {
+ switch (getBasic(Unshared)) {
case ext:
case noext:
case func:
@@ -1321,24 +1333,24 @@ std::optional<HeapType> HeapType::getSuperType() const {
case string:
return {};
case eq:
- return any;
+ return HeapType(any).getBasic(share);
case i31:
case struct_:
case array:
- return eq;
+ return HeapType(eq).getBasic(share);
}
}
auto* info = getHeapTypeInfo(*this);
switch (info->kind) {
case HeapTypeInfo::SignatureKind:
- return func;
+ return HeapType(func).getBasic(share);
case HeapTypeInfo::ContinuationKind:
- return cont;
+ return HeapType(cont).getBasic(share);
case HeapTypeInfo::StructKind:
- return struct_;
+ return HeapType(struct_).getBasic(share);
case HeapTypeInfo::ArrayKind:
- return array;
+ return HeapType(array).getBasic(share);
}
WASM_UNREACHABLE("unexpected kind");
}
@@ -1365,7 +1377,7 @@ size_t HeapType::getDepth() const {
}
} else {
// Some basic types have supers.
- switch (getBasic()) {
+ switch (getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
case HeapType::cont:
@@ -1393,9 +1405,9 @@ size_t HeapType::getDepth() const {
return depth;
}
-HeapType::BasicHeapType HeapType::getBottom() const {
+HeapType::BasicHeapType HeapType::getUnsharedBottom() const {
if (isBasic()) {
- switch (getBasic()) {
+ switch (getBasic(Unshared)) {
case ext:
return noext;
case func:
@@ -1435,8 +1447,8 @@ HeapType::BasicHeapType HeapType::getBottom() const {
WASM_UNREACHABLE("unexpected kind");
}
-HeapType::BasicHeapType HeapType::getTop() const {
- switch (getBottom()) {
+HeapType::BasicHeapType HeapType::getUnsharedTop() const {
+ switch (getUnsharedBottom()) {
case none:
return any;
case nofunc:
@@ -1730,30 +1742,34 @@ bool SubTyper::isSubType(HeapType a, HeapType b) {
if (a == b) {
return true;
}
+ if (a.isShared() != b.isShared()) {
+ return false;
+ }
if (b.isBasic()) {
- switch (b.getBasic()) {
+ auto aTop = a.getUnsharedTop();
+ auto aUnshared = a.isBasic() ? a.getBasic(Unshared) : a;
+ switch (b.getBasic(Unshared)) {
case HeapType::ext:
- return a.getTop() == HeapType::ext;
+ return aTop == HeapType::ext;
case HeapType::func:
- return a.getTop() == HeapType::func;
+ return aTop == HeapType::func;
case HeapType::cont:
- return a.getTop() == HeapType::cont;
+ return aTop == HeapType::cont;
case HeapType::exn:
- return a.getTop() == HeapType::exn;
+ return aTop == HeapType::exn;
case HeapType::any:
- return a.getTop() == HeapType::any;
+ return aTop == HeapType::any;
case HeapType::eq:
- return a == HeapType::i31 || a == HeapType::none ||
- a == HeapType::struct_ || a == HeapType::array || a.isStruct() ||
- a.isArray();
+ return aUnshared == HeapType::i31 || aUnshared == HeapType::none ||
+ aUnshared == HeapType::struct_ || aUnshared == HeapType::array ||
+ a.isStruct() || a.isArray();
case HeapType::i31:
- return a == HeapType::none;
+ case HeapType::string:
+ return aUnshared == HeapType::none;
case HeapType::struct_:
- return a == HeapType::none || a.isStruct();
+ return aUnshared == HeapType::none || a.isStruct();
case HeapType::array:
- return a == HeapType::none || a.isArray();
- case HeapType::string:
- return a == HeapType::none;
+ return aUnshared == HeapType::none || a.isArray();
case HeapType::none:
case HeapType::noext:
case HeapType::nofunc:
@@ -1865,9 +1881,9 @@ std::ostream& TypePrinter::print(Type type) {
print(type.getTuple());
} else if (type.isRef()) {
auto heapType = type.getHeapType();
- if (heapType.isBasic() && type.isNullable()) {
+ if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) {
// Print shorthands for certain basic heap types.
- switch (heapType.getBasic()) {
+ switch (heapType.getBasic(Unshared)) {
case HeapType::ext:
return os << "externref";
case HeapType::func:
@@ -1914,38 +1930,60 @@ std::ostream& TypePrinter::print(Type type) {
std::ostream& TypePrinter::print(HeapType type) {
if (type.isBasic()) {
- switch (type.getBasic()) {
+ if (type.isShared()) {
+ os << "(shared ";
+ }
+ switch (type.getBasic(Unshared)) {
case HeapType::ext:
- return os << "extern";
+ os << "extern";
+ break;
case HeapType::func:
- return os << "func";
+ os << "func";
+ break;
case HeapType::cont:
- return os << "cont";
+ os << "cont";
+ break;
case HeapType::any:
- return os << "any";
+ os << "any";
+ break;
case HeapType::eq:
- return os << "eq";
+ os << "eq";
+ break;
case HeapType::i31:
- return os << "i31";
+ os << "i31";
+ break;
case HeapType::struct_:
- return os << "struct";
+ os << "struct";
+ break;
case HeapType::array:
- return os << "array";
+ os << "array";
+ break;
case HeapType::exn:
- return os << "exn";
+ os << "exn";
+ break;
case HeapType::string:
- return os << "string";
+ os << "string";
+ break;
case HeapType::none:
- return os << "none";
+ os << "none";
+ break;
case HeapType::noext:
- return os << "noextern";
+ os << "noextern";
+ break;
case HeapType::nofunc:
- return os << "nofunc";
+ os << "nofunc";
+ break;
case HeapType::nocont:
- return os << "nocont";
+ os << "nocont";
+ break;
case HeapType::noexn:
- return os << "noexn";
+ os << "noexn";
+ break;
+ }
+ if (type.isShared()) {
+ os << ')';
}
+ return os;
}
auto names = generator(type);
@@ -2144,7 +2182,7 @@ size_t RecGroupHasher::hash(const HeapTypeInfo& info) const {
hash_combine(digest, hash(HeapType(uintptr_t(info.supertype))));
}
wasm::rehash(digest, info.isOpen);
- wasm::rehash(digest, info.isShared);
+ wasm::rehash(digest, info.share);
wasm::rehash(digest, info.kind);
switch (info.kind) {
case HeapTypeInfo::SignatureKind:
@@ -2281,7 +2319,7 @@ bool RecGroupEquator::eq(const HeapTypeInfo& a, const HeapTypeInfo& b) const {
if (a.isOpen != b.isOpen) {
return false;
}
- if (a.isShared != b.isShared) {
+ if (a.share != b.share) {
return false;
}
if (a.kind != b.kind) {
@@ -2559,9 +2597,9 @@ void TypeBuilder::setOpen(size_t i, bool open) {
impl->entries[i].info->isOpen = open;
}
-void TypeBuilder::setShared(size_t i, bool shared) {
+void TypeBuilder::setShared(size_t i, Shareability share) {
assert(i < size() && "index out of bounds");
- impl->entries[i].info->isShared = shared;
+ impl->entries[i].info->share = share;
}
namespace {
@@ -2570,7 +2608,7 @@ bool isValidSupertype(const HeapTypeInfo& sub, const HeapTypeInfo& super) {
if (!super.isOpen) {
return false;
}
- if (sub.isShared != super.isShared) {
+ if (sub.share != super.share) {
return false;
}
if (sub.kind != super.kind) {