Skip to content

Commit ac81c4d

Browse files
committed
[optimizer] Teach SIL optimizer that stack nesting should ignore nested stack allocations.
1 parent 2975584 commit ac81c4d

File tree

3 files changed

+78
-25
lines changed

3 files changed

+78
-25
lines changed

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7052,6 +7052,22 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
70527052
}
70537053
}
70547054
}
7055+
7056+
void handleScopeInst(SILInstruction &i) {
7057+
if (!ActiveOps.insert(&i).second) {
7058+
verificationFailure("operation was not ended before re-beginning it",
7059+
&i, nullptr, nullptr);
7060+
}
7061+
}
7062+
7063+
void handleScopeEndingInst(SILInstruction &i) {
7064+
if (auto beginOp = i.getOperand(0)->getDefiningInstruction()) {
7065+
if (!ActiveOps.erase(beginOp)) {
7066+
verificationFailure("operation has already been ended", &i, nullptr,
7067+
nullptr);
7068+
}
7069+
}
7070+
}
70557071
};
70567072

70577073
struct DeadEndRegionState {
@@ -7152,22 +7168,31 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
71527168
}
71537169

71547170
if (i.isAllocatingStack()) {
7155-
if (auto *BAI = dyn_cast<BeginApplyInst>(&i)) {
7156-
state.Stack.push_back(BAI->getCalleeAllocationResult());
7171+
// Nested stack allocations are pushed on the stack. Non-nested stack
7172+
// allocations are treated as active ops so that we can at least
7173+
// verify their joint post-dominance.
7174+
if (i.isStackAllocationNested()) {
7175+
state.Stack.push_back(i.getStackAllocation());
7176+
7177+
// Also track begin_apply as a scope instruction.
7178+
if (isa<BeginApplyInst>(i))
7179+
state.handleScopeInst(i);
71577180
} else {
7158-
state.Stack.push_back(cast<SingleValueInstruction>(&i));
7181+
state.handleScopeInst(i);
71597182
}
7160-
// Not "else if": begin_apply both allocates stack and begins an
7161-
// operation.
7162-
}
7163-
if (i.isDeallocatingStack()) {
7183+
} else if (i.isDeallocatingStack()) {
71647184
SILValue op = i.getOperand(0);
71657185
while (auto *mvi = dyn_cast<MoveValueInst>(op)) {
71667186
op = mvi->getOperand();
71677187
}
71687188

7169-
if (!state.Stack.empty() && op == state.Stack.back()) {
7189+
auto beginInst = op->getDefiningInstruction();
7190+
if (beginInst && !beginInst->isStackAllocationNested()) {
7191+
state.handleScopeEndingInst(i);
7192+
} else if (!state.Stack.empty() && op == state.Stack.back()) {
71707193
state.Stack.pop_back();
7194+
if (beginInst && isa<BeginApplyInst>(beginInst))
7195+
state.handleScopeEndingInst(i);
71717196
} else {
71727197
verificationFailure("deallocating allocation that is not the top of the stack",
71737198
&i, nullptr,
@@ -7178,20 +7203,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
71787203
});
71797204
}
71807205
} else if (isScopeInst(&i)) {
7181-
bool notAlreadyPresent = state.ActiveOps.insert(&i).second;
7182-
require(notAlreadyPresent,
7183-
"operation was not ended before re-beginning it");
7206+
state.handleScopeInst(i);
71847207
} else if (isScopeEndingInst(&i)) {
7185-
if (auto beginOp = i.getOperand(0)->getDefiningInstruction()) {
7186-
bool present = state.ActiveOps.erase(beginOp);
7187-
require(present, "operation has already been ended");
7188-
}
7208+
state.handleScopeEndingInst(i);
71897209
} else if (auto *endBorrow = dyn_cast<EndBorrowInst>(&i)) {
71907210
if (isa<StoreBorrowInst>(endBorrow->getOperand())) {
7191-
if (auto beginOp = i.getOperand(0)->getDefiningInstruction()) {
7192-
bool present = state.ActiveOps.erase(beginOp);
7193-
require(present, "operation has already been ended");
7194-
}
7211+
state.handleScopeEndingInst(i);
71957212
}
71967213
} else if (auto gaci = dyn_cast<GetAsyncContinuationInstBase>(&i)) {
71977214
require(!state.GotAsyncContinuation,

lib/SILOptimizer/Utils/StackNesting.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,9 @@ StackNesting::Changes StackNesting::fixNesting(SILFunction *F) {
579579
// In the formal presentation, the state change is
580580
// state = STATE_PUSH(state, alloc)
581581
if (I->isAllocatingStack()) {
582-
state.allocations.push_back(I);
582+
// Only handle nested stack allocations.
583+
if (I->isStackAllocationNested() == StackAllocationIsNested)
584+
state.allocations.push_back(I);
583585
continue;
584586
}
585587

@@ -594,6 +596,11 @@ StackNesting::Changes StackNesting::fixNesting(SILFunction *F) {
594596
SILInstruction *dealloc = I;
595597
SILInstruction *alloc = getAllocForDealloc(dealloc);
596598

599+
// Since we only processed nested allocations above, ignore deallocations
600+
// from non-nested allocations.
601+
if (alloc->isStackAllocationNested() == StackAllocationIsNotNested)
602+
continue;
603+
597604
#ifndef NDEBUG
598605
if (state.allocations.empty()) {
599606
state.abortForUnknownAllocation(alloc, dealloc);
@@ -657,9 +664,10 @@ StackNesting::Changes StackNesting::fixNesting(SILFunction *F) {
657664
}
658665

659666
namespace swift::test {
660-
static FunctionTest MyNewTest("stack_nesting_fixup",
661-
[](auto &function, auto &arguments, auto &test) {
662-
StackNesting::fixNesting(&function);
663-
function.print(llvm::outs());
664-
});
667+
static FunctionTest StackNestingTests("stack_nesting_fixup",
668+
[](auto &function, auto &arguments,
669+
auto &test) {
670+
StackNesting::fixNesting(&function);
671+
function.print(llvm::outs());
672+
});
665673
} // end namespace swift::test

test/SILOptimizer/stack-nesting.sil

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,31 @@ bb5:
281281
dealloc_stack %d
282282
br bb4
283283
}
284+
285+
// We only re-order b and c. We leave a alone.
286+
// CHECK-LABEL: sil [ossa] @test_simple_non_nested
287+
// CHECK: integer_literal $Builtin.Int32, 1
288+
// CHECK-NEXT: [[A:%.*]] = alloc_stack [non_nested] $Int
289+
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
290+
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
291+
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
292+
// CHECK-NEXT: dealloc_stack [[A]]
293+
// CHECK-NEXT: dealloc_stack [[C]]
294+
// CHECK-NEXT: dealloc_stack [[B]]
295+
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
296+
// CHECK-LABEL: // end sil function 'test_simple_non_nested'
297+
sil [ossa] @test_simple_non_nested : $@convention(thin) (Builtin.Int1) -> () {
298+
bb0(%cond: $Builtin.Int1):
299+
specify_test "stack_nesting_fixup"
300+
integer_literal $Builtin.Int32, 1
301+
%a = alloc_stack [non_nested] $Int
302+
%b = alloc_stack $Int
303+
%c = alloc_stack $Int
304+
integer_literal $Builtin.Int32, 2
305+
dealloc_stack %a
306+
dealloc_stack %b
307+
dealloc_stack %c
308+
integer_literal $Builtin.Int32, 3
309+
%ret = tuple ()
310+
return %ret : $()
311+
}

0 commit comments

Comments
 (0)