Skip to content

Commit 7e09feb

Browse files
Optimize logic for walking up supertype heirarchy
1 parent 4994a9d commit 7e09feb

File tree

2 files changed

+93
-13
lines changed

2 files changed

+93
-13
lines changed

src/passes/GlobalEffects.cpp

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ir/effects.h"
2525
#include "ir/module-utils.h"
2626
#include "pass.h"
27+
#include "support/graph_traversal.h"
2728
#include "support/strongly_connected_components.h"
2829
#include "wasm.h"
2930

@@ -148,6 +149,18 @@ CallGraph buildCallGraph(const Module& module,
148149
const std::map<Function*, FuncInfo>& funcInfos,
149150
bool closedWorld) {
150151
CallGraph callGraph;
152+
if (!closedWorld) {
153+
for (const auto& [caller, callerInfo] : funcInfos) {
154+
auto& callees = callGraph[caller];
155+
156+
// Function -> Function
157+
for (Name calleeFunction : callerInfo.calledFunctions) {
158+
callees.insert(module.getFunction(calleeFunction));
159+
}
160+
}
161+
162+
return callGraph;
163+
}
151164

152165
std::unordered_set<HeapType> allFunctionTypes;
153166
for (const auto& [caller, callerInfo] : funcInfos) {
@@ -158,9 +171,6 @@ CallGraph buildCallGraph(const Module& module,
158171
callees.insert(module.getFunction(calleeFunction));
159172
}
160173

161-
if (!closedWorld) {
162-
continue;
163-
}
164174
// Function -> Type
165175
allFunctionTypes.insert(caller->type.getHeapType());
166176
for (HeapType calleeType : callerInfo.indirectCalledTypes) {
@@ -176,16 +186,21 @@ CallGraph buildCallGraph(const Module& module,
176186
}
177187

178188
// Type -> Type
179-
for (HeapType type : allFunctionTypes) {
180-
// Not needed except that during lookup we expect the key to exist.
181-
callGraph[type];
182-
183-
HeapType curr = type;
184-
while (std::optional<HeapType> super = curr.getDeclaredSuperType()) {
185-
callGraph[*super].insert(type);
186-
curr = *super;
187-
}
188-
}
189+
// Do a DFS up the type heirarchy for all function implementations.
190+
// We are essentially walking up each supertype chain and adding edges from
191+
// super -> subtype, but doing it via DFS to avoid repeated work.
192+
Graph superTypeGraph(allFunctionTypes.begin(),
193+
allFunctionTypes.end(),
194+
[&callGraph](auto&& push, HeapType t) {
195+
// Not needed except that during lookup we expect the key to exist.
196+
callGraph[t];
197+
198+
if (auto super = t.getDeclaredSuperType()) {
199+
callGraph[*super].insert(t);
200+
push(*super);
201+
}
202+
});
203+
(void)superTypeGraph.traverseDepthFirst();
189204

190205
return callGraph;
191206
}

src/support/graph_traversal.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2026 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <concepts>
18+
19+
namespace wasm {
20+
21+
template<typename T, typename SuccessorFunction>
22+
requires std::invocable<SuccessorFunction, std::function<void(const T&)>&, const T&>
23+
class Graph {
24+
public:
25+
template<std::input_iterator It, std::sentinel_for<It> Sen>
26+
requires std::convertible_to<std::iter_reference_t<It>, T>
27+
Graph(It rootsBegin, Sen rootsEnd, auto&& successors)
28+
: roots(rootsBegin, rootsEnd),
29+
successors(std::forward<decltype(successors)>(successors)) {}
30+
31+
std::unordered_set<T> traverseDepthFirst() const {
32+
std::vector<T> stack(roots.begin(), roots.end());
33+
std::unordered_set<T> visited(roots.begin(), roots.end());
34+
35+
while (!stack.empty()) {
36+
auto curr = std::move(stack.back());
37+
stack.pop_back();
38+
39+
auto maybePush = [&](const T& t) {
40+
if (visited.contains(t)) {
41+
return;
42+
}
43+
44+
visited.insert(t);
45+
stack.push_back(t);
46+
};
47+
48+
successors(maybePush, curr);
49+
}
50+
51+
return visited;
52+
}
53+
54+
private:
55+
std::vector<T> roots;
56+
SuccessorFunction successors;
57+
};
58+
59+
template<std::input_iterator It,
60+
std::sentinel_for<It> Sen,
61+
typename SuccessorFunction>
62+
Graph(It, Sen, SuccessorFunction)
63+
-> Graph<std::iter_value_t<It>, std::decay_t<SuccessorFunction>>;
64+
65+
} // namespace wasm

0 commit comments

Comments
 (0)