Major addition to bugpoint: ability to debug code generators (LLC and LLI).

The C backend is assumed correct and is used to generate shared objects to be
loaded by the other two code generators.

LLC debugging should be functional now, LLI needs a few more additions to work,
the major one is renaming of external functions to call the JIT lazy function
resolver.

Bugpoint now has a command-line switch -mode with options 'compile' and
'codegen' to debug appropriate portions of tools.

ExecutionDriver.cpp: Added implementations of AbstractInterpreter for LLC and
GCC, broke out common code within other tools, and added ability to generate C
code with CBE individually, without executing the program, and the GCC tool can
generate executables shared objects or executables.

If no reference output is specified to Bugpoint, it will be generated with CBE,
because it is already assumed to be correct for the purposes of debugging using
this method. As a result, many functions now accept as an optional parameter a
shared object to be loaded in, if specified.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@7293 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Misha Brukman 2003-07-24 18:17:43 +00:00
parent 08fd7abb42
commit 5073336cd4
6 changed files with 620 additions and 167 deletions

View File

@ -7,13 +7,36 @@
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "SystemUtils.h"
#include "llvm/Module.h"
#include "llvm/Bytecode/Reader.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Transforms/Utils/Linker.h"
#include "llvm/Pass.h"
#include "Support/CommandLine.h"
#include <memory>
// Anonymous namespace to define command line options for debugging.
//
namespace {
// Output - The user can specify a file containing the expected output of the
// program. If this filename is set, it is used as the reference diff source,
// otherwise the raw input run through an interpreter is used as the reference
// source.
//
cl::opt<std::string>
OutputFile("output", cl::desc("Specify a reference program output "
"(for miscompilation detection)"));
enum DebugType { DebugCompile, DebugCodegen };
cl::opt<DebugType>
DebugMode("mode", cl::desc("Debug mode for bugpoint:"), cl::Prefix,
cl::values(clEnumValN(DebugCompile, "compile", " Compilation"),
clEnumValN(DebugCodegen, "codegen", " Code generation"),
0),
cl::init(DebugCompile));
}
/// getPassesString - Turn a list of passes into a string which indicates the
/// command line options that must be passed to add the passes.
///
@ -41,6 +64,11 @@ void DeleteFunctionBody(Function *F) {
assert(F->isExternal() && "This didn't make the function external!");
}
BugDriver::BugDriver(const char *toolname)
: ToolName(toolname), ReferenceOutputFile(OutputFile),
Program(0), Interpreter(0) {}
/// ParseInputFile - Given a bytecode or assembly input filename, parse and
/// return it, or return null if not possible.
///
@ -108,6 +136,44 @@ bool BugDriver::run() {
std::cout << "Running selected passes on program to test for crash: ";
if (runPasses(PassesToRun))
return debugCrash();
else
return debugMiscompilation();
std::cout << "Checking for a miscompilation...\n";
// Set up the execution environment, selecting a method to run LLVM bytecode.
if (initializeExecutionEnvironment()) return true;
// Run the raw input to see where we are coming from. If a reference output
// was specified, make sure that the raw output matches it. If not, it's a
// problem in the front-end or the code generator.
//
bool CreatedOutput = false, Result;
if (ReferenceOutputFile.empty()) {
std::cout << "Generating reference output from raw program...";
if (DebugCodegen) {
ReferenceOutputFile = executeProgramWithCBE("bugpoint.reference.out");
} else {
ReferenceOutputFile = executeProgram("bugpoint.reference.out");
}
CreatedOutput = true;
std::cout << "Reference output is: " << ReferenceOutputFile << "\n";
}
if (DebugMode == DebugCompile) {
std::cout << "\n*** Debugging miscompilation!\n";
Result = debugMiscompilation();
} else if (DebugMode == DebugCodegen) {
std::cout << "Debugging code generator problem!\n";
Result = debugCodeGenerator();
}
if (CreatedOutput) removeFile(ReferenceOutputFile);
return Result;
}
void BugDriver::PrintFunctionList(const std::vector<Function*> &Funcs)
{
for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
if (i) std::cout << ", ";
std::cout << Funcs[i]->getName();
}
}

View File

