Skip to content

Commit 35097b5

Browse files
committed
Optimizer: simplify unconditional_checked_cast to existential metatypes.
Replace `unconditional_checked_cast` to an existential metatype with an `init_existential_metatype`, it the source is a conforming type. Note that init_existential_metatype is better than unconditional_checked_cast because it does not need to do any runtime casting.
1 parent df81283 commit 35097b5

File tree

8 files changed

+97
-55
lines changed

8 files changed

+97
-55
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCheckedCast.swift

+40
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SIL
14+
import AST
1415

1516
extension CheckedCastAddrBranchInst : OnoneSimplifiable {
1617
func simplify(_ context: SimplifyContext) {
@@ -53,3 +54,42 @@ private extension CheckedCastAddrBranchInst {
5354
context.erase(instruction: self)
5455
}
5556
}
57+
58+
extension UnconditionalCheckedCastInst : Simplifiable, SILCombineSimplifiable {
59+
func simplify(_ context: SimplifyContext) {
60+
tryOptimizeCastToExistentialMetatype(context)
61+
}
62+
}
63+
64+
private extension UnconditionalCheckedCastInst {
65+
// Replace
66+
// %1 = unconditional_checked_cast %0 : $@thick T.Type to any P.Type
67+
// with
68+
// %1 = init_existential_metatype %0 : $@thick S.Type, $@thick any P.Type
69+
// if type T conforms to protocol P.
70+
// Note that init_existential_metatype is better than unconditional_checked_cast because it does not need
71+
// to do any runtime casting.
72+
func tryOptimizeCastToExistentialMetatype(_ context: SimplifyContext) {
73+
guard targetFormalType.isExistentialMetatypeType,
74+
sourceFormalType.isMetatypeType,
75+
!sourceFormalType.isExistentialMetatypeType
76+
else {
77+
return
78+
}
79+
80+
let instanceTy = targetFormalType.instanceTypeOfMetatype
81+
guard let nominal = instanceTy.anyNominal,
82+
let proto = nominal as? ProtocolDecl
83+
else {
84+
return
85+
}
86+
let conformance = sourceFormalType.instanceTypeOfMetatype.checkConformance(to: proto)
87+
guard conformance.isValid else {
88+
return
89+
}
90+
91+
let builder = Builder(before: self, context)
92+
let iemt = builder.createInitExistentialMetatype(metatype: operand.value, existentialType: self.type, conformances: [conformance])
93+
self.replace(with: iemt, context)
94+
}
95+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ private func registerSwiftPasses() {
119119
registerForSILCombine(ClassifyBridgeObjectInst.self, { run(ClassifyBridgeObjectInst.self, $0) })
120120
registerForSILCombine(PointerToAddressInst.self, { run(PointerToAddressInst.self, $0) })
121121
registerForSILCombine(UncheckedEnumDataInst.self, { run(UncheckedEnumDataInst.self, $0) })
122+
registerForSILCombine(UnconditionalCheckedCastInst.self, { run(UnconditionalCheckedCastInst.self, $0) })
122123

123124
// Test passes
124125
registerPass(aliasInfoDumper, { aliasInfoDumper.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

+1
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ SWIFT_SILCOMBINE_PASS(DestructureTupleInst)
539539
SWIFT_SILCOMBINE_PASS(PointerToAddressInst)
540540
SWIFT_SILCOMBINE_PASS(TypeValueInst)
541541
SWIFT_SILCOMBINE_PASS(UncheckedEnumDataInst)
542+
SWIFT_SILCOMBINE_PASS_WITH_LEGACY(UnconditionalCheckedCastInst)
542543

543544
#undef IRGEN_PASS
544545
#undef SWIFT_MODULE_PASS

lib/SILOptimizer/SILCombiner/SILCombiner.h

-2
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,6 @@ class SILCombiner :
266266
SILInstruction *visitEndCOWMutationInst(EndCOWMutationInst *URCI);
267267
SILInstruction *visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI);
268268
SILInstruction *visitBridgeObjectToRefInst(BridgeObjectToRefInst *BORI);
269-
SILInstruction *visitUnconditionalCheckedCastInst(
270-
UnconditionalCheckedCastInst *UCCI);
271269
SILInstruction *
272270
visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *UCCAI);
273271
SILInstruction *visitRawPointerToRefInst(RawPointerToRefInst *RPTR);

lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ SILInstruction *SILCombiner::visitUnconditionalCheckedCastAddrInst(
516516

517517
SILInstruction *
518518
SILCombiner::
519-
visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) {
519+
legacyVisitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) {
520520
CastOpt.optimizeUnconditionalCheckedCastInst(UCCI);
521521
if (UCCI->isDeleted()) {
522522
MadeChange = true;

test/SILOptimizer/sil_combine_uncheck.sil

-26
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@ class D : C {
1515
class E {
1616
}
1717

18-
// CHECK-LABEL: sil @test_unconditional_checked_cast
19-
// CHECK: bb0
20-
// CHECK-NEXT: unchecked_ref_cast
21-
// CHECK-NEXT: return
22-
sil @test_unconditional_checked_cast : $@convention(thin) <T_0_0, T_0_1> (RawBuffer) -> HeapBufferStorage<T_0_0, T_0_1> {
23-
bb0(%0 : $RawBuffer):
24-
%3 = unconditional_checked_cast %0 : $RawBuffer to HeapBufferStorage<T_0_0, T_0_1>
25-
return %3 : $HeapBufferStorage<T_0_0, T_0_1>
26-
}
27-
2818
// CHECK-LABEL: sil @test_cond_fail
2919
// CHECK: bb0
3020
// CHECK-NEXT: tuple ()
@@ -62,19 +52,3 @@ bb0(%0 : $C):
6252
return %3 : $C
6353
}
6454

65-
protocol Class : class {
66-
}
67-
68-
class G : Class {
69-
}
70-
71-
// CHECK-LABEL: sil @test_unconditional_checked_cast_class_exist
72-
// CHECK: unchecked_ref_cast
73-
// CHECK: return
74-
sil @test_unconditional_checked_cast_class_exist : $@convention(thin) (@owned Class) -> @owned G {
75-
bb0(%0 : $Class):
76-
strong_retain %0 : $Class
77-
%2 = unconditional_checked_cast %0 : $Class to G
78-
strong_release %0 : $Class
79-
return %2 : $G
80-
}

test/SILOptimizer/sil_combine_uncheck_ossa.sil

-26
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@ class D : C {
1515
class E {
1616
}
1717

18-
// CHECK-LABEL: sil [ossa] @test_unconditional_checked_cast
19-
// CHECK: bb0
20-
// CHECK-NEXT: unchecked_ref_cast
21-
// CHECK-NEXT: return
22-
sil [ossa] @test_unconditional_checked_cast : $@convention(thin) <T_0_0, T_0_1> (@owned RawBuffer) -> @owned HeapBufferStorage<T_0_0, T_0_1> {
23-
bb0(%0 : @owned $RawBuffer):
24-
%3 = unconditional_checked_cast %0 : $RawBuffer to HeapBufferStorage<T_0_0, T_0_1>
25-
return %3 : $HeapBufferStorage<T_0_0, T_0_1>
26-
}
27-
2818
// CHECK-LABEL: sil [ossa] @test_cond_fail
2919
// CHECK: bb0
3020
// CHECK-NEXT: tuple ()
@@ -60,19 +50,3 @@ bb0(%0 : @owned $C):
6050
return %3 : $C
6151
}
6252

63-
protocol Class : class {
64-
}
65-
66-
class G : Class {
67-
}
68-
69-
// CHECK-LABEL: sil [ossa] @test_unconditional_checked_cast_class_exist
70-
// CHECK: unchecked_ref_cast
71-
// CHECK: return
72-
sil [ossa] @test_unconditional_checked_cast_class_exist : $@convention(thin) (@owned Class) -> @owned G {
73-
bb0(%0 : @owned $Class):
74-
%0a = copy_value %0 : $Class
75-
%2 = unconditional_checked_cast %0a : $Class to G
76-
destroy_value %0 : $Class
77-
return %2 : $G
78-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=unconditional_checked_cast | %FileCheck %s
2+
3+
import Swift
4+
import Builtin
5+
6+
protocol P {}
7+
protocol PC: AnyObject {}
8+
9+
struct S : P {}
10+
11+
struct T {}
12+
13+
class C {}
14+
15+
// CHECK-LABEL: sil [ossa] @test_conforming_struct :
16+
// CHECK: %1 = init_existential_metatype %0, $@thick any P.Type
17+
// CHECK-NEXT: return %1
18+
// CHECK: } // end sil function 'test_conforming_struct'
19+
sil [ossa] @test_conforming_struct : $@convention(thin) (@thick S.Type) -> @thick any P.Type {
20+
bb0(%0 : $@thick S.Type):
21+
%1 = unconditional_checked_cast %0 to any P.Type
22+
return %1
23+
}
24+
25+
// CHECK-LABEL: sil [ossa] @test_non_conforming_struct :
26+
// CHECK: %1 = unconditional_checked_cast %0
27+
// CHECK-NEXT: return %1
28+
// CHECK: } // end sil function 'test_non_conforming_struct'
29+
sil [ossa] @test_non_conforming_struct : $@convention(thin) (@thick T.Type) -> @thick any P.Type {
30+
bb0(%0 : $@thick T.Type):
31+
%1 = unconditional_checked_cast %0 to any P.Type
32+
return %1
33+
}
34+
35+
36+
// CHECK-LABEL: sil [ossa] @test_non_metatype :
37+
// CHECK: %1 = unconditional_checked_cast %0
38+
// CHECK-NEXT: return %1
39+
// CHECK: } // end sil function 'test_non_metatype'
40+
sil [ossa] @test_non_metatype : $@convention(thin) (@guaranteed C) -> any PC {
41+
bb0(%0 : @guaranteed $C):
42+
%1 = unconditional_checked_cast %0 to any PC
43+
return %1
44+
}
45+
46+
// CHECK-LABEL: sil [ossa] @test_non_existential_target :
47+
// CHECK: %1 = unconditional_checked_cast %0
48+
// CHECK-NEXT: return %1
49+
// CHECK: } // end sil function 'test_non_existential_target'
50+
sil [ossa] @test_non_existential_target : $@convention(thin) (@thick S.Type) -> @thick Int.Type {
51+
bb0(%0 : $@thick S.Type):
52+
%1 = unconditional_checked_cast %0 to Int.Type
53+
return %1
54+
}

0 commit comments

Comments
 (0)