Skip to content

Commit 55009bf

Browse files
authored
Merge pull request #78163 from eeckstein/improve-dead-object-elimination
Two optimization improvements to fix dead-object-elimination with OSSA
2 parents 215db61 + bf496aa commit 55009bf

File tree

9 files changed

+194
-18
lines changed

9 files changed

+194
-18
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) {

lib/SILOptimizer/Transforms/DeadObjectElimination.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,8 @@ recursivelyCollectInteriorUses(ValueBase *DefInst,
604604

605605
// Lifetime endpoints that don't allow the address to escape.
606606
if (isa<RefCountingInst>(User) || isa<DebugValueInst>(User) ||
607-
isa<FixLifetimeInst>(User) || isa<DestroyValueInst>(User)) {
607+
isa<FixLifetimeInst>(User) || isa<DestroyValueInst>(User) ||
608+
isa<EndBorrowInst>(User)) {
608609
AllUsers.insert(User);
609610
continue;
610611
}
@@ -632,6 +633,13 @@ recursivelyCollectInteriorUses(ValueBase *DefInst,
632633
}
633634
continue;
634635
}
636+
if (auto *bb = dyn_cast<BeginBorrowInst>(User)) {
637+
if (!recursivelyCollectInteriorUses(bb, AddressNode,
638+
IsInteriorAddress)) {
639+
return false;
640+
}
641+
continue;
642+
}
635643
if (auto PTAI = dyn_cast<PointerToAddressInst>(User)) {
636644
// Only one pointer-to-address is allowed for safety.
637645
if (SeenPtrToAddr)

test/SILOptimizer/dead_array_elim_ossa.sil

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct NonTrivialStruct {
3131
sil [_semantics "array.uninitialized_intrinsic"] @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
3232

3333
sil [_semantics "array.uninitialized"] @adoptStorageSpecializedForInt : $@convention(method) (@guaranteed _ContiguousArrayStorage<Int>, Builtin.Word, @thin Array<Int>.Type) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
34+
sil [_semantics "array.uninitialized"] @allocUninitialized : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
3435

3536
sil [_semantics "array.finalize_intrinsic"] @finalize : $@convention(thin) (@owned Array<Int>) -> @owned Array<Int>
3637

@@ -190,3 +191,23 @@ bb0(%0 : @owned $Klass):
190191
return %t : $()
191192
}
192193

194+
195+
// CHECK-LABEL: sil [ossa] @dead_array_with_borrow :
196+
// CHECK-NOT: apply
197+
// CHECK: %1 = tuple
198+
// CHECK-NEXT: return
199+
// CHECK-LABEL: } // end sil function 'dead_array_with_borrow'
200+
sil [ossa] @dead_array_with_borrow : $@convention(thin) (Int) -> () {
201+
bb0(%0 : $Int):
202+
%2 = function_ref @allocUninitialized : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
203+
%3 = apply %2(%0) : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
204+
(%4, %5) = destructure_tuple %3
205+
debug_value %4, let, name "a"
206+
%7 = begin_borrow [lexical] %4
207+
fix_lifetime %7
208+
end_borrow %7
209+
destroy_value %4
210+
%11 = tuple ()
211+
return %11
212+
}
213+
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)