Skip to content

Commit b748546

Browse files
authored
Merge pull request #78054 from eeckstein/ossa-opt-improvements
Some OSSA related optimization improvements
2 parents b2ba590 + 3e35df0 commit b748546

23 files changed

+639
-69
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

+1-1
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

+57-3
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
//
@@ -12,7 +12,7 @@
1212

1313
import SIL
1414

15-
extension BeginBorrowInst : OnoneSimplifyable {
15+
extension BeginBorrowInst : OnoneSimplifyable, SILCombineSimplifyable {
1616
func simplify(_ context: SimplifyContext) {
1717
if borrowedValue.ownership == .owned,
1818
// We need to keep lexical lifetimes in place.
@@ -21,10 +21,51 @@ extension BeginBorrowInst : OnoneSimplifyable {
2121
!findPointerEscapingUse(of: borrowedValue)
2222
{
2323
tryReplaceBorrowWithOwnedOperand(beginBorrow: self, context)
24+
} else {
25+
removeBorrowOfThinFunction(beginBorrow: self, context)
2426
}
2527
}
2628
}
2729

30+
extension LoadBorrowInst : Simplifyable, SILCombineSimplifyable {
31+
func simplify(_ context: SimplifyContext) {
32+
if uses.ignoreDebugUses.ignoreUsers(ofType: EndBorrowInst.self).isEmpty {
33+
context.erase(instructionIncludingAllUsers: self)
34+
return
35+
}
36+
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)
51+
}
52+
53+
private func tryCombineWithCopy(_ context: SimplifyContext) {
54+
let forwardedValue = lookThroughSingleForwardingUses()
55+
guard let singleUser = forwardedValue.uses.ignoreUsers(ofType: 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 = replace(guaranteedValue: self, withOwnedValue: loadCopy, context)
63+
copy.uses.replaceAll(with: forwardedOwnedValue, context)
64+
context.erase(instruction: copy)
65+
context.erase(instructionIncludingAllUsers: self)
66+
}
67+
}
68+
2869
private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) {
2970
// The last value of a (potentially empty) forwarding chain, beginning at the `begin_borrow`.
3071
let forwardedValue = beginBorrow.lookThroughSingleForwardingUses()
@@ -38,6 +79,19 @@ private func tryReplaceBorrowWithOwnedOperand(beginBorrow: BeginBorrowInst, _ co
3879
}
3980
}
4081

82+
private func removeBorrowOfThinFunction(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) {
83+
guard let thin2thickFn = beginBorrow.borrowedValue as? ThinToThickFunctionInst,
84+
// For simplicity don't go into the trouble of removing reborrow phi arguments.
85+
beginBorrow.uses.filterUsers(ofType: BranchInst.self).isEmpty else
86+
{
87+
return
88+
}
89+
// `thin_to_thick_function` has "none" ownership and is compatible with guaranteed values.
90+
// Therefore the `begin_borrow` is not needed.
91+
beginBorrow.uses.ignoreUsers(ofType: EndBorrowInst.self).replaceAll(with: thin2thickFn, context)
92+
context.erase(instructionIncludingAllUsers: beginBorrow)
93+
}
94+
4195
/// Replace
4296
/// ```
4397
/// %1 = begin_borrow %0
@@ -156,7 +210,7 @@ private extension ForwardingInstruction {
156210
}
157211

158212
/// Replaces a guaranteed value with an owned value.
159-
///
213+
///
160214
/// If the `guaranteedValue`'s use is a ForwardingInstruction (or forwarding instruction chain),
161215
/// it is converted to an owned version of the forwarding instruction (or instruction chain).
162216
///

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift

+56
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ import SIL
1515
extension DestructureTupleInst : OnoneSimplifyable, SILCombineSimplifyable {
1616
func simplify(_ context: SimplifyContext) {
1717

18+
// If the tuple is trivial, replace
19+
// ```
20+
// (%1, %2) = destructure_tuple %t
21+
// ```
22+
// ->
23+
// ```
24+
// %1 = tuple_extract %t, 0
25+
// %2 = tuple_extract %t, 1
26+
// ```
27+
// This canonicalization helps other optimizations to e.g. CSE tuple_extracts.
28+
//
29+
if replaceWithTupleExtract(context) {
30+
return
31+
}
32+
1833
// Eliminate the redundant instruction pair
1934
// ```
2035
// %t = tuple (%0, %1, %2)
@@ -26,11 +41,39 @@ extension DestructureTupleInst : OnoneSimplifyable, SILCombineSimplifyable {
2641
tryReplaceConstructDestructPair(construct: tuple, destruct: self, context)
2742
}
2843
}
44+
45+
private func replaceWithTupleExtract(_ context: SimplifyContext) -> Bool {
46+
guard self.tuple.type.isTrivial(in: parentFunction) else {
47+
return false
48+
}
49+
let builder = Builder(before: self, context)
50+
for (elementIdx, result) in results.enumerated() {
51+
let elementValue = builder.createTupleExtract(tuple: self.tuple, elementIndex: elementIdx)
52+
result.uses.replaceAll(with: elementValue, context)
53+
}
54+
context.erase(instruction: self)
55+
return true
56+
}
2957
}
3058

3159
extension DestructureStructInst : OnoneSimplifyable, SILCombineSimplifyable {
3260
func simplify(_ context: SimplifyContext) {
3361

62+
// If the struct is trivial, replace
63+
// ```
64+
// (%1, %2) = destructure_struct %s
65+
// ```
66+
// ->
67+
// ```
68+
// %1 = struct_extract %s, #S.field0
69+
// %2 = struct_extract %s, #S.field1
70+
// ```
71+
// This canonicalization helps other optimizations to e.g. CSE tuple_extracts.
72+
//
73+
if replaceWithStructExtract(context) {
74+
return
75+
}
76+
3477
switch self.struct {
3578
case let str as StructInst:
3679
// Eliminate the redundant instruction pair
@@ -82,6 +125,19 @@ extension DestructureStructInst : OnoneSimplifyable, SILCombineSimplifyable {
82125
break
83126
}
84127
}
128+
129+
private func replaceWithStructExtract(_ context: SimplifyContext) -> Bool {
130+
guard self.struct.type.isTrivial(in: parentFunction) else {
131+
return false
132+
}
133+
let builder = Builder(before: self, context)
134+
for (fieldIdx, result) in results.enumerated() {
135+
let fieldValue = builder.createStructExtract(struct: self.struct, fieldIndex: fieldIdx)
136+
result.uses.replaceAll(with: fieldValue, context)
137+
}
138+
context.erase(instruction: self)
139+
return true
140+
}
85141
}
86142

87143
private func tryReplaceConstructDestructPair(construct: SingleValueInstruction,

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

+2
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,15 @@ private func registerSwiftPasses() {
101101
registerPass(autodiffClosureSpecialization, { autodiffClosureSpecialization.run($0) })
102102

103103
// Instruction passes
104+
registerForSILCombine(BeginBorrowInst.self, { run(BeginBorrowInst.self, $0) })
104105
registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) })
105106
registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) })
106107
registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) })
107108
registerForSILCombine(StrongReleaseInst.self, { run(StrongReleaseInst.self, $0) })
108109
registerForSILCombine(RetainValueInst.self, { run(RetainValueInst.self, $0) })
109110
registerForSILCombine(ReleaseValueInst.self, { run(ReleaseValueInst.self, $0) })
110111
registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) })
112+
registerForSILCombine(LoadBorrowInst.self, { run(LoadBorrowInst.self, $0) })
111113
registerForSILCombine(CopyValueInst.self, { run(CopyValueInst.self, $0) })
112114
registerForSILCombine(DestroyValueInst.self, { run(DestroyValueInst.self, $0) })
113115
registerForSILCombine(DestructureStructInst.self, { run(DestructureStructInst.self, $0) })

