mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-24 20:30:06 +00:00
For PR351:
Generally, remove use of fork/exec from bugpoint in favor of the portable sys::Program::ExecuteAndWait method. This change requires two new options to bugpoint to tell it that it is running in "child" mode. In this mode, it reads its input and runs the passes. The result code signals to the parent instance of bugpoint what happened (success, fail, crash). This change should make bugpoint usable on Win32 systems. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@24961 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
d555f413cd
commit
c4bb052ecc
@ -62,9 +62,9 @@ std::string llvm::getPassesString(const std::vector<const PassInfo*> &Passes) {
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BugDriver::BugDriver(const char *toolname)
|
BugDriver::BugDriver(const char *toolname, bool as_child)
|
||||||
: ToolName(toolname), ReferenceOutputFile(OutputFile),
|
: 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
|
/// ParseInputFile - Given a bytecode or assembly input filename, parse and
|
||||||
@ -97,13 +97,15 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
|
|||||||
// Load the first input file...
|
// Load the first input file...
|
||||||
Program = ParseInputFile(Filenames[0]);
|
Program = ParseInputFile(Filenames[0]);
|
||||||
if (Program == 0) return true;
|
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) {
|
for (unsigned i = 1, e = Filenames.size(); i != e; ++i) {
|
||||||
std::auto_ptr<Module> M(ParseInputFile(Filenames[i]));
|
std::auto_ptr<Module> M(ParseInputFile(Filenames[i]));
|
||||||
if (M.get() == 0) return true;
|
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;
|
std::string ErrorMessage;
|
||||||
if (Linker::LinkModules(Program, M.get(), &ErrorMessage)) {
|
if (Linker::LinkModules(Program, M.get(), &ErrorMessage)) {
|
||||||
std::cerr << ToolName << ": error linking in '" << Filenames[i] << "': "
|
std::cerr << ToolName << ": error linking in '" << Filenames[i] << "': "
|
||||||
@ -112,7 +114,8 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "*** All input ok\n";
|
if (!run_as_child)
|
||||||
|
std::cout << "*** All input ok\n";
|
||||||
|
|
||||||
// All input files read successfully!
|
// All input files read successfully!
|
||||||
return false;
|
return false;
|
||||||
@ -124,12 +127,21 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
|
|||||||
/// variables are set up from command line arguments.
|
/// variables are set up from command line arguments.
|
||||||
///
|
///
|
||||||
bool BugDriver::run() {
|
bool BugDriver::run() {
|
||||||
// The first thing that we must do is determine what the problem is. Does the
|
// The first thing to do is determine if we're running as a child. If we are,
|
||||||
// optimization series crash the compiler, or does it produce illegal code?
|
// then what to do is very narrow. This form of invocation is only called
|
||||||
// We make the top-level decision by trying to run all of the passes on the
|
// from the runPasses method to actually run those passes in a child process.
|
||||||
// the input program, which should generate a bytecode file. If it does
|
if (run_as_child) {
|
||||||
// generate a bytecode file, then we know the compiler didn't crash, so try
|
// Execute the passes
|
||||||
// to diagnose a miscompilation.
|
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()) {
|
if (!PassesToRun.empty()) {
|
||||||
std::cout << "Running selected passes on program to test for crash: ";
|
std::cout << "Running selected passes on program to test for crash: ";
|
||||||
if (runPasses(PassesToRun))
|
if (runPasses(PassesToRun))
|
||||||
|
@ -47,13 +47,14 @@ class BugDriver {
|
|||||||
AbstractInterpreter *Interpreter; // How to run the program
|
AbstractInterpreter *Interpreter; // How to run the program
|
||||||
CBE *cbe;
|
CBE *cbe;
|
||||||
GCC *gcc;
|
GCC *gcc;
|
||||||
|
bool run_as_child;
|
||||||
|
|
||||||
// FIXME: sort out public/private distinctions...
|
// FIXME: sort out public/private distinctions...
|
||||||
friend class ReducePassList;
|
friend class ReducePassList;
|
||||||
friend class ReduceMisCodegenFunctions;
|
friend class ReduceMisCodegenFunctions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BugDriver(const char *toolname);
|
BugDriver(const char *toolname, bool as_child);
|
||||||
|
|
||||||
const std::string &getToolName() const { return ToolName; }
|
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
|
/// 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();
|
bool run();
|
||||||
|
|
||||||
@ -249,6 +251,9 @@ private:
|
|||||||
return runPasses(PassesToRun, Filename, DeleteOutput);
|
return runPasses(PassesToRun, Filename, DeleteOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// runAsChild - The actual "runPasses" guts that runs in a child process.
|
||||||
|
int runPassesAsChild(const std::vector<const PassInfo*> &PassesToRun);
|
||||||
|
|
||||||
/// initializeExecutionEnvironment - This method is used to set up the
|
/// initializeExecutionEnvironment - This method is used to set up the
|
||||||
/// environment for executing LLVM programs.
|
/// environment for executing LLVM programs.
|
||||||
///
|
///
|
||||||
|
@ -19,10 +19,6 @@
|
|||||||
// independent code co-exist via conditional compilation until it is verified
|
// independent code co-exist via conditional compilation until it is verified
|
||||||
// that the new code works correctly on Unix.
|
// that the new code works correctly on Unix.
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define PLATFORMINDEPENDENT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "BugDriver.h"
|
#include "BugDriver.h"
|
||||||
#include "llvm/Module.h"
|
#include "llvm/Module.h"
|
||||||
#include "llvm/PassManager.h"
|
#include "llvm/PassManager.h"
|
||||||
@ -30,15 +26,18 @@
|
|||||||
#include "llvm/Bytecode/WriteBytecodePass.h"
|
#include "llvm/Bytecode/WriteBytecodePass.h"
|
||||||
#include "llvm/Target/TargetData.h"
|
#include "llvm/Target/TargetData.h"
|
||||||
#include "llvm/Support/FileUtilities.h"
|
#include "llvm/Support/FileUtilities.h"
|
||||||
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/System/Path.h"
|
#include "llvm/System/Path.h"
|
||||||
|
#include "llvm/System/Program.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#ifndef PLATFORMINDEPENDENT
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#endif
|
|
||||||
using namespace llvm;
|
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<std::string> ChildOutput("child-output", cl::ReallyHidden);
|
||||||
|
}
|
||||||
|
|
||||||
/// writeProgramToFile - This writes the current "Program" to the named bytecode
|
/// writeProgramToFile - This writes the current "Program" to the named bytecode
|
||||||
/// file. If an error occurs, true is returned.
|
/// 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";
|
std::cout << getPassesString(PassesToRun) << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RunChild(Module *Program,const std::vector<const PassInfo*> &Passes,
|
int BugDriver::runPassesAsChild(const std::vector<const PassInfo*> &Passes) {
|
||||||
const std::string &OutFilename) {
|
|
||||||
std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
|
std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
|
||||||
std::ios::binary;
|
std::ios::binary;
|
||||||
std::ofstream OutFile(OutFilename.c_str(), io_mode);
|
std::ofstream OutFile(ChildOutput.c_str(), io_mode);
|
||||||
if (!OutFile.good()) {
|
if (!OutFile.good()) {
|
||||||
std::cerr << "Error opening bytecode file: " << OutFilename << "\n";
|
std::cerr << "Error opening bytecode file: " << ChildOutput << "\n";
|
||||||
exit(1);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PassManager PM;
|
PassManager PM;
|
||||||
@ -115,6 +114,8 @@ static void RunChild(Module *Program,const std::vector<const PassInfo*> &Passes,
|
|||||||
|
|
||||||
// Run all queued passes.
|
// Run all queued passes.
|
||||||
PM.run(*Program);
|
PM.run(*Program);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// runPasses - Run the specified passes on Program, outputting a bytecode file
|
/// runPasses - Run the specified passes on Program, outputting a bytecode file
|
||||||
@ -128,60 +129,67 @@ static void RunChild(Module *Program,const std::vector<const PassInfo*> &Passes,
|
|||||||
bool BugDriver::runPasses(const std::vector<const PassInfo*> &Passes,
|
bool BugDriver::runPasses(const std::vector<const PassInfo*> &Passes,
|
||||||
std::string &OutputFilename, bool DeleteOutput,
|
std::string &OutputFilename, bool DeleteOutput,
|
||||||
bool Quiet) const{
|
bool Quiet) const{
|
||||||
|
// setup the output file name
|
||||||
std::cout << std::flush;
|
std::cout << std::flush;
|
||||||
sys::Path uniqueFilename("bugpoint-output.bc");
|
sys::Path uniqueFilename("bugpoint-output.bc");
|
||||||
uniqueFilename.makeUnique();
|
uniqueFilename.makeUnique();
|
||||||
OutputFilename = uniqueFilename.toString();
|
OutputFilename = uniqueFilename.toString();
|
||||||
|
|
||||||
#ifndef PLATFORMINDEPENDENT
|
// set up the input file name
|
||||||
pid_t child_pid;
|
sys::Path inputFilename("bugpoint-input.bc");
|
||||||
switch (child_pid = fork()) {
|
inputFilename.makeUnique();
|
||||||
case -1: // Error occurred
|
std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
|
||||||
std::cerr << ToolName << ": Error forking!\n";
|
std::ios::binary;
|
||||||
exit(1);
|
std::ofstream InFile(inputFilename.c_str(), io_mode);
|
||||||
case 0: // Child process runs passes.
|
if (!InFile.good()) {
|
||||||
RunChild(Program, Passes, OutputFilename);
|
std::cerr << "Error opening bytecode file: " << inputFilename << "\n";
|
||||||
exit(0); // If we finish successfully, return 0!
|
return(1);
|
||||||
default: // Parent continues...
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
WriteBytecodeToFile(Program,InFile,false);
|
||||||
|
InFile.close();
|
||||||
|
|
||||||
// Wait for the child process to get done.
|
// setup the child process' arguments
|
||||||
int Status;
|
const char** args = (const char**)
|
||||||
if (wait(&Status) != child_pid) {
|
alloca(sizeof(const char*)*(Passes.size()+10));
|
||||||
std::cerr << "Error waiting for child process!\n";
|
int n = 0;
|
||||||
exit(1);
|
args[n++] = ToolName.c_str();
|
||||||
}
|
args[n++] = "-as-child";
|
||||||
|
args[n++] = "-child-output";
|
||||||
|
args[n++] = OutputFilename.c_str();
|
||||||
|
std::vector<std::string> pass_args;
|
||||||
|
for (std::vector<const PassInfo*>::const_iterator I = Passes.begin(),
|
||||||
|
E = Passes.end(); I != E; ++I )
|
||||||
|
pass_args.push_back( std::string("-") + (*I)->getPassArgument() );
|
||||||
|
for (std::vector<std::string>::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;
|
sys::Path prog(sys::Program::FindProgramByName(ToolName));
|
||||||
#else
|
int result = sys::Program::ExecuteAndWait(prog,args);
|
||||||
bool ExitedOK = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// If we are supposed to delete the bytecode file or if the passes crashed,
|
// 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.
|
// 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();
|
sys::Path(OutputFilename).eraseFromDisk();
|
||||||
|
|
||||||
#ifndef PLATFORMINDEPENDENT
|
// Remove the temporary input file as well
|
||||||
|
inputFilename.eraseFromDisk();
|
||||||
|
|
||||||
if (!Quiet) {
|
if (!Quiet) {
|
||||||
if (ExitedOK)
|
if (result == 0)
|
||||||
std::cout << "Success!\n";
|
std::cout << "Success!\n";
|
||||||
else if (WIFEXITED(Status))
|
else if (result > 0)
|
||||||
std::cout << "Exited with error code '" << WEXITSTATUS(Status) << "'\n";
|
std::cout << "Exited with error code '" << result << "'\n";
|
||||||
else if (WIFSIGNALED(Status))
|
else if (result < 0)
|
||||||
std::cout << "Crashed with signal #" << WTERMSIG(Status) << "\n";
|
std::cout << "Crashed with signal #" << abs(result) << "\n";
|
||||||
#ifdef WCOREDUMP
|
if (result & 0x01000000)
|
||||||
else if (WCOREDUMP(Status))
|
|
||||||
std::cout << "Dumped core\n";
|
std::cout << "Dumped core\n";
|
||||||
#endif
|
|
||||||
else
|
|
||||||
std::cout << "Failed for unknown reason!\n";
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Was the child successful?
|
// Was the child successful?
|
||||||
return !ExitedOK;
|
return result != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,13 @@
|
|||||||
#include "llvm/System/Signals.h"
|
#include "llvm/System/Signals.h"
|
||||||
using namespace llvm;
|
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<bool>
|
||||||
|
AsChild("as-child", cl::desc("Run bugpoint as child process"),
|
||||||
|
cl::ReallyHidden);
|
||||||
|
|
||||||
static cl::list<std::string>
|
static cl::list<std::string>
|
||||||
InputFilenames(cl::Positional, cl::OneOrMore,
|
InputFilenames(cl::Positional, cl::OneOrMore,
|
||||||
cl::desc("<input llvm ll/bc files>"));
|
cl::desc("<input llvm ll/bc files>"));
|
||||||
@ -49,7 +56,7 @@ int main(int argc, char **argv) {
|
|||||||
sys::PrintStackTraceOnErrorSignal();
|
sys::PrintStackTraceOnErrorSignal();
|
||||||
sys::SetInterruptFunction(BugpointInterruptFunction);
|
sys::SetInterruptFunction(BugpointInterruptFunction);
|
||||||
|
|
||||||
BugDriver D(argv[0]);
|
BugDriver D(argv[0],AsChild);
|
||||||
if (D.addSources(InputFilenames)) return 1;
|
if (D.addSources(InputFilenames)) return 1;
|
||||||
D.addPasses(PassList.begin(), PassList.end());
|
D.addPasses(PassList.begin(), PassList.end());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user