Skip to content

Commit 68962f6

Browse files
committed
Merge pull request #2099 from eeckstein/stack_promotion
Re-instate "StackPromotion: Ignore unreachable blocks in post-dominat…
2 parents be78603 + 1eab8aa commit 68962f6

File tree

2 files changed

+250
-26
lines changed

2 files changed

+250
-26
lines changed

lib/SILOptimizer/Transforms/StackPromotion.cpp

Lines changed: 175 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
1818
#include "swift/SIL/SILArgument.h"
1919
#include "swift/SIL/SILBuilder.h"
20+
#include "swift/SIL/CFG.h"
21+
#include "llvm/Support/GenericDomTree.h"
22+
#include "llvm/Support/GenericDomTreeConstruction.h"
2023
#include "llvm/ADT/Statistic.h"
2124

2225
STATISTIC(NumStackPromoted, "Number of objects promoted to the stack");
@@ -48,9 +51,23 @@ class StackPromoter {
4851
SILFunction *F;
4952
EscapeAnalysis::ConnectionGraph *ConGraph;
5053
DominanceInfo *DT;
51-
PostDominanceInfo *PDT;
5254
EscapeAnalysis *EA;
5355

56+
// We use our own post-dominator tree instead of PostDominatorAnalysis,
57+
// because we ignore unreachable blocks (actually all unreachable sub-graphs).
58+
// Example:
59+
// |
60+
// bb1
61+
// / \
62+
// unreachable bb2
63+
// \
64+
//
65+
// We want to get bb2 as immediate post-domiator of bb1. This is not the case
66+
// with the regualar post-dominator tree.
67+
llvm::DominatorTreeBase<SILBasicBlock> PostDomTree;
68+
69+
bool PostDomTreeValid;
70+
5471
// Pseudo-functions for (de-)allocating array buffers on the stack.
5572

5673
SILFunction *BufferAllocFunc = nullptr;
@@ -120,30 +137,54 @@ class StackPromoter {
120137
SILInstruction *&AllocInsertionPoint,
121138
SILInstruction *&DeallocInsertionPoint);
122139

140+
/// Returns the place where to insert the deallocation.
141+
/// Returns null if this doesn't succeed or, in case \p RestartPoint is set,
142+
/// a new iteration should be triggered.
143+
SILInstruction *findDeallocPoint(SILInstruction *StartInst,
144+
SILInstruction *&RestartPoint,
145+
EscapeAnalysis::CGNode *Node,
146+
int NumUsePointsToFind);
147+
123148
bool strictlyDominates(SILBasicBlock *A, SILBasicBlock *B) {
124149
return A != B && DT->dominates(A, B);
125150
}
126151

127152
bool strictlyPostDominates(SILBasicBlock *A, SILBasicBlock *B) {
128-
return A != B && PDT->dominates(A, B);
153+
calculatePostDomTree();
154+
return A != B && PostDomTree.dominates(A, B);
155+
}
156+
157+
bool postDominates(SILBasicBlock *A, SILBasicBlock *B) {
158+
calculatePostDomTree();
159+
return PostDomTree.dominates(A, B);
129160
}
130161

131162
SILBasicBlock *getImmediatePostDom(SILBasicBlock *BB) {
132-
auto *Node = PDT->getNode(BB);
163+
calculatePostDomTree();
164+
auto *Node = PostDomTree.getNode(BB);
133165
if (!Node)
134166
return nullptr;
135167
auto *IDomNode = Node->getIDom();
136168
if (!IDomNode)
137169
return nullptr;
138170
return IDomNode->getBlock();
139171
}
172+
173+
void calculatePostDomTree() {
174+
if (!PostDomTreeValid) {
175+
// The StackPromoter acts as a "graph" for which the post-dominator-tree
176+
// is calculated.
177+
PostDomTree.recalculate(*this);
178+
PostDomTreeValid = true;
179+
}
180+
}
140181

141182
public:
142183

143184
StackPromoter(SILFunction *F, EscapeAnalysis::ConnectionGraph *ConGraph,
144-
DominanceInfo *DT, PostDominanceInfo *PDT,
145-
EscapeAnalysis *EA) :
146-
F(F), ConGraph(ConGraph), DT(DT), PDT(PDT), EA(EA) { }
185+
DominanceInfo *DT, EscapeAnalysis *EA) :
186+
F(F), ConGraph(ConGraph), DT(DT), EA(EA), PostDomTree(true),
187+
PostDomTreeValid(false) { }
147188

