Skip to content

Commit 97b249b

Browse files
authored
Merge pull request #80263 from atrick/markdep-addr
SIL: add mark_dependence_addr
2 parents a4aaa10 + 69b6b1d commit 97b249b

Some content is hidden

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

57 files changed

+952
-320
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

+1-1
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

+2-2
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

+10-15
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

+51-24
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

+2-1
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/TestPasses/MemBehaviorDumper.swift

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private extension Instruction {
7575
is BuiltinInst,
7676
is StoreBorrowInst,
7777
is MarkDependenceInst,
78+
is MarkDependenceAddrInst,
7879
is DebugValueInst:
7980
return true
8081
default:

0 commit comments

Comments
 (0)