Skip to content

Commit eccf94d

Browse files
authored
Merge pull request #68885 from atrick/bridge-forwarding
SwiftCompilerSources: added OwnershipUtils for ForwardingInstruction.
2 parents 06984b4 + e27b308 commit eccf94d

File tree

11 files changed

+736
-31
lines changed

11 files changed

+736
-31
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
EscapeUtils.swift
1111
OptUtils.swift
12+
ForwardingUtils.swift
1213
WalkUtils.swift
1314
AccessUtils.swift
1415
SSAUpdater.swift
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
//===--- OwnershipUtils.swift - Utilities for ownership -------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
// Visit the introducers of a forwarded lifetime (Value -> LifetimeIntroducer).
16+
//
17+
// A lifetime introducer produces an initial OSSA lifetime which may be extended by forwarding instructions. The introducer is never itself the result of a ForwardingInstruction. Example:
18+
//
19+
// # lifetime introducer
20+
// %1 = apply -+ -+
21+
// ... | OSSA lifetime |
22+
// # forwarding instruction | |
23+
// %2 = struct $S (%1) -+ -+ | forward-extended lifetime
24+
// | OSSA lifetime |
25+
// # non-forwarding consumer | |
26+
// destroy_value %2 -+ -+
27+
//
28+
// The lifetime of a single owned value ends when it is forwarded, but certain lifetime properties are relevant for the entire forward-extended lifetime. For example, if an owned lifetime has a pointer-escaping use, then all values in the forward-extended lifetime are also considered pointer-escaping. Certain propeties, like lexical lifetimes, only exist on the forward introducer and apply to all forwarded values.
29+
//
30+
// Note: Although move_value conceptually forwards an owned value, it also summarizes lifetime attributes; therefore, it is not formally a ForwardingInstruction.
31+
//
32+
// TODO: when phi lifetime flags are implemented, phis will introduce a lifetime in the same way as move_value.
33+
//
34+
// The lifetime introducer of a guaranteed value is the borrow introducer:
35+
//
36+
// # lifetime introducer / borrow introducer
37+
// %1 = begin_borrow -+
38+
// ... | OSSA lifetime == forwarded lifetime
39+
// # forwarding instruction |
40+
// %2 = struct $S (%1) | - forwarded uses are within the OSSA lifetime
41+
// |
42+
// end_borrow %1 -+
43+
//
44+
// TODO: When a begin_borrow has no lifetime flags, it can be ignored as a lifetime introducer. In that case, an owned value may introduce guaranteed OSSA lifetimes.
45+
//
46+
// This walker is used to query basic lifetime attributes on values, such as "escaping" or "lexical". It must be precise for correctness and is performance critical.
47+
protocol ForwardingUseDefWalker {
48+
mutating func introducer(_ value: Value) -> WalkResult
49+
50+
// Minimally, check a ValueSet. This walker may traverse chains of aggregation and destructuring by default. Implementations may traverse phis.
51+
mutating func needWalk(for value: Value) -> Bool
52+
53+
mutating func walkUp(value: Value) -> WalkResult
54+
}
55+
56+
extension ForwardingUseDefWalker {
57+
mutating func walkUp(value: Value) -> WalkResult {
58+
walkUpDefault(value: value)
59+
}
60+
mutating func walkUpDefault(value: Value) -> WalkResult {
61+
if let inst = value.definingInstruction as? ForwardingInstruction
62+
{
63+
return walkUp(instruction: inst)
64+
}
65+
return introducer(value)
66+
}
67+
mutating func walkUp(instruction: ForwardingInstruction) -> WalkResult {
68+
for operand in instruction.forwardedOperands {
69+
if needWalk(for: operand.value) {
70+
if walkUp(value: operand.value) == .abortWalk {
71+
return .abortWalk
72+
}
73+
}
74+
}
75+
return .continueWalk
76+
}
77+
}
78+
79+
// This conveniently gathers all forward introducers and deinitializes visitedValues before the caller has a chance to recurse.
80+
func gatherLifetimeIntroducers(for value: Value, _ context: Context) -> [Value] {
81+
var gather = GatherLifetimeIntroducers(context)
82+
defer { gather.visitedValues.deinitialize() }
83+
let result = gather.walkUp(value: value)
84+
assert(result == .continueWalk)
85+
return gather.introducers
86+
}
87+
88+
private struct GatherLifetimeIntroducers : ForwardingUseDefWalker {
89+
var visitedValues: ValueSet
90+
var introducers: [Value] = []
91+
92+
init(_ context: Context) {
93+
self.visitedValues = ValueSet(context)
94+
}
95+
96+
mutating func needWalk(for value: Value) -> Bool {
97+
visitedValues.insert(value)
98+
}
99+
100+
mutating func introducer(_ value: Value) -> WalkResult {
101+
introducers.append(value)
102+
return .continueWalk
103+
}
104+
}
105+
106+
// Visit all the uses in a forward-extended lifetime (LifetimeIntroducer -> Operand).
107+
protocol ForwardingDefUseWalker {
108+
// Minimally, check a ValueSet. This walker may traverse chains of aggregation and destructuring by default. Implementations may handle phis.
109+
mutating func needWalk(for value: Value) -> Bool
110+
111+
mutating func leafUse(_ operand: Operand) -> WalkResult
112+
113+
mutating func walkDownUses(of value: Value) -> WalkResult
114+
115+
mutating func walkDown(operand: Operand) -> WalkResult
116+
}
117+
118+
extension ForwardingDefUseWalker {
119+
mutating func walkDownUses(of value: Value) -> WalkResult {
120+
return walkDownUsesDefault(of: value)
121+
}
122+
123+
mutating func walkDownUsesDefault(of value: Value) -> WalkResult {
124+
if !needWalk(for: value) { return .continueWalk }
125+
126+
for operand in value.uses where !operand.isTypeDependent {
127+
if walkDown(operand: operand) == .abortWalk {
128+
return .abortWalk
129+
}
130+
}
131+
return .continueWalk
132+
}
133+
134+
mutating func walkDown(operand: Operand) -> WalkResult {
135+
walkDownDefault(operand: operand)
136+
}
137+
138+
mutating func walkDownDefault(operand: Operand) -> WalkResult {
139+
if let inst = operand.instruction as? ForwardingInstruction {
140+
return walkDownAllResults(of: inst)
141+
}
142+
return leafUse(operand)
143+
}
144+
145+
private mutating func walkDownAllResults(of inst: ForwardingInstruction)
146+
-> WalkResult {
147+
for result in inst.forwardedResults {
148+
if walkDownUses(of: result) == .abortWalk {
149+
return .abortWalk
150+
}
151+
}
152+
return .continueWalk
153+
}
154+
}
155+
156+
// This conveniently allows a closure to be called for each leaf use of a forward-extended lifetime. It should be called on a forward introducer provided by ForwardingDefUseWalker.introducer() or gatherLifetimeIntroducers().
157+
//
158+
// TODO: make the visitor non-escaping once Swift supports stored non-escaping closues.
159+
func visitForwardedUses(introducer: Value,
160+
visitor: @escaping (Operand) -> WalkResult,
161+
_ context: Context)
162+
-> WalkResult {
163+
var useVisitor = VisitForwardedUses(visitor: visitor, context)
164+
defer { useVisitor.visitedValues.deinitialize() }
165+
return useVisitor.walkDownUses(of: introducer)
166+
}
167+
168+
private struct VisitForwardedUses : ForwardingDefUseWalker {
169+
var visitedValues: ValueSet
170+
var visitor: (Operand) -> WalkResult
171+
172+
init(visitor: @escaping (Operand) -> WalkResult, _ context: Context) {
173+
self.visitedValues = ValueSet(context)
174+
self.visitor = visitor
175+
}
176+
177+
mutating func needWalk(for value: Value) -> Bool {
178+
visitedValues.insert(value)
179+
}
180+
181+
mutating func leafUse(_ operand: Operand) -> WalkResult {
182+
return visitor(operand)
183+
}
184+
}

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_swift_compiler_module(SIL
1515
BasicBlock.swift
1616
Builder.swift
1717
Effects.swift
18+
ForwardingInstruction.swift
1819
Function.swift
1920
GlobalVariable.swift
2021
Instruction.swift

0 commit comments

Comments
 (0)