@ -9,8 +9,10 @@
#ifndef BUGDRIVER_H
#define BUGDRIVER_H
#include "Support/CommandLine.h"
#include <vector>
#include <string>
class PassInfo;
class Module;
class Function;
@ -25,6 +27,7 @@ class ReduceCrashingBlocks;
class BugDriver {
const std::string ToolName; // Name of bugpoint
cl::opt<std::string> ReferenceOutputFile; // Name of `good' output file
Module *Program; // The raw program, linked together
std::vector<const PassInfo*> PassesToRun;
AbstractInterpreter *Interpreter; // How to run the program
@ -33,11 +36,12 @@ class BugDriver {
friend class DebugCrashes;
friend class ReduceMiscompilingPasses;
friend class ReduceMiscompilingFunctions;
friend class ReduceMisCodegenFunctions;
friend class ReduceCrashingFunctions;
friend class ReduceCrashingBlocks;
public:
BugDriver(const char *toolname)
: ToolName(toolname), Program(0), Interpreter(0) {}
BugDriver(const char *toolname);
const std::string &getToolName() const { return ToolName; }
@ -73,6 +77,17 @@ public:
bool debugPassMiscompilation(const PassInfo *ThePass,
const std::string &ReferenceOutput);
/// compileSharedObject - This method creates a SharedObject from a given
/// BytecodeFile for debugging a code generator.
int compileSharedObject(const std::string &BytecodeFile,
std::string &SharedObject);
/// 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();
private:
/// ParseInputFile - Given a bytecode or assembly input filename, parse and
/// return it, or return null if not possible.
@ -112,6 +127,9 @@ private:
return runPasses(PassesToRun, Filename, DeleteOutput);
}
/// PrintFunctionList - prints out list of problematic functions
static void PrintFunctionList(const std::vector<Function*> &Funcs);
/// deleteInstructionFromProgram - This method clones the current Program and
/// deletes the specified instruction from the cloned module. It then runs a
/// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code
@ -135,14 +153,22 @@ private:
/// filename may be optionally specified.
///
std::string executeProgram(std::string RequestedOutputFilename = "",
std::string Bytecode = "");
std::string Bytecode = "",
std::string SharedObject = "",
AbstractInterpreter *AI = 0);
/// executeProgramWithCBE - Used to create reference output with the C
/// backend, if reference output is not provided.
std::string executeProgramWithCBE(std::string RequestedOutputFilename = "",
std::string Bytecode = "",
std::string SharedObject = "");
/// diffProgram - This method executes the specified module and diffs the
/// output against the file specified by ReferenceOutputFile. If the output
/// is different, true is returned.
///
bool diffProgram(const std::string &ReferenceOutputFile,
const std::string &BytecodeFile = "",
bool diffProgram(const std::string &BytecodeFile = "",
const std::string &SharedObject = "",
bool RemoveBytecode = false);
};

View File

