[llvm-commits] CVS: llvm/tools/bugpoint/CodeGeneratorBug.cpp BugDriver.cpp BugDriver.h ExecutionDriver.cpp Miscompilation.cpp bugpoint.cpp

Misha Brukman brukman at cs.uiuc.edu
Thu Jul 24 13:18:01 PDT 2003


Changes in directory llvm/tools/bugpoint:

CodeGeneratorBug.cpp added (r1.1)
BugDriver.cpp updated: 1.7 -> 1.8
BugDriver.h updated: 1.8 -> 1.9
ExecutionDriver.cpp updated: 1.6 -> 1.7
Miscompilation.cpp updated: 1.9 -> 1.10
bugpoint.cpp updated: 1.2 -> 1.3

---
Log message:

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.


---
Diffs of the changes:

Index: llvm/tools/bugpoint/CodeGeneratorBug.cpp
diff -c /dev/null llvm/tools/bugpoint/CodeGeneratorBug.cpp:1.1
*** /dev/null	Thu Jul 24 13:17:54 2003
--- llvm/tools/bugpoint/CodeGeneratorBug.cpp	Thu Jul 24 13:17:43 2003
***************
*** 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;
+ }
+ 


Index: llvm/tools/bugpoint/BugDriver.cpp
diff -u llvm/tools/bugpoint/BugDriver.cpp:1.7 llvm/tools/bugpoint/BugDriver.cpp:1.8
--- llvm/tools/bugpoint/BugDriver.cpp:1.7	Fri May 23 00:34:32 2003
+++ llvm/tools/bugpoint/BugDriver.cpp	Thu Jul 24 13:17:43 2003
@@ -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 @@
   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 @@
   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();
+  }
 }


Index: llvm/tools/bugpoint/BugDriver.h
diff -u llvm/tools/bugpoint/BugDriver.h:1.8 llvm/tools/bugpoint/BugDriver.h:1.9
--- llvm/tools/bugpoint/BugDriver.h:1.8	Thu Apr 24 18:51:38 2003
+++ llvm/tools/bugpoint/BugDriver.h	Thu Jul 24 13:17:43 2003
@@ -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 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 @@
   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 @@
   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 @@
     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 @@
   /// 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);
 };
 


Index: llvm/tools/bugpoint/ExecutionDriver.cpp
diff -u llvm/tools/bugpoint/ExecutionDriver.cpp:1.6 llvm/tools/bugpoint/ExecutionDriver.cpp:1.7
--- llvm/tools/bugpoint/ExecutionDriver.cpp:1.6	Fri May  2 22:20:36 2003
+++ llvm/tools/bugpoint/ExecutionDriver.cpp	Thu Jul 24 13:17:43 2003
@@ -11,7 +11,7 @@
 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 @@
   };
   cl::opt<OutputType>
   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<std::string>
   InputFile("input", cl::init("/dev/null"),
@@ -52,8 +52,8 @@
   /// 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,29 +73,240 @@
       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 << "<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
 //
 class JIT : public AbstractInterpreter {
@@ -111,25 +322,35 @@
       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 @@
 //
 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 @@
   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>";
+  return 0;
+}
 
-  // 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";      
-    }
 
-    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 @@
   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 @@
 /// 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 @@
 
     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);
   }
 


Index: llvm/tools/bugpoint/Miscompilation.cpp
diff -u llvm/tools/bugpoint/Miscompilation.cpp:1.9 llvm/tools/bugpoint/Miscompilation.cpp:1.10
--- llvm/tools/bugpoint/Miscompilation.cpp:1.9	Mon Jul 14 12:16:01 2003
+++ llvm/tools/bugpoint/Miscompilation.cpp	Thu Jul 24 13:17:43 2003
@@ -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 @@
   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 @@
   }
 
   // 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 @@
   }
 
   // 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 @@
   }
 
   // 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 @@
   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 @@
   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 @@
 
   // 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 @@
 /// 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 @@
             << 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 @@
   // Output a bunch of bytecode files for the user...
   ReduceMiscompilingFunctions(*this).TestFuncs(MiscompiledFunctions, true);
 
-  if (CreatedOutput) removeFile(Output);
   return false;
 }


Index: llvm/tools/bugpoint/bugpoint.cpp
diff -u llvm/tools/bugpoint/bugpoint.cpp:1.2 llvm/tools/bugpoint/bugpoint.cpp:1.3
--- llvm/tools/bugpoint/bugpoint.cpp:1.2	Fri May  2 22:18:41 2003
+++ llvm/tools/bugpoint/bugpoint.cpp	Thu Jul 24 13:17:43 2003
@@ -18,7 +18,7 @@
 // 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 @@
 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());





More information about the llvm-commits mailing list