Skip to content

Commit e7090e3

Browse files
authored
Merge pull request #61641 from eeckstein/side-effect-improvements
Follow-up changes for function effects
2 parents d4f5b1b + 9ae24f8 commit e7090e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+397
-106
lines changed

SwiftCompilerSources/Sources/Basic/Utils.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren {
7676
}
7777

7878
public static func !=(lhs: StringRef, rhs: StaticString) -> Bool { !(lhs == rhs) }
79+
80+
public static func ~=(pattern: StaticString, value: StringRef) -> Bool { value == pattern }
7981
}
8082

8183
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ public struct CalleeAnalysis {
2121
// isDeinitBarrierFn:
2222
{ (inst : BridgedInstruction, bca: BridgedCalleeAnalysis) -> Bool in
2323
return inst.instruction.isDeinitBarrier(bca.analysis)
24+
},
25+
// getMemBehaviorFn
26+
{ (bridgedCtxt: BridgedPassContext, bridgedApply: BridgedInstruction, observeRetains: Bool) -> BridgedMemoryBehavior in
27+
let context = PassContext(_bridged: bridgedCtxt)
28+
let apply = bridgedApply.instruction as! ApplySite
29+
let e = context.calleeAnalysis.getSideEffects(of: apply)
30+
return e.getMemBehavior(observeRetains: observeRetains)
2431
}
2532
)
2633
}
@@ -52,6 +59,40 @@ public struct CalleeAnalysis {
5259
}
5360
return FunctionArray(bridged: bridgedDtors)
5461
}
62+
63+
/// Returns the global (i.e. not argument specific) side effects of an apply.
64+
public func getSideEffects(of apply: ApplySite) -> SideEffects.GlobalEffects {
65+
guard let callees = getCallees(callee: apply.callee) else {
66+
return .worstEffects
67+
}
68+
69+
var result = SideEffects.GlobalEffects()
70+
for callee in callees {
71+
let calleeEffects = callee.getSideEffects()
72+
result.merge(with: calleeEffects)
73+
}
74+
return result
75+
}
76+
77+
/// Returns the argument specific side effects of an apply.
78+
public func getSideEffects(of apply: ApplySite, forArgument argumentIdx: Int, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
79+
let calleeArgIdx = apply.calleeArgIndex(callerArgIndex: argumentIdx)
80+
let convention = apply.getArgumentConvention(calleeArgIndex: calleeArgIdx)
81+
let argument = apply.arguments[argumentIdx].at(path)
82+
83+
guard let callees = getCallees(callee: apply.callee) else {
84+
return .worstEffects.restrictedTo(argument: argument, withConvention: convention)
85+
}
86+
87+
var result = SideEffects.GlobalEffects()
88+
for callee in callees {
89+
let calleeEffects = callee.getSideEffects(forArgument: argument,
90+
atIndex: calleeArgIdx,
91+
withConvention: convention)
92+
result.merge(with: calleeEffects)
93+
}
94+
return result.restrictedTo(argument: argument, withConvention: convention)
95+
}
5596
}
5697

