Skip to content

Commit 848fa70

Browse files
committed
libswift: reimplement Instruction helpers in Swift
1 parent cec4b82 commit 848fa70

File tree

7 files changed

+305
-272
lines changed

7 files changed

+305
-272
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ReleaseDevirtualizer.swift

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)