Skip to content

Commit d9dd935

Browse files
committed
Support mark_dependence_addr in SIL passes.
1 parent e7000e4 commit d9dd935

24 files changed

+360
-157
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ struct AliasAnalysis {
253253
case let storeBorrow as StoreBorrowInst:
254254
return memLoc.mayAlias(with: storeBorrow.destination, self) ? .init(write: true) : .noEffects
255255

256-
case let mdi as MarkDependenceInst:
256+
case let mdi as MarkDependenceInstruction:
257257
if mdi.base.type.isAddress && memLoc.mayAlias(with: mdi.base, self) {
258258
return .init(read: true)
259259
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ private func analyze(dependence: LifetimeDependence, _ context: FunctionPassCont
123123
// Check each lifetime-dependent use via a def-use visitor
124124
var walker = DiagnoseDependenceWalker(diagnostics, context)
125125
defer { walker.deinitialize() }
126-
let result = walker.walkDown(root: dependence.dependentValue)
126+
let result = walker.walkDown(dependence: dependence)
127127
// The walk may abort without a diagnostic error.
128128
assert(!error || result == .abortWalk)
129129
return result == .continueWalk
@@ -354,7 +354,7 @@ private struct LifetimeVariable {
354354
}
355355

356356
/// Walk up an address into which a dependent value has been stored. If any address in the use-def chain is a
357-
/// mark_dependence, follow the depenedence base rather than the forwarded value. If any of the dependence bases in
357+
/// mark_dependence, follow the dependence base rather than the forwarded value. If any of the dependence bases in
358358
/// within the current scope is with (either local checkInoutResult), then storing a value into that address is
359359
/// nonescaping.
360360
///

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -286,26 +286,21 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
286286
_ context: FunctionPassContext) {
287287
var currentValue = value
288288
for base in bases {
289-
let markDep = builder.createMarkDependence(
290-
value: currentValue, base: base, kind: .Unresolved)
291-
292289
if value.type.isAddress {
293290
// Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the
294291
// dependent address.
295-
//
296-
// TODO: insert a separate mark_dependence_addr instruction with no return value and do not update currentValue.
297-
} else {
298-
// TODO: implement non-inout parameter dependencies. This assumes that currentValue is the apply immediately
299-
// preceeding the mark_dependence.
300-
let uses = currentValue.uses.lazy.filter {
301-
if $0.isScopeEndingUse {
302-
return false
303-
}
304-
let inst = $0.instruction
305-
return inst != markDep && inst != initializer && !(inst is Deallocation)
292+
_ = builder.createMarkDependenceAddr(value: currentValue, base: base, kind: .Unresolved)
293+
continue
294+
}
295+
let markDep = builder.createMarkDependence(value: currentValue, base: base, kind: .Unresolved)
296+
let uses = currentValue.uses.lazy.filter {
297+
if $0.isScopeEndingUse {
298+
return false
306299
}
307-
uses.replaceAll(with: markDep, context)
300+
let inst = $0.instruction
301+
return inst != markDep && inst != initializer && !(inst is Deallocation)
308302
}
303+
uses.replaceAll(with: markDep, context)
309304
currentValue = markDep
310305
}
311306
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
112112
let localReachabilityCache = LocalVariableReachabilityCache()
113113

114114
for instruction in function.instructions {
115-
guard let markDep = instruction as? MarkDependenceInst else {
115+
guard let markDep = instruction as? MarkDependenceInstruction else {
116116
continue
117117
}
118118
guard let innerLifetimeDep = LifetimeDependence(markDep, context) else {
@@ -129,46 +129,71 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
129129
}
130130
}
131131

132-
private extension MarkDependenceInst {
132+
private extension MarkDependenceInstruction {
133133
/// Rewrite the mark_dependence base operand to ignore inner borrow scopes (begin_borrow, load_borrow).
134134
///
135135
/// Note: this could be done as a general simplification, e.g. after inlining. But currently this is only relevant for
136136
/// diagnostics.
137137
func rewriteSkippingBorrow(scope: LifetimeDependence.Scope, _ context: FunctionPassContext) -> LifetimeDependence {
138138
guard let newScope = scope.ignoreBorrowScope(context) else {
139-
return LifetimeDependence(scope: scope, dependentValue: self)
139+
return LifetimeDependence(scope: scope, markDep: self)!
140140
}
141141
let newBase = newScope.parentValue
142142
if newBase != self.baseOperand.value {
143143
self.baseOperand.set(to: newBase, context)
144144
}
145-
return LifetimeDependence(scope: newScope, dependentValue: self)
145+
return LifetimeDependence(scope: newScope, markDep: self)!
146146
}
147147

148-
/// Rewrite the mark_dependence base operand, setting it to a function argument.
149-
///
150-
/// To handle more than one function argument, new mark_dependence instructions will be chained.
151-
/// This is called when the dependent value is returned by the function and the dependence base is in the caller.
152148
func redirectFunctionReturn(to args: SingleInlineArray<FunctionArgument>, _ context: FunctionPassContext) {
153-
var updatedMarkDep: MarkDependenceInst?
149+
var updatedMarkDep: MarkDependenceInstruction?
154150
for arg in args {
155151
guard let currentMarkDep = updatedMarkDep else {
156152
self.baseOperand.set(to: arg, context)
157153
updatedMarkDep = self
158154
continue
159155
}
160-
let newMarkDep = Builder(after: currentMarkDep, location: currentMarkDep.location, context)
161-
.createMarkDependence(value: currentMarkDep, base: arg, kind: .Unresolved)
162-
let uses = currentMarkDep.uses.lazy.filter {
163-
let inst = $0.instruction
164-
return inst != newMarkDep
156+
switch currentMarkDep {
157+
case let mdi as MarkDependenceInst:
158+
updatedMarkDep = mdi.redirectFunctionReturnForward(to: arg, input: mdi, context)
159+
case let mdi as MarkDependenceAddrInst:
160+
updatedMarkDep = mdi.redirectFunctionReturnAddress(to: arg, context)
161+
default:
162+
fatalError("unexpected MarkDependenceInstruction")
165163
}
166-
uses.replaceAll(with: newMarkDep, context)
167-
updatedMarkDep = newMarkDep
168164
}
169165
}
170166
}
171167

168+
private extension MarkDependenceInst {
169+
/// Rewrite the mark_dependence base operand, setting it to a function argument.
170+
///
171+
/// This is called when the dependent value is returned by the function and the dependence base is in the caller.
172+
func redirectFunctionReturnForward(to arg: FunctionArgument, input: MarkDependenceInst,
173+
_ context: FunctionPassContext) -> MarkDependenceInst {
174+
// To handle more than one function argument, new mark_dependence instructions will be chained.
175+
let newMarkDep = Builder(after: input, location: input.location, context)
176+
.createMarkDependence(value: input, base: arg, kind: .Unresolved)
177+
let uses = input.uses.lazy.filter {
178+
let inst = $0.instruction
179+
return inst != newMarkDep
180+
}
181+
uses.replaceAll(with: newMarkDep, context)
182+
return newMarkDep
183+
}
184+
}
185+
186+
private extension MarkDependenceAddrInst {
187+
/// Rewrite the mark_dependence_addr base operand, setting it to a function argument.
188+
///
189+
/// This is called when the dependent value is returned by the function and the dependence base is in the caller.
190+
func redirectFunctionReturnAddress(to arg: FunctionArgument, _ context: FunctionPassContext)
191+
-> MarkDependenceAddrInst {
192+
return Builder(after: self, location: self.location, context)
193+
.createMarkDependenceAddr(value: self.address, base: arg, kind: .Unresolved)
194+
}
195+
}
196+
172197
/// Transitively extend nested scopes that enclose the dependence base.
173198
///
174199
/// If the parent function returns the dependent value, then this returns the function arguments that represent the
@@ -211,8 +236,8 @@ private func extendScopes(dependence: LifetimeDependence,
211236
var dependsOnArgs = SingleInlineArray<FunctionArgument>()
212237
for scopeExtension in scopeExtensions {
213238
var scopeExtension = scopeExtension
214-
guard var useRange = computeDependentUseRange(of: dependence.dependentValue, within: &scopeExtension,
215-
localReachabilityCache, context) else {
239+
guard var useRange = computeDependentUseRange(of: dependence, within: &scopeExtension, localReachabilityCache,
240+
context) else {
216241
continue
217242
}
218243

@@ -463,20 +488,19 @@ extension ScopeExtension {
463488
}
464489
}
465490

466-
/// Return an InstructionRange covering all the dependent uses of 'value'.
467-
private func computeDependentUseRange(of value: Value, within scopeExtension: inout ScopeExtension,
491+
/// Return an InstructionRange covering all the dependent uses of 'dependence'.
492+
private func computeDependentUseRange(of dependence: LifetimeDependence, within scopeExtension: inout ScopeExtension,
468493
_ localReachabilityCache: LocalVariableReachabilityCache,
469494
_ context: FunctionPassContext)
470495
-> InstructionRange? {
471-
496+
let function = dependence.function
472497
guard var ownershipRange = scopeExtension.computeRange(localReachabilityCache, context) else {
473498
return nil
474499
}
475500
defer {ownershipRange.deinitialize()}
476501

477502
// The innermost scope that must be extended must dominate all uses.
478503
var useRange = InstructionRange(begin: scopeExtension.innerScope.extendableBegin!.instruction, context)
479-
let function = value.parentFunction
480504
var walker = LifetimeDependentUseWalker(function, localReachabilityCache, context) {
481505
// Do not extend the useRange past the ownershipRange.
482506
let dependentInst = $0.instruction
@@ -487,17 +511,20 @@ private func computeDependentUseRange(of value: Value, within scopeExtension: in
487511
}
488512
defer {walker.deinitialize()}
489513

490-
_ = walker.walkDown(root: value)
514+
_ = walker.walkDown(dependence: dependence)
491515

492516
log("Scope fixup for dependent uses:\n\(useRange)")
493517

494518
scopeExtension.dependsOnCaller = walker.dependsOnCaller
495519

496520
// Lifetime dependenent uses may not be dominated by the access. The dependent value may be used by a phi or stored
497521
// into a memory location. The access may be conditional relative to such uses. If any use was not dominated, then
498-
// `useRange` will include the function entry.
522+
// `useRange` will include the function entry. There is not way to directly check
523+
// useRange.isValid. useRange.blockRange.isValid is not a strong enough check because it will always succeed when
524+
// useRange.begin == entryBlock even if a use if above useRange.begin.
499525
let firstInst = function.entryBlock.instructions.first!
500526
if firstInst != useRange.begin, useRange.contains(firstInst) {
527+
useRange.deinitialize()
501528
return nil
502529
}
503530
return useRange

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ private func isValidUseOfObject(_ use: Operand) -> Bool {
305305
is DeallocStackRefInst,
306306
is StrongRetainInst,
307307
is StrongReleaseInst,
308-
is FixLifetimeInst:
308+
is FixLifetimeInst,
309+
is MarkDependenceAddrInst:
309310
return true
310311

311312
case let mdi as MarkDependenceInst:

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,14 @@ protocol AddressUseVisitor {
6868
/// only if its type is escapable.
6969
mutating func yieldedAddressUse(of operand: Operand) -> WalkResult
7070

71-
/// A non-address owned `value` whose ownership depends on the in-memory
72-
/// value at `address`, such as `mark_dependence %value on %address`.
73-
mutating func dependentAddressUse(of operand: Operand, into value: Value)
74-
-> WalkResult
71+
/// A forwarded `value` whose ownership depends on the in-memory value at the address `operand`.
72+
/// `%value` may be an address type, but only uses of this address value are dependent.
73+
/// e.g. `%value = mark_dependence %_ on %operand`
74+
mutating func dependentAddressUse(of operand: Operand, dependentValue value: Value) -> WalkResult
75+
76+
/// A dependent `address` whose lifetime depends on the in-memory value at `address`.
77+
/// e.g. `mark_dependence_addr %address on %operand`
78+
mutating func dependentAddressUse(of operand: Operand, dependentAddress address: Value) -> WalkResult
7579

7680
/// A pointer escape may propagate the address beyond the current instruction.
7781
mutating func escapingAddressUse(of operand: Operand) -> WalkResult
@@ -91,34 +95,11 @@ extension AddressUseVisitor {
9195
case is EndAccessInst, is EndApplyInst, is AbortApplyInst, is EndBorrowInst:
9296
return scopeEndingAddressUse(of: operand)
9397

94-
case let markDep as MarkDependenceInst:
95-
if markDep.valueOperand == operand {
96-
return projectedAddressUse(of: operand, into: markDep)
97-
}
98-
assert(markDep.baseOperand == operand)
99-
// If another address depends on the current address,
100-
// handle it like a projection.
101-
if markDep.type.isAddress {
102-
return projectedAddressUse(of: operand, into: markDep)
103-
}
104-
switch markDep.dependenceKind {
105-
case .Unresolved:
106-
if LifetimeDependence(markDep, context) == nil {
107-
break
108-
}
109-
fallthrough
110-
case .NonEscaping:
111-
// Note: This is unreachable from InteriorUseVisitor because the base address of a `mark_dependence
112-
// [nonescaping]` must be a `begin_access`, and interior liveness does not check uses of the accessed address.
113-
return dependentAddressUse(of: operand, into: markDep)
114-
case .Escaping:
115-
break
116-
}
117-
// A potentially escaping value depends on this address.
118-
return escapingAddressUse(of: operand)
98+
case is MarkDependenceInstruction:
99+
return classifyMarkDependence(operand: operand)
119100

120101
case let pai as PartialApplyInst where !pai.mayEscape:
121-
return dependentAddressUse(of: operand, into: pai)
102+
return dependentAddressUse(of: operand, dependentValue: pai)
122103

123104
case let pai as PartialApplyInst where pai.mayEscape:
124105
return escapingAddressUse(of: operand)
@@ -198,6 +179,57 @@ extension AddressUseVisitor {
198179
return unknownAddressUse(of: operand)
199180
}
200181
}
182+
183+
private mutating func classifyMarkDependence(operand: Operand) -> WalkResult {
184+
switch operand.instruction {
185+
case let markDep as MarkDependenceInst:
186+
if markDep.valueOperand == operand {
187+
return projectedAddressUse(of: operand, into: markDep)
188+
}
189+
assert(markDep.baseOperand == operand)
190+
// If another address depends on the current address,
191+
// handle it like a projection.
192+
if markDep.type.isAddress {
193+
return projectedAddressUse(of: operand, into: markDep)
194+
}
195+
switch markDep.dependenceKind {
196+
case .Unresolved:
197+
if LifetimeDependence(markDep, context) == nil {
198+
break
199+
}
200+
fallthrough
201+
case .NonEscaping:
202+
// Note: This is unreachable from InteriorUseVisitor because the base address of a `mark_dependence
203+
// [nonescaping]` must be a `begin_access`, and interior liveness does not check uses of the accessed address.
204+
return dependentAddressUse(of: operand, dependentValue: markDep)
205+
case .Escaping:
206+
break
207+
}
208+
// A potentially escaping value depends on this address.
209+
return escapingAddressUse(of: operand)
210+
211+
case let markDep as MarkDependenceAddrInst:
212+
if markDep.addressOperand == operand {
213+
return leafAddressUse(of: operand)
214+
}
215+
switch markDep.dependenceKind {
216+
case .Unresolved:
217+
if LifetimeDependence(markDep, context) == nil {
218+
break
219+
}
220+
fallthrough
221+
case .NonEscaping:
222+
return dependentAddressUse(of: operand, dependentAddress: markDep.address)
223+
case .Escaping:
224+
break
225+
}
226+
// A potentially escaping value depends on this address.
227+
return escapingAddressUse(of: operand)
228+
229+
default:
230+
fatalError("Unexpected MarkDependenceInstruction")
231+
}
232+
}
201233
}
202234

203235
extension AccessBase {
@@ -385,8 +417,11 @@ extension AddressInitializationWalker {
385417
return .continueWalk
386418
}
387419

388-
mutating func dependentAddressUse(of operand: Operand, into value: Value)
389-
-> WalkResult {
420+
mutating func dependentAddressUse(of operand: Operand, dependentValue value: Value) -> WalkResult {
421+
return .continueWalk
422+
}
423+
424+
mutating func dependentAddressUse(of operand: Operand, dependentAddress address: Value) -> WalkResult {
390425
return .continueWalk
391426
}
392427

0 commit comments

Comments
 (0)