Skip to content

Commit d90470d

Browse files
authored
Merge pull request #69002 from atrick/test-forwarding
ForwardingUtils.swift fixes and unit tests
2 parents 2a92eac + bd157b3 commit d90470d

File tree

10 files changed

+428
-33
lines changed

10 files changed

+428
-33
lines changed

SwiftCompilerSources/Sources/Basic/Utils.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ public extension NoReflectionChildren {
5252
var customMirror: Mirror { Mirror(self, children: []) }
5353
}
5454

55+
public var standardError = CFileStream(fp: stderr)
56+
57+
public struct CFileStream: TextOutputStream {
58+
var fp: UnsafeMutablePointer<FILE>
59+
60+
public func write(_ string: String) {
61+
fputs(string, fp)
62+
}
63+
64+
public func flush() {
65+
fflush(fp)
66+
}
67+
}
5568

5669
//===----------------------------------------------------------------------===//
5770
// StringRef

SwiftCompilerSources/Sources/Optimizer/Utilities/ForwardingUtils.swift

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- OwnershipUtils.swift - Utilities for ownership -------------------===//
1+
//===--- ForwardingUtils.swift - Utilities for ownership forwarding -------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -29,8 +29,6 @@ import SIL
2929
//
3030
// Note: Although move_value conceptually forwards an owned value, it also summarizes lifetime attributes; therefore, it is not formally a ForwardingInstruction.
3131
//
32-
// TODO: when phi lifetime flags are implemented, phis will introduce a lifetime in the same way as move_value.
33-
//
3432
// The lifetime introducer of a guaranteed value is the borrow introducer:
3533
//
3634
// # lifetime introducer / borrow introducer
@@ -43,11 +41,25 @@ import SIL
4341
//
4442
// 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.
4543
//
44+
// Forwarded lifetimes also extend through phis. In this case, however, there is no ForwardingInstruction.
45+
//
46+
// # lifetime introducer
47+
// %1 = apply -+ -+
48+
// ... | OSSA lifetime |
49+
// # phi operand | |
50+
// br bbContinue(%1: $S) -+ | forward-extended lifetime
51+
// |
52+
// bbContinue(%phi : $S): -+ OSSA lifetime |
53+
// ... | |
54+
// destroy_value %phi -+ -+
55+
//
56+
// TODO: when phi lifetime flags are implemented, phis will introduce a lifetime in the same way as move_value.
57+
//
4658
// 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.
4759
protocol ForwardingUseDefWalker {
4860
mutating func introducer(_ value: Value) -> WalkResult
4961

50-
// Minimally, check a ValueSet. This walker may traverse chains of aggregation and destructuring by default. Implementations may traverse phis.
62+
// Minimally, check a ValueSet. This walker may traverse chains of aggregation and destructuring along with phis.
5163
mutating func needWalk(for value: Value) -> Bool
5264

5365
mutating func walkUp(value: Value) -> WalkResult
@@ -58,10 +70,12 @@ extension ForwardingUseDefWalker {
5870
walkUpDefault(value: value)
5971
}
6072
mutating func walkUpDefault(value: Value) -> WalkResult {
61-
if let inst = value.definingInstruction as? ForwardingInstruction
62-
{
73+
if let inst = value.forwardingInstruction {
6374
return walkUp(instruction: inst)
6475
}
76+
if let phi = Phi(value) {
77+
return walkUp(phi: phi)
78+
}
6579
return introducer(value)
6680
}
6781
mutating func walkUp(instruction: ForwardingInstruction) -> WalkResult {
@@ -74,6 +88,16 @@ extension ForwardingUseDefWalker {
7488
}
7589
return .continueWalk
7690
}
91+
mutating func walkUp(phi: Phi) -> WalkResult {
92+
for operand in phi.incomingOperands {
93+
if needWalk(for: operand.value) {
94+
if walkUp(value: operand.value) == .abortWalk {
95+
return .abortWalk
96+
}
97+
}
98+
}
99+
return .continueWalk
100+
}
77101
}
78102

79103
// This conveniently gathers all forward introducers and deinitializes visitedValues before the caller has a chance to recurse.
@@ -103,14 +127,34 @@ private struct GatherLifetimeIntroducers : ForwardingUseDefWalker {
103127
}
104128
}
105129

