LLVM 24.0.0git
WebAssemblyCFGSort.cpp
Go to the documentation of this file.
1//===-- WebAssemblyCFGSort.cpp - CFG Sorting ------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements a CFG sorting pass.
11///
12/// This pass reorders the blocks in a function to put them into topological
13/// order, ignoring loop backedges, and without any loop or exception being
14/// interrupted by a block not dominated by the its header, with special care
15/// to keep the order as similar as possible to the original order.
16///
17////===----------------------------------------------------------------------===//
18
19#include "WebAssembly.h"
30#include "llvm/CodeGen/Passes.h"
31#include "llvm/IR/Analysis.h"
32#include "llvm/Support/Debug.h"
34using namespace llvm;
37
38#define DEBUG_TYPE "wasm-cfg-sort"
39
40// Option to disable EH pad first sorting. Only for testing unwind destination
41// mismatches in CFGStackify.
43 "wasm-disable-ehpad-sort", cl::ReallyHidden,
45 "WebAssembly: Disable EH pad-first sort order. Testing purpose only."),
46 cl::init(false));
47
48namespace {
49
50class WebAssemblyCFGSortLegacy final : public MachineFunctionPass {
51 StringRef getPassName() const override { return "WebAssembly CFG Sort"; }
52
53 void getAnalysisUsage(AnalysisUsage &AU) const override {
54 AU.setPreservesCFG();
55 AU.addRequired<MachineDominatorTreeWrapperPass>();
56 AU.addPreserved<MachineDominatorTreeWrapperPass>();
57 AU.addRequired<MachineLoopInfoWrapperPass>();
58 AU.addPreserved<MachineLoopInfoWrapperPass>();
59 AU.addRequired<WebAssemblyExceptionInfoWrapperPass>();
60 AU.addPreserved<WebAssemblyExceptionInfoWrapperPass>();
62 }
63
64 bool runOnMachineFunction(MachineFunction &MF) override;
65
66public:
67 static char ID; // Pass identification, replacement for typeid
68 WebAssemblyCFGSortLegacy() : MachineFunctionPass(ID) {}
69};
70} // end anonymous namespace
71
72char WebAssemblyCFGSortLegacy::ID = 0;
73INITIALIZE_PASS(WebAssemblyCFGSortLegacy, DEBUG_TYPE,
74 "Reorders blocks in topological order", false, false)
75
77 return new WebAssemblyCFGSortLegacy();
78}
79
81#ifndef NDEBUG
82 bool AnyBarrier = false;
83#endif
84 bool AllAnalyzable = true;
85 for (const MachineInstr &Term : MBB->terminators()) {
86#ifndef NDEBUG
87 AnyBarrier |= Term.isBarrier();
88#endif
89 AllAnalyzable &= Term.isBranch() && !Term.isIndirectBranch();
90 }
91 assert((AnyBarrier || AllAnalyzable) &&
92 "analyzeBranch needs to analyze any block with a fallthrough");
93
94 // Find the layout successor from the original block order.
95 MachineFunction *MF = MBB->getParent();
96 MachineBasicBlock *OriginalSuccessor =
97 unsigned(MBB->getNumber() + 1) < MF->getNumBlockIDs()
98 ? MF->getBlockNumbered(MBB->getNumber() + 1)
99 : nullptr;
100
101 if (AllAnalyzable)
102 MBB->updateTerminator(OriginalSuccessor);
103}
104
105namespace {
106// EH pads are selected first regardless of the block comparison order.
107// When only one of the BBs is an EH pad, we give a higher priority to it, to
108// prevent common mismatches between possibly throwing calls and ehpads they
109// unwind to, as in the example below:
110//
111// bb0:
112// call @foo // If this throws, unwind to bb2
113// bb1:
114// call @bar // If this throws, unwind to bb3
115// bb2 (ehpad):
116// handler_bb2
117// bb3 (ehpad):
118// handler_bb3
119// continuing code
120//
121// Because this pass tries to preserve the original BB order, this order will
122// not change. But this will result in this try-catch structure in CFGStackify,
123// resulting in a mismatch:
124// try
125// try
126// call @foo
127// call @bar // This should unwind to bb3, not bb2!
128// catch
129// handler_bb2
130// end
131// catch
132// handler_bb3
133// end
134// continuing code
135//
136// If we give a higher priority to an EH pad whenever it is ready in this
137// example, when both bb1 and bb2 are ready, we would pick up bb2 first.
138
139/// Sort blocks by their number.
140struct CompareBlockNumbers {
141 bool operator()(const MachineBasicBlock *A,
142 const MachineBasicBlock *B) const {
144 if (A->isEHPad() && !B->isEHPad())
145 return false;
146 if (!A->isEHPad() && B->isEHPad())
147 return true;
148 }
149
150 return A->getNumber() > B->getNumber();
151 }
152};
153/// Sort blocks by their number in the opposite order..
154struct CompareBlockNumbersBackwards {
155 bool operator()(const MachineBasicBlock *A,
156 const MachineBasicBlock *B) const {
158 if (A->isEHPad() && !B->isEHPad())
159 return false;
160 if (!A->isEHPad() && B->isEHPad())
161 return true;
162 }
163
164 return A->getNumber() < B->getNumber();
165 }
166};
167/// Bookkeeping for a region to help ensure that we don't mix blocks not
168/// dominated by the its header among its blocks.
169struct Entry {
170 const SortRegion *TheRegion;
171 unsigned NumBlocksLeft;
172
173 /// List of blocks not dominated by Loop's header that are deferred until
174 /// after all of Loop's blocks have been seen.
175 std::vector<MachineBasicBlock *> Deferred;
176
177 explicit Entry(const SortRegion *R)
178 : TheRegion(R), NumBlocksLeft(R->getNumBlocks()) {}
179};
180} // end anonymous namespace
181
182/// Sort the blocks, taking special care to make sure that regions are not
183/// interrupted by blocks not dominated by their header.
184/// TODO: There are many opportunities for improving the heuristics here.
185/// Explore them.
186static void sortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
187 const WebAssemblyExceptionInfo &WEI,
189 // Remember original layout ordering, so we can update terminators after
190 // reordering to point to the original layout successor.
191 MF.RenumberBlocks();
192
193 // Prepare for a topological sort: Record the number of predecessors each
194 // block has, ignoring loop backedges.
195 SmallVector<unsigned, 16> NumPredsLeft(MF.getNumBlockIDs(), 0);
196 for (MachineBasicBlock &MBB : MF) {
197 unsigned N = MBB.pred_size();
198 if (MachineLoop *L = MLI.getLoopFor(&MBB))
199 if (L->getHeader() == &MBB)
200 for (const MachineBasicBlock *Pred : MBB.predecessors())
201 if (L->contains(Pred))
202 --N;
203 NumPredsLeft[MBB.getNumber()] = N;
204 }
205
206 // Topological sort the CFG, with additional constraints:
207 // - Between a region header and the last block in the region, there can be
208 // no blocks not dominated by its header.
209 // - It's desirable to preserve the original block order when possible.
210 // We use two ready lists; Preferred and Ready. Preferred has recently
211 // processed successors, to help preserve block sequences from the original
212 // order. Ready has the remaining ready blocks. EH blocks are picked first
213 // from both queues.
215 CompareBlockNumbers>
216 Preferred;
218 CompareBlockNumbersBackwards>
219 Ready;
220
221 SortRegionInfo SRI(MLI, WEI);
222 SmallVector<Entry, 4> Entries;
223 for (MachineBasicBlock *MBB = &MF.front();;) {
224 const SortRegion *R = SRI.getRegionFor(MBB);
225 if (R) {
226 // If MBB is a region header, add it to the active region list. We can't
227 // put any blocks that it doesn't dominate until we see the end of the
228 // region.
229 if (R->getHeader() == MBB)
230 Entries.push_back(Entry(R));
231 // For each active region the block is in, decrement the count. If MBB is
232 // the last block in an active region, take it off the list and pick up
233 // any blocks deferred because the header didn't dominate them.
234 for (Entry &E : Entries)
235 if (E.TheRegion->contains(MBB) && --E.NumBlocksLeft == 0)
236 for (auto *DeferredBlock : E.Deferred)
237 Ready.push(DeferredBlock);
238 while (!Entries.empty() && Entries.back().NumBlocksLeft == 0)
239 Entries.pop_back();
240 }
241 // The main topological sort logic.
242 for (MachineBasicBlock *Succ : MBB->successors()) {
243 // Ignore backedges.
244 if (MachineLoop *SuccL = MLI.getLoopFor(Succ))
245 if (SuccL->getHeader() == Succ && SuccL->contains(MBB))
246 continue;
247 // Decrement the predecessor count. If it's now zero, it's ready.
248 if (--NumPredsLeft[Succ->getNumber()] == 0)
249 Preferred.push(Succ);
250 }
251 // Determine the block to follow MBB. First try to find a preferred block,
252 // to preserve the original block order when possible.
253 MachineBasicBlock *Next = nullptr;
254 while (!Preferred.empty()) {
255 Next = Preferred.top();
256 Preferred.pop();
257 // If X isn't dominated by the top active region header, defer it until
258 // that region is done.
259 if (!Entries.empty() &&
260 !MDT.dominates(Entries.back().TheRegion->getHeader(), Next)) {
261 Entries.back().Deferred.push_back(Next);
262 Next = nullptr;
263 continue;
264 }
265 // If Next was originally ordered before MBB, and it isn't because it was
266 // loop-rotated above the header, it's not preferred.
267 if (Next->getNumber() < MBB->getNumber() &&
268 (WasmDisableEHPadSort || !Next->isEHPad()) &&
269 (!R || !R->contains(Next) ||
270 R->getHeader()->getNumber() < Next->getNumber())) {
271 Ready.push(Next);
272 Next = nullptr;
273 continue;
274 }
275 break;
276 }
277 // If we didn't find a suitable block in the Preferred list, check the
278 // general Ready list.
279 if (!Next) {
280 // If there are no more blocks to process, we're done.
281 if (Ready.empty()) {
283 break;
284 }
285 for (;;) {
286 Next = Ready.top();
287 Ready.pop();
288 // If Next isn't dominated by the top active region header, defer it
289 // until that region is done.
290 if (!Entries.empty() &&
291 !MDT.dominates(Entries.back().TheRegion->getHeader(), Next)) {
292 Entries.back().Deferred.push_back(Next);
293 continue;
294 }
295 break;
296 }
297 }
298 // Move the next block into place and iterate.
299 Next->moveAfter(MBB);
301 MBB = Next;
302 }
303 assert(Entries.empty() && "Active sort region list not finished");
304 MF.RenumberBlocks();
305
306#ifndef NDEBUG
307 for (auto &MBB : MF) {
308 assert(MBB.getNumber() >= 0 && "Renumbered blocks should be non-negative.");
309 const SortRegion *Region = SRI.getRegionFor(&MBB);
310
311 if (Region && &MBB == Region->getHeader()) {
312 // Region header.
313 if (Region->isLoop()) {
314 // Loop header. The loop predecessor should be sorted above, and the
315 // other predecessors should be backedges below.
316 for (auto *Pred : MBB.predecessors())
317 assert(
318 (Pred->getNumber() < MBB.getNumber() || Region->contains(Pred)) &&
319 "Loop header predecessors must be loop predecessors or "
320 "backedges");
321 } else {
322 // Exception header. All predecessors should be sorted above.
323 for (auto *Pred : MBB.predecessors())
324 assert(Pred->getNumber() < MBB.getNumber() &&
325 "Non-loop-header predecessors should be topologically sorted");
326 }
327 } else {
328 // Not a region header. All predecessors should be sorted above.
329 for (auto *Pred : MBB.predecessors())
330 assert(Pred->getNumber() < MBB.getNumber() &&
331 "Non-loop-header predecessors should be topologically sorted");
332 }
333 }
334
336 for (auto &MBB : MF) {
337 const SortRegion *Region = SRI.getRegionFor(&MBB);
338 if (Region)
339 Regions.insert(Region);
340 }
341
342 SmallVector<std::pair<int, int>, 8> RegionIntervals(Regions.size(), {-1, -1});
343
344 unsigned RegionIdx = 0;
345 for (auto *Region : Regions) {
346 assert(Region->getHeader() != &MF.front() &&
347 "The function entry block shouldn't actually be a region header");
348
349 auto *Header = Region->getHeader();
350 auto *Bottom = SRI.getBottom(Region);
351
352 assert(Header && "Regions must have a header");
353 assert(Bottom && "Regions must have a bottom");
354
355 std::pair<int, int> Interval = {Header->getNumber(), Bottom->getNumber()};
356 assert(Interval.first <= Interval.second &&
357 "Region bottoms must be sorted after region headers");
358
359 RegionIntervals[RegionIdx++] = Interval;
360
361 for (auto *MBB : Region->blocks()) {
362 assert(MBB->getNumber() >= Interval.first &&
363 MBB->getNumber() <= Interval.second &&
364 "All blocks within a region must have numbers within the region's "
365 "interval");
366 }
367 }
368
369 for (const auto &IntervalA : RegionIntervals) {
370 for (const auto &IntervalB : RegionIntervals) {
371 auto AContainsB = IntervalA.first <= IntervalB.first &&
372 IntervalA.second >= IntervalB.second;
373 auto BContainsA = IntervalB.first <= IntervalA.first &&
374 IntervalB.second >= IntervalA.second;
375 auto Disjoint = IntervalA.second < IntervalB.first ||
376 IntervalA.first > IntervalB.second;
377 assert((AContainsB || BContainsA || Disjoint) &&
378 "Regions must be fully contained within their parents and not "
379 "overlap their siblings");
380 }
381 }
382#endif
383}
384
387 LLVM_DEBUG(dbgs() << "********** CFG Sorting **********\n"
388 "********** Function: "
389 << MF.getName() << '\n');
390
391 // Liveness is not tracked for VALUE_STACK physreg.
393
394 // Sort the blocks, with contiguous sort regions.
395 sortBlocks(MF, MLI, WEI, MDT);
396
397 return true;
398}
399
400bool WebAssemblyCFGSortLegacy::runOnMachineFunction(MachineFunction &MF) {
401 MachineLoopInfo &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
402 WebAssemblyExceptionInfo &WEI =
403 getAnalysis<WebAssemblyExceptionInfoWrapperPass>().getWEI();
404 MachineDominatorTree &MDT =
405 getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
406 return sortCFG(MF, MLI, WEI, MDT);
407}
408
409PreservedAnalyses
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define DEBUG_TYPE
std::pair< uint64_t, uint64_t > Interval
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file defines the PriorityQueue class.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static bool sortCFG(MachineFunction &MF, MachineLoopInfo &MLI, WebAssemblyExceptionInfo &WEI, MachineDominatorTree &MDT)
static void maybeUpdateTerminator(MachineBasicBlock *MBB)
static cl::opt< bool > WasmDisableEHPadSort("wasm-disable-ehpad-sort", cl::ReallyHidden, cl::desc("WebAssembly: Disable EH pad-first sort order. Testing purpose only."), cl::init(false))
This file implements WebAssemblyException information analysis.
This file implements regions used in CFGSort and CFGStackify.
This file contains the declaration of the WebAssembly-specific utility functions.
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end.
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:275
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
Analysis pass which computes a MachineDominatorTree.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
bool dominates(const MachineInstr *A, const MachineInstr *B) const
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
MachineBasicBlock * getBlockNumbered(unsigned N) const
getBlockNumbered - MachineBasicBlocks are automatically numbered when they are inserted into the mach...
unsigned getNumBlockIDs() const
getNumBlockIDs - Return the number of MBB ID's allocated.
void RenumberBlocks(MachineBasicBlock *MBBFrom=nullptr)
RenumberBlocks - This discards all of the MachineBasicBlock numbers and recomputes them.
const MachineBasicBlock & front() const
Representation of each machine instruction.
Analysis pass that exposes the MachineLoopInfo for a machine function.
void invalidateLiveness()
invalidateLiveness - Indicates that register liveness is no longer being tracked accurately.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
PriorityQueue - This class behaves like std::priority_queue and provides a few additional convenience...
block_range blocks()
Returns a range view of the basic blocks in the region.
Definition RegionInfo.h:620
bool contains(const BlockT *BB) const
Check if the region contains a BasicBlock.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition SmallSet.h:134
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition SmallSet.h:184
size_type size() const
Definition SmallSet.h:171
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
Pass manager infrastructure for declaring and invalidating analyses.
@ Entry
Definition COFF.h:862
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
bool sortBlocks(Function &F)
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
FunctionPass * createWebAssemblyCFGSortLegacyPass()
#define N