1212
1313#define DEBUG_TYPE " alloc-stack-hoisting"
1414
15- #include " swift/IRGen/IRGenSILPasses.h"
1615#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"
2019#include " swift/SIL/DebugUtils.h"
20+ #include " swift/SIL/Dominance.h"
21+ #include " swift/SIL/LoopInfo.h"
22+ #include " swift/SIL/SILArgument.h"
2123#include " swift/SIL/SILBuilder.h"
2224#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"
2530
2631#include " IRGenModule.h"
2732#include " NonFixedTypeInfo.h"
@@ -95,7 +100,22 @@ class Partition {
95100 // /
96101 // / This assumes that the live ranges of the alloc_stack instructions are
97102 // / 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+ }
99119};
100120} // end anonymous namespace
101121
@@ -125,21 +145,40 @@ insertDeallocStackAtEndOf(SmallVectorImpl<SILInstruction *> &FunctionExits,
125145
126146// / Hack to workaround a clang LTO bug.
127147LLVM_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+ }
129164 AS->moveFront (BB);
130165}
131166
132167// / Assign a single alloc_stack instruction to all the alloc_stacks in the
133168// / partition.
134169void Partition::assignStackLocation (
135- SmallVectorImpl<SILInstruction *> &FunctionExits) {
170+ SmallVectorImpl<SILInstruction *> &FunctionExits,
171+ SmallVectorImpl<DebugValueInst *> &DebugValueToBreakBlocksAt) {
136172 assert (!Elts.empty () && " Must have a least one location" );
173+ bool hasAtLeastOneMovedElt = hasMovedElt ();
174+
137175 // The assigned location is the first alloc_stack in our partition.
138176 auto *AssignedLoc = Elts[0 ];
139177
140178 // Move this assigned location to the beginning of the entry block.
141179 auto *EntryBB = AssignedLoc->getFunction ()->getEntryBlock ();
142- moveAllocStackToBeginningOfBlock (AssignedLoc, EntryBB);
180+ moveAllocStackToBeginningOfBlock (AssignedLoc, EntryBB, hasAtLeastOneMovedElt,
181+ DebugValueToBreakBlocksAt);
143182
144183 // Erase the dealloc_stacks.
145184 eraseDeallocStacks (AssignedLoc);
@@ -153,6 +192,15 @@ void Partition::assignStackLocation(
153192 if (AssignedLoc == AllocStack) continue ;
154193 eraseDeallocStacks (AllocStack);
155194 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+ }
156204 AllocStack->eraseFromParent ();
157205 }
158206}
@@ -234,14 +282,21 @@ class MergeStackSlots {
234282 SmallVector<Partition, 2 > PartitionByType;
235283 // / The function exits.
236284 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;
237292
238293public:
239294 MergeStackSlots (SmallVectorImpl<AllocStackInst *> &AllocStacks,
240295 SmallVectorImpl<SILInstruction *> &FuncExits);
241296
242297 // / Merge alloc_stack instructions if possible and hoist them to the entry
243298 // / block.
244- void mergeSlots ();
299+ SILAnalysis::InvalidationKind mergeSlots (DominanceInfo *domToUpdate );
245300};
246301} // end anonymous namespace
247302
@@ -264,7 +319,10 @@ MergeStackSlots::MergeStackSlots(SmallVectorImpl<AllocStackInst *> &AllocStacks,
264319
265320// / Merge alloc_stack instructions if possible and hoist them to the entry
266321// / block.
267- void MergeStackSlots::mergeSlots () {
322+ SILAnalysis::InvalidationKind
323+ MergeStackSlots::mergeSlots (DominanceInfo *DomToUpdate) {
324+ auto Result = SILAnalysis::InvalidationKind::Instructions;
325+
268326 for (auto &PartitionOfOneType : PartitionByType) {
269327 Liveness Live (PartitionOfOneType);
270328
@@ -312,11 +370,26 @@ void MergeStackSlots::mergeSlots() {
312370 // Assign stack locations to disjoint partition hoisting alloc_stacks to the
313371 // entry block at the same time.
314372 for (auto &Par : DisjointPartitions) {
315- Par.assignStackLocation (FunctionExits);
373+ Par.assignStackLocation (FunctionExits, DebugValueToBreakBlocksAt );
316374 }
317375 }
318- }
319376
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+ }
320393
321394namespace {
322395// / Hoist alloc_stack instructions to the entry block and merge them.
@@ -329,13 +402,20 @@ class HoistAllocStack {
329402 SmallVector<AllocStackInst *, 16 > AllocStackToHoist;
330403 SmallVector<SILInstruction *, 8 > FunctionExits;
331404
405+ Optional<SILAnalysis::InvalidationKind> InvalidationKind = None;
406+
407+ DominanceInfo *DomInfoToUpdate = nullptr ;
408+
332409public:
333410 HoistAllocStack (SILFunction *F, irgen::IRGenModule &Mod)
334411 : F(F), IRGenMod(Mod) {}
335412
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; }
339419
340420private:
341421 // / Collect generic alloc_stack instructions that can be moved to the entry
@@ -395,37 +475,38 @@ void HoistAllocStack::collectHoistableInstructions() {
395475// / Hoist the alloc_stack instructions to the entry block and sink the
396476// / dealloc_stack instructions to the function exists.
397477void HoistAllocStack::hoist () {
398-
399478 if (SILUseStackSlotMerging) {
400479 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);
415496 }
416497}
417498
418499// / Try to hoist generic alloc_stack instructions to the entry block.
419500// / Returns true if the function was changed.
420- bool HoistAllocStack::run () {
501+ Optional<SILAnalysis::InvalidationKind> HoistAllocStack::run () {
421502 collectHoistableInstructions ();
422503
423504 // Nothing to hoist?
424505 if (AllocStackToHoist.empty ())
425- return false ;
506+ return {} ;
426507
427508 hoist ();
428- return true ;
509+ return InvalidationKind ;
429510}
430511
431512namespace {
@@ -434,9 +515,20 @@ class AllocStackHoisting : public SILFunctionTransform {
434515 auto *F = getFunction ();
435516 auto *Mod = getIRGenModule ();
436517 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);
440532 }
441533 }
442534};
0 commit comments