summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wasm-type.cpp80
-rw-r--r--test/gtest/type-builder.cpp29
2 files changed, 94 insertions, 15 deletions
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index b0b218189..d05af96a7 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -883,6 +883,18 @@ struct RecGroupStore {
return canonical;
}
+ // Utility for canonicalizing HeapTypes with trivial recursion groups.
+ HeapType insert(std::unique_ptr<HeapTypeInfo>&& info) {
+ std::lock_guard<std::mutex> lock(mutex);
+ assert(!info->recGroup && "Unexpected nontrivial rec group");
+ auto group = asHeapType(info).getRecGroup();
+ auto canonical = insert(group);
+ if (group == canonical) {
+ globalHeapTypeStore.insert(std::move(info));
+ }
+ return canonical[0];
+ }
+
void clear() {
canonicalGroups.clear();
builtGroups.clear();
@@ -1232,19 +1244,27 @@ const Type& Type::Iterator::operator*() const {
HeapType::HeapType(Signature sig) {
assert(!isTemp(sig.params) && "Leaking temporary type!");
assert(!isTemp(sig.results) && "Leaking temporary type!");
- if (typeSystem == TypeSystem::Nominal) {
- // Special case the creation of signature types in nominal mode to return a
- // "canonical" type for the signature, which happens to be the first one
- // created. We depend on being able to create new function signatures in
- // many places, and historically they have always been structural, so
- // creating a copy of an existing signature did not result in any code bloat
- // or semantic changes. To avoid regressions or significant changes of
- // behavior in nominal mode, we cache the canonical heap types for each
- // signature to emulate structural behavior.
- new (this) HeapType(nominalSignatureCache.getType(sig));
- } else {
- new (this) HeapType(globalHeapTypeStore.insert(sig));
+ switch (getTypeSystem()) {
+ case TypeSystem::Nominal:
+ // Special case the creation of signature types in nominal mode to return
+ // a "canonical" type for the signature, which happens to be the first one
+ // created. We depend on being able to create new function signatures in
+ // many places, and historically they have always been structural, so
+ // creating a copy of an existing signature did not result in any code
+ // bloat or semantic changes. To avoid regressions or significant changes
+ // of behavior in nominal mode, we cache the canonical heap types for each
+ // signature to emulate structural behavior.
+ new (this) HeapType(nominalSignatureCache.getType(sig));
+ return;
+ case TypeSystem::Equirecursive:
+ new (this) HeapType(globalHeapTypeStore.insert(sig));
+ return;
+ case TypeSystem::Isorecursive:
+ new (this) HeapType(
+ globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(sig)));
+ return;
}
+ WASM_UNREACHABLE("unexpected type system");
}
HeapType::HeapType(const Struct& struct_) {
@@ -1253,7 +1273,17 @@ HeapType::HeapType(const Struct& struct_) {
assert(!isTemp(field.type) && "Leaking temporary type!");
}
#endif
- new (this) HeapType(globalHeapTypeStore.insert(struct_));
+ switch (getTypeSystem()) {
+ case TypeSystem::Nominal:
+ case TypeSystem::Equirecursive:
+ new (this) HeapType(globalHeapTypeStore.insert(struct_));
+ return;
+ case TypeSystem::Isorecursive:
+ new (this) HeapType(
+ globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(struct_)));
+ return;
+ }
+ WASM_UNREACHABLE("unexpected type system");
}
HeapType::HeapType(Struct&& struct_) {
@@ -1262,12 +1292,32 @@ HeapType::HeapType(Struct&& struct_) {
assert(!isTemp(field.type) && "Leaking temporary type!");
}
#endif
- new (this) HeapType(globalHeapTypeStore.insert(std::move(struct_)));
+ switch (getTypeSystem()) {
+ case TypeSystem::Nominal:
+ case TypeSystem::Equirecursive:
+ new (this) HeapType(globalHeapTypeStore.insert(std::move(struct_)));
+ return;
+ case TypeSystem::Isorecursive:
+ new (this) HeapType(globalRecGroupStore.insert(
+ std::make_unique<HeapTypeInfo>(std::move(struct_))));
+ return;
+ }
+ WASM_UNREACHABLE("unexpected type system");
}
HeapType::HeapType(Array array) {
assert(!isTemp(array.element.type) && "Leaking temporary type!");
- new (this) HeapType(globalHeapTypeStore.insert(array));
+ switch (getTypeSystem()) {
+ case TypeSystem::Nominal:
+ case TypeSystem::Equirecursive:
+ new (this) HeapType(globalHeapTypeStore.insert(array));
+ return;
+ case TypeSystem::Isorecursive:
+ new (this) HeapType(
+ globalRecGroupStore.insert(std::make_unique<HeapTypeInfo>(array)));
+ return;
+ }
+ WASM_UNREACHABLE("unexpected type system");
}
bool HeapType::isFunction() const {
diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp
index 8f922380d..b6ed40262 100644
--- a/test/gtest/type-builder.cpp
+++ b/test/gtest/type-builder.cpp
@@ -402,3 +402,32 @@ TEST_F(IsorecursiveTest, CanonicalizeSupertypes) {
EXPECT_NE(built[3], built[5]);
EXPECT_NE(built[4], built[5]);
}
+
+TEST_F(IsorecursiveTest, HeapTypeConstructors) {
+ HeapType sig(Signature(Type::i32, Type::i32));
+ HeapType struct_(Struct({Field(Type(sig, Nullable), Mutable)}));
+ HeapType array(Field(Type(struct_, Nullable), Mutable));
+
+ TypeBuilder builder(3);
+ builder[0] = Signature(Type::i32, Type::i32);
+ Type sigRef = builder.getTempRefType(builder[0], Nullable);
+ builder[1] = Struct({Field(sigRef, Mutable)});
+ Type structRef = builder.getTempRefType(builder[1], Nullable);
+ builder[2] = Array(Field(structRef, Mutable));
+
+ auto result = builder.build();
+ ASSERT_TRUE(result);
+ auto built = *result;
+
+ EXPECT_EQ(built[0], sig);
+ EXPECT_EQ(built[1], struct_);
+ EXPECT_EQ(built[2], array);
+
+ HeapType sig2(Signature(Type::i32, Type::i32));
+ HeapType struct2(Struct({Field(Type(sig, Nullable), Mutable)}));
+ HeapType array2(Field(Type(struct_, Nullable), Mutable));
+
+ EXPECT_EQ(sig, sig2);
+ EXPECT_EQ(struct_, struct2);
+ EXPECT_EQ(array, array2);
+}