Skip to content

Commit ed2f43e

Browse files
authored
Merge pull request #78354 from meg-gupta/enablesilcombine
Enable silcombine to propagate concrete type of existentials in ossa
2 parents aa2cfd3 + 5e1f9a3 commit ed2f43e

File tree

6 files changed

+156
-26
lines changed

6 files changed

+156
-26
lines changed

lib/SILOptimizer/PassManager/PassPipeline.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,9 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) {
10111011
// importing this module.
10121012
P.addSerializeSILPass();
10131013

1014+
if (P.getOptions().EnableOSSAModules && SILPrintFinalOSSAModule) {
1015+
addModulePrinterPipeline(P, "SIL Print Final OSSA Module");
1016+
}
10141017
// Strip any transparent functions that still have ownership.
10151018
P.addOwnershipModelEliminator();
10161019

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -1320,10 +1320,6 @@ SILInstruction *SILCombiner::createApplyWithConcreteType(
13201320
SILInstruction *
13211321
SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
13221322
WitnessMethodInst *WMI) {
1323-
// We do not perform this optimization in OSSA. In OSSA, we will have opaque
1324-
// values we will redo this.
1325-
if (WMI->getFunction()->hasOwnership())
1326-
return nullptr;
13271323

13281324
// Check if it is legal to perform the propagation.
13291325
if (WMI->getConformance().isConcrete())
@@ -1403,9 +1399,6 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
14031399
/// ==> apply %f<C : P>(%ref)
14041400
SILInstruction *
14051401
SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply) {
1406-
if (Apply.getFunction()->hasOwnership())
1407-
return nullptr;
1408-
14091402
// This optimization requires a generic argument.
14101403
if (!Apply.hasSubstitutions())
14111404
return nullptr;

lib/SILOptimizer/Utils/Existential.cpp

-5
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,6 @@ static SILInstruction *getStackInitInst(SILValue allocStackAddr,
119119
if (SingleWrite)
120120
return nullptr;
121121
SingleWrite = store;
122-
// When we support OSSA here, we need to insert a new copy of the value
123-
// before `store` (and make sure that the copy is destroyed when
124-
// replacing the apply operand).
125-
assert(store->getOwnershipQualifier() ==
126-
StoreOwnershipQualifier::Unqualified);
127122
}
128123
continue;
129124
}

test/SILOptimizer/sil_combine1.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend %s -O -Xllvm -sil-print-types -emit-sil | %FileCheck %s
2+
// RUN: %target-swift-frontend %s -O -Xllvm -sil-print-types -enable-ossa-modules -emit-sil | %FileCheck %s
23

34
func curry<T1, T2, T3, T4>(_ f: @escaping (T1, T2, T3) -> T4) -> (T1) -> (T2) -> (T3) -> T4 {
45
return { x in { y in { z in f(x, y, z) } } }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// RUN: %target-swift-frontend -O -enable-ossa-modules -Xllvm -sil-print-types -emit-sil -sil-verify-all -Xllvm -sil-disable-pass=function-signature-opts %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -O -enable-ossa-modules -Xllvm -sil-print-types -emit-sil -Xllvm -sil-verify-force-analysis-around-pass=devirtualizer -Xllvm -sil-disable-pass=function-signature-opts %s | %FileCheck %s
3+
4+
// REQUIRES: swift_in_compiler
5+
6+
//===----------------------------------------------------------------------===//
7+
// testReturnSelf: Call to a protocol extension method with
8+
// an existential self that can be type-propagated.
9+
// sil-combine should bailout since it does not propagate
10+
// type substitutions on the return value.
11+
//
12+
// rdar://40555427
13+
// https://github.com/apple/swift/issues/50312
14+
// 'SILCombiner::propagateConcreteTypeOfInitExistential' fails to fully
15+
// propagate type substitutions.
16+
//===----------------------------------------------------------------------===//
17+
public protocol P: class {}
18+
19+
extension P {
20+
public func returnSelf() -> Self {
21+
return self
22+
}
23+
}
24+
25+
final class C: P {}
26+
// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa14testReturnSelfAA1P_pyF : $@convention(thin) () -> @owned any P {
27+
// CHECK: [[EI:%.*]] = end_init_let_ref %0
28+
// CHECK: [[E1:%.*]] = init_existential_ref [[EI]] : $C : $C, $any P
29+
// CHECK: [[O1:%.*]] = open_existential_ref [[E1]] : $any P to $@opened("{{.*}}", any P) Self
30+
// CHECK: [[F1:%.*]] = function_ref @$s37sil_combine_concrete_existential_ossa1PPAAE10returnSelfxyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0
31+
// CHECK: [[C1:%.*]] = apply [[F1]]<@opened("{{.*}}", any P) Self>([[O1]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0
32+
// CHECK: [[E2:%.*]] = init_existential_ref [[C1]] : $@opened("{{.*}}", any P) Self : $@opened("{{.*}}", any P) Self, $any P
33+
// CHECK: [[O2:%.*]] = open_existential_ref [[E2]] : $any P to $@opened("{{.*}}", any P) Self
34+
// CHECK: apply [[F1]]<@opened("{{.*}}", any P) Self>([[O2]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0
35+
// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa14testReturnSelfAA1P_pyF'
36+
public func testReturnSelf() -> P {
37+
let p: P = C()
38+
return p.returnSelf().returnSelf()
39+
}
40+
41+
//===----------------------------------------------------------------------===//
42+
// testWitnessReturnOptionalSelf: Call to a witness method with an existential
43+
// self that can be type-propagated. sil-combine should bailout since it does
44+
// not propagate type substitutions on the return value, and it must walk the
45+
// Optional type to find Self in the return type.
46+
//
47+
// Although sil-combine will not replace the self operand, it will still
48+
// rewrite the witness_method. The devirtualizer then handles the first call.
49+
//===----------------------------------------------------------------------===//
50+
public protocol PP: class {
51+
func returnOptionalSelf() -> Self?
52+
}
53+
54+
final class CC: PP {
55+
init() {}
56+
func returnOptionalSelf() -> Self? {
57+
return self
58+
}
59+
}
60+
61+
// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa29testWitnessReturnOptionalSelfAA2PP_pSgyF : $@convention(thin) () -> @owned Optional<any PP> {
62+
// CHECK: [[EI:%.*]] = end_init_let_ref %0
63+
// CHECK: [[E1:%.*]] = init_existential_ref [[EI]] : $CC : $CC, $any PP
64+
// CHECK: [[O1:%.*]] = open_existential_ref [[E1]] : $any PP to $@opened("{{.*}}", any PP) Self
65+
// CHECK: [[E2:%.*]] = init_existential_ref %{{.*}} : $@opened("{{.*}}", any PP) Self : $@opened("{{.*}}", any PP) Self, $any PP
66+
// CHECK: [[O2:%.*]] = open_existential_ref [[E2]] : $any PP to $@opened("{{.*}}", any PP) Self
67+
// CHECK: [[U1:%.*]] = unchecked_ref_cast [[EI]] : $CC to $@opened("{{.*}}", any PP) Self
68+
// CHECK: [[E3:%.*]] = init_existential_ref [[U1]] : $@opened("{{.*}}", any PP) Self : $@opened("{{.*}}", any PP) Self, $any PP
69+
// CHECK: [[E:%.*]] = enum $Optional<any PP>, #Optional.some!enumelt, [[E3]] : $any PP
70+
// CHECK: return [[E]] : $Optional<any PP>
71+
// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa29testWitnessReturnOptionalSelfAA2PP_pSgyF'
72+
public func testWitnessReturnOptionalSelf() -> PP? {
73+
let p: PP = CC()
74+
return p.returnOptionalSelf()?.returnOptionalSelf()
75+
}
76+
77+
//===----------------------------------------------------------------------===//
78+
// testWitnessReturnOptionalIndirectSelf: Call to a witness method with an
79+
// existential self that can be type-propagated. sil-combine should bailout
80+
// since it does not propagate type substitutions on non-self arguments. It must
81+
// walk the Optional type to find Self in the non-self argument.
82+
//
83+
// Although sil-combine will not replace the self operand, it will still
84+
// rewrite the witness_method. The devirtualizer then handles the first call.
85+
//===----------------------------------------------------------------------===//
86+
protocol PPP {
87+
func returnsOptionalIndirect() -> Self?
88+
}
89+
90+
struct S: PPP {
91+
func returnsOptionalIndirect() -> S? {
92+
return self
93+
}
94+
}
95+
96+
struct SS: PPP {
97+
func returnsOptionalIndirect() -> SS? {
98+
return self
99+
}
100+
}
101+
102+
// The first apply has been devirtualized and inlined. The second remains unspecialized.
103+
// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa37testWitnessReturnOptionalIndirectSelfyyF : $@convention(thin) () -> () {
104+
// CHECK: [[O1:%.*]] = open_existential_addr immutable_access %{{.*}} : $*any PPP to $*@opened("{{.*}}", any PPP) Self
105+
// CHECK: switch_enum_addr %{{.*}} : $*Optional<@opened("{{.*}}", any PPP) Self>, case #Optional.some!enumelt: bb{{.*}}, case #Optional.none!enumelt: bb{{.*}}
106+
// CHECK: [[O2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*any PPP to $*@opened("{{.*}}", any PPP) Self
107+
// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}", any PPP) Self, #PPP.returnsOptionalIndirect : <Self where Self : PPP> (Self) -> () -> Self?, [[O1]] : $*@opened("{{.*}}", any PPP) Self : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0>
108+
// CHECK: apply [[W]]<@opened("{{.*}}", any PPP) Self>(%{{.*}}, [[O2]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0>
109+
// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa37testWitnessReturnOptionalIndirectSelfyyF'
110+
public func testWitnessReturnOptionalIndirectSelf() {
111+
let p: PPP = S()
112+
_ = p.returnsOptionalIndirect()?.returnsOptionalIndirect()
113+
}
114+
115+
//===----------------------------------------------------------------------===//
116+
// testExtensionProtocolComposition: Call to a witness method with an
117+
// existential self that can be type-propagated. Handle an existential with
118+
// multiple conformances.
119+
//
120+
// Previously crashed with in SILCombiner::propagateConcreteTypeOfInitExistential
121+
// with assertion failed: (proto == Conformance.getRequirement()).
122+
// ===----------------------------------------------------------------------===//
123+
public protocol Q {}
124+
125+
extension P where Self : Q {
126+
public func witnessComposition() {}
127+
}
128+
129+
public class C_PQ: P & Q {}
130+
131+
// testExtensionProtocolComposition(c:)
132+
// CHECK-LABEL: sil {{.*}}@$s37sil_combine_concrete_existential_ossa32testExtensionProtocolComposition1cyAA4C_PQC_tF : $@convention(thin) (@guaranteed C_PQ) -> () {
133+
// CHECK-NOT: init_existential_ref
134+
// CHECK-NOT: function_ref
135+
// CHECK-NOT: apply
136+
// CHECK: } // end sil function '$s37sil_combine_concrete_existential_ossa32testExtensionProtocolComposition1cyAA4C_PQC_tF'
137+
public func testExtensionProtocolComposition(c: C_PQ) {
138+
let pp: P & Q = c
139+
pp.witnessComposition()
140+
}

test/SILOptimizer/sil_combine_ossa.sil

+12-14
Original file line numberDiff line numberDiff line change
@@ -2845,13 +2845,11 @@ bb0(%0 : @owned $Klass):
28452845
return %3 : $Builtin.NativeObject
28462846
}
28472847

2848-
// CHECK-LABEL: sil [ossa] @collapse_existential_pack_unpack_unchecked_ref_cast_owned_fail_3 :
2849-
// CHECK: bb0(
2850-
// CHECK: init_existential_ref
2851-
// CHECK: open_existential_ref
2852-
// CHECK: unchecked_ref_cast
2853-
// CHECK: } // end sil function 'collapse_existential_pack_unpack_unchecked_ref_cast_owned_fail_3'
2854-
sil [ossa] @collapse_existential_pack_unpack_unchecked_ref_cast_owned_fail_3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
2848+
// CHECK-LABEL: sil [ossa] @collapse_existential_pack_unpack_unchecked_ref_cast_owned3 :
2849+
// CHECK-NOT: init_existential_ref
2850+
// CHECK-NOT: open_existential_ref
2851+
// CHECK: } // end sil function 'collapse_existential_pack_unpack_unchecked_ref_cast_owned3'
2852+
sil [ossa] @collapse_existential_pack_unpack_unchecked_ref_cast_owned3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
28552853
bb0(%0 : @owned $Klass):
28562854
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
28572855
%2 = open_existential_ref %1 : $AnyObject to $@opened("7CAE06CE-5F10-11E4-AF13-C82A1428F987", AnyObject) Self
@@ -4022,13 +4020,13 @@ bb0(%0 : @owned $VV):
40224020
sil [ossa] @any_to_object : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @owned AnyObject
40234021

40244022
// CHECK-LABEL: sil [ossa] @dont_crash_when_propagating_existentials
4025-
// XHECK: [[EM:%[0-9]+]] = init_existential_metatype %0
4026-
// XHECK: [[S:%[0-9]+]] = alloc_stack $Any
4027-
// XHECK: [[M:%[0-9]+]] = open_existential_metatype [[EM]]
4028-
// XHECK: [[E:%[0-9]+]] = init_existential_addr [[S]]
4029-
// XHECK: store [[M]] to [trivial] [[E]]
4030-
// XHECK: apply {{%[0-9]+}}<(@opened("5F99B72C-EC40-11EA-9534-8C8590A6A134", AnyObject) Self).Type>([[E]])
4031-
// XHECK: } // end sil function 'dont_crash_when_propagating_existentials'
4023+
// CHECK: [[EM:%[0-9]+]] = init_existential_metatype %0
4024+
// CHECK: [[S:%[0-9]+]] = alloc_stack $Any
4025+
// CHECK: [[M:%[0-9]+]] = open_existential_metatype [[EM]]
4026+
// CHECK: [[E:%[0-9]+]] = init_existential_addr [[S]]
4027+
// CHECK: store [[M]] to [trivial] [[E]]
4028+
// CHECK: apply {{%[0-9]+}}<(@opened("5F99B72C-EC40-11EA-9534-8C8590A6A134", AnyObject) Self).Type>([[E]])
4029+
// CHECK: } // end sil function 'dont_crash_when_propagating_existentials'
40324030
sil [ossa] @dont_crash_when_propagating_existentials : $@convention(thin) () -> @owned AnyObject {
40334031
bb0:
40344032
%0 = metatype $@thick C.Type

0 commit comments

Comments
 (0)