include/swift/SILOptimizer/PassManager/Passes.def

+2
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ PASS(PruneVTables, "prune-vtables",
518518
"Mark class methods that do not require vtable dispatch")
519519
PASS_RANGE(AllPasses, AliasInfoDumper, PruneVTables)
520520

521+
SWIFT_SILCOMBINE_PASS(BeginBorrowInst)
521522
SWIFT_SILCOMBINE_PASS(BeginCOWMutationInst)
522523
SWIFT_SILCOMBINE_PASS(ClassifyBridgeObjectInst)
523524
SWIFT_SILCOMBINE_PASS(GlobalValueInst)
@@ -526,6 +527,7 @@ SWIFT_SILCOMBINE_PASS(StrongReleaseInst)
526527
SWIFT_SILCOMBINE_PASS(RetainValueInst)
527528
SWIFT_SILCOMBINE_PASS(ReleaseValueInst)
528529
SWIFT_SILCOMBINE_PASS(LoadInst)
530+
SWIFT_SILCOMBINE_PASS(LoadBorrowInst)
529531
SWIFT_SILCOMBINE_PASS(CopyValueInst)
530532
SWIFT_SILCOMBINE_PASS(DestroyValueInst)
531533
SWIFT_SILCOMBINE_PASS(DestructureStructInst)

lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,11 @@ bool GlobalPropertyOpt::canAddressEscape(SILValue V, bool acceptStore) {
219219

220220
// These instructions do not cause the address to escape.
221221
if (isa<LoadInst>(User) ||
222+
isa<LoadBorrowInst>(User) ||
222223
isa<DebugValueInst>(User) ||
223224
isa<StrongReleaseInst>(User) ||
224225
isa<StrongRetainInst>(User) ||
226+
isa<DestroyAddrInst>(User) ||
225227
isa<DeallocBoxInst>(User) ||
226228
isa<DeallocStackInst>(User))
227229
continue;
@@ -285,10 +287,11 @@ void GlobalPropertyOpt::scanInstruction(swift::SILInstruction *Inst) {
285287
default:
286288
break;
287289
}
288-
} else if (auto *LI = dyn_cast<LoadInst>(Inst)) {
290+
} else if (isa<LoadInst>(Inst) || isa<LoadBorrowInst>(Inst)) {
291+
auto *LI = cast<SingleValueInstruction>(Inst);
289292
if (isArrayType(LI->getType())) {
290293
// Add a dependency from the value at the address to the loaded value.
291-
SILValue loadAddr = LI->getOperand();
294+
SILValue loadAddr = LI->getOperand(0);
292295
assert(loadAddr->getType().isAddress());
293296
addDependency(getAddrEntry(loadAddr), getValueEntry(LI));
294297
return;

lib/SILOptimizer/LoopTransforms/ArrayOpt.h

+8-8
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ class StructUseCollector {
8181

8282
UserList AggregateAddressUsers;
8383
UserList StructAddressUsers;
84-
SmallVector<LoadInst*, 16> StructLoads;
84+
SmallVector<SingleValueInstruction *, 16> StructLoads;
8585
UserList StructValueUsers;
8686
UserOperList ElementAddressUsers;
87-
SmallVector<std::pair<LoadInst*, Operand*>, 16> ElementLoads;
87+
SmallVector<std::pair<SingleValueInstruction *, Operand*>, 16> ElementLoads;
8888
UserOperList ElementValueUsers;
8989
VisitedSet Visited;
9090

@@ -126,7 +126,7 @@ class StructUseCollector {
126126
return false;
127127
for (SILInstruction *user : StructAddressUsers) {
128128
// ignore load users
129-
if (isa<LoadInst>(user))
129+
if (isa<LoadInst>(user) || isa<LoadBorrowInst>(user))
130130
continue;
131131
if (user != use1 && user != use2)
132132
return false;
@@ -162,8 +162,8 @@ class StructUseCollector {
162162
if (StructVal) {
163163
// Found a use of an element.
164164
assert(AccessPathSuffix.empty() && "should have accessed struct");
165-
if (auto *LoadI = dyn_cast<LoadInst>(UseInst)) {
166-
ElementLoads.push_back(std::make_pair(LoadI, StructVal));
165+
if (isa<LoadInst>(UseInst) || isa<LoadBorrowInst>(UseInst)) {
166+
ElementLoads.push_back(std::make_pair(cast<SingleValueInstruction>(UseInst), StructVal));
167167
continue;
168168
}
169169

@@ -185,9 +185,9 @@ class StructUseCollector {
185185

186186
if (AccessPathSuffix.empty()) {
187187
// Found a use of the struct at the given access path.
188-
if (auto *LoadI = dyn_cast<LoadInst>(UseInst)) {
189-
StructLoads.push_back(LoadI);
190-
StructAddressUsers.push_back(LoadI);
188+
if (isa<LoadInst>(UseInst) || isa<LoadBorrowInst>(UseInst)) {
189+
StructLoads.push_back(cast<SingleValueInstruction>(UseInst));
190+
StructAddressUsers.push_back(UseInst);
191191
continue;
192192
}
193193

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ bool areArraysEqual(RCIdentityFunctionInfo *RCIA, SILValue A, SILValue B,
5555
return true;
5656
// We have stripped off struct_extracts. Remove the load to look at the
5757
// address we are loading from.
58-
if (auto *ALoad = dyn_cast<LoadInst>(A))
59-
A = ALoad->getOperand();
60-
if (auto *BLoad = dyn_cast<LoadInst>(B))
61-
B = BLoad->getOperand();
58+
if (isa<LoadInst>(A) || isa<LoadBorrowInst>(A))
59+
A = cast<SingleValueInstruction>(A)->getOperand(0);
60+
if (isa<LoadInst>(B) || isa<LoadBorrowInst>(B))
61+
B = cast<SingleValueInstruction>(B)->getOperand(0);
6262
// Strip off struct_extract_refs until we hit array address.
6363
if (ArrayAddress) {
6464
StructElementAddrInst *SEAI = nullptr;
@@ -463,6 +463,10 @@ bool COWArrayOpt::checkSafeArrayAddressUses(UserList &AddressUsers) {
463463
return false;
464464
}
465465

466+
if (isa<LoadBorrowInst>(UseInst)) {
467+
continue;
468+
}
469+
466470
if (isa<DeallocStackInst>(UseInst)) {
467471
// Handle destruction of a local array.
468472
continue;
@@ -569,6 +573,9 @@ bool COWArrayOpt::checkSafeArrayValueUses(UserList &ArrayValueUsers) {
569573
if (isa<MarkDependenceInst>(UseInst))
570574
continue;
571575

576+
if (isa<EndBorrowInst>(UseInst))
577+
continue;
578+
572579
if (UseInst->isDebugInstruction())
573580
continue;
574581

0 commit comments

Comments
 (0)