diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index 17e3374117a..bd558db6650 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -62,9 +62,9 @@ std::string llvm::getPassesString(const std::vector &Passes) { return Result; } -BugDriver::BugDriver(const char *toolname) +BugDriver::BugDriver(const char *toolname, bool as_child) : ToolName(toolname), ReferenceOutputFile(OutputFile), - Program(0), Interpreter(0), cbe(0), gcc(0) {} + Program(0), Interpreter(0), cbe(0), gcc(0), run_as_child(as_child) {} /// ParseInputFile - Given a bytecode or assembly input filename, parse and @@ -97,13 +97,15 @@ bool BugDriver::addSources(const std::vector &Filenames) { // Load the first input file... Program = ParseInputFile(Filenames[0]); if (Program == 0) return true; - std::cout << "Read input file : '" << Filenames[0] << "'\n"; + if (!run_as_child) + std::cout << "Read input file : '" << Filenames[0] << "'\n"; for (unsigned i = 1, e = Filenames.size(); i != e; ++i) { std::auto_ptr M(ParseInputFile(Filenames[i])); if (M.get() == 0) return true; - std::cout << "Linking in input file: '" << Filenames[i] << "'\n"; + if (!run_as_child) + std::cout << "Linking in input file: '" << Filenames[i] << "'\n"; std::string ErrorMessage; if (Linker::LinkModules(Program, M.get(), &ErrorMessage)) { std::cerr << ToolName << ": error linking in '" << Filenames[i] << "': " @@ -112,7 +114,8 @@ bool BugDriver::addSources(const std::vector &Filenames) { } } - std::cout << "*** All input ok\n"; + if (!run_as_child) + std::cout << "*** All input ok\n"; // All input files read successfully! return false; @@ -124,12 +127,21 @@ bool BugDriver::addSources(const std::vector &Filenames) { /// variables are set up from command line arguments. /// bool BugDriver::run() { - // The first thing that we must do is determine what the problem is. Does the - // optimization series crash the compiler, or does it produce illegal code? - // We make the top-level decision by trying to run all of the passes on the - // the input program, which should generate a bytecode file. If it does - // generate a bytecode file, then we know the compiler didn't crash, so try - // to diagnose a miscompilation. + // The first thing to do is determine if we're running as a child. If we are, + // then what to do is very narrow. This form of invocation is only called + // from the runPasses method to actually run those passes in a child process. + if (run_as_child) { + // Execute the passes + return runPassesAsChild(PassesToRun); + } + + // If we're not running as a child, the first thing that we must do is + // determine what the problem is. Does the optimization series crash the + // compiler, or does it produce illegal code? We make the top-level + // decision by trying to run all of the passes on the the input program, + // which should generate a bytecode file. If it does generate a bytecode + // file, then we know the compiler didn't crash, so try to diagnose a + // miscompilation. if (!PassesToRun.empty()) { std::cout << "Running selected passes on program to test for crash: "; if (runPasses(PassesToRun)) diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 192ed527178..565a382d20a 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -47,13 +47,14 @@ class BugDriver { AbstractInterpreter *Interpreter; // How to run the program CBE *cbe; GCC *gcc; + bool run_as_child; // FIXME: sort out public/private distinctions... friend class ReducePassList; friend class ReduceMisCodegenFunctions; public: - BugDriver(const char *toolname); + BugDriver(const char *toolname, bool as_child); const std::string &getToolName() const { return ToolName; } @@ -71,7 +72,8 @@ public: } /// run - The top level method that is invoked after all of the instance - /// variables are set up from command line arguments. + /// 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(); @@ -249,6 +251,9 @@ private: return runPasses(PassesToRun, Filename, DeleteOutput); } + /// runAsChild - The actual "runPasses" guts that runs in a child process. + int runPassesAsChild(const std::vector &PassesToRun); + /// initializeExecutionEnvironment - This method is used to set up the /// environment for executing LLVM programs. /// diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index 3a77e16d4ef..bac7f51de83 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -19,10 +19,6 @@ // independent code co-exist via conditional compilation until it is verified // that the new code works correctly on Unix. -#ifdef _MSC_VER -#define PLATFORMINDEPENDENT -#endif - #include "BugDriver.h" #include "llvm/Module.h" #include "llvm/PassManager.h" @@ -30,15 +26,18 @@ #include "llvm/Bytecode/WriteBytecodePass.h" #include "llvm/Target/TargetData.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/CommandLine.h" #include "llvm/System/Path.h" +#include "llvm/System/Program.h" #include -#ifndef PLATFORMINDEPENDENT -#include -#include -#include -#endif using namespace llvm; +namespace { + // ChildOutput - This option captures the name of the child output file that + // is set up by the parent bugpoint process + cl::opt ChildOutput("child-output", cl::ReallyHidden); +} + /// writeProgramToFile - This writes the current "Program" to the named bytecode /// file. If an error occurs, true is returned. /// @@ -86,14 +85,14 @@ void BugDriver::EmitProgressBytecode(const std::string &ID, bool NoFlyer) { std::cout << getPassesString(PassesToRun) << "\n"; } -static void RunChild(Module *Program,const std::vector &Passes, - const std::string &OutFilename) { +int BugDriver::runPassesAsChild(const std::vector &Passes) { + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | std::ios::binary; - std::ofstream OutFile(OutFilename.c_str(), io_mode); + std::ofstream OutFile(ChildOutput.c_str(), io_mode); if (!OutFile.good()) { - std::cerr << "Error opening bytecode file: " << OutFilename << "\n"; - exit(1); + std::cerr << "Error opening bytecode file: " << ChildOutput << "\n"; + return 1; } PassManager PM; @@ -115,6 +114,8 @@ static void RunChild(Module *Program,const std::vector &Passes, // Run all queued passes. PM.run(*Program); + + return 0; } /// runPasses - Run the specified passes on Program, outputting a bytecode file @@ -128,60 +129,67 @@ static void RunChild(Module *Program,const std::vector &Passes, bool BugDriver::runPasses(const std::vector &Passes, std::string &OutputFilename, bool DeleteOutput, bool Quiet) const{ + // setup the output file name std::cout << std::flush; sys::Path uniqueFilename("bugpoint-output.bc"); uniqueFilename.makeUnique(); OutputFilename = uniqueFilename.toString(); -#ifndef PLATFORMINDEPENDENT - 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; + // set up the input file name + sys::Path inputFilename("bugpoint-input.bc"); + inputFilename.makeUnique(); + std::ios::openmode io_mode = std::ios::out | std::ios::trunc | + std::ios::binary; + std::ofstream InFile(inputFilename.c_str(), io_mode); + if (!InFile.good()) { + std::cerr << "Error opening bytecode file: " << inputFilename << "\n"; + return(1); } + WriteBytecodeToFile(Program,InFile,false); + InFile.close(); - // 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); - } + // setup the child process' arguments + const char** args = (const char**) + alloca(sizeof(const char*)*(Passes.size()+10)); + int n = 0; + args[n++] = ToolName.c_str(); + args[n++] = "-as-child"; + args[n++] = "-child-output"; + args[n++] = OutputFilename.c_str(); + std::vector pass_args; + for (std::vector::const_iterator I = Passes.begin(), + E = Passes.end(); I != E; ++I ) + pass_args.push_back( std::string("-") + (*I)->getPassArgument() ); + for (std::vector::const_iterator I = pass_args.begin(), + E = pass_args.end(); I != E; ++I ) + args[n++] = I->c_str(); + args[n++] = inputFilename.c_str(); + args[n++] = 0; - bool ExitedOK = WIFEXITED(Status) && WEXITSTATUS(Status) == 0; -#else - bool ExitedOK = false; -#endif + sys::Path prog(sys::Program::FindProgramByName(ToolName)); + int result = sys::Program::ExecuteAndWait(prog,args); // If we are supposed to delete the bytecode file or if the passes crashed, // remove it now. This may fail if the file was never created, but that's ok. - if (DeleteOutput || !ExitedOK) + if (DeleteOutput || result != 0) sys::Path(OutputFilename).eraseFromDisk(); -#ifndef PLATFORMINDEPENDENT + // Remove the temporary input file as well + inputFilename.eraseFromDisk(); + if (!Quiet) { - if (ExitedOK) + if (result == 0) std::cout << "Success!\n"; - else if (WIFEXITED(Status)) - std::cout << "Exited with error code '" << WEXITSTATUS(Status) << "'\n"; - else if (WIFSIGNALED(Status)) - std::cout << "Crashed with signal #" << WTERMSIG(Status) << "\n"; -#ifdef WCOREDUMP - else if (WCOREDUMP(Status)) + else if (result > 0) + std::cout << "Exited with error code '" << result << "'\n"; + else if (result < 0) + std::cout << "Crashed with signal #" << abs(result) << "\n"; + if (result & 0x01000000) std::cout << "Dumped core\n"; -#endif - else - std::cout << "Failed for unknown reason!\n"; } -#endif // Was the child successful? - return !ExitedOK; + return result != 0; } diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index 0f25cf8a506..dde7286ae9f 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -24,6 +24,13 @@ #include "llvm/System/Signals.h" using namespace llvm; +// AsChild - Specifies that this invocation of bugpoint is being generated +// from a parent process. It is not intended to be used by users so the +// option is hidden. +static cl::opt + AsChild("as-child", cl::desc("Run bugpoint as child process"), + cl::ReallyHidden); + static cl::list InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("")); @@ -49,7 +56,7 @@ int main(int argc, char **argv) { sys::PrintStackTraceOnErrorSignal(); sys::SetInterruptFunction(BugpointInterruptFunction); - BugDriver D(argv[0]); + BugDriver D(argv[0],AsChild); if (D.addSources(InputFilenames)) return 1; D.addPasses(PassList.begin(), PassList.end());