Skip to content

[Test] Moved SIL function tests next to tested code. #67069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4374b6b
[Test] Moved test_spec parsing to SIL.
nate-chandler Jun 29, 2023
e9ab3b4
[Test] Refactored SIL "unit" tests.
nate-chandler Jun 29, 2023
2e31790
[Test] Ensourced has-pointer-escape.
nate-chandler Jun 30, 2023
044625c
[Test] Ensourced lexical-destroy-folding.
nate-chandler Jun 30, 2023
3343ab9
[Test] Ensourced pruned-livenes-boundary...
nate-chandler Jun 30, 2023
86769af
[NFC] Included header with referenced type.
nate-chandler Jun 30, 2023
524c676
[Test] Ensourced print-type-lowering.
nate-chandler Jun 30, 2023
ef02dc2
[Test] Ensourced ssa-liveness.
nate-chandler Jun 30, 2023
303664c
[Test] Ensourced scoped-address-liveness.
nate-chandler Jun 30, 2023
f996371
[Test] Ensourced multidef-liveness.
nate-chandler Jun 30, 2023
fcec328
[Test] Ensourced multidefuse-liveness.
nate-chandler Jun 30, 2023
c0783aa
[Test] Ensourced canonicalize-ossa-lifetime.
nate-chandler Jun 30, 2023
ec120e4
[Test] Ensourced canonicalize-borrow-scope.
nate-chandler Jun 30, 2023
ee95732
[Test] Ensourced is-deinit-barrier.
nate-chandler Jun 30, 2023
ad9fee2
[Test] Ensourced shrink-borrow-scope.
nate-chandler Jun 30, 2023
bddc946
[Test] Ensourced is-lexical.
nate-chandler Jun 30, 2023
a3db694
[Test] Ensourced visit-inner-adjacent-phis.
nate-chandler Jun 30, 2023
8f0378c
[Test] Ensourced find-enclosing-defs.
nate-chandler Jun 30, 2023
9417651
[Test] Ensourced find-borrow-introducers.
nate-chandler Jun 30, 2023
84e42fd
[Test] Ensourced linear-liveness.
nate-chandler Jun 30, 2023
1cd2632
[Test] Ensourced interior-liveness.
nate-chandler Jun 30, 2023
906a4b9
[Test] Ensourced extended-liveness.
nate-chandler Jun 30, 2023
50f2a02
[Test] Ensourced ossa-lifetime-completion.
nate-chandler Jun 30, 2023
bc909e5
[Test] Ensourced simplify-cfg-simplify-argument.
nate-chandler Jun 30, 2023
df02726
[Test] Ensourced simplify-cfg-simplify-block-args.
nate-chandler Jun 30, 2023
bf229c3
[Test] Ensourced simplify-cfg-canonicalize....
nate-chandler Jun 30, 2023
49bc5a6
[Test] Ensourced simplify-cfg-simplify-switch....
nate-chandler Jun 30, 2023
d57b1e5
[Test] Ensourced simplify-cfg-simplify-switch....
nate-chandler Jun 30, 2023
736b1af
[Test] Ensourced simplify-cfg-simplify-switch....
nate-chandler Jun 30, 2023
f1d61af
[Test] Ensourced simplify-cfg-simplify....
nate-chandler Jun 30, 2023
b9b286e
[Test] Ensourced simplify-cfg-try-jump-threading.
nate-chandler Jun 30, 2023
0c17600
[Test] Ensourced accesspath-base.
nate-chandler Jun 30, 2023
22df319
[Test] Ensourced fieldsensitive-multidefuse...
nate-chandler Jun 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/ABI/ProtocolDispatchStrategy.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define SWIFT_ABI_PROTOCOLDISPATCHSTRATEGY_H

#include "swift/Basic/Unreachable.h"
#include <cstdint>

