Skip to content

Commit 50af8fd

Browse files
committed
[move-only] Box owned arguments like let parameters.
1 parent a571357 commit 50af8fd

7 files changed

+233
-92
lines changed

lib/SILGen/SILGenProlog.cpp

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
#include "ManagedValue.h"
1717
#include "SILGenFunction.h"
1818
#include "Scope.h"
19+
1920
#include "swift/AST/CanTypeVisitor.h"
2021
#include "swift/AST/DiagnosticsSIL.h"
2122
#include "swift/AST/GenericEnvironment.h"
2223
#include "swift/AST/ParameterList.h"
2324
#include "swift/AST/PropertyWrappers.h"
25+
#include "swift/Basic/Defer.h"
2426
#include "swift/SIL/SILArgument.h"
2527
#include "swift/SIL/SILArgumentConvention.h"
2628
#include "swift/SIL/SILInstruction.h"
@@ -293,13 +295,24 @@ struct ArgumentInitHelper {
293295
return argEmitter.visit(canTy, origTy);
294296
}
295297

296-
SILValue updateArgumentValueForBinding(ManagedValue argrv, SILLocation loc,
297-
ParamDecl *pd, SILValue value,
298-
const SILDebugVariable &varinfo) {
298+
void updateArgumentValueForBinding(ManagedValue argrv, SILLocation loc,
299+
ParamDecl *pd, SILValue value,
300+
const SILDebugVariable &varinfo) {
301+
bool calledCompletedUpdate = false;
302+
SWIFT_DEFER {
303+
assert(calledCompletedUpdate && "Forgot to call completed update along "
304+
"all paths or manually turn it off");
305+
};
306+
auto completeUpdate = [&](SILValue value) -> void {
307+
SGF.B.createDebugValue(loc, value, varinfo);
308+
SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(value);
309+
calledCompletedUpdate = true;
310+
};
311+
299312
// If we do not need to support lexical lifetimes, just return value as the
300313
// updated value.
301314
if (!SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule()))
302-
return value;
315+
return completeUpdate(value);
303316

304317
// Look for the following annotations on the function argument:
305318
// - @noImplicitCopy
@@ -314,25 +327,40 @@ struct ArgumentInitHelper {
314327
if (!value->getType().isMoveOnly()) {
315328
// Follow the normal path. The value's lifetime will be enforced based
316329
// on its ownership.
317-
return value;
330+
return completeUpdate(value);
318331
}
319332

320-
// At this point, we have a move only type.
333+
// At this point, we have a noncopyable type. If it is owned, create an
334+
// alloc_box for it.
321335
if (value->getOwnershipKind() == OwnershipKind::Owned) {
322-
value = SGF.B.createMoveValue(loc, argrv.forward(SGF),
323-
/*isLexical*/ true);
324-
value = SGF.B.createMarkMustCheckInst(
325-
loc, value, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
326-
SGF.emitManagedRValueWithCleanup(value);
327-
return value;
336+
// TODO: Once owned values are mutable, this needs to become mutable.
337+
auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture(
338+
pd,
339+
SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
340+
pd->getType()),
341+
SGF.F.getGenericEnvironment(),
342+
/*mutable*/ true);
343+
344+
auto *box = SGF.B.createAllocBox(loc, boxType, varinfo);
345+
SILValue destAddr = SGF.B.createProjectBox(loc, box, 0);
346+
SGF.B.emitStoreValueOperation(loc, argrv.forward(SGF), destAddr,
347+
StoreOwnershipQualifier::Init);
348+
SGF.emitManagedRValueWithCleanup(box);
349+
350+
// We manually set calledCompletedUpdate to true since we want to use
351+
// VarLoc::getForBox and use the debug info from the box rather than
352+
// insert a custom debug_value.
353+
calledCompletedUpdate = true;
354+
SGF.VarLocs[pd] = SILGenFunction::VarLoc::getForBox(box);
355+
return;
328356
}
329357

330358
assert(value->getOwnershipKind() == OwnershipKind::Guaranteed);
331359
value = SGF.B.createCopyValue(loc, value);
332360
value = SGF.B.createMarkMustCheckInst(
333361
loc, value, MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
334362
SGF.emitManagedRValueWithCleanup(value);
335-
return value;
363+
return completeUpdate(value);
336364
}
337365

338366
if (value->getType().isTrivial(SGF.F)) {
@@ -346,7 +374,7 @@ struct ArgumentInitHelper {
346374
kind = MarkMustCheckInst::CheckKind::ConsumableAndAssignable;
347375
value = SGF.B.createMarkMustCheckInst(loc, value, kind);
348376
SGF.emitManagedRValueWithCleanup(value);
349-
return value;
377+
return completeUpdate(value);
350378
}
351379

352380
if (value->getOwnershipKind() == OwnershipKind::Guaranteed) {
@@ -355,7 +383,7 @@ struct ArgumentInitHelper {
355383
value = SGF.B.createMarkMustCheckInst(
356384
loc, value, MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
357385
SGF.emitManagedRValueWithCleanup(value);
358-
return value;
386+
return completeUpdate(value);
359387
}
360388

361389
if (value->getOwnershipKind() == OwnershipKind::Owned) {
@@ -367,10 +395,10 @@ struct ArgumentInitHelper {
367395
value = SGF.B.createMarkMustCheckInst(
368396
loc, value, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
369397
SGF.emitManagedRValueWithCleanup(value);
370-
return value;
398+
return completeUpdate(value);
371399
}
372400

373-
return value;
401+
return completeUpdate(value);
374402
}
375403

376404
/// Create a SILArgument and store its value into the given Initialization,
@@ -405,8 +433,8 @@ struct ArgumentInitHelper {
405433
SILValue value = argrv.getValue();
406434
SILDebugVariable varinfo(pd->isImmutable(), ArgNo);
407435
if (!argrv.getType().isAddress()) {
408-
value = updateArgumentValueForBinding(argrv, loc, pd, value, varinfo);
409-
SGF.B.createDebugValue(loc, value, varinfo);
436+
// NOTE: We setup SGF.VarLocs[pd] in updateArgumentValueForBinding.
437+
updateArgumentValueForBinding(argrv, loc, pd, value, varinfo);
410438
} else {
411439
if (auto *allocStack = dyn_cast<AllocStackInst>(value)) {
412440
allocStack->setArgNo(ArgNo);
@@ -417,8 +445,8 @@ struct ArgumentInitHelper {
417445
} else {
418446
SGF.B.createDebugValueAddr(loc, value, varinfo);
419447
}
448+
SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(value);
420449
}
421-
SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(value);
422450
}
423451

424452
void emitParam(ParamDecl *PD) {

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -733,12 +733,11 @@ void UseState::initializeLiveness(
733733
liveness.updateForUse(endAccess, livenessInstAndValue.second,
734734
false /*lifetime ending*/);
735735
}
736-
} else {
737-
for (auto *ebi : li->getConsumingUses()) {
738-
liveness.updateForUse(ebi->getUser(), livenessInstAndValue.second,
739-
false /*lifetime ending*/);
740-
}
741736
}
737+
// NOTE: We used to add the destroy_value of our loads here to liveness. We
738+
// instead add them to the livenessUses array so that we can successfully
739+
// find them later when performing a forward traversal to find them for
740+
// error purposes.
742741
LLVM_DEBUG(llvm::dbgs() << "Added liveness for borrow: "
743742
<< *livenessInstAndValue.first;
744743
liveness.print(llvm::dbgs()));
@@ -1363,6 +1362,7 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
13631362
}
13641363

13651364
// Canonicalize the lifetime of the load [take], load [copy].
1365+
LLVM_DEBUG(llvm::dbgs() << "Running copy propagation!\n");
13661366
moveChecker.changed |= moveChecker.canonicalizer.canonicalize(li);
13671367

13681368
// If we are asked to perform no_consume_or_assign checking or
@@ -1397,6 +1397,18 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
13971397
}
13981398

13991399
useState.borrows.insert({user, *leafRange});
1400+
1401+
// If we had a load [copy], borrow then we know that all of its destroys
1402+
// must have been destroy_value. So we can just gather up those
1403+
// destroy_value and use then to create liveness to ensure that our
1404+
// value is alive over the entire borrow scope we are going to create.
1405+
LLVM_DEBUG(llvm::dbgs() << "Adding destroys from load as liveness uses "
1406+
"since they will become end_borrows.\n");
1407+
for (auto *consumeUse : li->getConsumingUses()) {
1408+
auto *dvi = cast<DestroyValueInst>(consumeUse->getUser());
1409+
useState.livenessUses.insert({dvi, *leafRange});
1410+
}
1411+
14001412
return true;
14011413
}
14021414

@@ -1433,6 +1445,16 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
14331445
}
14341446

14351447
useState.borrows.insert({user, *leafRange});
1448+
// If we had a load [copy], borrow then we know that all of its destroys
1449+
// must have been destroy_value. So we can just gather up those
1450+
// destroy_value and use then to create liveness to ensure that our
1451+
// value is alive over the entire borrow scope we are going to create.
1452+
LLVM_DEBUG(llvm::dbgs() << "Adding destroys from load as liveness uses "
1453+
"since they will become end_borrows.\n");
1454+
for (auto *consumeUse : li->getConsumingUses()) {
1455+
auto *dvi = cast<DestroyValueInst>(consumeUse->getUser());
1456+
useState.livenessUses.insert({dvi, *leafRange});
1457+
}
14361458
} else {
14371459
// If we had a load [copy], store this into the copy list. These are the
14381460
// things that we must merge into destroy_addr or reinits after we are
@@ -1460,19 +1482,15 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
14601482
return true;
14611483
}
14621484

1463-
auto insertLivenessUseForApply = [&](const Operand &op) -> bool {
1464-
auto leafRange = TypeTreeLeafTypeRange::get(op.get(), getRootAddress());
1465-
if (!leafRange)
1466-
return false;
1467-
1468-
useState.livenessUses.insert({user, *leafRange});
1469-
return true;
1470-
};
1471-
14721485
if (auto fas = FullApplySite::isa(user)) {
14731486
switch (fas.getArgumentConvention(*op)) {
14741487
case SILArgumentConvention::Indirect_In_Guaranteed: {
1475-
return insertLivenessUseForApply(*op);
1488+
auto leafRange = TypeTreeLeafTypeRange::get(op->get(), getRootAddress());
1489+
if (!leafRange)
1490+
return false;
1491+
1492+
useState.livenessUses.insert({user, *leafRange});
1493+
return true;
14761494
}
14771495

14781496
case SILArgumentConvention::Indirect_Inout:
@@ -1490,11 +1508,25 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
14901508
}
14911509
}
14921510

1493-
if (PartialApplyInst *pas = dyn_cast<PartialApplyInst>(user)) {
1511+
if (auto *pas = dyn_cast<PartialApplyInst>(user)) {
14941512
if (pas->isOnStack()) {
1495-
// On-stack partial applications are always a liveness use of their
1496-
// captures.
1497-
return insertLivenessUseForApply(*op);
1513+
LLVM_DEBUG(llvm::dbgs() << "Found on stack partial apply!\n");
1514+
// On-stack partial applications and their final consumes are always a
1515+
// liveness use of their captures.
1516+
auto leafRange = TypeTreeLeafTypeRange::get(op->get(), getRootAddress());
1517+
if (!leafRange) {
1518+
LLVM_DEBUG(llvm::dbgs() << "Failed to compute leaf range!\n");
1519+
return false;
1520+
}
1521+
1522+
useState.livenessUses.insert({user, *leafRange});
1523+
for (auto *use : pas->getConsumingUses()) {
1524+
LLVM_DEBUG(llvm::dbgs()
1525+
<< "Adding consuming use of partial apply as liveness use: "
1526+
<< *use->getUser());
1527+
useState.livenessUses.insert({use->getUser(), *leafRange});
1528+
}
1529+
return true;
14981530
}
14991531
}
15001532

@@ -1921,6 +1953,7 @@ void MoveOnlyAddressCheckerPImpl::insertDestroysOnBoundary(
19211953
void MoveOnlyAddressCheckerPImpl::rewriteUses(
19221954
FieldSensitiveMultiDefPrunedLiveRange &liveness,
19231955
const FieldSensitivePrunedLivenessBoundary &boundary) {
1956+
LLVM_DEBUG(llvm::dbgs() << "MoveOnlyAddressChecker Rewrite Uses!\n");
19241957
// First remove all destroy_addr that have not been claimed.
19251958
for (auto destroyPair : addressUseState.destroys) {
19261959
if (!consumes.claimConsume(destroyPair.first, destroyPair.second)) {

lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureUtils.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ bool Implementation::gatherUses(SILValue value) {
271271
while (!useWorklist.empty()) {
272272
auto *nextUse = useWorklist.pop_back_val();
273273
LLVM_DEBUG(llvm::dbgs() << " NextUse: " << *nextUse->getUser());
274+
LLVM_DEBUG(llvm::dbgs() << " Operand Ownership: "
275+
<< nextUse->getOperandOwnership() << '\n');
274276
switch (nextUse->getOperandOwnership()) {
275277
case OperandOwnership::NonUse:
276278
continue;
@@ -358,15 +360,35 @@ bool Implementation::gatherUses(SILValue value) {
358360
});
359361
continue;
360362

361-
case OperandOwnership::Borrow:
362-
LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n");
363+
case OperandOwnership::Borrow: {
363364
// Look through borrows.
364-
for (auto value : nextUse->getUser()->getResults()) {
365-
for (auto *use : value->getUses()) {
365+
if (auto *bbi = dyn_cast<BeginBorrowInst>(nextUse->getUser())) {
366+
LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n");
367+
for (auto *use : bbi->getUses()) {
366368
useWorklist.push_back(use);
367369
}
370+
continue;
368371
}
372+
373+
auto leafRange =
374+
TypeTreeLeafTypeRange::get(nextUse->get(), getRootValue());
375+
if (!leafRange) {
376+
LLVM_DEBUG(llvm::dbgs() << " Failed to compute leaf range?!\n");
377+
return false;
378+
}
379+
380+
// Otherwise, treat it as a normal use.
381+
LLVM_DEBUG(llvm::dbgs() << " Treating non-begin_borrow borrow as "
382+
"a non lifetime ending use!\n");
383+
blocksToUses.insert(
384+
nextUse->getParentBlock(),
385+
{nextUse, {*leafRange, false /*is lifetime ending*/}});
386+
liveness.updateForUse(nextUse->getUser(), *leafRange,
387+
false /*is lifetime ending*/);
388+
instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse);
389+
369390
continue;
391+
}
370392
case OperandOwnership::EndBorrow:
371393
LLVM_DEBUG(llvm::dbgs() << " Found end borrow!\n");
372394
continue;
@@ -1162,6 +1184,10 @@ void Implementation::rewriteUses(InstructionDeleter *deleter) {
11621184

11631185
LLVM_DEBUG(llvm::dbgs()
11641186
<< "Performing BorrowToDestructureTransform::rewriteUses()!\n");
1187+
SWIFT_DEFER {
1188+
LLVM_DEBUG(llvm::dbgs() << "Function after rewriting!\n";
1189+
getMarkedValue()->getFunction()->dump());
1190+
};
11651191

11661192
llvm::SmallPtrSet<Operand *, 8> seenOperands;
11671193
SmallBitVector bitsNeededInBlock(liveness.getNumSubElements());
@@ -1693,10 +1719,10 @@ gatherSwitchEnum(SILValue value,
16931719
continue;
16941720

16951721
case OperandOwnership::Borrow:
1696-
LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n");
16971722
// Look through borrows.
1698-
for (auto value : nextUse->getUser()->getResults()) {
1699-
for (auto *use : value->getUses()) {
1723+
if (auto *bbi = dyn_cast<BeginBorrowInst>(nextUse->getUser())) {
1724+
LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n");
1725+
for (auto *use : bbi->getUses()) {
17001726
useWorklist.push_back(use);
17011727
}
17021728
}
@@ -1734,6 +1760,7 @@ gatherSwitchEnum(SILValue value,
17341760
//===----------------------------------------------------------------------===//
17351761

17361762
bool BorrowToDestructureTransform::transform() {
1763+
LLVM_DEBUG(llvm::dbgs() << "Performing Borrow To Destructure Tranform!\n");
17371764
auto *fn = mmci->getFunction();
17381765
StackList<BeginBorrowInst *> borrowWorklist(mmci->getFunction());
17391766

@@ -1746,8 +1773,10 @@ bool BorrowToDestructureTransform::transform() {
17461773

17471774
// If we do not have any borrows to process, return true early to show we
17481775
// succeeded in processing.
1749-
if (borrowWorklist.empty())
1776+
if (borrowWorklist.empty()) {
1777+
LLVM_DEBUG(llvm::dbgs() << "No borrows found!\n");
17501778
return true;
1779+
}
17511780

17521781
// Then go through our borrows and attempt to gather up guaranteed
17531782
// switch_enums. If we see any of them, we need to transform them into owned

test/SILGen/moveonly.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ public func useNonTrivialStruct(_ s: __shared NonTrivialStruct) {
8787

8888
// CHECK-LABEL: sil [ossa] @$s8moveonly24useNonTrivialOwnedStructyyAA0cdF0VnF : $@convention(thin) (@owned NonTrivialStruct) -> () {
8989
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialStruct):
90-
// CHECK: [[MOVED_ARG:%.*]] = move_value [lexical] [[ARG]]
91-
// CHECK: mark_must_check [consumable_and_assignable] [[MOVED_ARG]]
90+
// CHECK: [[BOX:%.*]] = alloc_box
91+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
92+
// CHECK: store [[ARG]] to [init] [[PROJECT]]
93+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
94+
// CHECK: mark_must_check [assignable_but_not_consumable] [[PROJECT]]
9295
// CHECK: } // end sil function '$s8moveonly24useNonTrivialOwnedStructyyAA0cdF0VnF'
9396
public func useNonTrivialOwnedStruct(_ s: __owned NonTrivialStruct) {
9497
borrowVal(s)
@@ -117,8 +120,11 @@ public func useNonTrivialEnum(_ s: __shared NonTrivialEnum) {
117120

118121
// CHECK-LABEL: sil [ossa] @$s8moveonly22useNonTrivialOwnedEnumyyAA0cdF0OnF : $@convention(thin) (@owned NonTrivialEnum) -> () {
119122
// CHECK: bb0([[ARG:%.*]] : @owned $NonTrivialEnum):
120-
// CHECK: [[MOVED_ARG:%.*]] = move_value [lexical] [[ARG]]
121-
// CHECK: mark_must_check [consumable_and_assignable] [[MOVED_ARG]]
123+
// CHECK: [[BOX:%.*]] = alloc_box
124+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
125+
// CHECK: store [[ARG]] to [init] [[PROJECT]]
126+
// CHECK: [[PROJECT:%.*]] = project_box [[BOX]]
127+
// CHECK: mark_must_check [assignable_but_not_consumable] [[PROJECT]]
122128
// CHECK: } // end sil function '$s8moveonly22useNonTrivialOwnedEnumyyAA0cdF0OnF'
123129
public func useNonTrivialOwnedEnum(_ s: __owned NonTrivialEnum) {
124130
borrowVal(s)

0 commit comments

Comments
 (0)