1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
//===--- LifetimeDependenceInsertion.swift - insert lifetime dependence ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// Insert mark_dependence [nonescaping] markers on the owned returned
/// or yielded value of a call whose return type is non-escaping.
///
/// Pass dependencies: This must run as a SILGen cleanup pass before
/// any lifetime canonicalization or optimization can be performed.
///
//===----------------------------------------------------------------------===//
import SIL
private let verbose = false
private func log(prefix: Bool = true, _ message: @autoclosure () -> String) {
if verbose {
print((prefix ? "### " : "") + message())
}
}
let lifetimeDependenceInsertionPass = FunctionPass(
name: "lifetime-dependence-insertion")
{ (function: Function, context: FunctionPassContext) in
if !context.options.hasFeature(.NonescapableTypes) {
return
}
log(prefix: false, "\n--- Inserting lifetime dependence markers in \(function.name)")
for instruction in function.instructions {
if let dependentApply = LifetimeDependentApply(instruction) {
insertDependencies(for: dependentApply, context)
}
}
}
/// An apply that produces a non-escapable value, linking it to a parent value.
private struct LifetimeDependentApply {
let applySite: FullApplySite
init?(_ instruction: Instruction) {
guard let apply = instruction as? FullApplySite else {
return nil
}
if !apply.hasResultDependence {
return nil
}
self.applySite = apply
}
init?(withResult value: Value) {
switch value {
case let apply as ApplyInst:
if let dependentApply = LifetimeDependentApply(apply) {
self = dependentApply
}
case let arg as Argument:
guard let termResult = TerminatorResult(arg) else { return nil }
switch termResult.terminator {
case let ta as TryApplyInst:
if termResult.successor == ta.errorBlock {
if let dependentApply = LifetimeDependentApply(ta) {
self = dependentApply
}
}
default:
break
}
default:
break
}
return nil
}
}
extension LifetimeDependentApply {
/// A lifetime argument copies, borrows, or mutatably borrows the
/// lifetime of the argument value.
struct LifetimeArgument {
let convention: LifetimeDependenceConvention
let value: Value
}
func getLifetimeArguments() -> SingleInlineArray<LifetimeArgument> {
var args = SingleInlineArray<LifetimeArgument>()
for operand in applySite.parameterOperands {
guard let dep = applySite.resultDependence(on: operand) else {
continue
}
args.push(LifetimeArgument(convention: dep, value: operand.value))
}
return args
}
}
/// If the result of this apply depends on the scope of one or more
/// arguments, then insert a mark_dependence [unresolved] from the
/// result on each argument so that the result is recognized as a
/// dependent value within each scope.
private func insertDependencies(for apply: LifetimeDependentApply,
_ context: FunctionPassContext ) {
let bases = findDependenceBases(of: apply, context)
for dependentValue in apply.applySite.resultOrYields {
let builder = Builder(before: dependentValue.nextInstruction, context)
insertMarkDependencies(value: dependentValue, initializer: nil,
bases: bases, builder: builder, context)
}
let builder = Builder(after: apply.applySite, context)
for resultOper in apply.applySite.indirectResultOperands {
let accessBase = resultOper.value.accessBase
guard let (initialAddress, initializingStore) =
accessBase.findSingleInitializer(context) else {
continue
}
// TODO: This is currently too strict for a diagnostic pass. We
// should handle/cleanup projections and casts that occur before
// the initializingStore. Or check in the SIL verifier that all
// stores without an access scope follow this form. Then convert
// this bail-out to an assert.
guard initialAddress.usesOccurOnOrAfter(instruction: initializingStore,
context) else {
continue
}
assert(initializingStore == resultOper.instruction,
"an indirect result is a store")
insertMarkDependencies(value: initialAddress,
initializer: initializingStore, bases: bases,
builder: builder, context)
}
}
private func findDependenceBases(of apply: LifetimeDependentApply,
_ context: FunctionPassContext)
-> [Value] {
log("Creating dependencies for \(apply.applySite)")
var bases: [Value] = []
for lifetimeArg in apply.getLifetimeArguments() {
switch lifetimeArg.convention {
case .inherit:
continue
case .scope:
// Create a new dependence on the apply's access to the argument.
for varIntoducer in gatherVariableIntroducers(for: lifetimeArg.value,
context) {
if let scope =
LifetimeDependence.Scope(base: varIntoducer, context) {
log("Scoped lifetime from \(lifetimeArg.value)")
log(" scope: \(scope)")
bases.append(scope.parentValue)
}
}
}
}
return bases
}
private func insertMarkDependencies(value: Value, initializer: Instruction?,
bases: [Value], builder: Builder,
_ context: FunctionPassContext) {
var currentValue = value
for base in bases {
let markDep = builder.createMarkDependence(
value: currentValue, base: base, kind: .Unresolved)
let uses = currentValue.uses.lazy.filter {
let inst = $0.instruction
return inst != markDep && inst != initializer && !(inst is Deallocation)
}
uses.replaceAll(with: markDep, context)
currentValue = markDep
}
}
|