Skip to content

Commit 513b9cb

Browse files
Merge pull request #61495 from nate-chandler/rdar90412220
[SILOptimizer] Add deinit-barrier side-effect.
2 parents b80d141 + 4b85d0a commit 513b9cb

30 files changed

+492
-98
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ import SIL
1616
public struct CalleeAnalysis {
1717
let bridged: BridgedCalleeAnalysis
1818

19+
static func register() {
20+
CalleeAnalysis_register(
21+
// isDeinitBarrierFn:
22+
{ (inst : BridgedInstruction, bca: BridgedCalleeAnalysis) -> Bool in
23+
return inst.instruction.isDeinitBarrier(bca.analysis)
24+
}
25+
)
26+
}
27+
1928
public func getCallees(callee: Value) -> FunctionArray? {
2029
let bridgedFuncs = CalleeAnalysis_getCallees(bridged, callee.bridged)
2130
if bridgedFuncs.incomplete != 0 {
@@ -45,6 +54,33 @@ public struct CalleeAnalysis {
4554
}
4655
}
4756

57+
extension FullApplySite {
58+
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
59+
guard let callees = analysis.getCallees(callee: callee) else {
60+
return true
61+
}
62+
return callees.contains { $0.isDeinitBarrier }
63+
}
64+
}
65+
66+
extension Instruction {
67+
public final func maySynchronize(_ analysis: CalleeAnalysis) -> Bool {
68+
if let site = self as? FullApplySite {
69+
return site.isBarrier(analysis)
70+
}
71+
return maySynchronizeNotConsideringSideEffects
72+
}
73+
74+
/// Whether lifetime ends of lexical values may safely be hoisted over this
75+
/// instruction.
76+
///
77+
/// Deinitialization barriers constrain variable lifetimes. Lexical
78+
/// end_borrow, destroy_value, and destroy_addr cannot be hoisted above them.
79+
public final func isDeinitBarrier(_ analysis: CalleeAnalysis) -> Bool {
80+
return mayAccessPointer || mayLoadWeakOrUnowned || maySynchronize(analysis)
81+
}
82+
}
83+
4884
public struct FunctionArray : RandomAccessCollection, FormattedLikeArray {
4985
fileprivate let bridged: BridgedCalleeList
5086

@@ -55,3 +91,9 @@ public struct FunctionArray : RandomAccessCollection, FormattedLikeArray {
5591
return BridgedFunctionArray_get(bridged, index).function
5692
}
5793
}
94+
// Bridging utilities
95+
96+
extension BridgedCalleeAnalysis {
97+
public var analysis: CalleeAnalysis { .init(bridged: self) }
98+
}
99+

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ private struct CollectedEffects {
8888
}
8989

9090
mutating func addInstructionEffects(_ inst: Instruction) {
91+
var checkedIfDeinitBarrier = false
9192
switch inst {
9293
case is CopyValueInst, is RetainValueInst, is StrongRetainInst:
9394
addEffects(.copy, to: inst.operands[0].value, fromInitialPath: SmallProjectionPath(.anyValueFields))
@@ -131,12 +132,14 @@ private struct CollectedEffects {
131132
addDestroyEffects(of: calleeValue)
132133
}
133134
handleApply(apply)
135+
checkedIfDeinitBarrier = true
134136

135137
case let pa as PartialApplyInst:
136138
if pa.canBeAppliedInFunction(context) {
137139
// Only if the created closure can actually be called in the function
138140
// we have to consider side-effects within the closure.
139141
handleApply(pa)
142+
checkedIfDeinitBarrier = true
140143
}
141144

142145
case let fl as FixLifetimeInst:
@@ -178,6 +181,13 @@ private struct CollectedEffects {
178181
globalEffects.allocates = true
179182
}
180183
}
184+
// If we didn't already, check whether the instruction could be a deinit
185+
// barrier. If it's an apply of some sort, that was already done in
186+
// handleApply.
187+
if !checkedIfDeinitBarrier,
188+
inst.mayBeDeinitBarrierNotConsideringSideEffects {
189+
globalEffects.isDeinitBarrier = true
190+
}
181191
}
182192

183193
mutating func addEffectsForEcapingArgument(argument: FunctionArgument) {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Parse
1717
@_cdecl("initializeSwiftModules")
1818
public func initializeSwiftModules() {
1919
registerSILClasses()
20+
registerSwiftAnalyses()
2021
registerSwiftPasses()
2122
initializeSwiftParseModules()
2223
}
@@ -75,3 +76,7 @@ private func registerSwiftPasses() {
7576
registerPass(rangeDumper, { rangeDumper.run($0) })
7677
registerPass(runUnitTests, { runUnitTests.run($0) })
7778
}
79+
80+
private func registerSwiftAnalyses() {
81+
CalleeAnalysis.register()
82+
}

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,19 +395,29 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
395395
/// are not observable form the outside and are therefore not considered.
396396
public var allocates: Bool
397397

398+
/// If true, destroys of lexical values may not be hoisted over applies of
399+
/// the function.
400+
///
401+
/// This is true when the function (or a callee, transitively) contains a
402+
/// deinit barrier instruction.
403+
public var isDeinitBarrier: Bool
404+
398405
/// When called with default arguments, it creates an "effect-free" GlobalEffects.
399406
public init(memory: Memory = Memory(read: false, write: false),
400407
ownership: Ownership = Ownership(copy: false, destroy: false),
401-
allocates: Bool = false) {
408+
allocates: Bool = false,
409+
isDeinitBarrier: Bool = false) {
402410
self.memory = memory
403411
self.ownership = ownership
404412
self.allocates = allocates
413+
self.isDeinitBarrier = isDeinitBarrier
405414
}
406415

407416
public mutating func merge(with other: GlobalEffects) {
408417
memory.merge(with: other.memory)
409418
ownership.merge(with: other.ownership)
410419
allocates = allocates || other.allocates
420+
isDeinitBarrier = isDeinitBarrier || other.isDeinitBarrier
411421
}
412422

413423
/// Removes effects, which cannot occur for an `argument` value with a given `convention`.
@@ -444,12 +454,13 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
444454
}
445455

446456
public static var worstEffects: GlobalEffects {
447-
GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true)
457+
GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true, isDeinitBarrier: true)
448458
}
449459

