Skip to content

Commit d36281b

Browse files
committed
re-do
1 parent 43e5381 commit d36281b

File tree

1 file changed

+40
-5
lines changed

1 file changed

+40
-5
lines changed

src/passes/Heap2Local.cpp

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,16 @@ namespace {
170170
// Core analysis that provides an escapes() method to check if an allocation
171171
// escapes in a way that prevents optimizing it away as described above. It also
172172
// stashes information about the relevant expressions as it goes, which helps
173-
// optimization later (|reached|).
173+
// optimization later (|seen| and |reached|).
174174
struct EscapeAnalyzer {
175+
// All the expressions that have already been seen by the optimizer, see the
176+
// comment above on exclusivity: once we have seen something when analyzing
177+
// one allocation, if we reach it again then we can exit early since seeing it
178+
// a second time proves we lost exclusivity. We must track this across
179+
// multiple instances of EscapeAnalyzer as each handles a particular
180+
// allocation.
181+
std::unordered_set<Expression*>& seen;
182+
175183
// To find what escapes, we need to follow where values flow, both up to
176184
// parents, and via branches, and through locals.
177185
// TODO: for efficiency, only scan reference types in LocalGraph
@@ -182,12 +190,13 @@ struct EscapeAnalyzer {
182190
const PassOptions& passOptions;
183191
Module& wasm;
184192

185-
EscapeAnalyzer(const LocalGraph& localGraph,
193+
EscapeAnalyzer(std::unordered_set<Expression*>& seen,
194+
const LocalGraph& localGraph,
186195
const Parents& parents,
187196
const BranchUtils::BranchTargets& branchTargets,
188197
const PassOptions& passOptions,
189198
Module& wasm)
190-
: localGraph(localGraph), parents(parents),
199+
: seen(seen), localGraph(localGraph), parents(parents),
191200
branchTargets(branchTargets), passOptions(passOptions), wasm(wasm) {}
192201

193202
// We must track all the local.sets that write the allocation, to verify
@@ -252,6 +261,28 @@ struct EscapeAnalyzer {
252261
assert(interaction == ParentChildInteraction::FullyConsumes ||
253262
interaction == ParentChildInteraction::Flows);
254263

264+
// If we've already seen an expression, stop since we cannot optimize
265+
// things that overlap in any way (see the notes on exclusivity, above).
266+
// Note that we use a nonrepeating queue here, so we already do not visit
267+
// the same thing more than once; what this check does is verify we don't
268+
// look at something that another allocation reached, which would be in a
269+
// different call to this function and use a different queue (any overlap
270+
// between calls would prove non-exclusivity).
271+
//
272+
// It is ok, however, to see a parent more than once, if the allocation
273+
// flows to it from two children, as is the case for ref.eq. Likewise, for
274+
// struct.set, where we also have different considerations for the two
275+
// children (the reference does not escape, but the value does), and in
276+
// that case there may be different allocations for the children.
277+
if (interaction == ParentChildInteraction::Flows) {
278+
auto seenBefore = !seen.emplace(parent).second;
279+
auto reachedInThisAllocation = reached.count(parent) > 0;
280+
if (seenBefore && !reachedInThisAllocation) {
281+
// XXX struct.set can be from different allocations!
282+
return true;
283+
}
284+
}
285+
255286
// We can proceed, as the parent interacts with us properly, and we are
256287
// the only allocation to get here.
257288

@@ -1031,6 +1062,10 @@ struct Heap2Local {
10311062
// flow to.
10321063
localGraph.computeSetInfluences();
10331064

1065+
// All the expressions we have already looked at. We use this to avoid
1066+
// repeated work, see above.
1067+
std::unordered_set<Expression*> seen;
1068+
10341069
// Find all the relevant allocations in the function: StructNew, ArrayNew,
10351070
// ArrayNewFixed.
10361071
struct AllocationFinder : public PostWalker<AllocationFinder> {
@@ -1090,7 +1125,7 @@ struct Heap2Local {
10901125
}
10911126

10921127
EscapeAnalyzer analyzer(
1093-
localGraph, parents, branchTargets, passOptions, wasm);
1128+
seen, localGraph, parents, branchTargets, passOptions, wasm);
10941129
if (!analyzer.escapes(allocation)) {
10951130
// Convert the allocation and all its uses into a struct. Then convert
10961131
// the struct into locals.
@@ -1110,7 +1145,7 @@ struct Heap2Local {
11101145
// Check for escaping, noting relevant information as we go. If this does
11111146
// not escape, optimize it into locals.
11121147
EscapeAnalyzer analyzer(
1113-
localGraph, parents, branchTargets, passOptions, wasm);
1148+
seen, localGraph, parents, branchTargets, passOptions, wasm);
11141149
if (!analyzer.escapes(allocation)) {
11151150
Struct2Local(allocation, analyzer, func, wasm);
11161151
}

0 commit comments

Comments
 (0)