Skip to content

Commit e95283b

Browse files
committed
SwiftCompilerSources: make the testing infrastructure available in the SIL module
add `Test`, which is the SIL-equivalent of `FunctionTest`. It's invocation closure gets a `TestContext` instead of a `FunctionContext`. ^ The commit message #2 will be skipped: ^ - test
1 parent e89fdb5 commit e95283b

File tree

10 files changed

+201
-97
lines changed

10 files changed

+201
-97
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ swift_compiler_sources(Optimizer
1515
EscapeUtils.swift
1616
ForwardingUtils.swift
1717
FunctionSignatureTransforms.swift
18+
FunctionTest.swift
1819
GenericSpecialization.swift
1920
LifetimeDependenceUtils.swift
2021
LocalVariableUtils.swift
2122
OptUtils.swift
2223
OwnershipLiveness.swift
2324
PhiUpdater.swift
2425
StaticInitCloner.swift
25-
Test.swift
2626
Verifier.swift
2727
)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===----------- FunctionTest.swift ---------------------------------------===//
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+
// Optimizer test infrastructure.
14+
//
15+
// For general documentation on how to write tests see `Test.swift` in the
16+
// SIL module.
17+
// To add an optimizer test, use `FunctionTest` instead of `Test` and register
18+
// it in `registerOptimizerTests`.
19+
//
20+
//===----------------------------------------------------------------------===//
21+
22+
import SIL
23+
import OptimizerBridging
24+
25+
/// Like `SIL.Test`, but provides a `FunctionPass` to the invocation closure.
26+
struct FunctionTest {
27+
let name: String
28+
let invocation: FunctionTestInvocation
29+
30+
public init(_ name: String, invocation: @escaping FunctionTestInvocation) {
31+
self.name = name
32+
self.invocation = invocation
33+
}
34+
}
35+
36+
/// The type of the closure passed to a FunctionTest.
37+
typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, FunctionPassContext) -> ()
38+
39+
public func registerOptimizerTests() {
40+
SIL.registerTests()
41+
42+
// Register each test.
43+
registerFunctionTests(
44+
getAccessBaseTest,
45+
addressOwnershipLiveRangeTest,
46+
argumentConventionsTest,
47+
borrowIntroducersTest,
48+
enclosingValuesTest,
49+
forwardingDefUseTest,
50+
forwardingUseDefTest,
51+
getPullbackClosureInfoTest,
52+
interiorLivenessTest,
53+
lifetimeDependenceRootTest,
54+
lifetimeDependenceScopeTest,
55+
lifetimeDependenceUseTest,
56+
linearLivenessTest,
57+
localVariableReachableUsesTest,
58+
localVariableReachingAssignmentsTest,
59+
rangeOverlapsPathTest,
60+
rewrittenCallerBodyTest,
61+
specializedFunctionSignatureAndBodyTest,
62+
variableIntroducerTest
63+
)
64+
65+
// Finally register the thunk they all call through.
66+
registerFunctionTestThunk(functionTestThunk)
67+
}
68+
69+
private func registerFunctionTests(_ tests: FunctionTest...) {
70+
tests.forEach { registerFunctionTest($0) }
71+
}
72+
73+
private func registerFunctionTest(_ test: FunctionTest) {
74+
test.name._withBridgedStringRef { ref in
75+
registerFunctionTest(ref, castToOpaquePointer(fromInvocation: test.invocation))
76+
}
77+
}
78+
79+
/// The function called by the swift::test::FunctionTest which invokes the
80+
/// actual test function.
81+
///
82+
/// This function is necessary because tests need to be written in terms of
83+
/// native Swift types (Function, TestArguments, FunctionPassContext)
84+
/// rather than their bridged variants, but such a function isn't representable
85+
/// in C++. This thunk unwraps the bridged types and invokes the real function.
86+
private func functionTestThunk(
87+
_ erasedInvocation: UnsafeMutableRawPointer,
88+
_ function: BridgedFunction,
89+
_ arguments: BridgedTestArguments,
90+
_ bridgedContext: BridgedContext) {
91+
let invocation = castToInvocation(fromOpaquePointer: erasedInvocation)
92+
let context = FunctionPassContext(_bridged: bridgedContext)
93+
invocation(function.function, arguments.native, context)
94+
}
95+
96+
/// Bitcast a thin test closure to void *.
97+
///
98+
/// Needed so that the closure can be represented in C++ for storage in the test
99+
/// registry.
100+
private func castToOpaquePointer(fromInvocation invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer {
101+
return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self)
102+
}
103+
104+
/// Bitcast a void * to a thin test closure.
105+
///
106+
/// Needed so that the closure stored in the C++ test registry can be invoked
107+
/// via the functionTestThunk.
108+
private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation {
109+
return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self)
110+
}

