Skip to content

Commit cd3ebfb

Browse files
authored
Merge pull request #86113 from eeckstein/simplify-load
Several optimization improvements, mainly for `begin_borrow`, `load` and `load_borrow`
2 parents fdcff3a + a06733b commit cd3ebfb

38 files changed

+705
-296
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CopyToBorrowOptimization.swift

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,49 +53,60 @@ let copyToBorrowOptimization = FunctionPass(name: "copy-to-borrow-optimization")
5353
return
5454
}
5555

56+
var changed = false
57+
5658
for inst in function.instructions {
5759
switch inst {
5860
case let load as LoadInst:
59-
optimize(load: load, context)
61+
if optimize(load: load, context) {
62+
changed = true
63+
}
6064
case let copy as CopyValueInst:
61-
optimize(copy: copy, context)
65+
if optimize(copy: copy, context) {
66+
changed = true
67+
}
6268
default:
6369
break
6470
}
6571
}
72+
73+
if changed {
74+
updateBorrowedFrom(in: function, context)
75+
}
6676
}
6777

68-
private func optimize(load: LoadInst, _ context: FunctionPassContext) {
78+
private func optimize(load: LoadInst, _ context: FunctionPassContext) -> Bool {
6979
if load.loadOwnership != .copy {
70-
return
80+
return false
7181
}
7282

7383
var collectedUses = Uses(context)
7484
defer { collectedUses.deinitialize() }
7585
if !collectedUses.collectUses(of: load) {
76-
return
86+
return false
7787
}
7888

7989
if mayWrite(toAddressOf: load,
8090
within: collectedUses.destroys,
8191
usersInDeadEndBlocks: collectedUses.usersInDeadEndBlocks,
8292
context)
8393
{
84-
return
94+
return false
8595
}
8696

8797
load.replaceWithLoadBorrow(collectedUses: collectedUses)
98+
return true
8899
}
89100

90-
private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) {
101+
private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) -> Bool {
91102
if copy.fromValue.ownership != .guaranteed {
92-
return
103+
return false
93104
}
94105

95106
var collectedUses = Uses(context)
96107
defer { collectedUses.deinitialize() }
97108
if !collectedUses.collectUses(of: copy) {
98-
return
109+
return false
99110
}
100111

101112
var liverange = InstructionRange(begin: copy, context)
@@ -104,10 +115,11 @@ private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) {
104115
liverange.insert(contentsOf: collectedUses.usersInDeadEndBlocks)
105116

106117
if !liverange.isFullyContainedIn(borrowScopeOf: copy.fromValue.lookThroughForwardingInstructions) {
107-
return
118+
return false
108119
}
109120

110121
remove(copy: copy, collectedUses: collectedUses, liverange: liverange)
122+
return true
111123
}
112124

