/// TestMergedProgram - Given two modules, link them together and run the /// program, checking to see if the program matches the diff. If the diff /// matches, return false, otherwise return true. If the DeleteInputs argument /// is set to true then this function deletes both input modules before it /// returns. /// static bool TestMergedProgram(BugDriver &BD, Module *M1, Module *M2, bool DeleteInputs, std::string &Error) { // Link the two portions of the program back to together. std::string ErrorMsg; if (!DeleteInputs) { M1 = CloneModule(M1); M2 = CloneModule(M2); } if (Linker::LinkModules(M1, M2, &ErrorMsg)) { errs() << BD.getToolName() << ": Error linking modules together:" << ErrorMsg << '\n'; exit(1); } delete M2; // We are done with this module. OwningPtr<Module> OldProgram(BD.swapProgramIn(M1)); // Execute the program. If it does not match the expected output, we must // return true. bool Broken = BD.diffProgram("", "", false, &Error); if (!Error.empty()) { // Delete the linked module & restore the original delete BD.swapProgramIn(OldProgram.take()); } return Broken; }
/// TestOptimizer - This is the predicate function used to check to see if the /// "Test" portion of the program is misoptimized. If so, return true. In any /// case, both module arguments are deleted. /// static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe) { // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; Module *Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), /*AutoDebugCrashes*/true); outs() << "done.\n"; delete Test; outs() << " Checking to see if the merged program executes correctly: "; bool Broken = TestMergedProgram(BD, Optimized, Safe, true); outs() << (Broken ? " nope.\n" : " yup.\n"); return Broken; }
static Error ReduceGlobalInitializers(BugDriver &BD, bool (*TestFn)(const BugDriver &, Module *)) { if (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); I->setComdat(nullptr); 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(); Expected<bool> Result = ReduceCrashingGlobalVariables(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 bool TestForCodeGenCrash(BugDriver &BD, Module *M) { try { BD.compileProgram(M); std::cerr << '\n'; return false; } catch (ToolExecutionError &) { std::cerr << "<crash>\n"; return true; // Tool is still crashing. } }
static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { std::string Error; BD.compileProgram(M, &Error); if (!Error.empty()) { errs() << "<crash>\n"; return true; // Tool is still crashing. } errs() << '\n'; return false; }
/// TestCodeGenerator - This is the predicate function used to check to see if /// the "Test" portion of the program is miscompiled by the code generator under /// test. If so, return true. In any case, both module arguments are deleted. /// static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, std::string &Error) { CleanupAndPrepareModules(BD, Test, Safe); sys::Path TestModuleBC("bugpoint.test.bc"); std::string ErrMsg; if (TestModuleBC.makeUnique(true, &ErrMsg)) { errs() << BD.getToolName() << "Error making unique filename: " << ErrMsg << "\n"; exit(1); } if (BD.writeProgramToFile(TestModuleBC.str(), Test)) { errs() << "Error writing bitcode to `" << TestModuleBC.str() << "'\nExiting."; exit(1); } delete Test; FileRemover TestModuleBCRemover(TestModuleBC, !SaveTemps); // Make the shared library sys::Path SafeModuleBC("bugpoint.safe.bc"); if (SafeModuleBC.makeUnique(true, &ErrMsg)) { errs() << BD.getToolName() << "Error making unique filename: " << ErrMsg << "\n"; exit(1); } if (BD.writeProgramToFile(SafeModuleBC.str(), Safe)) { errs() << "Error writing bitcode to `" << SafeModuleBC.str() << "'\nExiting."; exit(1); } FileRemover SafeModuleBCRemover(SafeModuleBC, !SaveTemps); std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error); if (!Error.empty()) return false; delete Safe; FileRemover SharedObjectRemover(sys::Path(SharedObject), !SaveTemps); // Run the code generator on the `Test' code, loading the shared library. // The function returns whether or not the new output differs from reference. bool Result = BD.diffProgram(BD.getProgram(), TestModuleBC.str(), SharedObject, false, &Error); if (!Error.empty()) return false; if (Result) errs() << ": still failing!\n"; else errs() << ": didn't fail.\n"; return Result; }
/// TestOptimizer - This is the predicate function used to check to see if the /// "Test" portion of the program is misoptimized. If so, return true. In any /// case, both module arguments are deleted. /// static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe, std::string &Error) { // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; Module *Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), /*AutoDebugCrashes*/true); outs() << "done.\n"; delete Test; outs() << " Checking to see if the merged program executes correctly: "; bool Broken; Module *New = TestMergedProgram(BD, Optimized, Safe, true, Error, Broken); if (New) { outs() << (Broken ? " nope.\n" : " yup.\n"); // Delete the original and set the new program. delete BD.swapProgramIn(New); } return Broken; }
/// This is the predicate function used to check to see if the "Test" portion of /// the program is misoptimized. If so, return true. In any case, both module /// arguments are deleted. /// static bool TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test, std::unique_ptr<Module> Safe, std::string &Error) { // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; std::unique_ptr<Module> Optimized = BD.runPassesOn(Test.get(), BD.getPassesToRun(), /*AutoDebugCrashes*/ true); outs() << "done.\n"; outs() << " Checking to see if the merged program executes correctly: "; bool Broken; std::unique_ptr<Module> New = testMergedProgram( BD, std::move(Optimized), std::move(Safe), Error, Broken); if (New) { outs() << (Broken ? " nope.\n" : " yup.\n"); // Delete the original and set the new program. delete BD.swapProgramIn(New.release()); } return Broken; }
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; }
/// Given two modules, link them together and run the program, checking to see /// if the program matches the diff. If there is an error, return NULL. If not, /// return the merged module. The Broken argument will be set to true if the /// output is different. If the DeleteInputs argument is set to true then this /// function deletes both input modules before it returns. /// static std::unique_ptr<Module> testMergedProgram(const BugDriver &BD, std::unique_ptr<Module> M1, std::unique_ptr<Module> M2, std::string &Error, bool &Broken) { if (Linker::linkModules(*M1, *M2)) exit(1); // Execute the program. Broken = BD.diffProgram(M1.get(), "", "", false, &Error); if (!Error.empty()) return nullptr; return M1; }
/// TestCodeGenerator - This is the predicate function used to check to see if /// the "Test" portion of the program is miscompiled by the code generator under /// test. If so, return true. In any case, both module arguments are deleted. /// static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe) { CleanupAndPrepareModules(BD, Test, Safe); sys::Path TestModuleBC("bugpoint.test.bc"); std::string ErrMsg; if (TestModuleBC.makeUnique(true, &ErrMsg)) { std::cerr << BD.getToolName() << "Error making unique filename: " << ErrMsg << "\n"; exit(1); } if (BD.writeProgramToFile(TestModuleBC.toString(), Test)) { std::cerr << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; exit(1); } delete Test; // Make the shared library sys::Path SafeModuleBC("bugpoint.safe.bc"); if (SafeModuleBC.makeUnique(true, &ErrMsg)) { std::cerr << BD.getToolName() << "Error making unique filename: " << ErrMsg << "\n"; exit(1); } if (BD.writeProgramToFile(SafeModuleBC.toString(), Safe)) { std::cerr << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } std::string SharedObject = BD.compileSharedObject(SafeModuleBC.toString()); delete Safe; // Run the code generator on the `Test' code, loading the shared library. // The function returns whether or not the new output differs from reference. int Result = BD.diffProgram(TestModuleBC.toString(), SharedObject, false); if (Result) std::cerr << ": still failing!\n"; else std::cerr << ": didn't fail.\n"; TestModuleBC.eraseFromDisk(); SafeModuleBC.eraseFromDisk(); sys::Path(SharedObject).eraseFromDisk(); return Result; }
/// ExtractLoops - Given a reduced list of functions that still exposed the bug, /// check to see if we can extract the loops in the region without obscuring the /// bug. If so, it reduces the amount of code identified. /// static bool ExtractLoops(BugDriver &BD, bool (*TestFn)(BugDriver &, Module *, Module *, std::string &), std::vector<Function*> &MiscompiledFunctions, std::string &Error) { bool MadeChange = false; while (1) { if (BugpointIsInterrupted) return MadeChange; DenseMap<const Value*, Value*> ValueMap; Module *ToNotOptimize = CloneModule(BD.getProgram(), ValueMap); Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, MiscompiledFunctions, ValueMap); Module *ToOptimizeLoopExtracted = BD.ExtractLoop(ToOptimize); if (!ToOptimizeLoopExtracted) { // If the loop extractor crashed or if there were no extractible loops, // then this chapter of our odyssey is over with. delete ToNotOptimize; delete ToOptimize; return MadeChange; } errs() << "Extracted a loop from the breaking portion of the program.\n"; // Bugpoint is intentionally not very trusting of LLVM transformations. In // particular, we're not going to assume that the loop extractor works, so // we're going to test the newly loop extracted program to make sure nothing // has broken. If something broke, then we'll inform the user and stop // extraction. AbstractInterpreter *AI = BD.switchToSafeInterpreter(); bool Failure = TestMergedProgram(BD, ToOptimizeLoopExtracted, ToNotOptimize, false, Error); if (!Error.empty()) return false; if (Failure) { BD.switchToInterpreter(AI); // Merged program doesn't work anymore! errs() << " *** ERROR: Loop extraction broke the program. :(" << " Please report a bug!\n"; errs() << " Continuing on with un-loop-extracted version.\n"; BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-tno.bc", ToNotOptimize); BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to.bc", ToOptimize); BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to-le.bc", ToOptimizeLoopExtracted); errs() << "Please submit the " << OutputPrefix << "-loop-extract-fail-*.bc files.\n"; delete ToOptimize; delete ToNotOptimize; delete ToOptimizeLoopExtracted; return MadeChange; } delete ToOptimize; BD.switchToInterpreter(AI); outs() << " Testing after loop extraction:\n"; // Clone modules, the tester function will free them. Module *TOLEBackup = CloneModule(ToOptimizeLoopExtracted); Module *TNOBackup = CloneModule(ToNotOptimize); Failure = TestFn(BD, ToOptimizeLoopExtracted, ToNotOptimize, Error); if (!Error.empty()) return false; if (!Failure) { outs() << "*** Loop extraction masked the problem. Undoing.\n"; // If the program is not still broken, then loop extraction did something // that masked the error. Stop loop extraction now. delete TOLEBackup; delete TNOBackup; return MadeChange; } ToOptimizeLoopExtracted = TOLEBackup; ToNotOptimize = TNOBackup; outs() << "*** Loop extraction successful!\n"; std::vector<std::pair<std::string, const FunctionType*> > MisCompFunctions; for (Module::iterator I = ToOptimizeLoopExtracted->begin(), E = ToOptimizeLoopExtracted->end(); I != E; ++I) if (!I->isDeclaration()) MisCompFunctions.push_back(std::make_pair(I->getName(), I->getFunctionType())); // Okay, great! Now we know that we extracted a loop and that loop // extraction both didn't break the program, and didn't mask the problem. // Replace the current program with the loop extracted version, and try to // extract another loop. std::string ErrorMsg; if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, &ErrorMsg)){ errs() << BD.getToolName() << ": Error linking modules together:" << ErrorMsg << '\n'; exit(1); } delete ToOptimizeLoopExtracted; // All of the Function*'s in the MiscompiledFunctions list are in the old // module. Update this list to include all of the functions in the // optimized and loop extracted module. MiscompiledFunctions.clear(); for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) { Function *NewF = ToNotOptimize->getFunction(MisCompFunctions[i].first); assert(NewF && "Function not found??"); assert(NewF->getFunctionType() == MisCompFunctions[i].second && "found wrong function type?"); MiscompiledFunctions.push_back(NewF); } BD.setNewProgram(ToNotOptimize); MadeChange = true; } }
/// 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()); bool DeletedInit = false; for (Module::global_iterator I = M->global_begin(), E = M->global_end(); I != E; ++I) if (I->hasInitializer()) { I->setInitializer(nullptr); 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 (isa<LandingPadInst>(I)) 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); 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()); 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; }
static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) { return BD.runPasses(M); }
/// DebugAMiscompilation - This is a generic driver to narrow down /// miscompilations, either in an optimization or a code generator. /// static std::vector<Function*> DebugAMiscompilation(BugDriver &BD, bool (*TestFn)(BugDriver &, Module *, Module *)) { // Okay, now that we have reduced the list of passes which are causing the // failure, see if we can pin down which functions are being // miscompiled... first build a list of all of the non-external functions in // the program. std::vector<Function*> MiscompiledFunctions; Module *Prog = BD.getProgram(); for (Module::iterator I = Prog->begin(), E = Prog->end(); I != E; ++I) if (!I->isDeclaration()) MiscompiledFunctions.push_back(I); // Do the reduction... if (!BugpointIsInterrupted) ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; // See if we can rip any loops out of the miscompiled functions and still // trigger the problem. if (!BugpointIsInterrupted && !DisableLoopExtraction && ExtractLoops(BD, TestFn, MiscompiledFunctions)) { // Okay, we extracted some loops and the problem still appears. See if we // can eliminate some of the created functions from being candidates. // Loop extraction can introduce functions with the same name (foo_code). // Make sure to disambiguate the symbols so that when the program is split // apart that we can link it back together again. DisambiguateGlobalSymbols(BD.getProgram()); // Do the reduction... if (!BugpointIsInterrupted) ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; } if (!BugpointIsInterrupted && !DisableBlockExtraction && ExtractBlocks(BD, TestFn, MiscompiledFunctions)) { // Okay, we extracted some blocks and the problem still appears. See if we // can eliminate some of the created functions from being candidates. // Block extraction can introduce functions with the same name (foo_code). // Make sure to disambiguate the symbols so that when the program is split // apart that we can link it back together again. DisambiguateGlobalSymbols(BD.getProgram()); // Do the reduction... ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions); outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; } return MiscompiledFunctions; }
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(); }
/// TestCodeGenerator - This is the predicate function used to check to see if /// the "Test" portion of the program is miscompiled by the code generator under /// test. If so, return true. In any case, both module arguments are deleted. /// static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, std::string &Error) { CleanupAndPrepareModules(BD, Test, Safe); SmallString<128> TestModuleBC; int TestModuleFD; std::error_code EC = sys::fs::createTemporaryFile("bugpoint.test", "bc", TestModuleFD, TestModuleBC); if (EC) { errs() << BD.getToolName() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test)) { errs() << "Error writing bitcode to `" << TestModuleBC.str() << "'\nExiting."; exit(1); } delete Test; FileRemover TestModuleBCRemover(TestModuleBC.str(), !SaveTemps); // Make the shared library SmallString<128> SafeModuleBC; int SafeModuleFD; EC = sys::fs::createTemporaryFile("bugpoint.safe", "bc", SafeModuleFD, SafeModuleBC); if (EC) { errs() << BD.getToolName() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe)) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps); std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error); if (!Error.empty()) return false; delete Safe; FileRemover SharedObjectRemover(SharedObject, !SaveTemps); // Run the code generator on the `Test' code, loading the shared library. // The function returns whether or not the new output differs from reference. bool Result = BD.diffProgram(BD.getProgram(), TestModuleBC.str(), SharedObject, false, &Error); if (!Error.empty()) return false; if (Result) errs() << ": still failing!\n"; else errs() << ": didn't fail.\n"; return Result; }
/// 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, bool (*TestFn)(const BugDriver &, Module *)) { // 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(M.release()); }; 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: "; 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 Error::success(); }
/// CleanupAndPrepareModules - Get the specified modules ready for code /// generator testing. /// static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, Module *Safe) { // Clean up the modules, removing extra cruft that we don't need anymore... Test = BD.performFinalCleanups(Test); // If we are executing the JIT, we have several nasty issues to take care of. if (!BD.isExecutingJIT()) return; // First, if the main function is in the Safe module, we must add a stub to // the Test module to call into it. Thus, we create a new function `main' // which just calls the old one. if (Function *oldMain = Safe->getFunction("main")) if (!oldMain->isDeclaration()) { // Rename it oldMain->setName("llvm_bugpoint_old_main"); // Create a NEW `main' function with same type in the test module. Function *newMain = Function::Create(oldMain->getFunctionType(), GlobalValue::ExternalLinkage, "main", Test); // Create an `oldmain' prototype in the test module, which will // corresponds to the real main function in the same module. Function *oldMainProto = Function::Create(oldMain->getFunctionType(), GlobalValue::ExternalLinkage, oldMain->getName(), Test); // Set up and remember the argument list for the main function. std::vector<Value*> args; for (Function::arg_iterator I = newMain->arg_begin(), E = newMain->arg_end(), OI = oldMain->arg_begin(); I != E; ++I, ++OI) { I->setName(OI->getName()); // Copy argument names from oldMain args.push_back(I); } // Call the old main function and return its result BasicBlock *BB = BasicBlock::Create(Safe->getContext(), "entry", newMain); CallInst *call = CallInst::Create(oldMainProto, args.begin(), args.end(), "", BB); // If the type of old function wasn't void, return value of call ReturnInst::Create(Safe->getContext(), call, BB); } // The second nasty issue we must deal with in the JIT is that the Safe // module cannot directly reference any functions defined in the test // module. Instead, we use a JIT API call to dynamically resolve the // symbol. // Add the resolver to the Safe module. // Prototype: void *getPointerToNamedFunction(const char* Name) Constant *resolverFunc = Safe->getOrInsertFunction("getPointerToNamedFunction", Type::getInt8PtrTy(Safe->getContext()), Type::getInt8PtrTy(Safe->getContext()), (Type *)0); // Use the function we just added to get addresses of functions we need. for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) { if (F->isDeclaration() && !F->use_empty() && &*F != resolverFunc && !F->isIntrinsic() /* ignore intrinsics */) { Function *TestFn = Test->getFunction(F->getName()); // Don't forward functions which are external in the test module too. if (TestFn && !TestFn->isDeclaration()) { // 1. Add a string constant with its name to the global file Constant *InitArray = ConstantArray::get(F->getContext(), F->getName()); GlobalVariable *funcName = new GlobalVariable(*Safe, InitArray->getType(), true /*isConstant*/, GlobalValue::InternalLinkage, InitArray, F->getName() + "_name"); // 2. Use `GetElementPtr *funcName, 0, 0' to convert the string to an // sbyte* so it matches the signature of the resolver function. // GetElementPtr *funcName, ulong 0, ulong 0 std::vector<Constant*> GEPargs(2, Constant::getNullValue(Type::getInt32Ty(F->getContext()))); Value *GEP = ConstantExpr::getGetElementPtr(funcName, &GEPargs[0], 2); std::vector<Value*> ResolverArgs; ResolverArgs.push_back(GEP); // Rewrite uses of F in global initializers, etc. to uses of a wrapper // function that dynamically resolves the calls to F via our JIT API if (!F->use_empty()) { // Create a new global to hold the cached function pointer. Constant *NullPtr = ConstantPointerNull::get(F->getType()); GlobalVariable *Cache = new GlobalVariable(*F->getParent(), F->getType(), false, GlobalValue::InternalLinkage, NullPtr,F->getName()+".fpcache"); // Construct a new stub function that will re-route calls to F const FunctionType *FuncTy = F->getFunctionType(); Function *FuncWrapper = Function::Create(FuncTy, GlobalValue::InternalLinkage, F->getName() + "_wrapper", F->getParent()); BasicBlock *EntryBB = BasicBlock::Create(F->getContext(), "entry", FuncWrapper); BasicBlock *DoCallBB = BasicBlock::Create(F->getContext(), "usecache", FuncWrapper); BasicBlock *LookupBB = BasicBlock::Create(F->getContext(), "lookupfp", FuncWrapper); // Check to see if we already looked up the value. Value *CachedVal = new LoadInst(Cache, "fpcache", EntryBB); Value *IsNull = new ICmpInst(*EntryBB, ICmpInst::ICMP_EQ, CachedVal, NullPtr, "isNull"); BranchInst::Create(LookupBB, DoCallBB, IsNull, EntryBB); // Resolve the call to function F via the JIT API: // // call resolver(GetElementPtr...) CallInst *Resolver = CallInst::Create(resolverFunc, ResolverArgs.begin(), ResolverArgs.end(), "resolver", LookupBB); // Cast the result from the resolver to correctly-typed function. CastInst *CastedResolver = new BitCastInst(Resolver, PointerType::getUnqual(F->getFunctionType()), "resolverCast", LookupBB); // Save the value in our cache. new StoreInst(CastedResolver, Cache, LookupBB); BranchInst::Create(DoCallBB, LookupBB); PHINode *FuncPtr = PHINode::Create(NullPtr->getType(), "fp", DoCallBB); FuncPtr->addIncoming(CastedResolver, LookupBB); FuncPtr->addIncoming(CachedVal, EntryBB); // Save the argument list. std::vector<Value*> Args; for (Function::arg_iterator i = FuncWrapper->arg_begin(), e = FuncWrapper->arg_end(); i != e; ++i) Args.push_back(i); // Pass on the arguments to the real function, return its result if (F->getReturnType()->isVoidTy()) { CallInst::Create(FuncPtr, Args.begin(), Args.end(), "", DoCallBB); ReturnInst::Create(F->getContext(), DoCallBB); } else { CallInst *Call = CallInst::Create(FuncPtr, Args.begin(), Args.end(), "retval", DoCallBB); ReturnInst::Create(F->getContext(),Call, DoCallBB); } // Use the wrapper function instead of the old function F->replaceAllUsesWith(FuncWrapper); } } } } if (verifyModule(*Test) || verifyModule(*Safe)) { errs() << "Bugpoint has a bug, which corrupted a module!!\n"; abort(); } }
/// DebugAMiscompilation - This is a generic driver to narrow down /// miscompilations, either in an optimization or a code generator. /// static std::vector<Function*> DebugAMiscompilation(BugDriver &BD, bool (*TestFn)(BugDriver &, Module *, Module *, std::string &), std::string &Error) { // Okay, now that we have reduced the list of passes which are causing the // failure, see if we can pin down which functions are being // miscompiled... first build a list of all of the non-external functions in // the program. std::vector<Function*> MiscompiledFunctions; Module *Prog = BD.getProgram(); for (Module::iterator I = Prog->begin(), E = Prog->end(); I != E; ++I) if (!I->isDeclaration()) MiscompiledFunctions.push_back(I); // Do the reduction... if (!BugpointIsInterrupted) ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions, Error); if (!Error.empty()) return MiscompiledFunctions; outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; // See if we can rip any loops out of the miscompiled functions and still // trigger the problem. if (!BugpointIsInterrupted && !DisableLoopExtraction) { bool Ret = ExtractLoops(BD, TestFn, MiscompiledFunctions, Error); if (!Error.empty()) return MiscompiledFunctions; if (Ret) { // Okay, we extracted some loops and the problem still appears. See if // we can eliminate some of the created functions from being candidates. DisambiguateGlobalSymbols(BD.getProgram()); // Do the reduction... if (!BugpointIsInterrupted) ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions, Error); if (!Error.empty()) return MiscompiledFunctions; outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; } } if (!BugpointIsInterrupted && !DisableBlockExtraction) { bool Ret = ExtractBlocks(BD, TestFn, MiscompiledFunctions, Error); if (!Error.empty()) return MiscompiledFunctions; if (Ret) { // Okay, we extracted some blocks and the problem still appears. See if // we can eliminate some of the created functions from being candidates. DisambiguateGlobalSymbols(BD.getProgram()); // Do the reduction... ReduceMiscompilingFunctions(BD, TestFn).reduceList(MiscompiledFunctions, Error); if (!Error.empty()) return MiscompiledFunctions; outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") << " being miscompiled: "; PrintFunctionList(MiscompiledFunctions); outs() << '\n'; } } return MiscompiledFunctions; }
/// ExtractBlocks - Given a reduced list of functions that still expose the bug, /// extract as many basic blocks from the region as possible without obscuring /// the bug. /// static bool ExtractBlocks(BugDriver &BD, bool (*TestFn)(BugDriver &, Module *, Module *, std::string &), std::vector<Function*> &MiscompiledFunctions, std::string &Error) { if (BugpointIsInterrupted) return false; std::vector<BasicBlock*> Blocks; for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) for (Function::iterator I = MiscompiledFunctions[i]->begin(), E = MiscompiledFunctions[i]->end(); I != E; ++I) Blocks.push_back(I); // Use the list reducer to identify blocks that can be extracted without // obscuring the bug. The Blocks list will end up containing blocks that must // be retained from the original program. unsigned OldSize = Blocks.size(); // Check to see if all blocks are extractible first. bool Ret = ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) .TestFuncs(std::vector<BasicBlock*>(), Error); if (!Error.empty()) return false; if (Ret) { Blocks.clear(); } else { ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions).reduceList(Blocks, Error); if (!Error.empty()) return false; if (Blocks.size() == OldSize) return false; } DenseMap<const Value*, Value*> ValueMap; Module *ProgClone = CloneModule(BD.getProgram(), ValueMap); Module *ToExtract = SplitFunctionsOutOfModule(ProgClone, MiscompiledFunctions, ValueMap); Module *Extracted = BD.ExtractMappedBlocksFromModule(Blocks, ToExtract); if (Extracted == 0) { // Weird, extraction should have worked. errs() << "Nondeterministic problem extracting blocks??\n"; delete ProgClone; delete ToExtract; return false; } // Otherwise, block extraction succeeded. Link the two program fragments back // together. delete ToExtract; std::vector<std::pair<std::string, const FunctionType*> > MisCompFunctions; for (Module::iterator I = Extracted->begin(), E = Extracted->end(); I != E; ++I) if (!I->isDeclaration()) MisCompFunctions.push_back(std::make_pair(I->getName(), I->getFunctionType())); std::string ErrorMsg; if (Linker::LinkModules(ProgClone, Extracted, &ErrorMsg)) { errs() << BD.getToolName() << ": Error linking modules together:" << ErrorMsg << '\n'; exit(1); } delete Extracted; // Set the new program and delete the old one. BD.setNewProgram(ProgClone); // Update the list of miscompiled functions. MiscompiledFunctions.clear(); for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) { Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first); assert(NewF && "Function not found??"); assert(NewF->getFunctionType() == MisCompFunctions[i].second && "Function has wrong type??"); MiscompiledFunctions.push_back(NewF); } return true; }