Skip to content

Commit 18c711a

Browse files
authored
Merge pull request #8411 from eeckstein/box2stack
2 parents 72bc61c + 43ddc2a commit 18c711a

14 files changed

+817
-77
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
//===--- StackNesting.h - Utility for stack nesting -------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SILOPTIMIZER_UTILS_STACKNESTING_H
14+
#define SWIFT_SILOPTIMIZER_UTILS_STACKNESTING_H
15+
16+
#include "swift/SIL/SILInstruction.h"
17+
#include "llvm/ADT/SmallBitVector.h"
18+
19+
#include <vector>
20+
21+
namespace swift {
22+
23+
/// A utility to correct the nesting of stack allocating/deallocating
24+
/// instructions.
25+
///
26+
/// This utility is useful for optimizations which create stack allocations
27+
/// without caring about correct nesting with existing allocations. This may
28+
/// result in code like
29+
/// \code
30+
/// %1 = alloc_stack
31+
/// ...
32+
/// %2 = alloc_stack
33+
/// ...
34+
/// dealloc_stack %1
35+
/// ...
36+
/// dealloc_stack %2
37+
/// \endcode
38+
///
39+
/// The StackNesting utility is able to correct the code:
40+
/// \code
41+
/// %1 = alloc_stack
42+
/// ...
43+
/// %2 = alloc_stack
44+
/// ...
45+
/// dealloc_stack %2
46+
/// dealloc_stack %1
47+
/// \endcode
48+
///
49+
class StackNesting {
50+
51+
typedef llvm::SmallBitVector BitVector;
52+
53+
/// Data stored for each block (actually for each block which is not dead).
54+
struct BlockInfo {
55+
56+
/// Back-link to the block.
57+
SILBasicBlock *Block;
58+
59+
/// The cached list of successors.
60+
llvm::SmallVector<BlockInfo *, 8> Successors;
61+
62+
/// The list of stack allocating/deallocating instructions in the block.
63+
llvm::SmallVector<SILInstruction *, 8> StackInsts;
64+
65+
/// The bit-set of alive stack locations at the block entry.
66+
BitVector AliveStackLocsAtEntry;
67+
68+
/// True if there is a path from this block to a function-exit.
69+
///
70+
/// In other words: this block does not end in an unreachable-instruction.
71+
/// This flag is only used for verifying that the lifetime of a stack
72+
/// location does not end at the end of a block.
73+
bool ExitReachable = false;
74+
75+
BlockInfo(SILBasicBlock *Block) : Block(Block) { }
76+
};
77+
78+
/// Data stored for each stack location (= allocation).
79+
///
80+
/// Each stack location is allocated by a single allocation instruction.
81+
struct StackLoc {
82+
StackLoc(SILInstruction *Alloc) : Alloc(Alloc) { }
83+
84+
/// Back-link to the allocation instruction.
85+
SILInstruction *Alloc;
86+
87+
/// Bit-set which represents all alive locations at this allocation.
88+
/// It obviously includes this location itself. And it includes all "outer"
89+
/// locations which surround this location.
90+
BitVector AliveLocs;
91+
};
92+
93+
/// Mapping from stack allocations (= locations) to bit numbers.
94+
llvm::DenseMap<SILInstruction *, unsigned> StackLoc2BitNumbers;
95+
96+
/// The list of stack locations. The index into this array is also the bit
97+
/// number in the bit-sets.
98+
llvm::SmallVector<StackLoc, 8> StackLocs;
99+
100+
/// Block data for all (non-dead) blocks.
101+
std::vector<BlockInfo> BlockInfos;
102+
103+
public:
104+
105+
/// The possible return values of correctStackNesting().
106+
enum class Changes {
107+
/// No changes are made.
108+
None,
109+
110+
/// Only instructions were inserted or deleted.
111+
Instructions,
112+
113+
/// Instructions were inserted or deleted and new blocks were inserted.
114+
CFG
115+
};
116+
117+
StackNesting() { }
118+
119+
/// Performs correction of stack nesting by moving stack-deallocation
120+
/// instructions down the control flow.
121+
///
122+
/// Returns the status of what changes were made.
123+
Changes correctStackNesting(SILFunction *F);
124+
125+
/// For debug dumping.
126+
void dump() const;
127+
128+
static void dumpBits(const BitVector &Bits);
129+
130+
private:
131+
/// Initializes the data structures.
132+
void setup(SILFunction *F);
133+
134+
/// Solves the dataflow problem.
135+
///
136+
/// Returns true if there is a nesting of locations in any way, which can
137+
/// potentially in the wrong order.
138+
bool solve();
139+
140+
/// Insert deallocation instructions for all locations which are alive before
141+
/// the InsertionPoint (AliveBefore) but not alive after the InsertionPoint
142+
/// (AliveAfter).
143+
///
144+
/// Returns true if any deallocations were inserted.
145+
bool insertDeallocs(const BitVector &AliveBefore, const BitVector &AliveAfter,
146+
SILInstruction *InsertionPoint);
147+
148+
/// Modifies the SIL to end up with a correct stack nesting.
149+
///
150+
/// Returns the status of what changes were made.
151+
Changes adaptDeallocs();
152+
};
153+
154+
} // end namespace swift
155+
156+
#endif // SWIFT_SILOPTIMIZER_UTILS_STACKNESTING_H

