Skip to content

Commit dee59f2

Browse files
committed
Replace the old topological sort everywhere
To avoid having two separate topological sort utilities in the code base, replace remaining uses of the old DFS-based, CRTP topological sort with the newer Kahn's algorithm implementation. This would be NFC, except that the new topological sort produces a different order than the old topological sort, so the output of some passes is reordered.
1 parent 7c11d25 commit dee59f2

27 files changed

+1205
-1407
lines changed

Diff for: src/ir/subtypes.h

+13-23
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#define wasm_ir_subtypes_h
1919

2020
#include "ir/module-utils.h"
21-
#include "support/topological_sort.h"
21+
#include "support/topological_orders.h"
2222
#include "wasm.h"
2323

2424
namespace wasm {
@@ -79,29 +79,19 @@ struct SubTypes {
7979
}
8080

8181
// A topological sort that visits subtypes first.
82-
auto getSubTypesFirstSort() const {
83-
struct SubTypesFirstSort : TopologicalSort<HeapType, SubTypesFirstSort> {
84-
const SubTypes& parent;
85-
86-
SubTypesFirstSort(const SubTypes& parent) : parent(parent) {
87-
for (auto type : parent.types) {
88-
// The roots are types with no supertype.
89-
if (!type.getDeclaredSuperType()) {
90-
push(type);
91-
}
92-
}
93-
}
94-
95-
void pushPredecessors(HeapType type) {
96-
// Things we need to process before each type are its subtypes. Once we
97-
// know their depth, we can easily compute our own.
98-
for (auto pred : parent.getImmediateSubTypes(type)) {
99-
push(pred);
100-
}
82+
std::vector<HeapType> getSubTypesFirstSort() const {
83+
std::vector<std::pair<HeapType, std::vector<HeapType>>> graph;
84+
graph.reserve(types.size());
85+
for (auto type : types) {
86+
if (auto it = typeSubTypes.find(type); it != typeSubTypes.end()) {
87+
graph.emplace_back(*it);
88+
} else {
89+
graph.emplace_back(type, std::vector<HeapType>{});
10190
}
102-
};
103-
104-
return SubTypesFirstSort(*this);
91+
}
92+
auto sorted = TopologicalSort::sortOf(graph.begin(), graph.end());
93+
std::reverse(sorted.begin(), sorted.end());
94+
return sorted;
10595
}
10696

10797
// Computes the depth of children for each type. This is 0 if the type has no

Diff for: src/ir/type-updating.cpp

+4-11
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "ir/module-utils.h"
2121
#include "ir/names.h"
2222
#include "ir/utils.h"
23-
#include "support/topological_sort.h"
2423
#include "wasm-type-ordering.h"
2524
#include "wasm-type.h"
2625
#include "wasm.h"
@@ -59,18 +58,12 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
5958

6059
// Topological sort to have supertypes first, but we have to account for the
6160
// fact that we may be replacing the supertypes to get the order correct.
62-
struct SupertypesFirst
63-
: HeapTypeOrdering::SupertypesFirstBase<SupertypesFirst> {
64-
GlobalTypeRewriter& parent;
65-
66-
SupertypesFirst(GlobalTypeRewriter& parent) : parent(parent) {}
67-
std::optional<HeapType> getDeclaredSuperType(HeapType type) {
68-
return parent.getDeclaredSuperType(type);
69-
}
61+
auto supertypesFirst = [this](const auto& types) {
62+
return HeapTypeOrdering::supertypesFirst(
63+
types, [this](HeapType type) { return getDeclaredSuperType(type); });
7064
};
7165

72-
SupertypesFirst sortedTypes(*this);
73-
for (auto type : sortedTypes.sort(privateTypes)) {
66+
for (auto type : supertypesFirst(privateTypes)) {
7467
typeIndices[type] = i++;
7568
}
7669

Diff for: src/passes/GlobalTypeOptimization.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ struct GlobalTypeOptimization : public Pass {
171171
// fields in a supertype is a constraint on what subtypes can do. That is,
172172
// we decide for each supertype what the optimal order is, and consider that
173173
// fixed, and then subtypes can decide how to sort fields that they append.
174-
HeapTypeOrdering::SupertypesFirst sorted;
175-
for (auto type : sorted.sort(propagator.subTypes.types)) {
174+
for (auto type :
175+
HeapTypeOrdering::supertypesFirst(propagator.subTypes.types)) {
176176
if (!type.isStruct()) {
177177
continue;
178178
}

Diff for: src/passes/TypeMerging.cpp

+14-19
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ struct TypeMerging : public Pass {
142142
return type;
143143
}
144144

145+
std::vector<HeapType>
146+
mergeableSupertypesFirst(const std::vector<HeapType>& types) {
147+
return HeapTypeOrdering::supertypesFirst(
148+
types, [&](HeapType type) -> std::optional<HeapType> {
149+
if (auto super = type.getDeclaredSuperType()) {
150+
return getMerged(*super);
151+
}
152+
return std::nullopt;
153+
});
154+
}
155+
145156
void run(Module* module_) override;
146157

147158
// We will do two different kinds of merging: First, we will merge types into
@@ -163,20 +174,6 @@ struct TypeMerging : public Pass {
163174
void applyMerges();
164175
};
165176

166-
struct MergeableSupertypesFirst
167-
: HeapTypeOrdering::SupertypesFirstBase<MergeableSupertypesFirst> {
168-
TypeMerging& merging;
169-
170-
MergeableSupertypesFirst(TypeMerging& merging) : merging(merging) {}
171-
172-
std::optional<HeapType> getDeclaredSuperType(HeapType type) {
173-
if (auto super = type.getDeclaredSuperType()) {
174-
return merging.getMerged(*super);
175-
}
176-
return std::nullopt;
177-
}
178-
};
179-
180177
// Hash and equality-compare HeapTypes based on their top-level structure (i.e.
181178
// "shape"), ignoring nontrivial heap type children that will not be
182179
// differentiated between until we run the DFA partition refinement.
@@ -305,8 +302,7 @@ bool TypeMerging::merge(MergeKind kind) {
305302

306303
// For each type, either create a new partition or add to its supertype's
307304
// partition.
308-
MergeableSupertypesFirst sortedTypes(*this);
309-
for (auto type : sortedTypes.sort(mergeable)) {
305+
for (auto type : mergeableSupertypesFirst(mergeable)) {
310306
// We need partitions for any public children of this type since those
311307
// children will participate in the DFA we're creating.
312308
for (auto child : getPublicChildren(type)) {
@@ -414,7 +410,7 @@ bool TypeMerging::merge(MergeKind kind) {
414410
std::vector<HeapType> newMergeable;
415411
bool merged = false;
416412
for (const auto& partition : refinedPartitions) {
417-
auto target = *MergeableSupertypesFirst(*this).sort(partition).begin();
413+
auto target = mergeableSupertypesFirst(partition).front();
418414
newMergeable.push_back(target);
419415
for (auto type : partition) {
420416
if (type != target) {
@@ -452,8 +448,7 @@ TypeMerging::splitSupertypePartition(const std::vector<HeapType>& types) {
452448
std::unordered_set<HeapType> includedTypes(types.begin(), types.end());
453449
std::vector<std::vector<HeapType>> partitions;
454450
std::unordered_map<HeapType, Index> partitionIndices;
455-
MergeableSupertypesFirst sortedTypes(*this);
456-
for (auto type : sortedTypes.sort(types)) {
451+
for (auto type : mergeableSupertypesFirst(types)) {
457452
auto super = type.getDeclaredSuperType();
458453
if (super && includedTypes.count(*super)) {
459454
// We must already have a partition for the supertype we can add to.

Diff for: src/support/topological_sort.h

-118
This file was deleted.

Diff for: src/tools/wasm-ctor-eval.cpp

+9-27
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#include "support/insert_ordered.h"
3939
#include "support/small_set.h"
4040
#include "support/string.h"
41-
#include "support/topological_sort.h"
41+
#include "support/topological_orders.h"
4242
#include "tool-options.h"
4343
#include "wasm-builder.h"
4444
#include "wasm-interpreter.h"
@@ -609,9 +609,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
609609
Builder builder(*wasm);
610610

611611
// First, find what constraints we have on the ordering of the globals. We
612-
// will build up a map of each global to the globals it must be after.
613-
using MustBeAfter = InsertOrderedMap<Name, InsertOrderedSet<Name>>;
614-
MustBeAfter mustBeAfter;
612+
// will build up a map of each global to the globals it must be before.
613+
using MustBeBefore = InsertOrderedMap<Name, InsertOrderedSet<Name>>;
614+
MustBeBefore mustBeBefore;
615615

616616
for (auto& global : wasm->globals) {
617617
if (!global->init) {
@@ -672,31 +672,12 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
672672

673673
// Any global.gets that cannot be fixed up are constraints.
674674
for (auto* get : scanner.unfixableGets) {
675-
mustBeAfter[global->name].insert(get->name);
675+
mustBeBefore[global->name];
676+
mustBeBefore[get->name].insert(global->name);
676677
}
677678
}
678679

679-
if (!mustBeAfter.empty()) {
680-
// We found constraints that require reordering, so do so.
681-
struct MustBeAfterSort : TopologicalSort<Name, MustBeAfterSort> {
682-
MustBeAfter& mustBeAfter;
683-
684-
MustBeAfterSort(MustBeAfter& mustBeAfter) : mustBeAfter(mustBeAfter) {
685-
for (auto& [global, _] : mustBeAfter) {
686-
push(global);
687-
}
688-
}
689-
690-
void pushPredecessors(Name global) {
691-
auto iter = mustBeAfter.find(global);
692-
if (iter != mustBeAfter.end()) {
693-
for (auto other : iter->second) {
694-
push(other);
695-
}
696-
}
697-
}
698-
};
699-
680+
if (!mustBeBefore.empty()) {
700681
auto oldGlobals = std::move(wasm->globals);
701682
// After clearing the globals vector, clear the map as well.
702683
wasm->updateMaps();
@@ -706,7 +687,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
706687
globalIndexes[oldGlobals[i]->name] = i;
707688
}
708689
// Add the globals that had an important ordering, in the right order.
709-
for (auto global : MustBeAfterSort(mustBeAfter)) {
690+
for (auto global :
691+
TopologicalSort::sortOf(mustBeBefore.begin(), mustBeBefore.end())) {
710692
wasm->addGlobal(std::move(oldGlobals[globalIndexes[global]]));
711693
}
712694
// Add all other globals after them.

0 commit comments

Comments
 (0)