11
11
//===----------------------------------------------------------------------===//
12
12
13
13
import SIL
14
+ import AST
14
15
15
- extension ApplyInst : OnoneSimplifiable {
16
+ extension ApplyInst : OnoneSimplifiable , SILCombineSimplifiable {
16
17
func simplify( _ context: SimplifyContext ) {
17
18
if tryTransformThickToThinCallee ( of: self , context) {
18
19
return
@@ -21,13 +22,23 @@ extension ApplyInst : OnoneSimplifiable {
21
22
context. erase ( instruction: self )
22
23
return
23
24
}
24
- _ = context. tryDevirtualize ( apply: self , isMandatory: false )
25
+ if context. tryDevirtualize ( apply: self , isMandatory: false ) != nil {
26
+ return
27
+ }
28
+ if !context. preserveDebugInfo {
29
+ _ = tryReplaceExistentialArchetype ( of: self , context)
30
+ }
25
31
}
26
32
}
27
33
28
- extension TryApplyInst : OnoneSimplifiable {
34
+ extension TryApplyInst : OnoneSimplifiable , SILCombineSimplifiable {
29
35
func simplify( _ context: SimplifyContext ) {
30
- _ = context. tryDevirtualize ( apply: self , isMandatory: false )
36
+ if context. tryDevirtualize ( apply: self , isMandatory: false ) != nil {
37
+ return
38
+ }
39
+ if !context. preserveDebugInfo {
40
+ _ = tryReplaceExistentialArchetype ( of: self , context)
41
+ }
31
42
}
32
43
}
33
44
@@ -61,3 +72,131 @@ private func tryTransformThickToThinCallee(of apply: ApplyInst, _ context: Simpl
61
72
}
62
73
return false
63
74
}
75
+
76
+ /// If the apply uses an existential archetype (`@opened("...")`) and the concrete type is known,
77
+ /// replace the existential archetype with the concrete type
78
+ /// 1. in the apply's substitution map
79
+ /// 2. in the arguments, e.g. by inserting address casts
80
+ /// For example:
81
+ /// ```
82
+ /// %5 = apply %1<@opend("...")>(%2) : <τ_0_0> (τ_0_0) -> ()
83
+ /// ```
84
+ /// ->
85
+ /// ```
86
+ /// %4 = unchecked_addr_cast %2 to $*ConcreteType
87
+ /// %5 = apply %1<ConcreteType>(%4) : <τ_0_0> (τ_0_0) -> ()
88
+ /// ```
89
+ private func tryReplaceExistentialArchetype( of apply: ApplyInst , _ context: SimplifyContext ) -> Bool {
90
+ if let concreteType = apply. concreteTypeOfDependentExistentialArchetype,
91
+ apply. canReplaceExistentialArchetype ( )
92
+ {
93
+ let builder = Builder ( after: apply, context)
94
+
95
+ let newApply = builder. createApply (
96
+ function: apply. callee,
97
+ apply. replaceOpenedArchetypeInSubstituations ( withConcreteType: concreteType, context) ,
98
+ arguments: apply. replaceExistentialArchetypeInArguments ( withConcreteType: concreteType, context) ,
99
+ isNonThrowing: apply. isNonThrowing, isNonAsync: apply. isNonAsync,
100
+ specializationInfo: apply. specializationInfo)
101
+ apply. replace ( with: newApply, context)
102
+
103
+ return true
104
+ }
105
+ return false
106
+ }
107
+
108
+ // The same as the previous function, just for try_apply instructions.
109
+ private func tryReplaceExistentialArchetype( of tryApply: TryApplyInst , _ context: SimplifyContext ) -> Bool {
110
+ if let concreteType = tryApply. concreteTypeOfDependentExistentialArchetype,
111
+ tryApply. canReplaceExistentialArchetype ( )
112
+ {
113
+ let builder = Builder ( before: tryApply, context)
114
+
115
+ builder. createTryApply (
116
+ function: tryApply. callee,
117
+ tryApply. replaceOpenedArchetypeInSubstituations ( withConcreteType: concreteType, context) ,
118
+ arguments: tryApply. replaceExistentialArchetypeInArguments ( withConcreteType: concreteType, context) ,
119
+ normalBlock: tryApply. normalBlock, errorBlock: tryApply. errorBlock,
120
+ isNonAsync: tryApply. isNonAsync,
121
+ specializationInfo: tryApply. specializationInfo)
122
+ context. erase ( instruction: tryApply)
123
+
124
+ return true
125
+ }
126
+ return false
127
+ }
128
+
129
+ private extension FullApplySite {
130
+ // Precondition: the apply uses only a single existential archetype.
131
+ // This is checked in `concreteTypeOfDependentExistentialArchetype`
132
+ func canReplaceExistentialArchetype( ) -> Bool {
133
+ // Make sure that existential archetype _is_ a replacement type and not e.g. _contained_ in a
134
+ // replacement type, like
135
+ // apply %1<Array<@opened("...")>()
136
+ guard substitutionMap. replacementTypes. contains ( where: { $0. isExistentialArchetype } ) ,
137
+ substitutionMap. replacementTypes. allSatisfy ( { $0. isExistentialArchetype || !$0. hasLocalArchetype } )
138
+ else {
139
+ return false
140
+ }
141
+
142
+ // Don't allow existential archetypes in direct results and error results.
143
+ // Note that an opened existential value is address only, so it cannot be a direct result anyway
144
+ // (but it can be once we have opaque values).
145
+ // Also don't support things like direct `Array<@opened("...")>` return values.
146
+ if let singleDirectResult, singleDirectResult. type. astType. hasLocalArchetype {
147
+ return false
148
+ }
149
+ if let singleDirectErrorResult, singleDirectErrorResult. type. astType. hasLocalArchetype {
150
+ return false
151
+ }
152
+
153
+ return arguments. allSatisfy { value in
154
+ let astTy = value. type. astType
155
+ // Allow three cases:
156
+ // case 1. the argument _is_ the existential archetype
157
+ return astTy. isExistentialArchetype ||
158
+ // case 2. the argument _is_ a metatype of the existential archetype
159
+ ( astTy. isMetatypeType && astTy. instanceTypeOfMetatype. isExistentialArchetype) ||
160
+ // case 3. the argument has nothing to do with the existential archetype (or any other local archetype)
161
+ !astTy. hasLocalArchetype
162
+ }
163
+ }
164
+
165
+ func replaceExistentialArchetypeInArguments(
166
+ withConcreteType concreteType: CanonicalType ,
167
+ _ context: SimplifyContext
168
+ ) -> [ Value ] {
169
+ let newArgs = arguments. map { ( arg) -> Value in
170
+ let argTy = arg. type. astType
171
+ if argTy. isExistentialArchetype {
172
+ // case 1. the argument _is_ the existential archetype:
173
+ // just insert an address cast to satisfy type equivalence.
174
+ let builder = Builder ( before: self , context)
175
+ let concreteSILType = concreteType. loweredType ( in: self . parentFunction)
176
+ return builder. createUncheckedAddrCast ( from: arg, to: concreteSILType. addressType)
177
+ }
178
+ if argTy. isMetatypeType, argTy. instanceTypeOfMetatype. isExistentialArchetype {
179
+ // case 2. the argument _is_ a metatype of the existential archetype:
180
+ // re-create the metatype with the concrete type.
181
+ let builder = Builder ( before: self , context)
182
+ return builder. createMetatype ( ofInstanceType: concreteType, representation: argTy. representationOfMetatype)
183
+ }
184
+ // case 3. the argument has nothing to do with the existential archetype (or any other local archetype)
185
+ return arg
186
+ }
187
+ return Array ( newArgs)
188
+ }
189
+
190
+ func replaceOpenedArchetypeInSubstituations(
191
+ withConcreteType concreteType: CanonicalType ,
192
+ _ context: SimplifyContext
193
+ ) -> SubstitutionMap {
194
+ let openedArcheType = substitutionMap. replacementTypes. first ( where: { $0. isExistentialArchetype } ) !
195
+
196
+ let newReplacementTypes = substitutionMap. replacementTypes. map {
197
+ return $0 == openedArcheType ? concreteType. type : $0
198
+ }
199
+ let genSig = callee. type. astType. invocationGenericSignatureOfFunctionType
200
+ return SubstitutionMap ( genericSignature: genSig, replacementTypes: newReplacementTypes)
201
+ }
202
+ }
0 commit comments