@@ -43,24 +43,24 @@ let releaseDevirtualizerPass = FunctionPass(
4343 // these we know that they don't have associated objects, which are
4444 // _not_ released by the deinit method.
4545 if let deallocStackRef = instruction as? DeallocStackRefInst {
46- devirtualizeReleaseOfObject ( context, release, deallocStackRef)
46+ tryDevirtualizeReleaseOfObject ( context, release, deallocStackRef)
4747 lastRelease = nil
4848 continue
4949 }
5050 }
5151
5252 if instruction is ReleaseValueInst || instruction is StrongReleaseInst {
5353 lastRelease = instruction as? RefCountingInst
54- } else if instruction. mayReleaseOrReadRefCount {
54+ } else if instruction. mayRelease || instruction . mayReadRefCount {
5555 lastRelease = nil
5656 }
5757 }
5858 }
5959 }
6060)
6161
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 (
6464 _ context: PassContext ,
6565 _ release: RefCountingInst ,
6666 _ deallocStackRef: DeallocStackRefInst
@@ -71,30 +71,19 @@ private func devirtualizeReleaseOfObject(
7171 root = newRoot
7272 }
7373
74- guard root === allocRefInstruction else {
74+ if root != allocRefInstruction {
7575 return
7676 }
7777
78- createDeallocCall ( context, allocRefInstruction. type, release, allocRefInstruction)
79- }
78+ let type = allocRefInstruction. type
8079
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- ) {
8980 guard let dealloc = context. getDestructor ( ofClass: type) else {
9081 return
9182 }
9283
93- let substitutionMap = context. getContextSubstitutionMap ( for: type)
94-
9584 let builder = Builder ( at: release, location: release. location, context)
9685
97- var object = object
86+ var object : Value = allocRefInstruction
9887 if object. type != type {
9988 object = builder. createUncheckedRefCast ( object: object, type: type)
10089 }
@@ -107,6 +96,7 @@ private func createDeallocCall(
10796 // argument.
10897 let functionRef = builder. createFunctionRef ( dealloc)
10998
99+ let substitutionMap = context. getContextSubstitutionMap ( for: type)
110100 builder. createApply ( function: functionRef, substitutionMap, arguments: [ object] )
111101 context. erase ( instruction: release)
112102}
@@ -135,7 +125,7 @@ private func stripRCIdentityPreservingInsts(_ value: Value) -> Value? {
135125 // only reference count that can be modified is the non-trivial field. Return
136126 // the non-trivial field.
137127 case let si as StructInst :
138- return si. uniqueNonTrivialFieldValue
128+ return si. uniqueNonTrivialOperand
139129
140130 // If we have an enum instruction with a payload, strip off the enum to
141131 // expose the enum's payload.
@@ -152,9 +142,73 @@ private func stripRCIdentityPreservingInsts(_ value: Value) -> Value? {
152142 // semantics, a retain_value on the tuple is equivalent to a retain value on
153143 // the tuple operand.
154144 case let ti as TupleInst :
155- return ti. uniqueNonTrivialElt
145+ return ti. uniqueNonTrivialOperand
156146
157147 default :
158148 return nil
159149 }
160150}
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