130+
enum ForwardingUseResult: CustomStringConvertible {
131+
case operand(Operand)
132+
case deadValue(Value)
133+
134+
var description: String {
135+
switch self {
136+
case .operand(let operand):
137+
return operand.description
138+
case .deadValue(let deadValue):
139+
return "dead value: " + deadValue.description
140+
}
141+
}
142+
}
143+
106144
// Visit all the uses in a forward-extended lifetime (LifetimeIntroducer -> Operand).
107145
protocol ForwardingDefUseWalker {
108146
// Minimally, check a ValueSet. This walker may traverse chains of aggregation and destructuring by default. Implementations may handle phis.
109147
mutating func needWalk(for value: Value) -> Bool
110148

111-
mutating func leafUse(_ operand: Operand) -> WalkResult
149+
// Report any initial or forwarded with no uses. Only relevant for
150+
// guaranteed values or incomplete OSSA. This could be a dead
151+
// instruction, a terminator in which the result is dead on one
152+
// path, or a dead phi.
153+
mutating func deadValue(_: Value) -> WalkResult
154+
155+
mutating func leafUse(_: Operand) -> WalkResult
112156

113-
mutating func walkDownUses(of value: Value) -> WalkResult
157+
mutating func walkDownUses(of: Value) -> WalkResult
114158

115159
mutating func walkDown(operand: Operand) -> WalkResult
116160
}
@@ -123,10 +167,15 @@ extension ForwardingDefUseWalker {
123167
mutating func walkDownUsesDefault(of value: Value) -> WalkResult {
124168
if !needWalk(for: value) { return .continueWalk }
125169

170+
var hasUse = false
126171
for operand in value.uses where !operand.isTypeDependent {
127172
if walkDown(operand: operand) == .abortWalk {
128173
return .abortWalk
129174
}
175+
hasUse = true
176+
}
177+
if !hasUse {
178+
deadValue(value)
130179
}
131180
return .continueWalk
132181
}
@@ -139,6 +188,9 @@ extension ForwardingDefUseWalker {
139188
if let inst = operand.instruction as? ForwardingInstruction {
140189
return walkDownAllResults(of: inst)
141190
}
191+
if let phi = Phi(using: operand) {
192+
return walkDownUses(of: phi.value)
193+
}
142194
return leafUse(operand)
143195
}
144196

@@ -156,9 +208,8 @@ extension ForwardingDefUseWalker {
156208
// 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().
157209
//
158210
// 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)
211+
func visitForwardedUses(introducer: Value, _ context: Context,
212+
visitor: @escaping (ForwardingUseResult) -> WalkResult)
162213
-> WalkResult {
163214
var useVisitor = VisitForwardedUses(visitor: visitor, context)
164215
defer { useVisitor.visitedValues.deinitialize() }
@@ -167,9 +218,10 @@ func visitForwardedUses(introducer: Value,
167218

168219
private struct VisitForwardedUses : ForwardingDefUseWalker {
169220
var visitedValues: ValueSet
170-
var visitor: (Operand) -> WalkResult
221+
var visitor: (ForwardingUseResult) -> WalkResult
171222

172-
init(visitor: @escaping (Operand) -> WalkResult, _ context: Context) {
223+
init(visitor: @escaping (ForwardingUseResult) -> WalkResult,
224+
_ context: Context) {
173225
self.visitedValues = ValueSet(context)
174226
self.visitor = visitor
175227
}
@@ -179,6 +231,27 @@ private struct VisitForwardedUses : ForwardingDefUseWalker {
179231
}
180232

181233
mutating func leafUse(_ operand: Operand) -> WalkResult {
182-
return visitor(operand)
234+
return visitor(.operand(operand))
235+
}
236+
237+
mutating func deadValue(_ value: Value) -> WalkResult {
238+
return visitor(.deadValue(value))
239+
}
240+
}
241+
242+
let forwardingUseDefTest = FunctionTest("forwarding_use_def_test") {
243+
function, arguments, context in
244+
let value = arguments.takeValue()
245+
for introducer in gatherLifetimeIntroducers(for: value, context) {
246+
print("INTRODUCER: \(introducer)")
247+
}
248+
}
249+
250+
let forwardingDefUseTest = FunctionTest("forwarding_def_use_test") {
251+
function, arguments, context in
252+
let value = arguments.takeValue()
253+
_ = visitForwardedUses(introducer: value, context) { operand in
254+
print("USE: \(operand)")
255+
return .continueWalk
183256
}
184257
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
// FunctionTest("my_new_test") { function, arguments, context in
1717
// }
1818
// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerOptimizerTests
19-
// function, register the new test:
20-
// registerFunctionTest(myNewTest)
19+
// function, add a new argument to the variadic function:
20+
// registerFunctionTests(..., myNewTest)
2121
//
2222
//===----------------------------------------------------------------------===//
2323
//
@@ -100,7 +100,7 @@ struct FunctionTest {
100100
let name: String
101101
let invocation: FunctionTestInvocation
102102

103-
public init(name: String, invocation: @escaping FunctionTestInvocation) {
103+
public init(_ name: String, invocation: @escaping FunctionTestInvocation) {
104104
self.name = name
105105
self.invocation = invocation
106106
}
@@ -135,12 +135,19 @@ extension BridgedTestArguments {
135135
/// Registration of each test in the SIL module.
136136
public func registerOptimizerTests() {
137137
// Register each test.
138-
registerFunctionTest(parseTestSpecificationTest)
138+
registerFunctionTests(
139+
parseTestSpecificationTest,
140+
forwardingUseDefTest,
141+
forwardingDefUseTest
142+
)
139143

140144
// Finally register the thunk they all call through.
141145
registerFunctionTestThunk(functionTestThunk)
142146
}
143147

148+
private func registerFunctionTests(_ tests: FunctionTest...) {
149+
tests.forEach { registerFunctionTest($0) }
150+
}
144151

145152
private func registerFunctionTest(_ test: FunctionTest) {
146153
test.name._withBridgedStringRef { ref in
@@ -202,7 +209,7 @@ private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableR
202209
// - its type
203210
// - something to identify the instance (mostly this means calling dump)
204211
let parseTestSpecificationTest =
205-
FunctionTest(name: "test_specification_parsing") { function, arguments, context in
212+
FunctionTest("test_specification_parsing") { function, arguments, context in
206213
let expectedFields = arguments.takeString()
207214
for expectedField in expectedFields.string {
208215
switch expectedField {

SwiftCompilerSources/Sources/SIL/Argument.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,19 @@ public struct Phi {
6464
guard term is BranchInst || term is CondBranchInst else { return nil }
6565
self.value = argument
6666
}
67-
67+
68+
public init?(using operand: Operand) {
69+
switch operand.instruction {
70+
case let br as BranchInst:
71+
self.init(br.getArgument(for: operand))
72+
case let condBr as CondBranchInst:
73+
guard let arg = condBr.getArgument(for: operand) else { return nil }
74+
self.init(arg)
75+
default:
76+
return nil
77+
}
78+
}
79+
6880
public var predecessors: PredecessorList {
6981
return value.parentBlock.predecessors
7082
}

SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ extension ForwardingInstruction {
7979
// See ForwardingOperation::getSingleForwardingOperand().
8080
public var singleForwardedOperand: Operand? {
8181
let definedOps = self.definedOperands
82-
assert(definedOps.count == 1);
83-
return definedOps[0];
82+
assert(definedOps.count == 1, "expected single operand for forwarding")
83+
return definedOps[0]
8484
}
8585
}
8686

@@ -117,19 +117,19 @@ extension DifferentiableFunctionInst {
117117
// Instructions with a singleForwardedOperand and additional operands.
118118

119119
extension MarkDependenceInst {
120-
public var singleForwardedOperand: Operand {
120+
public var singleForwardedOperand: Operand? {
121121
return valueOperand
122122
}
123123
}
124124

125125
extension RefToBridgeObjectInst {
126-
public var singleForwardedOperand: Operand {
126+
public var singleForwardedOperand: Operand? {
127127
return convertedOperand
128128
}
129129
}
130130

131131
extension TuplePackExtractInst {
132-
public var singleForwardedOperand: Operand {
132+
public var singleForwardedOperand: Operand? {
133133
return tupleOperand
134134
}
135135
}

include/swift/SIL/Test.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class FunctionTest final {
167167
template <typename Analysis, typename Transform = SILFunctionTransform>
168168
Analysis *getAnalysis();
169169

170-
SwiftPassInvocation *getInvocation();
170+
SwiftPassInvocation *getSwiftPassInvocation();
171171

172172
//===----------------------------------------------------------------------===//
173173
//=== MARK: Implementation Details ===
@@ -247,7 +247,7 @@ class FunctionTest final {
247247
struct Dependencies {
248248
virtual DominanceInfo *getDominanceInfo() = 0;
249249
virtual SILPassManager *getPassManager() = 0;
250-
virtual SwiftPassInvocation *getInvocation() = 0;
250+
virtual SwiftPassInvocation *getSwiftPassInvocation() = 0;
251251
virtual ~Dependencies(){};
252252
};
253253

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ class SwiftPassInvocation {
9999
SILCombiner *silCombiner) :
100100
passManager(passManager), function(function), silCombiner(silCombiner) {}
101101

102+
SwiftPassInvocation(SILPassManager *passManager, SILTransform *transform,
103+
SILFunction *function) :
104+
passManager(passManager), transform(transform), function(function) {}
105+
102106
SwiftPassInvocation(SILPassManager *passManager) :
103107
passManager(passManager) {}
104108

lib/SIL/Utils/Test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void FunctionTest::run(SILFunction &function, Arguments &arguments,
100100
llvm::outs().flush();
101101
auto *fn = invocation.get<NativeSwiftInvocation>();
102102
Registry::get().getFunctionTestThunk()(fn, {&function}, {&arguments},
103-
{getInvocation()});
103+
{getSwiftPassInvocation()});
104104
}
105105
this->pass = nullptr;
106106
this->function = nullptr;
@@ -115,6 +115,6 @@ SILPassManager *FunctionTest::getPassManager() {
115115
return dependencies->getPassManager();
116116
}
117117

118-
SwiftPassInvocation *FunctionTest::getInvocation() {
119-
return dependencies->getInvocation();
118+
SwiftPassInvocation *FunctionTest::getSwiftPassInvocation() {
119+
return dependencies->getSwiftPassInvocation();
120120
}

lib/SILOptimizer/UtilityPasses/TestRunner.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,17 @@ class TestRunner : public SILFunctionTransform {
6060
: public test::FunctionTest::Dependencies {
6161
TestRunner *pass;
6262
SILFunction *function;
63-
SwiftPassInvocation invocation;
63+
SwiftPassInvocation swiftPassInvocation;
6464
FunctionTestDependenciesImpl(TestRunner *pass, SILFunction *function)
65-
: pass(pass), function(function), invocation(pass->getPassManager()) {}
65+
: pass(pass), function(function),
66+
swiftPassInvocation(pass->getPassManager(), pass, function) {}
6667
DominanceInfo *getDominanceInfo() override {
6768
auto *dominanceAnalysis = pass->getAnalysis<DominanceAnalysis>();
6869
return dominanceAnalysis->get(function);
6970
}
70-
SwiftPassInvocation *getInvocation() override { return &invocation; }
71+
SwiftPassInvocation *getSwiftPassInvocation() override {
72+
return &swiftPassInvocation;
73+
}
7174
SILPassManager *getPassManager() override { return pass->getPassManager(); }
7275
~FunctionTestDependenciesImpl() {}
7376
};

0 commit comments

Comments
 (0)