Skip to content

Commit 6b38f2a

Browse files
committed
Optimizer: simplify load_borrow
* Remove dead `load_borrow` instructions (replaces the old peephole optimization in SILCombine) * If the `load_borrow` is followed by a `copy_value`, combine both into a `load [copy]`
1 parent 73e36e2 commit 6b38f2a

File tree

7 files changed

+139
-17
lines changed

7 files changed

+139
-17
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
SimplifyAllocRefDynamic.swift
1111
SimplifyApply.swift
12-
SimplifyBeginBorrow.swift
12+
SimplifyBeginAndLoadBorrow.swift
1313
SimplifyBeginCOWMutation.swift
1414
SimplifyBranch.swift
1515
SimplifyBuiltin.swift

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- SimplifyBeginBorrow.swift ----------------------------------------===//
1+
//===--- SimplifyBeginAndLoadBorrow.swift ---------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -25,6 +25,45 @@ extension BeginBorrowInst : OnoneSimplifyable {
2525
}
2626
}
2727

28+
extension LoadBorrowInst : Simplifyable, SILCombineSimplifyable {
29+
func simplify(_ context: SimplifyContext) {
30+
if uses.ignoreDebugUses.ignoreUsers(ofType: EndBorrowInst.self).isEmpty {
31+
context.erase(instructionIncludingAllUsers: self)
32+
return
33+
}
34+
35+
// If the load_borrow is followed by a copy_value, combine both into a `load [copy]`:
36+
// ```
37+
// %1 = load_borrow %0
38+
// %2 = some_forwarding_instruction %1 // zero or more forwarding instructions
39+
// %3 = copy_value %2
40+
// end_borrow %1
41+
// ```
42+
// ->
43+
// ```
44+
// %1 = load [copy] %0
45+
// %3 = some_forwarding_instruction %1 // zero or more forwarding instructions
46+
// ```
47+
//
48+
tryCombineWithCopy(context)
49+
}
50+
51+
private func tryCombineWithCopy(_ context: SimplifyContext) {
52+
let forwardedValue = lookThroughSingleForwardingUses()
53+
guard let singleUser = forwardedValue.uses.ignoreUsers(ofType: EndBorrowInst.self).singleUse?.instruction,
54+
let copy = singleUser as? CopyValueInst,
55+
copy.parentBlock == self.parentBlock else {
56+
return
57+
}
58+
let builder = Builder(before: self, context)
59+
let loadCopy = builder.createLoad(fromAddress: address, ownership: .copy)
60+
let forwardedOwnedValue = replace(guaranteedValue: self, withOwnedValue: loadCopy, context)
61+
copy.uses.replaceAll(with: forwardedOwnedValue, context)
62+
context.erase(instruction: copy)
63+
context.erase(instructionIncludingAllUsers: self)
64+
}
65+
}
66+
2867
private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) {
2968
// The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`.
3069
let forwardedValue = beginBorrow.lookThroughSingleForwardingUses()
@@ -156,7 +195,7 @@ private extension ForwardingInstruction {
156195
}
157196

158197
/// Replaces a guaranteed value with an owned value.
159-
///
198+
///
160199
/// If the `guaranteedValue`'s use is a ForwardingInstruction (or forwarding instruction chain),
161200
/// it is converted to an owned version of the forwarding instruction (or instruction chain).
162201
///

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ private func registerSwiftPasses() {
108108
registerForSILCombine(RetainValueInst.self, { run(RetainValueInst.self, $0) })
109109
registerForSILCombine(ReleaseValueInst.self, { run(ReleaseValueInst.self, $0) })
110110
registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) })
111+
registerForSILCombine(LoadBorrowInst.self, { run(LoadBorrowInst.self, $0) })
111112
registerForSILCombine(CopyValueInst.self, { run(CopyValueInst.self, $0) })
112113
registerForSILCombine(DestroyValueInst.self, { run(DestroyValueInst.self, $0) })
113114
registerForSILCombine(DestructureStructInst.self, { run(DestructureStructInst.self, $0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ SWIFT_SILCOMBINE_PASS(StrongReleaseInst)
526526
SWIFT_SILCOMBINE_PASS(RetainValueInst)
527527
SWIFT_SILCOMBINE_PASS(ReleaseValueInst)
528528
SWIFT_SILCOMBINE_PASS(LoadInst)
529+
SWIFT_SILCOMBINE_PASS(LoadBorrowInst)
529530
SWIFT_SILCOMBINE_PASS(CopyValueInst)
530531
SWIFT_SILCOMBINE_PASS(DestroyValueInst)
531532
SWIFT_SILCOMBINE_PASS(DestructureStructInst)

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ class SILCombiner :
252252
// NOTE: The load optimized in this method is a load [trivial].
253253
SILInstruction *optimizeLoadFromStringLiteral(LoadInst *li);
254254

255-
SILInstruction *visitLoadBorrowInst(LoadBorrowInst *LI);
256255
SILInstruction *visitIndexAddrInst(IndexAddrInst *IA);
257256
bool optimizeStackAllocatedEnum(AllocStackInst *AS);
258257
SILInstruction *visitAllocStackInst(AllocStackInst *AS);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -779,19 +779,6 @@ static SILValue isConstIndexAddr(SILValue val, unsigned &index) {
779779
return IA->getBase();
780780
}
781781

782-
SILInstruction *SILCombiner::visitLoadBorrowInst(LoadBorrowInst *lbi) {
783-
// If we have a load_borrow that only has non_debug end_borrow uses, delete
784-
// it.
785-
if (llvm::all_of(getNonDebugUses(lbi), [](Operand *use) {
786-
return isa<EndBorrowInst>(use->getUser());
787-
})) {
788-
eraseInstIncludingUsers(lbi);
789-
return nullptr;
790-
}
791-
792-
return nullptr;
793-
}
794-
795782
/// Optimize nested index_addr instructions:
796783
/// Example in SIL pseudo code:
797784
/// %1 = index_addr %ptr, x
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// RUN: %target-sil-opt %s -simplification -simplify-instruction=load_borrow | %FileCheck %s
2+
3+
import Swift
4+
import Builtin
5+
6+
class B { }
7+
8+
class E : B {
9+
@_hasStorage var i: Int
10+
}
11+
12+
// CHECK-LABEL: sil [ossa] @load_borrow_and_copy :
13+
// CHECK: %1 = load [copy] %0
14+
// CHECK: %2 = begin_borrow %1
15+
// CHECK: %3 = ref_element_addr %2, #E.i
16+
// CHECK: %4 = load [trivial] %3
17+
// CHECK: end_borrow %2
18+
// CHECK: destroy_value %1
19+
// CHECK: return %4
20+
// CHEKCK } // end sil function '@load_borrow_and_copy'
21+
sil [ossa] @load_borrow_and_copy : $@convention(thin) (@inout E) -> Int {
22+
bb0(%0 : $*E):
23+
%1 = load_borrow %0
24+
%2 = copy_value %1
25+
end_borrow %1
26+
%4 = begin_borrow %2
27+
%5 = ref_element_addr %4, #E.i
28+
%6 = load [trivial] %5
29+
end_borrow %4
30+
destroy_value %2
31+
return %6
32+
}
33+
34+
// CHECK-LABEL: sil [ossa] @load_borrow_and_copy_forwarding :
35+
// CHECK: %1 = load [copy] %0
36+
// CHECK: %2 = unchecked_ref_cast %1 to $E
37+
// CHECK: %3 = begin_borrow %2
38+
// CHECK: %4 = ref_element_addr %3, #E.i
39+
// CHECK: %5 = load [trivial] %4
40+
// CHECK: end_borrow %3
41+
// CHECK: destroy_value %2
42+
// CHECK: return %5
43+
// CHEKCK } // end sil function '@load_borrow_and_copy_forwarding'
44+
sil [ossa] @load_borrow_and_copy_forwarding : $@convention(thin) (@inout B) -> Int {
45+
bb0(%0 : $*B):
46+
%1 = load_borrow %0
47+
%2 = unchecked_ref_cast %1 to $E
48+
%3 = copy_value %2
49+
end_borrow %1
50+
%5 = begin_borrow %3
51+
%6 = ref_element_addr %5, #E.i
52+
%7 = load [trivial] %6
53+
end_borrow %5
54+
destroy_value %3
55+
return %7
56+
}
57+
58+
// CHECK-LABEL: sil [ossa] @load_borrow_and_copy_different_block :
59+
// CHECK: %1 = load_borrow %0
60+
// CHECK: %3 = copy_value %1
61+
// CHEKCK } // end sil function '@load_borrow_and_copy_different_block'
62+
sil [ossa] @load_borrow_and_copy_different_block : $@convention(thin) (@inout E) -> () {
63+
bb0(%0 : $*E):
64+
%1 = load_borrow %0
65+
cond_br undef, bb1, bb2
66+
bb1:
67+
%3 = copy_value %1
68+
%4 = begin_borrow %3
69+
%5 = ref_element_addr %4, #E.i
70+
%6 = load [trivial] %5
71+
fix_lifetime %6
72+
end_borrow %4
73+
destroy_value %3
74+
br bb3
75+
bb2:
76+
br bb3
77+
bb3:
78+
end_borrow %1
79+
%r = tuple ()
80+
return %r
81+
}
82+
83+
// CHECK-LABEL: sil [ossa] @dead_load_borrow :
84+
// CHECK-NOT: load_borrow
85+
// CHECK: %1 = tuple ()
86+
// CHECK: return %1
87+
// CHEKCK } // end sil function '@dead_load_borrow'
88+
sil [ossa] @dead_load_borrow : $@convention(thin) (@inout B) -> () {
89+
bb0(%0 : $*B):
90+
%1 = load_borrow %0
91+
debug_value %1, var, name "x"
92+
end_borrow %1
93+
%4 = tuple ()
94+
return %4
95+
}

0 commit comments

Comments
 (0)