//===--- 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 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 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 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(), ArrayRef(), 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(&II); if (!Apply) continue; auto *FRI = dyn_cast(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(), false /*NoThrow*/); Apply->replaceAllUsesWith(SILUndef::get(Apply->getType(), M)); Apply->eraseFromParent(); CausedError = true; return; } } } }; } // end anonymous namespace SILTransform *swift::createBugReducerTester() { return new BugReducerTester(); }