12
12
13
13
#define DEBUG_TYPE " alloc-stack-hoisting"
14
14
15
- #include " swift/IRGen/IRGenSILPasses.h"
16
15
#include " swift/AST/Availability.h"
17
- #include " swift/SILOptimizer/Analysis/Analysis .h"
18
- #include " swift/SILOptimizer/PassManager/Passes .h"
19
- #include " swift/SILOptimizer/PassManager/Transforms .h"
16
+ #include " swift/AST/IRGenOptions .h"
17
+ #include " swift/AST/SemanticAttrs .h"
18
+ #include " swift/IRGen/IRGenSILPasses .h"
20
19
#include " swift/SIL/DebugUtils.h"
20
+ #include " swift/SIL/Dominance.h"
21
+ #include " swift/SIL/LoopInfo.h"
22
+ #include " swift/SIL/SILArgument.h"
21
23
#include " swift/SIL/SILBuilder.h"
22
24
#include " swift/SIL/SILInstruction.h"
23
- #include " swift/SIL/SILArgument.h"
24
- #include " swift/AST/SemanticAttrs.h"
25
+ #include " swift/SILOptimizer/Analysis/Analysis.h"
26
+ #include " swift/SILOptimizer/Analysis/DominanceAnalysis.h"
27
+ #include " swift/SILOptimizer/PassManager/Passes.h"
28
+ #include " swift/SILOptimizer/PassManager/Transforms.h"
29
+ #include " swift/SILOptimizer/Utils/CFGOptUtils.h"
25
30
26
31
#include " IRGenModule.h"
27
32
#include " NonFixedTypeInfo.h"
@@ -95,7 +100,22 @@ class Partition {
95
100
// /
96
101
// / This assumes that the live ranges of the alloc_stack instructions are
97
102
// / non-overlapping.
98
- void assignStackLocation (SmallVectorImpl<SILInstruction *> &FunctionExits);
103
+ void assignStackLocation (
104
+ SmallVectorImpl<SILInstruction *> &FunctionExits,
105
+ SmallVectorImpl<DebugValueInst *> &DebugValueToBreakBlocksAt);
106
+
107
+ // / Returns true if any of the alloc_stack that we are merging were
108
+ // / moved. Causes us to insert extra debug addr.
109
+ // /
110
+ // / TODO: In the future we want to do this for /all/ alloc_stack but that
111
+ // / would require us moving /most of/ swift's IRGen emission to use
112
+ // / llvm.dbg.addr instead of llvm.dbg.declare and that would require us to do
113
+ // / statistics to make sure that we haven't hurt debuggability by making the
114
+ // / change.
115
+ bool hasMovedElt () const {
116
+ return llvm::any_of (Elts,
117
+ [](AllocStackInst *asi) { return asi->getWasMoved (); });
118
+ }
99
119
};
100
120
} // end anonymous namespace
101
121
@@ -125,21 +145,40 @@ insertDeallocStackAtEndOf(SmallVectorImpl<SILInstruction *> &FunctionExits,
125
145
126
146
// / Hack to workaround a clang LTO bug.
127
147
LLVM_ATTRIBUTE_NOINLINE
128
- void moveAllocStackToBeginningOfBlock (AllocStackInst* AS, SILBasicBlock *BB) {
148
+ void moveAllocStackToBeginningOfBlock (
149
+ AllocStackInst *AS, SILBasicBlock *BB, bool haveMovedElt,
150
+ SmallVectorImpl<DebugValueInst *> &DebugValueToBreakBlocksAt) {
151
+ // If we have var info, create the debug_value at the alloc_stack position and
152
+ // invalidate the alloc_stack's var info. This transfers the debug info state
153
+ // of the debug_value to the original position.
154
+ if (haveMovedElt) {
155
+ if (auto varInfo = AS->getVarInfo ()) {
156
+ SILBuilderWithScope Builder (AS);
157
+ auto *DVI = Builder.createDebugValue (AS->getLoc (), AS, *varInfo);
158
+ DVI->markAsMoved ();
159
+ DebugValueToBreakBlocksAt.push_back (DVI);
160
+ AS->invalidateVarInfo ();
161
+ AS->markAsMoved ();
162
+ }
163
+ }
129
164
AS->moveFront (BB);
130
165
}
131
166
132
167
// / Assign a single alloc_stack instruction to all the alloc_stacks in the
133
168
// / partition.
134
169
void Partition::assignStackLocation (
135
- SmallVectorImpl<SILInstruction *> &FunctionExits) {
170
+ SmallVectorImpl<SILInstruction *> &FunctionExits,
171
+ SmallVectorImpl<DebugValueInst *> &DebugValueToBreakBlocksAt) {
136
172
assert (!Elts.empty () && " Must have a least one location" );
173
+ bool hasAtLeastOneMovedElt = hasMovedElt ();
174
+
137
175
// The assigned location is the first alloc_stack in our partition.
138
176
auto *AssignedLoc = Elts[0 ];
139
177
140
178
// Move this assigned location to the beginning of the entry block.
141
179
auto *EntryBB = AssignedLoc->getFunction ()->getEntryBlock ();
142
- moveAllocStackToBeginningOfBlock (AssignedLoc, EntryBB);
180
+ moveAllocStackToBeginningOfBlock (AssignedLoc, EntryBB, hasAtLeastOneMovedElt,
181
+ DebugValueToBreakBlocksAt);
143
182
144
183
// Erase the dealloc_stacks.
145
184
eraseDeallocStacks (AssignedLoc);
@@ -153,6 +192,15 @@ void Partition::assignStackLocation(
153
192
if (AssignedLoc == AllocStack) continue ;
154
193
eraseDeallocStacks (AllocStack);
155
194
AllocStack->replaceAllUsesWith (AssignedLoc);
195
+ if (hasAtLeastOneMovedElt) {
196
+ if (auto VarInfo = AllocStack->getVarInfo ()) {
197
+ SILBuilderWithScope Builder (AllocStack);
198
+ auto *DVI = Builder.createDebugValue (AllocStack->getLoc (), AssignedLoc,
199
+ *VarInfo);
200
+ DVI->markAsMoved ();
201
+ DebugValueToBreakBlocksAt.push_back (DVI);
202
+ }
203
+ }
156
204
AllocStack->eraseFromParent ();
157
205
}
158
206
}
@@ -234,14 +282,21 @@ class MergeStackSlots {
234
282
SmallVector<Partition, 2 > PartitionByType;
235
283
// / The function exits.
236
284
SmallVectorImpl<SILInstruction *> &FunctionExits;
285
+ // / If we are merging any alloc_stack that were moved, to work around a bug in
286
+ // / SelectionDAG that sinks to llvm.dbg.addr, we need to break blocks right
287
+ // / after each llvm.dbg.addr.
288
+ // /
289
+ // / TODO: Once we have /any/ FastISel/better SelectionDAG support, this can be
290
+ // / removed.
291
+ SmallVector<DebugValueInst *, 4 > DebugValueToBreakBlocksAt;
237
292
238
293
public:
239
294
MergeStackSlots (SmallVectorImpl<AllocStackInst *> &AllocStacks,
240
295
SmallVectorImpl<SILInstruction *> &FuncExits);
241
296
242
297
// / Merge alloc_stack instructions if possible and hoist them to the entry
243
298
// / block.
244
- void mergeSlots ();
299
+ SILAnalysis::InvalidationKind mergeSlots (DominanceInfo *domToUpdate );
245
300
};
246
301
} // end anonymous namespace
247
302
@@ -264,7 +319,10 @@ MergeStackSlots::MergeStackSlots(SmallVectorImpl<AllocStackInst *> &AllocStacks,
264
319
265
320
// / Merge alloc_stack instructions if possible and hoist them to the entry
266
321
// / block.
267
- void MergeStackSlots::mergeSlots () {
322
+ SILAnalysis::InvalidationKind
323
+ MergeStackSlots::mergeSlots (DominanceInfo *DomToUpdate) {
324
+ auto Result = SILAnalysis::InvalidationKind::Instructions;
325
+
268
326
for (auto &PartitionOfOneType : PartitionByType) {
269
327
Liveness Live (PartitionOfOneType);
270
328
@@ -312,11 +370,26 @@ void MergeStackSlots::mergeSlots() {
312
370
// Assign stack locations to disjoint partition hoisting alloc_stacks to the
313
371
// entry block at the same time.
314
372
for (auto &Par : DisjointPartitions) {
315
- Par.assignStackLocation (FunctionExits);
373
+ Par.assignStackLocation (FunctionExits, DebugValueToBreakBlocksAt );
316
374
}
317
375
}
318
- }
319
376
377
+ // Now that we have finished merging slots/hoisting, break any blocks that we
378
+ // need to.
379
+ if (!DebugValueToBreakBlocksAt.empty ()) {
380
+ auto &Mod = DebugValueToBreakBlocksAt.front ()->getModule ();
381
+ SILBuilderContext Context (Mod);
382
+ do {
383
+ auto *Next = DebugValueToBreakBlocksAt.pop_back_val ();
384
+ splitBasicBlockAndBranch (Context, Next->getNextInstruction (), DomToUpdate,
385
+ nullptr );
386
+ } while (!DebugValueToBreakBlocksAt.empty ());
387
+
388
+ Result = SILAnalysis::InvalidationKind::BranchesAndInstructions;
389
+ }
390
+
391
+ return Result;
392
+ }
320
393
321
394
namespace {
322
395
// / Hoist alloc_stack instructions to the entry block and merge them.
@@ -329,13 +402,20 @@ class HoistAllocStack {
329
402
SmallVector<AllocStackInst *, 16 > AllocStackToHoist;
330
403
SmallVector<SILInstruction *, 8 > FunctionExits;
331
404
405
+ Optional<SILAnalysis::InvalidationKind> InvalidationKind = None;
406
+
407
+ DominanceInfo *DomInfoToUpdate = nullptr ;
408
+
332
409
public:
333
410
HoistAllocStack (SILFunction *F, irgen::IRGenModule &Mod)
334
411
: F(F), IRGenMod(Mod) {}
335
412
336
- // / Try to hoist generic alloc_stack instructions to the entry block.
337
- // / Returns true if the function was changed.
338
- bool run ();
413
+ // / Try to hoist generic alloc_stack instructions to the entry block. Returns
414
+ // / none if the function was not changed. Otherwise, returns the analysis
415
+ // / invalidation kind to use if the function was changed.
416
+ Optional<SILAnalysis::InvalidationKind> run ();
417
+
418
+ void setDominanceToUpdate (DominanceInfo *DI) { DomInfoToUpdate = DI; }
339
419
340
420
private:
341
421
// / Collect generic alloc_stack instructions that can be moved to the entry
@@ -395,37 +475,38 @@ void HoistAllocStack::collectHoistableInstructions() {
395
475
// / Hoist the alloc_stack instructions to the entry block and sink the
396
476
// / dealloc_stack instructions to the function exists.
397
477
void HoistAllocStack::hoist () {
398
-
399
478
if (SILUseStackSlotMerging) {
400
479
MergeStackSlots Merger (AllocStackToHoist, FunctionExits);
401
- Merger.mergeSlots ();
402
- } else {
403
- // Hoist alloc_stacks to the entry block and delete dealloc_stacks.
404
- auto *EntryBB = F->getEntryBlock ();
405
- for (auto *AllocStack : AllocStackToHoist) {
406
- // Insert at the beginning of the entry block.
407
- AllocStack->moveFront (EntryBB);
408
- // Delete dealloc_stacks.
409
- eraseDeallocStacks (AllocStack);
410
- }
411
- // Insert dealloc_stack in the exit blocks.
412
- for (auto *AllocStack : AllocStackToHoist) {
413
- insertDeallocStackAtEndOf (FunctionExits, AllocStack);
414
- }
480
+ InvalidationKind = Merger.mergeSlots (DomInfoToUpdate);
481
+ return ;
482
+ }
483
+
484
+ // Hoist alloc_stacks to the entry block and delete dealloc_stacks.
485
+ auto *EntryBB = F->getEntryBlock ();
486
+ for (auto *AllocStack : AllocStackToHoist) {
487
+ // Insert at the beginning of the entry block.
488
+ AllocStack->moveFront (EntryBB);
489
+ // Delete dealloc_stacks.
490
+ eraseDeallocStacks (AllocStack);
491
+ InvalidationKind = SILAnalysis::InvalidationKind::Instructions;
492
+ }
493
+ // Insert dealloc_stack in the exit blocks.
494
+ for (auto *AllocStack : AllocStackToHoist) {
495
+ insertDeallocStackAtEndOf (FunctionExits, AllocStack);
415
496
}
416
497
}
417
498
418
499
// / Try to hoist generic alloc_stack instructions to the entry block.
419
500
// / Returns true if the function was changed.
420
- bool HoistAllocStack::run () {
501
+ Optional<SILAnalysis::InvalidationKind> HoistAllocStack::run () {
421
502
collectHoistableInstructions ();
422
503
423
504
// Nothing to hoist?
424
505
if (AllocStackToHoist.empty ())
425
- return false ;
506
+ return {} ;
426
507
427
508
hoist ();
428
- return true ;
509
+ return InvalidationKind ;
429
510
}
430
511
431
512
namespace {
@@ -434,9 +515,20 @@ class AllocStackHoisting : public SILFunctionTransform {
434
515
auto *F = getFunction ();
435
516
auto *Mod = getIRGenModule ();
436
517
assert (Mod && " This pass must be run as part of an IRGen pipeline" );
437
- bool Changed = HoistAllocStack (F, *Mod).run ();
438
- if (Changed) {
439
- PM->invalidateAnalysis (F, SILAnalysis::InvalidationKind::Instructions);
518
+
519
+ HoistAllocStack Hoist (F, *Mod);
520
+
521
+ // Update DomInfo when breaking. We don't use loop info right now this late,
522
+ // so we don't need to do that.
523
+ auto *DA = getAnalysis<DominanceAnalysis>();
524
+ if (DA->hasFunctionInfo (F))
525
+ Hoist.setDominanceToUpdate (DA->get (F));
526
+
527
+ auto InvalidationKind = Hoist.run ();
528
+
529
+ if (InvalidationKind) {
530
+ AnalysisPreserver preserveDominance (DA);
531
+ PM->invalidateAnalysis (F, *InvalidationKind);
440
532
}
441
533
}
442
534
};
0 commit comments