| //===- CrashDebugger.cpp - Debug compilation crashes ----------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines the bugpoint internals that narrow down compilation crashes |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "BugDriver.h" |
| #include "ListReducer.h" |
| #include "ToolRunner.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/Analysis/TargetTransformInfo.h" |
| #include "llvm/Transforms/Utils/Local.h" |
| #include "llvm/IR/CFG.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/LegacyPassManager.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/IR/ValueSymbolTable.h" |
| #include "llvm/IR/Verifier.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/FileUtilities.h" |
| #include "llvm/Transforms/Scalar.h" |
| #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
| #include "llvm/Transforms/Utils/Cloning.h" |
| #include <set> |
| using namespace llvm; |
| |
| namespace { |
| cl::opt<bool> KeepMain("keep-main", |
| cl::desc("Force function reduction to keep main"), |
| cl::init(false)); |
| cl::opt<bool> NoGlobalRM("disable-global-remove", |
| cl::desc("Do not remove global variables"), |
| cl::init(false)); |
| |
| cl::opt<bool> ReplaceFuncsWithNull( |
| "replace-funcs-with-null", |
| cl::desc("When stubbing functions, replace all uses will null"), |
| cl::init(false)); |
| cl::opt<bool> DontReducePassList("disable-pass-list-reduction", |
| cl::desc("Skip pass list reduction steps"), |
| cl::init(false)); |
| |
| cl::opt<bool> NoNamedMDRM("disable-namedmd-remove", |
| cl::desc("Do not remove global named metadata"), |
| cl::init(false)); |
| cl::opt<bool> NoStripDebugInfo("disable-strip-debuginfo", |
| cl::desc("Do not strip debug info metadata"), |
| cl::init(false)); |
| cl::opt<bool> NoStripDebugTypeInfo("disable-strip-debug-types", |
| cl::desc("Do not strip debug type info metadata"), |
| cl::init(false)); |
| cl::opt<bool> VerboseErrors("verbose-errors", |
| cl::desc("Print the output of crashing program"), |
| cl::init(false)); |
| } |
| |
| namespace llvm { |
| class ReducePassList : public ListReducer<std::string> { |
| BugDriver &BD; |
| |
| public: |
| ReducePassList(BugDriver &bd) : BD(bd) {} |
| |
| // Return true iff running the "removed" passes succeeds, and running the |
| // "Kept" passes fail when run on the output of the "removed" passes. If we |
| // return true, we update the current module of bugpoint. |
| Expected<TestResult> doTest(std::vector<std::string> &Removed, |
| std::vector<std::string> &Kept) override; |
| }; |
| } |
| |
| Expected<ReducePassList::TestResult> |
| ReducePassList::doTest(std::vector<std::string> &Prefix, |
| std::vector<std::string> &Suffix) { |
| std::string PrefixOutput; |
| std::unique_ptr<Module> OrigProgram; |
| if (!Prefix.empty()) { |
| outs() << "Checking to see if these passes crash: " |
| << getPassesString(Prefix) << ": "; |
| if (BD.runPasses(BD.getProgram(), Prefix, PrefixOutput)) |
| return KeepPrefix; |
| |
| OrigProgram = std::move(BD.Program); |
| |
| BD.Program = parseInputFile(PrefixOutput, BD.getContext()); |
| if (BD.Program == nullptr) { |
| errs() << BD.getToolName() << ": Error reading bitcode file '" |
| << PrefixOutput << "'!\n"; |
| exit(1); |
| } |
| sys::fs::remove(PrefixOutput); |
| } |
| |
| outs() << "Checking to see if these passes crash: " << getPassesString(Suffix) |
| << ": "; |
| |
| if (BD.runPasses(BD.getProgram(), Suffix)) |
| return KeepSuffix; // The suffix crashes alone... |
| |
| // Nothing failed, restore state... |
| if (OrigProgram) |
| BD.Program = std::move(OrigProgram); |
| return NoFailure; |
| } |
| |
| using BugTester = bool (*)(const BugDriver &, Module *); |
| |
| namespace { |
| /// ReduceCrashingGlobalInitializers - This works by removing global variable |
| /// initializers and seeing if the program still crashes. If it does, then we |
| /// keep that program and try again. |
| class ReduceCrashingGlobalInitializers : public ListReducer<GlobalVariable *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingGlobalInitializers(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<GlobalVariable *> &Prefix, |
| std::vector<GlobalVariable *> &Kept) override { |
| if (!Kept.empty() && TestGlobalVariables(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestGlobalVariables(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestGlobalVariables(std::vector<GlobalVariable *> &GVs); |
| }; |
| } |
| |
| bool ReduceCrashingGlobalInitializers::TestGlobalVariables( |
| std::vector<GlobalVariable *> &GVs) { |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| std::set<GlobalVariable *> GVSet; |
| |
| for (unsigned i = 0, e = GVs.size(); i != e; ++i) { |
| GlobalVariable *CMGV = cast<GlobalVariable>(VMap[GVs[i]]); |
| assert(CMGV && "Global Variable not in module?!"); |
| GVSet.insert(CMGV); |
| } |
| |
| outs() << "Checking for crash with only these global variables: "; |
| PrintGlobalVariableList(GVs); |
| outs() << ": "; |
| |
| // Loop over and delete any global variables which we aren't supposed to be |
| // playing with... |
| for (GlobalVariable &I : M->globals()) |
| if (I.hasInitializer() && !GVSet.count(&I)) { |
| DeleteGlobalInitializer(&I); |
| I.setLinkage(GlobalValue::ExternalLinkage); |
| I.setComdat(nullptr); |
| } |
| |
| // Try running the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use global variable pointers that point into the now-current |
| // module. |
| GVs.assign(GVSet.begin(), GVSet.end()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| namespace { |
| /// ReduceCrashingFunctions reducer - This works by removing functions and |
| /// seeing if the program still crashes. If it does, then keep the newer, |
| /// smaller program. |
| /// |
| class ReduceCrashingFunctions : public ListReducer<Function *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingFunctions(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<Function *> &Prefix, |
| std::vector<Function *> &Kept) override { |
| if (!Kept.empty() && TestFuncs(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestFuncs(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestFuncs(std::vector<Function *> &Prefix); |
| }; |
| } |
| |
| static void RemoveFunctionReferences(Module *M, const char *Name) { |
| auto *UsedVar = M->getGlobalVariable(Name, true); |
| if (!UsedVar || !UsedVar->hasInitializer()) |
| return; |
| if (isa<ConstantAggregateZero>(UsedVar->getInitializer())) { |
| assert(UsedVar->use_empty()); |
| UsedVar->eraseFromParent(); |
| return; |
| } |
| auto *OldUsedVal = cast<ConstantArray>(UsedVar->getInitializer()); |
| std::vector<Constant *> Used; |
| for (Value *V : OldUsedVal->operand_values()) { |
| Constant *Op = cast<Constant>(V->stripPointerCasts()); |
| if (!Op->isNullValue()) { |
| Used.push_back(cast<Constant>(V)); |
| } |
| } |
| auto *NewValElemTy = OldUsedVal->getType()->getElementType(); |
| auto *NewValTy = ArrayType::get(NewValElemTy, Used.size()); |
| auto *NewUsedVal = ConstantArray::get(NewValTy, Used); |
| UsedVar->mutateType(NewUsedVal->getType()->getPointerTo()); |
| UsedVar->setInitializer(NewUsedVal); |
| } |
| |
| bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) { |
| // If main isn't present, claim there is no problem. |
| if (KeepMain && !is_contained(Funcs, BD.getProgram().getFunction("main"))) |
| return false; |
| |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| std::set<Function *> Functions; |
| for (unsigned i = 0, e = Funcs.size(); i != e; ++i) { |
| Function *CMF = cast<Function>(VMap[Funcs[i]]); |
| assert(CMF && "Function not in module?!"); |
| assert(CMF->getFunctionType() == Funcs[i]->getFunctionType() && "wrong ty"); |
| assert(CMF->getName() == Funcs[i]->getName() && "wrong name"); |
| Functions.insert(CMF); |
| } |
| |
| outs() << "Checking for crash with only these functions: "; |
| PrintFunctionList(Funcs); |
| outs() << ": "; |
| if (!ReplaceFuncsWithNull) { |
| // Loop over and delete any functions which we aren't supposed to be playing |
| // with... |
| for (Function &I : *M) |
| if (!I.isDeclaration() && !Functions.count(&I)) |
| DeleteFunctionBody(&I); |
| } else { |
| std::vector<GlobalValue *> ToRemove; |
| // First, remove aliases to functions we're about to purge. |
| for (GlobalAlias &Alias : M->aliases()) { |
| GlobalObject *Root = Alias.getBaseObject(); |
| Function *F = dyn_cast_or_null<Function>(Root); |
| if (F) { |
| if (Functions.count(F)) |
| // We're keeping this function. |
| continue; |
| } else if (Root->isNullValue()) { |
| // This referenced a globalalias that we've already replaced, |
| // so we still need to replace this alias. |
| } else if (!F) { |
| // Not a function, therefore not something we mess with. |
| continue; |
| } |
| |
| PointerType *Ty = cast<PointerType>(Alias.getType()); |
| Constant *Replacement = ConstantPointerNull::get(Ty); |
| Alias.replaceAllUsesWith(Replacement); |
| ToRemove.push_back(&Alias); |
| } |
| |
| for (Function &I : *M) { |
| if (!I.isDeclaration() && !Functions.count(&I)) { |
| PointerType *Ty = cast<PointerType>(I.getType()); |
| Constant *Replacement = ConstantPointerNull::get(Ty); |
| I.replaceAllUsesWith(Replacement); |
| ToRemove.push_back(&I); |
| } |
| } |
| |
| for (auto *F : ToRemove) { |
| F->eraseFromParent(); |
| } |
| |
| // Finally, remove any null members from any global intrinsic. |
| RemoveFunctionReferences(M.get(), "llvm.used"); |
| RemoveFunctionReferences(M.get(), "llvm.compiler.used"); |
| } |
| // Try running the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use function pointers that point into the now-current |
| // module. |
| Funcs.assign(Functions.begin(), Functions.end()); |
| return true; |
| } |
| return false; |
| } |
| |
| namespace { |
| /// Simplify the CFG without completely destroying it. |
| /// This is not well defined, but basically comes down to "try to eliminate |
| /// unreachable blocks and constant fold terminators without deciding that |
| /// certain undefined behavior cuts off the program at the legs". |
| void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) { |
| if (F.empty()) |
| return; |
| |
| for (auto *BB : BBs) { |
| ConstantFoldTerminator(BB); |
| MergeBlockIntoPredecessor(BB); |
| } |
| |
| // Remove unreachable blocks |
| // removeUnreachableBlocks can't be used here, it will turn various |
| // undefined behavior into unreachables, but bugpoint was the thing that |
| // generated the undefined behavior, and we don't want it to kill the entire |
| // program. |
| SmallPtrSet<BasicBlock *, 16> Visited; |
| for (auto *BB : depth_first(&F.getEntryBlock())) |
| Visited.insert(BB); |
| |
| SmallVector<BasicBlock *, 16> Unreachable; |
| for (auto &BB : F) |
| if (!Visited.count(&BB)) |
| Unreachable.push_back(&BB); |
| |
| // The dead BB's may be in a dead cycle or otherwise have references to each |
| // other. Because of this, we have to drop all references first, then delete |
| // them all at once. |
| for (auto *BB : Unreachable) { |
| for (BasicBlock *Successor : successors(&*BB)) |
| if (Visited.count(Successor)) |
| Successor->removePredecessor(&*BB); |
| BB->dropAllReferences(); |
| } |
| for (auto *BB : Unreachable) |
| BB->eraseFromParent(); |
| } |
| /// ReduceCrashingBlocks reducer - This works by setting the terminators of |
| /// all terminators except the specified basic blocks to a 'ret' instruction, |
| /// then running the simplify-cfg pass. This has the effect of chopping up |
| /// the CFG really fast which can reduce large functions quickly. |
| /// |
| class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingBlocks(BugDriver &BD, BugTester testFn) |
| : BD(BD), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, |
| std::vector<const BasicBlock *> &Kept) override { |
| if (!Kept.empty() && TestBlocks(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestBlocks(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestBlocks(std::vector<const BasicBlock *> &Prefix); |
| }; |
| } |
| |
| bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) { |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| SmallPtrSet<BasicBlock *, 8> Blocks; |
| for (unsigned i = 0, e = BBs.size(); i != e; ++i) |
| Blocks.insert(cast<BasicBlock>(VMap[BBs[i]])); |
| |
| outs() << "Checking for crash with only these blocks:"; |
| unsigned NumPrint = Blocks.size(); |
| if (NumPrint > 10) |
| NumPrint = 10; |
| for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| outs() << " " << BBs[i]->getName(); |
| if (NumPrint < Blocks.size()) |
| outs() << "... <" << Blocks.size() << " total>"; |
| outs() << ": "; |
| |
| // Loop over and delete any hack up any blocks that are not listed... |
| for (Function &F : M->functions()) { |
| for (BasicBlock &BB : F) { |
| if (!Blocks.count(&BB) && BB.getTerminator()->getNumSuccessors()) { |
| // Loop over all of the successors of this block, deleting any PHI nodes |
| // that might include it. |
| for (BasicBlock *Succ : successors(&BB)) |
| Succ->removePredecessor(&BB); |
| |
| TerminatorInst *BBTerm = BB.getTerminator(); |
| if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy()) |
| continue; |
| if (!BBTerm->getType()->isVoidTy()) |
| BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType())); |
| |
| // Replace the old terminator instruction. |
| BB.getInstList().pop_back(); |
| new UnreachableInst(BB.getContext(), &BB); |
| } |
| } |
| } |
| |
| // The CFG Simplifier pass may delete one of the basic blocks we are |
| // interested in. If it does we need to take the block out of the list. Make |
| // a "persistent mapping" by turning basic blocks into <function, name> pairs. |
| // This won't work well if blocks are unnamed, but that is just the risk we |
| // have to take. FIXME: Can we just name the blocks? |
| std::vector<std::pair<std::string, std::string>> BlockInfo; |
| |
| for (BasicBlock *BB : Blocks) |
| BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); |
| |
| SmallVector<BasicBlock *, 16> ToProcess; |
| for (auto &F : *M) { |
| for (auto &BB : F) |
| if (!Blocks.count(&BB)) |
| ToProcess.push_back(&BB); |
| simpleSimplifyCfg(F, ToProcess); |
| ToProcess.clear(); |
| } |
| // Verify we didn't break anything |
| std::vector<std::string> Passes; |
| Passes.push_back("verify"); |
| std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes); |
| if (!New) { |
| errs() << "verify failed!\n"; |
| exit(1); |
| } |
| M = std::move(New); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use basic block pointers that point into the now-current |
| // module, and that they don't include any deleted blocks. |
| BBs.clear(); |
| const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable(); |
| for (const auto &BI : BlockInfo) { |
| Function *F = cast<Function>(GST.lookup(BI.first)); |
| Value *V = F->getValueSymbolTable()->lookup(BI.second); |
| if (V && V->getType() == Type::getLabelTy(V->getContext())) |
| BBs.push_back(cast<BasicBlock>(V)); |
| } |
| return true; |
| } |
| // It didn't crash, try something else. |
| return false; |
| } |
| |
| namespace { |
| /// ReduceCrashingConditionals reducer - This works by changing |
| /// conditional branches to unconditional ones, then simplifying the CFG |
| /// This has the effect of chopping up the CFG really fast which can reduce |
| /// large functions quickly. |
| /// |
| class ReduceCrashingConditionals : public ListReducer<const BasicBlock *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| bool Direction; |
| |
| public: |
| ReduceCrashingConditionals(BugDriver &bd, BugTester testFn, bool Direction) |
| : BD(bd), TestFn(testFn), Direction(Direction) {} |
| |
| Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, |
| std::vector<const BasicBlock *> &Kept) override { |
| if (!Kept.empty() && TestBlocks(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestBlocks(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestBlocks(std::vector<const BasicBlock *> &Prefix); |
| }; |
| } |
| |
| bool ReduceCrashingConditionals::TestBlocks( |
| std::vector<const BasicBlock *> &BBs) { |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| SmallPtrSet<const BasicBlock *, 8> Blocks; |
| for (const auto *BB : BBs) |
| Blocks.insert(cast<BasicBlock>(VMap[BB])); |
| |
| outs() << "Checking for crash with changing conditionals to always jump to " |
| << (Direction ? "true" : "false") << ":"; |
| unsigned NumPrint = Blocks.size(); |
| if (NumPrint > 10) |
| NumPrint = 10; |
| for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| outs() << " " << BBs[i]->getName(); |
| if (NumPrint < Blocks.size()) |
| outs() << "... <" << Blocks.size() << " total>"; |
| outs() << ": "; |
| |
| // Loop over and delete any hack up any blocks that are not listed... |
| for (auto &F : *M) |
| for (auto &BB : F) |
| if (!Blocks.count(&BB)) { |
| auto *BR = dyn_cast<BranchInst>(BB.getTerminator()); |
| if (!BR || !BR->isConditional()) |
| continue; |
| if (Direction) |
| BR->setCondition(ConstantInt::getTrue(BR->getContext())); |
| else |
| BR->setCondition(ConstantInt::getFalse(BR->getContext())); |
| } |
| |
| // The following may destroy some blocks, so we save them first |
| std::vector<std::pair<std::string, std::string>> BlockInfo; |
| |
| for (const BasicBlock *BB : Blocks) |
| BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); |
| |
| SmallVector<BasicBlock *, 16> ToProcess; |
| for (auto &F : *M) { |
| for (auto &BB : F) |
| if (!Blocks.count(&BB)) |
| ToProcess.push_back(&BB); |
| simpleSimplifyCfg(F, ToProcess); |
| ToProcess.clear(); |
| } |
| // Verify we didn't break anything |
| std::vector<std::string> Passes; |
| Passes.push_back("verify"); |
| std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes); |
| if (!New) { |
| errs() << "verify failed!\n"; |
| exit(1); |
| } |
| M = std::move(New); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use basic block pointers that point into the now-current |
| // module, and that they don't include any deleted blocks. |
| BBs.clear(); |
| const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable(); |
| for (auto &BI : BlockInfo) { |
| auto *F = cast<Function>(GST.lookup(BI.first)); |
| Value *V = F->getValueSymbolTable()->lookup(BI.second); |
| if (V && V->getType() == Type::getLabelTy(V->getContext())) |
| BBs.push_back(cast<BasicBlock>(V)); |
| } |
| return true; |
| } |
| // It didn't crash, try something else. |
| return false; |
| } |
| |
| namespace { |
| /// SimplifyCFG reducer - This works by calling SimplifyCFG on each basic block |
| /// in the program. |
| |
| class ReduceSimplifyCFG : public ListReducer<const BasicBlock *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| TargetTransformInfo TTI; |
| |
| public: |
| ReduceSimplifyCFG(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn), TTI(bd.getProgram().getDataLayout()) {} |
| |
| Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, |
| std::vector<const BasicBlock *> &Kept) override { |
| if (!Kept.empty() && TestBlocks(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestBlocks(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestBlocks(std::vector<const BasicBlock *> &Prefix); |
| }; |
| } |
| |
| bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) { |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| SmallPtrSet<const BasicBlock *, 8> Blocks; |
| for (const auto *BB : BBs) |
| Blocks.insert(cast<BasicBlock>(VMap[BB])); |
| |
| outs() << "Checking for crash with CFG simplifying:"; |
| unsigned NumPrint = Blocks.size(); |
| if (NumPrint > 10) |
| NumPrint = 10; |
| for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| outs() << " " << BBs[i]->getName(); |
| if (NumPrint < Blocks.size()) |
| outs() << "... <" << Blocks.size() << " total>"; |
| outs() << ": "; |
| |
| // The following may destroy some blocks, so we save them first |
| std::vector<std::pair<std::string, std::string>> BlockInfo; |
| |
| for (const BasicBlock *BB : Blocks) |
| BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); |
| |
| // Loop over and delete any hack up any blocks that are not listed... |
| for (auto &F : *M) |
| // Loop over all of the basic blocks and remove them if they are unneeded. |
| for (Function::iterator BBIt = F.begin(); BBIt != F.end();) { |
| if (!Blocks.count(&*BBIt)) { |
| ++BBIt; |
| continue; |
| } |
| simplifyCFG(&*BBIt++, TTI); |
| } |
| // Verify we didn't break anything |
| std::vector<std::string> Passes; |
| Passes.push_back("verify"); |
| std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes); |
| if (!New) { |
| errs() << "verify failed!\n"; |
| exit(1); |
| } |
| M = std::move(New); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use basic block pointers that point into the now-current |
| // module, and that they don't include any deleted blocks. |
| BBs.clear(); |
| const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable(); |
| for (auto &BI : BlockInfo) { |
| auto *F = cast<Function>(GST.lookup(BI.first)); |
| Value *V = F->getValueSymbolTable()->lookup(BI.second); |
| if (V && V->getType() == Type::getLabelTy(V->getContext())) |
| BBs.push_back(cast<BasicBlock>(V)); |
| } |
| return true; |
| } |
| // It didn't crash, try something else. |
| return false; |
| } |
| |
| namespace { |
| /// ReduceCrashingInstructions reducer - This works by removing the specified |
| /// non-terminator instructions and replacing them with undef. |
| /// |
| class ReduceCrashingInstructions : public ListReducer<const Instruction *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingInstructions(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<const Instruction *> &Prefix, |
| std::vector<const Instruction *> &Kept) override { |
| if (!Kept.empty() && TestInsts(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestInsts(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestInsts(std::vector<const Instruction *> &Prefix); |
| }; |
| } |
| |
| bool ReduceCrashingInstructions::TestInsts( |
| std::vector<const Instruction *> &Insts) { |
| // Clone the program to try hacking it apart... |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // Convert list to set for fast lookup... |
| SmallPtrSet<Instruction *, 32> Instructions; |
| for (unsigned i = 0, e = Insts.size(); i != e; ++i) { |
| assert(!isa<TerminatorInst>(Insts[i])); |
| Instructions.insert(cast<Instruction>(VMap[Insts[i]])); |
| } |
| |
| outs() << "Checking for crash with only " << Instructions.size(); |
| if (Instructions.size() == 1) |
| outs() << " instruction: "; |
| else |
| outs() << " instructions: "; |
| |
| for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI) |
| for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI) |
| for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) { |
| Instruction *Inst = &*I++; |
| if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) && |
| !Inst->isEHPad() && !Inst->getType()->isTokenTy() && |
| !Inst->isSwiftError()) { |
| if (!Inst->getType()->isVoidTy()) |
| Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); |
| Inst->eraseFromParent(); |
| } |
| } |
| |
| // Verify that this is still valid. |
| legacy::PassManager Passes; |
| Passes.add(createVerifierPass(/*FatalErrors=*/false)); |
| Passes.run(*M); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| |
| // Make sure to use instruction pointers that point into the now-current |
| // module, and that they don't include any deleted blocks. |
| Insts.clear(); |
| for (Instruction *Inst : Instructions) |
| Insts.push_back(Inst); |
| return true; |
| } |
| // It didn't crash, try something else. |
| return false; |
| } |
| |
| namespace { |
| // Reduce the list of Named Metadata nodes. We keep this as a list of |
| // names to avoid having to convert back and forth every time. |
| class ReduceCrashingNamedMD : public ListReducer<std::string> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingNamedMD(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<std::string> &Prefix, |
| std::vector<std::string> &Kept) override { |
| if (!Kept.empty() && TestNamedMDs(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestNamedMDs(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestNamedMDs(std::vector<std::string> &NamedMDs); |
| }; |
| } |
| |
| bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) { |
| |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| outs() << "Checking for crash with only these named metadata nodes:"; |
| unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10); |
| for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| outs() << " " << NamedMDs[i]; |
| if (NumPrint < NamedMDs.size()) |
| outs() << "... <" << NamedMDs.size() << " total>"; |
| outs() << ": "; |
| |
| // Make a StringMap for faster lookup |
| StringSet<> Names; |
| for (const std::string &Name : NamedMDs) |
| Names.insert(Name); |
| |
| // First collect all the metadata to delete in a vector, then |
| // delete them all at once to avoid invalidating the iterator |
| std::vector<NamedMDNode *> ToDelete; |
| ToDelete.reserve(M->named_metadata_size() - Names.size()); |
| for (auto &NamedMD : M->named_metadata()) |
| // Always keep a nonempty llvm.dbg.cu because the Verifier would complain. |
| if (!Names.count(NamedMD.getName()) && |
| (!(NamedMD.getName() == "llvm.dbg.cu" && NamedMD.getNumOperands() > 0))) |
| ToDelete.push_back(&NamedMD); |
| |
| for (auto *NamedMD : ToDelete) |
| NamedMD->eraseFromParent(); |
| |
| // Verify that this is still valid. |
| legacy::PassManager Passes; |
| Passes.add(createVerifierPass(/*FatalErrors=*/false)); |
| Passes.run(*M); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| return true; |
| } |
| return false; |
| } |
| |
| namespace { |
| // Reduce the list of operands to named metadata nodes |
| class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> { |
| BugDriver &BD; |
| BugTester TestFn; |
| |
| public: |
| ReduceCrashingNamedMDOps(BugDriver &bd, BugTester testFn) |
| : BD(bd), TestFn(testFn) {} |
| |
| Expected<TestResult> doTest(std::vector<const MDNode *> &Prefix, |
| std::vector<const MDNode *> &Kept) override { |
| if (!Kept.empty() && TestNamedMDOps(Kept)) |
| return KeepSuffix; |
| if (!Prefix.empty() && TestNamedMDOps(Prefix)) |
| return KeepPrefix; |
| return NoFailure; |
| } |
| |
| bool TestNamedMDOps(std::vector<const MDNode *> &NamedMDOps); |
| }; |
| } |
| |
| bool ReduceCrashingNamedMDOps::TestNamedMDOps( |
| std::vector<const MDNode *> &NamedMDOps) { |
| // Convert list to set for fast lookup... |
| SmallPtrSet<const MDNode *, 32> OldMDNodeOps; |
| for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) { |
| OldMDNodeOps.insert(NamedMDOps[i]); |
| } |
| |
| outs() << "Checking for crash with only " << OldMDNodeOps.size(); |
| if (OldMDNodeOps.size() == 1) |
| outs() << " named metadata operand: "; |
| else |
| outs() << " named metadata operands: "; |
| |
| ValueToValueMapTy VMap; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap); |
| |
| // This is a little wasteful. In the future it might be good if we could have |
| // these dropped during cloning. |
| for (auto &NamedMD : BD.getProgram().named_metadata()) { |
| // Drop the old one and create a new one |
| M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName())); |
| NamedMDNode *NewNamedMDNode = |
| M->getOrInsertNamedMetadata(NamedMD.getName()); |
| for (MDNode *op : NamedMD.operands()) |
| if (OldMDNodeOps.count(op)) |
| NewNamedMDNode->addOperand(cast<MDNode>(MapMetadata(op, VMap))); |
| } |
| |
| // Verify that this is still valid. |
| legacy::PassManager Passes; |
| Passes.add(createVerifierPass(/*FatalErrors=*/false)); |
| Passes.run(*M); |
| |
| // Try running on the hacked up program... |
| if (TestFn(BD, M.get())) { |
| // Make sure to use instruction pointers that point into the now-current |
| // module, and that they don't include any deleted blocks. |
| NamedMDOps.clear(); |
| for (const MDNode *Node : OldMDNodeOps) |
| NamedMDOps.push_back(cast<MDNode>(*VMap.getMappedMD(Node))); |
| |
| BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... |
| return true; |
| } |
| // It didn't crash, try something else. |
| return false; |
| } |
| |
| /// Attempt to eliminate as many global initializers as possible. |
| static Error ReduceGlobalInitializers(BugDriver &BD, BugTester TestFn) { |
| Module &OrigM = BD.getProgram(); |
| if (OrigM.global_empty()) |
| return Error::success(); |
| |
| // Now try to reduce the number of global variable initializers in the |
| // module to something small. |
| std::unique_ptr<Module> M = CloneModule(OrigM); |
| bool DeletedInit = false; |
| |
| for (GlobalVariable &GV : M->globals()) { |
| if (GV.hasInitializer()) { |
| DeleteGlobalInitializer(&GV); |
| GV.setLinkage(GlobalValue::ExternalLinkage); |
| GV.setComdat(nullptr); |
| DeletedInit = true; |
| } |
| } |
| |
| if (!DeletedInit) |
| return Error::success(); |
| |
| // See if the program still causes a crash... |
| outs() << "\nChecking to see if we can delete global inits: "; |
| |
| if (TestFn(BD, M.get())) { // Still crashes? |
| BD.setNewProgram(std::move(M)); |
| outs() << "\n*** Able to remove all global initializers!\n"; |
| return Error::success(); |
| } |
| |
| // No longer crashes. |
| outs() << " - Removing all global inits hides problem!\n"; |
| |
| std::vector<GlobalVariable *> GVs; |
| for (GlobalVariable &GV : OrigM.globals()) |
| if (GV.hasInitializer()) |
| GVs.push_back(&GV); |
| |
| if (GVs.size() > 1 && !BugpointIsInterrupted) { |
| outs() << "\n*** Attempting to reduce the number of global initializers " |
| << "in the testcase\n"; |
| |
| unsigned OldSize = GVs.size(); |
| Expected<bool> Result = |
| ReduceCrashingGlobalInitializers(BD, TestFn).reduceList(GVs); |
| if (Error E = Result.takeError()) |
| return E; |
| |
| if (GVs.size() < OldSize) |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables"); |
| } |
| return Error::success(); |
| } |
| |
| static Error ReduceInsts(BugDriver &BD, BugTester TestFn) { |
| // Attempt to delete instructions using bisection. This should help out nasty |
| // cases with large basic blocks where the problem is at one end. |
| if (!BugpointIsInterrupted) { |
| std::vector<const Instruction *> Insts; |
| for (const Function &F : BD.getProgram()) |
| for (const BasicBlock &BB : F) |
| for (const Instruction &I : BB) |
| if (!isa<TerminatorInst>(&I)) |
| Insts.push_back(&I); |
| |
| Expected<bool> Result = |
| ReduceCrashingInstructions(BD, TestFn).reduceList(Insts); |
| if (Error E = Result.takeError()) |
| return E; |
| } |
| |
| unsigned Simplification = 2; |
| do { |
| if (BugpointIsInterrupted) |
| // TODO: Should we distinguish this with an "interrupted error"? |
| return Error::success(); |
| --Simplification; |
| outs() << "\n*** Attempting to reduce testcase by deleting instruc" |
| << "tions: Simplification Level #" << Simplification << '\n'; |
| |
| // Now that we have deleted the functions that are unnecessary for the |
| // program, try to remove instructions that are not necessary to cause the |
| // crash. To do this, we loop through all of the instructions in the |
| // remaining functions, deleting them (replacing any values produced with |
| // nulls), and then running ADCE and SimplifyCFG. If the transformed input |
| // still triggers failure, keep deleting until we cannot trigger failure |
| // anymore. |
| // |
| unsigned InstructionsToSkipBeforeDeleting = 0; |
| TryAgain: |
| |
| // Loop over all of the (non-terminator) instructions remaining in the |
| // function, attempting to delete them. |
| unsigned CurInstructionNum = 0; |
| for (Module::const_iterator FI = BD.getProgram().begin(), |
| E = BD.getProgram().end(); |
| FI != E; ++FI) |
| if (!FI->isDeclaration()) |
| for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E; |
| ++BI) |
| for (BasicBlock::const_iterator I = BI->begin(), E = --BI->end(); |
| I != E; ++I, ++CurInstructionNum) { |
| if (InstructionsToSkipBeforeDeleting) { |
| --InstructionsToSkipBeforeDeleting; |
| } else { |
| if (BugpointIsInterrupted) |
| // TODO: Should this be some kind of interrupted error? |
| return Error::success(); |
| |
| if (I->isEHPad() || I->getType()->isTokenTy() || |
| I->isSwiftError()) |
| continue; |
| |
| outs() << "Checking instruction: " << *I; |
| std::unique_ptr<Module> M = |
| BD.deleteInstructionFromProgram(&*I, Simplification); |
| |
| // Find out if the pass still crashes on this pass... |
| if (TestFn(BD, M.get())) { |
| // Yup, it does, we delete the old module, and continue trying |
| // to reduce the testcase... |
| BD.setNewProgram(std::move(M)); |
| InstructionsToSkipBeforeDeleting = CurInstructionNum; |
| goto TryAgain; // I wish I had a multi-level break here! |
| } |
| } |
| } |
| |
| if (InstructionsToSkipBeforeDeleting) { |
| InstructionsToSkipBeforeDeleting = 0; |
| goto TryAgain; |
| } |
| |
| } while (Simplification); |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions"); |
| return Error::success(); |
| } |
| |
| /// DebugACrash - Given a predicate that determines whether a component crashes |
| /// on a program, try to destructively reduce the program while still keeping |
| /// the predicate true. |
| static Error DebugACrash(BugDriver &BD, BugTester TestFn) { |
| // See if we can get away with nuking some of the global variable initializers |
| // in the program... |
| if (!NoGlobalRM) |
| if (Error E = ReduceGlobalInitializers(BD, TestFn)) |
| return E; |
| |
| // Now try to reduce the number of functions in the module to something small. |
| std::vector<Function *> Functions; |
| for (Function &F : BD.getProgram()) |
| if (!F.isDeclaration()) |
| Functions.push_back(&F); |
| |
| if (Functions.size() > 1 && !BugpointIsInterrupted) { |
| outs() << "\n*** Attempting to reduce the number of functions " |
| "in the testcase\n"; |
| |
| unsigned OldSize = Functions.size(); |
| Expected<bool> Result = |
| ReduceCrashingFunctions(BD, TestFn).reduceList(Functions); |
| if (Error E = Result.takeError()) |
| return E; |
| |
| if (Functions.size() < OldSize) |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); |
| } |
| |
| // Attempt to change conditional branches into unconditional branches to |
| // eliminate blocks. |
| if (!DisableSimplifyCFG && !BugpointIsInterrupted) { |
| std::vector<const BasicBlock *> Blocks; |
| for (Function &F : BD.getProgram()) |
| for (BasicBlock &BB : F) |
| Blocks.push_back(&BB); |
| unsigned OldSize = Blocks.size(); |
| Expected<bool> Result = |
| ReduceCrashingConditionals(BD, TestFn, true).reduceList(Blocks); |
| if (Error E = Result.takeError()) |
| return E; |
| Result = ReduceCrashingConditionals(BD, TestFn, false).reduceList(Blocks); |
| if (Error E = Result.takeError()) |
| return E; |
| if (Blocks.size() < OldSize) |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-conditionals"); |
| } |
| |
| // Attempt to delete entire basic blocks at a time to speed up |
| // convergence... this actually works by setting the terminator of the blocks |
| // to a return instruction then running simplifycfg, which can potentially |
| // shrinks the code dramatically quickly |
| // |
| if (!DisableSimplifyCFG && !BugpointIsInterrupted) { |
| std::vector<const BasicBlock *> Blocks; |
| for (Function &F : BD.getProgram()) |
| for (BasicBlock &BB : F) |
| Blocks.push_back(&BB); |
| unsigned OldSize = Blocks.size(); |
| Expected<bool> Result = ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks); |
| if (Error E = Result.takeError()) |
| return E; |
| if (Blocks.size() < OldSize) |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); |
| } |
| |
| if (!DisableSimplifyCFG && !BugpointIsInterrupted) { |
| std::vector<const BasicBlock *> Blocks; |
| for (Function &F : BD.getProgram()) |
| for (BasicBlock &BB : F) |
| Blocks.push_back(&BB); |
| unsigned OldSize = Blocks.size(); |
| Expected<bool> Result = ReduceSimplifyCFG(BD, TestFn).reduceList(Blocks); |
| if (Error E = Result.takeError()) |
| return E; |
| if (Blocks.size() < OldSize) |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplifycfg"); |
| } |
| |
| // Attempt to delete instructions using bisection. This should help out nasty |
| // cases with large basic blocks where the problem is at one end. |
| if (!BugpointIsInterrupted) |
| if (Error E = ReduceInsts(BD, TestFn)) |
| return E; |
| |
| // Attempt to strip debug info metadata. |
| auto stripMetadata = [&](std::function<bool(Module &)> strip) { |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram()); |
| strip(*M); |
| if (TestFn(BD, M.get())) |
| BD.setNewProgram(std::move(M)); |
| }; |
| if (!NoStripDebugInfo && !BugpointIsInterrupted) { |
| outs() << "\n*** Attempting to strip the debug info: "; |
| stripMetadata(StripDebugInfo); |
| } |
| if (!NoStripDebugTypeInfo && !BugpointIsInterrupted) { |
| outs() << "\n*** Attempting to strip the debug type info: "; |
| stripMetadata(stripNonLineTableDebugInfo); |
| } |
| |
| if (!NoNamedMDRM) { |
| if (!BugpointIsInterrupted) { |
| // Try to reduce the amount of global metadata (particularly debug info), |
| // by dropping global named metadata that anchors them |
| outs() << "\n*** Attempting to remove named metadata: "; |
| std::vector<std::string> NamedMDNames; |
| for (auto &NamedMD : BD.getProgram().named_metadata()) |
| NamedMDNames.push_back(NamedMD.getName().str()); |
| Expected<bool> Result = |
| ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames); |
| if (Error E = Result.takeError()) |
| return E; |
| } |
| |
| if (!BugpointIsInterrupted) { |
| // Now that we quickly dropped all the named metadata that doesn't |
| // contribute to the crash, bisect the operands of the remaining ones |
| std::vector<const MDNode *> NamedMDOps; |
| for (auto &NamedMD : BD.getProgram().named_metadata()) |
| for (auto op : NamedMD.operands()) |
| NamedMDOps.push_back(op); |
| Expected<bool> Result = |
| ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps); |
| if (Error E = Result.takeError()) |
| return E; |
| } |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-named-md"); |
| } |
| |
| // Try to clean up the testcase by running funcresolve and globaldce... |
| if (!BugpointIsInterrupted) { |
| outs() << "\n*** Attempting to perform final cleanups: "; |
| std::unique_ptr<Module> M = CloneModule(BD.getProgram()); |
| M = BD.performFinalCleanups(std::move(M), true); |
| |
| // Find out if the pass still crashes on the cleaned up program... |
| if (M && TestFn(BD, M.get())) |
| BD.setNewProgram( |
| std::move(M)); // Yup, it does, keep the reduced version... |
| } |
| |
| BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified"); |
| |
| return Error::success(); |
| } |
| |
| static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) { |
| return BD.runPasses(*M, BD.getPassesToRun()); |
| } |
| |
| /// debugOptimizerCrash - This method is called when some pass crashes on input. |
| /// It attempts to prune down the testcase to something reasonable, and figure |
| /// out exactly which pass is crashing. |
| /// |
| Error BugDriver::debugOptimizerCrash(const std::string &ID) { |
| outs() << "\n*** Debugging optimizer crash!\n"; |
| |
| // Reduce the list of passes which causes the optimizer to crash... |
| if (!BugpointIsInterrupted && !DontReducePassList) { |
| Expected<bool> Result = ReducePassList(*this).reduceList(PassesToRun); |
| if (Error E = Result.takeError()) |
| return E; |
| } |
| |
| outs() << "\n*** Found crashing pass" |
| << (PassesToRun.size() == 1 ? ": " : "es: ") |
| << getPassesString(PassesToRun) << '\n'; |
| |
| EmitProgressBitcode(*Program, ID); |
| |
| return DebugACrash(*this, TestForOptimizerCrash); |
| } |
| |
| static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { |
| if (Error E = BD.compileProgram(*M)) { |
| if (VerboseErrors) |
| errs() << toString(std::move(E)) << "\n"; |
| else { |
| consumeError(std::move(E)); |
| errs() << "<crash>\n"; |
| } |
| return true; // Tool is still crashing. |
| } |
| errs() << '\n'; |
| return false; |
| } |
| |
| /// debugCodeGeneratorCrash - This method is called when the code generator |
| /// crashes on an input. It attempts to reduce the input as much as possible |
| /// while still causing the code generator to crash. |
| Error BugDriver::debugCodeGeneratorCrash() { |
| errs() << "*** Debugging code generator crash!\n"; |
| |
| return DebugACrash(*this, TestForCodeGenCrash); |
| } |