From 1c039155009c04f4efc37fd76ad75d1342edfccd Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Tue, 6 Sep 2016 17:18:22 +0000 Subject: [PATCH] bugpoint: Return Errors instead of passing around strings This replaces the threading of `std::string &Error` through all of these APIs with checked Error returns instead. There are very few places here that actually emit any errors right now, but threading the APIs through will allow us to replace a bunch of exit(1)'s that are scattered through this code with proper error handling. This is more or less NFC, but does move around where a couple of error messages are printed out. llvm-svn: 280720 --- llvm/tools/bugpoint/BugDriver.cpp | 49 ++--- llvm/tools/bugpoint/BugDriver.h | 67 +++--- llvm/tools/bugpoint/CrashDebugger.cpp | 161 +++++++------- llvm/tools/bugpoint/ExecutionDriver.cpp | 112 +++++----- llvm/tools/bugpoint/FindBugs.cpp | 49 ++--- llvm/tools/bugpoint/ListReducer.h | 49 +++-- llvm/tools/bugpoint/Miscompilation.cpp | 370 +++++++++++++++++--------------- llvm/tools/bugpoint/ToolRunner.cpp | 173 ++++++++------- llvm/tools/bugpoint/ToolRunner.h | 63 +++--- llvm/tools/bugpoint/bugpoint.cpp | 8 +- 10 files changed, 558 insertions(+), 543 deletions(-) diff --git a/llvm/tools/bugpoint/BugDriver.cpp b/llvm/tools/bugpoint/BugDriver.cpp index dde5521..78f3529 100644 --- a/llvm/tools/bugpoint/BugDriver.cpp +++ b/llvm/tools/bugpoint/BugDriver.cpp @@ -146,11 +146,11 @@ bool BugDriver::addSources(const std::vector &Filenames) { /// run - The top level method that is invoked after all of the instance /// variables are set up from command line arguments. /// -bool BugDriver::run(std::string &ErrMsg) { +Error BugDriver::run() { if (run_find_bugs) { // Rearrange the passes and apply them to the program. Repeat this process // until the user kills the program or we find a bug. - return runManyPasses(PassesToRun, ErrMsg); + return runManyPasses(PassesToRun); } // If we're not running as a child, the first thing that we must do is @@ -167,16 +167,14 @@ bool BugDriver::run(std::string &ErrMsg) { } // Set up the execution environment, selecting a method to run LLVM bitcode. - if (initializeExecutionEnvironment()) - return true; + if (Error E = initializeExecutionEnvironment()) + return E; // Test to see if we have a code generator crash. outs() << "Running the code generator to test for a crash: "; - std::string Error; - compileProgram(Program, &Error); - if (!Error.empty()) { - outs() << Error; - return debugCodeGeneratorCrash(ErrMsg); + if (Error E = compileProgram(Program)) { + outs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } outs() << '\n'; @@ -187,8 +185,9 @@ bool BugDriver::run(std::string &ErrMsg) { bool CreatedOutput = false; if (ReferenceOutputFile.empty()) { outs() << "Generating reference output from raw program: "; - if (!createReferenceFile(Program)) { - return debugCodeGeneratorCrash(ErrMsg); + if (Error E = createReferenceFile(Program)) { + errs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } CreatedOutput = true; } @@ -202,29 +201,27 @@ bool BugDriver::run(std::string &ErrMsg) { // matches, then we assume there is a miscompilation bug and try to // diagnose it. outs() << "*** Checking the code generator...\n"; - bool Diff = diffProgram(Program, "", "", false, &Error); - if (!Error.empty()) { - errs() << Error; - return debugCodeGeneratorCrash(ErrMsg); + Expected Diff = diffProgram(Program, "", "", false); + if (Error E = Diff.takeError()) { + errs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } - if (!Diff) { + if (!*Diff) { outs() << "\n*** Output matches: Debugging miscompilation!\n"; - debugMiscompilation(&Error); - if (!Error.empty()) { - errs() << Error; - return debugCodeGeneratorCrash(ErrMsg); + if (Error E = debugMiscompilation()) { + errs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } - return false; + return Error::success(); } outs() << "\n*** Input program does not match reference diff!\n"; outs() << "Debugging code generator problem!\n"; - bool Failure = debugCodeGenerator(&Error); - if (!Error.empty()) { - errs() << Error; - return debugCodeGeneratorCrash(ErrMsg); + if (Error E = debugCodeGenerator()) { + errs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } - return Failure; + return Error::success(); } void llvm::PrintFunctionList(const std::vector &Funcs) { diff --git a/llvm/tools/bugpoint/BugDriver.h b/llvm/tools/bugpoint/BugDriver.h index 7ae2d5c..af80e8b 100644 --- a/llvm/tools/bugpoint/BugDriver.h +++ b/llvm/tools/bugpoint/BugDriver.h @@ -17,6 +17,7 @@ #define LLVM_TOOLS_BUGPOINT_BUGDRIVER_H #include "llvm/IR/ValueMap.h" +#include "llvm/Support/Error.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include #include @@ -85,23 +86,23 @@ public: /// variables are set up from command line arguments. The \p as_child argument /// indicates whether the driver is to run in parent mode or child mode. /// - bool run(std::string &ErrMsg); + Error run(); /// debugOptimizerCrash - This method is called when some optimizer pass /// crashes on input. It attempts to prune down the testcase to something /// reasonable, and figure out exactly which pass is crashing. /// - bool debugOptimizerCrash(const std::string &ID = "passes"); + Error debugOptimizerCrash(const std::string &ID = "passes"); /// 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. - bool debugCodeGeneratorCrash(std::string &Error); + Error debugCodeGeneratorCrash(); /// debugMiscompilation - This method is used when the passes selected are not /// crashing, but the generated output is semantically different from the /// input. - void debugMiscompilation(std::string *Error); + Error debugMiscompilation(); /// debugPassMiscompilation - This method is called when the specified pass /// miscompiles Program as input. It tries to reduce the testcase to @@ -115,13 +116,12 @@ public: /// compileSharedObject - This method creates a SharedObject from a given /// BitcodeFile for debugging a code generator. /// - std::string compileSharedObject(const std::string &BitcodeFile, - std::string &Error); + Expected compileSharedObject(const std::string &BitcodeFile); /// debugCodeGenerator - This method narrows down a module to a function or /// set of functions, using the CBE as a ``safe'' code generator for other /// functions that are not under consideration. - bool debugCodeGenerator(std::string *Error); + Error debugCodeGenerator(); /// isExecutingJIT - Returns true if bugpoint is currently testing the JIT /// @@ -150,46 +150,45 @@ public: /// the specified one as the current program. void setNewProgram(Module *M); - /// compileProgram - Try to compile the specified module, returning false and - /// setting Error if an error occurs. This is used for code generation + /// Try to compile the specified module. This is used for code generation /// crash testing. - /// - void compileProgram(Module *M, std::string *Error) const; + Error compileProgram(Module *M) const; /// executeProgram - This method runs "Program", capturing the output of the /// program to a file. A recommended filename may be optionally specified. /// - std::string executeProgram(const Module *Program, std::string OutputFilename, - std::string Bitcode, - const std::string &SharedObjects, - AbstractInterpreter *AI, std::string *Error) const; + Expected executeProgram(const Module *Program, + std::string OutputFilename, + std::string Bitcode, + const std::string &SharedObjects, + AbstractInterpreter *AI) const; /// executeProgramSafely - Used to create reference output with the "safe" /// backend, if reference output is not provided. If there is a problem with /// the code generator (e.g., llc crashes), this will return false and set /// Error. /// - std::string executeProgramSafely(const Module *Program, - const std::string &OutputFile, - std::string *Error) const; + Expected + executeProgramSafely(const Module *Program, + const std::string &OutputFile) const; /// createReferenceFile - calls compileProgram and then records the output /// into ReferenceOutputFile. Returns true if reference file created, false /// otherwise. Note: initializeExecutionEnvironment should be called BEFORE /// this function. /// - bool createReferenceFile(Module *M, const std::string &Filename = - "bugpoint.reference.out-%%%%%%%"); + Error createReferenceFile(Module *M, const std::string &Filename = + "bugpoint.reference.out-%%%%%%%"); /// diffProgram - This method executes the specified module and diffs the /// output against the file specified by ReferenceOutputFile. If the output /// is different, 1 is returned. If there is a problem with the code /// generator (e.g., llc crashes), this will return -1 and set Error. /// - bool diffProgram(const Module *Program, const std::string &BitcodeFile = "", - const std::string &SharedObj = "", - bool RemoveBitcode = false, - std::string *Error = nullptr) const; + Expected diffProgram(const Module *Program, + const std::string &BitcodeFile = "", + const std::string &SharedObj = "", + bool RemoveBitcode = false) const; /// EmitProgressBitcode - This function is used to output M to a file named /// "bugpoint-ID.bc". @@ -257,17 +256,13 @@ public: return runPasses(M, PassesToRun, Filename, true); } - /// runManyPasses - Take the specified pass list and create different - /// combinations of passes to compile the program with. Compile the program - /// with - /// each set and mark test to see if it compiled correctly. If the passes - /// compiled correctly output nothing and rearrange the passes into a new - /// order. - /// If the passes did not compile correctly, output the command required to - /// recreate the failure. This returns true if a compiler error is found. - /// - bool runManyPasses(const std::vector &AllPasses, - std::string &ErrMsg); + /// Take the specified pass list and create different combinations of passes + /// to compile the program with. Compile the program with each set and mark + /// test to see if it compiled correctly. If the passes compiled correctly + /// output nothing and rearrange the passes into a new order. If the passes + /// did not compile correctly, output the command required to recreate the + /// failure. + Error runManyPasses(const std::vector &AllPasses); /// writeProgramToFile - This writes the current "Program" to the named /// bitcode file. If an error occurs, true is returned. @@ -280,7 +275,7 @@ private: /// initializeExecutionEnvironment - This method is used to set up the /// environment for executing LLVM programs. /// - bool initializeExecutionEnvironment(); + Error initializeExecutionEnvironment(); }; /// Given a bitcode or assembly input filename, parse and return it, or return diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp index 649cac7..e736739 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -66,19 +66,17 @@ class ReducePassList : public ListReducer { public: ReducePassList(BugDriver &bd) : BD(bd) {} - // doTest - 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. - // - TestResult doTest(std::vector &Removed, - std::vector &Kept, - std::string &Error) override; + // 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 doTest(std::vector &Removed, + std::vector &Kept) override; }; } -ReducePassList::TestResult +Expected ReducePassList::doTest(std::vector &Prefix, - std::vector &Suffix, std::string &Error) { + std::vector &Suffix) { std::string PrefixOutput; Module *OrigProgram = nullptr; if (!Prefix.empty()) { @@ -128,9 +126,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestGlobalVariables(Kept)) return KeepSuffix; if (!Prefix.empty() && TestGlobalVariables(Prefix)) @@ -198,9 +195,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestFuncs(Kept)) return KeepSuffix; if (!Prefix.empty() && TestFuncs(Prefix)) @@ -372,9 +368,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(BD), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -494,9 +489,8 @@ public: bool Direction) : BD(bd), TestFn(testFn), Direction(Direction) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -602,9 +596,8 @@ public: ReduceSimplifyCFG(BugDriver &bd, bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn), TTI(bd.getProgram()->getDataLayout()) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -697,9 +690,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestInsts(Kept)) return KeepSuffix; if (!Prefix.empty() && TestInsts(Prefix)) @@ -774,9 +766,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestNamedMDs(Kept)) return KeepSuffix; if (!Prefix.empty() && TestNamedMDs(Prefix)) @@ -844,9 +835,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector &Prefix, - std::vector &Kept, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Kept) override { if (!Kept.empty() && TestNamedMDOps(Kept)) return KeepSuffix; if (!Prefix.empty() && TestNamedMDOps(Prefix)) @@ -907,10 +897,9 @@ bool ReduceCrashingNamedMDOps::TestNamedMDOps( return false; } -static void ReduceGlobalInitializers(BugDriver &BD, - bool (*TestFn)(const BugDriver &, - Module *), - std::string &Error) { +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. @@ -952,8 +941,10 @@ static void ReduceGlobalInitializers(BugDriver &BD, << "variables in the testcase\n"; unsigned OldSize = GVs.size(); - ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs, Error); - assert(!Error.empty()); + Expected Result = + ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs); + if (Error E = Result.takeError()) + return E; if (GVs.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables"); @@ -961,11 +952,11 @@ static void ReduceGlobalInitializers(BugDriver &BD, } } } + return Error::success(); } -static void ReduceInsts(BugDriver &BD, - bool (*TestFn)(const BugDriver &, Module *), - std::string &Error) { +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) { @@ -976,13 +967,17 @@ static void ReduceInsts(BugDriver &BD, if (!isa(&I)) Insts.push_back(&I); - ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error); + Expected Result = + ReduceCrashingInstructions(BD, TestFn).reduceList(Insts); + if (Error E = Result.takeError()) + return E; } unsigned Simplification = 2; do { if (BugpointIsInterrupted) - return; + // 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'; @@ -1013,7 +1008,8 @@ static void ReduceInsts(BugDriver &BD, --InstructionsToSkipBeforeDeleting; } else { if (BugpointIsInterrupted) - return; + // TODO: Should this be some kind of interrupted error? + return Error::success(); if (I->isEHPad() || I->getType()->isTokenTy()) continue; @@ -1040,18 +1036,19 @@ static void ReduceInsts(BugDriver &BD, } 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) { +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) - ReduceGlobalInitializers(BD, TestFn, Error); + if (Error E = ReduceGlobalInitializers(BD, TestFn)) + return E; // Now try to reduce the number of functions in the module to something small. std::vector Functions; @@ -1064,7 +1061,10 @@ static bool DebugACrash(BugDriver &BD, "in the testcase\n"; unsigned OldSize = Functions.size(); - ReduceCrashingFunctions(BD, TestFn).reduceList(Functions, Error); + Expected Result = + ReduceCrashingFunctions(BD, TestFn).reduceList(Functions); + if (Error E = Result.takeError()) + return E; if (Functions.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); @@ -1078,8 +1078,13 @@ static bool DebugACrash(BugDriver &BD, for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); - ReduceCrashingConditionals(BD, TestFn, true).reduceList(Blocks, Error); - ReduceCrashingConditionals(BD, TestFn, false).reduceList(Blocks, Error); + Expected 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"); } @@ -1095,7 +1100,9 @@ static bool DebugACrash(BugDriver &BD, for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); - ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks, Error); + Expected Result = ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks); + if (Error E = Result.takeError()) + return E; if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks"); } @@ -1106,7 +1113,9 @@ static bool DebugACrash(BugDriver &BD, for (BasicBlock &BB : F) Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); - ReduceSimplifyCFG(BD, TestFn).reduceList(Blocks, Error); + Expected Result = ReduceSimplifyCFG(BD, TestFn).reduceList(Blocks); + if (Error E = Result.takeError()) + return E; if (Blocks.size() < OldSize) BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplifycfg"); } @@ -1114,7 +1123,8 @@ static bool DebugACrash(BugDriver &BD, // 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) - ReduceInsts(BD, TestFn, Error); + if (Error E = ReduceInsts(BD, TestFn)) + return E; if (!NoNamedMDRM) { if (!BugpointIsInterrupted) { @@ -1124,7 +1134,10 @@ static bool DebugACrash(BugDriver &BD, std::vector NamedMDNames; for (auto &NamedMD : BD.getProgram()->named_metadata()) NamedMDNames.push_back(NamedMD.getName().str()); - ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error); + Expected Result = + ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames); + if (Error E = Result.takeError()) + return E; } if (!BugpointIsInterrupted) { @@ -1134,7 +1147,10 @@ static bool DebugACrash(BugDriver &BD, for (auto &NamedMD : BD.getProgram()->named_metadata()) for (auto op : NamedMD.operands()) NamedMDOps.push_back(op); - ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error); + Expected Result = + ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps); + if (Error E = Result.takeError()) + return E; } BD.EmitProgressBitcode(BD.getProgram(), "reduced-named-md"); } @@ -1155,7 +1171,7 @@ static bool DebugACrash(BugDriver &BD, BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified"); - return false; + return Error::success(); } static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) { @@ -1166,14 +1182,15 @@ static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) { /// It attempts to prune down the testcase to something reasonable, and figure /// out exactly which pass is crashing. /// -bool BugDriver::debugOptimizerCrash(const std::string &ID) { +Error BugDriver::debugOptimizerCrash(const std::string &ID) { outs() << "\n*** Debugging optimizer crash!\n"; - std::string Error; // Reduce the list of passes which causes the optimizer to crash... - if (!BugpointIsInterrupted && !DontReducePassList) - ReducePassList(*this).reduceList(PassesToRun, Error); - assert(Error.empty()); + if (!BugpointIsInterrupted && !DontReducePassList) { + Expected Result = ReducePassList(*this).reduceList(PassesToRun); + if (Error E = Result.takeError()) + return E; + } outs() << "\n*** Found crashing pass" << (PassesToRun.size() == 1 ? ": " : "es: ") @@ -1181,19 +1198,17 @@ bool BugDriver::debugOptimizerCrash(const std::string &ID) { EmitProgressBitcode(Program, ID); - bool Success = DebugACrash(*this, TestForOptimizerCrash, Error); - assert(Error.empty()); - return Success; + return DebugACrash(*this, TestForOptimizerCrash); } static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { - std::string Error; - BD.compileProgram(M, &Error); - if (!Error.empty()) { + if (Error E = BD.compileProgram(M)) { if (VerboseErrors) - errs() << Error << "\n"; - else + errs() << toString(std::move(E)) << "\n"; + else { + consumeError(std::move(E)); errs() << "\n"; + } return true; // Tool is still crashing. } errs() << '\n'; @@ -1203,8 +1218,8 @@ static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { /// 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. -bool BugDriver::debugCodeGeneratorCrash(std::string &Error) { +Error BugDriver::debugCodeGeneratorCrash() { errs() << "*** Debugging code generator crash!\n"; - return DebugACrash(*this, TestForCodeGenCrash, Error); + return DebugACrash(*this, TestForCodeGenCrash); } diff --git a/llvm/tools/bugpoint/ExecutionDriver.cpp b/llvm/tools/bugpoint/ExecutionDriver.cpp index 695461e..b0f3880 100644 --- a/llvm/tools/bugpoint/ExecutionDriver.cpp +++ b/llvm/tools/bugpoint/ExecutionDriver.cpp @@ -141,7 +141,7 @@ cl::list CCToolArgv("gcc-tool-args", cl::Positional, /// initializeExecutionEnvironment - This method is used to set up the /// environment for executing LLVM programs. /// -bool BugDriver::initializeExecutionEnvironment() { +Error BugDriver::initializeExecutionEnvironment() { outs() << "Initializing execution environment: "; // Create an instance of the AbstractInterpreter interface as specified on @@ -261,14 +261,17 @@ bool BugDriver::initializeExecutionEnvironment() { } // If there was an error creating the selected interpreter, quit with error. - return Interpreter == nullptr; + if (Interpreter == nullptr) + return make_error("Failed to init execution environment", + inconvertibleErrorCode()); + return Error::success(); } /// compileProgram - Try to compile the specified module, returning false and /// setting Error if an error occurs. This is used for code generation /// crash testing. /// -void BugDriver::compileProgram(Module *M, std::string *Error) const { +Error BugDriver::compileProgram(Module *M) const { // Emit the program to a bitcode file... SmallString<128> BitcodeFile; int BitcodeFD; @@ -289,17 +292,18 @@ void BugDriver::compileProgram(Module *M, std::string *Error) const { FileRemover BitcodeFileRemover(BitcodeFile.str(), !SaveTemps); // Actually compile the program! - Interpreter->compileProgram(BitcodeFile.str(), Error, Timeout, MemoryLimit); + return Interpreter->compileProgram(BitcodeFile.str(), Timeout, MemoryLimit); } /// executeProgram - This method runs "Program", capturing the output of the /// program to a file, returning the filename of the file. A recommended /// filename may be optionally specified. /// -std::string -BugDriver::executeProgram(const Module *Program, std::string OutputFile, - std::string BitcodeFile, const std::string &SharedObj, - AbstractInterpreter *AI, std::string *Error) const { +Expected BugDriver::executeProgram(const Module *Program, + std::string OutputFile, + std::string BitcodeFile, + const std::string &SharedObj, + AbstractInterpreter *AI) const { if (!AI) AI = Interpreter; assert(AI && "Interpreter should have been created already!"); @@ -347,13 +351,13 @@ BugDriver::executeProgram(const Module *Program, std::string OutputFile, if (!SharedObj.empty()) SharedObjs.push_back(SharedObj); - int RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile, OutputFile, - Error, AdditionalLinkerArgs, SharedObjs, - Timeout, MemoryLimit); - if (!Error->empty()) - return OutputFile; + Expected RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile, + OutputFile, AdditionalLinkerArgs, + SharedObjs, Timeout, MemoryLimit); + if (Error E = RetVal.takeError()) + return std::move(E); - if (RetVal == -1) { + if (*RetVal == -1) { errs() << ""; static bool FirstTimeout = true; if (FirstTimeout) { @@ -372,7 +376,7 @@ BugDriver::executeProgram(const Module *Program, std::string OutputFile, if (AppendProgramExitCode) { std::ofstream outFile(OutputFile.c_str(), std::ios_base::app); - outFile << "exit " << RetVal << '\n'; + outFile << "exit " << *RetVal << '\n'; outFile.close(); } @@ -383,29 +387,27 @@ BugDriver::executeProgram(const Module *Program, std::string OutputFile, /// executeProgramSafely - Used to create reference output with the "safe" /// backend, if reference output is not provided. /// -std::string BugDriver::executeProgramSafely(const Module *Program, - const std::string &OutputFile, - std::string *Error) const { - return executeProgram(Program, OutputFile, "", "", SafeInterpreter, Error); +Expected +BugDriver::executeProgramSafely(const Module *Program, + const std::string &OutputFile) const { + return executeProgram(Program, OutputFile, "", "", SafeInterpreter); } -std::string BugDriver::compileSharedObject(const std::string &BitcodeFile, - std::string &Error) { +Expected +BugDriver::compileSharedObject(const std::string &BitcodeFile) { assert(Interpreter && "Interpreter should have been created already!"); std::string OutputFile; // Using the known-good backend. - CC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile, Error); - if (!Error.empty()) - return ""; + Expected FT = + SafeInterpreter->OutputCode(BitcodeFile, OutputFile); + if (Error E = FT.takeError()) + return std::move(E); std::string SharedObjectFile; - bool Failure = cc->MakeSharedObject(OutputFile, FT, SharedObjectFile, - AdditionalLinkerArgs, Error); - if (!Error.empty()) - return ""; - if (Failure) - exit(1); + if (Error E = cc->MakeSharedObject(OutputFile, *FT, SharedObjectFile, + AdditionalLinkerArgs)) + return std::move(E); // Remove the intermediate C file sys::fs::remove(OutputFile); @@ -418,25 +420,27 @@ std::string BugDriver::compileSharedObject(const std::string &BitcodeFile, /// otherwise. Note: initializeExecutionEnvironment should be called BEFORE /// this function. /// -bool BugDriver::createReferenceFile(Module *M, const std::string &Filename) { - std::string Error; - compileProgram(Program, &Error); - if (!Error.empty()) - return false; +Error BugDriver::createReferenceFile(Module *M, const std::string &Filename) { + if (Error E = compileProgram(Program)) + return E; - ReferenceOutputFile = executeProgramSafely(Program, Filename, &Error); - if (!Error.empty()) { - errs() << Error; + Expected Result = executeProgramSafely(Program, Filename); + if (Error E = Result.takeError()) { if (Interpreter != SafeInterpreter) { - errs() << "*** There is a bug running the \"safe\" backend. Either" - << " debug it (for example with the -run-jit bugpoint option," - << " if JIT is being used as the \"safe\" backend), or fix the" - << " error some other way.\n"; + E = joinErrors( + std::move(E), + make_error( + "*** There is a bug running the \"safe\" backend. Either" + " debug it (for example with the -run-jit bugpoint option," + " if JIT is being used as the \"safe\" backend), or fix the" + " error some other way.\n", + inconvertibleErrorCode())); } - return false; + return E; } + ReferenceOutputFile = *Result; outs() << "\nReference output is: " << ReferenceOutputFile << "\n\n"; - return true; + return Error::success(); } /// diffProgram - This method executes the specified module and diffs the @@ -444,19 +448,19 @@ bool BugDriver::createReferenceFile(Module *M, const std::string &Filename) { /// is different, 1 is returned. If there is a problem with the code /// generator (e.g., llc crashes), this will set ErrMsg. /// -bool BugDriver::diffProgram(const Module *Program, - const std::string &BitcodeFile, - const std::string &SharedObject, bool RemoveBitcode, - std::string *ErrMsg) const { +Expected BugDriver::diffProgram(const Module *Program, + const std::string &BitcodeFile, + const std::string &SharedObject, + bool RemoveBitcode) const { // Execute the program, generating an output file... - std::string Output( - executeProgram(Program, "", BitcodeFile, SharedObject, nullptr, ErrMsg)); - if (!ErrMsg->empty()) - return false; + Expected Output = + executeProgram(Program, "", BitcodeFile, SharedObject, nullptr); + if (Error E = Output.takeError()) + return std::move(E); std::string Error; bool FilesDifferent = false; - if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, Output, + if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, *Output, AbsTolerance, RelTolerance, &Error)) { if (Diff == 2) { errs() << "While diffing output: " << Error << '\n'; @@ -465,7 +469,7 @@ bool BugDriver::diffProgram(const Module *Program, FilesDifferent = true; } else { // Remove the generated output if there are no differences. - sys::fs::remove(Output); + sys::fs::remove(*Output); } // Remove the bitcode file if we are supposed to. diff --git a/llvm/tools/bugpoint/FindBugs.cpp b/llvm/tools/bugpoint/FindBugs.cpp index 3c345c5..156f4d0 100644 --- a/llvm/tools/bugpoint/FindBugs.cpp +++ b/llvm/tools/bugpoint/FindBugs.cpp @@ -23,27 +23,20 @@ #include using namespace llvm; -/// runManyPasses - Take the specified pass list and create different -/// combinations of passes to compile the program with. Compile the program with -/// each set and mark test to see if it compiled correctly. If the passes -/// compiled correctly output nothing and rearrange the passes into a new order. -/// If the passes did not compile correctly, output the command required to -/// recreate the failure. This returns true if a compiler error is found. -/// -bool BugDriver::runManyPasses(const std::vector &AllPasses, - std::string &ErrMsg) { +Error +BugDriver::runManyPasses(const std::vector &AllPasses) { setPassesToRun(AllPasses); outs() << "Starting bug finding procedure...\n\n"; // Creating a reference output if necessary - if (initializeExecutionEnvironment()) - return false; + if (Error E = initializeExecutionEnvironment()) + return E; outs() << "\n"; if (ReferenceOutputFile.empty()) { outs() << "Generating reference output from raw program: \n"; - if (!createReferenceFile(Program)) - return false; + if (Error E = createReferenceFile(Program)) + return E; } srand(time(nullptr)); @@ -67,8 +60,7 @@ bool BugDriver::runManyPasses(const std::vector &AllPasses, if (runPasses(Program, PassesToRun, Filename, false)) { outs() << "\n"; outs() << "Optimizer passes caused failure!\n\n"; - debugOptimizerCrash(); - return true; + return debugOptimizerCrash(); } else { outs() << "Combination " << num << " optimized successfully!\n"; } @@ -77,12 +69,10 @@ bool BugDriver::runManyPasses(const std::vector &AllPasses, // Step 3: Compile the optimized code. // outs() << "Running the code generator to test for a crash: "; - std::string Error; - compileProgram(Program, &Error); - if (!Error.empty()) { + if (Error E = compileProgram(Program)) { outs() << "\n*** compileProgram threw an exception: "; - outs() << Error; - return debugCodeGeneratorCrash(ErrMsg); + outs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } outs() << '\n'; @@ -91,17 +81,16 @@ bool BugDriver::runManyPasses(const std::vector &AllPasses, // output (created above). // outs() << "*** Checking if passes caused miscompliation:\n"; - bool Diff = diffProgram(Program, Filename, "", false, &Error); - if (Error.empty() && Diff) { - outs() << "\n*** diffProgram returned true!\n"; - debugMiscompilation(&Error); - if (Error.empty()) - return true; + Expected Diff = diffProgram(Program, Filename, "", false); + if (Error E = Diff.takeError()) { + errs() << toString(std::move(E)); + return debugCodeGeneratorCrash(); } - if (!Error.empty()) { - errs() << Error; - debugCodeGeneratorCrash(ErrMsg); - return true; + if (*Diff) { + outs() << "\n*** diffProgram returned true!\n"; + Error E = debugMiscompilation(); + if (!E) + return Error::success(); } outs() << "\n*** diff'd output matches!\n"; diff --git a/llvm/tools/bugpoint/ListReducer.h b/llvm/tools/bugpoint/ListReducer.h index 78af1ae..621a072 100644 --- a/llvm/tools/bugpoint/ListReducer.h +++ b/llvm/tools/bugpoint/ListReducer.h @@ -15,7 +15,7 @@ #ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H #define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H -#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -27,10 +27,9 @@ extern bool BugpointIsInterrupted; template struct ListReducer { enum TestResult { - NoFailure, // No failure of the predicate was detected - KeepSuffix, // The suffix alone satisfies the predicate - KeepPrefix, // The prefix alone satisfies the predicate - InternalError // Encountered an error trying to run the predicate + NoFailure, // No failure of the predicate was detected + KeepSuffix, // The suffix alone satisfies the predicate + KeepPrefix // The prefix alone satisfies the predicate }; virtual ~ListReducer() {} @@ -39,16 +38,19 @@ template struct ListReducer { /// test desired. The testcase is only required to test to see if the Kept /// list still satisfies the property, but if it is going to check the prefix /// anyway, it can. - virtual TestResult doTest(std::vector &Prefix, std::vector &Kept, - std::string &Error) = 0; + virtual Expected doTest(std::vector &Prefix, + std::vector &Kept) = 0; /// This function attempts to reduce the length of the specified list while /// still maintaining the "test" property. This is the core of the "work" /// that bugpoint does. - bool reduceList(std::vector &TheList, std::string &Error) { + Expected reduceList(std::vector &TheList) { std::vector empty; std::srand(0x6e5ea738); // Seed the random number generator - switch (doTest(TheList, empty, Error)) { + Expected Result = doTest(TheList, empty); + if (Error E = Result.takeError()) + return std::move(E); + switch (*Result) { case KeepPrefix: if (TheList.size() == 1) // we are done, it's the base case and it fails return true; @@ -62,10 +64,6 @@ template struct ListReducer { case NoFailure: return false; // there is no failure with the full set of passes/funcs! - - case InternalError: - assert(!Error.empty()); - return true; } // Maximal number of allowed splitting iterations, @@ -96,8 +94,14 @@ template struct ListReducer { std::vector ShuffledList(TheList); std::random_shuffle(ShuffledList.begin(), ShuffledList.end()); errs() << "\n\n*** Testing shuffled set...\n\n"; - // Check that random shuffle doesn't loose the bug - if (doTest(ShuffledList, empty, Error) == KeepPrefix) { + // Check that random shuffle doesn't lose the bug + Expected Result = doTest(ShuffledList, empty); + // TODO: Previously, this error was ignored and we treated it as if + // shuffling hid the bug. This should really either be consumeError if + // that behaviour was sensible, or we should propagate the error. + assert(!Result.takeError() && "Shuffling caused internal error?"); + + if (*Result == KeepPrefix) { // If the bug is still here, use the shuffled list. TheList.swap(ShuffledList); MidTop = TheList.size(); @@ -116,7 +120,10 @@ template struct ListReducer { std::vector Prefix(TheList.begin(), TheList.begin() + Mid); std::vector Suffix(TheList.begin() + Mid, TheList.end()); - switch (doTest(Prefix, Suffix, Error)) { + Expected Result = doTest(Prefix, Suffix); + if (Error E = Result.takeError()) + return std::move(E); + switch (*Result) { case KeepSuffix: // The property still holds. We can just drop the prefix elements, and // shorten the list to the "kept" elements. @@ -140,10 +147,7 @@ template struct ListReducer { MidTop = Mid; NumOfIterationsWithoutProgress++; break; - case InternalError: - return true; // Error was set by doTest. } - assert(Error.empty() && "doTest did not return InternalError for error"); } // Probability of backjumping from the trimming loop back to the binary @@ -179,14 +183,15 @@ template struct ListReducer { std::vector TestList(TheList); TestList.erase(TestList.begin() + i); - if (doTest(EmptyList, TestList, Error) == KeepSuffix) { + Expected Result = doTest(EmptyList, TestList); + if (Error E = Result.takeError()) + return std::move(E); + if (*Result == KeepSuffix) { // We can trim down the list! TheList.swap(TestList); --i; // Don't skip an element of the list Changed = true; } - if (!Error.empty()) - return true; } if (TrimIterations >= MaxTrimIterationsWithoutBackJump) break; diff --git a/llvm/tools/bugpoint/Miscompilation.cpp b/llvm/tools/bugpoint/Miscompilation.cpp index 9069e00..20acb30 100644 --- a/llvm/tools/bugpoint/Miscompilation.cpp +++ b/llvm/tools/bugpoint/Miscompilation.cpp @@ -50,19 +50,17 @@ class ReduceMiscompilingPasses : public ListReducer { public: ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {} - TestResult doTest(std::vector &Prefix, - std::vector &Suffix, - std::string &Error) override; + Expected doTest(std::vector &Prefix, + std::vector &Suffix) override; }; } // end anonymous namespace /// TestResult - After passes have been split into a test group and a control /// group, see if they still break the program. /// -ReduceMiscompilingPasses::TestResult +Expected ReduceMiscompilingPasses::doTest(std::vector &Prefix, - std::vector &Suffix, - std::string &Error) { + std::vector &Suffix) { // First, run the program with just the Suffix passes. If it is still broken // with JUST the kept passes, discard the prefix passes. outs() << "Checking to see if '" << getPassesString(Suffix) @@ -75,15 +73,18 @@ ReduceMiscompilingPasses::doTest(std::vector &Prefix, << " on the input program!\n"; BD.setPassesToRun(Suffix); BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false); - exit(BD.debugOptimizerCrash()); + // TODO: This should propagate the error instead of exiting. + if (Error E = BD.debugOptimizerCrash()) + exit(1); + exit(0); } // Check to see if the finished program matches the reference output... - bool Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", - true /*delete bitcode*/, &Error); - if (!Error.empty()) - return InternalError; - if (Diff) { + Expected Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", + true /*delete bitcode*/); + if (Error E = Diff.takeError()) + return std::move(E); + if (*Diff) { outs() << " nope.\n"; if (Suffix.empty()) { errs() << BD.getToolName() << ": I'm confused: the test fails when " @@ -114,14 +115,17 @@ ReduceMiscompilingPasses::doTest(std::vector &Prefix, << " on the input program!\n"; BD.setPassesToRun(Prefix); BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false); - exit(BD.debugOptimizerCrash()); + // TODO: This should propagate the error instead of exiting. + if (Error E = BD.debugOptimizerCrash()) + exit(1); + exit(0); } // If the prefix maintains the predicate by itself, only keep the prefix! - Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", false, &Error); - if (!Error.empty()) - return InternalError; - if (Diff) { + Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", false); + if (Error E = Diff.takeError()) + return std::move(E); + if (*Diff) { outs() << " nope.\n"; sys::fs::remove(BitcodeResult); return KeepPrefix; @@ -156,15 +160,18 @@ ReduceMiscompilingPasses::doTest(std::vector &Prefix, << " on the input program!\n"; BD.setPassesToRun(Suffix); BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false); - exit(BD.debugOptimizerCrash()); + // TODO: This should propagate the error instead of exiting. + if (Error E = BD.debugOptimizerCrash()) + exit(1); + exit(0); } // Run the result... Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", - true /*delete bitcode*/, &Error); - if (!Error.empty()) - return InternalError; - if (Diff) { + true /*delete bitcode*/); + if (Error E = Diff.takeError()) + return std::move(E); + if (*Diff) { outs() << " nope.\n"; return KeepSuffix; } @@ -179,36 +186,36 @@ ReduceMiscompilingPasses::doTest(std::vector &Prefix, namespace { class ReduceMiscompilingFunctions : public ListReducer { BugDriver &BD; - bool (*TestFn)(BugDriver &, std::unique_ptr, std::unique_ptr, - std::string &); + Expected (*TestFn)(BugDriver &, std::unique_ptr, + std::unique_ptr); public: ReduceMiscompilingFunctions(BugDriver &bd, - bool (*F)(BugDriver &, std::unique_ptr, - std::unique_ptr, std::string &)) + Expected (*F)(BugDriver &, + std::unique_ptr, + std::unique_ptr)) : BD(bd), TestFn(F) {} - TestResult doTest(std::vector &Prefix, - std::vector &Suffix, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Suffix) override { if (!Suffix.empty()) { - bool Ret = TestFuncs(Suffix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected Ret = TestFuncs(Suffix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepSuffix; } if (!Prefix.empty()) { - bool Ret = TestFuncs(Prefix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected Ret = TestFuncs(Prefix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepPrefix; } return NoFailure; } - bool TestFuncs(const std::vector &Prefix, std::string &Error); + Expected TestFuncs(const std::vector &Prefix); }; } // end anonymous namespace @@ -218,27 +225,27 @@ public: /// 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 testMergedProgram(const BugDriver &BD, - std::unique_ptr M1, - std::unique_ptr M2, - std::string &Error, - bool &Broken) { +static Expected> +testMergedProgram(const BugDriver &BD, std::unique_ptr M1, + std::unique_ptr M2, bool &Broken) { if (Linker::linkModules(*M1, std::move(M2))) + // TODO: Shouldn't we thread the error up instead of exiting? exit(1); // Execute the program. - Broken = BD.diffProgram(M1.get(), "", "", false, &Error); - if (!Error.empty()) - return nullptr; - return M1; + Expected Diff = BD.diffProgram(M1.get(), "", "", false); + if (Error E = Diff.takeError()) + return std::move(E); + Broken = *Diff; + return std::move(M1); } /// TestFuncs - split functions in a Module into two groups: those that are /// under consideration for miscompilation vs. those that are not, and test /// accordingly. Each group of functions becomes a separate Module. /// -bool ReduceMiscompilingFunctions::TestFuncs( - const std::vector &Funcs, std::string &Error) { +Expected +ReduceMiscompilingFunctions::TestFuncs(const std::vector &Funcs) { // Test to see if the function is misoptimized if we ONLY run it on the // functions listed in Funcs. outs() << "Checking to see if the program is misoptimized when " @@ -271,8 +278,8 @@ bool ReduceMiscompilingFunctions::TestFuncs( std::unique_ptr ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap); - bool Broken = - TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize), Error); + Expected Broken = + TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize)); delete BD.swapProgramIn(Orig); @@ -295,11 +302,11 @@ static void DisambiguateGlobalSymbols(Module *M) { /// 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 &, std::unique_ptr, - std::unique_ptr, std::string &), - std::vector &MiscompiledFunctions, - std::string &Error) { +static Expected +ExtractLoops(BugDriver &BD, + Expected (*TestFn)(BugDriver &, std::unique_ptr, + std::unique_ptr), + std::vector &MiscompiledFunctions) { bool MadeChange = false; while (1) { if (BugpointIsInterrupted) @@ -328,14 +335,16 @@ static bool ExtractLoops(BugDriver &BD, // extraction. AbstractInterpreter *AI = BD.switchToSafeInterpreter(); bool Failure; - std::unique_ptr New = + Expected> New = testMergedProgram(BD, std::move(ToOptimizeLoopExtracted), - std::move(ToNotOptimize), Error, Failure); - if (!New) + std::move(ToNotOptimize), Failure); + if (Error E = New.takeError()) + return std::move(E); + if (!*New) return false; // Delete the original and set the new program. - Module *Old = BD.swapProgramIn(New.release()); + Module *Old = BD.swapProgramIn(New->release()); for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) MiscompiledFunctions[i] = cast(VMap[MiscompiledFunctions[i]]); delete Old; @@ -372,15 +381,15 @@ static bool ExtractLoops(BugDriver &BD, for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) MiscompiledFunctions[i] = cast(VMap[MiscompiledFunctions[i]]); - Failure = TestFn(BD, std::move(ToOptimizeLoopExtracted), - std::move(ToNotOptimize), Error); - if (!Error.empty()) - return false; + Expected Result = TestFn(BD, std::move(ToOptimizeLoopExtracted), + std::move(ToNotOptimize)); + if (Error E = Result.takeError()) + return std::move(E); ToOptimizeLoopExtracted = std::move(TOLEBackup); ToNotOptimize = std::move(TNOBackup); - if (!Failure) { + if (!*Result) { 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. @@ -441,46 +450,46 @@ static bool ExtractLoops(BugDriver &BD, namespace { class ReduceMiscompiledBlocks : public ListReducer { BugDriver &BD; - bool (*TestFn)(BugDriver &, std::unique_ptr, std::unique_ptr, - std::string &); + Expected (*TestFn)(BugDriver &, std::unique_ptr, + std::unique_ptr); std::vector FunctionsBeingTested; public: ReduceMiscompiledBlocks(BugDriver &bd, - bool (*F)(BugDriver &, std::unique_ptr, - std::unique_ptr, std::string &), + Expected (*F)(BugDriver &, + std::unique_ptr, + std::unique_ptr), const std::vector &Fns) : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {} - TestResult doTest(std::vector &Prefix, - std::vector &Suffix, - std::string &Error) override { + Expected doTest(std::vector &Prefix, + std::vector &Suffix) override { if (!Suffix.empty()) { - bool Ret = TestFuncs(Suffix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected Ret = TestFuncs(Suffix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepSuffix; } if (!Prefix.empty()) { - bool Ret = TestFuncs(Prefix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected Ret = TestFuncs(Prefix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepPrefix; } return NoFailure; } - bool TestFuncs(const std::vector &BBs, std::string &Error); + Expected TestFuncs(const std::vector &BBs); }; } // end anonymous namespace /// TestFuncs - Extract all blocks for the miscompiled functions except for the /// specified blocks. If the problem still exists, return true. /// -bool ReduceMiscompiledBlocks::TestFuncs(const std::vector &BBs, - std::string &Error) { +Expected +ReduceMiscompiledBlocks::TestFuncs(const std::vector &BBs) { // Test to see if the function is misoptimized if we ONLY run it on the // functions listed in Funcs. outs() << "Checking to see if the program is misoptimized when all "; @@ -519,7 +528,7 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector &BBs, // or something, in which case bugpoint can't chase down this possibility. if (std::unique_ptr New = BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) { - bool Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize), Error); + Expected Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize)); delete BD.swapProgramIn(Orig); return Ret; } @@ -530,12 +539,11 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector &BBs, /// 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 &, std::unique_ptr, - std::unique_ptr, - std::string &), - std::vector &MiscompiledFunctions, - std::string &Error) { +static Expected +ExtractBlocks(BugDriver &BD, + Expected (*TestFn)(BugDriver &, std::unique_ptr, + std::unique_ptr), + std::vector &MiscompiledFunctions) { if (BugpointIsInterrupted) return false; @@ -550,17 +558,18 @@ static bool ExtractBlocks(BugDriver &BD, unsigned OldSize = Blocks.size(); // Check to see if all blocks are extractible first. - bool Ret = ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) - .TestFuncs(std::vector(), Error); - if (!Error.empty()) - return false; - if (Ret) { + Expected Ret = ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) + .TestFuncs(std::vector()); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) { Blocks.clear(); } else { - ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) - .reduceList(Blocks, Error); - if (!Error.empty()) - return false; + Expected Ret = + ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) + .reduceList(Blocks); + if (Error E = Ret.takeError()) + return std::move(E); if (Blocks.size() == OldSize) return false; } @@ -611,11 +620,10 @@ static bool ExtractBlocks(BugDriver &BD, /// This is a generic driver to narrow down miscompilations, either in an /// optimization or a code generator. /// -static std::vector -DebugAMiscompilation(BugDriver &BD, - bool (*TestFn)(BugDriver &, std::unique_ptr, - std::unique_ptr, std::string &), - std::string &Error) { +static Expected> DebugAMiscompilation( + BugDriver &BD, + Expected (*TestFn)(BugDriver &, std::unique_ptr, + std::unique_ptr)) { // 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 @@ -627,12 +635,13 @@ DebugAMiscompilation(BugDriver &BD, MiscompiledFunctions.push_back(&F); // Do the reduction... - if (!BugpointIsInterrupted) - ReduceMiscompilingFunctions(BD, TestFn) - .reduceList(MiscompiledFunctions, Error); - if (!Error.empty()) { - errs() << "\n***Cannot reduce functions: "; - return MiscompiledFunctions; + if (!BugpointIsInterrupted) { + Expected Ret = ReduceMiscompilingFunctions(BD, TestFn) + .reduceList(MiscompiledFunctions); + if (Error E = Ret.takeError()) { + errs() << "\n***Cannot reduce functions: "; + return std::move(E); + } } outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") @@ -644,20 +653,20 @@ DebugAMiscompilation(BugDriver &BD, // trigger the problem. if (!BugpointIsInterrupted && !DisableLoopExtraction) { - bool Ret = ExtractLoops(BD, TestFn, MiscompiledFunctions, Error); - if (!Error.empty()) - return MiscompiledFunctions; - if (Ret) { + Expected Ret = ExtractLoops(BD, TestFn, MiscompiledFunctions); + if (Error E = Ret.takeError()) + return std::move(E); + 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; + Ret = ReduceMiscompilingFunctions(BD, TestFn) + .reduceList(MiscompiledFunctions); + if (Error E = Ret.takeError()) + return std::move(E); outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") @@ -668,19 +677,19 @@ DebugAMiscompilation(BugDriver &BD, } if (!BugpointIsInterrupted && !DisableBlockExtraction) { - bool Ret = ExtractBlocks(BD, TestFn, MiscompiledFunctions, Error); - if (!Error.empty()) - return MiscompiledFunctions; - if (Ret) { + Expected Ret = ExtractBlocks(BD, TestFn, MiscompiledFunctions); + if (Error E = Ret.takeError()) + return std::move(E); + 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; + Ret = ReduceMiscompilingFunctions(BD, TestFn) + .reduceList(MiscompiledFunctions); + if (Error E = Ret.takeError()) + return std::move(E); outs() << "\n*** The following function" << (MiscompiledFunctions.size() == 1 ? " is" : "s are") @@ -697,8 +706,8 @@ DebugAMiscompilation(BugDriver &BD, /// the program is misoptimized. If so, return true. In any case, both module /// arguments are deleted. /// -static bool TestOptimizer(BugDriver &BD, std::unique_ptr Test, - std::unique_ptr Safe, std::string &Error) { +static Expected TestOptimizer(BugDriver &BD, std::unique_ptr Test, + std::unique_ptr Safe) { // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; @@ -709,15 +718,19 @@ static bool TestOptimizer(BugDriver &BD, std::unique_ptr Test, << " on the input program!\n"; delete BD.swapProgramIn(Test.get()); BD.EmitProgressBitcode(Test.get(), "pass-error", false); - return BD.debugOptimizerCrash(); + if (Error E = BD.debugOptimizerCrash()) + return std::move(E); + return false; } outs() << "done.\n"; outs() << " Checking to see if the merged program executes correctly: "; bool Broken; - std::unique_ptr New = testMergedProgram( - BD, std::move(Optimized), std::move(Safe), Error, Broken); - if (New) { + auto Result = + testMergedProgram(BD, std::move(Optimized), std::move(Safe), Broken); + if (Error E = Result.takeError()) + return std::move(E); + if (auto New = std::move(*Result)) { outs() << (Broken ? " nope.\n" : " yup.\n"); // Delete the original and set the new program. delete BD.swapProgramIn(New.release()); @@ -729,32 +742,36 @@ static bool TestOptimizer(BugDriver &BD, std::unique_ptr Test, /// crashing, but the generated output is semantically different from the /// input. /// -void BugDriver::debugMiscompilation(std::string *Error) { +Error BugDriver::debugMiscompilation() { // Make sure something was miscompiled... - if (!BugpointIsInterrupted) - if (!ReduceMiscompilingPasses(*this).reduceList(PassesToRun, *Error)) { - if (Error->empty()) - errs() << "*** Optimized program matches reference output! No problem" - << " detected...\nbugpoint can't help you with your problem!\n"; - return; - } + if (!BugpointIsInterrupted) { + Expected Result = + ReduceMiscompilingPasses(*this).reduceList(PassesToRun); + if (Error E = Result.takeError()) + return E; + if (!*Result) + return make_error( + "*** Optimized program matches reference output! No problem" + " detected...\nbugpoint can't help you with your problem!\n", + inconvertibleErrorCode()); + } outs() << "\n*** Found miscompiling pass" << (getPassesToRun().size() == 1 ? "" : "es") << ": " << getPassesString(getPassesToRun()) << '\n'; EmitProgressBitcode(Program, "passinput"); - std::vector MiscompiledFunctions = - DebugAMiscompilation(*this, TestOptimizer, *Error); - if (!Error->empty()) - return; + Expected> MiscompiledFunctions = + DebugAMiscompilation(*this, TestOptimizer); + if (Error E = MiscompiledFunctions.takeError()) + return E; // Output a bunch of bitcode files for the user... outs() << "Outputting reduced bitcode files which expose the problem:\n"; ValueToValueMapTy VMap; Module *ToNotOptimize = CloneModule(getProgram(), VMap).release(); Module *ToOptimize = - SplitFunctionsOutOfModule(ToNotOptimize, MiscompiledFunctions, VMap) + SplitFunctionsOutOfModule(ToNotOptimize, *MiscompiledFunctions, VMap) .release(); outs() << " Non-optimized portion: "; @@ -764,6 +781,8 @@ void BugDriver::debugMiscompilation(std::string *Error) { outs() << " Portion that is input to optimizer: "; EmitProgressBitcode(ToOptimize, "tooptimize"); delete ToOptimize; // Delete hacked module. + + return Error::success(); } /// Get the specified modules ready for code generator testing. @@ -928,9 +947,9 @@ static void CleanupAndPrepareModules(BugDriver &BD, /// 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, std::unique_ptr Test, - std::unique_ptr Safe, - std::string &Error) { +static Expected TestCodeGenerator(BugDriver &BD, + std::unique_ptr Test, + std::unique_ptr Safe) { CleanupAndPrepareModules(BD, Test, Safe.get()); SmallString<128> TestModuleBC; @@ -968,20 +987,21 @@ static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr Test, FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps); - std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error); - if (!Error.empty()) - return false; + Expected SharedObject = + BD.compileSharedObject(SafeModuleBC.str()); + if (Error E = SharedObject.takeError()) + return std::move(E); - FileRemover SharedObjectRemover(SharedObject, !SaveTemps); + 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; + Expected Result = + BD.diffProgram(BD.getProgram(), TestModuleBC.str(), *SharedObject, false); + if (Error E = Result.takeError()) + return std::move(E); - if (Result) + if (*Result) errs() << ": still failing!\n"; else errs() << ": didn't fail.\n"; @@ -991,34 +1011,34 @@ static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr Test, /// debugCodeGenerator - debug errors in LLC, LLI, or CBE. /// -bool BugDriver::debugCodeGenerator(std::string *Error) { +Error BugDriver::debugCodeGenerator() { if ((void *)SafeInterpreter == (void *)Interpreter) { - std::string Result = - executeProgramSafely(Program, "bugpoint.safe.out", Error); - if (Error->empty()) { + Expected Result = + executeProgramSafely(Program, "bugpoint.safe.out"); + if (Result) { outs() << "\n*** The \"safe\" i.e. 'known good' backend cannot match " << "the reference diff. This may be due to a\n front-end " << "bug or a bug in the original program, but this can also " << "happen if bugpoint isn't running the program with the " << "right flags or input.\n I left the result of executing " << "the program with the \"safe\" backend in this file for " - << "you: '" << Result << "'.\n"; + << "you: '" << *Result << "'.\n"; } - return true; + return Error::success(); } DisambiguateGlobalSymbols(Program); - std::vector Funcs = - DebugAMiscompilation(*this, TestCodeGenerator, *Error); - if (!Error->empty()) - return true; + Expected> Funcs = + DebugAMiscompilation(*this, TestCodeGenerator); + if (Error E = Funcs.takeError()) + return E; // Split the module into the two halves of the program we want. ValueToValueMapTy VMap; std::unique_ptr ToNotCodeGen = CloneModule(getProgram(), VMap); std::unique_ptr ToCodeGen = - SplitFunctionsOutOfModule(ToNotCodeGen.get(), Funcs, VMap); + SplitFunctionsOutOfModule(ToNotCodeGen.get(), *Funcs, VMap); // Condition the modules CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen.get()); @@ -1054,16 +1074,16 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } - std::string SharedObject = compileSharedObject(SafeModuleBC.str(), *Error); - if (!Error->empty()) - return true; + Expected SharedObject = compileSharedObject(SafeModuleBC.str()); + if (Error E = SharedObject.takeError()) + return E; outs() << "You can reproduce the problem with the command line: \n"; if (isExecutingJIT()) { - outs() << " lli -load " << SharedObject << " " << TestModuleBC; + outs() << " lli -load " << *SharedObject << " " << TestModuleBC; } else { outs() << " llc " << TestModuleBC << " -o " << TestModuleBC << ".s\n"; - outs() << " cc " << SharedObject << " " << TestModuleBC.str() << ".s -o " + outs() << " cc " << *SharedObject << " " << TestModuleBC.str() << ".s -o " << TestModuleBC << ".exe"; #if defined(HAVE_LINK_R) outs() << " -Wl,-R."; @@ -1076,7 +1096,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { outs() << '\n'; outs() << "The shared object was created with:\n llc -march=c " << SafeModuleBC.str() << " -o temporary.c\n" - << " cc -xc temporary.c -O2 -o " << SharedObject; + << " cc -xc temporary.c -O2 -o " << *SharedObject; if (TargetTriple.getArch() == Triple::sparc) outs() << " -G"; // Compile a shared library, `-G' for Sparc else @@ -1084,5 +1104,5 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { outs() << " -fno-strict-aliasing\n"; - return false; + return Error::success(); } diff --git a/llvm/tools/bugpoint/ToolRunner.cpp b/llvm/tools/bugpoint/ToolRunner.cpp index 98bc4d8..5bee828 100644 --- a/llvm/tools/bugpoint/ToolRunner.cpp +++ b/llvm/tools/bugpoint/ToolRunner.cpp @@ -105,9 +105,8 @@ static int RunProgramRemotelyWithTimeout(StringRef RemoteClientPath, return ReturnCode; } -static std::string ProcessFailure(StringRef ProgPath, const char **Args, - unsigned Timeout = 0, - unsigned MemoryLimit = 0) { +static Error ProcessFailure(StringRef ProgPath, const char **Args, + unsigned Timeout = 0, unsigned MemoryLimit = 0) { std::ostringstream OS; OS << "\nError running tool:\n "; for (const char **Arg = Args; *Arg; ++Arg) @@ -137,7 +136,7 @@ static std::string ProcessFailure(StringRef ProgPath, const char **Args, } sys::fs::remove(ErrorFilename.c_str()); - return OS.str(); + return make_error(OS.str(), inconvertibleErrorCode()); } //===---------------------------------------------------------------------===// @@ -156,22 +155,22 @@ public: } } - int ExecuteProgram( + Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs, + const std::vector &CCArgs, const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int LLI::ExecuteProgram(const std::string &Bitcode, - const std::vector &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector &CCArgs, - const std::vector &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected LLI::ExecuteProgram(const std::string &Bitcode, + const std::vector &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector &CCArgs, + const std::vector &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { std::vector LLIArgs; LLIArgs.push_back(LLIPath.c_str()); LLIArgs.push_back("-force-interpreter=true"); @@ -200,7 +199,7 @@ int LLI::ExecuteProgram(const std::string &Bitcode, << " " << LLIArgs[i]; errs() << "\n";); return RunProgramWithTimeout(LLIPath, &LLIArgs[0], InputFile, OutputFile, - OutputFile, Timeout, MemoryLimit, Error); + OutputFile, Timeout, MemoryLimit); } void AbstractInterpreter::anchor() {} @@ -267,25 +266,24 @@ public: std::vector CompArgs) : CompilerCommand(CompilerCmd), CompilerArgs(std::move(CompArgs)) {} - void compileProgram(const std::string &Bitcode, std::string *Error, - unsigned Timeout = 0, unsigned MemoryLimit = 0) override; + Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0, + unsigned MemoryLimit = 0) override; - int ExecuteProgram( + Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs = std::vector(), const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override { - *Error = "Execution not supported with -compile-custom"; - return -1; + return make_error( + "Execution not supported with -compile-custom", + inconvertibleErrorCode()); } }; } -void CustomCompiler::compileProgram(const std::string &Bitcode, - std::string *Error, unsigned Timeout, - unsigned MemoryLimit) { +Error CustomCompiler::compileProgram(const std::string &Bitcode, + unsigned Timeout, unsigned MemoryLimit) { std::vector ProgramArgs; ProgramArgs.push_back(CompilerCommand.c_str()); @@ -300,9 +298,10 @@ void CustomCompiler::compileProgram(const std::string &Bitcode, ProgramArgs.push_back(CompilerArgs[i].c_str()); if (RunProgramWithTimeout(CompilerCommand, &ProgramArgs[0], "", "", "", - Timeout, MemoryLimit, Error)) - *Error = - ProcessFailure(CompilerCommand, &ProgramArgs[0], Timeout, MemoryLimit); + Timeout, MemoryLimit)) + return ProcessFailure(CompilerCommand, &ProgramArgs[0], Timeout, + MemoryLimit); + return Error::success(); } //===---------------------------------------------------------------------===// @@ -321,23 +320,21 @@ public: std::vector ExecArgs) : ExecutionCommand(ExecutionCmd), ExecutorArgs(std::move(ExecArgs)) {} - int ExecuteProgram( + Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs, + const std::vector &CCArgs, const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int CustomExecutor::ExecuteProgram(const std::string &Bitcode, - const std::vector &Args, - const std::string &InputFile, - const std::string &OutputFile, - std::string *Error, - const std::vector &CCArgs, - const std::vector &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected CustomExecutor::ExecuteProgram( + const std::string &Bitcode, const std::vector &Args, + const std::string &InputFile, const std::string &OutputFile, + const std::vector &CCArgs, + const std::vector &SharedLibs, unsigned Timeout, + unsigned MemoryLimit) { std::vector ProgramArgs; ProgramArgs.push_back(ExecutionCommand.c_str()); @@ -352,8 +349,7 @@ int CustomExecutor::ExecuteProgram(const std::string &Bitcode, ProgramArgs.push_back(Args[i].c_str()); return RunProgramWithTimeout(ExecutionCommand, &ProgramArgs[0], InputFile, - OutputFile, OutputFile, Timeout, MemoryLimit, - Error); + OutputFile, OutputFile, Timeout, MemoryLimit); } // Tokenize the CommandLine to the command and the args to allow @@ -429,9 +425,9 @@ AbstractInterpreter::createCustomExecutor(std::string &Message, //===----------------------------------------------------------------------===// // LLC Implementation of AbstractIntepreter interface // -CC::FileType LLC::OutputCode(const std::string &Bitcode, - std::string &OutputAsmFile, std::string &Error, - unsigned Timeout, unsigned MemoryLimit) { +Expected LLC::OutputCode(const std::string &Bitcode, + std::string &OutputAsmFile, + unsigned Timeout, unsigned MemoryLimit) { const char *Suffix = (UseIntegratedAssembler ? ".llc.o" : ".llc.s"); SmallString<128> UniqueFile; @@ -466,36 +462,42 @@ CC::FileType LLC::OutputCode(const std::string &Bitcode, errs() << "\n";); if (RunProgramWithTimeout(LLCPath, &LLCArgs[0], "", "", "", Timeout, MemoryLimit)) - Error = ProcessFailure(LLCPath, &LLCArgs[0], Timeout, MemoryLimit); + return ProcessFailure(LLCPath, &LLCArgs[0], Timeout, MemoryLimit); return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile; } -void LLC::compileProgram(const std::string &Bitcode, std::string *Error, - unsigned Timeout, unsigned MemoryLimit) { +Error LLC::compileProgram(const std::string &Bitcode, unsigned Timeout, + unsigned MemoryLimit) { std::string OutputAsmFile; - OutputCode(Bitcode, OutputAsmFile, *Error, Timeout, MemoryLimit); + Expected Result = + OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit); sys::fs::remove(OutputAsmFile); + if (Error E = Result.takeError()) + return E; + return Error::success(); } -int LLC::ExecuteProgram(const std::string &Bitcode, - const std::vector &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector &ArgsForCC, - const std::vector &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected LLC::ExecuteProgram(const std::string &Bitcode, + const std::vector &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector &ArgsForCC, + const std::vector &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { std::string OutputAsmFile; - CC::FileType FileKind = - OutputCode(Bitcode, OutputAsmFile, *Error, Timeout, MemoryLimit); + Expected FileKind = + OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit); FileRemover OutFileRemover(OutputAsmFile, !SaveTemps); + if (Error E = FileKind.takeError()) + return std::move(E); std::vector CCArgs(ArgsForCC); CCArgs.insert(CCArgs.end(), SharedLibs.begin(), SharedLibs.end()); // Assuming LLC worked, compile the result with CC and run it. - return cc->ExecuteProgram(OutputAsmFile, Args, FileKind, InputFile, - OutputFile, Error, CCArgs, Timeout, MemoryLimit); + return cc->ExecuteProgram(OutputAsmFile, Args, *FileKind, InputFile, + OutputFile, CCArgs, Timeout, MemoryLimit); } /// createLLC - Try to find the LLC executable @@ -537,23 +539,22 @@ public: } } - int ExecuteProgram( + Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs = std::vector(), const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int JIT::ExecuteProgram(const std::string &Bitcode, - const std::vector &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector &CCArgs, - const std::vector &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected JIT::ExecuteProgram(const std::string &Bitcode, + const std::vector &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector &CCArgs, + const std::vector &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { // Construct a vector of parameters, incorporating those from the command-line std::vector JITArgs; JITArgs.push_back(LLIPath.c_str()); @@ -581,7 +582,7 @@ int JIT::ExecuteProgram(const std::string &Bitcode, errs() << "\n";); DEBUG(errs() << "\nSending output to " << OutputFile << "\n"); return RunProgramWithTimeout(LLIPath, &JITArgs[0], InputFile, OutputFile, - OutputFile, Timeout, MemoryLimit, Error); + OutputFile, Timeout, MemoryLimit); } /// createJIT - Try to find the LLI executable @@ -618,12 +619,13 @@ static bool IsARMArchitecture(std::vector Args) { return false; } -int CC::ExecuteProgram(const std::string &ProgramFile, - const std::vector &Args, FileType fileType, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector &ArgsForCC, - unsigned Timeout, unsigned MemoryLimit) { +Expected CC::ExecuteProgram(const std::string &ProgramFile, + const std::vector &Args, + FileType fileType, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector &ArgsForCC, + unsigned Timeout, unsigned MemoryLimit) { std::vector CCArgs; CCArgs.push_back(CCPath.c_str()); @@ -690,10 +692,8 @@ int CC::ExecuteProgram(const std::string &ProgramFile, for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs() << " " << CCArgs[i]; errs() << "\n";); - if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) { - *Error = ProcessFailure(CCPath, &CCArgs[0]); - return -1; - } + if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) + return ProcessFailure(CCPath, &CCArgs[0]); std::vector ProgramArgs; @@ -745,16 +745,16 @@ int CC::ExecuteProgram(const std::string &ProgramFile, if (RemoteClientPath.empty()) { DEBUG(errs() << ""); + std::string Error; int ExitCode = RunProgramWithTimeout(OutputBinary.str(), &ProgramArgs[0], InputFile, OutputFile, OutputFile, - Timeout, MemoryLimit, Error); + Timeout, MemoryLimit, &Error); // Treat a signal (usually SIGSEGV) or timeout as part of the program output // so that crash-causing miscompilation is handled seamlessly. if (ExitCode < -1) { std::ofstream outFile(OutputFile.c_str(), std::ios_base::app); - outFile << *Error << '\n'; + outFile << Error << '\n'; outFile.close(); - Error->clear(); } return ExitCode; } else { @@ -766,10 +766,9 @@ int CC::ExecuteProgram(const std::string &ProgramFile, } } -int CC::MakeSharedObject(const std::string &InputFile, FileType fileType, - std::string &OutputFile, - const std::vector &ArgsForCC, - std::string &Error) { +Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector &ArgsForCC) { SmallString<128> UniqueFilename; std::error_code EC = sys::fs::createUniqueFile( InputFile + "-%%%%%%%" + LTDL_SHLIB_EXT, UniqueFilename); @@ -838,11 +837,9 @@ int CC::MakeSharedObject(const std::string &InputFile, FileType fileType, for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs() << " " << CCArgs[i]; errs() << "\n";); - if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) { - Error = ProcessFailure(CCPath, &CCArgs[0]); - return 1; - } - return 0; + if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) + return ProcessFailure(CCPath, &CCArgs[0]); + return Error::success();; } /// create - Try to find the CC executable diff --git a/llvm/tools/bugpoint/ToolRunner.h b/llvm/tools/bugpoint/ToolRunner.h index 3e5d83d..f218ad5 100644 --- a/llvm/tools/bugpoint/ToolRunner.h +++ b/llvm/tools/bugpoint/ToolRunner.h @@ -19,7 +19,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include "llvm/Support/SystemUtils.h" #include @@ -59,20 +59,19 @@ public: /// option specifies optional native shared objects that can be loaded into /// the program for execution. /// - int ExecuteProgram( + Expected ExecuteProgram( const std::string &ProgramFile, const std::vector &Args, FileType fileType, const std::string &InputFile, - const std::string &OutputFile, std::string *Error = nullptr, + const std::string &OutputFile, const std::vector &CCArgs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0); /// MakeSharedObject - This compiles the specified file (which is either a .c /// file or a .s file) into a shared object. /// - int MakeSharedObject(const std::string &InputFile, FileType fileType, - std::string &OutputFile, - const std::vector &ArgsForCC, - std::string &Error); + Error MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector &ArgsForCC); }; //===---------------------------------------------------------------------===// @@ -111,30 +110,31 @@ public: /// compileProgram - Compile the specified program from bitcode to executable /// code. This does not produce any output, it is only used when debugging /// the code generator. It returns false if the code generator fails. - virtual void compileProgram(const std::string &Bitcode, std::string *Error, - unsigned Timeout = 0, unsigned MemoryLimit = 0) {} - - /// OutputCode - Compile the specified program from bitcode to code - /// understood by the CC driver (either C or asm). If the code generator - /// fails, it sets Error, otherwise, this function returns the type of code - /// emitted. - virtual CC::FileType OutputCode(const std::string &Bitcode, - std::string &OutFile, std::string &Error, - unsigned Timeout = 0, - unsigned MemoryLimit = 0) { - Error = "OutputCode not supported by this AbstractInterpreter!"; - return CC::AsmFile; + virtual Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0, + unsigned MemoryLimit = 0) { + return Error::success(); + } + + /// Compile the specified program from bitcode to code understood by the CC + /// driver (either C or asm). Returns an error if the code generator fails,, + /// otherwise, the type of code emitted. + virtual Expected OutputCode(const std::string &Bitcode, + std::string &OutFile, + unsigned Timeout = 0, + unsigned MemoryLimit = 0) { + return make_error( + "OutputCode not supported by this AbstractInterpreter!", + inconvertibleErrorCode()); } /// ExecuteProgram - Run the specified bitcode file, emitting output to the /// specified filename. This sets RetVal to the exit code of the program or - /// returns false if a problem was encountered that prevented execution of + /// returns an Error if a problem was encountered that prevented execution of /// the program. /// - virtual int ExecuteProgram( + virtual Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs = std::vector(), const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) = 0; @@ -163,24 +163,19 @@ public: /// compileProgram - Compile the specified program from bitcode to executable /// code. This does not produce any output, it is only used when debugging /// the code generator. Returns false if the code generator fails. - void compileProgram(const std::string &Bitcode, std::string *Error, - unsigned Timeout = 0, unsigned MemoryLimit = 0) override; + Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0, + unsigned MemoryLimit = 0) override; - int ExecuteProgram( + Expected ExecuteProgram( const std::string &Bitcode, const std::vector &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector &CCArgs = std::vector(), const std::vector &SharedLibs = std::vector(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; - /// OutputCode - Compile the specified program from bitcode to code - /// understood by the CC driver (either C or asm). If the code generator - /// fails, it sets Error, otherwise, this function returns the type of code - /// emitted. - CC::FileType OutputCode(const std::string &Bitcode, std::string &OutFile, - std::string &Error, unsigned Timeout = 0, - unsigned MemoryLimit = 0) override; + Expected OutputCode(const std::string &Bitcode, + std::string &OutFile, unsigned Timeout = 0, + unsigned MemoryLimit = 0) override; }; } // End llvm namespace diff --git a/llvm/tools/bugpoint/bugpoint.cpp b/llvm/tools/bugpoint/bugpoint.cpp index fa4243f..a5de953 100644 --- a/llvm/tools/bugpoint/bugpoint.cpp +++ b/llvm/tools/bugpoint/bugpoint.cpp @@ -197,11 +197,9 @@ int main(int argc, char **argv) { sys::Process::PreventCoreFiles(); #endif - std::string Error; - bool Failure = D.run(Error); - if (!Error.empty()) { - errs() << Error; + if (Error E = D.run()) { + errs() << toString(std::move(E)); return 1; } - return Failure; + return 0; } -- 2.7.4