Skip to content

Commit 7a2ca23

Browse files
committed
Fix post-dominator tree in stack promotion
StackPromotion: Ignore unreachable blocks in post-dominator tree.
2 parents 4c55e8d + 3d050f7 commit 7a2ca23

File tree

3 files changed

+169
-12
lines changed

3 files changed

+169
-12
lines changed

lib/SILOptimizer/Transforms/StackPromotion.cpp

Lines changed: 125 additions & 11 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;
@@ -125,25 +142,41 @@ class StackPromoter {
125142
}
126143

127144
bool strictlyPostDominates(SILBasicBlock *A, SILBasicBlock *B) {
128-
return A != B && PDT->dominates(A, B);
145+
calculatePostDomTree();
146+
return A != B && PostDomTree.dominates(A, B);
147+
}
148+
149+
bool postDominates(SILBasicBlock *A, SILBasicBlock *B) {
150+
calculatePostDomTree();
151+
return PostDomTree.dominates(A, B);
129152
}
130153

131154
SILBasicBlock *getImmediatePostDom(SILBasicBlock *BB) {
132-
auto *Node = PDT->getNode(BB);
155+
calculatePostDomTree();
156+
auto *Node = PostDomTree.getNode(BB);
133157
if (!Node)
134158
return nullptr;
135159
auto *IDomNode = Node->getIDom();
136160
if (!IDomNode)
137161
return nullptr;
138162
return IDomNode->getBlock();
139163
}
164+
165+
void calculatePostDomTree() {
166+
if (!PostDomTreeValid) {
167+
// The StackPromoter acts as a "graph" for which the post-dominator-tree
168+
// is calculated.
169+
PostDomTree.recalculate(*this);
170+
PostDomTreeValid = true;
171+
}
172+
}
140173

141174
public:
142175

143176
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) { }
177+
DominanceInfo *DT, EscapeAnalysis *EA) :
178+
F(F), ConGraph(ConGraph), DT(DT), EA(EA), PostDomTree(true),
179+
PostDomTreeValid(false) { }
147180

148181
/// What did the optimization change?
149182
enum class ChangeState {
@@ -152,6 +185,8 @@ class StackPromoter {
152185
Calls
153186
};
154187

188+
SILFunction *getFunction() const { return F; }
189+
155190
/// The main entry point for the optimization.
156191
ChangeState promote();
157192
};
@@ -284,6 +319,87 @@ SILFunction *StackPromoter::getBufferDeallocFunc(SILFunction *OrigFunc,
284319
return BufferDeallocFunc;
285320
}
286321

