Skip to content

Commit 384d7f7

Browse files
authored
Merge pull request #75434 from eeckstein/fix-walkutils
WalkUtils: handle `unsafe_ref_cast` which casts between AnyObject and a class
2 parents 044d8c9 + b739c62 commit 384d7f7

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

SwiftCompilerSources/Sources/SIL/Type.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
5656
public var isEnum: Bool { bridged.isEnumOrBoundGenericEnum() }
5757
public var isFunction: Bool { bridged.isFunction() }
5858
public var isMetatype: Bool { bridged.isMetatype() }
59+
public var isClassExistential: Bool { bridged.isClassExistential() }
5960
public var isNoEscapeFunction: Bool { bridged.isNoEscapeFunction() }
6061
public var containsNoEscapeFunction: Bool { bridged.containsNoEscapeFunction() }
6162
public var isThickFunction: Bool { bridged.isThickFunction() }

SwiftCompilerSources/Sources/SIL/Utilities/WalkUtils.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,17 @@ extension ValueDefUseWalker {
360360
return unmatchedPath(value: operand, path: path)
361361
}
362362
case is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
363-
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst, is EndInitLetRefInst,
363+
is UpcastInst, is EndCOWMutationInst, is EndInitLetRefInst,
364364
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkUnresolvedNonCopyableValueInst:
365365
return walkDownUses(ofValue: (instruction as! SingleValueInstruction), path: path)
366+
case let urc as UncheckedRefCastInst:
367+
if urc.type.isClassExistential || urc.fromInstance.type.isClassExistential {
368+
// Sometimes `unchecked_ref_cast` is misused to cast between AnyObject and a class (instead of
369+
// init_existential_ref and open_existential_ref).
370+
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
371+
return leafUse(value: operand, path: path)
372+
}
373+
return walkDownUses(ofValue: urc, path: path)
366374
case let beginDealloc as BeginDeallocRefInst:
367375
if operand.index == 0 {
368376
return walkDownUses(ofValue: beginDealloc, path: path)
@@ -680,10 +688,18 @@ extension ValueUseDefWalker {
680688
case let oer as OpenExistentialRefInst:
681689
return walkUp(value: oer.existential, path: path.push(.existential, index: 0))
682690
case is BeginBorrowInst, is CopyValueInst, is MoveValueInst,
683-
is UpcastInst, is UncheckedRefCastInst, is EndCOWMutationInst, is EndInitLetRefInst,
691+
is UpcastInst, is EndCOWMutationInst, is EndInitLetRefInst,
684692
is BeginDeallocRefInst,
685693
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkUnresolvedNonCopyableValueInst:
686694
return walkUp(value: (def as! Instruction).operands[0].value, path: path)
695+
case let urc as UncheckedRefCastInst:
696+
if urc.type.isClassExistential || urc.fromInstance.type.isClassExistential {
697+
// Sometimes `unchecked_ref_cast` is misused to cast between AnyObject and a class (instead of
698+
// init_existential_ref and open_existential_ref).
699+
// We need to ignore this because otherwise the path wouldn't contain the right `existential` field kind.
700+
return rootDef(value: urc, path: path)
701+
}
702+
return walkUp(value: urc.fromInstance, path: path)
687703
case let arg as Argument:
688704
if let phi = Phi(arg) {
689705
for incoming in phi.incomingValues {

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ struct BridgedType {
400400
BRIDGED_INLINE bool isEnumOrBoundGenericEnum() const;
401401
BRIDGED_INLINE bool isFunction() const;
402402
BRIDGED_INLINE bool isMetatype() const;
403+
BRIDGED_INLINE bool isClassExistential() const;
403404
BRIDGED_INLINE bool isNoEscapeFunction() const;
404405
BRIDGED_INLINE bool containsNoEscapeFunction() const;
405406
BRIDGED_INLINE bool isThickFunction() const;

include/swift/SIL/SILBridgingImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ bool BridgedType::isMetatype() const {
296296
return unbridged().isMetatype();
297297
}
298298

299+
bool BridgedType::isClassExistential() const {
300+
return unbridged().isClassExistentialType();
301+
}
302+
299303
bool BridgedType::isNoEscapeFunction() const {
300304
return unbridged().isNoEscapeFunction();
301305
}

test/SILOptimizer/addr_escape_info.sil

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class X {
1717
@_hasStorage var s: Str
1818
}
1919

20+
class D: X {}
21+
2022
struct Container {
2123
@_hasStorage var x: X
2224
}
@@ -755,3 +757,42 @@ bb0:
755757
return %8 : $()
756758
}
757759

760+
// CHECK-LABEL: Address escape information for test_unchecked_ref_cast:
761+
// CHECK: pair 0 - 1
762+
// CHECK-NEXT: %4 = ref_element_addr %2 : $X, #X.s
763+
// CHECK-NEXT: %6 = ref_element_addr %3 : $X, #X.s
764+
// CHECK-NEXT: no alias
765+
// CHECK: End function test_unchecked_ref_cast
766+
sil @test_unchecked_ref_cast : $@convention(thin) () -> () {
767+
bb0:
768+
%0 = alloc_ref $D
769+
%1 = alloc_ref $D
770+
%2 = unchecked_ref_cast %0 : $D to $X
771+
%3 = unchecked_ref_cast %1 : $D to $X
772+
%4 = ref_element_addr %2 : $X, #X.s
773+
fix_lifetime %4 : $*Str
774+
%6 = ref_element_addr %3 : $X, #X.s
775+
fix_lifetime %6 : $*Str
776+
%r = tuple ()
777+
return %r : $()
778+
}
779+
780+
// CHECK-LABEL: Address escape information for test_anyobject_cast:
781+
// CHECK: pair 0 - 1
782+
// CHECK-NEXT: %3 = ref_element_addr %2 : $X, #X.s
783+
// CHECK-NEXT: %5 = ref_element_addr %2 : $X, #X.s
784+
// CHECK-NEXT: may alias
785+
// CHECK: End function test_anyobject_cast
786+
sil @test_anyobject_cast : $@convention(thin) () -> () {
787+
bb0:
788+
%0 = alloc_ref $X
789+
%1 = init_existential_ref %0 : $X : $X, $AnyObject
790+
%2 = unchecked_ref_cast %1 : $AnyObject to $X
791+
%3 = ref_element_addr %2 : $X, #X.s
792+
fix_lifetime %3 : $*Str
793+
%5 = ref_element_addr %2 : $X, #X.s
794+
fix_lifetime %5 : $*Str
795+
%r = tuple ()
796+
return %r : $()
797+
}
798+

test/SILOptimizer/escape_info.sil

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,3 +1445,15 @@ bb0(%0 : $F):
14451445
return %r : $()
14461446
}
14471447

1448+
// CHECK-LABEL: Escape information for test_unchecked_ref_cast:
1449+
// CHECK: return[]: %0 = alloc_ref $Derived
1450+
// CHECK: - : %1 = alloc_ref $Derived
1451+
// CHECK: End function test_unchecked_ref_cast
1452+
sil @test_unchecked_ref_cast : $@convention(thin) () -> @owned X {
1453+
bb0:
1454+
%0 = alloc_ref $Derived
1455+
%1 = alloc_ref $Derived
1456+
%2 = unchecked_ref_cast %0 : $Derived to $X
1457+
%3 = unchecked_ref_cast %1 : $Derived to $X
1458+
return %2 : $X
1459+
}

test/SILOptimizer/redundant_load_elim.sil

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ final class NewHalfOpenRangeGenerator : NewRangeGenerator1 {
123123
override init(start: Int32, end: Int32)
124124
}
125125

126+
class COpt {
127+
final var i: Optional<Int32>
128+
init()
129+
}
130+
126131
sil_global @total : $Int32
127132

128133
sil @use : $@convention(thin) (Builtin.Int32) -> ()
@@ -1315,3 +1320,24 @@ bb0(%0 : $Int):
13151320
dealloc_stack %1 : $*Int
13161321
return %53 : $Int
13171322
}
1323+
1324+
// CHECK-LABEL: sil [ossa] @test_anyobject_cast :
1325+
// CHECK: store
1326+
// CHECK: [[L:%[0-9]+]] = load
1327+
// CHECK: return [[L]]
1328+
// CHECK-LABEL: } // end sil function 'test_anyobject_cast'
1329+
sil [ossa] @test_anyobject_cast : $@convention(thin) (@guaranteed COpt, Int32) -> Optional<Int32> {
1330+
bb0(%0 : @guaranteed $COpt, %1 : $Int32):
1331+
%2 = init_existential_ref %0 : $COpt : $COpt, $AnyObject
1332+
%3 = unchecked_ref_cast %2 : $AnyObject to $COpt
1333+
%4 = ref_element_addr %3 : $COpt, #COpt.i
1334+
%5 = load [trivial] %4 : $*Optional<Int32>
1335+
%6 = ref_element_addr %3 : $COpt, #COpt.i
1336+
%7 = init_enum_data_addr %6 : $*Optional<Int32>, #Optional.some!enumelt
1337+
store %1 to [trivial] %7 : $*Int32
1338+
%9 = ref_element_addr %3 : $COpt, #COpt.i
1339+
%10 = load [trivial] %9 : $*Optional<Int32>
1340+
return %10 : $Optional<Int32>
1341+
}
1342+
1343+

0 commit comments

Comments
 (0)