Skip to content

Commit bf496aa

Browse files
committed
Optimizer: add simplification for fix_lifetime
Canonicalize a `fix_lifetime` from an address to a `load` + `fix_lifetime`: ``` %1 = alloc_stack $T ... fix_lifetime %1 ``` -> ``` %1 = alloc_stack $T ... %2 = load %1 fix_lifetime %2 ``` This transformation is done for `alloc_stack` and `store_borrow` (which always has an `alloc_stack` operand). The benefit of this transformation is that it enables other optimizations, like mem2reg. This peephole optimization was already done in SILCombine, but it didn't handle store_borrow. A good opportunity to make an instruction simplification out of it. This is part of fixing regressions when enabling OSSA modules: rdar://140229560
1 parent 9781e99 commit bf496aa

File tree

7 files changed

+164
-17
lines changed

7 files changed

+164
-17
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ swift_compiler_sources(Optimizer
2222
SimplifyDebugStep.swift
2323
SimplifyDestroyValue.swift
2424
SimplifyDestructure.swift
25+
SimplifyFixLifetime.swift
2526
SimplifyGlobalValue.swift
2627
SimplifyInitEnumDataAddr.swift
2728
SimplifyKeyPath.swift
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===--- SimplifyFixLifetime.swift ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Canonicalize a `fix_lifetime` from an address to a `load` + `fix_lifetime`:
16+
/// ```
17+
/// %1 = alloc_stack $T
18+
/// ...
19+
/// fix_lifetime %1
20+
/// ```
21+
/// ->
22+
/// ```
23+
/// %1 = alloc_stack $T
24+
/// ...
25+
/// %2 = load %1
26+
/// fix_lifetime %2
27+
/// ```
28+
///
29+
/// This transformation is done for `alloc_stack` and `store_borrow` (which always has an `alloc_stack`
30+
/// operand).
31+
/// The benefit of this transformation is that it enables other optimizations, like mem2reg.
32+
///
33+
extension FixLifetimeInst : Simplifyable, SILCombineSimplifyable {
34+
func simplify(_ context: SimplifyContext) {
35+
let opValue = operand.value
36+
guard opValue is AllocStackInst || opValue is StoreBorrowInst,
37+
opValue.type.isLoadable(in: parentFunction)
38+
else {
39+
return
40+
}
41+
42+
let builder = Builder(before: self, context)
43+
let loadedValue: Value
44+
if !parentFunction.hasOwnership {
45+
loadedValue = builder.createLoad(fromAddress: opValue, ownership: .unqualified)
46+
} else if opValue.type.isTrivial(in: parentFunction) {
47+
loadedValue = builder.createLoad(fromAddress: opValue, ownership: .trivial)
48+
} else {
49+
loadedValue = builder.createLoadBorrow(fromAddress: opValue)
50+
Builder(after: self, context).createEndBorrow(of: loadedValue)
51+
}
52+
operand.set(to: loadedValue, context)
53+
}
54+
}
55+

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ private func registerSwiftPasses() {
103103
// Instruction passes
104104
registerForSILCombine(BeginBorrowInst.self, { run(BeginBorrowInst.self, $0) })
105105
registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) })
106+
registerForSILCombine(FixLifetimeInst.self, { run(FixLifetimeInst.self, $0) })
106107
registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) })
107108
registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) })
108109
registerForSILCombine(StrongReleaseInst.self, { run(StrongReleaseInst.self, $0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ PASS_RANGE(AllPasses, AliasInfoDumper, PruneVTables)
521521
SWIFT_SILCOMBINE_PASS(BeginBorrowInst)
522522
SWIFT_SILCOMBINE_PASS(BeginCOWMutationInst)
523523
SWIFT_SILCOMBINE_PASS(ClassifyBridgeObjectInst)
524+
SWIFT_SILCOMBINE_PASS(FixLifetimeInst)
524525
SWIFT_SILCOMBINE_PASS(GlobalValueInst)
525526
SWIFT_SILCOMBINE_PASS(StrongRetainInst)
526527
SWIFT_SILCOMBINE_PASS(StrongReleaseInst)

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ class SILCombiner :
281281
SILInstruction *visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCMI);
282282
SILInstruction *visitObjCToThickMetatypeInst(ObjCToThickMetatypeInst *OCTTMI);
283283
SILInstruction *visitTupleExtractInst(TupleExtractInst *TEI);
284-
SILInstruction *visitFixLifetimeInst(FixLifetimeInst *FLI);
285284
SILInstruction *visitSwitchValueInst(SwitchValueInst *SVI);
286285
SILInstruction *
287286
visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,22 +1760,6 @@ SILInstruction *SILCombiner::visitTupleExtractInst(TupleExtractInst *TEI) {
17601760
return nullptr;
17611761
}
17621762

1763-
SILInstruction *SILCombiner::visitFixLifetimeInst(FixLifetimeInst *fli) {
1764-
// fix_lifetime(alloc_stack) -> fix_lifetime(load(alloc_stack))
1765-
Builder.setCurrentDebugScope(fli->getDebugScope());
1766-
if (auto *ai = dyn_cast<AllocStackInst>(fli->getOperand())) {
1767-
if (fli->getOperand()->getType().isLoadable(*fli->getFunction())) {
1768-
// load when ossa is disabled
1769-
auto load = Builder.emitLoadBorrowOperation(fli->getLoc(), ai);
1770-
Builder.createFixLifetime(fli->getLoc(), load);
1771-
// no-op when ossa is disabled
1772-
Builder.emitEndBorrowOperation(fli->getLoc(), load);
1773-
return eraseInstFromFunction(*fli);
1774-
}
1775-
}
1776-
return nullptr;
1777-
}
1778-
17791763
static std::optional<SILType>
17801764
shouldReplaceCallByContiguousArrayStorageAnyObject(SILFunction &F,
17811765
CanType storageMetaTy) {
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// RUN: %target-sil-opt %s -simplification -simplify-instruction=fix_lifetime | %FileCheck %s
2+
3+
import Swift
4+
import Builtin
5+
6+
class C {}
7+
8+
// CHECK-LABEL: sil @load_from_alloc_stack :
9+
// CHECK: %1 = alloc_stack
10+
// CHECK-NEXT: store %0 to %1
11+
// CHECK-NEXT: %3 = load %1
12+
// CHECK-NEXT: fix_lifetime %3
13+
// CHECK-NEXT: dealloc_stack %1
14+
// CHECK: } // end sil function 'load_from_alloc_stack'
15+
sil @load_from_alloc_stack : $@convention(thin) (@owned C) -> () {
16+
bb0(%0: $C):
17+
%1 = alloc_stack $C
18+
store %0 to %1
19+
fix_lifetime %1
20+
dealloc_stack %1
21+
%r = tuple ()
22+
return %r : $()
23+
}
24+
25+
// CHECK-LABEL: sil @load_from_inout :
26+
// CHECK-NOT: load
27+
// CHECK: fix_lifetime %0
28+
// CHECK: } // end sil function 'load_from_inout'
29+
sil @load_from_inout : $@convention(thin) (@inout C) -> () {
30+
bb0(%0: $*C):
31+
fix_lifetime %0
32+
%r = tuple ()
33+
return %r : $()
34+
}
35+
36+
// CHECK-LABEL: sil @not_loadable :
37+
// CHECK-NOT: load
38+
// CHECK: fix_lifetime %1
39+
// CHECK: } // end sil function 'not_loadable'
40+
sil @not_loadable : $@convention(thin) <T> (@in_guaranteed T) -> () {
41+
bb0(%0: $*T):
42+
%1 = alloc_stack $T
43+
copy_addr %0 to %1
44+
fix_lifetime %1
45+
dealloc_stack %1
46+
%r = tuple ()
47+
return %r : $()
48+
}
49+
50+
// CHECK-LABEL: sil [ossa] @load_from_alloc_stack_ossa :
51+
// CHECK: %1 = alloc_stack
52+
// CHECK-NEXT: store %0 to [init] %1
53+
// CHECK-NEXT: %3 = load_borrow %1
54+
// CHECK-NEXT: fix_lifetime %3
55+
// CHECK-NEXT: end_borrow %3
56+
// CHECK-NEXT: destroy_addr %1
57+
// CHECK-NEXT: dealloc_stack %1
58+
// CHECK: } // end sil function 'load_from_alloc_stack_ossa'
59+
sil [ossa] @load_from_alloc_stack_ossa : $@convention(thin) (@owned C) -> () {
60+
bb0(%0: @owned $C):
61+
%1 = alloc_stack $C
62+
store %0 to [init] %1
63+
fix_lifetime %1
64+
destroy_addr %1
65+
dealloc_stack %1
66+
%r = tuple ()
67+
return %r : $()
68+
}
69+
70+
// CHECK-LABEL: sil [ossa] @load_from_store_borrow :
71+
// CHECK: %1 = alloc_stack
72+
// CHECK-NEXT: %2 = store_borrow %0 to %1
73+
// CHECK-NEXT: %3 = load_borrow %2
74+
// CHECK-NEXT: fix_lifetime %3
75+
// CHECK-NEXT: end_borrow %3
76+
// CHECK-NEXT: end_borrow %2
77+
// CHECK-NEXT: dealloc_stack %1
78+
// CHECK: } // end sil function 'load_from_store_borrow'
79+
sil [ossa] @load_from_store_borrow : $@convention(thin) (@guaranteed C) -> () {
80+
bb0(%0: @guaranteed $C):
81+
%1 = alloc_stack $C
82+
%2 = store_borrow %0 to %1
83+
fix_lifetime %2
84+
end_borrow %2
85+
dealloc_stack %1
86+
%r = tuple ()
87+
return %r : $()
88+
}
89+
90+
// CHECK-LABEL: sil [ossa] @load_trivial_from_alloc_stack :
91+
// CHECK: %1 = alloc_stack
92+
// CHECK-NEXT: store %0 to [trivial] %1
93+
// CHECK-NEXT: %3 = load [trivial] %1
94+
// CHECK-NEXT: fix_lifetime %3
95+
// CHECK-NEXT: dealloc_stack %1
96+
// CHECK: } // end sil function 'load_trivial_from_alloc_stack'
97+
sil [ossa] @load_trivial_from_alloc_stack : $@convention(thin) (Int) -> () {
98+
bb0(%0: $Int):
99+
%1 = alloc_stack $Int
100+
store %0 to [trivial] %1
101+
fix_lifetime %1
102+
dealloc_stack %1
103+
%r = tuple ()
104+
return %r : $()
105+
}
106+

0 commit comments

Comments
 (0)