@@ -43,24 +43,24 @@ let releaseDevirtualizerPass = FunctionPass(
43
43
// these we know that they don't have associated objects, which are
44
44
// _not_ released by the deinit method.
45
45
if let deallocStackRef = instruction as? DeallocStackRefInst {
46
- devirtualizeReleaseOfObject ( context, release, deallocStackRef)
46
+ tryDevirtualizeReleaseOfObject ( context, release, deallocStackRef)
47
47
lastRelease = nil
48
48
continue
49
49
}
50
50
}
51
51
52
52
if instruction is ReleaseValueInst || instruction is StrongReleaseInst {
53
53
lastRelease = instruction as? RefCountingInst
54
- } else if instruction. mayReleaseOrReadRefCount {
54
+ } else if instruction. mayRelease || instruction . mayReadRefCount {
55
55
lastRelease = nil
56
56
}
57
57
}
58
58
}
59
59
}
60
60
)
61
61
62
- /// Devirtualize releases of array buffers .
63
- private func devirtualizeReleaseOfObject (
62
+ /// Tries to de-virtualize the final release of a stack promoted object .
63
+ private func tryDevirtualizeReleaseOfObject (
64
64
_ context: PassContext ,
65
65
_ release: RefCountingInst ,
66
66
_ deallocStackRef: DeallocStackRefInst
@@ -71,30 +71,19 @@ private func devirtualizeReleaseOfObject(
71
71
root = newRoot
72
72
}
73
73
74
- guard root === allocRefInstruction else {
74
+ if root != allocRefInstruction {
75
75
return
76
76
}
77
77
78
- createDeallocCall ( context, allocRefInstruction. type, release, allocRefInstruction)
79
- }
78
+ let type = allocRefInstruction. type
80
79
81
- /// Replaces the release-instruction `release` with an explicit call to
82
- /// the deallocating destructor of `type` for `object`.
83
- private func createDeallocCall(
84
- _ context: PassContext ,
85
- _ type: Type ,
86
- _ release: RefCountingInst ,
87
- _ object: Value
88
- ) {
89
80
guard let dealloc = context. getDestructor ( ofClass: type) else {
90
81
return
91
82
}
92
83
93
- let substitutionMap = context. getContextSubstitutionMap ( for: type)
94
-
95
84
let builder = Builder ( at: release, location: release. location, context)
96
85
97
- var object = object
86
+ var object : Value = allocRefInstruction
98
87
if object. type != type {
99
88
object = builder. createUncheckedRefCast ( object: object, type: type)
100
89
}
@@ -107,6 +96,7 @@ private func createDeallocCall(
107
96
// argument.
108
97
let functionRef = builder. createFunctionRef ( dealloc)
109
98
99
+ let substitutionMap = context. getContextSubstitutionMap ( for: type)
110
100
builder. createApply ( function: functionRef, substitutionMap, arguments: [ object] )
111
101
context. erase ( instruction: release)
112
102
}
@@ -135,7 +125,7 @@ private func stripRCIdentityPreservingInsts(_ value: Value) -> Value? {
135
125
// only reference count that can be modified is the non-trivial field. Return
136
126
// the non-trivial field.
137
127
case let si as StructInst :
138
- return si. uniqueNonTrivialFieldValue
128
+ return si. uniqueNonTrivialOperand
139
129
140
130
// If we have an enum instruction with a payload, strip off the enum to
141
131
// expose the enum's payload.
@@ -152,9 +142,73 @@ private func stripRCIdentityPreservingInsts(_ value: Value) -> Value? {
152
142
// semantics, a retain_value on the tuple is equivalent to a retain value on
153
143
// the tuple operand.
154
144
case let ti as TupleInst :
155
- return ti. uniqueNonTrivialElt
145
+ return ti. uniqueNonTrivialOperand
156
146
157
147
default :
158
148
return nil
159
149
}
160
150
}
151
+
152
+ private extension Instruction {
153
+ /// Search the operands of this tuple for a unique non-trivial elt. If we find
154
+ /// it, return it. Otherwise return `nil`.
155
+ var uniqueNonTrivialOperand : Value ? {
156
+ var candidateElt : Value ?
157
+ let function = self . function
158
+
159
+ // For each operand...
160
+ for op in operands. enumerated ( ) {
161
+ // If the operand is not trivial...
162
+ if !op. type. isTrivial ( in: function) {
163
+ // And we have not found a `candidateElt` yet, set index to `op` and continue.
164
+ if candidateElt == nil {
165
+ candidateElt = op
166
+ continue
167
+ }
168
+
169
+ // Otherwise, we have two values that are non-trivial. Bail.
170
+ return nil
171
+ }
172
+ }
173
+
174
+ return candidateElt
175
+ }
176
+ }
177
+
178
+ private extension TupleExtractInst {
179
+ var isEltOnlyNonTrivialElt : Bool {
180
+ let function = self . function
181
+ // If the elt we are extracting is trivial, we cannot be a non-trivial
182
+ // field... return false.
183
+ if type. isTrivial ( in: function) {
184
+ return false
185
+ }
186
+
187
+ // Ok, we know that the elt we are extracting is non-trivial. Make sure that
188
+ // we have no other non-trivial elts.
189
+ let opTy = operand [ 0 ] . type
190
+ let fieldNo = self . fieldIndex
191
+
192
+ // For each element index of the tuple...
193
+ for (i, eltType) in opType. tupleElements. enumerated ( ) {
194
+ // If the element index is the one we are extracting, skip it...
195
+ if i == fieldNo {
196
+ continue
197
+ }
198
+
199
+ // Otherwise check if we have a non-trivial type. If we don't have one,
200
+ // continue.
201
+ if eltType. isTrivial ( in: function) {
202
+ continue
203
+ }
204
+
205
+ // If we do have a non-trivial type, return false. We have multiple
206
+ // non-trivial types violating our condition.
207
+ return false
208
+ }
209
+
210
+ // We checked every other elt of the tuple and did not find any
211
+ // non-trivial elt except for ourselves. Return `true``.
212
+ return true
213
+ }
214
+ }
0 commit comments