SwiftCompilerSources/Sources/SIL/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ swift_compiler_sources(SIL
1111
SequenceUtilities.swift
1212
SmallProjectionPath.swift
1313
SSAUpdater.swift
14+
Test.swift
1415
WalkUtils.swift
1516
)
1617

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift renamed to SwiftCompilerSources/Sources/SIL/Utilities/Test.swift

Lines changed: 44 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,25 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
//
13-
// TO ADD A NEW TEST, just add a new FunctionTest instance.
13+
// TO ADD A NEW TEST, just add a new Test instance.
1414
// - In the source file containing the functionality you want to test:
15-
// let myNewTest =
16-
// FunctionTest("my_new_test") { function, arguments, context in
15+
// let myNewTest = Test("my_new_test") { function, arguments, context in
1716
// }
18-
// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerOptimizerTests
17+
// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerTests
1918
// function, add a new argument to the variadic function:
20-
// registerFunctionTests(..., myNewTest)
21-
//
22-
//===----------------------------------------------------------------------===//
23-
//
24-
// TO TROUBLESHOOT A NEW TEST, consider the following scenarios:
25-
// - PROBLEM: The test isn't running on PLATFORM and the failure says
26-
//
27-
// Found no test named my_new_test.
28-
//
29-
// SOLUTION: Is this a platform one that doesn't use SwiftCompilerSources
30-
// (e.g. Windows)? Then add
31-
//
32-
// // REQUIRES: swift_in_compiler
33-
//
34-
// to the test file.
35-
// EXPLANATION: The tests written within SwiftCompilerSources only get built
36-
// and registered on platforms where the SwiftCompilerSources are
37-
// built and used.
19+
// registerTests(..., myNewTest)
3820
//
3921
//===----------------------------------------------------------------------===//
4022
//
4123
// Provides a mechanism for writing tests against compiler code in the context
42-
// of a function. The goal is to get the same effect as calling a function and
24+
// of a function. The goal is to get the same effect as calling a function and
4325
// checking its output.
4426
//
45-
// This is done via the specify_test instruction. Using one or more instances
27+
// This is done via the specify_test instruction. Using one or more instances
4628
// of it in your test case's SIL function, you can specify which test (instance
47-
// of FunctionTest) should be run and what arguments should be provided to it.
48-
// For full details of the specify_test instruction's grammar, see SIL.rst.
29+
// of Test) should be run and what arguments should be provided to it.
30+
// For full details of the specify_test instruction's grammar,
31+
// see doc/SIL/Instructions.md.
4932
//
5033
// The test grabs the arguments it expects out of the TestArguments instance
5134
// it is provided. It calls some function or functions. It then prints out
@@ -59,8 +42,7 @@
5942
//
6043
// and
6144
//
62-
// let myNeatoUtilityTest =
63-
// FunctionTest("my_neato_utility") { function, arguments, test in
45+
// let myNeatoUtilityTest = Test("my_neato_utility") { function, arguments, test in
6446
// // The code here is described in detail below.
6547
// // See 4).
6648
// let count = arguments.takeInt()
@@ -82,9 +64,9 @@
8264
// specify_test "my_neato_utility 43 %2 @function[other_fun]"
8365
// ...
8466
// }
85-
// 3) TestRunner finds the FunctionTest instance myNeatoUtilityTest registered
67+
// 3) TestRunner finds the Test instance myNeatoUtilityTest registered
8668
// under the name "my_neato_utility", and calls run() on it, passing the
87-
// passing first the function, last the FunctionTest instance, AND in the
69+
// passing first the function, last the Test instance, AND in the
8870
// middle, most importantly a TestArguments instance that contains
8971
//
9072
// (43 : Int, someValue : Value, other_fun : Function)
@@ -108,23 +90,29 @@
10890
//===----------------------------------------------------------------------===//
10991

