Skip to content

Commit 841f6c4

Browse files
committed
SwiftCompilerSources: added OwnershipUtils for ForwardingInstruction.
This could be combined with ValueUseDefWalker if the latter is refactored to classsify instructions by projections and aggegation (which always forward) vs. other arbitrary hard-coded instruction types. It would also need to limit the walk to real operands (which are always forwarded). Then this walker can call into the default walk for projections and track the projection path. The current implementation is however simpler and more efficient.
1 parent 7e9ebce commit 841f6c4

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
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+
OwnershipUtils.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+
}

0 commit comments

Comments
 (0)