|
| 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 | +} |
0 commit comments