450460
public var description: String {
451461
var res: [String] = [memory.description, ownership.description].filter { !$0.isEmpty }
452462
if allocates { res += ["allocate"] }
463+
if isDeinitBarrier { res += ["deinit_barrier"] }
453464
return res.joined(separator: ",")
454465
}
455466
}
@@ -652,6 +663,7 @@ extension StringParser {
652663
else if consume("copy") { globalEffects.ownership.copy = true }
653664
else if consume("destroy") { globalEffects.ownership.destroy = true }
654665
else if consume("allocate") { globalEffects.allocates = true }
666+
else if consume("deinit_barrier") { globalEffects.isDeinitBarrier = true }
655667
else {
656668
break
657669
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
186186
SILFunction_needsStackProtection(bridged) != 0
187187
}
188188

189+
public var isDeinitBarrier: Bool {
190+
effects.sideEffects?.global.isDeinitBarrier ?? true
191+
}
192+
189193
// Only to be called by PassContext
190194
public func _modifyEffects(_ body: (inout FunctionEffects) -> ()) {
191195
body(&effects)

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,41 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
108108
return SILInstruction_hasUnspecifiedSideEffects(bridged)
109109
}
110110

111+
public final var mayAccessPointer: Bool {
112+
return swift_mayAccessPointer(bridged)
113+
}
114+
115+
/// Whether this instruction loads or copies a value whose storage does not
116+
/// increment the stored value's reference count.
117+
public final var mayLoadWeakOrUnowned: Bool {
118+
switch self {
119+
case is LoadWeakInst, is LoadUnownedInst, is StrongCopyUnownedValueInst, is StrongCopyUnmanagedValueInst:
120+
return true
121+
default:
122+
return false
123+
}
124+
}
125+
126+
/// Conservatively, whether this instruction could involve a synchronization
127+
/// point like a memory barrier, lock or syscall.
128+
public final var maySynchronizeNotConsideringSideEffects: Bool {
129+
switch self {
130+
case is FullApplySite, is EndApplyInst, is AbortApplyInst:
131+
return true
132+
default:
133+
return false
134+
}
135+
}
136+
137+
/// Conservatively, whether this instruction could be a barrier to hoisting
138+
/// destroys.
139+
///
140+
/// Does not consider function so effects, so every apply is treated as a
141+
/// barrier.
142+
public final var mayBeDeinitBarrierNotConsideringSideEffects: Bool {
143+
return mayAccessPointer || mayLoadWeakOrUnowned || maySynchronizeNotConsideringSideEffects
144+
}
145+
111146
public func visitReferencedFunctions(_ cl: (Function) -> ()) {
112147
}
113148

@@ -618,6 +653,10 @@ final public class ProjectBoxInst : SingleValueInstruction, UnaryInstruction {
618653

619654
final public class CopyValueInst : SingleValueInstruction, UnaryInstruction {}
620655

656+
final public class StrongCopyUnownedValueInst : SingleValueInstruction, UnaryInstruction {}
657+
658+
final public class StrongCopyUnmanagedValueInst : SingleValueInstruction, UnaryInstruction {}
659+
621660
final public class EndCOWMutationInst : SingleValueInstruction, UnaryInstruction {}
622661

623662
final public

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public func registerSILClasses() {
6060
register(ReleaseValueInst.self)
6161
register(DestroyValueInst.self)
6262
register(DestroyAddrInst.self)
63+
register(StrongCopyUnownedValueInst.self)
64+
register(StrongCopyUnmanagedValueInst.self)
6365
register(InjectEnumAddrInst.self)
6466
register(LoadInst.self)
6567
register(LoadWeakInst.self)

docs/SIL.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,10 +2444,10 @@ required.
24442444
Deinit Barriers
24452445
```````````````
24462446

2447-
Deinit barriers (see swift::isDeinitBarrier) are instructions which would be
2448-
affected by the side effects of deinitializers. To maintain the order of
2449-
effects that is visible to the programmer, destroys of lexical values cannot be
2450-
reordered with respect to them. There are three kinds:
2447+
Deinit barriers (see Instruction.isDeinitBarrier(_:)) are instructions which
2448+
would be affected by the side effects of deinitializers. To maintain the order
2449+
of effects that is visible to the programmer, destroys of lexical values cannot
2450+
be reordered with respect to them. There are three kinds:
24512451

24522452
1. synchronization points (locks, memory barriers, syscalls, etc.)
24532453
2. loads of weak or unowned values

include/swift/SIL/MemAccessUtils.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,9 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
232232
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
233233
}
234234

235-
/// Return true if \p instruction is a deinitialization barrier.
236-
///
237-
/// Deinitialization barriers constrain variable lifetimes. Lexical end_borrow,
238-
/// destroy_value, and destroy_addr cannot be hoisted above them.
239-
bool isDeinitBarrier(SILInstruction *instruction);
235+
/// Whether \p instruction accesses storage whose representation is unidentified
236+
/// such as by reading a pointer.
237+
bool mayAccessPointer(SILInstruction *instruction);
240238

241239
} // end namespace swift
242240

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ swift::SILDebugLocation SILInstruction_getLocation(BridgedInstruction inst);
364364
BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst);
365365
bool SILInstruction_mayRelease(BridgedInstruction inst);
366366
bool SILInstruction_hasUnspecifiedSideEffects(BridgedInstruction inst);
367+
bool swift_mayAccessPointer(BridgedInstruction inst);
367368

368369
BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result);
369370
SwiftInt MultiValueInstResult_getIndex(BridgedMultiValueResult result);

include/swift/SIL/SILInstruction.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -649,12 +649,6 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
649649
/// Can this instruction abort the program in some manner?
650650
bool mayTrap() const;
651651

652-
/// Involves a synchronization point like a memory barrier, lock or syscall.
653-
///
654-
/// TODO: We need side-effect analysis and library annotation for this to be
655-
/// a reasonable API. For now, this is just a placeholder.
656-
bool maySynchronize() const;
657-
658652
/// Returns true if the given instruction is completely identical to RHS.
659653
bool isIdenticalTo(const SILInstruction *RHS) const {
660654
return isIdenticalTo(RHS,

include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ class BasicCalleeAnalysis : public SILAnalysis {
230230
}
231231
};
232232

233+
bool isDeinitBarrier(SILInstruction *const instruction,
234+
BasicCalleeAnalysis *bca);
235+
233236
} // end namespace swift
234237

235238
#endif

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ typedef struct {
4040
void * _Nullable bca;
4141
} BridgedCalleeAnalysis;
4242

43+
typedef bool (* _Nonnull InstructionIsDeinitBarrierFn)(BridgedInstruction, BridgedCalleeAnalysis bca);
44+
45+
void CalleeAnalysis_register(InstructionIsDeinitBarrierFn isDeinitBarrierFn);
46+
4347
typedef struct {
4448
void * _Nullable dea;
4549
} BridgedDeadEndBlocksAnalysis;

include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
namespace swift {
3939

40+
class BasicCalleeAnalysis;
41+
4042
//===----------------------------------------------------------------------===//
4143
// MARK: CanonicalizeBorrowScope
4244
//===----------------------------------------------------------------------===//
@@ -149,6 +151,7 @@ class CanonicalizeBorrowScope {
149151

150152
bool shrinkBorrowScope(
151153
BeginBorrowInst const &bbi, InstructionDeleter &deleter,
154+
BasicCalleeAnalysis *calleeAnalysis,
152155
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts);
153156

154157
MoveValueInst *foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,
@@ -157,7 +160,8 @@ MoveValueInst *foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,
157160

158161
bool hoistDestroysOfOwnedLexicalValue(SILValue const value,
159162
SILFunction &function,
160-
InstructionDeleter &deleter);
163+
InstructionDeleter &deleter,
164+
BasicCalleeAnalysis *calleeAnalysis);
161165

162166
} // namespace swift
163167

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace swift {
3434

3535
class DominanceInfo;
3636
class DeadEndBlocks;
37+
class BasicCalleeAnalysis;
3738
template <class T> class NullablePtr;
3839

3940
/// Transform a Use Range (Operand*) into a User Range (SILInstruction *)

lib/SIL/IR/SILInstruction.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,13 +1358,6 @@ bool SILInstruction::mayTrap() const {
13581358
}
13591359
}
13601360

1361-
bool SILInstruction::maySynchronize() const {
1362-
// TODO: We need side-effect analysis and library annotation for this to be
1363-
// a reasonable API. For now, this is just a placeholder.
1364-
return isa<FullApplySite>(this) || isa<EndApplyInst>(this) ||
1365-
isa<AbortApplyInst>(this);
1366-
}
1367-
13681361
bool SILInstruction::isMetaInstruction() const {
13691362
// Every instruction that implements getVarInfo() should be in this list.
13701363
switch (getKind()) {

0 commit comments

Comments
 (0)