148189
/// What did the optimization change?
149190
enum class ChangeState {
@@ -152,6 +193,8 @@ class StackPromoter {
152193
Calls
153194
};
154195

196+
SILFunction *getFunction() const { return F; }
197+
155198
/// The main entry point for the optimization.
156199
ChangeState promote();
157200
};
@@ -284,6 +327,87 @@ SILFunction *StackPromoter::getBufferDeallocFunc(SILFunction *OrigFunc,
284327
return BufferDeallocFunc;
285328
}
286329

330+
namespace {
331+
332+
/// Iterator which iterates over all basic blocks of a function which are not
333+
/// terminated by an unreachable inst.
334+
class NonUnreachableBlockIter :
335+
public std::iterator<std::forward_iterator_tag, SILBasicBlock, ptrdiff_t> {
336+
337+
SILFunction::iterator BaseIterator;
338+
SILFunction::iterator End;
339+
340+
void skipUnreachables() {
341+
while (true) {
342+
if (BaseIterator == End)
343+
return;
344+
if (!isa<UnreachableInst>(BaseIterator->getTerminator()))
345+
return;
346+
BaseIterator++;
347+
}
348+
}
349+
350+
public:
351+
NonUnreachableBlockIter(SILFunction::iterator BaseIterator,
352+
SILFunction::iterator End) :
353+
BaseIterator(BaseIterator), End(End) {
354+
skipUnreachables();
355+
}
356+
357+
NonUnreachableBlockIter() = default;
358+
359+
SILBasicBlock &operator*() const { return *BaseIterator; }
360+
SILBasicBlock &operator->() const { return *BaseIterator; }
361+
362+
NonUnreachableBlockIter &operator++() {
363+
BaseIterator++;
364+
skipUnreachables();
365+
return *this;
366+
}
367+
368+
NonUnreachableBlockIter operator++(int unused) {
369+
NonUnreachableBlockIter Copy = *this;
370+
++*this;
371+
return Copy;
372+
}
373+
374+
friend bool operator==(NonUnreachableBlockIter lhs,
375+
NonUnreachableBlockIter rhs) {
376+
return lhs.BaseIterator == rhs.BaseIterator;
377+
}
378+
friend bool operator!=(NonUnreachableBlockIter lhs,
379+
NonUnreachableBlockIter rhs) {
380+
return !(lhs == rhs);
381+
}
382+
};
383+
}
384+
385+
namespace llvm {
386+
387+
/// Use the StackPromoter as a wrapper for the function. It holds the list of
388+
/// basic blocks excluding all unreachable blocks.
389+
template <> struct GraphTraits<StackPromoter *>
390+
: public GraphTraits<swift::SILBasicBlock*> {
391+
typedef StackPromoter *GraphType;
392+
393+
static NodeType *getEntryNode(GraphType SP) {
394+
return &SP->getFunction()->front();
395+
}
396+
397+
typedef NonUnreachableBlockIter nodes_iterator;
398+
static nodes_iterator nodes_begin(GraphType SP) {
399+
return nodes_iterator(SP->getFunction()->begin(), SP->getFunction()->end());
400+
}
401+
static nodes_iterator nodes_end(GraphType SP) {
402+
return nodes_iterator(SP->getFunction()->end(), SP->getFunction()->end());
403+
}
404+
static unsigned size(GraphType SP) {
405+
return std::distance(nodes_begin(SP), nodes_end(SP));
406+
}
407+
};
408+
409+
}
410+
287411
bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
288412
SILInstruction *&AllocInsertionPoint,
289413
SILInstruction *&DeallocInsertionPoint) {
@@ -311,13 +435,42 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
311435
return false;
312436
}
313437