5798
extension FullApplySite {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeEscapeEffects.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ import SIL
2828
let computeEscapeEffects = FunctionPass(name: "compute-escape-effects", {
2929
(function: Function, context: PassContext) in
3030

31-
var newEffects = Stack<EscapeEffects.ArgumentEffect>(context)
32-
defer { newEffects.deinitialize() }
31+
var newEffects = function.effects.escapeEffects.arguments.filter {!$0.isDerived }
3332

3433
let returnInst = function.returnInstruction
3534
let argsWithDefinedEffects = getArgIndicesWithDefinedEscapingEffects(of: function)
3635

3736
for arg in function.arguments {
3837
// We are not interested in arguments with trivial types.
39-
if !arg.type.isNonTrivialOrContainsRawPointer(in: function) { continue }
38+
if arg.hasTrivialNonPointerType { continue }
4039

4140
// Also, we don't want to override defined effects.
4241
if argsWithDefinedEffects.contains(arg.index) { continue }
@@ -52,7 +51,7 @@ let computeEscapeEffects = FunctionPass(name: "compute-escape-effects", {
5251
context) {
5352
let effect = EscapeEffects.ArgumentEffect(.notEscaping, argumentIndex: arg.index,
5453
pathPattern: SmallProjectionPath(.anything))
55-
newEffects.push(effect)
54+
newEffects.append(effect)
5655
continue
5756
}
5857

@@ -65,17 +64,22 @@ let computeEscapeEffects = FunctionPass(name: "compute-escape-effects", {
6564
}
6665
}
6766

67+
// Don't modify the effects if they didn't change. This avoids sending a change notification
68+
// which can trigger unnecessary other invalidations.
69+
if newEffects == function.effects.escapeEffects.arguments {
70+
return
71+
}
72+
6873
context.modifyEffects(in: function) { (effects: inout FunctionEffects) in
69-
effects.escapeEffects.arguments = effects.escapeEffects.arguments.filter { !$0.isDerived }
70-
effects.escapeEffects.arguments.append(contentsOf: newEffects)
74+
effects.escapeEffects.arguments = newEffects
7175
}
7276
})
7377

7478

7579
/// Returns true if an argument effect was added.
7680
private
7781
func addArgEffects(_ arg: FunctionArgument, argPath ap: SmallProjectionPath,
78-
to newEffects: inout Stack<EscapeEffects.ArgumentEffect>,
82+
to newEffects: inout [EscapeEffects.ArgumentEffect],
7983
_ returnInst: ReturnInst?, _ context: PassContext) -> Bool {
8084
// Correct the path if the argument is not a class reference itself, but a value type
8185
// containing one or more references.
@@ -159,7 +163,7 @@ func addArgEffects(_ arg: FunctionArgument, argPath ap: SmallProjectionPath,
159163
effect = EscapeEffects.ArgumentEffect(.escapingToArgument(toArgIdx, toPath, exclusive),
160164
argumentIndex: arg.index, pathPattern: argPath)
161165
}
162-
newEffects.push(effect)
166+
newEffects.append(effect)
163167
return true
164168
}
165169

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ let computeSideEffects = FunctionPass(name: "compute-side-effects", {
6666
collectedEffects.addEffectsForEcapingArgument(argument: argument)
6767
}
6868

69+
// Don't modify the effects if they didn't change. This avoids sending a change notification
70+
// which can trigger unnecessary other invalidations.
71+
if let existingEffects = function.effects.sideEffects,
72+
existingEffects.arguments == collectedEffects.argumentEffects,
73+
existingEffects.global == collectedEffects.globalEffects {
74+
return
75+
}
76+
6977
// Finally replace the function's side effects.
7078
context.modifyEffects(in: function) { (effects: inout FunctionEffects) in
7179
effects.sideEffects = SideEffects(arguments: collectedEffects.argumentEffects, global: collectedEffects.globalEffects)
@@ -160,6 +168,11 @@ private struct CollectedEffects {
160168
is CondFailInst:
161169
break
162170

171+
case is BeginCOWMutationInst, is IsUniqueInst, is IsEscapingClosureInst:
172+
// Model reference count reading as "destroy" for now. Although we could intoduce a "read-refcount"
173+
// effect, it would not give any significant benefit in any of our current optimizations.
174+
addEffects(.destroy, to: inst.operands[0].value, fromInitialPath: SmallProjectionPath(.anyValueFields))
175+
163176
default:
164177
if inst.mayRelease {
165178
globalEffects = .worstEffects
@@ -195,15 +208,17 @@ private struct CollectedEffects {
195208

196209
if escapeWalker.hasUnknownUses(argument: argument) {
197210
// Worst case: we don't know anything about how the argument escapes.
198-
addEffects(globalEffects.restrictedTo(argument: argument, withConvention: argument.convention), to: argument)
211+
addEffects(globalEffects.restrictedTo(argument: argument.at(SmallProjectionPath(.anything)),
212+
withConvention: argument.convention), to: argument)
199213

200214
} else if escapeWalker.foundTakingLoad {
201215
// In most cases we can just ignore loads. But if the load is actually "taking" the
202216
// underlying memory allocation, we must consider this as a "destroy", because we don't
203217
// know what's happening with the loaded value. If there is any destroying instruction in the
204218
// function, it might be the destroy of the loaded value.
205219
let effects = SideEffects.GlobalEffects(ownership: globalEffects.ownership)
206-
addEffects(effects.restrictedTo(argument: argument, withConvention: argument.convention), to: argument)
220+
addEffects(effects.restrictedTo(argument: argument.at(SmallProjectionPath(.anything)),
221+
withConvention: argument.convention), to: argument)
207222

208223
} else if escapeWalker.foundConsumingPartialApply && globalEffects.ownership.destroy {
209224
// Similar situation with apply instructions which consume the callee closure.
@@ -228,7 +243,7 @@ private struct CollectedEffects {
228243
} else {
229244
// The callee doesn't have any computed effects. At least we can do better
230245
// if it has any defined effect attribute (like e.g. `[readnone]`).
231-
globalEffects.merge(with: callee.definedSideEffects)
246+
globalEffects.merge(with: callee.definedGlobalEffects)
232247
}
233248
}
234249

@@ -244,15 +259,12 @@ private struct CollectedEffects {
244259
if let calleePath = calleeEffect.copy { addEffects(.copy, to: argument, fromInitialPath: calleePath) }
245260
if let calleePath = calleeEffect.destroy { addEffects(.destroy, to: argument, fromInitialPath: calleePath) }
246261
} else {
247-
var calleeEffects = callee.definedSideEffects
248-
249262
let convention = callee.getArgumentConvention(for: calleeArgIdx)
250-
if convention.isIndirectIn {
251-
calleeEffects.memory.read = true
252-
} else if convention == .indirectOut {
253-
calleeEffects.memory.write = true
254-
}
255-
addEffects(calleeEffects.restrictedTo(argument: argument, withConvention: convention), to: argument)
263+
let wholeArgument = argument.at(SmallProjectionPath(.anything))
264+
let calleeEffects = callee.getSideEffects(forArgument: wholeArgument,
265+
atIndex: calleeArgIdx,
266+
withConvention: convention)
267+
addEffects(calleeEffects.restrictedTo(argument: wholeArgument, withConvention: convention), to: argument)
256268
}
257269
}
258270
}
@@ -266,7 +278,7 @@ private struct CollectedEffects {
266278
// deallocates an object.
267279
if let destructors = calleeAnalysis.getDestructors(of: addressOrValue.type) {
268280
for destructor in destructors {
269-
globalEffects.merge(with: destructor.effects.accumulatedSideEffects)
281+
globalEffects.merge(with: destructor.getSideEffects())
270282
}
271283
} else {
272284
globalEffects = .worstEffects
@@ -368,7 +380,7 @@ private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
368380
mutating func hasUnknownUses(argument: FunctionArgument) -> Bool {
369381
if argument.type.isAddress {
370382
return walkDownUses(ofAddress: argument, path: UnusedWalkingPath()) == .abortWalk
371-
} else if argument.type.isTrivial(in: argument.function) {
383+
} else if argument.hasTrivialType {
372384
return false
373385
} else {
374386
return walkDownUses(ofValue: argument, path: UnusedWalkingPath()) == .abortWalk
@@ -407,22 +419,22 @@ private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
407419
switch inst {
408420
case let copy as CopyAddrInst:
409421
if address == copy.sourceOperand &&
410-
!address.value.type.isTrivial(in: inst.function) &&
422+
!address.value.hasTrivialType &&
411423
(!function.hasOwnership || copy.isTakeOfSrc) {
412424
foundTakingLoad = true
413425
}
414426
return .continueWalk
415427

416428
case let load as LoadInst:
417-
if !address.value.type.isTrivial(in: function) &&
429+
if !address.value.hasTrivialType &&
418430
// In non-ossa SIL we don't know if a load is taking.
419431
(!function.hasOwnership || load.ownership == .take) {
420432
foundTakingLoad = true
421433
}
422434
return .continueWalk
423435

424436
case is LoadWeakInst, is LoadUnownedInst, is LoadBorrowInst:
425-
if !function.hasOwnership && !address.value.type.isTrivial(in: function) {
437+
if !function.hasOwnership && !address.value.hasTrivialType {
426438
foundTakingLoad = true
427439
}
428440
return .continueWalk

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ReleaseDevirtualizer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private struct FindAllocationOfRelease : ValueUseDefWalker {
149149
//
150150
var nonTrivialOperandFound = false
151151
for operand in def.operands {
152-
if !operand.value.type.isTrivial(in: def.function) {
152+
if !operand.value.hasTrivialType {
153153
if nonTrivialOperandFound {
154154
return .abortWalk
155155
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassContext.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ struct PassContext {
119119
}
120120

121121
func modifyEffects(in function: Function, _ body: (inout FunctionEffects) -> ()) {
122-
notifyFunctionDataChanged()
122+
notifyEffectsChanged()
123123
function._modifyEffects(body)
124124
}
125125

@@ -139,8 +139,8 @@ struct PassContext {
139139
PassContext_notifyChanges(_bridged, branchesChanged)
140140
}
141141

142-
fileprivate func notifyFunctionDataChanged() {
143-
PassContext_notifyChanges(_bridged, functionDataChanged)
142+
fileprivate func notifyEffectsChanged() {
143+
PassContext_notifyChanges(_bridged, effectsChanged)
144144
}
145145
}
146146

@@ -243,7 +243,7 @@ extension RefCountingInst {
243243

244244
extension Function {
245245
func set(needStackProtection: Bool, _ context: PassContext) {
246-
context.notifyFunctionDataChanged()
246+
context.notifyEffectsChanged()
247247
SILFunction_setNeedStackProtection(bridged, needStackProtection ? 1 : 0)
248248
}
249249
}

SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,11 @@ extension EscapeVisitor {
197197
}
198198

199199
func hasRelevantType(_ value: Value, at path: SmallProjectionPath, analyzeAddresses: Bool) -> Bool {
200-
let type = value.type
201-
if type.isNonTrivialOrContainsRawPointer(in: value.function) { return true }
200+
if !value.hasTrivialNonPointerType { return true }
202201

203202
// For selected addresses we also need to consider trivial types (`value`
204203
// is a selected address if the path does not contain any class projections).
205-
if analyzeAddresses && type.isAddress && !path.hasClassProjection { return true }
204+
if analyzeAddresses && value.type.isAddress && !path.hasClassProjection { return true }
206205
return false
207206
}
208207
}
@@ -542,14 +541,15 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
542541
let svi = instruction as! SingleValueInstruction
543542

544543
// Even when analyzing addresses, a loaded trivial value can be ignored.
545-
if !svi.type.isNonTrivialOrContainsRawPointer(in: svi.function) { return .continueWalk }
544+
if svi.hasTrivialNonPointerType { return .continueWalk }
546545
return walkDownUses(ofValue: svi, path: path.with(knownType: nil))
547546
case let atp as AddressToPointerInst:
548547
return walkDownUses(ofValue: atp, path: path.with(knownType: nil))
549548
case let ia as IndexAddrInst:
550549
assert(operand.index == 0)
551550
return walkDownUses(ofAddress: ia, path: path.with(knownType: nil))
552-
case is DeallocStackInst, is InjectEnumAddrInst, is FixLifetimeInst, is EndBorrowInst, is EndAccessInst:
551+
case is DeallocStackInst, is InjectEnumAddrInst, is FixLifetimeInst, is EndBorrowInst, is EndAccessInst,
552+
is DebugValueInst:
553553
return .continueWalk
554554
default:
555555
return isEscaping

SwiftCompilerSources/Sources/SIL/ApplySite.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,11 @@ extension FullApplySite {
133133
}
134134
return nil
135135
}
136+
137+
/// The number of indirect out arguments.
138+
///
139+
/// 0 if the callee has a direct or no return value and 1, if it has an indirect return value.
140+
public var numIndirectResultArguments: Int {
141+
return FullApplySite_numIndirectResultArguments(bridged)
142+
}
136143
}

0 commit comments

Comments
 (0)