lib/SILOptimizer/Transforms/AllocBoxToStack.cpp

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/SIL/SILCloner.h"
2020
#include "swift/SILOptimizer/PassManager/Transforms.h"
2121
#include "swift/SILOptimizer/Utils/Local.h"
22+
#include "swift/SILOptimizer/Utils/StackNesting.h"
2223
#include "llvm/ADT/DenseMap.h"
2324
#include "llvm/ADT/SmallPtrSet.h"
2425
#include "llvm/ADT/SmallSet.h"
@@ -395,8 +396,7 @@ static bool canPromoteAllocBox(AllocBoxInst *ABI,
395396

396397
/// rewriteAllocBoxAsAllocStack - Replace uses of the alloc_box with a
397398
/// new alloc_stack, but do not delete the alloc_box yet.
398-
static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI,
399-
llvm::SmallVectorImpl<TermInst *> &Returns) {
399+
static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
400400
DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI);
401401

402402
llvm::SmallVector<SILInstruction*, 4> FinalReleases;
@@ -405,8 +405,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI,
405405

406406
// Promote this alloc_box to an alloc_stack. Insert the alloc_stack
407407
// at the beginning of the function.
408-
auto &Entry = ABI->getFunction()->front();
409-
SILBuilder BuildAlloc(&Entry, Entry.begin());
408+
SILBuilder BuildAlloc(ABI);
410409
BuildAlloc.setCurrentDebugScope(ABI->getDebugScope());
411410
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
412411
&& "rewriting multi-field box not implemented");
@@ -440,25 +439,18 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI,
440439
.getTypeLowering(ABI->getBoxType()->getFieldType(ABI->getModule(), 0));
441440
auto Loc = CleanupLocation::get(ABI->getLoc());
442441

443-
// For non-trivial types, insert destroys for each final release-like
444-
// instruction we found that isn't an explicit dealloc_box.
445-
if (!Lowering.isTrivial()) {
446-
for (auto LastRelease : FinalReleases) {
447-
if (isa<DeallocBoxInst>(LastRelease))
448-
continue;
449-
450-
SILBuilderWithScope BuildDestroy(LastRelease);
451-
BuildDestroy.emitDestroyAddrAndFold(Loc, PointerResult);
442+
for (auto LastRelease : FinalReleases) {
443+
SILBuilderWithScope Builder(LastRelease);
444+
if (!isa<DeallocBoxInst>(LastRelease)&& !Lowering.isTrivial()) {
445+
// For non-trivial types, insert destroys for each final release-like
446+
// instruction we found that isn't an explicit dealloc_box.
447+
Builder.emitDestroyAddrAndFold(Loc, PointerResult);
452448
}
449+
Builder.createDeallocStack(Loc, ASI);
453450
}
454451

455-
for (auto Return : Returns) {
456-
SILBuilderWithScope BuildDealloc(Return);
457-
BuildDealloc.createDeallocStack(Loc, ASI);
458-
}
459-
460-
// Remove any retain and release instructions. Since all uses of result #1
461-
// are gone, this only walks through uses of result #0 (the retain count
452+
// Remove any retain and release instructions. Since all uses of project_box
453+
// are gone, this only walks through uses of the box itself (the retain count
462454
// pointer).
463455
while (!ABI->use_empty()) {
464456
auto *User = (*ABI->use_begin())->getUser();
@@ -825,7 +817,6 @@ rewritePartialApplies(llvm::SmallVectorImpl<Operand *> &PromotedOperands,
825817
static unsigned
826818
rewritePromotedBoxes(llvm::SmallVectorImpl<AllocBoxInst *> &Promoted,
827819
llvm::SmallVectorImpl<Operand *> &PromotedOperands,
828-
llvm::SmallVectorImpl<TermInst *> &Returns,
829820
bool &CFGChanged) {
830821
// First we'll rewrite any partial applies that we can to remove the
831822
// box container pointer from the operands.
@@ -835,7 +826,7 @@ rewritePromotedBoxes(llvm::SmallVectorImpl<AllocBoxInst *> &Promoted,
835826
auto rend = Promoted.rend();
836827
for (auto I = Promoted.rbegin(); I != rend; ++I) {
837828
auto *ABI = *I;
838-
if (rewriteAllocBoxAsAllocStack(ABI, Returns)) {
829+
if (rewriteAllocBoxAsAllocStack(ABI)) {
839830
++Count;
840831
ABI->eraseFromParent();
841832
}
@@ -849,13 +840,8 @@ class AllocBoxToStack : public SILFunctionTransform {
849840
void run() override {
850841
llvm::SmallVector<AllocBoxInst *, 8> Promotable;
851842
llvm::SmallVector<Operand *, 8> PromotedOperands;
852-
llvm::SmallVector<TermInst *, 8> Returns;
853843

854844
for (auto &BB : *getFunction()) {
855-
auto *Term = BB.getTerminator();
856-
if (Term->isFunctionExiting())
857-
Returns.push_back(Term);
858-
859845
for (auto &I : BB)
860846
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
861847
if (canPromoteAllocBox(ABI, PromotedOperands))
@@ -864,9 +850,14 @@ class AllocBoxToStack : public SILFunctionTransform {
864850

865851
if (!Promotable.empty()) {
866852
bool CFGChanged = false;
867-
auto Count = rewritePromotedBoxes(Promotable, PromotedOperands, Returns,
853+
auto Count = rewritePromotedBoxes(Promotable, PromotedOperands,
868854
CFGChanged);
869855
NumStackPromoted += Count;
856+
if (Count) {
857+
StackNesting SN;
858+
if (SN.correctStackNesting(getFunction()) == StackNesting::Changes::CFG)
859+
CFGChanged = true;
860+
}
870861

871862
invalidateAnalysis(CFGChanged ?
872863
SILAnalysis::InvalidationKind::FunctionBody :

lib/SILOptimizer/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ set(UTILS_SOURCES
1313
Utils/SILInliner.cpp
1414
Utils/SILSSAUpdater.cpp
1515
Utils/SpecializationMangler.cpp
16+
Utils/StackNesting.cpp
1617
PARENT_SCOPE)
1718

0 commit comments

Comments
 (0)