diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index ed1a1351255..53a8395aaf1 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -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 +// 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 + OutputFile("output", cl::desc("Specify a reference program output " + "(for miscompilation detection)")); + + enum DebugType { DebugCompile, DebugCodegen }; + cl::opt + 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 &Funcs) +{ + for (unsigned i = 0, e = Funcs.size(); i != e; ++i) { + if (i) std::cout << ", "; + std::cout << Funcs[i]->getName(); + } } diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 05d4108824a..0c461d1e172 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -9,8 +9,10 @@ #ifndef BUGDRIVER_H #define BUGDRIVER_H +#include "Support/CommandLine.h" #include #include + class PassInfo; class Module; class Function; @@ -25,6 +27,7 @@ class ReduceCrashingBlocks; class BugDriver { const std::string ToolName; // Name of bugpoint + cl::opt ReferenceOutputFile; // Name of `good' output file Module *Program; // The raw program, linked together std::vector 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 &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); }; diff --git a/tools/bugpoint/CodeGeneratorBug.cpp b/tools/bugpoint/CodeGeneratorBug.cpp new file mode 100644 index 00000000000..772b4fdf754 --- /dev/null +++ b/tools/bugpoint/CodeGeneratorBug.cpp @@ -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 +#include + +// Passed as a command-line argument to Bugpoint +extern cl::opt Output; + +class ReduceMisCodegenFunctions : public ListReducer { + BugDriver &BD; +public: + ReduceMisCodegenFunctions(BugDriver &bd) : BD(bd) {} + + virtual TestResult doTest(std::vector &Prefix, + std::vector &Suffix) { + if (!Prefix.empty() && TestFuncs(Prefix)) + return KeepPrefix; + if (!Suffix.empty() && TestFuncs(Suffix)) + return KeepSuffix; + return NoFailure; + } + + bool TestFuncs(const std::vector &CodegenTest); + + void DisambiguateGlobalSymbols(Module *M); +}; + + +bool ReduceMisCodegenFunctions::TestFuncs(const std::vector &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*/ { + std::set SymbolNames; + std::set 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 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; +} + diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp index 014ffa2d606..307f408606b 100644 --- a/tools/bugpoint/ExecutionDriver.cpp +++ b/tools/bugpoint/ExecutionDriver.cpp @@ -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" @@ -29,11 +29,11 @@ namespace { }; cl::opt InterpreterSel(cl::desc("Specify how LLVM code should be executed:"), - cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"), - clEnumValN(RunJIT, "run-jit", "Execute with JIT"), - clEnumValN(RunLLC, "run-llc", "Compile with LLC"), - clEnumValN(RunCBE, "run-cbe", "Compile with CBE"), - 0)); + cl::values(clEnumValN(RunLLI, "run-lli", "Execute with LLI"), + clEnumValN(RunJIT, "run-jit", "Execute with JIT"), + clEnumValN(RunLLC, "run-llc", "Compile with LLC"), + clEnumValN(RunCBE, "run-cbe", "Compile with CBE"), + 0)); cl::opt InputFile("input", cl::init("/dev/null"), @@ -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,28 +73,239 @@ 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", Bytecode.c_str(), 0 }; - + return RunProgramWithTimeout(LLIPath, Args, - InputFile, OutputFile, OutputFile); + 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 << ""; + 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 << ""; + + // 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 << ""; + 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(ErrorFile), + std::istreambuf_iterator(), + std::ostreambuf_iterator(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 << ""; + 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,25 +322,35 @@ 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 char *Args[] = { - "-lli", - "-quiet", - "-force-interpreter=false", - Bytecode.c_str(), - 0 - }; - - return RunProgramWithTimeout(LLIPath, Args, - InputFile, OutputFile, OutputFile); + const std::string &OutputFile, + const std::string &SharedLib) { + if (SharedLib == "") { + const char* Args[] = { + 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); + } } //===----------------------------------------------------------------------===// @@ -137,37 +358,39 @@ int JIT::ExecuteProgram(const std::string &Bytecode, // 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; - } - - Message += "Found gcc: " + GCCPath + "\n"; - return new CBE(DISPath, GCCPath); + GCC *gcc = GCC::create(BD, Message); + return new CBE(DISPath, gcc); } + 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! + return 0; +} - std::cout << ""; - // 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(ErrorFile), - std::istreambuf_iterator(), - std::ostreambuf_iterator(std::cerr)); - ErrorFile.close(); - std::cerr << "\n"; - } - - 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 << ""; - - // 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... @@ -295,42 +477,85 @@ std::string BugDriver::executeProgram(std::string OutputFile, if (writeProgramToFile(BytecodeFile, Program)) { std::cerr << ToolName << ": Error emitting bytecode to file '" - << BytecodeFile << "'!\n"; + << BytecodeFile << "'!\n"; exit(1); } CreatedBytecode = true; } if (OutputFile.empty()) OutputFile = "bugpoint-execution-output"; - + // Check to see if this is a valid output filename... 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) { std::cerr << "Couldn't open reference output file '" - << ReferenceOutputFile << "'\n"; + << ReferenceOutputFile << "'\n"; exit(1); } diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 333eb13244e..833dd437b63 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/tools/bugpoint/Miscompilation.cpp @@ -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 - Output("output", cl::desc("Specify a reference program output " - "(for miscompilation detection)")); -} class ReduceMiscompilingPasses : public ListReducer { BugDriver &BD; @@ -33,7 +18,7 @@ public: ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {} virtual TestResult doTest(std::vector &Prefix, - std::vector &Kept); + std::vector &Suffix); }; ReduceMiscompilingPasses::TestResult @@ -52,7 +37,7 @@ ReduceMiscompilingPasses::doTest(std::vector &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 &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 &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 &Prefix, return NoFailure; } -static void PrintFunctionList(const std::vector &Funcs) { - for (unsigned i = 0, e = Funcs.size(); i != e; ++i) { - if (i) std::cout << ", "; - std::cout << Funcs[i]->getName(); - } -} - - class ReduceMiscompilingFunctions : public ListReducer { BugDriver &BD; public: @@ -154,7 +131,7 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector &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 &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,24 +261,10 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector &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"; + << " 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; } diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index e781aa8f3cd..dda8c5122b4 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -18,7 +18,7 @@ InputFilenames(cl::Positional, cl::OneOrMore, // PassNameParser. // static cl::list -PassList(cl::desc("Passes available:"), cl::OneOrMore); +PassList(cl::desc("Passes available:"), cl::ZeroOrMore); //cl::list //InputArgv(cl::ConsumeAfter, cl::desc("...")); @@ -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());