Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,21 @@ template<typename Info> struct Store {
// Maps from constructed types to their canonical Type IDs.
std::unordered_map<Info, uintptr_t> typeIDs;

TypeID recordCanonical(std::unique_ptr<Info>&& info) {
TypeID id = uintptr_t(info.get());
assert(id > Info::type_t::_last_basic_type);
typeIDs[*info] = id;
constructedTypes.emplace_back(std::move(info));
return id;
}

typename Info::type_t canonicalize(const Info& info) {
std::lock_guard<std::mutex> lock(mutex);
auto indexIt = typeIDs.find(info);
if (indexIt != typeIDs.end()) {
return typename Info::type_t(indexIt->second);
}
auto ptr = std::make_unique<Info>(info);
auto id = uintptr_t(ptr.get());
constructedTypes.push_back(std::move(ptr));
assert(id > Info::type_t::_last_basic_type);
typeIDs[info] = id;
return typename Info::type_t(id);
return typename Info::type_t(recordCanonical(std::make_unique<Info>(info)));
}
};

Expand Down Expand Up @@ -1360,10 +1363,6 @@ struct Canonicalizer {
void canonicalize(T* type, std::unordered_map<T, T>& canonicals);
};

// Used to extend the lifetime of self-referential HeapTypes so they don't need
// to be canonicalized.
std::vector<std::unique_ptr<HeapTypeInfo>> noncanonicalHeapTypes;

// Traverse the type graph rooted at the initialized HeapTypeInfos in reverse
// postorder, replacing in place all Types and HeapTypes backed by the
// TypeBuilder's Stores with equivalent globally canonicalized Types and
Expand Down Expand Up @@ -1413,11 +1412,12 @@ Canonicalizer::Canonicalizer(TypeBuilder& builder) : builder(builder) {
}

// Now that all other Types and HeapTypes have been canonicalized, move
// self-referential HeapTypes to the global store so that they will outlive
// the TypeBuilder without their IDs changing.
// self-referential HeapTypes to the global store so that they will be
// considered canonical and outlive the TypeBuilder.
std::lock_guard<std::mutex> lock(globalHeapTypeStore.mutex);
for (auto& entry : builder.impl->entries) {
if (selfReferentialHeapTypes.count(entry.get())) {
noncanonicalHeapTypes.emplace_back(std::move(entry.info));
globalHeapTypeStore.recordCanonical(std::move(entry.info));
}
}
}
Expand Down
8 changes: 0 additions & 8 deletions test/lit/recursive-types.wast
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@

;; RUN: wasm-opt %s -all -S -o - | filecheck %s

;; TODO: Fix the bug where self-referential function signatures are emitted
;; twice. This happens because collectHeapTypes has to reconstruct a HeapType
;; from each function's signature, but self-referential HeapTypes aren't
;; canonicalized when they are parsed so collectHeapTypes ends up with a
;; separate, unfolded version of the type.

;; TODO: Fix the bug where structurally identical types are given the same
;; generated name, making the wast invalid due to duplicate names.

;; CHECK: (module
;; CHECK-NEXT: (type $ref?|...0|_=>_ref?|...0| (func (param (ref null $ref?|...0|_=>_ref?|...0|)) (result (ref null $ref?|...0|_=>_ref?|...0|))))
;; CHECK-NEXT: (type $ref?|...0|_=>_ref?|...0| (func (param (ref null $ref?|...0|_=>_ref?|...0|)) (result (ref null $ref?|...0|_=>_ref?|...0|))))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, this output looks like it emits two types with the exact same name, which should be invalid - that is, I think this file would error on round-tripping through the text format?

(I guess #3627 could fix that?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, correct on both counts 👍

;; CHECK-NEXT: (type $ref?|ref?|...0|_->_ref?|...0||_=>_ref?|ref?|...0|_->_ref?|...0|| (func (param (ref null $ref?|...0|_=>_ref?|...0|)) (result (ref null $ref?|...0|_=>_ref?|...0|))))
;; CHECK-NEXT: (type $ref?|ref?|...0|_->_ref?|...0||_=>_ref?|ref?|...0|_->_ref?|...0|| (func (param (ref null $ref?|...0|_=>_ref?|...0|)) (result (ref null $ref?|...0|_=>_ref?|...0|))))
;; CHECK-NEXT: (func $foo (param $0 (ref null $ref?|...0|_=>_ref?|...0|)) (result (ref null $ref?|...0|_=>_ref?|...0|))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
Expand Down