diff options
Diffstat (limited to 'tools/bugpoint/OptimizerDriver.cpp')
-rw-r--r-- | tools/bugpoint/OptimizerDriver.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp new file mode 100644 index 0000000000..26a6ae70fe --- /dev/null +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -0,0 +1,150 @@ +//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===// +// +// This file defines an interface that allows bugpoint to run various passes +// without the threat of a buggy pass corrupting bugpoint (of course bugpoint +// may have it's own bugs, but that's another story...). It acheives this by +// forking a copy of itself and having the child process do the optimizations. +// If this client dies, we can always fork a new one. :) +// +//===----------------------------------------------------------------------===// + +#include "BugDriver.h" +#include "llvm/PassManager.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bytecode/WriteBytecodePass.h" +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <stdlib.h> +#include <fstream> + +/// removeFile - Delete the specified file +/// +void BugDriver::removeFile(const std::string &Filename) const { + unlink(Filename.c_str()); +} + +/// writeProgramToFile - This writes the current "Program" to the named bytecode +/// file. If an error occurs, true is returned. +/// +bool BugDriver::writeProgramToFile(const std::string &Filename) const { + std::ofstream Out(Filename.c_str()); + if (!Out.good()) return true; + + WriteBytecodeToFile(Program, Out); + return false; +} + + +/// EmitProgressBytecode - This function is used to output the current Program +/// to a file named "bugpoing-ID.bc". +/// +void BugDriver::EmitProgressBytecode(const PassInfo *Pass, + const std::string &ID) { + // Output the input to the current pass to a bytecode file, emit a message + // telling the user how to reproduce it: opt -foo blah.bc + // + std::string Filename = "bugpoint-" + ID + ".bc"; + if (writeProgramToFile(Filename)) { + std::cerr << "Error opening file '" << Filename << "' for writing!\n"; + return; + } + + std::cout << "Emitted bytecode to 'bugpoint-" << Filename << ".bc'\n"; + std::cout << "\n*** You can reproduce the problem with: "; + + unsigned PassType = Pass->getPassType(); + if (PassType & PassInfo::Analysis) + std::cout << "analyze"; + else if (PassType & PassInfo::Optimization) + std::cout << "opt"; + else if (PassType & PassInfo::LLC) + std::cout << "llc"; + else + std::cout << "bugpoint"; + std::cout << " " << Filename << " -" << Pass->getPassArgument() << "\n"; +} + + +static void RunChild(Module *Program,const std::vector<const PassInfo*> &Passes, + const std::string &OutFilename) { + std::ofstream OutFile(OutFilename.c_str()); + if (!OutFile.good()) { + std::cerr << "Error opening bytecode file: " << OutFilename << "\n"; + exit(1); + } + + PassManager PM; + for (unsigned i = 0, e = Passes.size(); i != e; ++i) { + if (Passes[i]->getNormalCtor()) + PM.add(Passes[i]->getNormalCtor()()); + else + std::cerr << "Cannot create pass yet: " << Passes[i]->getPassName() + << "\n"; + } + // Check that the module is well formed on completion of optimization + PM.add(createVerifierPass()); + + // Write bytecode out to disk as the last step... + PM.add(new WriteBytecodePass(&OutFile)); + + // Run all queued passes. + PM.run(*Program); +} + +/// runPasses - Run the specified passes on Program, outputting a bytecode file +/// and writting the filename into OutputFile if successful. If the +/// optimizations fail for some reason (optimizer crashes), return true, +/// otherwise return false. If DeleteOutput is set to true, the bytecode is +/// deleted on success, and the filename string is undefined. This prints to +/// cout a single line message indicating whether compilation was successful or +/// failed. +/// +bool BugDriver::runPasses(const std::vector<const PassInfo*> &Passes, + std::string &OutputFilename, bool DeleteOutput) const{ + std::cout << std::flush; + + // Agree on a temporary file name to use.... + char FNBuffer[] = "bugpoint-output.bc-XXXXXX"; + int TempFD; + if ((TempFD = mkstemp(FNBuffer)) == -1) { + std::cerr << ToolName << ": ERROR: Cannot create temporary" + << " file in the current directory!\n"; + exit(1); + } + OutputFilename = FNBuffer; + + // We don't need to hold the temp file descriptor... we will trust that noone + // will overwrite/delete the file while we are working on it... + close(TempFD); + + pid_t child_pid; + switch (child_pid = fork()) { + case -1: // Error occurred + std::cerr << ToolName << ": Error forking!\n"; + exit(1); + case 0: // Child process runs passes. + RunChild(Program, Passes, OutputFilename); + exit(0); // If we finish successfully, return 0! + default: // Parent continues... + break; + } + + // Wait for the child process to get done. + int Status; + if (wait(&Status) != child_pid) { + std::cerr << "Error waiting for child process!\n"; + exit(1); + } + + // If we are supposed to delete the bytecode file, remove it now + // unconditionally... this may fail if the file was never created, but that's + // ok. + if (DeleteOutput) + removeFile(OutputFilename); + + std::cout << (Status ? "Crashed!\n" : "Success!\n"); + + // Was the child successful? + return Status != 0; +} |