| //===--- 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/SILOptimizer/Utils/SILOptFunctionBuilder.h" |
| #include "swift/SIL/SILInstruction.h" |
| #include "swift/SIL/ApplySite.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() |
| .getASTType(); |
| ResultInfoArray.push_back( |
| SILResultInfo(EmptyTupleCanType, ResultConvention::Unowned)); |
| auto FuncType = SILFunctionType::get( |
| nullptr, SILFunctionType::ExtInfo::getThin(), SILCoroutineKind::None, |
| ParameterConvention::Direct_Unowned, ArrayRef<SILParameterInfo>(), |
| ArrayRef<SILYieldInfo>(), ResultInfoArray, None, SubstitutionMap(), |
| SubstitutionMap(), getFunction()->getModule().getASTContext()); |
| |
| SILOptFunctionBuilder FunctionBuilder(*this); |
| SILFunction *F = FunctionBuilder.getOrCreateSharedFunction( |
| RegularLocation::getAutoGeneratedLocation(), RuntimeCrasherFunctionName, |
| FuncType, IsBare, IsNotTransparent, IsSerialized, ProfileCounter(), |
| IsNotThunk, IsNotDynamic); |
| 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); |
| for (auto &BB : *getFunction()) { |
| for (auto II = BB.begin(), IE = BB.end(); II != IE;) { |
| // Skip try_apply. We do not support them for now. |
| if (isa<TryApplyInst>(&*II)) { |
| ++II; |
| continue; |
| } |
| |
| auto FAS = FullApplySite::isa(&*II); |
| if (!FAS) { |
| ++II; |
| continue; |
| } |
| |
| auto *FRI = dyn_cast<FunctionRefInst>(FAS.getCallee()); |
| if (!FRI || !FRI->getInitiallyReferencedFunction()->getName().equals( |
| FunctionTarget)) { |
| ++II; |
| 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) { |
| // Ok, we need to insert a runtime miscompile. Move II to |
| // the next instruction and then replace its current value |
| // with undef. |
| auto *Inst = cast<SingleValueInstruction>(&*II); |
| Inst->replaceAllUsesWith(SILUndef::get(Inst->getType(), *getFunction())); |
| Inst->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(II); |
| B.createApply(Loc, B.createFunctionRef(Loc, RuntimeCrasherFunc), |
| SubstitutionMap(), |
| ArrayRef<SILValue>()); |
| |
| auto *Inst = cast<SingleValueInstruction>(&*II); |
| ++II; |
| Inst->replaceAllUsesWith(SILUndef::get(Inst->getType(), *getFunction())); |
| Inst->eraseFromParent(); |
| |
| CausedError = true; |
| return; |
| } |
| } |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| SILTransform *swift::createBugReducerTester() { return new BugReducerTester(); } |