Inliner: fix a stack nesting problem when inlining coroutines

Beside fixing the compiler crash, this change also improves the stack-nesting correction mechanisms in the inliners:

* Instead of trying to correct the nesting after each inlining of a callee, correct the nesting once when inlining is finished for a caller function.
This fixes a potential compile time problem, because StackNesting iterates over the whole function.
In worst case this can lead to quadratic behavior in case many begin_apply instructions with overlapping stack locations are inlined.

* Because we are doing it only once for a caller, we can remove the complex logic for checking if it is necessary.
We can just do it unconditionally in case any coroutine gets inlined.
The inliners iterate over all instruction of a function anyway, so this does not increase the computational complexity (StackNesting is roughly linear with the number of instructions).

rdar://problem/47615442
This commit is contained in:
Erik Eckstein
2019-01-31 16:05:40 -08:00
parent 784e7d39ce
commit 767ad5e70a
7 changed files with 70 additions and 157 deletions

View File

@@ -18,75 +18,6 @@
using namespace swift;
bool swift::hasStackDifferencesAt(SILInstruction *start,
InstructionMatcher matcher) {
struct StackStatus {
/// The number of currently-active stack allocations on this path.
/// Because of the stack discipline, we only have to maintain a count.
unsigned depth;
/// Whether we've popped anything off of the stack that was active
/// at the start of the search.
bool hasPops;
bool hasDifferences() const { return hasPops || depth != 0; }
};
SmallPtrSet<SILBasicBlock*, 8> visited;
SmallVector<std::pair<SILInstruction *, StackStatus>, 8> worklist;
worklist.push_back({start, {0, false}});
while (!worklist.empty()) {
// Pull a block and depth off the worklist.
// Visitation order doesn't matter.
auto pair = worklist.pop_back_val();
auto status = pair.second;
auto firstInst = pair.first;
auto block = firstInst->getParent();
for (SILBasicBlock::iterator i(firstInst), e = block->end(); i != e; ++i) {
auto match = matcher(&*i);
// If this is an interesting instruction, check for stack
// differences here.
if (match.matches && status.hasDifferences())
return true;
// If we should halt, just go to the next work-list item, bypassing
// visiting the successors.
if (match.halt)
goto nextWorkListItem; // a labelled continue would be nice
// Otherwise, do stack-depth bookkeeping.
if (i->isAllocatingStack()) {
status.depth++;
} else if (i->isDeallocatingStack()) {
// If the stack depth is already zero, we're deallocating a stack
// allocation that was live into the search.
if (status.depth > 0) {
status.depth--;
} else {
status.hasPops = true;
}
}
}
// Add the successors to the worklist.
for (auto &succ : block->getSuccessors()) {
auto succBlock = succ.getBB();
auto insertResult = visited.insert(succBlock);
if (insertResult.second) {
worklist.push_back({&succBlock->front(), status});
}
}
nextWorkListItem: ;
}
return false;
}
void StackNesting::setup(SILFunction *F) {
SmallVector<BlockInfo *, 8> WorkList;
llvm::DenseMap<SILBasicBlock *, BlockInfo *> BlockMapping;