mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
LifetimeDependenceDefUseWalker: handle addressable dependencies
This commit is contained in:
@@ -56,12 +56,12 @@ protocol AddressUseVisitor {
|
|||||||
-> WalkResult
|
-> WalkResult
|
||||||
|
|
||||||
/// A loaded address use propagates the value at the address.
|
/// A loaded address use propagates the value at the address.
|
||||||
mutating func loadedAddressUse(of operand: Operand, into value: Value)
|
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value)
|
||||||
-> WalkResult
|
-> WalkResult
|
||||||
|
|
||||||
/// A loaded address use propagates the value at the address to the
|
/// A loaded address use propagates the value at the address to the
|
||||||
/// destination address operand.
|
/// destination address operand.
|
||||||
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
|
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand)
|
||||||
-> WalkResult
|
-> WalkResult
|
||||||
|
|
||||||
/// Yielding an address may modify the value at the yield, but not past the yield. The yielded value may escape, but
|
/// Yielding an address may modify the value at the yield, but not past the yield. The yielded value may escape, but
|
||||||
@@ -137,14 +137,14 @@ extension AddressUseVisitor {
|
|||||||
case is LoadInst, is LoadUnownedInst, is LoadWeakInst, is ValueMetatypeInst, is ExistentialMetatypeInst,
|
case is LoadInst, is LoadUnownedInst, is LoadWeakInst, is ValueMetatypeInst, is ExistentialMetatypeInst,
|
||||||
is PackElementGetInst:
|
is PackElementGetInst:
|
||||||
let svi = operand.instruction as! SingleValueInstruction
|
let svi = operand.instruction as! SingleValueInstruction
|
||||||
return loadedAddressUse(of: operand, into: svi)
|
return loadedAddressUse(of: operand, intoValue: svi)
|
||||||
|
|
||||||
case is YieldInst:
|
case is YieldInst:
|
||||||
return yieldedAddressUse(of: operand)
|
return yieldedAddressUse(of: operand)
|
||||||
|
|
||||||
case let sdai as SourceDestAddrInstruction
|
case let sdai as SourceDestAddrInstruction
|
||||||
where sdai.sourceOperand == operand:
|
where sdai.sourceOperand == operand:
|
||||||
return loadedAddressUse(of: operand, into: sdai.destinationOperand)
|
return loadedAddressUse(of: operand, intoAddress: sdai.destinationOperand)
|
||||||
|
|
||||||
case let sdai as SourceDestAddrInstruction
|
case let sdai as SourceDestAddrInstruction
|
||||||
where sdai.destinationOperand == operand:
|
where sdai.destinationOperand == operand:
|
||||||
@@ -401,12 +401,12 @@ extension AddressInitializationWalker {
|
|||||||
return convention.isIndirectIn ? .continueWalk : .abortWalk
|
return convention.isIndirectIn ? .continueWalk : .abortWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into value: Value)
|
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
|
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,7 +584,7 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
|
|
||||||
// Override ForwardingDefUseWalker.
|
// Override ForwardingDefUseWalker.
|
||||||
mutating func walkDown(operand: Operand) -> WalkResult {
|
mutating func walkDown(operand: Operand) -> WalkResult {
|
||||||
// Initially delegate all usess to OwnershipUseVisitor.
|
// Initially delegate all uses to OwnershipUseVisitor.
|
||||||
// walkDownDefault will be called for uses that forward ownership.
|
// walkDownDefault will be called for uses that forward ownership.
|
||||||
return classify(operand: operand)
|
return classify(operand: operand)
|
||||||
}
|
}
|
||||||
@@ -631,7 +631,7 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
// like [nonescaping] even though they are not considered OSSA
|
// like [nonescaping] even though they are not considered OSSA
|
||||||
// borrows until after resolution.
|
// borrows until after resolution.
|
||||||
assert(operand == mdi.baseOperand)
|
assert(operand == mdi.baseOperand)
|
||||||
return dependentUse(of: operand, into: mdi)
|
return dependentUse(of: operand, dependentValue: mdi)
|
||||||
|
|
||||||
case is ExistentialMetatypeInst, is FixLifetimeInst, is WitnessMethodInst,
|
case is ExistentialMetatypeInst, is FixLifetimeInst, is WitnessMethodInst,
|
||||||
is DynamicMethodBranchInst, is ValueMetatypeInst,
|
is DynamicMethodBranchInst, is ValueMetatypeInst,
|
||||||
@@ -676,11 +676,24 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
return escapingDependence(on: operand)
|
return escapingDependence(on: operand)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func dependentUse(of operand: Operand, into value: Value)
|
// Handle address or non-address operands.
|
||||||
|
mutating func dependentUse(of operand: Operand, dependentValue value: Value)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
return walkDownUses(of: value, using: operand)
|
return walkDownUses(of: value, using: operand)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle address or non-address operands.
|
||||||
|
mutating func dependentUse(of operand: Operand, dependentAddress address: Value)
|
||||||
|
-> WalkResult {
|
||||||
|
// Consider this a leaf use in addition to the dependent address uses, which might all occur earlier.
|
||||||
|
if leafUse(of: operand) == .abortWalk {
|
||||||
|
return .abortWalk
|
||||||
|
}
|
||||||
|
// The lifetime dependence is effectively "copied into" the dependent address. Find all uses of the dependent
|
||||||
|
// address as if this were a stored use.
|
||||||
|
return visitStoredUses(of: operand, into: address)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func borrowingUse(of operand: Operand,
|
mutating func borrowingUse(of operand: Operand,
|
||||||
by borrowInst: BorrowingInstruction)
|
by borrowInst: BorrowingInstruction)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
@@ -736,7 +749,7 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
return visitAppliedUse(of: operand, by: apply)
|
return visitAppliedUse(of: operand, by: apply)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
|
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value) -> WalkResult {
|
||||||
// Record the load itself, in case the loaded value is Escapable.
|
// Record the load itself, in case the loaded value is Escapable.
|
||||||
if leafUse(of: operand) == .abortWalk {
|
if leafUse(of: operand) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
@@ -745,7 +758,7 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy_addr
|
// copy_addr
|
||||||
mutating func loadedAddressUse(of operand: Operand, into address: Operand) -> WalkResult {
|
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand) -> WalkResult {
|
||||||
if leafUse(of: operand) == .abortWalk {
|
if leafUse(of: operand) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
}
|
}
|
||||||
@@ -761,18 +774,12 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutating func dependentAddressUse(of operand: Operand, dependentValue value: Value) -> WalkResult {
|
mutating func dependentAddressUse(of operand: Operand, dependentValue value: Value) -> WalkResult {
|
||||||
walkDownUses(of: value, using: operand)
|
dependentUse(of: operand, dependentValue: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark_dependence_addr
|
// mark_dependence_addr
|
||||||
mutating func dependentAddressUse(of operand: Operand, dependentAddress address: Value) -> WalkResult {
|
mutating func dependentAddressUse(of operand: Operand, dependentAddress address: Value) -> WalkResult {
|
||||||
// Consider this a leaf use in addition to the dependent address uses, which might all occur earlier.
|
dependentUse(of: operand, dependentAddress: address)
|
||||||
if leafUse(of: operand) == .abortWalk {
|
|
||||||
return .abortWalk
|
|
||||||
}
|
|
||||||
// The lifetime dependence is effectively "copied into" the dependent address. Find all uses of the dependent
|
|
||||||
// address as if this were a stored use.
|
|
||||||
return visitStoredUses(of: operand, into: address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func escapingAddressUse(of operand: Operand) -> WalkResult {
|
mutating func escapingAddressUse(of operand: Operand) -> WalkResult {
|
||||||
@@ -898,19 +905,30 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
case .load:
|
case .load:
|
||||||
switch localAccess.instruction! {
|
switch localAccess.instruction! {
|
||||||
case let load as LoadInst:
|
case let load as LoadInst:
|
||||||
return loadedAddressUse(of: localAccess.operand!, into: load)
|
return loadedAddressUse(of: localAccess.operand!, intoValue: load)
|
||||||
case let load as LoadBorrowInst:
|
case let load as LoadBorrowInst:
|
||||||
return loadedAddressUse(of: localAccess.operand!, into: load)
|
return loadedAddressUse(of: localAccess.operand!, intoValue: load)
|
||||||
case let copyAddr as SourceDestAddrInstruction:
|
case let copyAddr as SourceDestAddrInstruction:
|
||||||
return loadedAddressUse(of: localAccess.operand!, into: copyAddr.destinationOperand)
|
return loadedAddressUse(of: localAccess.operand!, intoAddress: copyAddr.destinationOperand)
|
||||||
default:
|
default:
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
}
|
}
|
||||||
case .dependence:
|
case .dependenceSource:
|
||||||
// An address-forwarding mark_dependence is simply a marker that indicates the start of an in-memory
|
switch localAccess.instruction! {
|
||||||
// dependent value. Typically, it has no uses. If it does have uses, then they are visited earlier by
|
case let md as MarkDependenceInst:
|
||||||
// LocalVariableAccessWalker to record any other local accesses.
|
if md.type.isAddress {
|
||||||
return .continueWalk
|
return loadedAddressUse(of: localAccess.operand!, intoAddress: md.valueOperand)
|
||||||
|
}
|
||||||
|
return loadedAddressUse(of: localAccess.operand!, intoValue: md)
|
||||||
|
case let md as MarkDependenceAddrInst:
|
||||||
|
return loadedAddressUse(of: localAccess.operand!, intoAddress: md.addressOperand)
|
||||||
|
default:
|
||||||
|
return .abortWalk
|
||||||
|
}
|
||||||
|
case .dependenceDest:
|
||||||
|
// Simply a marker that indicates the start of an in-memory dependent value. If this was a mark_dependence, uses
|
||||||
|
// of its forwarded address has were visited by LocalVariableAccessWalker and recorded as separate local accesses.
|
||||||
|
return .continueWalk
|
||||||
case .store:
|
case .store:
|
||||||
let si = localAccess.operand!.instruction as! StoringInstruction
|
let si = localAccess.operand!.instruction as! StoringInstruction
|
||||||
assert(si.sourceOperand == initialValue, "the only reachable store should be the current assignment")
|
assert(si.sourceOperand == initialValue, "the only reachable store should be the current assignment")
|
||||||
@@ -948,13 +966,13 @@ extension LifetimeDependenceDefUseWalker {
|
|||||||
// because a mark_dependence [nonescaping] represents the
|
// because a mark_dependence [nonescaping] represents the
|
||||||
// dependence.
|
// dependence.
|
||||||
if let result = apply.singleDirectResult, !result.isEscapable {
|
if let result = apply.singleDirectResult, !result.isEscapable {
|
||||||
if dependentUse(of: operand, into: result) == .abortWalk {
|
if dependentUse(of: operand, dependentValue: result) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for resultAddr in apply.indirectResultOperands
|
for resultAddr in apply.indirectResultOperands
|
||||||
where !resultAddr.value.isEscapable {
|
where !resultAddr.value.isEscapable {
|
||||||
if visitStoredUses(of: operand, into: resultAddr.value) == .abortWalk {
|
if dependentUse(of: operand, dependentAddress: resultAddr.value) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,29 @@ private func log(_ message: @autoclosure () -> String) {
|
|||||||
// Local variables are accessed in one of these ways.
|
// Local variables are accessed in one of these ways.
|
||||||
//
|
//
|
||||||
// Note: @in is only immutable up to when it is destroyed, so still requires a local live range.
|
// Note: @in is only immutable up to when it is destroyed, so still requires a local live range.
|
||||||
|
//
|
||||||
|
// A .dependenceSource access creates a new dependent value that keeps this local alive.
|
||||||
|
//
|
||||||
|
// %local = alloc_stack // this local
|
||||||
|
// %md = mark_dependence %val on %local
|
||||||
|
// mark_dependence_addr %adr on %local
|
||||||
|
//
|
||||||
|
// The effect of .dependenceSource on reachability is like a load of this local. The dependent value depends on any
|
||||||
|
// value in this local that reaches this point.
|
||||||
|
//
|
||||||
|
// A .dependenceDest access is the point where another value becomes dependent on this local:
|
||||||
|
//
|
||||||
|
// %local = alloc_stack // this local
|
||||||
|
// %md = mark_dependence %local on %val
|
||||||
|
// mark_dependence_addr %local on %val
|
||||||
|
//
|
||||||
|
// The effect of .dependenceDest on reachability is like a store of this local. All uses of this local reachable from
|
||||||
|
// this point are uses of the dependence base.
|
||||||
|
//
|
||||||
|
// Note that the same mark_dependence_addr often involves two locals:
|
||||||
|
//
|
||||||
|
// mark_dependence_addr %localDest on %localSource
|
||||||
|
//
|
||||||
struct LocalVariableAccess: CustomStringConvertible {
|
struct LocalVariableAccess: CustomStringConvertible {
|
||||||
enum Kind {
|
enum Kind {
|
||||||
case incomingArgument // @in, @inout, @inout_aliasable
|
case incomingArgument // @in, @inout, @inout_aliasable
|
||||||
@@ -43,7 +66,8 @@ struct LocalVariableAccess: CustomStringConvertible {
|
|||||||
case inoutYield // indirect yield from this accessor
|
case inoutYield // indirect yield from this accessor
|
||||||
case beginAccess // Reading or reassigning a 'var'
|
case beginAccess // Reading or reassigning a 'var'
|
||||||
case load // Reading a 'let'. Returning 'var' from an initializer.
|
case load // Reading a 'let'. Returning 'var' from an initializer.
|
||||||
case dependence // A mark_dependence after an apply with an indirect result. No effect.
|
case dependenceSource // A value/address depends on this local here (like a load)
|
||||||
|
case dependenceDest // This local depends on another value/address here (like a store)
|
||||||
case store // 'var' initialization and destruction
|
case store // 'var' initialization and destruction
|
||||||
case apply // indirect arguments
|
case apply // indirect arguments
|
||||||
case escape // alloc_box captures
|
case escape // alloc_box captures
|
||||||
@@ -77,7 +101,7 @@ struct LocalVariableAccess: CustomStringConvertible {
|
|||||||
case .`init`, .modify:
|
case .`init`, .modify:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case .load, .dependence:
|
case .load, .dependenceSource, .dependenceDest:
|
||||||
return false
|
return false
|
||||||
case .incomingArgument, .outgoingArgument, .store, .inoutYield:
|
case .incomingArgument, .outgoingArgument, .store, .inoutYield:
|
||||||
return true
|
return true
|
||||||
@@ -116,8 +140,10 @@ struct LocalVariableAccess: CustomStringConvertible {
|
|||||||
str += "beginAccess"
|
str += "beginAccess"
|
||||||
case .load:
|
case .load:
|
||||||
str += "load"
|
str += "load"
|
||||||
case .dependence:
|
case .dependenceSource:
|
||||||
str += "dependence"
|
str += "dependenceSource"
|
||||||
|
case .dependenceDest:
|
||||||
|
str += "dependenceDest"
|
||||||
case .store:
|
case .store:
|
||||||
str += "store"
|
str += "store"
|
||||||
case .apply:
|
case .apply:
|
||||||
@@ -152,7 +178,7 @@ class LocalVariableAccessInfo: CustomStringConvertible {
|
|||||||
case .`init`, .modify:
|
case .`init`, .modify:
|
||||||
break // lazily compute full assignment
|
break // lazily compute full assignment
|
||||||
}
|
}
|
||||||
case .load, .dependence:
|
case .load, .dependenceSource, .dependenceDest:
|
||||||
self._isFullyAssigned = false
|
self._isFullyAssigned = false
|
||||||
case .store:
|
case .store:
|
||||||
if let store = localAccess.instruction as? StoringInstruction {
|
if let store = localAccess.instruction as? StoringInstruction {
|
||||||
@@ -365,7 +391,7 @@ extension LocalVariableAccessWalker : ForwardingDefUseWalker {
|
|||||||
break
|
break
|
||||||
case let markDep as MarkDependenceInst:
|
case let markDep as MarkDependenceInst:
|
||||||
assert(markDep.baseOperand == operand)
|
assert(markDep.baseOperand == operand)
|
||||||
visit(LocalVariableAccess(.dependence, operand))
|
visit(LocalVariableAccess(.dependenceSource, operand))
|
||||||
default:
|
default:
|
||||||
visit(LocalVariableAccess(.escape, operand))
|
visit(LocalVariableAccess(.escape, operand))
|
||||||
}
|
}
|
||||||
@@ -381,10 +407,6 @@ extension LocalVariableAccessWalker : ForwardingDefUseWalker {
|
|||||||
extension LocalVariableAccessWalker: AddressUseVisitor {
|
extension LocalVariableAccessWalker: AddressUseVisitor {
|
||||||
private mutating func walkDownAddressUses(address: Value) -> WalkResult {
|
private mutating func walkDownAddressUses(address: Value) -> WalkResult {
|
||||||
for operand in address.uses.ignoreTypeDependence {
|
for operand in address.uses.ignoreTypeDependence {
|
||||||
if let md = operand.instruction as? MarkDependenceInst, operand == md.valueOperand {
|
|
||||||
// Record the forwarding mark_dependence as a fake access before continuing to walk down.
|
|
||||||
visit(LocalVariableAccess(.dependence, operand))
|
|
||||||
}
|
|
||||||
if classifyAddress(operand: operand) == .abortWalk {
|
if classifyAddress(operand: operand) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
}
|
}
|
||||||
@@ -399,6 +421,13 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
|
|||||||
// temporaries do not have access scopes, so we need to walk down any projection that may be used to initialize the
|
// temporaries do not have access scopes, so we need to walk down any projection that may be used to initialize the
|
||||||
// temporary.
|
// temporary.
|
||||||
mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
|
mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
|
||||||
|
// Intercept mark_dependence destination to record an access point which can be used like a store when finding all
|
||||||
|
// uses that affect the base after the point that the dependence was marked.
|
||||||
|
if let md = value as? MarkDependenceInst {
|
||||||
|
assert(operand == md.valueOperand)
|
||||||
|
visit(LocalVariableAccess(.dependenceDest, operand))
|
||||||
|
// walk down the forwarded address as usual...
|
||||||
|
}
|
||||||
return walkDownAddressUses(address: value)
|
return walkDownAddressUses(address: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +461,9 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
|
|||||||
is PackElementSetInst:
|
is PackElementSetInst:
|
||||||
// Handle instructions that initialize both temporaries and local variables.
|
// Handle instructions that initialize both temporaries and local variables.
|
||||||
visit(LocalVariableAccess(.store, operand))
|
visit(LocalVariableAccess(.store, operand))
|
||||||
|
case let md as MarkDependenceAddrInst:
|
||||||
|
assert(operand == md.addressOperand)
|
||||||
|
visit(LocalVariableAccess(.dependenceDest, operand))
|
||||||
case is DeallocStackInst:
|
case is DeallocStackInst:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
@@ -473,12 +505,12 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
|
|||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
|
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value) -> WalkResult {
|
||||||
visit(LocalVariableAccess(.load, operand))
|
visit(LocalVariableAccess(.load, operand))
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into address: Operand) -> WalkResult {
|
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand) -> WalkResult {
|
||||||
visit(LocalVariableAccess(.load, operand))
|
visit(LocalVariableAccess(.load, operand))
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,8 @@ struct InteriorLivenessResult: CustomDebugStringConvertible {
|
|||||||
/// - forwardingUse(of:isInnerlifetime:)
|
/// - forwardingUse(of:isInnerlifetime:)
|
||||||
/// - interiorPointerUse(of:into:)
|
/// - interiorPointerUse(of:into:)
|
||||||
/// - pointerEscapingUse(of:)
|
/// - pointerEscapingUse(of:)
|
||||||
/// - dependentUse(of:into:)
|
/// - dependentUse(of:dependentValue:)
|
||||||
|
/// - dependentUse(of:dependentAddress:)
|
||||||
/// - borrowingUse(of:by:)
|
/// - borrowingUse(of:by:)
|
||||||
///
|
///
|
||||||
/// This only visits the first level of uses. The implementation may transitively visit forwarded, borrowed, dependent,
|
/// This only visits the first level of uses. The implementation may transitively visit forwarded, borrowed, dependent,
|
||||||
@@ -269,7 +270,13 @@ protocol OwnershipUseVisitor {
|
|||||||
/// there are no explicit scope-ending operations. Instead
|
/// there are no explicit scope-ending operations. Instead
|
||||||
/// BorrowingInstruction.scopeEndingOperands will return the final
|
/// BorrowingInstruction.scopeEndingOperands will return the final
|
||||||
/// consumes in the dependent value's forwarding chain.
|
/// consumes in the dependent value's forwarding chain.
|
||||||
mutating func dependentUse(of operand: Operand, into value: Value) -> WalkResult
|
mutating func dependentUse(of operand: Operand, dependentValue value: Value) -> WalkResult
|
||||||
|
|
||||||
|
/// A use that creates an implicit borrow scope over all reachable uses of a value stored in
|
||||||
|
/// `dependentAddress`. This could conservatively be handled has `pointerEscapingUse`, but note that accessing the
|
||||||
|
/// `dependentAddress` only keeps the original owner alive, it cannot modify the original (modifying a dependent
|
||||||
|
/// address is still just a "read" of the dependence source.
|
||||||
|
mutating func dependentUse(of operand: Operand, dependentAddress address: Value) -> WalkResult
|
||||||
|
|
||||||
/// A use that is scoped to an inner borrow scope.
|
/// A use that is scoped to an inner borrow scope.
|
||||||
mutating func borrowingUse(of operand: Operand, by borrowInst: BorrowingInstruction) -> WalkResult
|
mutating func borrowingUse(of operand: Operand, by borrowInst: BorrowingInstruction) -> WalkResult
|
||||||
@@ -384,6 +391,9 @@ extension OwnershipUseVisitor {
|
|||||||
return forwardingUse(of: operand, isInnerLifetime: false)
|
return forwardingUse(of: operand, isInnerLifetime: false)
|
||||||
|
|
||||||
case .pointerEscape:
|
case .pointerEscape:
|
||||||
|
if let mdai = operand.instruction as? MarkDependenceAddrInst, operand == mdai.baseOperand {
|
||||||
|
return dependentUse(of: operand, dependentAddress: mdai.address)
|
||||||
|
}
|
||||||
return pointerEscapingUse(of: operand)
|
return pointerEscapingUse(of: operand)
|
||||||
|
|
||||||
case .instantaneousUse, .forwardingUnowned, .unownedInstantaneousUse, .bitwiseEscape:
|
case .instantaneousUse, .forwardingUnowned, .unownedInstantaneousUse, .bitwiseEscape:
|
||||||
@@ -413,6 +423,9 @@ extension OwnershipUseVisitor {
|
|||||||
if operand.instruction is ProjectBoxInst {
|
if operand.instruction is ProjectBoxInst {
|
||||||
return visitInteriorPointerUse(of: operand)
|
return visitInteriorPointerUse(of: operand)
|
||||||
}
|
}
|
||||||
|
if let mdai = operand.instruction as? MarkDependenceAddrInst, operand == mdai.baseOperand {
|
||||||
|
return dependentUse(of: operand, dependentAddress: mdai.address)
|
||||||
|
}
|
||||||
return pointerEscapingUse(of: operand)
|
return pointerEscapingUse(of: operand)
|
||||||
|
|
||||||
case .instantaneousUse, .forwardingUnowned, .unownedInstantaneousUse, .bitwiseEscape, .endBorrow, .reborrow:
|
case .instantaneousUse, .forwardingUnowned, .unownedInstantaneousUse, .bitwiseEscape, .endBorrow, .reborrow:
|
||||||
@@ -436,12 +449,14 @@ extension OwnershipUseVisitor {
|
|||||||
switch operand.instruction {
|
switch operand.instruction {
|
||||||
case let pai as PartialApplyInst:
|
case let pai as PartialApplyInst:
|
||||||
assert(!pai.mayEscape)
|
assert(!pai.mayEscape)
|
||||||
return dependentUse(of: operand, into: pai)
|
return dependentUse(of: operand, dependentValue: pai)
|
||||||
case let mdi as MarkDependenceInst:
|
case let mdi as MarkDependenceInst:
|
||||||
|
// .borrow operand ownership only applies to the base operand of a non-escaping markdep that forwards a
|
||||||
|
// non-address value.
|
||||||
assert(operand == mdi.baseOperand && mdi.isNonEscaping)
|
assert(operand == mdi.baseOperand && mdi.isNonEscaping)
|
||||||
return dependentUse(of: operand, into: mdi)
|
return dependentUse(of: operand, dependentValue: mdi)
|
||||||
case let bfi as BorrowedFromInst where !bfi.borrowedPhi.isReborrow:
|
case let bfi as BorrowedFromInst where !bfi.borrowedPhi.isReborrow:
|
||||||
return dependentUse(of: operand, into: bfi)
|
return dependentUse(of: operand, dependentValue: bfi)
|
||||||
default:
|
default:
|
||||||
return borrowingUse(of: operand,
|
return borrowingUse(of: operand,
|
||||||
by: BorrowingInstruction(operand.instruction)!)
|
by: BorrowingInstruction(operand.instruction)!)
|
||||||
@@ -586,7 +601,7 @@ extension InteriorUseWalker: OwnershipUseVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle partial_apply [on_stack] and mark_dependence [nonescaping].
|
// Handle partial_apply [on_stack] and mark_dependence [nonescaping].
|
||||||
mutating func dependentUse(of operand: Operand, into value: Value) -> WalkResult {
|
mutating func dependentUse(of operand: Operand, dependentValue value: Value) -> WalkResult {
|
||||||
// OSSA lifetime ignores trivial types.
|
// OSSA lifetime ignores trivial types.
|
||||||
if value.type.isTrivial(in: function) {
|
if value.type.isTrivial(in: function) {
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
@@ -602,6 +617,12 @@ extension InteriorUseWalker: OwnershipUseVisitor {
|
|||||||
return walkDownUses(of: value)
|
return walkDownUses(of: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func dependentUse(of operand: Operand, dependentAddress address: Value) -> WalkResult {
|
||||||
|
// An mutable local variable depends a value that depends on the original interior pointer. This would require data
|
||||||
|
// flow to find local uses. InteriorUseWalker only walks the SSA uses.
|
||||||
|
pointerEscapingUse(of: operand)
|
||||||
|
}
|
||||||
|
|
||||||
mutating func pointerEscapingUse(of operand: Operand) -> WalkResult {
|
mutating func pointerEscapingUse(of operand: Operand) -> WalkResult {
|
||||||
if useVisitor(operand) == .abortWalk {
|
if useVisitor(operand) == .abortWalk {
|
||||||
return .abortWalk
|
return .abortWalk
|
||||||
@@ -699,12 +720,12 @@ extension InteriorUseWalker: AddressUseVisitor {
|
|||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into value: Value)
|
mutating func loadedAddressUse(of operand: Operand, intoValue value: Value)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func loadedAddressUse(of operand: Operand, into address: Operand)
|
mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand)
|
||||||
-> WalkResult {
|
-> WalkResult {
|
||||||
return .continueWalk
|
return .continueWalk
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user