Skip to content

Commit 19b34ee

Browse files
committed
Rewrite StackNesting to be a non-iterative single-pass algorithm.
The previous algorithm was doing an iterative forward data flow analysis followed by a reverse data flow analysis. I suspect the history here is that it was a reverse analysis, and that didn't really work for infinite loops, and so complexity kindof accumulated. The new algorithm is quite straightforward and relies on the allocations being properly jointly post-dominated, just not nested. We simply walk forward through the blocks in consistent-with-dominance order, maintaining the stack of active allocations and deferring deallocations that are improperly nested until we deallocate the allocations above it. The reason I'm doing this, besides it just being a simpler, faster algorithm, is that modeling some of the uses of the async stack allocator properly requires builtins that cannot just be semantically reordered. That should be somewhat easier to handle with the new approach, although really (1) we should not have runtime functions that need this and (2) we're going to need a conservatively-correct solution that's different from this anyway because hoisting allocations is *also* limited in its own way. The test cases that changed are... I don't think the new output is wrong under the current rules that are being enforced, but really we should be enforcing different rules, because it's not really okay to have broken stack nesting in blocks just because they don't lead to an exit. But it's broken coming into StackNesting, and I don't think the rewrite actually makes it worse, so... The thing that concerns me most about the rewritten pass is that it isn't actually validating joint post-dominance on input, so if you give it bad input, it might be a little mystifying to debug the verifier failures.
1 parent 6992276 commit 19b34ee

File tree

4 files changed

+434
-488
lines changed

4 files changed

+434
-488
lines changed

include/swift/SILOptimizer/Utils/StackNesting.h

Lines changed: 4 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ namespace swift {
4545
/// dealloc_stack %1
4646
/// \endcode
4747
///
48+
/// Each allocation must still be properly jointly post-dominated by
49+
/// its deallocations. StackNesting only fixes the nesting of allocations
50+
/// deallocations; it does not insert required deallocations that are
51+
/// missing entirely.
4852
class StackNesting {
49-
5053
public:
5154

5255
/// The possible return values of fixNesting().
@@ -61,122 +64,6 @@ class StackNesting {
6164
CFG
6265
};
6366

64-
private:
65-
typedef SmallBitVector BitVector;
66-
67-
/// Data stored for each block (actually for each block which is not dead).
68-
struct BlockInfo {
69-
/// The list of stack allocating/deallocating instructions in the block.
70-
llvm::SmallVector<SILInstruction *, 8> StackInsts;
71-
72-
/// The bit-set of alive stack locations at the block entry.
73-
BitVector AliveStackLocsAtEntry;
74-
75-
/// The bit-set of alive stack locations at the block exit.
76-
BitVector AliveStackLocsAtExit;
77-
78-
/// Used in the setup function to walk over the CFG.
79-
bool visited = false;
80-
81-
/// True for dead-end blocks, i.e. blocks from which there is no path to
82-
/// a function exit, e.g. blocks which end with `unreachable` or an
83-
/// infinite loop.
84-
bool isDeadEnd = false;
85-
};
86-
87-
/// Data stored for each stack location (= allocation).
88-
///
89-
/// Each stack location is allocated by a single allocation instruction.
90-
struct StackLoc {
91-
StackLoc(SILInstruction *Alloc) : Alloc(Alloc) {}
92-
93-
/// Back-link to the allocation instruction.
94-
SILInstruction *Alloc;
95-
96-
/// Bit-set which represents all alive locations at this allocation.
97-
/// It obviously includes this location itself. And it includes all "outer"
98-
/// locations which surround this location.
99-
BitVector AliveLocs;
100-
};
101-
102-
/// Mapping from stack allocations (= locations) to bit numbers.
103-
llvm::DenseMap<SILInstruction *, unsigned> StackLoc2BitNumbers;
104-
105-
/// The list of stack locations. The index into this array is also the bit
106-
/// number in the bit-sets.
107-
llvm::SmallVector<StackLoc, 8> StackLocs;
108-
109-
BasicBlockData<BlockInfo> BlockInfos;
110-
111-
StackNesting(SILFunction *F) : BlockInfos(F) { }
112-
113-
/// Performs correction of stack nesting by moving stack-deallocation
114-
/// instructions down the control flow.
115-
///
116-
/// Returns the status of what changes were made.
117-
Changes run();
118-
119-
/// For debug dumping.
120-
void dump() const;
121-
122-
static void dumpBits(const BitVector &Bits);
123-
124-
/// Initializes the data structures.
125-
void setup();
126-
127-
/// Solves the dataflow problem.
128-
///
129-
/// Returns true if there is a nesting of locations in any way, which can
130-
/// potentially in the wrong order.
131-
bool solve();
132-
133-
bool analyze() {
134-
setup();
135-
return solve();
136-
}
137-
138-
/// Insert deallocation instructions for all locations which are alive before
139-
/// the InsertionPoint (AliveBefore) but not alive after the InsertionPoint
140-
/// (AliveAfter).
141-
///
142-
/// Returns true if any deallocations were inserted.
143-
bool insertDeallocs(const BitVector &AliveBefore, const BitVector &AliveAfter,
144-
SILInstruction *InsertionPoint,
145-
std::optional<SILLocation> Location);
146-
147-
/// Returns the location bit number for a stack allocation instruction.
148-
int bitNumberForAlloc(SILInstruction *AllocInst) {
149-
assert(AllocInst->isAllocatingStack());
150-
return StackLoc2BitNumbers[AllocInst];
151-
}
152-
153-
/// Returns the location bit number for a stack deallocation instruction.
154-
int bitNumberForDealloc(SILInstruction *DeallocInst) {
155-
assert(DeallocInst->isDeallocatingStack());
156-
auto *AllocInst = getAllocForDealloc(DeallocInst);
157-
return bitNumberForAlloc(AllocInst);
158-
}
159-
160-
/// Returns the stack allocation instruction for a stack deallocation
161-
/// instruction.
162-
SILInstruction *getAllocForDealloc(SILInstruction *Dealloc) const {
163-
SILValue op = Dealloc->getOperand(0);
164-
while (auto *mvi = dyn_cast<MoveValueInst>(op)) {
165-
op = mvi->getOperand();
166-
}
167-
return op->getDefiningInstruction();
168-
}
169-
170-
/// Insert deallocations at block boundaries.
171-
Changes insertDeallocsAtBlockBoundaries();
172-
173-
/// Modifies the SIL to end up with a correct stack nesting.
174-
///
175-
/// Returns the status of what changes were made.
176-
Changes adaptDeallocs();
177-
178-
public:
179-
18067
/// Performs correction of stack nesting by moving stack-deallocation
18168
/// instructions down the control flow.
18269
///

0 commit comments

Comments
 (0)