static Error ReduceInsts(BugDriver &BD, bool (*TestFn)(const BugDriver &, Module *)) { // 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()) 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(M.release()); 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 bool DebugACrash(BugDriver &BD, bool (*TestFn)(const BugDriver &, Module *), std::string &Error) { // See if we can get away with nuking some of the global variable initializers // in the program... if (!NoGlobalRM && BD.getProgram()->global_begin() != BD.getProgram()->global_end()) { // Now try to reduce the number of global variable initializers in the // module to something small. Module *M = CloneModule(BD.getProgram()).release(); bool DeletedInit = false; for (Module::global_iterator I = M->global_begin(), E = M->global_end(); I != E; ++I) if (I->hasInitializer()) { DeleteGlobalInitializer(&*I); I->setLinkage(GlobalValue::ExternalLinkage); DeletedInit = true; } if (!DeletedInit) { delete M; // No change made... } else { // See if the program still causes a crash... outs() << "\nChecking to see if we can delete global inits: "; if (TestFn(BD, M)) { // Still crashes? BD.setNewProgram(M); outs() << "\n*** Able to remove all global initializers!\n"; } else { // No longer crashes? outs() << " - Removing all global inits hides problem!\n"; delete M; std::vector<GlobalVariable*> GVs; for (Module::global_iterator I = BD.getProgram()->global_begin(), E = BD.getProgram()->global_end(); I != E; ++I) if (I->hasInitializer()) GVs.push_back(&*I); if (GVs.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of global " << "variables in the testcase\n"; unsigned OldSize = GVs.size(); ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs, Error); if (!Error.empty()) return true; if (GVs.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables"); } } } } // 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(); ReduceCrashingFunctions(BD, TestFn).reduceList(Functions, Error); if (Functions.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); } // 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(); ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks, Error); if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); } // 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); ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error); } // FIXME: This should use the list reducer to converge faster by deleting // larger chunks of instructions at a time! unsigned Simplification = 2; do { if (BugpointIsInterrupted) break; --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) goto ExitLoops; if (I->isEHPad() || I->getType()->isTokenTy()) 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(M.release()); InstructionsToSkipBeforeDeleting = CurInstructionNum; goto TryAgain; // I wish I had a multi-level break here! } } } if (InstructionsToSkipBeforeDeleting) { InstructionsToSkipBeforeDeleting = 0; goto TryAgain; } } while (Simplification); if (!NoNamedMDRM) { BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions"); 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()); ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error); } 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); ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error); } } ExitLoops: // Try to clean up the testcase by running funcresolve and globaldce... if (!BugpointIsInterrupted) { outs() << "\n*** Attempting to perform final cleanups: "; Module *M = CloneModule(BD.getProgram()).release(); M = BD.performFinalCleanups(M, true).release(); // Find out if the pass still crashes on the cleaned up program... if (TestFn(BD, M)) { BD.setNewProgram(M); // Yup, it does, keep the reduced version... } else { delete M; } } BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified"); return false; }