namespace swift {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//
//
// This file defines Test::Argument.
// This file defines test::Argument.
//
//===----------------------------------------------------------------------===//

Expand Down
284 changes: 284 additions & 0 deletions include/swift/SIL/Test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
//===---- Test.h - Testing based on test_specification insts -*- C++ ----*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// TO ADD A NEW TEST, just add a new FunctionTest instance.
// - In the source file containing the functionality you want to test.
// - #include "swift/SIL/Test.h"
// - namespace swift::test {
// static FunctionTest MyNewTest(
// "my-new-test",
// [](auto &function, auto &arguments, auto &test) {
// });
// } // end namespace swift::test
//
//===----------------------------------------------------------------------===//
//
// Provides a mechanism for writing tests against compiler code in the context
// of a function. The goal is to get the same effect as calling a function and
// checking its output.
//
// This is done via the test_specification instruction. Using one or more
// instances of it in your test case's SIL function, you can specify which test
// (instance of FunctionTest) should be run and what arguments should be
// provided to it. The test grabs the arguments it expects out of the
// test::Arguments instance it is provided. It calls some function or
// functions. It then prints out interesting results. These results can then
// be FileCheck'd.
//
// CASE STUDY:
// Here's an example of how it works:
// 0) A source file, NeatUtils.cpp contains
//
// static void myNeatoUtility(unsigned, SILValue, SILFunction *) { ... }
//
// and
//
// static FunctionTest MyNeatoUtilityTest(
// "my-neato-utility",
// [](auto *test, auto *function, auto &arguments) {
// // The code here is described in detail below.
// // See 4).
// auto count = arguments.takeUInt();
// auto target = arguments.takeValue();
// auto callee = arguments.takeFunction();
// // See 5)
// myNeatoUtility(count, target, callee);
// // See 6)
// getFunction()->dump();
// });
// 1) A test test/SILOptimizer/interesting_functionality_unit.sil runs the
// TestRunner pass:
// // RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
// 2) A function in interesting_functionality_unit.sil contains the
// test_specification instruction.
// sil @f : $() -> () {
// ...
// test_specification "my-neato-utility 43 @trace[2] @function[other_fun]"
// ...
// }
// 3) TestRunner finds the FunctionTest instance MyNeatoUtilityTest registered
// under the name "my-neato-utility", and calls ::run() on it, passing an
// the pass, the function AND most importantly an test::Arguments instance
// that contains
// (43 : unsigned long, someValue : SILValue, other_fun : SILFunction *)
//
// 4) MyNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on
// the test::Arguments instance.
// auto count = arguments.takeUInt();
// auto target = arguments.takeValue();
// auto callee = arguments.takeFunction();
// WARNING: Don't call more than one of these in a single expression! The
// order of evaluation is implementation defined, but the values
// must be taken out of the arguments in the order they appear in
// the test.
// 5) MyNeatoUtilityTest calls myNeatoUtility, passing these values along.
// myNeatoUtility(count, target, callee);
// 6) MyNeatoUtilityTest then dumps out the current function, which was modified
// in the process.
// getFunction()->dump();
// 7) The test file test/SILOptimizer/interesting_functionality_unit.sil matches
// the
// expected contents of the modified function:
// // CHECK-LABEL: sil @f
// // CHECK-NOT: function_ref @other_fun
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/ParseTestSpecification.h"
#include "llvm/ADT/STLFunctionalExtras.h"

namespace swift {

class SILFunction;
class SILFunctionTransform;
class SILPassManager;
class DominanceAnalysis;
class DominanceInfo;

namespace test {

struct Arguments;
class TestRunner;

/// A test that is run when the corresponding test_specification instruction is
/// processed by the TestRunner pass.
///
/// Tests are instantiated once at program launch. At that time, they are
/// stored in a registry name -> test. When an test_specification instruction
/// naming a particular test is processed, that test is run.
class FunctionTest final {
public:
/// Wraps a test lambda.
///
/// There are three arguments in order of priority:
/// - SILFunction & - the function with the test_specification instruction
/// - Arguments & - the resolved list of args specified by the instruction
/// - FunctionTest & - the test being run; used to find less commonly used
/// values such as the results of analyses
using Invocation =
llvm::function_ref<void(SILFunction &, Arguments &, FunctionTest &)>;

private:
/// The lambda to be run.
Invocation invocation;

public:
/// Creates a test that will run \p invocation and stores it in the global
/// registry.
///
/// To create a new test, just write
///
/// namespace swift::test {
/// static FunctionTest myTest("my-test", [](
/// SILFunction &function, Arguments &arguments, FunctionTest &test){
/// // test code
/// });
/// } // end namespace swift::test
FunctionTest(StringRef name, Invocation invocation);

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

/// Returns the active pass manager.
SILPassManager *getPassManager();

/// Returns the indicated analysis.
///
/// NOTE: This function can only be called from files that can import
/// SILFunctionTransform--those in SILOptimizer and libraries that
/// depend on it. See `Layering` below for more details.
template <typename Analysis, typename Transform = SILFunctionTransform>
Analysis *getAnalysis();

//===----------------------------------------------------------------------===//
//=== MARK: Implementation Details ===
//===----------------------------------------------------------------------===//
//
// To read, write, and debug function test failures, see above.
//
// The following implementation details have to do with executing these tests
// and handling library layering.

// TestRunner interface:
private:
struct Dependencies;

/// Run the stored \p invocation lambda.
void run(SILFunction &function, Arguments &arguments,
SILFunctionTransform &pass, Dependencies &dependencies);

/// Retrieve the test with named \p name from the global registry.
static FunctionTest *get(StringRef name);

/// The instance of the TestRunner pass currently running this test. Only
/// non-null when FunctionTest::run is executing.
SILFunctionTransform *pass;
/// The function which the TestRunner pass is currently processing. Only
/// non-null when FunctionTest::run is executing.
SILFunction *function;

friend class TestRunner;

// Layering:
//
// Dealing with the differences between the SIL and SILOptimizer libraries.
//
// Motivation: 1) Enable writing FunctionTests inline in any source file that
// can import the SIL library's headers.
// 2) Allow tests to access the tools that are visible in the source
// file where they are written.
//
// Two examples:
// A) Tests in the SIL library (or libraries that can import its headers) must
// be able to access the results of analyses, e.g. DominanceInfo.
// B) Tests in the SILOptimizer library (or libraries that can import its
// headers) must be able to access analyses themselves, e.g.
// DominanceAnalysis.
//
// Because analyses aren't part of the SIL library, the code that computes such
// a result can't called from one of it's source files. For example, this
// isn't possible in some SIL library cpp file
//
// test->getPass()->getAnalysis<DominanceAnalysis>()->get(function);
//
// because DominanceAnalysis isn't visible in SIL (it's defined in
// SILOptimizer). Indeed calling anything on getPass() isn't possible because
// SILFunctionTransform isn't visible in SIL (again, defined in SILOptimizer).
//
// This is further exacerbated by the fact that getting an analysis from a pass
// requires instantiating a template function.
//
// There are two consequences:
//
// 1) Dependencies must be provided directly (i.e. not via analyses) to tests
// in the SIL library.
//
// For example ::getDominanceInfo in `struct Dependencies` below.
//
// 2) The code that allows tests in the SILOptimizer library to access analyses
// must not be instantiated when it's imported into the SIL library.
//
// Concretely, the "extra" template argument `typename Transform` on
// getAnalysis and the thunk in the impl:: namespace below.
private:
/// Functions for getting tools that are visible in the SIL library.
///
/// The implementation is provided in the SILOptimizer libary where analyses
/// are visible: TestRunner::FunctionTestDependenciesImpl.
struct Dependencies {
virtual DominanceInfo *getDominanceInfo() = 0;
virtual SILPassManager *getPassManager() = 0;
virtual ~Dependencies(){};
};

/// The vendor for dependencies provided to the test by TestRunner. Only
/// non-null when FunctionTest::run is executing.
Dependencies *dependencies;
};

/// Thunks for delaying template instantiation.
///
/// Because C++ lacks "runtime concepts" (Swift's protocols), it's not possible
/// for the Dependencies struct to have a method like
///
/// template <typename Analysis>
/// virtual Analysis *getAnalysis() = 0;
///
/// In order to give tests access to analyses, SILFunctionTransform::getAnalysis
/// template must be instantiated by the test code. This could be done by
/// making the test-running pass available directly to test code. Here,
/// instead, it is done by providing a passthrough on test. But this is
/// complicated by library layering:
///
/// Calling methods directly on `FunctionTest::pass` is illegal from the SIL
/// library (or any library that can't import SILOptimizer headers, where
/// SILFunctionTransform is defined):
///
/// member access into incomplete type 'swift::SILFunctionTransform'
///
/// Instead, these thunks are called with the pass as an argument. Because the
/// static type of pass in these thunks is just "pointer to `typename
/// Transform`", it's legal to call methods on the pass here: these templates
/// can only be instantiated in libraries that can import SILOptimizer.
namespace impl {
template <typename Analysis, typename Transform>
Analysis *getAnalysisFromTransform(Transform *pass) {
return pass->template getAnalysis<Analysis>();
}
} // end namespace impl

template <typename Analysis, typename Transform>
Analysis *FunctionTest::getAnalysis() {
return impl::getAnalysisFromTransform<Analysis, Transform>(pass);
}
} // namespace test
} // namespace swift
2 changes: 1 addition & 1 deletion include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-block
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
PASS(SerializeSILPass, "serialize-sil",
"Utility pass. Serializes the current SILModule")
PASS(UnitTestRunner, "unit-test-runner",
PASS(UnitTestRunner, "test-runner",
"Utility pass. Parses arguments and runs code with them.")
PASS(YieldOnceCheck, "yield-once-check",
"Check correct usage of yields in yield-once coroutines")
Expand Down
1 change: 0 additions & 1 deletion include/swift/SILOptimizer/PassManager/Transforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ namespace swift {
/// Get the transform's name as a C++ identifier.
llvm::StringRef getID() { return PassKindID(getPassKind()); }

protected:
/// Searches for an analysis of type T in the list of registered
/// analysis. If the analysis is not found, the program terminates.
template<typename T>
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SILOptimizer/Transforms/SimplifyCFG.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"

namespace swift {
class DominanceAnalysis;
}

using namespace swift;

struct ThreadInfo;
Expand Down
18 changes: 18 additions & 0 deletions lib/SIL/IR/SILValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/Test.h"
#include "llvm/ADT/StringSwitch.h"

using namespace swift;
Expand Down Expand Up @@ -154,6 +155,23 @@ bool ValueBase::isLexical() const {
return false;
}

namespace swift::test {
// Arguments:
// - value
// Dumps:
// - value
// - whether it's lexical
static FunctionTest IsLexicalTest("is-lexical", [](auto &function,
auto &arguments,
auto &test) {
auto value = arguments.takeValue();
auto isLexical = value->isLexical();
value->dump();
auto *boolString = isLexical ? "true" : "false";
llvm::errs() << boolString << "\n";
});
} // end namespace swift::test

bool ValueBase::isGuaranteedForwarding() const {
if (getOwnershipKind() != OwnershipKind::Guaranteed) {
return false;
Expand Down
Loading