Skip to content

Commit f0ed0ea

Browse files
committed
[ownership] Do not lower copy_unowned_value to strong_retain_unowned.
The major important thing here is that by using copy_unowned_value we can guarantee that the non-ownership SIL ARC optimizer will treat the release associated with the strong_retain_unowned as on a distinc rc-identity from its argument. As an example of this problem consider the following SILGen like output: ---- %1 = copy_value %0 : $Builtin.NativeObject %2 = ref_to_unowned %1 %3 = copy_unowned_value %2 destroy_value %1 ... destroy_value %3 ---- In this case, we are converting a strong reference to an unowned value and then lifetime extending the value past the original value. After eliminating ownership this lowers to: ---- strong_retain %0 : $Builtin.NativeObject %1 = ref_to_unowned %0 strong_retain_unowned %1 strong_release %0 ... strong_release %0 ---- From an RC identity perspective, we have now blurred the lines in between %3 and %1 in the previous example. This can then result in the following miscompile: ---- %1 = ref_to_unowned %0 strong_retain_unowned %1 ... strong_release %0 ---- In this case, it is possible that we created a lifetime gap that will then cause strong_retain_unowned to assert. By not lowering copy_unowned_value throughout the SIL pipeline, we instead get this after lowering: ---- strong_retain %0 : $Builtin.NativeObject %1 = ref_to_unowned %0 %2 = copy_unowned_value %1 strong_release %0 ... strong_release %2 ---- And we do not miscompile since we preserved the high level rc identity pairing. There shouldn't be any performance impact since we do not really optimize strong_retain_unowned at the SIL level. I went through all of the places that strong_retain_unowned was referenced and added appropriate handling for copy_unowned_value. rdar://41328987 **NOTE** I am going to remove strong_retain_unowned in a forthcoming commit. I just want something more minimal for cherry-picking purposes. (cherry picked from commit 97367d3)
1 parent 73535cc commit f0ed0ea

15 files changed

+97
-26
lines changed

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t VERSION_MINOR = 414; // Last change: track whether xrefs come from Clang
58+
const uint16_t VERSION_MINOR = 415; // Last change: {strong_retain,copy}_unowned
5959

6060
using DeclIDField = BCFixed<31>;
6161

lib/IRGen/IRGenSIL.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -963,9 +963,7 @@ class IRGenSILFunction :
963963
void visitRetainValueInst(RetainValueInst *i);
964964
void visitRetainValueAddrInst(RetainValueAddrInst *i);
965965
void visitCopyValueInst(CopyValueInst *i);
966-
void visitCopyUnownedValueInst(CopyUnownedValueInst *i) {
967-
llvm_unreachable("unimplemented");
968-
}
966+
void visitCopyUnownedValueInst(CopyUnownedValueInst *i);
969967
void visitReleaseValueInst(ReleaseValueInst *i);
970968
void visitReleaseValueAddrInst(ReleaseValueAddrInst *i);
971969
void visitDestroyValueInst(DestroyValueInst *i);
@@ -3844,6 +3842,18 @@ visitStrongRetainUnownedInst(swift::StrongRetainUnownedInst *i) {
38443842
: irgen::Atomicity::NonAtomic);
38453843
}
38463844

3845+
void IRGenSILFunction::visitCopyUnownedValueInst(
3846+
swift::CopyUnownedValueInst *i) {
3847+
Explosion in = getLoweredExplosion(i->getOperand());
3848+
auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType());
3849+
ti.strongRetainUnowned(*this, in, irgen::Atomicity::Atomic);
3850+
// Semantically we are just passing through the input parameter but as a
3851+
// strong reference... at LLVM IR level these type differences don't
3852+
// matter. So just set the lowered explosion appropriately.
3853+
Explosion output = getLoweredExplosion(i->getOperand());
3854+
setLoweredExplosion(i, output);
3855+
}
3856+
38473857
void IRGenSILFunction::visitUnownedRetainInst(swift::UnownedRetainInst *i) {
38483858
Explosion lowered = getLoweredExplosion(i->getOperand());
38493859
auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType());

lib/SIL/SILBuilder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,8 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) {
264264
isa<RetainValueInst>(Inst) || isa<UnownedRetainInst>(Inst) ||
265265
isa<UnownedReleaseInst>(Inst) || isa<StrongRetainUnownedInst>(Inst) ||
266266
isa<StoreWeakInst>(Inst) || isa<StrongRetainInst>(Inst) ||
267-
isa<AllocStackInst>(Inst) || isa<DeallocStackInst>(Inst))
267+
isa<AllocStackInst>(Inst) || isa<DeallocStackInst>(Inst) ||
268+
isa<CopyUnownedValueInst>(Inst))
268269
return false;
269270

270271
// Assign and copyaddr of trivial types cannot drop refcounts, and 'inits'

