Skip to content

Commit 18ce3ab

Browse files
Merge pull request #69007 from nate-chandler/swiftcompilersources/test-bridging-registration-cleanup
[SwiftCompilerSources] Simplified test registration.
2 parents c59c793 + ce4ba6c commit 18ce3ab

File tree

5 files changed

+90
-41
lines changed

5 files changed

+90
-41
lines changed

SwiftCompilerSources/Sources/SIL/Test.swift

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
// }
1818
// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerSILTests function,
1919
// register the new test:
20-
// registerFunctionTest(myNewTest, implementation: { myNewTest.run($0, $1, $2) })
20+
// registerFunctionTest(myNewTest)
2121
//
2222
//===----------------------------------------------------------------------===//
2323
//
@@ -92,22 +92,21 @@
9292
import Basic
9393
import SILBridging
9494

95+
/// The primary interface to in-IR tests.
9596
public struct FunctionTest {
96-
public var name: String
97-
public typealias FunctionTestImplementation = (Function, TestArguments, TestContext) -> ()
98-
private var implementation: FunctionTestImplementation
99-
init(name: String, implementation: @escaping FunctionTestImplementation) {
97+
let name: String
98+
let invocation: FunctionTestInvocation
99+
100+
public init(name: String, invocation: @escaping FunctionTestInvocation) {
100101
self.name = name
101-
self.implementation = implementation
102-
}
103-
fileprivate func run(
104-
_ function: BridgedFunction,
105-
_ arguments: BridgedTestArguments,
106-
_ test: BridgedTestContext) {
107-
implementation(function.function, arguments.native, test.native)
102+
self.invocation = invocation
108103
}
109104
}
110105

106+
/// The type of the closure passed to a FunctionTest.
107+
public typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, TestContext) -> ()
108+
109+
/// Wraps the arguments specified in the specify_test instruction.
111110
public struct TestArguments {
112111
public var bridged: BridgedTestArguments
113112
fileprivate init(bridged: BridgedTestArguments) {
@@ -130,6 +129,7 @@ extension BridgedTestArguments {
130129
public var native: TestArguments { TestArguments(bridged: self) }
131130
}
132131

132+
/// An interface to the various analyses that are available.
133133
public struct TestContext {
134134
public var bridged: BridgedTestContext
135135
fileprivate init(bridged: BridgedTestContext) {
@@ -141,16 +141,52 @@ extension BridgedTestContext {
141141
public var native: TestContext { TestContext(bridged: self) }
142142
}
143143

144-
private func registerFunctionTest(
145-
_ test: FunctionTest,
146-
implementation: BridgedFunctionTestThunk) {
144+
/// Registration of each test in the SIL module.
145+
public func registerSILTests() {
146+
// Register each test.
147+
registerFunctionTest(parseTestSpecificationTest)
148+
149+
// Finally register the thunk they all call through.
150+
registerFunctionTestThunk(functionTestThunk)
151+
}
152+
153+
154+
private func registerFunctionTest(_ test: FunctionTest) {
147155
test.name._withStringRef { ref in
148-
registerFunctionTest(ref, implementation)
156+
registerFunctionTest(ref, eraseInvocation(test.invocation))
149157
}
150158
}
151159

152-
public func registerSILTests() {
153-
registerFunctionTest(parseTestSpecificationTest, implementation: { parseTestSpecificationTest.run($0, $1, $2) })
160+
/// The function called by the swift::test::FunctionTest which invokes the
161+
/// actual test function.
162+
///
163+
/// This function is necessary because tests need to be written in terms of
164+
/// native Swift types (Function, TestArguments, TestContext) rather than their
165+
/// bridged variants, but such a function isn't representable in C++. This
166+
/// thunk unwraps the bridged types and invokes the real function.
167+
private func functionTestThunk(
168+
_ erasedInvocation: UnsafeMutableRawPointer,
169+
_ function: BridgedFunction,
170+
_ arguments: BridgedTestArguments,
171+
_ context: BridgedTestContext) {
172+
let invocation = uneraseInvocation(erasedInvocation)
173+
invocation(function.function, arguments.native, context.native)
174+
}
175+
176+
/// Bitcast a thin test closure to void *.
177+
///
178+
/// Needed so that the closure can be represented in C++ for storage in the test
179+
/// registry.
180+
private func eraseInvocation(_ invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer {
181+
return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self)
182+
}
183+
184+
/// Bitcast a void * to a thin test closure.
185+
///
186+
/// Needed so that the closure stored in the C++ test registry can be invoked
187+
/// via the functionTestThunk.
188+
private func uneraseInvocation(_ erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation {
189+
return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self)
154190
}
155191

156192
// Arguments:

include/swift/SIL/SILBridging.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,10 @@ struct Arguments;
16221622
class FunctionTest;
16231623
} // namespace swift::test
16241624

1625+
struct BridgedFunctionTest {
1626+
swift::test::FunctionTest *_Nonnull test;
1627+
};
1628+
16251629
struct BridgedTestArguments {
16261630
swift::test::Arguments *_Nonnull arguments;
16271631

@@ -1657,10 +1661,15 @@ struct BridgedTestContext {
16571661
swift::SwiftPassInvocation *_Nonnull invocation;
16581662
};
16591663

1660-
typedef void (*_Nonnull BridgedFunctionTestThunk)(BridgedFunction,
1661-
BridgedTestArguments,
1662-
BridgedTestContext);
1663-
void registerFunctionTest(llvm::StringRef, BridgedFunctionTestThunk thunk);
1664+
using SwiftNativeFunctionTestThunk = void (*_Nonnull)(void *_Nonnull,
1665+
BridgedFunction,
1666+
BridgedTestArguments,
1667+
BridgedTestContext);
1668+
1669+
void registerFunctionTestThunk(SwiftNativeFunctionTestThunk);
1670+
1671+
void registerFunctionTest(llvm::StringRef,
1672+
void *_Nonnull nativeSwiftInvocation);
16641673

16651674
SWIFT_END_NULLABILITY_ANNOTATIONS
16661675

include/swift/SIL/Test.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,11 @@ class FunctionTest final {
131131
/// values such as the results of analyses
132132
using Invocation = void (*)(SILFunction &, Arguments &, FunctionTest &);
133133

134-
using InvocationWithContext = void (*)(SILFunction &, Arguments &,
135-
FunctionTest &, void *);
134+
using NativeSwiftInvocation = void *;
136135

137136
private:
138137
/// The lambda to be run.
139-
TaggedUnion<Invocation, std::pair<InvocationWithContext, void *>> invocation;
138+
TaggedUnion<Invocation, NativeSwiftInvocation> invocation;
140139

141140
public:
142141
/// Creates a test that will run \p invocation and stores it in the global
@@ -152,7 +151,7 @@ class FunctionTest final {
152151
/// } // end namespace swift::test
153152
FunctionTest(StringRef name, Invocation invocation);
154153

155-
FunctionTest(StringRef name, void *context, InvocationWithContext invocation);
154+
FunctionTest(StringRef name, NativeSwiftInvocation invocation);
156155

157156
/// Computes and returns the function's dominance tree.
158157
DominanceInfo *getDominanceInfo();

lib/SIL/Utils/SILBridging.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,8 @@ void registerBridgedClass(StringRef className, SwiftMetatype metatype) {
133133
// Test
134134
//===----------------------------------------------------------------------===//
135135

136-
void registerFunctionTest(llvm::StringRef name,
137-
BridgedFunctionTestThunk thunk) {
138-
new swift::test::FunctionTest(
139-
name, reinterpret_cast<void *>(thunk),
140-
[](auto &function, auto &args, auto &test, void *ctx) {
141-
auto thunk = reinterpret_cast<BridgedFunctionTestThunk>(ctx);
142-
thunk({&function}, {&args}, test.getContext());
143-
});
136+
void registerFunctionTest(llvm::StringRef name, void *nativeSwiftInvocation) {
137+
new swift::test::FunctionTest(name, nativeSwiftInvocation);
144138
}
145139

146140
bool BridgedTestArguments::hasUntaken() const {

lib/SIL/Utils/Test.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace {
2626

2727
class Registry {
2828
DenseMap<StringRef, FunctionTest *> registeredTests;
29+
SwiftNativeFunctionTestThunk thunk;
2930

3031
public:
3132
static Registry &get() {
@@ -39,6 +40,12 @@ class Registry {
3940
(void)inserted;
4041
}
4142

43+
void registerFunctionTestThunk(SwiftNativeFunctionTestThunk thunk) {
44+
this->thunk = thunk;
45+
}
46+
47+
SwiftNativeFunctionTestThunk getFunctionTestThunk() { return thunk; }
48+
4249
FunctionTest *getFunctionTest(StringRef name) {
4350
auto *res = registeredTests[name];
4451
if (!res) {
@@ -62,15 +69,18 @@ class Registry {
6269

6370
} // end anonymous namespace
6471

72+
void registerFunctionTestThunk(SwiftNativeFunctionTestThunk thunk) {
73+
Registry::get().registerFunctionTestThunk(thunk);
74+
}
75+
6576
FunctionTest::FunctionTest(StringRef name, Invocation invocation)
6677
: invocation(invocation), pass(nullptr), function(nullptr),
6778
dependencies(nullptr) {
6879
Registry::get().registerFunctionTest(this, name);
6980
}
70-
FunctionTest::FunctionTest(StringRef name, void *context,
71-
InvocationWithContext invocation)
72-
: invocation(std::make_pair(invocation, context)), pass(nullptr),
73-
function(nullptr), dependencies(nullptr) {
81+
FunctionTest::FunctionTest(StringRef name, NativeSwiftInvocation invocation)
82+
: invocation(invocation), pass(nullptr), function(nullptr),
83+
dependencies(nullptr) {
7484
Registry::get().registerFunctionTest(this, name);
7585
}
7686

@@ -84,11 +94,12 @@ void FunctionTest::run(SILFunction &function, Arguments &arguments,
8494
this->function = &function;
8595
this->dependencies = &dependencies;
8696
if (invocation.isa<Invocation>()) {
87-
auto fn = this->invocation.get<Invocation>();
97+
auto fn = invocation.get<Invocation>();
8898
fn(function, arguments, *this);
8999
} else {
90-
auto pair = invocation.get<std::pair<InvocationWithContext, void *>>();
91-
pair.first(function, arguments, *this, pair.second);
100+
auto *fn = invocation.get<NativeSwiftInvocation>();
101+
Registry::get().getFunctionTestThunk()(fn, {&function}, {&arguments},
102+
getContext());
92103
}
93104
this->pass = nullptr;
94105
this->function = nullptr;

0 commit comments

Comments
 (0)