Skip to content

Commit 7bc5c50

Browse files
authored
Merge pull request swiftlang#25985 from atrick/fix-arc-builtin
ARCAnalysis: fix canNeverUseObject to correctly handle builtins.
2 parents c0d76bf + cdae12c commit 7bc5c50

File tree

5 files changed

+88
-36
lines changed

5 files changed

+88
-36
lines changed

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,13 @@ bool mustGuaranteedUseValue(SILInstruction *User, SILValue Ptr,
7272
/// Returns true if \p Inst can never conservatively decrement reference counts.
7373
bool canNeverDecrementRefCounts(SILInstruction *Inst);
7474

75-
/// \returns True if \p User can never use a value in a way that requires the
76-
/// value to be alive.
75+
/// Returns true if \p Inst may access any indirect object either via an address
76+
/// or reference.
7777
///
78-
/// This is purposefully a negative query to contrast with canUseValue which is
79-
/// about a specific value while this is about general values.
80-
bool canNeverUseValues(SILInstruction *User);
78+
/// If false is returned and \p Inst has an address or reference type operand,
79+
/// then \p Inst only operates on the value of the address itself, not the
80+
/// memory. i.e. it does not dereference the address.
81+
bool canUseObject(SILInstruction *Inst);
8182

8283
/// \returns true if the user \p User may use \p Ptr in a manner that requires
8384
/// Ptr's life to be guaranteed to exist at this point.

lib/SILOptimizer/ARC/ARCRegionState.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,9 @@ void ARCRegionState::summarizeBlock(SILBasicBlock *BB) {
531531
SummarizedInterestingInsts.clear();
532532

533533
for (auto &I : *BB)
534-
if (!canNeverUseValues(&I) || I.mayReleaseOrReadRefCount() ||
534+
// FIXME: mayReleaseOrReadRefCount should be a strict subset of
535+
// canUseObject. If not, there is a bug in canUseObject.
536+
if (canUseObject(&I) || I.mayReleaseOrReadRefCount() ||
535537
isStrongEntranceInstruction(I))
536538
SummarizedInterestingInsts.push_back(&I);
537539
}

lib/SILOptimizer/Analysis/ARCAnalysis.cpp

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ bool swift::mayDecrementRefCount(SILInstruction *User,
9191
// Use Analysis
9292
//===----------------------------------------------------------------------===//
9393

94-
/// Returns true if a builtin apply cannot use reference counted values.
94+
/// Returns true if a builtin apply can use reference counted values.
9595
///
9696
/// The main case that this handles here are builtins that via read none imply
9797
/// that they cannot read globals and at the same time do not take any
@@ -106,28 +106,33 @@ static bool canApplyOfBuiltinUseNonTrivialValues(BuiltinInst *BInst) {
106106
if (II.hasAttribute(llvm::Attribute::ReadNone)) {
107107
for (auto &Op : BInst->getAllOperands()) {
108108
if (!Op.get()->getType().isTrivial(*F)) {
109-
return false;
109+
return true;
110110
}
111111
}
112112
}
113113

114-
return true;
114+
return false;
115115
}
116116

117117
auto &BI = BInst->getBuiltinInfo();
118-
if (BI.isReadNone()) {
119-
for (auto &Op : BInst->getAllOperands()) {
120-
if (!Op.get()->getType().isTrivial(*F)) {
121-
return false;
122-
}
118+
if (!BI.isReadNone())
119+
return true;
120+
121+
for (auto &Op : BInst->getAllOperands()) {
122+
if (!Op.get()->getType().isTrivial(*F)) {
123+
return true;
123124
}
124125
}
125-
126-
return true;
126+
return false;
127127
}
128128

129-
/// Returns true if Inst is a function that we know never uses ref count values.
130-
bool swift::canNeverUseValues(SILInstruction *Inst) {
129+
/// Returns true if \p Inst may access any indirect object either via an address
130+
/// or reference.
131+
///
132+
/// If these instructions do have an address or reference type operand, then
133+
/// they only operate on the value of the address itself, not the
134+
/// memory. i.e. they don't dereference the address.
135+
bool swift::canUseObject(SILInstruction *Inst) {
131136
switch (Inst->getKind()) {
132137
// These instructions do not use other values.
133138
case SILInstructionKind::FunctionRefInst:
@@ -142,33 +147,36 @@ bool swift::canNeverUseValues(SILInstruction *Inst) {
142147
case SILInstructionKind::AllocBoxInst:
143148
case SILInstructionKind::MetatypeInst:
144149
case SILInstructionKind::WitnessMethodInst:
145-
return true;
150+
return false;
146151

147152
// DeallocStackInst do not use reference counted values.
148153
case SILInstructionKind::DeallocStackInst:
149-
return true;
154+
return false;
150155

151156
// Debug values do not use referenced counted values in a manner we care
152157
// about.
153158
case SILInstructionKind::DebugValueInst:
154159
case SILInstructionKind::DebugValueAddrInst:
155-
return true;
160+
return false;
156161

157162
// Casts do not use pointers in a manner that we care about since we strip
158163
// them during our analysis. The reason for this is if the cast is not dead
159164
// then there must be some other use after the cast that we will protect if a
160165
// release is not in between the cast and the use.
166+
//
167+
// Note: UncheckedRefCastAddrInst moves a reference into a new object. While
168+
// the net reference count should be zero, there's no guarantee it won't
169+
// access the object.
161170
case SILInstructionKind::UpcastInst:
162171
case SILInstructionKind::AddressToPointerInst:
163172
case SILInstructionKind::PointerToAddressInst:
164173
case SILInstructionKind::UncheckedRefCastInst:
165-
case SILInstructionKind::UncheckedRefCastAddrInst:
166174
case SILInstructionKind::UncheckedAddrCastInst:
167175
case SILInstructionKind::RefToRawPointerInst:
168176
case SILInstructionKind::RawPointerToRefInst:
169177
case SILInstructionKind::UnconditionalCheckedCastInst:
170178
case SILInstructionKind::UncheckedBitwiseCastInst:
171-
return true;
179+
return false;
172180

173181
// If we have a trivial bit cast between trivial types, it is not something
174182
// that can use ref count ops in a way we care about. We do need to be careful
@@ -183,7 +191,7 @@ bool swift::canNeverUseValues(SILInstruction *Inst) {
183191
// safe.
184192
case SILInstructionKind::UncheckedTrivialBitCastInst: {
185193
SILValue Op = cast<UncheckedTrivialBitCastInst>(Inst)->getOperand();
186-
return Op->getType().isTrivial(*Inst->getFunction());
194+
return !Op->getType().isTrivial(*Inst->getFunction());
187195
}
188196

189197
// Typed GEPs do not use pointers. The user of the typed GEP may but we will
@@ -198,18 +206,18 @@ bool swift::canNeverUseValues(SILInstruction *Inst) {
198206
case SILInstructionKind::UncheckedEnumDataInst:
199207
case SILInstructionKind::IndexAddrInst:
200208
case SILInstructionKind::IndexRawPointerInst:
201-
return true;
209+
return false;
202210

203211
// Aggregate formation by themselves do not create new uses since it is their
204212
// users that would create the appropriate uses.
205213
case SILInstructionKind::EnumInst:
206214
case SILInstructionKind::StructInst:
207215
case SILInstructionKind::TupleInst:
208-
return true;
216+
return false;
209217

210218
// Only uses non reference counted values.
211219
case SILInstructionKind::CondFailInst:
212-
return true;
220+
return false;
213221

214222
case SILInstructionKind::BuiltinInst: {
215223
auto *BI = cast<BuiltinInst>(Inst);
@@ -221,9 +229,9 @@ bool swift::canNeverUseValues(SILInstruction *Inst) {
221229
// dead, LLVM will clean it up.
222230
case SILInstructionKind::BranchInst:
223231
case SILInstructionKind::CondBranchInst:
224-
return true;
225-
default:
226232
return false;
233+
default:
234+
return true;
227235
}
228236
}
229237

@@ -268,14 +276,15 @@ static bool canTerminatorUseValue(TermInst *TI, SILValue Ptr,
268276

269277

270278
bool swift::mayHaveSymmetricInterference(SILInstruction *User, SILValue Ptr, AliasAnalysis *AA) {
279+
// If Inst is an instruction that we know can never use values with reference
280+
// semantics, return true. Check this before AliasAnalysis because some memory
281+
// operations, like dealloc_stack, don't use ref counted values.
282+
if (!canUseObject(User))
283+
return false;
284+
271285
// Check whether releasing this value can call deinit and interfere with User.
272286
if (AA->mayValueReleaseInterfereWithInstruction(User, Ptr))
273287
return true;
274-
275-
// If Inst is an instruction that we know can never use values with reference
276-
// semantics, return true.
277-
if (canNeverUseValues(User))
278-
return false;
279288

280289
// If the user is a load or a store and we can prove that it does not access
281290
// the object then return true.

test/SILOptimizer/existential_transform.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,8 @@ func wrap_gcp<T:GP>(_ a:T,_ b:GP) -> Int {
301301
// CHECK: strong_retain
302302
// CHECK: apply
303303
// CHECK: destroy_addr
304-
// CHECK: dealloc_stack
305304
// CHECK: strong_release
305+
// CHECK: dealloc_stack
306306
// CHECK: return
307307
// CHECK: } // end sil function '$s21existential_transform3gcpySixAA2GPRzlF'
308308
@inline(never) func gcp<T:GP>(_ a:T) -> Int {
@@ -328,8 +328,8 @@ func wrap_gcp_arch<T:GP>(_ a:T,_ b:GP, _ c:inout Array<T>) -> Int {
328328
// CHECK: strong_retain
329329
// CHECK: apply
330330
// CHECK: destroy_addr
331-
// CHECK: dealloc_stack
332331
// CHECK: strong_release
332+
// CHECK: dealloc_stack
333333
// CHECK: return
334334
// CHECK-LABEL: } // end sil function '$s21existential_transform8gcp_archySix_SayxGztAA2GPRzlF'
335335
@inline(never) func gcp_arch<T:GP>(_ a:T, _ b:inout Array<T>) -> Int {

test/SILOptimizer/retain_release_code_motion.sil

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,3 +718,43 @@ bb2:
718718
return %9999 : $()
719719
}
720720

721+
// Hoist releases above dealloc_stack
722+
// CHECK-LABEL: sil @testReleaseHoistDeallocStack : $@convention(thin) (AnyObject) -> () {
723+
// CHECK: bb0(%0 : $AnyObject):
724+
// CHECK-NOT: retain
725+
// CHECK: [[A:%.*]] = alloc_stack $Int64
726+
// CHECK-NEXT dealloc_stack [[A]] : $*Int64
727+
// CHECK-NOT: release
728+
// CHECK-LABEL: } // end sil function 'testReleaseHoistDeallocStack'
729+
sil @testReleaseHoistDeallocStack : $@convention(thin) (AnyObject)->() {
730+
bb0(%0 : $AnyObject):
731+
strong_retain %0 : $AnyObject
732+
%alloc = alloc_stack $Int64
733+
dealloc_stack %alloc : $*Int64
734+
strong_release %0 : $AnyObject
735+
%34 = tuple ()
736+
return %34 : $()
737+
}
738+
739+
// Do not hoist releases above builtins that operate on object references.
740+
//
741+
// CHECK-RELEASE-HOISTING-LABEL: sil @testCopyArray : $@convention(thin) (_ContiguousArrayBuffer<AnyObject>, Builtin.Word, Builtin.Word) -> Builtin.RawPointer {
742+
// CHECK-RELEASE-HOISTING: bb0(%0 : $_ContiguousArrayBuffer<AnyObject>, %1 : $Builtin.Word, %2 : $Builtin.Word):
743+
// CHECK-RELEASE-HOISTING: builtin "copyArray"<AnyObject>
744+
// CHECK-RELEASE-HOISTING: release_value %0 : $_ContiguousArrayBuffer<AnyObject>
745+
// CHECK-RELEASE-HOISTING-LABEL: } // end sil function 'testCopyArray'
746+
sil @testCopyArray : $@convention(thin) (_ContiguousArrayBuffer<AnyObject>, Builtin.Word, Builtin.Word) -> Builtin.RawPointer {
747+
bb0(%0 : $_ContiguousArrayBuffer<AnyObject>, %1 : $Builtin.Word, %2 : $Builtin.Word):
748+
%eltty = metatype $@thick AnyObject.Protocol
749+
%newptr = builtin "allocRaw"(%1 : $Builtin.Word, %1 : $Builtin.Word) : $Builtin.RawPointer
750+
bind_memory %newptr : $Builtin.RawPointer, %1 : $Builtin.Word to $*AnyObject
751+
%storage = struct_extract %0 : $_ContiguousArrayBuffer<AnyObject>, #_ContiguousArrayBuffer._storage
752+
%elements = ref_tail_addr %storage : $__ContiguousArrayStorageBase, $AnyObject
753+
%eltptr = address_to_pointer %elements : $*AnyObject to $Builtin.RawPointer
754+
%objptr = struct $UnsafePointer<AnyObject> (%eltptr : $Builtin.RawPointer)
755+
%ptrdep = mark_dependence %objptr : $UnsafePointer<AnyObject> on %storage : $__ContiguousArrayStorageBase
756+
%rawptr = struct_extract %ptrdep : $UnsafePointer<AnyObject>, #UnsafePointer._rawValue
757+
%copy = builtin "copyArray"<AnyObject>(%eltty : $@thick AnyObject.Protocol, %newptr : $Builtin.RawPointer, %rawptr : $Builtin.RawPointer, %1 : $Builtin.Word) : $()
758+
release_value %0 : $_ContiguousArrayBuffer<AnyObject>
759+
return %newptr : $Builtin.RawPointer
760+
}

0 commit comments

Comments
 (0)