Skip to content

Commit f61bd58

Browse files
authored
Merge pull request swiftlang#68139 from eeckstein/fix-alloc-stack-hoisting
AllocStackHoisting: fix a quadratic complexity bug when merging stack locations.
2 parents 68e1ba0 + 1a01f87 commit f61bd58

File tree

2 files changed

+77
-23
lines changed

2 files changed

+77
-23
lines changed

lib/IRGen/AllocStackHoisting.cpp

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ static bool isHoistable(AllocStackInst *Inst, irgen::IRGenModule &Mod) {
8888
/// a set of alloc_stack instructions that can be assigned a single stack
8989
/// location.
9090
namespace {
91+
92+
using InstructionIndices = llvm::SmallDenseMap<SILInstruction *, int>;
93+
9194
class Partition {
9295
public:
9396
SmallVector<AllocStackInst *, 4> Elts;
@@ -235,7 +238,8 @@ class Liveness {
235238
/// If they are in the same basic block we scan the basic block to determine
236239
/// whether one dealloc_stack dominates the other alloc_stack. If this is the
237240
/// case the live ranges can't overlap.
238-
bool mayOverlap(AllocStackInst *A, AllocStackInst *B) {
241+
bool mayOverlap(AllocStackInst *A, AllocStackInst *B,
242+
const InstructionIndices &stackInstructionIndices) {
239243
assert(A != B);
240244

241245
// Check that we have a single dealloc_stack user in the same block.
@@ -251,23 +255,15 @@ class Liveness {
251255
// Different basic blocks.
252256
if (A->getParent() != B->getParent())
253257
return false;
254-
bool ALive = false;
255-
bool BLive = false;
256-
for (auto &Inst : *A->getParent()) {
257-
if (A == &Inst) {
258-
ALive = true;
259-
} else if (singleDeallocA == &Inst) {
260-
ALive = false;
261-
} else if (B == &Inst) {
262-
BLive = true;
263-
} else if (singleDeallocB == &Inst) {
264-
BLive = false;
265-
}
266258

267-
if (ALive && BLive)
268-
return true;
269-
}
270-
return false;
259+
// Within the same basic block we can use the consecutive instruction indices
260+
// to check for overlapping.
261+
if (stackInstructionIndices.lookup(A) > stackInstructionIndices.lookup(singleDeallocB))
262+
return false;
263+
if (stackInstructionIndices.lookup(B) > stackInstructionIndices.lookup(singleDeallocA))
264+
return false;
265+
266+
return true;
271267
}
272268
};
273269
} // end anonymous namespace
@@ -285,6 +281,11 @@ class MergeStackSlots {
285281
SmallVector<Partition, 2> PartitionByType;
286282
/// The function exits.
287283
SmallVectorImpl<SILInstruction *> &FunctionExits;
284+
285+
/// Consecutive indices for all `alloc_stack` and `dealloc_stack`
286+
/// instructions in the function.
287+
const InstructionIndices &stackInstructionIndices;
288+
288289
/// If we are merging any alloc_stack that were moved, to work around a bug in
289290
/// SelectionDAG that sinks to llvm.dbg.addr, we need to break blocks right
290291
/// after each llvm.dbg.addr.
@@ -295,7 +296,8 @@ class MergeStackSlots {
295296

296297
public:
297298
MergeStackSlots(SmallVectorImpl<AllocStackInst *> &AllocStacks,
298-
SmallVectorImpl<SILInstruction *> &FuncExits);
299+
SmallVectorImpl<SILInstruction *> &FuncExits,
300+
const InstructionIndices &stackInstructionIndices);
299301

300302
/// Merge alloc_stack instructions if possible and hoist them to the entry
301303
/// block.
@@ -304,8 +306,9 @@ class MergeStackSlots {
304306
} // end anonymous namespace
305307

306308
MergeStackSlots::MergeStackSlots(SmallVectorImpl<AllocStackInst *> &AllocStacks,
307-
SmallVectorImpl<SILInstruction *> &FuncExits)
308-
: FunctionExits(FuncExits) {
309+
SmallVectorImpl<SILInstruction *> &FuncExits,
310+
const InstructionIndices &stackInstructionIndices)
311+
: FunctionExits(FuncExits), stackInstructionIndices(stackInstructionIndices) {
309312
// Build initial partitions based on the type.
310313
llvm::DenseMap<SILType, unsigned> TypeToPartitionMap;
311314
for (auto *AS : AllocStacks) {
@@ -350,7 +353,7 @@ MergeStackSlots::mergeSlots(DominanceInfo *DomToUpdate) {
350353
// candidate partition.
351354
bool InterferesWithCandidateP = false;
352355
for (auto *AllocStackInPartition : CandidateP.Elts) {
353-
if (Live.mayOverlap(AllocStackInPartition, CurAllocStack)) {
356+
if (Live.mayOverlap(AllocStackInPartition, CurAllocStack, stackInstructionIndices)) {
354357
InterferesWithCandidateP = true;
355358
break;
356359
}
@@ -405,6 +408,10 @@ class HoistAllocStack {
405408
SmallVector<AllocStackInst *, 16> AllocStackToHoist;
406409
SmallVector<SILInstruction *, 8> FunctionExits;
407410

411+
/// Consecutive indices for all `alloc_stack` and `dealloc_stack`
412+
/// instructions in the function.
413+
InstructionIndices stackInstructionIndices;
414+
408415
llvm::Optional<SILAnalysis::InvalidationKind> InvalidationKind = llvm::None;
409416

410417
DominanceInfo *DomInfoToUpdate = nullptr;
@@ -450,6 +457,8 @@ bool inhibitsAllocStackHoisting(SILInstruction *I) {
450457
/// A generic alloc_stack could reference an opened archetype that was not
451458
/// opened in the entry block.
452459
void HoistAllocStack::collectHoistableInstructions() {
460+
int stackInstructionIndex = 0;
461+
453462
for (auto &BB : *F) {
454463
for (auto &Inst : BB) {
455464
// Terminators that are function exits are our dealloc_stack
@@ -466,10 +475,15 @@ void HoistAllocStack::collectHoistableInstructions() {
466475
AllocStackToHoist.clear();
467476
return;
468477
}
478+
if (isa<DeallocStackInst>(&Inst))
479+
stackInstructionIndices[&Inst] = stackInstructionIndex++;
480+
469481
auto *ASI = dyn_cast<AllocStackInst>(&Inst);
470482
if (!ASI) {
471483
continue;
472484
}
485+
stackInstructionIndices[ASI] = stackInstructionIndex++;
486+
473487
if (isHoistable(ASI, IRGenMod)) {
474488
LLVM_DEBUG(llvm::dbgs() << "Hoisting " << Inst);
475489
AllocStackToHoist.push_back(ASI);
@@ -484,7 +498,7 @@ void HoistAllocStack::collectHoistableInstructions() {
484498
/// dealloc_stack instructions to the function exists.
485499
void HoistAllocStack::hoist() {
486500
if (SILUseStackSlotMerging) {
487-
MergeStackSlots Merger(AllocStackToHoist, FunctionExits);
501+
MergeStackSlots Merger(AllocStackToHoist, FunctionExits, stackInstructionIndices);
488502
InvalidationKind = Merger.mergeSlots(DomInfoToUpdate);
489503
return;
490504
}

test/SILOptimizer/allocstack_hoisting.sil

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -enable-sil-verify-all %s -alloc-stack-hoisting | %FileCheck %s
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -alloc-stack-hoisting | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-MERGING %s
22
// RUN: %target-sil-opt -enable-sil-verify-all %s -alloc-stack-hoisting -sil-merge-stack-slots=false | %FileCheck %s
33
sil_stage canonical
44

@@ -280,3 +280,43 @@ bb3:
280280
%3 = tuple ()
281281
return %3 : $()
282282
}
283+
284+
// CHECK-MERGING-LABEL: sil @merging
285+
// CHECK-MERGING: %1 = alloc_stack $T
286+
// CHECK-MERGING-NEXT: debug_value %1
287+
// CHECK-MERGING-NEXT: debug_value %1
288+
// CHECK-MERGING: dealloc_stack %1
289+
// CHECK-MERGING-NOT: alloc_stack
290+
// CHECK: } // end sil function 'merging'
291+
sil @merging : $@convention(thin) <T> (@in T) -> () {
292+
bb0(%0 : $*T):
293+
%1 = alloc_stack $T
294+
debug_value %1 : $*T, name "x"
295+
dealloc_stack %1 : $*T
296+
%2 = alloc_stack $T
297+
debug_value %2 : $*T, name "y"
298+
dealloc_stack %2 : $*T
299+
%r = tuple ()
300+
return %r : $()
301+
}
302+
303+
// CHECK-LABEL: sil @dont_merge
304+
// CHECK: %1 = alloc_stack $T
305+
// CHECK-NEXT: %2 = alloc_stack $T
306+
// CHECK-NEXT: debug_value %2
307+
// CHECK-NEXT: debug_value %1
308+
// CHECK: dealloc_stack %2
309+
// CHECK-NEXT: dealloc_stack %1
310+
// CHECK: } // end sil function 'dont_merge'
311+
sil @dont_merge : $@convention(thin) <T> (@in T) -> () {
312+
bb0(%0 : $*T):
313+
%1 = alloc_stack $T
314+
debug_value %1 : $*T, name "x"
315+
%3 = alloc_stack $T
316+
debug_value %3 : $*T, name "y"
317+
dealloc_stack %3 : $*T
318+
dealloc_stack %1 : $*T
319+
%r = tuple ()
320+
return %r : $()
321+
}
322+

0 commit comments

Comments
 (0)