lib/SIL/SILVerifier.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,9 +1866,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
18661866
"Operand of unowned_retain");
18671867
require(unownedType->isLoadable(ResilienceExpansion::Maximal),
18681868
"unowned_retain requires unowned type to be loadable");
1869-
require(F.hasQualifiedOwnership(),
1870-
"copy_unowned_value is only valid in functions with qualified "
1871-
"ownership");
1869+
// *NOTE* We allow copy_unowned_value to be used throughout the entire
1870+
// pipeline even though it is a higher level instruction.
18721871
}
18731872

18741873
void checkDestroyValueInst(DestroyValueInst *I) {

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
13721372
case SILInstructionKind::DeallocStackInst:
13731373
case SILInstructionKind::StrongRetainInst:
13741374
case SILInstructionKind::StrongRetainUnownedInst:
1375+
case SILInstructionKind::CopyUnownedValueInst:
13751376
case SILInstructionKind::RetainValueInst:
13761377
case SILInstructionKind::UnownedRetainInst:
13771378
case SILInstructionKind::BranchInst:

lib/SILOptimizer/Analysis/MemoryBehavior.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class MemoryBehaviorVisitor
152152
}
153153
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainInst)
154154
REFCOUNTINC_MEMBEHAVIOR_INST(StrongRetainUnownedInst)
155+
REFCOUNTINC_MEMBEHAVIOR_INST(CopyUnownedValueInst)
155156
REFCOUNTINC_MEMBEHAVIOR_INST(UnownedRetainInst)
156157
REFCOUNTINC_MEMBEHAVIOR_INST(RetainValueInst)
157158
#undef REFCOUNTINC_MEMBEHAVIOR_INST

lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ void FunctionSideEffects::analyzeInstruction(SILInstruction *I) {
481481
return;
482482
case SILInstructionKind::StrongRetainInst:
483483
case SILInstructionKind::StrongRetainUnownedInst:
484+
case SILInstructionKind::CopyUnownedValueInst:
484485
case SILInstructionKind::RetainValueInst:
485486
case SILInstructionKind::UnownedRetainInst:
486487
getEffectsOn(I->getOperand(0))->Retains = true;

lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) {
7575
if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
7676
isa<RetainValueInst>(Inst) || isa<UnownedRetainInst>(Inst) ||
7777
isa<UnownedReleaseInst>(Inst) || isa<StrongRetainUnownedInst>(Inst) ||
78+
isa<CopyUnownedValueInst>(Inst) ||
7879
isa<StoreWeakInst>(Inst) || isa<StrongRetainInst>(Inst) ||
7980
isa<AllocStackInst>(Inst) || isa<DeallocStackInst>(Inst) ||
8081
isa<BeginAccessInst>(Inst) || isa<EndAccessInst>(Inst) ||

lib/SILOptimizer/Transforms/DeadStoreElimination.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ static bool isDeadStoreInertInstruction(SILInstruction *Inst) {
149149
switch (Inst->getKind()) {
150150
case SILInstructionKind::StrongRetainInst:
151151
case SILInstructionKind::StrongRetainUnownedInst:
152+
case SILInstructionKind::CopyUnownedValueInst:
152153
case SILInstructionKind::UnownedRetainInst:
153154
case SILInstructionKind::RetainValueInst:
154155
case SILInstructionKind::DeallocStackInst:

lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ struct OwnershipModelEliminatorVisitor
5757
bool visitStoreInst(StoreInst *SI);
5858
bool visitStoreBorrowInst(StoreBorrowInst *SI);
5959
bool visitCopyValueInst(CopyValueInst *CVI);
60-
bool visitCopyUnownedValueInst(CopyUnownedValueInst *CVI);
6160
bool visitDestroyValueInst(DestroyValueInst *DVI);
6261
bool visitLoadBorrowInst(LoadBorrowInst *LBI);
6362
bool visitBeginBorrowInst(BeginBorrowInst *BBI) {
@@ -160,19 +159,6 @@ bool OwnershipModelEliminatorVisitor::visitCopyValueInst(CopyValueInst *CVI) {
160159
return true;
161160
}
162161

163-
bool OwnershipModelEliminatorVisitor::visitCopyUnownedValueInst(
164-
CopyUnownedValueInst *CVI) {
165-
B.createStrongRetainUnowned(CVI->getLoc(), CVI->getOperand(),
166-
B.getDefaultAtomicity());
167-
// Users of copy_value_unowned expect an owned value. So we need to convert
168-
// our unowned value to a ref.
169-
auto *UTRI =
170-
B.createUnownedToRef(CVI->getLoc(), CVI->getOperand(), CVI->getType());
171-
CVI->replaceAllUsesWith(UTRI);
172-
CVI->eraseFromParent();
173-
return true;
174-
}
175-
176162
bool OwnershipModelEliminatorVisitor::visitUnmanagedRetainValueInst(
177163
UnmanagedRetainValueInst *URVI) {
178164
// Now that we have set the unqualified ownership flag, destroy value

0 commit comments

Comments
 (0)