11092
import Basic
111-
import SIL
11293
import SILBridging
113-
import OptimizerBridging
11494

11595
/// The primary interface to in-IR tests.
116-
struct FunctionTest {
96+
public struct Test {
11797
let name: String
118-
let invocation: FunctionTestInvocation
98+
let invocation: TestInvocation
11999

120-
public init(_ name: String, invocation: @escaping FunctionTestInvocation) {
100+
public init(_ name: String, invocation: @escaping TestInvocation) {
121101
self.name = name
122102
self.invocation = invocation
123103
}
124104
}
125105

126-
/// The type of the closure passed to a FunctionTest.
127-
typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, FunctionPassContext) -> ()
106+
/// The type of the closure passed to a Test.
107+
public typealias TestInvocation = @convention(thin) (Function, TestArguments, TestContext) -> ()
108+
109+
110+
public struct TestContext: MutatingContext {
111+
public let _bridged: BridgedContext
112+
public let notifyInstructionChanged: (Instruction) -> () = { inst in }
113+
114+
fileprivate init(bridged: BridgedContext) { self._bridged = bridged}
115+
}
128116

129117
/// Wraps the arguments specified in the specify_test instruction.
130118
public struct TestArguments {
@@ -150,77 +138,55 @@ extension BridgedTestArguments {
150138
}
151139

152140
/// Registration of each test in the SIL module.
153-
public func registerOptimizerTests() {
141+
public func registerTests() {
154142
// Register each test.
155-
registerFunctionTests(
156-
getAccessBaseTest,
157-
addressOwnershipLiveRangeTest,
158-
argumentConventionsTest,
159-
borrowIntroducersTest,
160-
enclosingValuesTest,
161-
forwardingDefUseTest,
162-
forwardingUseDefTest,
163-
getPullbackClosureInfoTest,
164-
interiorLivenessTest,
165-
lifetimeDependenceRootTest,
166-
lifetimeDependenceScopeTest,
167-
lifetimeDependenceUseTest,
168-
linearLivenessTest,
169-
localVariableReachableUsesTest,
170-
localVariableReachingAssignmentsTest,
171-
parseTestSpecificationTest,
172-
rangeOverlapsPathTest,
173-
rewrittenCallerBodyTest,
174-
specializedFunctionSignatureAndBodyTest,
175-
variableIntroducerTest
143+
registerTests(
144+
parseTestSpecificationTest
176145
)
177146

178-
// Finally register the thunk they all call through.
179-
registerFunctionTestThunk(functionTestThunk)
147+
registerTestThunk(testThunk)
180148
}
181149

182-
private func registerFunctionTests(_ tests: FunctionTest...) {
183-
tests.forEach { registerFunctionTest($0) }
150+
private func registerTests(_ tests: Test...) {
151+
tests.forEach { registerTest($0) }
184152
}
185153

186-
private func registerFunctionTest(_ test: FunctionTest) {
154+
private func registerTest(_ test: Test) {
187155
test.name._withBridgedStringRef { ref in
188-
registerFunctionTest(ref, castToOpaquePointer(fromInvocation: test.invocation))
156+
registerTest(ref, castToOpaquePointer(fromInvocation: test.invocation))
189157
}
190158
}
191159

192160
/// The function called by the swift::test::FunctionTest which invokes the
193161
/// actual test function.
194162
///
195163
/// This function is necessary because tests need to be written in terms of
196-
/// native Swift types (Function, TestArguments, BridgedPassContext)
164+
/// native Swift types (Function, TestArguments, TestContext)
197165
/// rather than their bridged variants, but such a function isn't representable
198-
/// in C++. This thunk unwraps the bridged types and invokes the real
199-
/// function.
200-
private func functionTestThunk(
166+
/// in C++. This thunk unwraps the bridged types and invokes the real function.
167+
private func testThunk(
201168
_ erasedInvocation: UnsafeMutableRawPointer,
202-
_ function: BridgedFunction,
203-
_ arguments: BridgedTestArguments,
169+
_ function: BridgedFunction,
170+
_ arguments: BridgedTestArguments,
204171
_ bridgedContext: BridgedContext) {
205172
let invocation = castToInvocation(fromOpaquePointer: erasedInvocation)
206-
let context = FunctionPassContext(_bridged: bridgedContext)
173+
let context = TestContext(bridged: bridgedContext)
207174
invocation(function.function, arguments.native, context)
208175
}
209176

210177
/// Bitcast a thin test closure to void *.
211178
///
212179
/// Needed so that the closure can be represented in C++ for storage in the test
213180
/// registry.
214-
private func castToOpaquePointer(fromInvocation invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer {
181+
private func castToOpaquePointer(fromInvocation invocation: TestInvocation) -> UnsafeMutableRawPointer {
215182
return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self)
216183
}
217184

218185
/// Bitcast a void * to a thin test closure.
219186
///
220-
/// Needed so that the closure stored in the C++ test registry can be invoked
221-
/// via the functionTestThunk.
222-
private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation {
223-
return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self)
187+
/// Needed so that the closure stored in the C++ test registry can be invoked via the testThunk.
188+
private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> TestInvocation {
189+
return unsafeBitCast(erasedInvocation, to: TestInvocation.self)
224190
}
225191

226192
// Arguments:
@@ -241,8 +207,7 @@ private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableR
241207
// - for each argument (after the initial string)
242208
// - its type
243209
// - something to identify the instance (mostly this means calling dump)
244-
let parseTestSpecificationTest =
245-
FunctionTest("test_specification_parsing") { function, arguments, context in
210+
let parseTestSpecificationTest = Test("test_specification_parsing") { function, arguments, context in
246211
let expectedFields = arguments.takeString()
247212
for expectedField in expectedFields.string {
248213
switch expectedField {

include/swift/SIL/SILBridging.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,9 +1521,8 @@ struct BridgedTestArguments {
15211521
using SwiftNativeFunctionTestThunk =
15221522
void (*_Nonnull)(void *_Nonnull, BridgedFunction, BridgedTestArguments, BridgedContext);
15231523

1524-
void registerFunctionTestThunk(SwiftNativeFunctionTestThunk);
1525-
1526-
void registerFunctionTest(BridgedStringRef, void *_Nonnull nativeSwiftContext);
1524+
void registerTestThunk(SwiftNativeFunctionTestThunk);
1525+
void registerTest(BridgedStringRef, void *_Nonnull nativeSwiftContext);
15271526

15281527
SWIFT_END_NULLABILITY_ANNOTATIONS
15291528

include/swift/SIL/Test.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ class FunctionTest final {
141141
/// The lambda to be run.
142142
TaggedUnion<Invocation, NativeSwiftInvocation> invocation;
143143

144+
bool isSwiftSILTest = false;
145+
144146
public:
145147
/// Creates a test that will run \p invocation and stores it in the global
146148
/// registry.
@@ -158,7 +160,8 @@ class FunctionTest final {
158160
/// Creates a test that will run \p invocation and stores it in the global
159161
/// registry.
160162
static void createNativeSwiftFunctionTest(StringRef name,
161-
NativeSwiftInvocation invocation);
163+
NativeSwiftInvocation invocation,
164+
bool isSILTest);
162165

163166
/// Computes and returns the function's dominance tree.
164167
DominanceInfo *getDominanceInfo();
@@ -271,7 +274,7 @@ class FunctionTest final {
271274
public:
272275
/// Creates a test that will run \p invocation. For use by
273276
/// createNativeSwiftFunctionTest.
274-
FunctionTest(StringRef name, NativeSwiftInvocation invocation);
277+
FunctionTest(StringRef name, NativeSwiftInvocation invocation, bool isSILTest);
275278
};
276279

277280
/// Thunks for delaying template instantiation.

0 commit comments

Comments
 (0)