@ -0,0 +1,176 @@
//===- CodeGeneratorBug.cpp - Debug code generation bugs ------------------===//
//
// This file implements program code generation debugging support.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
#include "SystemUtils.h"
#include "ListReducer.h"
#include "llvm/Pass.h"
#include "llvm/Module.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Linker.h"
#include "Support/CommandLine.h"
#include "Support/Statistic.h"
#include "Support/StringExtras.h"
#include <algorithm>
#include <set>
// Passed as a command-line argument to Bugpoint
extern cl::opt<std::string> Output;
class ReduceMisCodegenFunctions : public ListReducer<Function*> {
BugDriver &BD;
public:
ReduceMisCodegenFunctions(BugDriver &bd) : BD(bd) {}
virtual TestResult doTest(std::vector<Function*> &Prefix,
std::vector<Function*> &Suffix) {
if (!Prefix.empty() && TestFuncs(Prefix))
return KeepPrefix;
if (!Suffix.empty() && TestFuncs(Suffix))
return KeepSuffix;
return NoFailure;
}
bool TestFuncs(const std::vector<Function*> &CodegenTest);
void DisambiguateGlobalSymbols(Module *M);
};
bool ReduceMisCodegenFunctions::TestFuncs(const std::vector<Function*> &Funcs)
{
// Clone the module for the two halves of the program we want.
Module *SafeModule = CloneModule(BD.Program);
// Make sure functions & globals are all external so that linkage
// between the two modules will work.
for (Module::iterator I = SafeModule->begin(), E = SafeModule->end();I!=E;++I)
I->setLinkage(GlobalValue::ExternalLinkage);
for (Module::giterator I=SafeModule->gbegin(),E = SafeModule->gend();I!=E;++I)
I->setLinkage(GlobalValue::ExternalLinkage);
DisambiguateGlobalSymbols(SafeModule);
Module *TestModule = CloneModule(SafeModule);
// Make sure global initializers exist only in the safe module (CBE->.so)
for (Module::giterator I=TestModule->gbegin(),E = TestModule->gend();I!=E;++I)
I->setInitializer(0); // Delete the initializer to make it external
// Remove the Test functions from the Safe module, and
// all of the global variables.
for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
Function *TNOF = SafeModule->getFunction(Funcs[i]->getName(),
Funcs[i]->getFunctionType());
assert(TNOF && "Function doesn't exist in module!");
DeleteFunctionBody(TNOF); // Function is now external in this module!
}
// Write out the bytecode to be sent to CBE
std::string SafeModuleBC = "bugpoint.safe.bc";
if (BD.writeProgramToFile(SafeModuleBC, SafeModule)) {
std::cerr << "Error writing bytecode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
// Make a shared library
std::string SharedObject;
BD.compileSharedObject(SafeModuleBC, SharedObject);
// Remove all functions from the Test module EXCEPT for the ones specified in
// Funcs. We know which ones these are because they are non-external in
// ToOptimize, but external in ToNotOptimize.
//
for (Module::iterator I = TestModule->begin(), E = TestModule->end();I!=E;++I)
if (!I->isExternal()) {
Function *TNOF = SafeModule->getFunction(I->getName(),
I->getFunctionType());
assert(TNOF && "Function doesn't exist in ToNotOptimize module??");
if (!TNOF->isExternal())
DeleteFunctionBody(I);
}
std::string TestModuleBC = "bugpoint.test.bc";
if (BD.writeProgramToFile(TestModuleBC, TestModule)) {
std::cerr << "Error writing bytecode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
// Run the code generator on the `Test' code, loading the shared library.
// The function returns whether or not the new output differs from reference.
return BD.diffProgram(TestModuleBC, SharedObject, false);
}
namespace {
struct Disambiguator /*: public unary_function<GlobalValue&, void>*/ {
std::set<std::string> SymbolNames;
std::set<Value*> Symbols;
uint64_t uniqueCounter;
bool externalOnly;
Disambiguator() : uniqueCounter(0), externalOnly(true) {}
void setExternalOnly(bool value) { externalOnly = value; }
void operator() (GlobalValue &V) {
if (externalOnly && !V.isExternal()) return;
if (SymbolNames.count(V.getName()) == 0) {
DEBUG(std::cerr << "Disambiguator: adding " << V.getName()
<< ", no conflicts.\n");
Symbols.insert(&V);
SymbolNames.insert(V.getName());
} else {
// Mangle name before adding
std::string newName;
do {
newName = V.getName() + "_" + utostr(uniqueCounter);
if (SymbolNames.count(newName) == 0) break;
else ++uniqueCounter;
} while (1);
//while (SymbolNames.count(V->getName()+utostr(uniqueCounter++))==0);
DEBUG(std::cerr << "Disambiguator: conflict: " << V.getName()
<< ", adding: " << newName << "\n");
V.setName(newName);
SymbolNames.insert(newName);
Symbols.insert(&V);
}
}
};
}
void ReduceMisCodegenFunctions::DisambiguateGlobalSymbols(Module *M) {
// First, try not to cause collisions by minimizing chances of renaming an
// already-external symbol, so take in external globals and functions as-is.
Disambiguator D = std::for_each(M->gbegin(), M->gend(), Disambiguator());
std::for_each(M->begin(), M->end(), D);
// Now just rename functions and globals as necessary, keeping what's already
// in the set unique.
D.setExternalOnly(false);
std::for_each(M->gbegin(), M->gend(), D);
std::for_each(M->begin(), M->end(), D);
}
bool BugDriver::debugCodeGenerator() {
// See if we can pin down which functions are being miscompiled...
//First, build a list of all of the non-external functions in the program.
std::vector<Function*> MisCodegenFunctions;
for (Module::iterator I = Program->begin(), E = Program->end(); I != E; ++I)
if (!I->isExternal())
MisCodegenFunctions.push_back(I);
// Do the reduction...
ReduceMisCodegenFunctions(*this).reduceList(MisCodegenFunctions);
std::cout << "\n*** The following functions are being miscompiled: ";
PrintFunctionList(MisCodegenFunctions);
std::cout << "\n";
// Output a bunch of bytecode files for the user...
ReduceMisCodegenFunctions(*this).TestFuncs(MisCodegenFunctions);
return false;
}

View File

@ -11,7 +11,7 @@ BUGPOINT NOTES:
1. Bugpoint should not leave any files behind if the program works properly
2. There should be an option to specify the program name, which specifies a
unique string to put into output files. This allows operation in the
SingleSource directory f.e. Default to the first input filename.
SingleSource directory, e.g. default to the first input filename.
*/
#include "BugDriver.h"
@ -52,8 +52,8 @@ struct AbstractInterpreter {
/// specified filename. This returns the exit code of the program.
///
virtual int ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile) = 0;
const std::string &OutputFile,
const std::string &SharedLib = "") = 0;
};
@ -73,17 +73,25 @@ public:
return new LLI(LLIPath);
}
Message = "Cannot find 'lli' in bugpoint executable directory or PATH!\n";
Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n";
return 0;
}
virtual int ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile);
const std::string &OutputFile,
const std::string &SharedLib = "");
};
int LLI::ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile) {
const std::string &OutputFile,
const std::string &SharedLib) {
if (SharedLib != "") {
std::cerr << "LLI currently does not support loading shared libraries.\n"
<< "Exiting.\n";
exit(1);
}
const char *Args[] = {
"lli",
LLIPath.c_str(),
"-abort-on-exception",
"-quiet",
"-force-interpreter=true",
@ -95,6 +103,209 @@ int LLI::ExecuteProgram(const std::string &Bytecode,
InputFile, OutputFile, OutputFile);
}
//===----------------------------------------------------------------------===//
// GCC Implementation of AbstractIntepreter interface
//
// This is not a *real* AbstractInterpreter as it does not accept bytecode
// files, but only input acceptable to GCC, i.e. C, C++, and assembly files
//
class GCC : public AbstractInterpreter {
std::string GCCPath; // The path to the gcc executable
public:
GCC(const std::string &gccPath) : GCCPath(gccPath) { }
// GCC create method - Try to find the `gcc' executable
static GCC *create(BugDriver *BD, std::string &Message) {
std::string GCCPath = FindExecutable("gcc", BD->getToolName());
if (GCCPath.empty()) {
Message = "Cannot find `gcc' in bugpoint executable directory or PATH!\n";
return 0;
}
Message = "Found gcc: " + GCCPath + "\n";
return new GCC(GCCPath);
}
virtual int ExecuteProgram(const std::string &ProgramFile,
const std::string &OutputFile,
const std::string &SharedLib = "");
int MakeSharedObject(const std::string &InputFile,
std::string &OutputFile);
void ProcessFailure(const char **Args);
};
int GCC::ExecuteProgram(const std::string &ProgramFile,
const std::string &OutputFile,
const std::string &SharedLib) {
std::string OutputBinary = "bugpoint.gcc.exe";
const char **GCCArgs;
const char *ArgsWithoutSO[] = {
GCCPath.c_str(),
ProgramFile.c_str(), // Specify the input filename...
"-o", OutputBinary.c_str(), // Output to the right filename...
"-lm", // Hard-code the math library...
"-O2", // Optimize the program a bit...
0
};
const char *ArgsWithSO[] = {
GCCPath.c_str(),
ProgramFile.c_str(), // Specify the input filename...
SharedLib.c_str(), // Specify the shared library to link in...
"-o", OutputBinary.c_str(), // Output to the right filename...
"-lm", // Hard-code the math library...
"-O2", // Optimize the program a bit...
0
};
GCCArgs = (SharedLib == "") ? ArgsWithoutSO : ArgsWithSO;
std::cout << "<gcc>";
if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null",
"/dev/null", "/dev/null")) {
ProcessFailure(GCCArgs);
exit(1); // Leave stuff around for the user to inspect or debug the CBE
}
const char *ProgramArgs[] = {
OutputBinary.c_str(),
0
};
std::cout << "<program>";
// Now that we have a binary, run it!
int ProgramResult = RunProgramWithTimeout(OutputBinary, ProgramArgs,
InputFile, OutputFile, OutputFile);
std::cout << "\n";
removeFile(OutputBinary);
return ProgramResult;
}
int GCC::MakeSharedObject(const std::string &InputFile,
std::string &OutputFile) {
OutputFile = "./bugpoint.so";
// Compile the C/asm file into a shared object
const char* GCCArgs[] = {
GCCPath.c_str(),
InputFile.c_str(), // Specify the input filename...
#if defined(sparc) || defined(__sparc__) || defined(__sparcv9)
"-G", // Compile a shared library, `-G' for Sparc
#else
"-shared", // `-shared' for Linux/X86, maybe others
#endif
"-o", OutputFile.c_str(), // Output to the right filename...
"-O2", // Optimize the program a bit...
0
};
std::cout << "<gcc>";
if(RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
"/dev/null")) {
ProcessFailure(GCCArgs);
exit(1);
}
return 0;
}
void GCC::ProcessFailure(const char** GCCArgs) {
std::cerr << "\n*** bugpoint error: invocation of the C compiler failed!\n";
for (const char **Arg = GCCArgs; *Arg; ++Arg)
std::cerr << " " << *Arg;
std::cerr << "\n";
// Rerun the compiler, capturing any error messages to print them.
std::string ErrorFilename = getUniqueFilename("bugpoint.gcc.errors");
RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(),
ErrorFilename.c_str());
// Print out the error messages generated by GCC if possible...
std::ifstream ErrorFile(ErrorFilename.c_str());
if (ErrorFile) {
std::copy(std::istreambuf_iterator<char>(ErrorFile),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::cerr));
ErrorFile.close();
std::cerr << "\n";
}
removeFile(ErrorFilename);
}
//===----------------------------------------------------------------------===//
// LLC Implementation of AbstractIntepreter interface
//
class LLC : public AbstractInterpreter {
std::string LLCPath; // The path to the LLC executable
GCC *gcc;
public:
LLC(const std::string &llcPath, GCC *Gcc)
: LLCPath(llcPath), gcc(Gcc) { }
~LLC() { delete gcc; }
// LLC create method - Try to find the LLC executable
static LLC *create(BugDriver *BD, std::string &Message) {
std::string LLCPath = FindExecutable("llc", BD->getToolName());
if (LLCPath.empty()) {
Message = "Cannot find `llc' in bugpoint executable directory or PATH!\n";
return 0;
}
Message = "Found llc: " + LLCPath + "\n";
GCC *gcc = GCC::create(BD, Message);
return new LLC(LLCPath, gcc);
}
virtual int ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile,
const std::string &SharedLib = "");
int OutputAsm(const std::string &Bytecode,
std::string &OutputAsmFile);
};
int LLC::OutputAsm(const std::string &Bytecode,
std::string &OutputAsmFile) {
OutputAsmFile = "bugpoint.llc.s";
const char *LLCArgs[] = {
LLCPath.c_str(),
"-o", OutputAsmFile.c_str(), // Output to the Asm file
"-f", // Overwrite as necessary...
Bytecode.c_str(), // This is the input bytecode
0
};
std::cout << "<llc>";
if (RunProgramWithTimeout(LLCPath, LLCArgs, "/dev/null", "/dev/null",
"/dev/null")) {
// If LLC failed on the bytecode, print error...
std::cerr << "bugpoint error: `llc' failed!\n";
removeFile(OutputAsmFile);
return 1;
}
return 0;
}
int LLC::ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile,
const std::string &SharedLib) {
std::string OutputAsmFile;
if (OutputAsm(Bytecode, OutputAsmFile)) {
std::cerr << "Could not generate asm code with `llc', exiting.\n";
exit(1);
}
// Assuming LLC worked, compile the result with GCC and run it.
int Result = gcc->ExecuteProgram(OutputAsmFile, OutputFile, SharedLib);
removeFile(OutputAsmFile);
return Result;
}
//===----------------------------------------------------------------------===//
// JIT Implementation of AbstractIntepreter interface
//
@ -111,63 +322,75 @@ public:
return new JIT(LLIPath);
}
Message = "Cannot find 'lli' in bugpoint executable directory or PATH!\n";
Message = "Cannot find `lli' in bugpoint executable directory or PATH!\n";
return 0;
}
virtual int ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile);
const std::string &OutputFile,
const std::string &SharedLib = "");
};
int JIT::ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile) {
const std::string &OutputFile,
const std::string &SharedLib) {
if (SharedLib == "") {
const char* Args[] = {
"-lli",
"-quiet",
"-force-interpreter=false",
LLIPath.c_str(), "-quiet", "-force-interpreter=false", Bytecode.c_str(),
0
};
return RunProgramWithTimeout(LLIPath, Args,
InputFile, OutputFile, OutputFile);
} else {
std::string SharedLibOpt = "-load=" + SharedLib;
const char* Args[] = {
LLIPath.c_str(), "-quiet", "-force-interpreter=false",
SharedLibOpt.c_str(),
Bytecode.c_str(),
0
};
return RunProgramWithTimeout(LLIPath, Args,
InputFile, OutputFile, OutputFile);
}
}
//===----------------------------------------------------------------------===//
// CBE Implementation of AbstractIntepreter interface
//
class CBE : public AbstractInterpreter {
std::string DISPath; // The path to the LLVM 'dis' executable
std::string GCCPath; // The path to the gcc executable
GCC *gcc;
public:
CBE(const std::string &disPath, const std::string &gccPath)
: DISPath(disPath), GCCPath(gccPath) { }
CBE(const std::string &disPath, GCC *Gcc) : DISPath(disPath), gcc(Gcc) { }
~CBE() { delete gcc; }
// CBE create method - Try to find the 'dis' executable
static CBE *create(BugDriver *BD, std::string &Message) {
std::string DISPath = FindExecutable("dis", BD->getToolName());
if (DISPath.empty()) {
Message = "Cannot find 'dis' in bugpoint executable directory or PATH!\n";
Message = "Cannot find `dis' in bugpoint executable directory or PATH!\n";
return 0;
}
Message = "Found dis: " + DISPath + "\n";
std::string GCCPath = FindExecutable("gcc", BD->getToolName());
if (GCCPath.empty()) {
Message = "Cannot find 'gcc' in bugpoint executable directory or PATH!\n";
return 0;
GCC *gcc = GCC::create(BD, Message);
return new CBE(DISPath, gcc);
}
Message += "Found gcc: " + GCCPath + "\n";
return new CBE(DISPath, GCCPath);
}
virtual int ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile);
const std::string &OutputFile,
const std::string &SharedLib = "");
// Sometimes we just want to go half-way and only generate the C file,
// not necessarily compile it with GCC and run the program
virtual int OutputC(const std::string &Bytecode,
std::string &OutputCFile);
};
int CBE::ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile) {
std::string OutputCFile = getUniqueFilename("bugpoint.cbe.c");
int CBE::OutputC(const std::string &Bytecode,
std::string &OutputCFile) {
OutputCFile = "bugpoint.cbe.c";
const char *DisArgs[] = {
DISPath.c_str(),
"-o", OutputCFile.c_str(), // Output to the C file
@ -181,74 +404,30 @@ int CBE::ExecuteProgram(const std::string &Bytecode,
if (RunProgramWithTimeout(DISPath, DisArgs, "/dev/null", "/dev/null",
"/dev/null")) {
// If dis failed on the bytecode, print error...
std::cerr << "bugpoint error: dis -c failed!?\n";
removeFile(OutputCFile);
std::cerr << "bugpoint error: `dis -c' failed!\n";
return 1;
}
// Assuming the c backend worked, compile the result with GCC...
std::string OutputBinary = getUniqueFilename("bugpoint.cbe.exe");
const char *GCCArgs[] = {
GCCPath.c_str(),
"-x", "c", // Force recognition as a C file
"-o", OutputBinary.c_str(), // Output to the right filename...
OutputCFile.c_str(), // Specify the input filename...
"-O2", // Optimize the program a bit...
0
};
// FIXME: Eventually the CC program and arguments for it should be settable on
// the bugpoint command line!
std::cout << "<gcc>";
// Run the C compiler on the output of the C backend...
if (RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", "/dev/null",
"/dev/null")) {
std::cerr << "\n*** bugpoint error: invocation of the C compiler "
"failed on CBE result!\n";
for (const char **Arg = DisArgs; *Arg; ++Arg)
std::cerr << " " << *Arg;
std::cerr << "\n";
for (const char **Arg = GCCArgs; *Arg; ++Arg)
std::cerr << " " << *Arg;
std::cerr << "\n";
// Rerun the compiler, capturing any error messages to print them.
std::string ErrorFilename = getUniqueFilename("bugpoint.cbe.errors");
RunProgramWithTimeout(GCCPath, GCCArgs, "/dev/null", ErrorFilename.c_str(),
ErrorFilename.c_str());
// Print out the error messages generated by GCC if possible...
std::ifstream ErrorFile(ErrorFilename.c_str());
if (ErrorFile) {
std::copy(std::istreambuf_iterator<char>(ErrorFile),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(std::cerr));
ErrorFile.close();
std::cerr << "\n";
return 0;
}
removeFile(ErrorFilename);
exit(1); // Leave stuff around for the user to inspect or debug the CBE
int CBE::ExecuteProgram(const std::string &Bytecode,
const std::string &OutputFile,
const std::string &SharedLib) {
std::string OutputCFile;
if (OutputC(Bytecode, OutputCFile)) {
std::cerr << "Could not generate C code with `dis', exiting.\n";
exit(1);
}
const char *ProgramArgs[] = {
OutputBinary.c_str(),
0
};
std::cout << "<program>";
// Now that we have a binary, run it!
int Result = RunProgramWithTimeout(OutputBinary, ProgramArgs,
InputFile, OutputFile, OutputFile);
std::cout << " ";
int Result = gcc->ExecuteProgram(OutputCFile, OutputFile, SharedLib);
removeFile(OutputCFile);
removeFile(OutputBinary);
return Result;
}
//===----------------------------------------------------------------------===//
// BugDriver method implementation
//
@ -267,6 +446,7 @@ bool BugDriver::initializeExecutionEnvironment() {
std::string Message;
switch (InterpreterSel) {
case RunLLI: Interpreter = LLI::create(this, Message); break;
case RunLLC: Interpreter = LLC::create(this, Message); break;
case RunJIT: Interpreter = JIT::create(this, Message); break;
case RunCBE: Interpreter = CBE::create(this, Message); break;
default:
@ -286,8 +466,10 @@ bool BugDriver::initializeExecutionEnvironment() {
/// filename may be optionally specified.
///
std::string BugDriver::executeProgram(std::string OutputFile,
std::string BytecodeFile) {
assert(Interpreter && "Interpreter should have been created already!");
std::string BytecodeFile,
std::string SharedObject,
AbstractInterpreter *AI) {
assert((Interpreter || AI) &&"Interpreter should have been created already!");
bool CreatedBytecode = false;
if (BytecodeFile.empty()) {
// Emit the program to a bytecode file...
@ -307,25 +489,68 @@ std::string BugDriver::executeProgram(std::string OutputFile,
OutputFile = getUniqueFilename(OutputFile);
// Actually execute the program!
int RetVal = Interpreter->ExecuteProgram(BytecodeFile, OutputFile);
int RetVal = (AI != 0) ?
AI->ExecuteProgram(BytecodeFile, OutputFile, SharedObject) :
Interpreter->ExecuteProgram(BytecodeFile, OutputFile, SharedObject);
// Remove the temporary bytecode file.
if (CreatedBytecode)
removeFile(BytecodeFile);
if (CreatedBytecode) removeFile(BytecodeFile);
// Return the filename we captured the output to.
return OutputFile;
}
std::string BugDriver::executeProgramWithCBE(std::string OutputFile,
std::string BytecodeFile,
std::string SharedObject) {
std::string Output;
CBE *cbe = CBE::create(this, Output);
Output = executeProgram(OutputFile, BytecodeFile, SharedObject, cbe);
delete cbe;
return Output;
}
int BugDriver::compileSharedObject(const std::string &BytecodeFile,
std::string &SharedObject) {
assert(Interpreter && "Interpreter should have been created already!");
std::string Message, OutputCFile;
// Using CBE
CBE *cbe = CBE::create(this, Message);
cbe->OutputC(BytecodeFile, OutputCFile);
#if 0 /* This is an alternative, as yet unimplemented */
// Using LLC
LLC *llc = LLC::create(this, Message);
if (llc->OutputAsm(BytecodeFile, OutputFile)) {
std::cerr << "Could not generate asm code with `llc', exiting.\n";
exit(1);
}
#endif
GCC *gcc = GCC::create(this, Message);
gcc->MakeSharedObject(OutputCFile, SharedObject);
// Remove the intermediate C file
removeFile(OutputCFile);
// We are done with the CBE & GCC
delete cbe;
delete gcc;
return 0;
}
/// diffProgram - This method executes the specified module and diffs the output
/// against the file specified by ReferenceOutputFile. If the output is
/// different, true is returned.
///
bool BugDriver::diffProgram(const std::string &ReferenceOutputFile,
const std::string &BytecodeFile,
bool BugDriver::diffProgram(const std::string &BytecodeFile,
const std::string &SharedObject,
bool RemoveBytecode) {
// Execute the program, generating an output file...
std::string Output = executeProgram("", BytecodeFile);
std::string Output = executeProgram("", BytecodeFile, SharedObject);
std::ifstream ReferenceFile(ReferenceOutputFile.c_str());
if (!ReferenceFile) {

View File

@ -11,21 +11,6 @@
#include "llvm/Module.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Linker.h"
#include "Support/CommandLine.h"
// Anonymous namespace to define command line options for miscompilation
// debugging.
//
namespace {
// Output - The user can specify a file containing the expected output of the
// program. If this filename is set, it is used as the reference diff source,
// otherwise the raw input run through an interpreter is used as the reference
// source.
//
cl::opt<std::string>
Output("output", cl::desc("Specify a reference program output "
"(for miscompilation detection)"));
}
class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> {
BugDriver &BD;
@ -33,7 +18,7 @@ public:
ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}
virtual TestResult doTest(std::vector<const PassInfo*> &Prefix,
std::vector<const PassInfo*> &Kept);
std::vector<const PassInfo*> &Suffix);
};
ReduceMiscompilingPasses::TestResult
@ -52,7 +37,7 @@ ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
}
// Check to see if the finished program matches the reference output...
if (BD.diffProgram(Output, BytecodeResult, true /*delete bytecode*/)) {
if (BD.diffProgram(BytecodeResult, "", true /*delete bytecode*/)) {
std::cout << "nope.\n";
return KeepSuffix; // Miscompilation detected!
}
@ -78,7 +63,7 @@ ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
}
// If the prefix maintains the predicate by itself, only keep the prefix!
if (BD.diffProgram(Output, BytecodeResult)) {
if (BD.diffProgram(BytecodeResult)) {
std::cout << "nope.\n";
removeFile(BytecodeResult);
return KeepPrefix;
@ -109,7 +94,7 @@ ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
}
// Run the result...
if (BD.diffProgram(Output, BytecodeResult, true/*delete bytecode*/)) {
if (BD.diffProgram(BytecodeResult, "", true/*delete bytecode*/)) {
std::cout << "nope.\n";
delete OriginalInput; // We pruned down the original input...
return KeepSuffix;
@ -122,14 +107,6 @@ ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
return NoFailure;
}
static void PrintFunctionList(const std::vector<Function*> &Funcs) {
for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
if (i) std::cout << ", ";
std::cout << Funcs[i]->getName();
}
}
class ReduceMiscompilingFunctions : public ListReducer<Function*> {
BugDriver &BD;
public:
@ -154,7 +131,7 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
if (!EmitBytecode) {
std::cout << "Checking to see if the program is misoptimized when these "
<< "functions are run\nthrough the passes: ";
PrintFunctionList(Funcs);
BD.PrintFunctionList(Funcs);
std::cout << "\n";
} else {
std::cout <<"Outputting reduced bytecode files which expose the problem:\n";
@ -269,7 +246,7 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
// Eighth step: Execute the program. If it does not match the expected
// output, then 'Funcs' are being misoptimized!
bool Broken = BD.diffProgram(Output);
bool Broken = BD.diffProgram();
delete BD.Program; // Delete the hacked up program
BD.Program = OldProgram; // Restore the original
@ -284,22 +261,8 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
/// input.
///
bool BugDriver::debugMiscompilation() {
std::cout << "*** Debugging miscompilation!\n";
// Set up the execution environment, selecting a method to run LLVM bytecode.
if (initializeExecutionEnvironment()) return true;
// Run the raw input to see where we are coming from. If a reference output
// was specified, make sure that the raw output matches it. If not, it's a
// problem in the front-end or whatever produced the input code.
//
bool CreatedOutput = false;
if (Output.empty()) {
std::cout << "Generating reference output from raw program...";
Output = executeProgram("bugpoint.reference.out");
CreatedOutput = true;
std::cout << " done! Reference output is: " << Output << "\n";
} else if (diffProgram(Output)) {
if (diffProgram()) {
std::cout << "\n*** Input program does not match reference diff!\n"
<< " Must be problem with input source!\n";
return false; // Problem found
@ -321,7 +284,6 @@ bool BugDriver::debugMiscompilation() {
<< getPassesString(PassesToRun) << "\n";
EmitProgressBytecode("passinput");
// 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
@ -341,6 +303,5 @@ bool BugDriver::debugMiscompilation() {
// Output a bunch of bytecode files for the user...
ReduceMiscompilingFunctions(*this).TestFuncs(MiscompiledFunctions, true);
if (CreatedOutput) removeFile(Output);
return false;
}

View File

@ -18,7 +18,7 @@ InputFilenames(cl::Positional, cl::OneOrMore,
// PassNameParser.
//
static cl::list<const PassInfo*, bool, PassNameParser>
PassList(cl::desc("Passes available:"), cl::OneOrMore);
PassList(cl::desc("Passes available:"), cl::ZeroOrMore);
//cl::list<std::string>
//InputArgv(cl::ConsumeAfter, cl::desc("<program arguments>..."));
@ -26,7 +26,6 @@ PassList(cl::desc("Passes available:"), cl::OneOrMore);
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv);
BugDriver D(argv[0]);
if (D.addSources(InputFilenames)) return 1;
D.addPasses(PassList.begin(), PassList.end());