diff options
-rw-r--r-- | llvm/tools/bugpoint/BugDriver.cpp | 49 | ||||
-rw-r--r-- | llvm/tools/bugpoint/BugDriver.h | 67 | ||||
-rw-r--r-- | llvm/tools/bugpoint/CrashDebugger.cpp | 161 | ||||
-rw-r--r-- | llvm/tools/bugpoint/ExecutionDriver.cpp | 112 | ||||
-rw-r--r-- | llvm/tools/bugpoint/FindBugs.cpp | 49 | ||||
-rw-r--r-- | llvm/tools/bugpoint/ListReducer.h | 49 | ||||
-rw-r--r-- | llvm/tools/bugpoint/Miscompilation.cpp | 370 | ||||
-rw-r--r-- | llvm/tools/bugpoint/ToolRunner.cpp | 173 | ||||
-rw-r--r-- | llvm/tools/bugpoint/ToolRunner.h | 63 | ||||
-rw-r--r-- | 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 dde552174b4..78f35293d2a 100644 --- a/llvm/tools/bugpoint/BugDriver.cpp +++ b/llvm/tools/bugpoint/BugDriver.cpp @@ -146,11 +146,11 @@ bool BugDriver::addSources(const std::vector<std::string> &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<bool> 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<Function *> &Funcs) { diff --git a/llvm/tools/bugpoint/BugDriver.h b/llvm/tools/bugpoint/BugDriver.h index 7ae2d5c2f99..af80e8b2b77 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 <memory> #include <string> @@ -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<std::string> 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<std::string> 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<std::string> + 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<bool> 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<std::string> &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<std::string> &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 649cac7c681..e7367392337 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -66,19 +66,17 @@ class ReducePassList : public ListReducer<std::string> { 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<std::string> &Removed, - std::vector<std::string> &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<TestResult> doTest(std::vector<std::string> &Removed, + std::vector<std::string> &Kept) override; }; } -ReducePassList::TestResult +Expected<ReducePassList::TestResult> ReducePassList::doTest(std::vector<std::string> &Prefix, - std::vector<std::string> &Suffix, std::string &Error) { + std::vector<std::string> &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<GlobalVariable *> &Prefix, - std::vector<GlobalVariable *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<GlobalVariable *> &Prefix, + std::vector<GlobalVariable *> &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<Function *> &Prefix, - std::vector<Function *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<Function *> &Prefix, + std::vector<Function *> &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<const BasicBlock *> &Prefix, - std::vector<const BasicBlock *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, + std::vector<const BasicBlock *> &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -494,9 +489,8 @@ public: bool Direction) : BD(bd), TestFn(testFn), Direction(Direction) {} - TestResult doTest(std::vector<const BasicBlock *> &Prefix, - std::vector<const BasicBlock *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, + std::vector<const BasicBlock *> &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -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<const BasicBlock *> &Prefix, - std::vector<const BasicBlock *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix, + std::vector<const BasicBlock *> &Kept) override { if (!Kept.empty() && TestBlocks(Kept)) return KeepSuffix; if (!Prefix.empty() && TestBlocks(Prefix)) @@ -697,9 +690,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector<const Instruction *> &Prefix, - std::vector<const Instruction *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<const Instruction *> &Prefix, + std::vector<const Instruction *> &Kept) override { if (!Kept.empty() && TestInsts(Kept)) return KeepSuffix; if (!Prefix.empty() && TestInsts(Prefix)) @@ -774,9 +766,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector<std::string> &Prefix, - std::vector<std::string> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<std::string> &Prefix, + std::vector<std::string> &Kept) override { if (!Kept.empty() && TestNamedMDs(Kept)) return KeepSuffix; if (!Prefix.empty() && TestNamedMDs(Prefix)) @@ -844,9 +835,8 @@ public: bool (*testFn)(const BugDriver &, Module *)) : BD(bd), TestFn(testFn) {} - TestResult doTest(std::vector<const MDNode *> &Prefix, - std::vector<const MDNode *> &Kept, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<const MDNode *> &Prefix, + std::vector<const MDNode *> &Kept) override { if (!Kept.empty() && TestNamedMDOps(Kept)) return KeepSuffix; if (!Prefix.empty() && TestNamedMDOps(Prefix)) @@ -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<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"); @@ -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<TerminatorInst>(&I)) Insts.push_back(&I); - ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error); + Expected<bool> 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<Function *> 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<bool> 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<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"); } @@ -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<bool> 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<bool> 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<std::string> NamedMDNames; for (auto &NamedMD : BD.getProgram()->named_metadata()) NamedMDNames.push_back(NamedMD.getName().str()); - ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error); + Expected<bool> 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<bool> 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<bool> 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() << "<crash>\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 695461e2d50..b0f3880fa74 100644 --- a/llvm/tools/bugpoint/ExecutionDriver.cpp +++ b/llvm/tools/bugpoint/ExecutionDriver.cpp @@ -141,7 +141,7 @@ cl::list<std::string> 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<StringError>("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<std::string> 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<int> 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() << "<timeout>"; 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<std::string> +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<std::string> +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<CC::FileType> 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<std::string> 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<StringError>( + "*** 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<bool> 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<std::string> 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 3c345c5503c..156f4d0d78f 100644 --- a/llvm/tools/bugpoint/FindBugs.cpp +++ b/llvm/tools/bugpoint/FindBugs.cpp @@ -23,27 +23,20 @@ #include <ctime> 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<std::string> &AllPasses, - std::string &ErrMsg) { +Error +BugDriver::runManyPasses(const std::vector<std::string> &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<std::string> &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<std::string> &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<std::string> &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<bool> 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 78af1aeb6a2..621a072d84e 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 <algorithm> #include <cstdlib> @@ -27,10 +27,9 @@ extern bool BugpointIsInterrupted; template <typename ElTy> 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 <typename ElTy> 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<ElTy> &Prefix, std::vector<ElTy> &Kept, - std::string &Error) = 0; + virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix, + std::vector<ElTy> &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<ElTy> &TheList, std::string &Error) { + Expected<bool> reduceList(std::vector<ElTy> &TheList) { std::vector<ElTy> empty; std::srand(0x6e5ea738); // Seed the random number generator - switch (doTest(TheList, empty, Error)) { + Expected<TestResult> 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 <typename ElTy> 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 <typename ElTy> struct ListReducer { std::vector<ElTy> 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<TestResult> 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 <typename ElTy> struct ListReducer { std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid); std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end()); - switch (doTest(Prefix, Suffix, Error)) { + Expected<TestResult> 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 <typename ElTy> 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 <typename ElTy> struct ListReducer { std::vector<ElTy> TestList(TheList); TestList.erase(TestList.begin() + i); - if (doTest(EmptyList, TestList, Error) == KeepSuffix) { + Expected<TestResult> 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 9069e00acb9..20acb304a02 100644 --- a/llvm/tools/bugpoint/Miscompilation.cpp +++ b/llvm/tools/bugpoint/Miscompilation.cpp @@ -50,19 +50,17 @@ class ReduceMiscompilingPasses : public ListReducer<std::string> { public: ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {} - TestResult doTest(std::vector<std::string> &Prefix, - std::vector<std::string> &Suffix, - std::string &Error) override; + Expected<TestResult> doTest(std::vector<std::string> &Prefix, + std::vector<std::string> &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::TestResult> ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix, - std::vector<std::string> &Suffix, - std::string &Error) { + std::vector<std::string> &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<std::string> &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<bool> 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<std::string> &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<std::string> &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<std::string> &Prefix, namespace { class ReduceMiscompilingFunctions : public ListReducer<Function *> { BugDriver &BD; - bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, std::unique_ptr<Module>, - std::string &); + Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>); public: ReduceMiscompilingFunctions(BugDriver &bd, - bool (*F)(BugDriver &, std::unique_ptr<Module>, - std::unique_ptr<Module>, std::string &)) + Expected<bool> (*F)(BugDriver &, + std::unique_ptr<Module>, + std::unique_ptr<Module>)) : BD(bd), TestFn(F) {} - TestResult doTest(std::vector<Function *> &Prefix, - std::vector<Function *> &Suffix, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<Function *> &Prefix, + std::vector<Function *> &Suffix) override { if (!Suffix.empty()) { - bool Ret = TestFuncs(Suffix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected<bool> 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<bool> Ret = TestFuncs(Prefix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepPrefix; } return NoFailure; } - bool TestFuncs(const std::vector<Function *> &Prefix, std::string &Error); + Expected<bool> TestFuncs(const std::vector<Function *> &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<Module> testMergedProgram(const BugDriver &BD, - std::unique_ptr<Module> M1, - std::unique_ptr<Module> M2, - std::string &Error, - bool &Broken) { +static Expected<std::unique_ptr<Module>> +testMergedProgram(const BugDriver &BD, std::unique_ptr<Module> M1, + std::unique_ptr<Module> 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<bool> 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<Function *> &Funcs, std::string &Error) { +Expected<bool> +ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function *> &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<Module> ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap); - bool Broken = - TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize), Error); + Expected<bool> 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<Module>, - std::unique_ptr<Module>, std::string &), - std::vector<Function *> &MiscompiledFunctions, - std::string &Error) { +static Expected<bool> +ExtractLoops(BugDriver &BD, + Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>), + std::vector<Function *> &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<Module> New = + Expected<std::unique_ptr<Module>> 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<Function>(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<Function>(VMap[MiscompiledFunctions[i]]); - Failure = TestFn(BD, std::move(ToOptimizeLoopExtracted), - std::move(ToNotOptimize), Error); - if (!Error.empty()) - return false; + Expected<bool> 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<BasicBlock *> { BugDriver &BD; - bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, std::unique_ptr<Module>, - std::string &); + Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>); std::vector<Function *> FunctionsBeingTested; public: ReduceMiscompiledBlocks(BugDriver &bd, - bool (*F)(BugDriver &, std::unique_ptr<Module>, - std::unique_ptr<Module>, std::string &), + Expected<bool> (*F)(BugDriver &, + std::unique_ptr<Module>, + std::unique_ptr<Module>), const std::vector<Function *> &Fns) : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {} - TestResult doTest(std::vector<BasicBlock *> &Prefix, - std::vector<BasicBlock *> &Suffix, - std::string &Error) override { + Expected<TestResult> doTest(std::vector<BasicBlock *> &Prefix, + std::vector<BasicBlock *> &Suffix) override { if (!Suffix.empty()) { - bool Ret = TestFuncs(Suffix, Error); - if (!Error.empty()) - return InternalError; - if (Ret) + Expected<bool> 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<bool> Ret = TestFuncs(Prefix); + if (Error E = Ret.takeError()) + return std::move(E); + if (*Ret) return KeepPrefix; } return NoFailure; } - bool TestFuncs(const std::vector<BasicBlock *> &BBs, std::string &Error); + Expected<bool> TestFuncs(const std::vector<BasicBlock *> &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<BasicBlock *> &BBs, - std::string &Error) { +Expected<bool> +ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock *> &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<BasicBlock *> &BBs, // or something, in which case bugpoint can't chase down this possibility. if (std::unique_ptr<Module> New = BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) { - bool Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize), Error); + Expected<bool> 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<BasicBlock *> &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<Module>, - std::unique_ptr<Module>, - std::string &), - std::vector<Function *> &MiscompiledFunctions, - std::string &Error) { +static Expected<bool> +ExtractBlocks(BugDriver &BD, + Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>), + std::vector<Function *> &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<BasicBlock *>(), Error); - if (!Error.empty()) - return false; - if (Ret) { + Expected<bool> Ret = ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions) + .TestFuncs(std::vector<BasicBlock *>()); + 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<bool> 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<Function *> -DebugAMiscompilation(BugDriver &BD, - bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, - std::unique_ptr<Module>, std::string &), - std::string &Error) { +static Expected<std::vector<Function *>> DebugAMiscompilation( + BugDriver &BD, + Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<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 @@ -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<bool> 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<bool> 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<bool> 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<Module> Test, - std::unique_ptr<Module> Safe, std::string &Error) { +static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test, + std::unique_ptr<Module> 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<Module> 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<Module> 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<Module> 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<bool> Result = + ReduceMiscompilingPasses(*this).reduceList(PassesToRun); + if (Error E = Result.takeError()) + return E; + if (!*Result) + return make_error<StringError>( + "*** 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<Function *> MiscompiledFunctions = - DebugAMiscompilation(*this, TestOptimizer, *Error); - if (!Error->empty()) - return; + Expected<std::vector<Function *>> 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<Module> Test, - std::unique_ptr<Module> Safe, - std::string &Error) { +static Expected<bool> TestCodeGenerator(BugDriver &BD, + std::unique_ptr<Module> Test, + std::unique_ptr<Module> Safe) { CleanupAndPrepareModules(BD, Test, Safe.get()); SmallString<128> TestModuleBC; @@ -968,20 +987,21 @@ static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr<Module> Test, FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps); - std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error); - if (!Error.empty()) - return false; + Expected<std::string> 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<bool> 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<Module> 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<std::string> 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<Function *> Funcs = - DebugAMiscompilation(*this, TestCodeGenerator, *Error); - if (!Error->empty()) - return true; + Expected<std::vector<Function *>> 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<Module> ToNotCodeGen = CloneModule(getProgram(), VMap); std::unique_ptr<Module> 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<std::string> 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 98bc4d846b6..5bee8280621 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<StringError>(OS.str(), inconvertibleErrorCode()); } //===---------------------------------------------------------------------===// @@ -156,22 +155,22 @@ public: } } - int ExecuteProgram( + Expected<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int LLI::ExecuteProgram(const std::string &Bitcode, - const std::vector<std::string> &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &CCArgs, - const std::vector<std::string> &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected<int> LLI::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &CCArgs, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { std::vector<const char *> 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<std::string> 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<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override { - *Error = "Execution not supported with -compile-custom"; - return -1; + return make_error<StringError>( + "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<const char *> 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<std::string> ExecArgs) : ExecutionCommand(ExecutionCmd), ExecutorArgs(std::move(ExecArgs)) {} - int ExecuteProgram( + Expected<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int CustomExecutor::ExecuteProgram(const std::string &Bitcode, - const std::vector<std::string> &Args, - const std::string &InputFile, - const std::string &OutputFile, - std::string *Error, - const std::vector<std::string> &CCArgs, - const std::vector<std::string> &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected<int> CustomExecutor::ExecuteProgram( + const std::string &Bitcode, const std::vector<std::string> &Args, + const std::string &InputFile, const std::string &OutputFile, + const std::vector<std::string> &CCArgs, + const std::vector<std::string> &SharedLibs, unsigned Timeout, + unsigned MemoryLimit) { std::vector<const char *> 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<CC::FileType> 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<CC::FileType> 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<std::string> &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &ArgsForCC, - const std::vector<std::string> &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected<int> LLC::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &ArgsForCC, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { std::string OutputAsmFile; - CC::FileType FileKind = - OutputCode(Bitcode, OutputAsmFile, *Error, Timeout, MemoryLimit); + Expected<CC::FileType> FileKind = + OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit); FileRemover OutFileRemover(OutputAsmFile, !SaveTemps); + if (Error E = FileKind.takeError()) + return std::move(E); std::vector<std::string> 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<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, unsigned MemoryLimit = 0) override; }; } -int JIT::ExecuteProgram(const std::string &Bitcode, - const std::vector<std::string> &Args, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &CCArgs, - const std::vector<std::string> &SharedLibs, - unsigned Timeout, unsigned MemoryLimit) { +Expected<int> JIT::ExecuteProgram(const std::string &Bitcode, + const std::vector<std::string> &Args, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &CCArgs, + const std::vector<std::string> &SharedLibs, + unsigned Timeout, unsigned MemoryLimit) { // Construct a vector of parameters, incorporating those from the command-line std::vector<const char *> 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<const char *> Args) { return false; } -int CC::ExecuteProgram(const std::string &ProgramFile, - const std::vector<std::string> &Args, FileType fileType, - const std::string &InputFile, - const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &ArgsForCC, - unsigned Timeout, unsigned MemoryLimit) { +Expected<int> CC::ExecuteProgram(const std::string &ProgramFile, + const std::vector<std::string> &Args, + FileType fileType, + const std::string &InputFile, + const std::string &OutputFile, + const std::vector<std::string> &ArgsForCC, + unsigned Timeout, unsigned MemoryLimit) { std::vector<const char *> 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<const char *> ProgramArgs; @@ -745,16 +745,16 @@ int CC::ExecuteProgram(const std::string &ProgramFile, if (RemoteClientPath.empty()) { DEBUG(errs() << "<run locally>"); + 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<std::string> &ArgsForCC, - std::string &Error) { +Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector<std::string> &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 3e5d83d2e1c..f218ad534ee 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 <exception> @@ -59,20 +59,19 @@ public: /// option specifies optional native shared objects that can be loaded into /// the program for execution. /// - int ExecuteProgram( + Expected<int> ExecuteProgram( const std::string &ProgramFile, const std::vector<std::string> &Args, FileType fileType, const std::string &InputFile, - const std::string &OutputFile, std::string *Error = nullptr, + const std::string &OutputFile, const std::vector<std::string> &CCArgs = std::vector<std::string>(), 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<std::string> &ArgsForCC, - std::string &Error); + Error MakeSharedObject(const std::string &InputFile, FileType fileType, + std::string &OutputFile, + const std::vector<std::string> &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<CC::FileType> OutputCode(const std::string &Bitcode, + std::string &OutFile, + unsigned Timeout = 0, + unsigned MemoryLimit = 0) { + return make_error<StringError>( + "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<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), 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<int> ExecuteProgram( const std::string &Bitcode, const std::vector<std::string> &Args, const std::string &InputFile, const std::string &OutputFile, - std::string *Error, const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), 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<CC::FileType> 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 fa4243fd057..a5de953b2b7 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; } |