322+
namespace {
323+
324+
/// Iterator which iterates over all basic blocks of a function which are not
325+
/// terminated by an unreachable inst.
326+
class NonUnreachableBlockIter :
327+
public std::iterator<std::forward_iterator_tag, SILBasicBlock, ptrdiff_t> {
328+
329+
SILFunction::iterator BaseIterator;
330+
SILFunction::iterator End;
331+
332+
void skipUnreachables() {
333+
while (true) {
334+
if (BaseIterator == End)
335+
return;
336+
if (!isa<UnreachableInst>(BaseIterator->getTerminator()))
337+
return;
338+
BaseIterator++;
339+
}
340+
}
341+
342+
public:
343+
NonUnreachableBlockIter(SILFunction::iterator BaseIterator,
344+
SILFunction::iterator End) :
345+
BaseIterator(BaseIterator), End(End) {
346+
skipUnreachables();
347+
}
348+
349+
NonUnreachableBlockIter() = default;
350+
351+
SILBasicBlock &operator*() const { return *BaseIterator; }
352+
SILBasicBlock &operator->() const { return *BaseIterator; }
353+
354+
NonUnreachableBlockIter &operator++() {
355+
BaseIterator++;
356+
skipUnreachables();
357+
return *this;
358+
}
359+
360+
NonUnreachableBlockIter operator++(int unused) {
361+
NonUnreachableBlockIter Copy = *this;
362+
++*this;
363+
return Copy;
364+
}
365+
366+
friend bool operator==(NonUnreachableBlockIter lhs,
367+
NonUnreachableBlockIter rhs) {
368+
return lhs.BaseIterator == rhs.BaseIterator;
369+
}
370+
friend bool operator!=(NonUnreachableBlockIter lhs,
371+
NonUnreachableBlockIter rhs) {
372+
return !(lhs == rhs);
373+
}
374+
};
375+
}
376+
377+
namespace llvm {
378+
379+
/// Use the StackPromoter as a wrapper for the function. It holds the list of
380+
/// basic blocks excluding all unreachable blocks.
381+
template <> struct GraphTraits<StackPromoter *>
382+
: public GraphTraits<swift::SILBasicBlock*> {
383+
typedef StackPromoter *GraphType;
384+
385+
static NodeType *getEntryNode(GraphType SP) {
386+
return &SP->getFunction()->front();
387+
}
388+
389+
typedef NonUnreachableBlockIter nodes_iterator;
390+
static nodes_iterator nodes_begin(GraphType SP) {
391+
return nodes_iterator(SP->getFunction()->begin(), SP->getFunction()->end());
392+
}
393+
static nodes_iterator nodes_end(GraphType SP) {
394+
return nodes_iterator(SP->getFunction()->end(), SP->getFunction()->end());
395+
}
396+
static unsigned size(GraphType SP) {
397+
return std::distance(nodes_begin(SP), nodes_end(SP));
398+
}
399+
};
400+
401+
}
402+
287403
bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
288404
SILInstruction *&AllocInsertionPoint,
289405
SILInstruction *&DeallocInsertionPoint) {
@@ -394,7 +510,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
394510
if (!Alloc)
395511
return false;
396512
// This should always be the case, but let's be on the safe side.
397-
if (!PDT->dominates(StartBlock, Alloc->getParent()))
513+
if (!postDominates(StartBlock, Alloc->getParent()))
398514
return false;
399515
AllocInsertionPoint = Alloc;
400516
StackDepth++;
@@ -466,20 +582,18 @@ class StackPromotion : public SILFunctionTransform {
466582

467583
auto *EA = PM->getAnalysis<EscapeAnalysis>();
468584
auto *DA = PM->getAnalysis<DominanceAnalysis>();
469-
auto *PDA = PM->getAnalysis<PostDominanceAnalysis>();
470585

471586
SILFunction *F = getFunction();
472587
if (auto *ConGraph = EA->getConnectionGraph(F)) {
473-
StackPromoter promoter(F, ConGraph, DA->get(F), PDA->get(F), EA);
588+
StackPromoter promoter(F, ConGraph, DA->get(F), EA);
474589
switch (promoter.promote()) {
475590
case StackPromoter::ChangeState::None:
476591
break;
477592
case StackPromoter::ChangeState::Insts:
478593
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
479594
break;
480-
case StackPromoter::ChangeState::Calls: {
595+
case StackPromoter::ChangeState::Calls:
481596
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
482-
}
483597
break;
484598
}
485599
}

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,7 @@ SILInstruction *ValueLifetimeAnalysis:: findLastUserInBlock(SILBasicBlock *BB) {
11341134
bool ValueLifetimeAnalysis::computeFrontier(Frontier &Fr, Mode mode) {
11351135
bool NoCriticalEdges = true;
11361136

1137-
// Exit-blocks from the lifetime region. The value if live at the end of
1137+
// Exit-blocks from the lifetime region. The value is live at the end of
11381138
// a predecessor block but not in the frontier block itself.
11391139
llvm::SmallSetVector<SILBasicBlock *, 16> FrontierBlocks;
11401140

test/SILOptimizer/stack_promotion.sil

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,49 @@ bb0:
8888
return %t : $()
8989
}
9090

91+
// CHECK-LABEL: sil @promote_with_unreachable_block
92+
// CHECK: [[O:%[0-9]+]] = alloc_ref [stack] $XX
93+
// CHECK: bb1:
94+
// CHECK-NEXT: unreachable
95+
// CHECK: bb2:
96+
// CHECK: strong_release
97+
// CHECK: dealloc_ref [stack] [[O]] : $XX
98+
// CHECK: return
99+
sil @promote_with_unreachable_block : $@convention(thin) () -> Int32 {
100+
bb0:
101+
%o1 = alloc_ref $XX
102+
%f1 = function_ref @xx_init : $@convention(thin) (@guaranteed XX) -> XX
103+
%n1 = apply %f1(%o1) : $@convention(thin) (@guaranteed XX) -> XX
104+
cond_br undef, bb1, bb2
105+
106+
bb1:
107+
unreachable
108+
109+
bb2:
110+
%l1 = ref_element_addr %n1 : $XX, #XX.x
111+
%l2 = load %l1 : $*Int32
112+
strong_release %n1 : $XX
113+
return %l2 : $Int32
114+
}
115+
116+
// CHECK-LABEL: sil @no_return_function
117+
// Just check that we don't crash on this.
118+
// It's a corner case, so we don't care if stack promotion is done or not.
119+
// CHECK: unreachable
120+
sil @no_return_function : $@convention(thin) () -> Int32 {
121+
bb0:
122+
%o1 = alloc_ref $XX
123+
%f1 = function_ref @xx_init : $@convention(thin) (@guaranteed XX) -> XX
124+
%n1 = apply %f1(%o1) : $@convention(thin) (@guaranteed XX) -> XX
125+
br bb1
126+
127+
bb1:
128+
%l1 = ref_element_addr %n1 : $XX, #XX.x
129+
%l2 = load %l1 : $*Int32
130+
strong_release %n1 : $XX
131+
unreachable
132+
}
133+
91134
// CHECK-LABEL: sil @promote_in_loop_with_if
92135
// CHECK: [[O:%[0-9]+]] = alloc_ref [stack] $XX
93136
// CHECK: {{^}}bb4({{.*}}):

0 commit comments

Comments
 (0)