Skip to content

Commit 7f05b0f

Browse files
authored
Merge pull request swiftlang#8825 from gottesmm/move_fixup_past_capture_promotion
[semantic-sil] Update capture promotion for moving mark_uninitialized onto alloc_box instead of project_box and move the fixup pass behind it.
2 parents d1be8b0 + 21973e0 commit 7f05b0f

File tree

6 files changed

+105
-133
lines changed

6 files changed

+105
-133
lines changed

lib/SILOptimizer/IPO/CapturePromotion.cpp

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -991,18 +991,7 @@ static bool isProjectBoxNonEscapingUse(ProjectBoxInst *PBI,
991991
EscapeMutationScanningState &State) {
992992
DEBUG(llvm::dbgs() << " Found project box: " << *PBI);
993993

994-
// Check for mutations of the address component.
995-
SILValue Addr = PBI;
996-
997-
// If the AllocBox is used by a mark_uninitialized, scan the MUI for
998-
// interesting uses.
999-
if (Addr->hasOneUse()) {
1000-
SILInstruction *SingleAddrUser = Addr->use_begin()->getUser();
1001-
if (isa<MarkUninitializedInst>(SingleAddrUser))
1002-
Addr = SILValue(SingleAddrUser);
1003-
}
1004-
1005-
for (Operand *AddrOp : Addr->getUses()) {
994+
for (Operand *AddrOp : PBI->getUses()) {
1006995
if (!isNonEscapingUse(AddrOp, State)) {
1007996
DEBUG(llvm::dbgs() << " FAIL! Has escaping user of addr:"
1008997
<< *AddrOp->getUser());
@@ -1015,11 +1004,13 @@ static bool isProjectBoxNonEscapingUse(ProjectBoxInst *PBI,
10151004

10161005
static bool scanUsesForEscapesAndMutations(Operand *Op,
10171006
EscapeMutationScanningState &State) {
1018-
if (auto *PAI = dyn_cast<PartialApplyInst>(Op->getUser())) {
1007+
SILInstruction *User = Op->getUser();
1008+
1009+
if (auto *PAI = dyn_cast<PartialApplyInst>(User)) {
10191010
return isPartialApplyNonEscapingUser(Op, PAI, State);
10201011
}
10211012

1022-
if (auto *PBI = dyn_cast<ProjectBoxInst>(Op->getUser())) {
1013+
if (auto *PBI = dyn_cast<ProjectBoxInst>(User)) {
10231014
// It is assumed in later code that we will only have 1 project_box. This
10241015
// can be seen since there is no code for reasoning about multiple
10251016
// boxes. Just put in the restriction so we are consistent.
@@ -1029,20 +1020,16 @@ static bool scanUsesForEscapesAndMutations(Operand *Op,
10291020
return isProjectBoxNonEscapingUse(PBI, State);
10301021
}
10311022

1032-
// Given a top level copy value use, check all of its user operands as if
1033-
// they were apart of the use list of the base operand.
1034-
//
1035-
// This is because even though we are copying the box, a copy of the box is
1036-
// just a retain + bitwise copy of the pointer. This has nothing to do with
1037-
// whether or not the address escapes in some way.
1023+
// Given a top level copy value use or mark_uninitialized, check all of its
1024+
// user operands as if they were apart of the use list of the base operand.
10381025
//
10391026
// This is a separate code path from the non escaping user visitor check since
10401027
// we want to be more conservative around non-top level copies (i.e. a copy
10411028
// derived from a projection like instruction). In fact such a thing may not
10421029
// even make any sense!
1043-
if (auto *CVI = dyn_cast<CopyValueInst>(Op->getUser())) {
1044-
return all_of(CVI->getUses(), [&State](Operand *CopyOp) -> bool {
1045-
return scanUsesForEscapesAndMutations(CopyOp, State);
1030+
if (isa<CopyValueInst>(User) || isa<MarkUninitializedInst>(User)) {
1031+
return all_of(User->getUses(), [&State](Operand *UserOp) -> bool {
1032+
return scanUsesForEscapesAndMutations(UserOp, State);
10461033
});
10471034
}
10481035

@@ -1171,36 +1158,18 @@ static SILValue getOrCreateProjectBoxHelper(SILValue PartialOperand) {
11711158
return B.createProjectBox(CVI->getLoc(), CVI, 0);
11721159
}
11731160

1174-
// Otherwise, handle the alloc_box case.
1175-
auto *ABI = cast<AllocBoxInst>(PartialOperand);
1176-
// Load and copy from the address value, passing the result as an argument
1177-
// to the new closure.
1178-
//
1179-
// *NOTE* This code assumes that we only have one project box user. We
1180-
// enforce this with the assert below.
1181-
assert(count_if(ABI->getUses(), [](Operand *Op) -> bool {
1182-
return isa<ProjectBoxInst>(Op->getUser());
1183-
}) == 1);
1184-
1185-
// If the address is marked uninitialized, load through the mark, so
1186-
// that DI can reason about it.
1187-
for (Operand *BoxValueUse : ABI->getUses()) {
1188-
auto *PBI = dyn_cast<ProjectBoxInst>(BoxValueUse->getUser());
1189-
if (!PBI)
1190-
continue;
1191-
1192-
auto *OptIter = PBI->getSingleUse();
1193-
if (!OptIter)
1194-
continue;
1195-
1196-
if (!isa<MarkUninitializedInst>(OptIter->getUser()))
1197-
continue;
1198-
1199-
return OptIter->getUser();
1161+
// Otherwise, handle the alloc_box case. If we have a mark_uninitialized on
1162+
// the box, we create the project value through that.
1163+
SILInstruction *Box = cast<AllocBoxInst>(PartialOperand);
1164+
if (auto *Op = Box->getSingleUse()) {
1165+
if (auto *MUI = dyn_cast<MarkUninitializedInst>(Op->getUser())) {
1166+
Box = MUI;
1167+
}
12001168
}
12011169

1202-
// Otherwise, just return a project_box.
1203-
return getOrCreateProjectBox(ABI, 0);
1170+
// Just return a project_box.
1171+
SILBuilder B(std::next(Box->getIterator()));
1172+
return B.createProjectBox(Box->getLoc(), Box, 0);
12041173
}
12051174

12061175
/// \brief Given a partial_apply instruction and a set of promotable indices,

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) {
7474
static void addMandatoryOptPipeline(SILPassPipelinePlan &P,
7575
const SILOptions &Options) {
7676
P.startPipeline("Guaranteed Passes");
77-
P.addMarkUninitializedFixup();
7877
if (Options.EnableMandatorySemanticARCOpts) {
7978
P.addSemanticARCOpts();
8079
}
8180
P.addDiagnoseStaticExclusivity();
8281
P.addCapturePromotion();
8382
P.addAllocBoxToStack();
83+
P.addMarkUninitializedFixup();
8484
P.addNoReturnFolding();
8585
P.addOwnershipModelEliminator();
8686
P.addDefiniteInitialization();

lib/SILOptimizer/Transforms/AllocBoxToStack.cpp

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,21 @@ static bool addLastRelease(SILValue V, SILBasicBlock *BB,
109109
// Find the final releases of the alloc_box along any given path.
110110
// These can include paths from a release back to the alloc_box in a
111111
// loop.
112-
static bool getFinalReleases(AllocBoxInst *ABI,
113-
llvm::SmallVectorImpl<SILInstruction*> &Releases) {
112+
static bool
113+
getFinalReleases(SILValue Box,
114+
llvm::SmallVectorImpl<SILInstruction *> &Releases) {
114115
llvm::SmallPtrSet<SILBasicBlock*, 16> LiveIn;
115116
llvm::SmallPtrSet<SILBasicBlock*, 16> UseBlocks;
116117

117-
auto *DefBB = ABI->getParent();
118+
auto *DefBB = Box->getParentBlock();
118119

119120
auto seenRelease = false;
120121
SILInstruction *OneRelease = nullptr;
121122

122123
// We'll treat this like a liveness problem where the alloc_box is
123124
// the def. Each block that has a use of the owning pointer has the
124125
// value live-in unless it is the block with the alloc_box.
125-
llvm::SmallVector<Operand *, 32> Worklist(ABI->use_begin(), ABI->use_end());
126+
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
126127
while (!Worklist.empty()) {
127128
auto *Op = Worklist.pop_back_val();
128129
auto *User = Op->getUser();
@@ -137,12 +138,10 @@ static bool getFinalReleases(AllocBoxInst *ABI,
137138
// Also keep track of the blocks with uses.
138139
UseBlocks.insert(BB);
139140

140-
// If we have a copy value, add its uses to the work list and continue.
141-
//
142-
// We are actually performing multiple operations here. We are eliminating
143-
// copies of the box and the box itself.
144-
if (auto *CVI = dyn_cast<CopyValueInst>(User)) {
145-
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
141+
// If we have a copy value or a mark_uninitialized, add its uses to the work
142+
// list and continue.
143+
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
144+
copy(User->getUses(), std::back_inserter(Worklist));
146145
continue;
147146
}
148147

@@ -171,7 +170,7 @@ static bool getFinalReleases(AllocBoxInst *ABI,
171170
// release/dealloc.
172171
for (auto *BB : UseBlocks)
173172
if (!successorHasLiveIn(BB, LiveIn))
174-
if (!addLastRelease(ABI, BB, Releases))
173+
if (!addLastRelease(Box, BB, Releases))
175174
return false;
176175

177176
return true;
@@ -213,9 +212,8 @@ static bool partialApplyEscapes(SILValue V, bool examineApply) {
213212
// If we have a copy_value, the copy value does not cause an escape, but its
214213
// uses might do so... so add the copy_value's uses to the worklist and
215214
// continue.
216-
if (auto *Copy = dyn_cast<CopyValueInst>(User)) {
217-
std::copy(Copy->use_begin(), Copy->use_end(),
218-
std::back_inserter(Worklist));
215+
if (isa<CopyValueInst>(User)) {
216+
copy(User->getUses(), std::back_inserter(Worklist));
219217
continue;
220218
}
221219

@@ -353,11 +351,9 @@ static bool checkPartialApplyBody(Operand *O) {
353351
PromotedOperands);
354352
}
355353

356-
357-
/// findUnexpectedBoxUse - Validate that the uses of a pointer to a
358-
/// box do not eliminate it from consideration for promotion to a
359-
/// stack element. Optionally examine the body of partial_apply
360-
/// to see if there is an unexpected use inside. Return the
354+
/// Validate that the uses of a pointer to a box do not eliminate it from
355+
/// consideration for promotion to a stack element. Optionally examine the body
356+
/// of partial_apply to see if there is an unexpected use inside. Return the
361357
/// instruction with the unexpected use if we find one.
362358
static SILInstruction* findUnexpectedBoxUse(SILValue Box,
363359
bool examinePartialApply,
@@ -386,9 +382,10 @@ static SILInstruction* findUnexpectedBoxUse(SILValue Box,
386382
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
387383
continue;
388384

389-
// If we have a copy_value, visit the users of the copy_value.
390-
if (auto *CVI = dyn_cast<CopyValueInst>(User)) {
391-
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
385+
// If our user instruction is a copy_value or a marked_uninitialized, visit
386+
// the users recursively.
387+
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
388+
copy(User->getUses(), std::back_inserter(Worklist));
392389
continue;
393390
}
394391

@@ -432,19 +429,20 @@ static bool canPromoteAllocBox(AllocBoxInst *ABI,
432429
return true;
433430
}
434431

435-
static void replaceProjectBoxUsers(AllocBoxInst *ABI, AllocStackInst *ASI) {
436-
llvm::SmallVector<Operand *, 8> Worklist(ABI->use_begin(), ABI->use_end());
432+
static void replaceProjectBoxUsers(SILValue HeapBox, SILValue StackBox) {
433+
llvm::SmallVector<Operand *, 8> Worklist(HeapBox->use_begin(),
434+
HeapBox->use_end());
437435
while (!Worklist.empty()) {
438436
auto *Op = Worklist.pop_back_val();
439437
if (auto *PBI = dyn_cast<ProjectBoxInst>(Op->getUser())) {
440-
PBI->replaceAllUsesWith(ASI);
438+
PBI->replaceAllUsesWith(StackBox);
441439
continue;
442440
}
443441

444442
auto *CVI = dyn_cast<CopyValueInst>(Op->getUser());
445443
if (!CVI)
446444
continue;
447-
std::copy(CVI->use_begin(), CVI->use_end(), std::back_inserter(Worklist));
445+
copy(CVI->getUses(), std::back_inserter(Worklist));
448446
}
449447
}
450448

@@ -453,36 +451,40 @@ static void replaceProjectBoxUsers(AllocBoxInst *ABI, AllocStackInst *ASI) {
453451
static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
454452
DEBUG(llvm::dbgs() << "*** Promoting alloc_box to stack: " << *ABI);
455453

454+
SILValue HeapBox = ABI;
455+
Optional<MarkUninitializedInst::Kind> Kind;
456+
if (HeapBox->hasOneUse()) {
457+
auto *User = HeapBox->getSingleUse()->getUser();
458+
if (auto *MUI = dyn_cast<MarkUninitializedInst>(User)) {
459+
HeapBox = MUI;
460+
Kind = MUI->getKind();
461+
}
462+
}
463+
456464
llvm::SmallVector<SILInstruction *, 4> FinalReleases;
457-
if (!getFinalReleases(ABI, FinalReleases))
465+
if (!getFinalReleases(HeapBox, FinalReleases))
458466
return false;
459467

460468
// Promote this alloc_box to an alloc_stack. Insert the alloc_stack
461469
// at the beginning of the function.
462-
SILBuilder BuildAlloc(ABI);
463-
BuildAlloc.setCurrentDebugScope(ABI->getDebugScope());
470+
SILBuilder Builder(ABI);
471+
Builder.setCurrentDebugScope(ABI->getDebugScope());
464472
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
465473
&& "rewriting multi-field box not implemented");
466-
auto *ASI = BuildAlloc.createAllocStack(ABI->getLoc(),
467-
ABI->getBoxType()->getFieldType(ABI->getModule(), 0),
468-
ABI->getVarInfo());
474+
auto *ASI = Builder.createAllocStack(
475+
ABI->getLoc(), ABI->getBoxType()->getFieldType(ABI->getModule(), 0),
476+
ABI->getVarInfo());
477+
478+
// Transfer a mark_uninitialized if we have one.
479+
SILValue StackBox = ASI;
480+
if (Kind) {
481+
StackBox =
482+
Builder.createMarkUninitialized(ASI->getLoc(), ASI, Kind.getValue());
483+
}
469484

470485
// Replace all uses of the address of the box's contained value with
471486
// the address of the stack location.
472-
replaceProjectBoxUsers(ABI, ASI);
473-
474-
// Check to see if the alloc_box was used by a mark_uninitialized instruction.
475-
// If so, any uses of the pointer result need to keep using the MUI, not the
476-
// alloc_stack directly. If we don't do this, DI will miss the uses.
477-
SILValue PointerResult = ASI;
478-
for (auto UI : ASI->getUses()) {
479-
if (auto *MUI = dyn_cast<MarkUninitializedInst>(UI->getUser())) {
480-
assert(ASI->hasOneUse() &&
481-
"alloc_stack used by mark_uninitialized, but not exclusively!");
482-
PointerResult = MUI;
483-
break;
484-
}
485-
}
487+
replaceProjectBoxUsers(HeapBox, StackBox);
486488

487489
assert(ABI->getBoxType()->getLayout()->getFields().size() == 1
488490
&& "promoting multi-field box not implemented");
@@ -495,7 +497,7 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
495497
if (!isa<DeallocBoxInst>(LastRelease)&& !Lowering.isTrivial()) {
496498
// For non-trivial types, insert destroys for each final release-like
497499
// instruction we found that isn't an explicit dealloc_box.
498-
Builder.emitDestroyAddrAndFold(Loc, PointerResult);
500+
Builder.emitDestroyAddrAndFold(Loc, StackBox);
499501
}
500502
Builder.createDeallocStack(Loc, ASI);
501503
}
@@ -509,10 +511,10 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
509511
while (!Worklist.empty()) {
510512
auto *User = Worklist.pop_back_val();
511513

512-
if (isa<CopyValueInst>(User)) {
513-
std::transform(
514-
User->use_begin(), User->use_end(), std::back_inserter(Worklist),
515-
[](Operand *Op) -> SILInstruction * { return Op->getUser(); });
514+
// Look through any mark_uninitialized, copy_values.
515+
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
516+
transform(User->getUses(), std::back_inserter(Worklist),
517+
[](Operand *Op) -> SILInstruction * { return Op->getUser(); });
516518
User->replaceAllUsesWith(
517519
SILUndef::get(User->getType(), User->getModule()));
518520
User->eraseFromParent();

0 commit comments

Comments
 (0)