11
11
// / This file implements a CFG sorting pass.
12
12
// /
13
13
// / This pass reorders the blocks in a function to put them into topological
14
- // / order, ignoring loop backedges, and without any loop being interrupted
15
- // / by a block not dominated by the loop header, with special care to keep the
16
- // / order as similar as possible to the original order.
14
+ // / order, ignoring loop backedges, and without any loop or exception being
15
+ // / interrupted by a block not dominated by the its header, with special care
16
+ // / to keep the order as similar as possible to the original order.
17
17
// /
18
18
// //===----------------------------------------------------------------------===//
19
19
20
20
#include " MCTargetDesc/WebAssemblyMCTargetDesc.h"
21
21
#include " WebAssembly.h"
22
+ #include " WebAssemblyExceptionInfo.h"
22
23
#include " WebAssemblySubtarget.h"
23
24
#include " WebAssemblyUtilities.h"
24
25
#include " llvm/ADT/PriorityQueue.h"
@@ -35,6 +36,73 @@ using namespace llvm;
35
36
#define DEBUG_TYPE " wasm-cfg-sort"
36
37
37
38
namespace {
39
+
40
+ // Wrapper for loops and exceptions
41
+ class Region {
42
+ public:
43
+ virtual ~Region () = default ;
44
+ virtual MachineBasicBlock *getHeader () const = 0;
45
+ virtual bool contains (const MachineBasicBlock *MBB) const = 0;
46
+ virtual unsigned getNumBlocks () const = 0;
47
+ using block_iterator = typename ArrayRef<MachineBasicBlock *>::const_iterator;
48
+ virtual iterator_range<block_iterator> blocks () const = 0;
49
+ virtual bool isLoop () const = 0;
50
+ };
51
+
52
+ template <typename T> class ConcreteRegion : public Region {
53
+ const T *Region;
54
+
55
+ public:
56
+ ConcreteRegion (const T *Region) : Region(Region) {}
57
+ MachineBasicBlock *getHeader () const override { return Region->getHeader (); }
58
+ bool contains (const MachineBasicBlock *MBB) const override {
59
+ return Region->contains (MBB);
60
+ }
61
+ unsigned getNumBlocks () const override { return Region->getNumBlocks (); }
62
+ iterator_range<block_iterator> blocks () const override {
63
+ return Region->blocks ();
64
+ }
65
+ bool isLoop () const override { return false ; }
66
+ };
67
+
68
+ template <> bool ConcreteRegion<MachineLoop>::isLoop() const { return true ; }
69
+
70
+ // This class has information of nested Regions; this is analogous to what
71
+ // LoopInfo is for loops.
72
+ class RegionInfo {
73
+ const MachineLoopInfo &MLI;
74
+ const WebAssemblyExceptionInfo &WEI;
75
+ std::vector<const Region *> Regions;
76
+ DenseMap<const MachineLoop *, std::unique_ptr<Region>> LoopMap;
77
+ DenseMap<const WebAssemblyException *, std::unique_ptr<Region>> ExceptionMap;
78
+
79
+ public:
80
+ RegionInfo (const MachineLoopInfo &MLI, const WebAssemblyExceptionInfo &WEI)
81
+ : MLI(MLI), WEI(WEI) {}
82
+
83
+ // Returns a smallest loop or exception that contains MBB
84
+ const Region *getRegionFor (const MachineBasicBlock *MBB) {
85
+ const auto *ML = MLI.getLoopFor (MBB);
86
+ const auto *WE = WEI.getExceptionFor (MBB);
87
+ if (!ML && !WE)
88
+ return nullptr ;
89
+ if ((ML && !WE) || (ML && WE && ML->getNumBlocks () < WE->getNumBlocks ())) {
90
+ // If the smallest region containing MBB is a loop
91
+ if (LoopMap.count (ML))
92
+ return LoopMap[ML].get ();
93
+ LoopMap[ML] = llvm::make_unique<ConcreteRegion<MachineLoop>>(ML);
94
+ return LoopMap[ML].get ();
95
+ } else {
96
+ // If the smallest region containing MBB is an exception
97
+ if (ExceptionMap.count (WE))
98
+ return ExceptionMap[WE].get ();
99
+ ExceptionMap[WE] =
100
+ llvm::make_unique<ConcreteRegion<WebAssemblyException>>(WE);
101
+ return ExceptionMap[WE].get ();
102
+ }
103
+ }
104
+ };
105
+
38
106
class WebAssemblyCFGSort final : public MachineFunctionPass {
39
107
StringRef getPassName () const override { return " WebAssembly CFG Sort" ; }
40
108
@@ -44,6 +112,8 @@ class WebAssemblyCFGSort final : public MachineFunctionPass {
44
112
AU.addPreserved <MachineDominatorTree>();
45
113
AU.addRequired <MachineLoopInfo>();
46
114
AU.addPreserved <MachineLoopInfo>();
115
+ AU.addRequired <WebAssemblyExceptionInfo>();
116
+ AU.addPreserved <WebAssemblyExceptionInfo>();
47
117
MachineFunctionPass::getAnalysisUsage (AU);
48
118
}
49
119
@@ -81,40 +151,85 @@ static void MaybeUpdateTerminator(MachineBasicBlock *MBB) {
81
151
}
82
152
83
153
namespace {
154
+ // EH pads are selected first regardless of the block comparison order.
155
+ // When only one of the BBs is an EH pad, we give a higher priority to it, to
156
+ // prevent common mismatches between possibly throwing calls and ehpads they
157
+ // unwind to, as in the example below:
158
+ //
159
+ // bb0:
160
+ // call @foo // If this throws, unwind to bb2
161
+ // bb1:
162
+ // call @bar // If this throws, unwind to bb3
163
+ // bb2 (ehpad):
164
+ // handler_bb2
165
+ // bb3 (ehpad):
166
+ // handler_bb3
167
+ // continuing code
168
+ //
169
+ // Because this pass tries to preserve the original BB order, this order will
170
+ // not change. But this will result in this try-catch structure in CFGStackify,
171
+ // resulting in a mismatch:
172
+ // try
173
+ // try
174
+ // call @foo
175
+ // call @bar // This should unwind to bb3, not bb2!
176
+ // catch
177
+ // handler_bb2
178
+ // end
179
+ // catch
180
+ // handler_bb3
181
+ // end
182
+ // continuing code
183
+ //
184
+ // If we give a higher priority to an EH pad whenever it is ready in this
185
+ // example, when both bb1 and bb2 are ready, we would pick up bb2 first.
186
+
84
187
// / Sort blocks by their number.
85
188
struct CompareBlockNumbers {
86
189
bool operator ()(const MachineBasicBlock *A,
87
190
const MachineBasicBlock *B) const {
191
+ if (A->isEHPad () && !B->isEHPad ())
192
+ return false ;
193
+ if (!A->isEHPad () && B->isEHPad ())
194
+ return true ;
195
+
88
196
return A->getNumber () > B->getNumber ();
89
197
}
90
198
};
91
199
// / Sort blocks by their number in the opposite order..
92
200
struct CompareBlockNumbersBackwards {
93
201
bool operator ()(const MachineBasicBlock *A,
94
202
const MachineBasicBlock *B) const {
203
+ // We give a higher priority to an EH pad
204
+ if (A->isEHPad () && !B->isEHPad ())
205
+ return false ;
206
+ if (!A->isEHPad () && B->isEHPad ())
207
+ return true ;
208
+
95
209
return A->getNumber () < B->getNumber ();
96
210
}
97
211
};
98
- // / Bookkeeping for a loop to help ensure that we don't mix blocks not dominated
99
- // / by the loop header among the loop's blocks.
212
+ // / Bookkeeping for a region to help ensure that we don't mix blocks not
213
+ // / dominated by the its header among its blocks.
100
214
struct Entry {
101
- const MachineLoop *Loop ;
215
+ const Region *Region ;
102
216
unsigned NumBlocksLeft;
103
217
104
218
// / List of blocks not dominated by Loop's header that are deferred until
105
219
// / after all of Loop's blocks have been seen.
106
220
std::vector<MachineBasicBlock *> Deferred;
107
221
108
- explicit Entry (const MachineLoop *L )
109
- : Loop(L ), NumBlocksLeft(L ->getNumBlocks ()) {}
222
+ explicit Entry (const class Region *R )
223
+ : Region(R ), NumBlocksLeft(R ->getNumBlocks ()) {}
110
224
};
111
225
} // end anonymous namespace
112
226
113
- // / Sort the blocks, taking special care to make sure that loops are not
227
+ // / Sort the blocks, taking special care to make sure that regions are not
114
228
// / interrupted by blocks not dominated by their header.
115
229
// / TODO: There are many opportunities for improving the heuristics here.
116
230
// / Explore them.
117
231
static void SortBlocks (MachineFunction &MF, const MachineLoopInfo &MLI,
232
+ const WebAssemblyExceptionInfo &WEI,
118
233
const MachineDominatorTree &MDT) {
119
234
// Prepare for a topological sort: Record the number of predecessors each
120
235
// block has, ignoring loop backedges.
@@ -131,35 +246,39 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
131
246
}
132
247
133
248
// Topological sort the CFG, with additional constraints:
134
- // - Between a loop header and the last block in the loop , there can be
135
- // no blocks not dominated by the loop header.
249
+ // - Between a region header and the last block in the region , there can be
250
+ // no blocks not dominated by its header.
136
251
// - It's desirable to preserve the original block order when possible.
137
252
// We use two ready lists; Preferred and Ready. Preferred has recently
138
253
// processed successors, to help preserve block sequences from the original
139
- // order. Ready has the remaining ready blocks.
254
+ // order. Ready has the remaining ready blocks. EH blocks are picked first
255
+ // from both queues.
140
256
PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>,
141
257
CompareBlockNumbers>
142
258
Preferred;
143
259
PriorityQueue<MachineBasicBlock *, std::vector<MachineBasicBlock *>,
144
260
CompareBlockNumbersBackwards>
145
261
Ready;
146
- SmallVector<Entry, 4 > Loops;
262
+
263
+ RegionInfo SUI (MLI, WEI);
264
+ SmallVector<Entry, 4 > Entries;
147
265
for (MachineBasicBlock *MBB = &MF.front ();;) {
148
- const MachineLoop *L = MLI.getLoopFor (MBB);
149
- if (L) {
150
- // If MBB is a loop header, add it to the active loop list. We can't put
151
- // any blocks that it doesn't dominate until we see the end of the loop.
152
- if (L->getHeader () == MBB)
153
- Loops.push_back (Entry (L));
154
- // For each active loop the block is in, decrement the count. If MBB is
155
- // the last block in an active loop, take it off the list and pick up any
156
- // blocks deferred because the header didn't dominate them.
157
- for (Entry &E : Loops)
158
- if (E.Loop ->contains (MBB) && --E.NumBlocksLeft == 0 )
266
+ const Region *R = SUI.getRegionFor (MBB);
267
+ if (R) {
268
+ // If MBB is a region header, add it to the active region list. We can't
269
+ // put any blocks that it doesn't dominate until we see the end of the
270
+ // region.
271
+ if (R->getHeader () == MBB)
272
+ Entries.push_back (Entry (R));
273
+ // For each active region the block is in, decrement the count. If MBB is
274
+ // the last block in an active region, take it off the list and pick up
275
+ // any blocks deferred because the header didn't dominate them.
276
+ for (Entry &E : Entries)
277
+ if (E.Region ->contains (MBB) && --E.NumBlocksLeft == 0 )
159
278
for (auto DeferredBlock : E.Deferred )
160
279
Ready.push (DeferredBlock);
161
- while (!Loops .empty () && Loops .back ().NumBlocksLeft == 0 )
162
- Loops .pop_back ();
280
+ while (!Entries .empty () && Entries .back ().NumBlocksLeft == 0 )
281
+ Entries .pop_back ();
163
282
}
164
283
// The main topological sort logic.
165
284
for (MachineBasicBlock *Succ : MBB->successors ()) {
@@ -177,19 +296,19 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
177
296
while (!Preferred.empty ()) {
178
297
Next = Preferred.top ();
179
298
Preferred.pop ();
180
- // If X isn't dominated by the top active loop header, defer it until that
181
- // loop is done.
182
- if (!Loops .empty () &&
183
- !MDT.dominates (Loops .back ().Loop ->getHeader (), Next)) {
184
- Loops .back ().Deferred .push_back (Next);
299
+ // If X isn't dominated by the top active region header, defer it until
300
+ // that region is done.
301
+ if (!Entries .empty () &&
302
+ !MDT.dominates (Entries .back ().Region ->getHeader (), Next)) {
303
+ Entries .back ().Deferred .push_back (Next);
185
304
Next = nullptr ;
186
305
continue ;
187
306
}
188
307
// If Next was originally ordered before MBB, and it isn't because it was
189
308
// loop-rotated above the header, it's not preferred.
190
309
if (Next->getNumber () < MBB->getNumber () &&
191
- (!L || !L ->contains (Next) ||
192
- L ->getHeader ()->getNumber () < Next->getNumber ())) {
310
+ (!R || !R ->contains (Next) ||
311
+ R ->getHeader ()->getNumber () < Next->getNumber ())) {
193
312
Ready.push (Next);
194
313
Next = nullptr ;
195
314
continue ;
@@ -207,11 +326,11 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
207
326
for (;;) {
208
327
Next = Ready.top ();
209
328
Ready.pop ();
210
- // If Next isn't dominated by the top active loop header, defer it until
211
- // that loop is done.
212
- if (!Loops .empty () &&
213
- !MDT.dominates (Loops .back ().Loop ->getHeader (), Next)) {
214
- Loops .back ().Deferred .push_back (Next);
329
+ // If Next isn't dominated by the top active region header, defer it
330
+ // until that region is done.
331
+ if (!Entries .empty () &&
332
+ !MDT.dominates (Entries .back ().Region ->getHeader (), Next)) {
333
+ Entries .back ().Deferred .push_back (Next);
215
334
continue ;
216
335
}
217
336
break ;
@@ -222,11 +341,11 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
222
341
MaybeUpdateTerminator (MBB);
223
342
MBB = Next;
224
343
}
225
- assert (Loops .empty () && " Active loop list not finished" );
344
+ assert (Entries .empty () && " Active sort region list not finished" );
226
345
MF.RenumberBlocks ();
227
346
228
347
#ifndef NDEBUG
229
- SmallSetVector<MachineLoop *, 8 > OnStack;
348
+ SmallSetVector<const Region *, 8 > OnStack;
230
349
231
350
// Insert a sentinel representing the degenerate loop that starts at the
232
351
// function entry block and includes the entire function as a "loop" that
@@ -235,29 +354,39 @@ static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI,
235
354
236
355
for (auto &MBB : MF) {
237
356
assert (MBB.getNumber () >= 0 && " Renumbered blocks should be non-negative." );
357
+ const Region *Region = SUI.getRegionFor (&MBB);
358
+
359
+ if (Region && &MBB == Region->getHeader ()) {
360
+ if (Region->isLoop ()) {
361
+ // Loop header. The loop predecessor should be sorted above, and the
362
+ // other predecessors should be backedges below.
363
+ for (auto Pred : MBB.predecessors ())
364
+ assert (
365
+ (Pred->getNumber () < MBB.getNumber () || Region->contains (Pred)) &&
366
+ " Loop header predecessors must be loop predecessors or "
367
+ " backedges" );
368
+ } else {
369
+ // Not a loop header. All predecessors should be sorted above.
370
+ for (auto Pred : MBB.predecessors ())
371
+ assert (Pred->getNumber () < MBB.getNumber () &&
372
+ " Non-loop-header predecessors should be topologically sorted" );
373
+ }
374
+ assert (OnStack.insert (Region) &&
375
+ " Regions should be declared at most once." );
238
376
239
- MachineLoop *Loop = MLI.getLoopFor (&MBB);
240
- if (Loop && &MBB == Loop->getHeader ()) {
241
- // Loop header. The loop predecessor should be sorted above, and the other
242
- // predecessors should be backedges below.
243
- for (auto Pred : MBB.predecessors ())
244
- assert (
245
- (Pred->getNumber () < MBB.getNumber () || Loop->contains (Pred)) &&
246
- " Loop header predecessors must be loop predecessors or backedges" );
247
- assert (OnStack.insert (Loop) && " Loops should be declared at most once." );
248
377
} else {
249
378
// Not a loop header. All predecessors should be sorted above.
250
379
for (auto Pred : MBB.predecessors ())
251
380
assert (Pred->getNumber () < MBB.getNumber () &&
252
381
" Non-loop-header predecessors should be topologically sorted" );
253
- assert (OnStack.count (MLI. getLoopFor (&MBB)) &&
254
- " Blocks must be nested in their loops " );
382
+ assert (OnStack.count (SUI. getRegionFor (&MBB)) &&
383
+ " Blocks must be nested in their regions " );
255
384
}
256
385
while (OnStack.size () > 1 && &MBB == WebAssembly::getBottom (OnStack.back ()))
257
386
OnStack.pop_back ();
258
387
}
259
388
assert (OnStack.pop_back_val () == nullptr &&
260
- " The function entry block shouldn't actually be a loop header" );
389
+ " The function entry block shouldn't actually be a region header" );
261
390
assert (OnStack.empty () &&
262
391
" Control flow stack pushes and pops should be balanced." );
263
392
#endif
@@ -269,12 +398,13 @@ bool WebAssemblyCFGSort::runOnMachineFunction(MachineFunction &MF) {
269
398
<< MF.getName () << ' \n ' );
270
399
271
400
const auto &MLI = getAnalysis<MachineLoopInfo>();
401
+ const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>();
272
402
auto &MDT = getAnalysis<MachineDominatorTree>();
273
403
// Liveness is not tracked for VALUE_STACK physreg.
274
404
MF.getRegInfo ().invalidateLiveness ();
275
405
276
- // Sort the blocks, with contiguous loops .
277
- SortBlocks (MF, MLI, MDT);
406
+ // Sort the blocks, with contiguous sort regions .
407
+ SortBlocks (MF, MLI, WEI, MDT);
278
408
279
409
return true ;
280
410
}
0 commit comments