Skip to content

Commit 971932d

Browse files
authored
Merge pull request #34159 from atrick/opt-ref-root
2 parents db594b5 + d8dd620 commit 971932d

File tree

8 files changed

+265
-71
lines changed

8 files changed

+265
-71
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,10 @@ SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess);
12181218

12191219
namespace swift {
12201220

1221+
/// Return true if \p svi is a cast that preserves the identity and
1222+
/// reference-counting equivalence of the reference at operand zero.
1223+
bool isRCIdentityPreservingCast(SingleValueInstruction *svi);
1224+
12211225
/// If \p svi is an access projection, return an address-type operand for the
12221226
/// incoming address.
12231227
///

lib/SIL/IR/SILType.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ bool SILType::isPointerSizeAndAligned() {
129129
//
130130
// TODO: handle casting to a loadable existential by generating
131131
// init_existential_ref. Until then, only promote to a heap object dest.
132+
//
133+
// This cannot allow trivial-to-reference casts, as required by
134+
// isRCIdentityPreservingCast.
132135
bool SILType::canRefCast(SILType operTy, SILType resultTy, SILModule &M) {
133136
auto fromTy = operTy.unwrapOptionalType();
134137
auto toTy = resultTy.unwrapOptionalType();

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#define DEBUG_TYPE "sil-inst-utils"
1414
#include "swift/SIL/InstructionUtils.h"
15+
#include "swift/SIL/MemAccessUtils.h"
1516
#include "swift/AST/SubstitutionMap.h"
1617
#include "swift/Basic/NullablePtr.h"
1718
#include "swift/Basic/STLExtras.h"
@@ -64,20 +65,6 @@ SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) {
6465
}
6566
}
6667

67-
static bool isRCIdentityPreservingCast(ValueKind Kind) {
68-
switch (Kind) {
69-
case ValueKind::UpcastInst:
70-
case ValueKind::UncheckedRefCastInst:
71-
case ValueKind::UnconditionalCheckedCastInst:
72-
case ValueKind::UnconditionalCheckedCastValueInst:
73-
case ValueKind::RefToBridgeObjectInst:
74-
case ValueKind::BridgeObjectToRefInst:
75-
return true;
76-
default:
77-
return false;
78-
}
79-
}
80-
8168
/// Return the underlying SILValue after stripping off identity SILArguments if
8269
/// we belong to a BB with one predecessor.
8370
SILValue swift::stripSinglePredecessorArgs(SILValue V) {
@@ -122,42 +109,39 @@ SILValue swift::stripSinglePredecessorArgs(SILValue V) {
122109
}
123110
}
124111

125-
SILValue swift::stripCastsWithoutMarkDependence(SILValue V) {
112+
SILValue swift::stripCastsWithoutMarkDependence(SILValue v) {
126113
while (true) {
127-
V = stripSinglePredecessorArgs(V);
128-
129-
auto K = V->getKind();
130-
if (isRCIdentityPreservingCast(K)
131-
|| K == ValueKind::UncheckedTrivialBitCastInst
132-
|| K == ValueKind::BeginAccessInst
133-
|| K == ValueKind::EndCOWMutationInst) {
134-
V = cast<SingleValueInstruction>(V)->getOperand(0);
135-
continue;
114+
v = stripSinglePredecessorArgs(v);
115+
if (auto *svi = dyn_cast<SingleValueInstruction>(v)) {
116+
if (isRCIdentityPreservingCast(svi)
117+
|| isa<UncheckedTrivialBitCastInst>(v)
118+
|| isa<BeginAccessInst>(v)
119+
|| isa<EndCOWMutationInst>(v)) {
120+
v = svi->getOperand(0);
121+
continue;
122+
}
136123
}
137-
138-
return V;
124+
return v;
139125
}
140126
}
141127

142128
SILValue swift::stripCasts(SILValue v) {
143129
while (true) {
144130
v = stripSinglePredecessorArgs(v);
145-
146-
auto k = v->getKind();
147-
if (isRCIdentityPreservingCast(k)
148-
|| k == ValueKind::UncheckedTrivialBitCastInst
149-
|| k == ValueKind::MarkDependenceInst
150-
|| k == ValueKind::BeginAccessInst) {
151-
v = cast<SingleValueInstruction>(v)->getOperand(0);
152-
continue;
131+
if (auto *svi = dyn_cast<SingleValueInstruction>(v)) {
132+
if (isRCIdentityPreservingCast(svi)
133+
|| isa<UncheckedTrivialBitCastInst>(v)
134+
|| isa<MarkDependenceInst>(v)
135+
|| isa<BeginAccessInst>(v)) {
136+
v = cast<SingleValueInstruction>(v)->getOperand(0);
137+
continue;
138+
}
153139
}
154-
155140
SILValue v2 = stripOwnershipInsts(v);
156141
if (v2 != v) {
157142
v = v2;
158143
continue;
159144
}
160-
161145
return v;
162146
}
163147
}

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -365,15 +365,38 @@ bool swift::isLetAddress(SILValue address) {
365365
// MARK: FindReferenceRoot
366366
//===----------------------------------------------------------------------===//
367367

368+
// On some platforms, casting from a metatype to a reference type dynamically
369+
// allocates a ref-counted box for the metatype. Naturally that is the place
370+
// where RC-identity begins. Considering the source of such a casts to be
371+
// RC-identical would confuse ARC optimization, which might eliminate a retain
372+
// of such an object completely.
373+
//
374+
// The SILVerifier checks that none of these operations cast a nontrivial value
375+
// to a reference except unconditional_checked_cast[_value].
376+
bool swift::isRCIdentityPreservingCast(SingleValueInstruction *svi) {
377+
switch (svi->getKind()) {
378+
default:
379+
return false;
380+
// Ignore ownership casts
381+
case SILInstructionKind::CopyValueInst:
382+
case SILInstructionKind::BeginBorrowInst:
383+
// Ignore class type casts
384+
case SILInstructionKind::UpcastInst:
385+
case SILInstructionKind::UncheckedRefCastInst:
386+
case SILInstructionKind::RefToBridgeObjectInst:
387+
case SILInstructionKind::BridgeObjectToRefInst:
388+
return true;
389+
case SILInstructionKind::UnconditionalCheckedCastInst:
390+
case SILInstructionKind::UnconditionalCheckedCastValueInst:
391+
// If the source is nontrivial, then this checked cast may actually create a
392+
// new object, so its source is not ref-count equivalent.
393+
return !svi->getOperand(0)->getType().isTrivial(*svi->getFunction());
394+
}
395+
}
396+
368397
namespace {
369398

370399
// Essentially RC identity where the starting point is already a reference.
371-
//
372-
// FIXME: We cannot currently see through type casts for true RC identity,
373-
// because property indices are not unique within a class hierarchy. Either fix
374-
// RefElementAddr::getFieldNo so it returns a unique index, or figure out a
375-
// different way to encode the property's VarDecl. (Note that the lack of a
376-
// unique property index could be the source of bugs elsewhere).
377400
class FindReferenceRoot {
378401
SmallPtrSet<SILPhiArgument *, 4> visitedPhis;
379402

@@ -386,18 +409,13 @@ class FindReferenceRoot {
386409

387410
protected:
388411
// Return an invalid value for a phi with no resolved inputs.
389-
//
390-
// FIXME: We should be able to see through RC identity like this:
391-
// nextRoot = stripRCIdentityCasts(nextRoot);
392412
SILValue recursiveFindRoot(SILValue ref) {
393-
while (true) {
394-
SILValue nextRoot = ref;
395-
nextRoot = stripOwnershipInsts(nextRoot);
396-
if (nextRoot == ref)
413+
while (auto *svi = dyn_cast<SingleValueInstruction>(ref)) {
414+
if (!isRCIdentityPreservingCast(svi)) {
397415
break;
398-
ref = nextRoot;
399-
}
400-
416+
}
417+
ref = svi->getOperand(0);
418+
};
401419
auto *phi = dyn_cast<SILPhiArgument>(ref);
402420
if (!phi || !phi->isPhiArgument()) {
403421
return ref;
@@ -558,8 +576,16 @@ const ValueDecl *AccessedStorage::getDecl(SILValue base) const {
558576
return global->getDecl();
559577

560578
case Class: {
561-
auto *decl = getObject()->getType().getNominalOrBoundGenericNominal();
562-
return decl ? getIndexedField(decl, getPropertyIndex()) : nullptr;
579+
// The property index is relative to the VarDecl in ref_element_addr, and
580+
// can only be reliably determined when the base is avaiable. Otherwise, we
581+
// can only make a best effort to extract it from the object type, which
582+
// might not even be a class in the case of bridge objects.
583+
if (ClassDecl *classDecl =
584+
base ? cast<RefElementAddrInst>(base)->getClassDecl()
585+
: getObject()->getType().getClassOrBoundGenericClass()) {
586+
return getIndexedField(classDecl, getPropertyIndex());
587+
}
588+
return nullptr;
563589
}
564590
case Tail:
565591
return nullptr;
@@ -1297,7 +1323,14 @@ void AccessPathDefUseTraversal::followProjection(SingleValueInstruction *svi,
12971323
AccessPathDefUseTraversal::UseKind
12981324
AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi,
12991325
DFSEntry dfs) {
1300-
if (isAccessedStorageCast(svi)) {
1326+
if (dfs.isRef()) {
1327+
if (isRCIdentityPreservingCast(svi)) {
1328+
pushUsers(svi, dfs);
1329+
return IgnoredUse;
1330+
}
1331+
// 'svi' will be processed below as either RefElementAddrInst,
1332+
// RefTailAddrInst, or some unknown LeafUse.
1333+
} else if (isAccessedStorageCast(svi)) {
13011334
pushUsers(svi, dfs);
13021335
return IgnoredUse;
13031336
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3591,6 +3591,15 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
35913591
verifyOpenedArchetype(CI, CI->getType().getASTType());
35923592
}
35933593

3594+
// Make sure that opcodes handled by isRCIdentityPreservingCast cannot cast
3595+
// from a trivial to a reference type. Such a cast may dynamically
3596+
// instantiate a new reference-counted object.
3597+
void checkNoTrivialToReferenceCast(SingleValueInstruction *svi) {
3598+
require(!svi->getOperand(0)->getType().isTrivial(*svi->getFunction())
3599+
|| svi->getType().isTrivial(*svi->getFunction()),
3600+
"Unexpected trivial-to-reference conversion: ");
3601+
}
3602+
35943603
/// Verify if a given type is or contains an opened archetype or dynamic self.
35953604
/// If this is the case, verify that the provided instruction has a type
35963605
/// dependent operand for it.
@@ -3753,6 +3762,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
37533762
void checkUpcastInst(UpcastInst *UI) {
37543763
require(UI->getType() != UI->getOperand()->getType(),
37553764
"can't upcast to same type");
3765+
checkNoTrivialToReferenceCast(UI);
37563766
if (UI->getType().is<MetatypeType>()) {
37573767
CanType instTy(UI->getType().castTo<MetatypeType>()->getInstanceType());
37583768
require(UI->getOperand()->getType().is<MetatypeType>(),

test/SILOptimizer/accessed_storage.sil

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ struct MyArray<T> {
131131

132132
// CHECK-LABEL: @arrayValue
133133
// CHECK: load [trivial] %{{.*}} : $*Builtin.Int64
134-
// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
134+
// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
135135
// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64
136-
// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
136+
// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
137137
// CHECK: Path: (@3,#0)
138138
// CHECK: load [trivial] %{{.*}} : $*Builtin.Int64
139-
// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
139+
// CHECK: Tail %{{.*}} = struct_extract %5 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
140140
// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $Int64
141-
// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
141+
// CHECK: Storage: Tail %{{.*}} = struct_extract %5 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
142142
// CHECK: Path: (@4,#0)
143143
sil [ossa] @arrayValue : $@convention(thin) (@guaranteed MyArray<Int64>) -> Int64 {
144144
bb0(%0 : @guaranteed $MyArray<Int64>):
@@ -191,7 +191,7 @@ bb0(%0 : $Builtin.RawPointer):
191191

192192
// CHECK-LABEL: @staticIndexAddrSubobject
193193
// CHECK: ###For MemOp: %5 = load [trivial] %4 : $*Int64
194-
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer // user: %1
194+
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer
195195
// CHECK: INVALID
196196
sil [ossa] @staticIndexAddrSubobject : $@convention(thin) (Builtin.RawPointer) -> () {
197197
bb0(%0 : $Builtin.RawPointer):
@@ -271,10 +271,10 @@ class B : A {
271271
// CHECK: Field: var prop1: Int64 Index: 1
272272
// CHECK: Path: ()
273273
// CHECK: store %0 to %{{.*}} : $*Int64
274-
// CHECK: Class %{{.*}} = upcast %{{.*}} : $B to $A
274+
// CHECK: Class %{{.*}} = alloc_ref $B
275275
// CHECK: Field: var prop0: Int64 Index: 0
276276
// CHECK: Base: %{{.*}} = ref_element_addr %{{.*}} : $A, #A.prop0
277-
// CHECK: Storage: Class %{{.*}} = upcast %{{.*}} : $B to $A
277+
// CHECK: Storage: Class %{{.*}} = alloc_ref $B
278278
// CHECK: Field: var prop0: Int64 Index: 0
279279
// CHECK: Path: ()
280280
sil @testNonUniquePropertyIndex : $@convention(thin) (Int64) -> () {
@@ -291,21 +291,21 @@ bb0(%0 : $Int64):
291291

292292
// CHECK-LABEL: @testRefTailAndStruct0
293293
// CHECK: %{{.*}} = load %{{.*}} : $*Builtin.Int64
294-
// CHECK: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
295-
// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0
294+
// CHECK: Class %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
295+
// CHECK: Index: 0
296296
// CHECK: Base: %{{.*}} = ref_element_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
297-
// CHECK: Storage: Class %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
298-
// CHECK: Field: @usableFromInline final var countAndCapacity: _ArrayBody Index: 0
297+
// CHECK: Storage: Class %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
298+
// CHECK: Index: 0
299299
// CHECK: Path: (#0,#0,#0)
300300
// CHECK: %{{.*}} = load %{{.*}} : $*_StringGuts
301-
// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
301+
// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
302302
// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String
303-
// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
303+
// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
304304
// CHECK: Path: (#0)
305305
// CHECK: %{{.*}} = load %{{.*}} : $*String
306-
// CHECK: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
306+
// CHECK: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
307307
// CHECK: Base: %{{.*}} = ref_tail_addr [immutable] %{{.*}} : $__ContiguousArrayStorageBase, $String
308-
// CHECK: Storage: Tail %{{.*}} = unchecked_ref_cast %{{.*}} : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
308+
// CHECK: Storage: Tail %{{.*}} = struct_extract %{{.*}} : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
309309
// CHECK: Path: ()
310310
sil hidden [noinline] @testRefTailAndStruct0 : $@convention(thin) (@owned MyArray<String>) -> () {
311311
bb0(%0 : $MyArray<String>):

test/SILOptimizer/accessed_storage_analysis.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ class BaseClass {
638638
class SubClass : BaseClass {}
639639

640640
// CHECK-LABEL: @testElementAddressPhiAccess
641-
// CHECK: [read] [no_nested_conflict] Class %{{.*}} = upcast %0 : $SubClass to $BaseClass
641+
// CHECK: [read] [no_nested_conflict] Class %{{.*}} = argument of bb0 : $SubClass
642642
// CHECK: Field: @_hasInitialValue var f: Float
643643
sil @testElementAddressPhiAccess : $@convention(thin) (@guaranteed SubClass) -> () {
644644
bb0(%0 : $SubClass):

0 commit comments

Comments
 (0)