blob: 4219ac246789a212a6e238ddf42fe0343f6d1f5f [file] [log] [blame]
//===--- BugReducerTester.cpp ---------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This pass is a testing pass for sil-bug-reducer. It asserts when it visits a
/// function that calls a function specified by an llvm::cl::opt.
///
//===----------------------------------------------------------------------===//
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLocation.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "llvm/Support/CommandLine.h"
using namespace swift;
static llvm::cl::opt<std::string> FunctionTarget(
"bug-reducer-tester-target-func",
llvm::cl::desc("Function that when called by an apply should cause "
"BugReducerTester to blow up or miscompile if the pass "
"visits the apply"));
namespace {
enum class FailureKind {
OptimizerCrasher,
RuntimeMiscompile,
RuntimeCrasher,
None
};
} // end anonymous namespace
static llvm::cl::opt<FailureKind> TargetFailureKind(
"bug-reducer-tester-failure-kind",
llvm::cl::desc("The type of failure to perform"),
llvm::cl::values(
clEnumValN(FailureKind::OptimizerCrasher, "opt-crasher",
"Crash the optimizer when we see the specified apply"),
clEnumValN(FailureKind::RuntimeMiscompile, "miscompile",
"Delete the target function call to cause a runtime "
"miscompile that is not a crasher"),
clEnumValN(FailureKind::RuntimeCrasher, "runtime-crasher",
"Delete the target function call to cause a runtime "
"miscompile that is not a crasher")),
llvm::cl::init(FailureKind::None));
LLVM_ATTRIBUTE_NOINLINE
void THIS_TEST_IS_EXPECTED_TO_CRASH_HERE() {
llvm_unreachable("Found the target!");
}
namespace {
class BugReducerTester : public SILFunctionTransform {
// We only want to cause 1 miscompile.
bool CausedError = false;
StringRef RuntimeCrasherFunctionName = "bug_reducer_runtime_crasher_func";
SILFunction *getRuntimeCrasherFunction() {
assert(TargetFailureKind == FailureKind::RuntimeCrasher);
llvm::SmallVector<SILResultInfo, 1> ResultInfoArray;
auto EmptyTupleCanType = getFunction()
->getModule()
.Types.getEmptyTupleType()
.getSwiftRValueType();
ResultInfoArray.push_back(
SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned));
auto FuncType = SILFunctionType::get(
nullptr, SILFunctionType::ExtInfo(SILFunctionType::Representation::Thin,
false /*isPseudoGeneric*/,
false /*noescape*/),
SILCoroutineKind::None, ParameterConvention::Direct_Unowned,
ArrayRef<SILParameterInfo>(), ArrayRef<SILYieldInfo>(),
ResultInfoArray, None, getFunction()->getModule().getASTContext());
SILFunction *F = getFunction()->getModule().getOrCreateSharedFunction(
RegularLocation::getAutoGeneratedLocation(), RuntimeCrasherFunctionName,
FuncType, IsBare, IsNotTransparent, IsSerialized, ProfileCounter(),
IsNotThunk);
if (F->isDefinition())
return F;
// Create a new block.
SILBasicBlock *BB = F->createBasicBlock();
// Insert a builtin int trap. Then return F.
SILBuilder B(BB);
B.createBuiltinTrap(RegularLocation::getAutoGeneratedLocation());
B.createUnreachable(ArtificialUnreachableLocation());
return F;
}
void run() override {
// If we don't have a target function or we already caused a miscompile,
// just return.
if (FunctionTarget.empty() || CausedError)
return;
assert(TargetFailureKind != FailureKind::None);
SILModule &M = getFunction()->getModule();
for (auto &BB : *getFunction()) {
for (auto &II : BB) {
auto *Apply = dyn_cast<ApplyInst>(&II);
if (!Apply)
continue;
auto *FRI = dyn_cast<FunctionRefInst>(Apply->getCallee());
if (!FRI ||
!FRI->getReferencedFunction()->getName().equals(FunctionTarget))
continue;
// Ok, we found the Apply that we want! If we are asked to crash, crash
// here.
if (TargetFailureKind == FailureKind::OptimizerCrasher)
THIS_TEST_IS_EXPECTED_TO_CRASH_HERE();
// Otherwise, if we are asked to perform a runtime time miscompile,
// delete the apply target.
if (TargetFailureKind == FailureKind::RuntimeMiscompile) {
Apply->replaceAllUsesWith(SILUndef::get(Apply->getType(), M));
Apply->eraseFromParent();
// Mark that we found the miscompile and return so we do not try to
// visit any more instructions in this function.
CausedError = true;
return;
}
assert(TargetFailureKind == FailureKind::RuntimeCrasher);
// Finally, if we reach this point we are being asked to replace the
// given apply with a new apply that calls the crasher func.
auto Loc = RegularLocation::getAutoGeneratedLocation();
SILFunction *RuntimeCrasherFunc = getRuntimeCrasherFunction();
llvm::dbgs() << "Runtime Crasher Func!\n";
RuntimeCrasherFunc->dump();
SILBuilder B(Apply->getIterator());
B.createApply(Loc, B.createFunctionRef(Loc, RuntimeCrasherFunc),
SubstitutionList(),
ArrayRef<SILValue>(), false /*NoThrow*/);
Apply->replaceAllUsesWith(SILUndef::get(Apply->getType(), M));
Apply->eraseFromParent();
CausedError = true;
return;
}
}
}
};
} // end anonymous namespace
SILTransform *swift::createBugReducerTester() { return new BugReducerTester(); }