113125
private struct Uses {

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ swift_compiler_sources(Optimizer
1010
SimplifyAllocRefDynamic.swift
1111
SimplifyAllocStack.swift
1212
SimplifyApply.swift
13-
SimplifyBeginAndLoadBorrow.swift
13+
SimplifyBeginBorrow.swift
1414
SimplifyBeginCOWMutation.swift
1515
SimplifyBranch.swift
1616
SimplifyBuiltin.swift
@@ -31,8 +31,8 @@ swift_compiler_sources(Optimizer
3131
SimplifyInitEnumDataAddr.swift
3232
SimplifyKeyPath.swift
3333
SimplifyLoad.swift
34+
SimplifyLoadBorrow.swift
3435
SimplifyMarkDependence.swift
35-
SimplifyMisc.swift
3636
SimplifyPartialApply.swift
3737
SimplifyPointerToAddress.swift
3838
SimplifyRefCasts.swift
@@ -43,6 +43,7 @@ swift_compiler_sources(Optimizer
4343
SimplifySwitchEnum.swift
4444
SimplifyTuple.swift
4545
SimplifyTupleExtract.swift
46+
SimplifyTypeValue.swift
4647
SimplifyUncheckedAddrCast.swift
4748
SimplifyUncheckedEnumData.swift
4849
SimplifyValueToBridgeObject.swift

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginAndLoadBorrow.swift renamed to SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginBorrow.swift

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- SimplifyBeginAndLoadBorrow.swift ---------------------------------===//
1+
//===--- SimplifyBeginBorrow.swift ----------------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -14,68 +14,72 @@ import SIL
1414

1515
extension BeginBorrowInst : OnoneSimplifiable, SILCombineSimplifiable {
1616
func simplify(_ context: SimplifyContext) {
17-
if borrowedValue.ownership == .owned,
18-
// We need to keep lexical lifetimes in place.
19-
!isLexical,
20-
// The same for borrow-scopes which encapsulated pointer escapes.
21-
!findPointerEscapingUse(of: borrowedValue)
22-
{
23-
tryReplaceBorrowWithOwnedOperand(beginBorrow: self, context)
24-
} else {
25-
removeBorrowOfThinFunction(beginBorrow: self, context)
17+
if isLexical && context.preserveDebugInfo {
18+
// We must not remove `begin_borrow [lexical] because this is important for diagnostic passes.
19+
return
2620
}
27-
}
28-
}
2921

30-
extension LoadBorrowInst : Simplifiable, SILCombineSimplifiable {
31-
func simplify(_ context: SimplifyContext) {
32-
if uses.ignoreDebugUses.ignore(usersOfType: EndBorrowInst.self).isEmpty {
33-
context.erase(instructionIncludingAllUsers: self)
34-
return
22+
switch borrowedValue.ownership {
23+
case .owned:
24+
if tryReplaceBorrowWithOwnedOperand(beginBorrow: self, context) {
25+
return
26+
}
27+
case .guaranteed:
28+
if tryReplaceInnerBorrowScope(beginBorrow: self, context) {
29+
return
30+
}
31+
default:
32+
// Note that the operand of `begin_borrow` can have "none" ownership, e.g. in case of
33+
// ```
34+
// %1 = enum $NonTrivialEnum, #NonTrivialEnum.trivialCase!enumelt // ownership = none
35+
// %2 = begin_borrow %1
36+
// ```
37+
break
3538
}
39+
removeBorrowOfThinFunction(beginBorrow: self, context)
40+
}
41+
}
3642

37-
// If the load_borrow is followed by a copy_value, combine both into a `load [copy]`:
38-
// ```
39-
// %1 = load_borrow %0
40-
// %2 = some_forwarding_instruction %1 // zero or more forwarding instructions
41-
// %3 = copy_value %2
42-
// end_borrow %1
43-
// ```
44-
// ->
45-
// ```
46-
// %1 = load [copy] %0
47-
// %3 = some_forwarding_instruction %1 // zero or more forwarding instructions
48-
// ```
49-
//
50-
tryCombineWithCopy(context)
43+
// See comments of `tryReplaceCopy` and `convertAllUsesToOwned`
44+
private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) -> Bool {
45+
if findPointerEscapingUse(of: beginBorrow.borrowedValue) {
46+
return false
5147
}
5248

53-
private func tryCombineWithCopy(_ context: SimplifyContext) {
54-
let forwardedValue = lookThroughSingleForwardingUses()
55-
guard let singleUser = forwardedValue.uses.ignore(usersOfType: EndBorrowInst.self).singleUse?.instruction,
56-
let copy = singleUser as? CopyValueInst,
57-
copy.parentBlock == self.parentBlock else {
58-
return
59-
}
60-
let builder = Builder(before: self, context)
61-
let loadCopy = builder.createLoad(fromAddress: address, ownership: .copy)
62-
let forwardedOwnedValue = replaceGuaranteed(value: self, withOwnedValue: loadCopy, context)
63-
copy.replace(with: forwardedOwnedValue, context)
64-
context.erase(instructionIncludingAllUsers: self)
49+
// The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`.
50+
let forwardedValue = beginBorrow.lookThroughOwnedConvertibaleForwardingChain()
51+
guard forwardedValue.allUsesCanBeConvertedToOwned else {
52+
return false
6553
}
54+
if tryReplaceCopy(of: forwardedValue, withCopiedOperandOf: beginBorrow, context) {
55+
return true
56+
}
57+
if beginBorrow.borrowedValue.isDestroyed(after: beginBorrow) {
58+
convertAllUsesToOwned(of: beginBorrow, context)
59+
return true
60+
}
61+
return false
6662
}
6763

68-
private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) {
69-
// The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`.
70-
let forwardedValue = beginBorrow.lookThroughSingleForwardingUses()
71-
if forwardedValue.allUsesCanBeConvertedToOwned {
72-
if tryReplaceCopy(of: forwardedValue, withCopiedOperandOf: beginBorrow, context) {
73-
return
74-
}
75-
if beginBorrow.borrowedValue.isDestroyed(after: beginBorrow) {
76-
convertAllUsesToOwned(of: beginBorrow, context)
77-
}
64+
/// Removes a borrow scope if the borrowed operand is already a guaranteed value.
65+
/// ```
66+
/// bb0(%0 : @guaranteed $T):
67+
/// %1 = begin_borrow %0
68+
/// // ... uses of %1
69+
/// end_borrow %1
70+
/// ```
71+
/// ->
72+
/// ```
73+
/// bb0(%0 : @guaranteed $T):
74+
/// // ... uses of %0
75+
/// ```
76+
private func tryReplaceInnerBorrowScope(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) -> Bool {
77+
guard beginBorrow.scopeEndingOperands.allSatisfy({ $0.instruction is EndBorrowInst }) else {
78+
return false
7879
}
80+
beginBorrow.uses.ignore(usersOfType: EndBorrowInst.self).replaceAll(with: beginBorrow.borrowedValue, context)
81+
context.erase(instructionIncludingAllUsers: beginBorrow)
82+
return true
7983
}
8084

8185
private func removeBorrowOfThinFunction(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) {
@@ -143,27 +147,30 @@ private func convertAllUsesToOwned(of beginBorrow: BeginBorrowInst, _ context: S
143147
context.erase(instructionIncludingAllUsers: beginBorrow)
144148
}
145149

146-
private extension Value {
147-
/// Returns the last value of a (potentially empty) forwarding chain.
150+
extension Value {
151+
/// Returns the last value of a (potentially empty) forwarding chain where all operands can be
152+
/// converted to "owned" ownership.
148153
/// For example, returns %3 for the following def-use chain:
149154
/// ```
150155
/// %1 = struct_extract %self, #someField
151156
/// %2 = tuple_extract %1, 0
152157
/// %3 = struct $S(%2) // %3 has no forwarding users
153158
/// ```
154159
/// Returns self if this value has no uses which are ForwardingInstructions.
155-
func lookThroughSingleForwardingUses() -> Value {
156-
if let singleUse = uses.ignore(usersOfType: EndBorrowInst.self).singleUse,
160+
func lookThroughOwnedConvertibaleForwardingChain() -> Value {
161+
if let singleUse = uses.ignore(usersOfType: EndBorrowInst.self).ignoreDebugUses.ignoreTypeDependence.singleUse,
157162
let fwdInst = singleUse.instruction as? (SingleValueInstruction & ForwardingInstruction),
158163
fwdInst.canConvertToOwned,
159164
fwdInst.isSingleForwardedOperand(singleUse),
160165
fwdInst.parentBlock == parentBlock
161166
{
162-
return fwdInst.lookThroughSingleForwardingUses()
167+
return fwdInst.lookThroughOwnedConvertibaleForwardingChain()
163168
}
164169
return self
165170
}
171+
}
166172

173+
private extension Value {
167174
var allUsesCanBeConvertedToOwned: Bool {
168175
let relevantUses = uses.ignore(usersOfType: EndBorrowInst.self)
169176
return relevantUses.allSatisfy { $0.canAccept(ownership: .owned) }
@@ -209,12 +216,12 @@ private extension ForwardingInstruction {
209216

210217
/// Replaces a guaranteed value with an owned value.
211218
///
212-
/// If the `guaranteedValue`'s use is a ForwardingInstruction (or forwarding instruction chain),
219+
/// If the `value`'s use is a ForwardingInstruction (or forwarding instruction chain),
213220
/// it is converted to an owned version of the forwarding instruction (or instruction chain).
214221
///
215-
/// Returns the last owned value in a forwarding-chain or `ownedValue` if `guaranteedValue` has
222+
/// Returns the last owned value in a forwarding-chain or `ownedValue` if `value` has
216223
/// no forwarding uses.
217-
private func replaceGuaranteed(value: Value, withOwnedValue ownedValue: Value, _ context: SimplifyContext) -> Value {
224+
func replaceGuaranteed(value: SingleValueInstruction, withOwnedValue ownedValue: Value, _ context: SimplifyContext) -> Value {
218225
var result = ownedValue
219226
var numForwardingUses = 0
220227
for use in value.uses {
@@ -239,6 +246,11 @@ private func replaceGuaranteed(value: Value, withOwnedValue ownedValue: Value, _
239246
result = replaceGuaranteed(value: fwdInst, withOwnedValue: fwdInst, context)
240247
case is EndBorrowInst:
241248
break
249+
case let dv as DebugValueInst where dv != value.next:
250+
// Move the debug_value immediatly after the value definition to avoid a use-after-consume
251+
// in case the debug_value is originally located after the forwarding instruction.
252+
dv.move(before: value.next!, context)
253+
fallthrough
242254
default:
243255
precondition(use.canAccept(ownership: .owned))
244256
use.set(to: ownedValue, context)

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,6 @@ private extension DestructureInstruction {
5050

5151
func foldWithAggregateConstruction(_ context: SimplifyContext) {
5252

53-
if aggregate.type.isTrivial(in: parentFunction) {
54-
// ```
55-
// (%1, %2) = destructure_tuple %t
56-
// ```
57-
// ->
58-
// ```
59-
// %1 = tuple_extract %t, 0
60-
// %2 = tuple_extract %t, 1
61-
// ```
62-
replaceWithAggregateExtract(context)
63-
return
64-
}
65-
6653
switch aggregate {
6754
case let constructInst as ConstructureInstruction:
6855
// Eliminate the redundant instruction pair
@@ -99,6 +86,21 @@ private extension DestructureInstruction {
9986
default:
10087
break
10188
}
89+
90+
if !isDeleted,
91+
aggregate.type.isTrivial(in: parentFunction) || aggregate.ownership == .guaranteed
92+
{
93+
// ```
94+
// (%1, %2) = destructure_tuple %t
95+
// ```
96+
// ->
97+
// ```
98+
// %1 = tuple_extract %t, 0
99+
// %2 = tuple_extract %t, 1
100+
// ```
101+
replaceWithAggregateExtract(context)
102+
return
103+
}
102104
}
103105

104106
private func replaceWithAggregateExtract(_ context: SimplifyContext) {

0 commit comments

Comments
 (0)