Skip to content

Commit

Permalink
Support type use before definition in binaries (#3588)
Browse files Browse the repository at this point in the history
Update parsing of binary type sections to use TypeBuilder to support uses before
definitions. Now that both the binary and text parsers support out-of-order type
uses, this PR also relaxes the logic for emitting types to allow uses to be
emitted before definitions.
  • Loading branch information
tlively authored Feb 19, 2021
1 parent e24c1f0 commit 611e6de
Show file tree
Hide file tree
Showing 13 changed files with 216 additions and 167 deletions.
54 changes: 5 additions & 49 deletions src/ir/module-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,7 @@ inline void collectHeapTypes(Module& wasm,
std::unordered_map<HeapType, Index>& typeIndices) {
struct Counts : public std::unordered_map<HeapType, size_t> {
bool isRelevant(Type type) {
if (type.isRef()) {
return !type.getHeapType().isBasic();
}
return type.isRtt();
return (type.isRef() || type.isRtt()) && !type.getHeapType().isBasic();
}
void note(HeapType type) { (*this)[type]++; }
void maybeNote(Type type) {
Expand Down Expand Up @@ -469,10 +466,11 @@ inline void collectHeapTypes(Module& wasm,
} else if (auto* set = curr->dynCast<StructSet>()) {
counts.maybeNote(set->ref->type);
} else if (Properties::isControlFlowStructure(curr)) {
counts.maybeNote(curr->type);
if (curr->type.isTuple()) {
// TODO: Allow control flow to have input types as well
counts.note(Signature(Type::none, curr->type));
} else {
counts.maybeNote(curr->type);
}
}
}
Expand Down Expand Up @@ -509,8 +507,7 @@ inline void collectHeapTypes(Module& wasm,
}
// A generic utility to traverse the child types of a type.
// TODO: work with tlively to refactor this to a shared place
auto walkRelevantChildren = [&](HeapType type,
std::function<void(HeapType)> callback) {
auto walkRelevantChildren = [&](HeapType type, auto callback) {
auto callIfRelevant = [&](Type type) {
if (counts.isRelevant(type)) {
callback(type.getHeapType());
Expand Down Expand Up @@ -538,7 +535,6 @@ inline void collectHeapTypes(Module& wasm,
// As we do this we may find more and more types, as nested children of
// previous ones. Each such type will appear in the type section once, so
// we just need to visit it once.
// TODO: handle struct and array fields
std::unordered_set<HeapType> newTypes;
for (auto& pair : counts) {
newTypes.insert(pair.first);
Expand All @@ -555,49 +551,9 @@ inline void collectHeapTypes(Module& wasm,
});
}

// We must sort all the dependencies of a type before it. For example,
// (func (param (ref (func)))) must appear after (func). To do that, find the
// depth of dependencies of each type. For example, if A depends on B
// which depends on C, then A's depth is 2, B's is 1, and C's is 0 (assuming
// no other dependencies).
Counts depthOfDependencies;
std::unordered_map<HeapType, std::unordered_set<HeapType>> isDependencyOf;
// To calculate the depth of dependencies, we'll do a flow analysis, visiting
// each type as we find out new things about it.
std::set<HeapType> toVisit;
for (auto& pair : counts) {
auto type = pair.first;
depthOfDependencies[type] = 0;
toVisit.insert(type);
walkRelevantChildren(type, [&](HeapType childType) {
isDependencyOf[childType].insert(type); // XXX flip?
});
}
while (!toVisit.empty()) {
auto iter = toVisit.begin();
auto type = *iter;
toVisit.erase(iter);
// Anything that depends on this has a depth of dependencies equal to this
// type's, plus this type itself.
auto newDepth = depthOfDependencies[type] + 1;
if (newDepth > counts.size()) {
Fatal() << "Cyclic types detected, cannot sort them.";
}
for (auto& other : isDependencyOf[type]) {
if (depthOfDependencies[other] < newDepth) {
// We found something new to propagate.
depthOfDependencies[other] = newDepth;
toVisit.insert(other);
}
}
}
// Sort by frequency and then simplicity, and also keeping every type
// before things that depend on it.
// Sort by frequency and then simplicity.
std::vector<std::pair<HeapType, size_t>> sorted(counts.begin(), counts.end());
std::sort(sorted.begin(), sorted.end(), [&](auto a, auto b) {
if (depthOfDependencies[a.first] != depthOfDependencies[b.first]) {
return depthOfDependencies[a.first] < depthOfDependencies[b.first];
}
if (a.second != b.second) {
return a.second > b.second;
}
Expand Down
4 changes: 2 additions & 2 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1322,14 +1322,14 @@ class WasmBinaryBuilder {
int64_t getS64LEB();
uint64_t getUPtrLEB();

bool getBasicType(int32_t code, Type& out);
bool getBasicHeapType(int64_t code, HeapType& out);
// Read a value and get a type for it.
Type getType();
// Get a type given the initial S32LEB has already been read, and is provided.
Type getType(int initial);

HeapType getHeapType();
Mutability getMutability();
Field getField();
Type getConcreteType();
Name getInlineString();
void verifyInt8(int8_t x);
Expand Down
3 changes: 3 additions & 0 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ class HeapType {
// But converting raw TypeID is more dangerous, so make it explicit
explicit HeapType(TypeID id) : id(id) {}

// Choose an arbitrary heap type as the default.
constexpr HeapType() : HeapType(func) {}

HeapType(Signature signature);
HeapType(const Struct& struct_);
HeapType(Struct&& struct_);
Expand Down
Loading

0 comments on commit 611e6de

Please sign in to comment.