438+
// Try to find the point where to insert the deallocation.
439+
// This might need more than one try in case we need to move the allocation
440+
// out of a stack-alloc-dealloc pair. See findDeallocPoint().
441+
SILInstruction *StartInst = AI;
442+
for (;;) {
443+
SILInstruction *RestartPoint = nullptr;
444+
DeallocInsertionPoint = findDeallocPoint(StartInst, RestartPoint, Node,
445+
NumUsePointsToFind);
446+
if (DeallocInsertionPoint)
447+
return true;
448+
449+
if (!RestartPoint)
450+
return false;
451+
452+
// Moving a buffer allocation call is not trivial because we would need to
453+
// move all the parameter calculations as well. So we just don't do it.
454+
if (!isa<AllocRefInst>(AI))
455+
return false;
456+
457+
// Retry with moving the allocation up.
458+
AllocInsertionPoint = RestartPoint;
459+
StartInst = RestartPoint;
460+
}
461+
}
462+
463+
SILInstruction *StackPromoter::findDeallocPoint(SILInstruction *StartInst,
464+
SILInstruction *&RestartPoint,
465+
EscapeAnalysis::CGNode *Node,
466+
int NumUsePointsToFind) {
314467
// In the following we check two requirements for stack promotion:
315468
// 1) Are all uses in the same control region as the alloc? E.g. if the
316469
// allocation is in a loop then there may not be any uses of the object
317470
// outside the loop.
318471
// 2) We need to find an insertion place for the deallocation so that it
319472
// preserves a properly nested stack allocation-deallocation structure.
320-
SILBasicBlock *StartBlock = AI->getParent();
473+
SILBasicBlock *StartBlock = StartInst->getParent();
321474

322475
// The block where we assume we can insert the deallocation.
323476
SILBasicBlock *EndBlock = StartBlock;
@@ -336,7 +489,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
336489
if (BB == StartBlock) {
337490
// In the first block we start at the allocation instruction and not at
338491
// the begin of the block.
339-
Iter = AI->getIterator();
492+
Iter = StartInst->getIterator();
340493
} else {
341494
// Track all uses in the block arguments.
342495
for (SILArgument *BBArg : BB->getBBArgs()) {
@@ -359,7 +512,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
359512
while (!strictlyPostDominates(EndBlock, Pred)) {
360513
EndBlock = getImmediatePostDom(EndBlock);
361514
if (!EndBlock)
362-
return false;
515+
return nullptr;
363516
}
364517
}
365518
Iter = BB->begin();
@@ -370,8 +523,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
370523
SILInstruction &I = *Iter++;
371524
if (BB == EndBlock && StackDepth == 0 && NumUsePointsToFind == 0) {
372525
// We found a place to insert the stack deallocation.
373-
DeallocInsertionPoint = &I;
374-
return true;
526+
return &I;
375527
}
376528
if (I.isAllocatingStack()) {
377529
StackDepth++;
@@ -388,16 +540,17 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
388540
//
389541
// In this case we can move the alloc_ref before the alloc_stack
390542
// to fix the nesting.
391-
if (!isa<AllocRefInst>(AI))
392-
return false;
393543
auto *Alloc = dyn_cast<SILInstruction>(I.getOperand(0));
394544
if (!Alloc)
395-
return false;
545+
return nullptr;
546+
396547
// This should always be the case, but let's be on the safe side.
397-
if (!PDT->dominates(StartBlock, Alloc->getParent()))
398-
return false;
399-
AllocInsertionPoint = Alloc;
400-
StackDepth++;
548+
if (!postDominates(StartBlock, Alloc->getParent()))
549+
return nullptr;
550+
551+
// Trigger another iteration with a new start point;
552+
RestartPoint = Alloc;
553+
return nullptr;
401554
}
402555
StackDepth--;
403556
}
@@ -422,7 +575,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
422575
// dealloc_stack %1 // this is the new EndBlock
423576
EndBlock = getImmediatePostDom(EndBlock);
424577
if (!EndBlock)
425-
return false;
578+
return nullptr;
426579
}
427580
// Again, it's important that the EndBlock is the first in the WorkList.
428581
WorkList.insert(EndBlock, -1);
@@ -441,7 +594,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
441594
// cond_br ..., loop, exit
442595
// exit:
443596
// use(%container)
444-
return false;
597+
return nullptr;
445598
}
446599
WorkList.insert(Succ, StackDepth);
447600
}
@@ -466,20 +619,18 @@ class StackPromotion : public SILFunctionTransform {
466619

467620
auto *EA = PM->getAnalysis<EscapeAnalysis>();
468621
auto *DA = PM->getAnalysis<DominanceAnalysis>();
469-
auto *PDA = PM->getAnalysis<PostDominanceAnalysis>();
470622

471623
SILFunction *F = getFunction();
472624
if (auto *ConGraph = EA->getConnectionGraph(F)) {
473-
StackPromoter promoter(F, ConGraph, DA->get(F), PDA->get(F), EA);
625+
StackPromoter promoter(F, ConGraph, DA->get(F), EA);
474626
switch (promoter.promote()) {
475627
case StackPromoter::ChangeState::None:
476628
break;
477629
case StackPromoter::ChangeState::Insts:
478630
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
479631
break;
480-
case StackPromoter::ChangeState::Calls: {
632+
case StackPromoter::ChangeState::Calls:
481633
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
482-
}
483634
break;
484635
}
485636
}

0 commit comments

Comments
 (0)