Skip to content

Commit dab8c14

Browse files
committed
[SwiftCompilerSources] Bridged in-IR testing.
Added the bridging types involved and the basic functionality.
1 parent 7c41103 commit dab8c14

File tree

8 files changed

+284
-8
lines changed

8 files changed

+284
-8
lines changed

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public func initializeSwiftModules() {
2020
registerSwiftAnalyses()
2121
registerSwiftPasses()
2222
initializeSwiftParseModules()
23+
registerSILTests()
2324
}
2425

2526
private func registerPass(

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_swift_compiler_module(SIL
2323
Registration.swift
2424
SmallProjectionPath.swift
2525
SubstitutionMap.swift
26+
Test.swift
2627
Type.swift
2728
Utils.swift
2829
Value.swift
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//===----------- Test.swift - In-IR tests from Swift source ---------------===//
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+
// TO ADD A NEW TEST, just add a new FunctionTest instance.
14+
// - In the source file containing the functionality you want to test:
15+
// let myNewTest =
16+
// FunctionTest("my_new_test") { function, arguments, context in
17+
// }
18+
// - In SwiftCompilerSources/Sources/SIL/Test.swift's registerSILTests function,
19+
// register the new test:
20+
// registerFunctionTest(myNewTest, implementation: { myNewTest.run($0, $1, $2) })
21+
//
22+
//===----------------------------------------------------------------------===//
23+
//
24+
// Provides a mechanism for writing tests against compiler code in the context
25+
// of a function. The goal is to get the same effect as calling a function and
26+
// checking its output.
27+
//
28+
// This is done via the specify_test instruction. Using one or more instances
29+
// of it in your test case's SIL function, you can specify which test (instance
30+
// of FunctionTest) should be run and what arguments should be provided to it.
31+
// The test grabs the arguments it expects out of the TestArguments instance
32+
// it is provided. It calls some function or functions. It then prints out
33+
// interesting results. These results can then be FileCheck'd.
34+
//
35+
// CASE STUDY:
36+
// Here's an example of how it works:
37+
// 0) A source file, NeatUtils.cpp contains
38+
//
39+
// fileprivate func myNeatoUtility(int: Int, value: Value, function: Function) { ... }
40+
//
41+
// and
42+
//
43+
// let myNeatoUtilityTest =
44+
// FunctionTest("my_neato_utility") { function, arguments, test in
45+
// // The code here is described in detail below.
46+
// // See 4).
47+
// let count = arguments.takeInt()
48+
// let target = arguments.takeValue()
49+
// let callee = arguments.takeFunction()
50+
// // See 5)
51+
// myNeatoUtility(int: count, value: target, function: callee)
52+
// // See 6)
53+
// print(function)
54+
// }
55+
// 1) A test test/SILOptimizer/interesting_functionality_unit.sil runs the
56+
// TestRunner pass:
57+
// // RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
58+
// 2) A function in interesting_functionality_unit.sil contains the
59+
// specify_test instruction.
60+
// sil @f : $() -> () {
61+
// ...
62+
// specify_test "my_neato_utility 43 %2 @function[other_fun]"
63+
// ...
64+
// }
65+
// 3) TestRunner finds the FunctionTest instance myNeatoUtilityTest registered
66+
// under the name "my_neato_utility", and calls run() on it, passing the
67+
// passing first the function, last the FunctionTest instance, AND in the
68+
// middle, most importantly a TestArguments instance that contains
69+
//
70+
// (43 : Int, someValue : Value, other_fun : Function)
71+
//
72+
// 4) myNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on
73+
// the test::Arguments instance.
74+
// let count = arguments.takeInt()
75+
// let target = arguments.takeValue()
76+
// let callee = arguments.takeFunction()
77+
// 5) myNeatoUtilityTest calls myNeatoUtility, passing these values along.
78+
// myNeatoUtility(int: count, value: target, function: callee)
79+
// 6) myNeatoUtilityTest then dumps out the current function, which was modified
80+
// in the process.
81+
// print(function)
82+
// 7) The test file test/SILOptimizer/interesting_functionality_unit.sil matches
83+
// the
84+
// expected contents of the modified function:
85+
// // CHECK-LABEL: sil @f
86+
// // CHECK-NOT: function_ref @other_fun
87+
//
88+
//===----------------------------------------------------------------------===//
89+
90+
import Basic
91+
import SILBridging
92+
93+
public struct FunctionTest {
94+
public var name: String
95+
public typealias FunctionTestImplementation = (Function, TestArguments, TestContext) -> ()
96+
private var implementation: FunctionTestImplementation
97+
init(name: String, implementation: @escaping FunctionTestImplementation) {
98+
self.name = name
99+
self.implementation = implementation
100+
}
101+
fileprivate func run(
102+
_ function: BridgedFunction,
103+
_ arguments: BridgedTestArguments,
104+
_ test: BridgedTestContext) {
105+
implementation(function.function, arguments.native, test.native)
106+
}
107+
}
108+
109+
public struct TestArguments {
110+
public var bridged: BridgedTestArguments
111+
fileprivate init(bridged: BridgedTestArguments) {
112+
self.bridged = bridged
113+
}
114+
115+
public var hasUntaken: Bool { bridged.hasUntaken() }
116+
public func takeString() -> StringRef { StringRef(bridged: bridged.takeString()) }
117+
public func takeBool() -> Bool { bridged.takeBool() }
118+
public func takeInt() -> Int { bridged.takeInt() }
119+
public func takeOperand() -> Operand { Operand(bridged: bridged.takeOperand()) }
120+
public func takeValue() -> Value { bridged.takeValue().value }
121+
public func takeInstruction() -> Instruction { bridged.takeInstruction().instruction }
122+
public func takeArgument() -> Argument { bridged.takeArgument().argument }
123+
public func takeBlock() -> BasicBlock { bridged.takeBlock().block }
124+
public func takeFunction() -> Function { bridged.takeFunction().function }
125+
}
126+
127+
extension BridgedTestArguments {
128+
public var native: TestArguments { TestArguments(bridged: self) }
129+
}
130+
131+
public struct TestContext {
132+
public var bridged: BridgedTestContext
133+
fileprivate init(bridged: BridgedTestContext) {
134+
self.bridged = bridged
135+
}
136+
}
137+
138+
extension BridgedTestContext {
139+
public var native: TestContext { TestContext(bridged: self) }
140+
}
141+
142+
private func registerFunctionTest(
143+
_ test: FunctionTest,
144+
implementation: BridgedFunctionTestThunk) {
145+
test.name._withStringRef { ref in
146+
registerFunctionTest(ref, implementation)
147+
}
148+
}
149+
150+
public func registerSILTests() {
151+
}

include/swift/SIL/SILBridging.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,50 @@ BridgedBasicBlock BridgedArgument::getParent() const {
15221522
return {getArgument()->getParent()};
15231523
}
15241524

1525+
namespace swift::test {
1526+
struct Arguments;
1527+
class FunctionTest;
1528+
} // namespace swift::test
1529+
1530+
struct BridgedTestArguments {
1531+
swift::test::Arguments *_Nonnull arguments;
1532+
1533+
bool hasUntaken() const;
1534+
1535+
SWIFT_IMPORT_UNSAFE
1536+
llvm::StringRef takeString() const;
1537+
1538+
bool takeBool() const;
1539+
1540+
SwiftInt takeInt() const;
1541+
1542+
SWIFT_IMPORT_UNSAFE
1543+
BridgedOperand takeOperand() const;
1544+
1545+
SWIFT_IMPORT_UNSAFE
1546+
BridgedValue takeValue() const;
1547+
1548+
SWIFT_IMPORT_UNSAFE
1549+
BridgedInstruction takeInstruction() const;
1550+
1551+
SWIFT_IMPORT_UNSAFE
1552+
BridgedArgument takeArgument() const;
1553+
1554+
SWIFT_IMPORT_UNSAFE
1555+
BridgedBasicBlock takeBlock() const;
1556+
1557+
SWIFT_IMPORT_UNSAFE
1558+
BridgedFunction takeFunction() const;
1559+
};
1560+
1561+
struct BridgedTestContext {
1562+
swift::SwiftPassInvocation *_Nonnull invocation;
1563+
};
1564+
1565+
typedef void (*_Nonnull BridgedFunctionTestThunk)(BridgedFunction,
1566+
BridgedTestArguments,
1567+
BridgedTestContext);
1568+
void registerFunctionTest(llvm::StringRef, BridgedFunctionTestThunk thunk);
15251569

15261570
SWIFT_END_NULLABILITY_ANNOTATIONS
15271571

include/swift/SIL/Test.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
#define SWIFT_SIL_TEST_H
9999

100100
#include "swift/SIL/ParseTestSpecification.h"
101+
#include "swift/SIL/SILBridging.h"
101102

102103
namespace swift {
103104

@@ -129,9 +130,12 @@ class FunctionTest final {
129130
/// values such as the results of analyses
130131
using Invocation = void (*)(SILFunction &, Arguments &, FunctionTest &);
131132

133+
using InvocationWithContext = void (*)(SILFunction &, Arguments &,
134+
FunctionTest &, void *);
135+
132136
private:
133137
/// The lambda to be run.
134-
Invocation invocation;
138+
TaggedUnion<Invocation, std::pair<InvocationWithContext, void *>> invocation;
135139

136140
public:
137141
/// Creates a test that will run \p invocation and stores it in the global
@@ -147,6 +151,8 @@ class FunctionTest final {
147151
/// } // end namespace swift::test
148152
FunctionTest(StringRef name, Invocation invocation);
149153

154+
FunctionTest(StringRef name, void *context, InvocationWithContext invocation);
155+
150156
/// Computes and returns the function's dominance tree.
151157
DominanceInfo *getDominanceInfo();
152158

@@ -161,6 +167,8 @@ class FunctionTest final {
161167
template <typename Analysis, typename Transform = SILFunctionTransform>
162168
Analysis *getAnalysis();
163169

170+
BridgedTestContext getContext();
171+
164172
//===----------------------------------------------------------------------===//
165173
//=== MARK: Implementation Details ===
166174
//===----------------------------------------------------------------------===//
@@ -239,6 +247,7 @@ class FunctionTest final {
239247
struct Dependencies {
240248
virtual DominanceInfo *getDominanceInfo() = 0;
241249
virtual SILPassManager *getPassManager() = 0;
250+
virtual SwiftPassInvocation *getInvocation() = 0;
242251
virtual ~Dependencies(){};
243252
};
244253

lib/SIL/Utils/SILBridging.cpp

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "swift/Basic/BridgingUtils.h"
13+
#include "swift/SIL/SILBridging.h"
1414
#include "swift/AST/Attr.h"
1515
#include "swift/AST/SemanticAttrs.h"
16-
#include "swift/SIL/SILNode.h"
16+
#include "swift/Basic/BridgingUtils.h"
1717
#include "swift/SIL/ApplySite.h"
18-
#include "swift/SIL/SILBridging.h"
19-
#include "swift/SIL/SILGlobalVariable.h"
20-
#include "swift/SIL/SILBuilder.h"
2118
#include "swift/SIL/MemAccessUtils.h"
19+
#include "swift/SIL/ParseTestSpecification.h"
20+
#include "swift/SIL/SILBuilder.h"
21+
#include "swift/SIL/SILGlobalVariable.h"
22+
#include "swift/SIL/SILNode.h"
23+
#include "swift/SIL/Test.h"
2224
#include <string>
2325

2426
using namespace swift;
@@ -127,6 +129,56 @@ void registerBridgedClass(StringRef className, SwiftMetatype metatype) {
127129
nodeMetatypes[(unsigned)kind] = metatype;
128130
}
129131

132+
//===----------------------------------------------------------------------===//
133+
// Test
134+
//===----------------------------------------------------------------------===//
135+
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+
});
144+
}
145+
146+
bool BridgedTestArguments::hasUntaken() const {
147+
return arguments->hasUntaken();
148+
}
149+
150+
llvm::StringRef BridgedTestArguments::takeString() const {
151+
return arguments->takeString();
152+
}
153+
154+
bool BridgedTestArguments::takeBool() const { return arguments->takeBool(); }
155+
156+
SwiftInt BridgedTestArguments::takeInt() const { return arguments->takeUInt(); }
157+
158+
BridgedOperand BridgedTestArguments::takeOperand() const {
159+
return {arguments->takeOperand()};
160+
}
161+
162+
BridgedValue BridgedTestArguments::takeValue() const {
163+
return {arguments->takeValue()};
164+
}
165+
166+
BridgedInstruction BridgedTestArguments::takeInstruction() const {
167+
return {arguments->takeInstruction()->asSILNode()};
168+
}
169+
170+
BridgedArgument BridgedTestArguments::takeArgument() const {
171+
return {arguments->takeBlockArgument()};
172+
}
173+
174+
BridgedBasicBlock BridgedTestArguments::takeBlock() const {
175+
return {arguments->takeBlock()};
176+
}
177+
178+
BridgedFunction BridgedTestArguments::takeFunction() const {
179+
return {arguments->takeFunction()};
180+
}
181+
130182
//===----------------------------------------------------------------------===//
131183
// SILFunction
132184
//===----------------------------------------------------------------------===//

lib/SIL/Utils/Test.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ FunctionTest::FunctionTest(StringRef name, Invocation invocation)
6767
dependencies(nullptr) {
6868
Registry::get().registerFunctionTest(this, name);
6969
}
70+
FunctionTest::FunctionTest(StringRef name, void *context,
71+
InvocationWithContext invocation)
72+
: invocation(std::make_pair(invocation, context)), pass(nullptr),
73+
function(nullptr), dependencies(nullptr) {
74+
Registry::get().registerFunctionTest(this, name);
75+
}
7076

7177
FunctionTest *FunctionTest::get(StringRef name) {
7278
return Registry::get().getFunctionTest(name);
@@ -77,7 +83,13 @@ void FunctionTest::run(SILFunction &function, Arguments &arguments,
7783
this->pass = &pass;
7884
this->function = &function;
7985
this->dependencies = &dependencies;
80-
invocation(function, arguments, *this);
86+
if (invocation.isa<Invocation>()) {
87+
auto fn = this->invocation.get<Invocation>();
88+
fn(function, arguments, *this);
89+
} else {
90+
auto pair = invocation.get<std::pair<InvocationWithContext, void *>>();
91+
pair.first(function, arguments, *this, pair.second);
92+
}
8193
this->pass = nullptr;
8294
this->function = nullptr;
8395
this->dependencies = nullptr;
@@ -90,3 +102,7 @@ DominanceInfo *FunctionTest::getDominanceInfo() {
90102
SILPassManager *FunctionTest::getPassManager() {
91103
return dependencies->getPassManager();
92104
}
105+
106+
BridgedTestContext FunctionTest::getContext() {
107+
return BridgedTestContext{dependencies->getInvocation()};
108+
}

lib/SILOptimizer/UtilityPasses/TestRunner.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,14 @@ class TestRunner : public SILFunctionTransform {
6060
: public test::FunctionTest::Dependencies {
6161
TestRunner *pass;
6262
SILFunction *function;
63+
SwiftPassInvocation invocation;
6364
FunctionTestDependenciesImpl(TestRunner *pass, SILFunction *function)
64-
: pass(pass), function(function) {}
65+
: pass(pass), function(function), invocation(pass->getPassManager()) {}
6566
DominanceInfo *getDominanceInfo() override {
6667
auto *dominanceAnalysis = pass->getAnalysis<DominanceAnalysis>();
6768
return dominanceAnalysis->get(function);
6869
}
70+
SwiftPassInvocation *getInvocation() override { return &invocation; }
6971
SILPassManager *getPassManager() override { return pass->getPassManager(); }
7072
~FunctionTestDependenciesImpl() {}
7173
};

0 commit comments

Comments
 (0)