Skip to content

Commit 5d3232f

Browse files
authored
Merge pull request #62189 from nate-chandler/opaque-values/1/20221118
[AddressLowering] Shorten stack lifetimes.
2 parents 9936a60 + 693d601 commit 5d3232f

File tree

3 files changed

+63
-23
lines changed

3 files changed

+63
-23
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2805,7 +2805,7 @@ class SILBuilder {
28052805
}
28062806
};
28072807

2808-
/// An wrapper on top of SILBuilder's constructor that automatically sets the
2808+
/// A wrapper on top of SILBuilder's constructor that automatically sets the
28092809
/// current SILDebugScope based on the specified insertion point. This is useful
28102810
/// for situations where a single SIL instruction is lowered into a sequence of
28112811
/// SIL instructions.

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,16 +1002,28 @@ namespace {
10021002
/// function in postorder. If the definition is an argument of this function,
10031003
/// simply replace the function argument with an address representing the
10041004
/// caller's storage.
1005-
///
1006-
/// TODO: shrink lifetimes by inserting alloc_stack at the dominance LCA and
1007-
/// finding the lifetime boundary with a simple backward walk from uses.
10081005
class OpaqueStorageAllocation {
10091006
AddressLoweringState &pass;
1007+
/// The alloc_stacks that have been created which eventually need to have
1008+
/// corresponding dealloc_stacks created.
1009+
///
1010+
/// Supports erasure because created alloc_stacks may be erased when block
1011+
/// arguments are coalesced.
1012+
SmallBlotSetVector<AllocStackInst *, 16> allocs;
1013+
/// The alloc_stacks that have been created which eventually need to be
1014+
/// positioned appropriately.
1015+
///
1016+
/// The subset of allocs which aren't for opened existentials.
1017+
InstructionSet allocsToReposition;
10101018

10111019
public:
1012-
explicit OpaqueStorageAllocation(AddressLoweringState &pass) : pass(pass) {}
1020+
explicit OpaqueStorageAllocation(AddressLoweringState &pass)
1021+
: pass(pass), allocsToReposition(pass.function) {}
10131022

10141023
void allocateOpaqueStorage();
1024+
/// Position alloc_stacks according to uses and create dealloc_stacks that
1025+
/// jointly postdominate.
1026+
void finalizeOpaqueStorage();
10151027

10161028
protected:
10171029
void allocateValue(SILValue value);
@@ -1037,6 +1049,8 @@ class OpaqueStorageAllocation {
10371049

10381050
AllocStackInst *createStackAllocation(SILValue value);
10391051

1052+
SILBasicBlock *getLeastCommonAncestorOfUses(SILValue value);
1053+
10401054
void createStackAllocationStorage(SILValue value) {
10411055
pass.valueStorageMap.getStorage(value).storageAddress =
10421056
createStackAllocation(value);
@@ -1226,9 +1240,20 @@ void OpaqueStorageAllocation::removeAllocation(SILValue value) {
12261240
for (Operand *use : uses) {
12271241
pass.deleter.forceDelete(cast<DeallocStackInst>(use->getUser()));
12281242
}
1243+
allocs.erase(allocInst);
12291244
pass.deleter.forceDelete(allocInst);
12301245
}
12311246

1247+
SILBasicBlock *
1248+
OpaqueStorageAllocation::getLeastCommonAncestorOfUses(SILValue value) {
1249+
SILBasicBlock *lca = nullptr;
1250+
for (auto *use : value->getUses()) {
1251+
auto *block = use->getParentBlock();
1252+
lca = lca ? pass.domInfo->findNearestCommonDominator(lca, block) : block;
1253+
}
1254+
return lca;
1255+
}
1256+
12321257
// Create alloc_stack that dominates an owned value \p value. Create
12331258
// jointly-postdominating dealloc_stack instructions. Nesting will be fixed
12341259
// later.
@@ -1250,8 +1275,8 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
12501275

12511276
// For opened existential types, allocate stack space at the type
12521277
// definition. Allocating as early as possible provides more opportunity for
1253-
// creating use projections into value. But allocation must be no earlier then
1254-
// the latest type definition.
1278+
// creating use projections into value. But allocation must be no earlier
1279+
// then the latest type definition.
12551280
SILInstruction *latestOpeningInst = nullptr;
12561281
allocTy.getASTType().visit([&](CanType type) {
12571282
auto archetype = dyn_cast<ArchetypeType>(type);
@@ -1274,31 +1299,44 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
12741299
latestOpeningInst = openingInst;
12751300
}
12761301
});
1277-
auto allocPt = latestOpeningInst ? std::next(latestOpeningInst->getIterator())
1278-
: pass.function->begin()->begin();
1302+
1303+
auto allocPt = latestOpeningInst
1304+
? latestOpeningInst->getNextInstruction()->getIterator()
1305+
: pass.function->getEntryBlock()->front().getIterator();
12791306
auto allocBuilder = pass.getBuilder(allocPt);
12801307
AllocStackInst *alloc = allocBuilder.createAllocStack(pass.genLoc(), allocTy);
1308+
allocs.insert(alloc);
1309+
if (latestOpeningInst == nullptr) {
1310+
allocsToReposition.insert(alloc);
1311+
}
1312+
return alloc;
1313+
}
1314+
1315+
void OpaqueStorageAllocation::finalizeOpaqueStorage() {
1316+
SmallVector<SILBasicBlock *, 4> boundary;
1317+
for (auto maybeAlloc : allocs) {
1318+
// An allocation may be erased when coalescing block arguments.
1319+
if (!maybeAlloc.hasValue())
1320+
continue;
1321+
1322+
auto *alloc = maybeAlloc.value();
1323+
1324+
if (allocsToReposition.contains(alloc)) {
1325+
auto allocPt = &*getLeastCommonAncestorOfUses(alloc)->begin();
1326+
alloc->moveBefore(allocPt);
1327+
}
12811328

1282-
auto dealloc = [&](SILBasicBlock::iterator insertPt) {
1283-
auto deallocBuilder = pass.getBuilder(insertPt);
1284-
deallocBuilder.createDeallocStack(pass.genLoc(), alloc);
1285-
};
1286-
if (latestOpeningInst) {
12871329
// Deallocate at the predecessors of dominance frontier blocks that are
12881330
// dominated by the alloc to ensure that allocation encloses not only the
12891331
// uses of the current value, but also of any values reusing this storage as
12901332
// a use projection.
1291-
SmallVector<SILBasicBlock *, 4> boundary;
12921333
computeDominatedBoundaryBlocks(alloc->getParent(), pass.domInfo, boundary);
12931334
for (SILBasicBlock *deallocBlock : boundary) {
1294-
dealloc(deallocBlock->getTerminator()->getIterator());
1295-
}
1296-
} else {
1297-
for (SILInstruction *deallocPoint : pass.exitingInsts) {
1298-
dealloc(deallocPoint->getIterator());
1335+
auto deallocBuilder = pass.getBuilder(deallocBlock->back().getIterator());
1336+
deallocBuilder.createDeallocStack(pass.genLoc(), alloc);
12991337
}
1338+
boundary.clear();
13001339
}
1301-
return alloc;
13021340
}
13031341

13041342
//===----------------------------------------------------------------------===//
@@ -3850,6 +3888,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
38503888
// forward order, setting 'storageAddress' for each projection as it goes.
38513889
rewriteFunction(pass);
38523890

3891+
allocator.finalizeOpaqueStorage();
3892+
38533893
deleteRewrittenInstructions(pass);
38543894

38553895
StackNesting::fixNesting(function);

test/SILOptimizer/opaque_values_Onone.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ enum Maybe1<T : Equatable> {
1515
case yep(T)
1616
// CHECK-LABEL: sil hidden @maybe1_compare {{.*}} {
1717
// CHECK: [[LHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe1
18-
// CHECK: [[RHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe1
1918
// CHECK: switch_enum_addr [[LHS_ADDR]] : $*Maybe1<T>, case #Maybe1.yep!enumelt: [[L_YEP:bb[0-9]+]], case #Maybe1.nope!enumelt: {{bb[0-9]+}}
2019
// CHECK: [[L_YEP]]:
20+
// CHECK: [[RHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe1
2121
// CHECK: unchecked_take_enum_data_addr [[LHS_ADDR]] : $*Maybe1<T>, #Maybe1.yep!enumelt
2222
// CHECK: switch_enum_addr [[RHS_ADDR]] : $*Maybe1<T>, case #Maybe1.yep!enumelt: [[L_AND_R_YEP:bb[0-9]+]], default {{bb[0-9]+}}
2323
// CHECK: [[L_AND_R_YEP]]:
@@ -42,9 +42,9 @@ enum Maybe2<T : Equatable> {
4242

4343
// CHECK-LABEL: sil hidden @maybe2_compare {{.*}} {
4444
// CHECK: [[LHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe2<T>
45-
// CHECK: [[RHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe2<T>
4645
// CHECK: switch_enum_addr [[LHS_ADDR]] : $*Maybe2<T>, case #Maybe2.yep!enumelt: [[L_YEP:bb[0-9]+]], case #Maybe2.nope!enumelt: {{bb[0-9]+}}
4746
// CHECK: [[L_YEP]]:
47+
// CHECK: [[RHS_ADDR:%[^,]+]] = alloc_stack [lexical] $Maybe2<T>
4848
// CHECK: unchecked_take_enum_data_addr [[LHS_ADDR]] : $*Maybe2<T>, #Maybe2.yep!enumelt
4949
// CHECK: switch_enum_addr [[RHS_ADDR]] : $*Maybe2<T>, case #Maybe2.yep!enumelt: [[R_YEP:bb[0-9]+]], default {{bb[0-9]+}}
5050
// CHECK: [[L_AND_R_YEP]]:

0 commit comments

Comments
 (0)