/// 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
}
// 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';
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;
}
// 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) {
#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>
/// 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
/// 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
///
/// 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".
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.
/// 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
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()) {
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))
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))
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))
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))
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))
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))
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))
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))
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.
<< "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");
}
}
}
+ 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) {
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';
--InstructionsToSkipBeforeDeleting;
} else {
if (BugpointIsInterrupted)
- return;
+ // TODO: Should this be some kind of interrupted error?
+ return Error::success();
if (I->isEHPad() || I->getType()->isTokenTy())
continue;
} 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;
"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");
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");
}
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");
}
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");
}
// 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) {
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) {
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");
}
BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified");
- return false;
+ return Error::success();
}
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: ")
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';
/// 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);
}
/// 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
}
// 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;
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!");
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) {
if (AppendProgramExitCode) {
std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);
- outFile << "exit " << RetVal << '\n';
+ outFile << "exit " << *RetVal << '\n';
outFile.close();
}
/// 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);
/// 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
/// 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';
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.
#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));
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";
}
// 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';
// 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";
#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>
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() {}
/// 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;
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,
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();
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.
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
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;
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)
<< " 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 "
<< " 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;
<< " 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;
}
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
/// 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 "
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);
/// 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)
// 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;
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.
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 ";
// 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;
}
/// 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;
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;
}
/// 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
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")
// 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")
}
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")
/// 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: ";
<< " 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());
/// 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: ";
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.
/// 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;
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";
/// 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());
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.";
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
outs() << " -fno-strict-aliasing\n";
- return false;
+ return Error::success();
}
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)
}
sys::fs::remove(ErrorFilename.c_str());
- return OS.str();
+ return make_error<StringError>(OS.str(), inconvertibleErrorCode());
}
//===---------------------------------------------------------------------===//
}
}
- 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");
<< " " << LLIArgs[i];
errs() << "\n";);
return RunProgramWithTimeout(LLIPath, &LLIArgs[0], InputFile, OutputFile,
- OutputFile, Timeout, MemoryLimit, Error);
+ OutputFile, Timeout, MemoryLimit);
}
void AbstractInterpreter::anchor() {}
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());
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();
}
//===---------------------------------------------------------------------===//
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());
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
//===----------------------------------------------------------------------===//
// 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;
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
}
}
- 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());
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
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());
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;
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 {
}
}
-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);
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
#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>
/// 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);
};
//===---------------------------------------------------------------------===//
/// 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;
/// 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
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;
}