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

Misha Brukman brukman at cs.uiuc.edu
Mon Mar 1 19:26:07 PST 2004


Changes in directory llvm/tools/bugpoint:

BugDriver.cpp updated: 1.22 -> 1.22.2.1
BugDriver.h updated: 1.22 -> 1.22.4.1
CodeGeneratorBug.cpp updated: 1.34 -> 1.34.2.1
CrashDebugger.cpp updated: 1.27 -> 1.27.2.1
ExecutionDriver.cpp updated: 1.34 -> 1.34.2.1
ExtractFunction.cpp updated: 1.22 -> 1.22.4.1
Miscompilation.cpp updated: 1.26 -> 1.26.2.1
OptimizerDriver.cpp updated: 1.17 -> 1.17.2.1
bugpoint.cpp updated: 1.11 -> 1.11.4.1

---
Log message:

Merge from trunk

---
Diffs of the changes:  (+375 -202)

Index: llvm/tools/bugpoint/BugDriver.cpp
diff -u llvm/tools/bugpoint/BugDriver.cpp:1.22 llvm/tools/bugpoint/BugDriver.cpp:1.22.2.1
--- llvm/tools/bugpoint/BugDriver.cpp:1.22	Tue Jan 13 21:38:36 2004
+++ llvm/tools/bugpoint/BugDriver.cpp	Mon Mar  1 17:59:18 2004
@@ -19,6 +19,7 @@
 #include "llvm/Assembly/Parser.h"
 #include "llvm/Bytecode/Reader.h"
 #include "llvm/Transforms/Utils/Linker.h"
+#include "llvm/Support/ToolRunner.h"
 #include "Support/CommandLine.h"
 #include "Support/FileUtilities.h"
 #include <memory>
@@ -37,6 +38,15 @@
                                 "(for miscompilation detection)"));
 }
 
+/// setNewProgram - If we reduce or update the program somehow, call this method
+/// to update bugdriver with it.  This deletes the old module and sets the
+/// specified one as the current program.
+void BugDriver::setNewProgram(Module *M) {
+  delete Program;
+  Program = M;
+}
+
+
 /// getPassesString - Turn a list of passes into a string which indicates the
 /// command line options that must be passed to add the passes.
 ///
@@ -131,12 +141,23 @@
   if (!PassesToRun.empty()) {
     std::cout << "Running selected passes on program to test for crash: ";
     if (runPasses(PassesToRun))
-      return debugCrash();
+      return debugOptimizerCrash();
   }
 
   // Set up the execution environment, selecting a method to run LLVM bytecode.
   if (initializeExecutionEnvironment()) return true;
 
+  // Test to see if we have a code generator crash.
+  std::cout << "Running the code generator to test for a crash: ";
+  try {
+    compileProgram(Program);
+    std::cout << "\n";
+  } catch (ToolExecutionError &TEE) {
+    std::cout << TEE.what();
+    return debugCodeGeneratorCrash();
+  }
+
+
   // 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.
@@ -144,33 +165,47 @@
   bool CreatedOutput = false;
   if (ReferenceOutputFile.empty()) {
     std::cout << "Generating reference output from raw program...";
-    ReferenceOutputFile = executeProgramWithCBE("bugpoint.reference.out");
-    CreatedOutput = true;
-    std::cout << "Reference output is: " << ReferenceOutputFile << "\n";
+    try {
+      ReferenceOutputFile = executeProgramWithCBE("bugpoint.reference.out");
+      CreatedOutput = true;
+      std::cout << "Reference output is: " << ReferenceOutputFile << "\n";
+    } catch (ToolExecutionError &TEE) {
+      std::cerr << TEE.what();
+      if (Interpreter != cbe) {
+        std::cerr << "*** There is a bug running the C backend.  Either debug"
+                  << " it (use the -run-cbe bugpoint option), or fix the error"
+                  << " some other way.\n";
+        return 1;
+      }
+      return debugCodeGeneratorCrash();
+    }
   }
 
   // Make sure the reference output file gets deleted on exit from this
   // function, if appropriate.
-  struct Remover {
-    bool DeleteIt; const std::string &Filename;
-    Remover(bool deleteIt, const std::string &filename)
-      : DeleteIt(deleteIt), Filename(filename) {}
-    ~Remover() {
-      if (DeleteIt) removeFile(Filename);
-    }
-  } RemoverInstance(CreatedOutput, ReferenceOutputFile);
+  FileRemover RemoverInstance(ReferenceOutputFile, CreatedOutput);
 
   // Diff the output of the raw program against the reference output.  If it
   // matches, then we have a miscompilation bug.
   std::cout << "*** Checking the code generator...\n";
-  if (!diffProgram()) {
-    std::cout << "\n*** Debugging miscompilation!\n";
-    return debugMiscompilation();
+  try {
+    if (!diffProgram()) {
+      std::cout << "\n*** Debugging miscompilation!\n";
+      return debugMiscompilation();
+    }
+  } catch (ToolExecutionError &TEE) {
+    std::cerr << TEE.what();
+    return debugCodeGeneratorCrash();
   }
 
   std::cout << "\n*** Input program does not match reference diff!\n";
   std::cout << "Debugging code generator problem!\n";
-  return debugCodeGenerator();
+  try {
+    return debugCodeGenerator();
+  } catch (ToolExecutionError &TEE) {
+    std::cerr << TEE.what();
+    return debugCodeGeneratorCrash();
+  }
 }
 
 void BugDriver::PrintFunctionList(const std::vector<Function*> &Funcs) {


Index: llvm/tools/bugpoint/BugDriver.h
diff -u llvm/tools/bugpoint/BugDriver.h:1.22 llvm/tools/bugpoint/BugDriver.h:1.22.4.1
--- llvm/tools/bugpoint/BugDriver.h:1.22	Tue Nov 11 16:41:34 2003
+++ llvm/tools/bugpoint/BugDriver.h	Mon Mar  1 17:59:18 2004
@@ -30,8 +30,6 @@
 class DebugCrashes;
 class ReduceMiscompilingPasses;
 class ReduceMiscompilingFunctions;
-class ReduceCrashingFunctions;
-class ReduceCrashingBlocks;
 
 class CBE;
 class GCC;
@@ -48,12 +46,10 @@
   GCC *gcc;
 
   // FIXME: sort out public/private distinctions...
-  friend class DebugCrashes;
+  friend class ReducePassList;
   friend class ReduceMiscompilingPasses;
   friend class ReduceMiscompilingFunctions;
   friend class ReduceMisCodegenFunctions;
-  friend class ReduceCrashingFunctions;
-  friend class ReduceCrashingBlocks;
 
 public:
   BugDriver(const char *toolname);
@@ -75,11 +71,16 @@
   ///
   bool run();
 
-  /// debugCrash - This method is called when some pass crashes on input.  It
-  /// attempts to prune down the testcase to something reasonable, and figure
-  /// out exactly which pass is crashing.
+  /// debugOptimizerCrash - This method is called when some optimizer pass
+  /// crashes on input.  It attempts to prune down the testcase to something
+  /// reasonable, and figure out exactly which pass is crashing.
   ///
-  bool debugCrash();
+  bool debugOptimizerCrash();
+  
+  /// debugCodeGeneratorCrash - This method is called when the code generator
+  /// crashes on an input.  It attempts to reduce the input as much as possible
+  /// while still causing the code generator to crash.
+  bool debugCodeGeneratorCrash();
 
   /// debugMiscompilation - This method is used when the passes selected are not
   /// crashing, but the generated output is semantically different from the
@@ -109,6 +110,74 @@
   ///
   bool isExecutingJIT();
 
+  /// runPasses - Run all of the passes in the "PassesToRun" list, discard the
+  /// output, and return true if any of the passes crashed.
+  bool runPasses(Module *M = 0) {
+    if (M == 0) M = Program;
+    std::swap(M, Program);
+    bool Result = runPasses(PassesToRun);
+    std::swap(M, Program);
+    return Result;
+  }
+
+  const Module *getProgram() const { return Program; }
+
+  /// setNewProgram - If we reduce or update the program somehow, call this
+  /// method to update bugdriver with it.  This deletes the old module and sets
+  /// the specified one as the current program.
+  void setNewProgram(Module *M);
+
+  /// compileProgram - Try to compile the specified module, throwing an
+  /// exception if an error occurs, or returning normally if not.  This is used
+  /// for code generation crash testing.
+  ///
+  void compileProgram(Module *M);
+
+  /// executeProgram - This method runs "Program", capturing the output of the
+  /// program to a file, returning the filename of the file.  A recommended
+  /// filename may be optionally specified.  If there is a problem with the code
+  /// generator (e.g., llc crashes), this will throw an exception.
+  ///
+  std::string executeProgram(std::string RequestedOutputFilename = "",
+                             std::string Bytecode = "",
+                             const std::string &SharedObjects = "",
+                             AbstractInterpreter *AI = 0,
+                             bool *ProgramExitedNonzero = 0);
+
+  /// executeProgramWithCBE - Used to create reference output with the C
+  /// backend, if reference output is not provided.  If there is a problem with
+  /// the code generator (e.g., llc crashes), this will throw an exception.
+  ///
+  std::string executeProgramWithCBE(std::string OutputFile = "");
+
+  /// 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.  If there is a problem with the code
+  /// generator (e.g., llc crashes), this will throw an exception.
+  ///
+  bool diffProgram(const std::string &BytecodeFile = "",
+                   const std::string &SharedObj = "",
+                   bool RemoveBytecode = false);
+  /// EmitProgressBytecode - This function is used to output the current Program
+  /// to a file named "bugpoint-ID.bc".
+  ///
+  void EmitProgressBytecode(const std::string &ID, bool NoFlyer = false);
+
+  /// 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
+  /// which depends on the value.  The modified module is then returned.
+  ///
+  Module *deleteInstructionFromProgram(const Instruction *I, unsigned Simp)
+    const;
+
+  /// performFinalCleanups - This method clones the current Program and performs
+  /// a series of cleanups intended to get rid of extra cruft on the module.  If
+  /// the MayModifySemantics argument is true, then the cleanups is allowed to
+  /// modify how the code behaves.
+  ///
+  Module *performFinalCleanups(Module *M, bool MayModifySemantics = false);
+
 private:
   /// ParseInputFile - Given a bytecode or assembly input filename, parse and
   /// return it, or return null if not possible.
@@ -120,12 +189,6 @@
   ///
   bool writeProgramToFile(const std::string &Filename, Module *M = 0) const;
 
-
-  /// EmitProgressBytecode - This function is used to output the current Program
-  /// to a file named "bugpoint-ID.bc".
-  ///
-  void EmitProgressBytecode(const std::string &ID, bool NoFlyer = false);
-  
   /// runPasses - Run the specified passes on Program, outputting a bytecode
   /// file and writting the filename into OutputFile if successful.  If the
   /// optimizations fail for some reason (optimizer crashes), return true,
@@ -152,51 +215,10 @@
   ///
   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
-  /// which depends on the value.  The modified module is then returned.
-  ///
-  Module *deleteInstructionFromProgram(Instruction *I, unsigned Simp) const;
-
-  /// performFinalCleanups - This method clones the current Program and performs
-  /// a series of cleanups intended to get rid of extra cruft on the module.  If
-  /// the MayModifySemantics argument is true, then the cleanups is allowed to
-  /// modify how the code behaves.
-  ///
-  Module *performFinalCleanups(Module *M, bool MayModifySemantics = false);
-
   /// initializeExecutionEnvironment - This method is used to set up the
   /// environment for executing LLVM programs.
   ///
   bool initializeExecutionEnvironment();
-
-  /// executeProgram - This method runs "Program", capturing the output of the
-  /// program to a file, returning the filename of the file.  A recommended
-  /// filename may be optionally specified.
-  ///
-  std::string executeProgram(std::string RequestedOutputFilename = "",
-                             std::string Bytecode = "",
-                             const std::string &SharedObjects = "",
-                             AbstractInterpreter *AI = 0);
-
-  /// executeProgramWithCBE - Used to create reference output with the C
-  /// backend, if reference output is not provided.
-  ///
-  std::string executeProgramWithCBE(std::string OutputFile = "",
-                                    std::string BytecodeFile = "",
-                                    const std::string &SharedObj = "") {
-    return executeProgram(OutputFile, BytecodeFile, SharedObj,
-                          (AbstractInterpreter*)cbe);
-  }
-
-  /// 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 &BytecodeFile = "",
-                   const std::string &SharedObj = "",
-                   bool RemoveBytecode = false);
 };
 
 /// getPassesString - Turn a list of passes into a string which indicates the


Index: llvm/tools/bugpoint/CodeGeneratorBug.cpp
diff -u llvm/tools/bugpoint/CodeGeneratorBug.cpp:1.34 llvm/tools/bugpoint/CodeGeneratorBug.cpp:1.34.2.1
--- llvm/tools/bugpoint/CodeGeneratorBug.cpp:1.34	Tue Jan 13 21:38:36 2004
+++ llvm/tools/bugpoint/CodeGeneratorBug.cpp	Mon Mar  1 17:59:18 2004
@@ -262,7 +262,7 @@
     for (unsigned i=0, e = InputArgv.size(); i != e; ++i)
       std::cout << " " << InputArgv[i];
     std::cout << "\n";
-    std::cout << "The shared object was created with:\n  llvm-dis -c "
+    std::cout << "The shared object was created with:\n  llc -march=c "
               << SafeModuleBC << " -o temporary.c\n"
               << "  gcc -xc temporary.c -O2 -o " << SharedObject
 #if defined(sparc) || defined(__sparc__) || defined(__sparcv9)
@@ -348,8 +348,19 @@
 
 
 bool BugDriver::debugCodeGenerator() {
+  if ((void*)cbe == (void*)Interpreter) {
+    std::string Result = executeProgramWithCBE("bugpoint.cbe.out");
+    std::cout << "\n*** The C backend cannot match the reference diff, but it "
+              << "is used as the 'known good'\n    code generator, so I can't"
+              << " debug it.  Perhaps you have a front-end problem?\n    As a"
+              << " sanity check, I left the result of executing the program "
+              << "with the C backend\n    in this file for you: '"
+              << Result << "'.\n";
+    return true;
+  }
+
   // 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.
+  // 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())


Index: llvm/tools/bugpoint/CrashDebugger.cpp
diff -u llvm/tools/bugpoint/CrashDebugger.cpp:1.27 llvm/tools/bugpoint/CrashDebugger.cpp:1.27.2.1
--- llvm/tools/bugpoint/CrashDebugger.cpp:1.27	Tue Jan 13 21:38:37 2004
+++ llvm/tools/bugpoint/CrashDebugger.cpp	Mon Mar  1 17:59:18 2004
@@ -23,6 +23,7 @@
 #include "llvm/Analysis/Verifier.h"
 #include "llvm/Bytecode/Writer.h"
 #include "llvm/Support/CFG.h"
+#include "llvm/Support/ToolRunner.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Utils/Cloning.h"
 #include "Support/FileUtilities.h"
@@ -31,10 +32,10 @@
 using namespace llvm;
 
 namespace llvm {
-  class DebugCrashes : public ListReducer<const PassInfo*> {
+  class ReducePassList : public ListReducer<const PassInfo*> {
     BugDriver &BD;
   public:
-    DebugCrashes(BugDriver &bd) : BD(bd) {}
+    ReducePassList(BugDriver &bd) : BD(bd) {}
     
     // doTest - Return true iff running the "removed" passes succeeds, and
     // running the "Kept" passes fail when run on the output of the "removed"
@@ -45,9 +46,9 @@
   };
 }
 
-DebugCrashes::TestResult
-DebugCrashes::doTest(std::vector<const PassInfo*> &Prefix,
-                     std::vector<const PassInfo*> &Suffix) {
+ReducePassList::TestResult
+ReducePassList::doTest(std::vector<const PassInfo*> &Prefix,
+                       std::vector<const PassInfo*> &Suffix) {
   std::string PrefixOutput;
   Module *OrigProgram = 0;
   if (!Prefix.empty()) {
@@ -84,13 +85,16 @@
 }
 
 namespace llvm {
-  class ReduceCrashingFunctions : public ListReducer<Function*> {
+  class ReduceCrashingFunctions : public ListReducer<const Function*> {
     BugDriver &BD;
+    bool (*TestFn)(BugDriver &, Module *);
   public:
-    ReduceCrashingFunctions(BugDriver &bd) : BD(bd) {}
+    ReduceCrashingFunctions(BugDriver &bd,
+                            bool (*testFn)(BugDriver &, Module *))
+      : BD(bd), TestFn(testFn) {}
     
-    virtual TestResult doTest(std::vector<Function*> &Prefix,
-                              std::vector<Function*> &Kept) {
+    virtual TestResult doTest(std::vector<const Function*> &Prefix,
+                              std::vector<const Function*> &Kept) {
       if (!Kept.empty() && TestFuncs(Kept))
         return KeepSuffix;
       if (!Prefix.empty() && TestFuncs(Prefix))
@@ -98,13 +102,13 @@
       return NoFailure;
     }
     
-    bool TestFuncs(std::vector<Function*> &Prefix);
+    bool TestFuncs(std::vector<const Function*> &Prefix);
   };
 }
 
-bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) {
+bool ReduceCrashingFunctions::TestFuncs(std::vector<const Function*> &Funcs) {
   // Clone the program to try hacking it apart...
-  Module *M = CloneModule(BD.Program);
+  Module *M = CloneModule(BD.getProgram());
   
   // Convert list to set for fast lookup...
   std::set<Function*> Functions;
@@ -131,34 +135,34 @@
       DeleteFunctionBody(I);
 
   // Try running the hacked up program...
-  std::swap(BD.Program, M);
-  if (BD.runPasses(BD.PassesToRun)) {
-    delete M;         // It crashed, keep the trimmed version...
+  if (TestFn(BD, M)) {
+    BD.setNewProgram(M);        // It crashed, keep the trimmed version...
 
     // Make sure to use function pointers that point into the now-current
     // module.
     Funcs.assign(Functions.begin(), Functions.end());
     return true;
   }
-  delete BD.Program;  // It didn't crash, revert...
-  BD.Program = M;
+  delete M;
   return false;
 }
 
 
-namespace llvm {
+namespace {
   /// ReduceCrashingBlocks reducer - This works by setting the terminators of
   /// all terminators except the specified basic blocks to a 'ret' instruction,
   /// then running the simplify-cfg pass.  This has the effect of chopping up
   /// the CFG really fast which can reduce large functions quickly.
   ///
-  class ReduceCrashingBlocks : public ListReducer<BasicBlock*> {
+  class ReduceCrashingBlocks : public ListReducer<const BasicBlock*> {
     BugDriver &BD;
+    bool (*TestFn)(BugDriver &, Module *);
   public:
-    ReduceCrashingBlocks(BugDriver &bd) : BD(bd) {}
+    ReduceCrashingBlocks(BugDriver &bd, bool (*testFn)(BugDriver &, Module *))
+      : BD(bd), TestFn(testFn) {}
     
-    virtual TestResult doTest(std::vector<BasicBlock*> &Prefix,
-                              std::vector<BasicBlock*> &Kept) {
+    virtual TestResult doTest(std::vector<const BasicBlock*> &Prefix,
+                              std::vector<const BasicBlock*> &Kept) {
       if (!Kept.empty() && TestBlocks(Kept))
         return KeepSuffix;
       if (!Prefix.empty() && TestBlocks(Prefix))
@@ -166,25 +170,26 @@
       return NoFailure;
     }
     
-    bool TestBlocks(std::vector<BasicBlock*> &Prefix);
+    bool TestBlocks(std::vector<const BasicBlock*> &Prefix);
   };
 }
 
-bool ReduceCrashingBlocks::TestBlocks(std::vector<BasicBlock*> &BBs) {
+bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
   // Clone the program to try hacking it apart...
-  Module *M = CloneModule(BD.Program);
+  Module *M = CloneModule(BD.getProgram());
   
   // Convert list to set for fast lookup...
   std::set<BasicBlock*> Blocks;
   for (unsigned i = 0, e = BBs.size(); i != e; ++i) {
     // Convert the basic block from the original module to the new module...
-    Function *F = BBs[i]->getParent();
+    const Function *F = BBs[i]->getParent();
     Function *CMF = M->getFunction(F->getName(), F->getFunctionType());
     assert(CMF && "Function not in module?!");
 
     // Get the mapped basic block...
     Function::iterator CBI = CMF->begin();
-    std::advance(CBI, std::distance(F->begin(), Function::iterator(BBs[i])));
+    std::advance(CBI, std::distance(F->begin(),
+                                    Function::const_iterator(BBs[i])));
     Blocks.insert(CBI);
   }
 
@@ -237,9 +242,8 @@
   Passes.run(*M);
 
   // Try running on the hacked up program...
-  std::swap(BD.Program, M);
-  if (BD.runPasses(BD.PassesToRun)) {
-    delete M;         // It crashed, keep the trimmed version...
+  if (TestFn(BD, M)) {
+    BD.setNewProgram(M);      // It crashed, keep the trimmed version...
 
     // Make sure to use basic block pointers that point into the now-current
     // module, and that they don't include any deleted blocks.
@@ -252,33 +256,20 @@
     }
     return true;
   }
-  delete BD.Program;  // It didn't crash, revert...
-  BD.Program = M;
+  delete M;  // It didn't crash, try something else.
   return false;
 }
 
-/// debugCrash - This method is called when some pass crashes on input.  It
-/// attempts to prune down the testcase to something reasonable, and figure
-/// out exactly which pass is crashing.
-///
-bool BugDriver::debugCrash() {
+/// DebugACrash - Given a predicate that determines whether a component crashes
+/// on a program, try to destructively reduce the program while still keeping
+/// the predicate true.
+static bool DebugACrash(BugDriver &BD,  bool (*TestFn)(BugDriver &, Module *)) {
   bool AnyReduction = false;
-  std::cout << "\n*** Debugging optimizer crash!\n";
-
-  // Reduce the list of passes which causes the optimizer to crash...
-  unsigned OldSize = PassesToRun.size();
-  DebugCrashes(*this).reduceList(PassesToRun);
-
-  std::cout << "\n*** Found crashing pass"
-            << (PassesToRun.size() == 1 ? ": " : "es: ")
-            << getPassesString(PassesToRun) << "\n";
-
-  EmitProgressBytecode("passinput");
 
   // See if we can get away with nuking all of the global variable initializers
   // in the program...
-  if (Program->gbegin() != Program->gend()) {
-    Module *M = CloneModule(Program);
+  if (BD.getProgram()->gbegin() != BD.getProgram()->gend()) {
+    Module *M = CloneModule(BD.getProgram());
     bool DeletedInit = false;
     for (Module::giterator I = M->gbegin(), E = M->gend(); I != E; ++I)
       if (I->hasInitializer()) {
@@ -292,22 +283,21 @@
     } else {
       // See if the program still causes a crash...
       std::cout << "\nChecking to see if we can delete global inits: ";
-      std::swap(Program, M);
-      if (runPasses(PassesToRun)) {  // Still crashes?
+      if (TestFn(BD, M)) {  // Still crashes?
+        BD.setNewProgram(M);
         AnyReduction = true;
-        delete M;
         std::cout << "\n*** Able to remove all global initializers!\n";
       } else {                       // No longer crashes?
-        delete Program;              // Restore program.
-        Program = M;
         std::cout << "  - Removing all global inits hides problem!\n";
+        delete M;
       }
     }
   }
   
   // Now try to reduce the number of functions in the module to something small.
-  std::vector<Function*> Functions;
-  for (Module::iterator I = Program->begin(), E = Program->end(); I != E; ++I)
+  std::vector<const Function*> Functions;
+  for (Module::const_iterator I = BD.getProgram()->begin(),
+         E = BD.getProgram()->end(); I != E; ++I)
     if (!I->isExternal())
       Functions.push_back(I);
 
@@ -315,11 +305,11 @@
     std::cout << "\n*** Attempting to reduce the number of functions "
       "in the testcase\n";
 
-    OldSize = Functions.size();
-    ReduceCrashingFunctions(*this).reduceList(Functions);
+    unsigned OldSize = Functions.size();
+    ReduceCrashingFunctions(BD, TestFn).reduceList(Functions);
 
     if (Functions.size() < OldSize) {
-      EmitProgressBytecode("reduced-function");
+      BD.EmitProgressBytecode("reduced-function");
       AnyReduction = true;
     }
   }
@@ -330,11 +320,12 @@
   // shrinks the code dramatically quickly
   //
   if (!DisableSimplifyCFG) {
-    std::vector<BasicBlock*> Blocks;
-    for (Module::iterator I = Program->begin(), E = Program->end(); I != E; ++I)
-      for (Function::iterator FI = I->begin(), E = I->end(); FI != E; ++FI)
+    std::vector<const BasicBlock*> Blocks;
+    for (Module::const_iterator I = BD.getProgram()->begin(),
+           E = BD.getProgram()->end(); I != E; ++I)
+      for (Function::const_iterator FI = I->begin(), E = I->end(); FI !=E; ++FI)
         Blocks.push_back(FI);
-    ReduceCrashingBlocks(*this).reduceList(Blocks);
+    ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks);
   }
 
   // FIXME: This should use the list reducer to converge faster by deleting
@@ -353,58 +344,106 @@
     // still triggers failure, keep deleting until we cannot trigger failure
     // anymore.
     //
+    unsigned InstructionsToSkipBeforeDeleting = 0;
   TryAgain:
     
     // Loop over all of the (non-terminator) instructions remaining in the
     // function, attempting to delete them.
-    for (Module::iterator FI = Program->begin(), E = Program->end();
-         FI != E; ++FI)
-      if (!FI->isExternal()) {
-        for (Function::iterator BI = FI->begin(), E = FI->end(); BI != E; ++BI)
-          for (BasicBlock::iterator I = BI->begin(), E = --BI->end();
-               I != E; ++I) {
-            Module *M = deleteInstructionFromProgram(I, Simplification);
-            
-            // Make the function the current program...
-            std::swap(Program, M);
-            
-            // Find out if the pass still crashes on this pass...
-            std::cout << "Checking instruction '" << I->getName() << "': ";
-            if (runPasses(PassesToRun)) {
-              // Yup, it does, we delete the old module, and continue trying to
-              // reduce the testcase...
+    unsigned CurInstructionNum = 0;
+    for (Module::const_iterator FI = BD.getProgram()->begin(),
+           E = BD.getProgram()->end(); FI != E; ++FI)
+      if (!FI->isExternal())
+        for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E;
+             ++BI)
+          for (BasicBlock::const_iterator I = BI->begin(), E = --BI->end();
+               I != E; ++I, ++CurInstructionNum)
+            if (InstructionsToSkipBeforeDeleting) {
+              --InstructionsToSkipBeforeDeleting;
+            } else {
+              std::cout << "Checking instruction '" << I->getName() << "': ";
+              Module *M = BD.deleteInstructionFromProgram(I, Simplification);
+              
+              // Find out if the pass still crashes on this pass...
+              if (TestFn(BD, M)) {
+                // Yup, it does, we delete the old module, and continue trying
+                // to reduce the testcase...
+                BD.setNewProgram(M);
+                AnyReduction = true;
+                InstructionsToSkipBeforeDeleting = CurInstructionNum;
+                goto TryAgain;  // I wish I had a multi-level break here!
+              }
+              
+              // This pass didn't crash without this instruction, try the next
+              // one.
               delete M;
-              AnyReduction = true;
-              goto TryAgain;  // I wish I had a multi-level break here!
             }
-            
-            // This pass didn't crash without this instruction, try the next
-            // one.
-            delete Program;
-            Program = M;
-          }
-      }
+
+    if (InstructionsToSkipBeforeDeleting) {
+      InstructionsToSkipBeforeDeleting = 0;
+      goto TryAgain;
+    }
+      
   } while (Simplification);
 
   // Try to clean up the testcase by running funcresolve and globaldce...
   std::cout << "\n*** Attempting to perform final cleanups: ";
-  Module *M = CloneModule(Program);
-  M = performFinalCleanups(M, true);
-  std::swap(Program, M);
+  Module *M = CloneModule(BD.getProgram());
+  M = BD.performFinalCleanups(M, true);
             
   // Find out if the pass still crashes on the cleaned up program...
-  if (runPasses(PassesToRun)) {
-    // Yup, it does, keep the reduced version...
-    delete M;
+  if (TestFn(BD, M)) {
+    BD.setNewProgram(M);     // Yup, it does, keep the reduced version...
     AnyReduction = true;
   } else {
-    delete Program;   // Otherwise, restore the original module...
-    Program = M;
+    delete M;
   }
 
   if (AnyReduction)
-    EmitProgressBytecode("reduced-simplified");
+    BD.EmitProgressBytecode("reduced-simplified");
 
-  return false;
+  return false;  
+}
+
+static bool TestForOptimizerCrash(BugDriver &BD, Module *M) {
+  return BD.runPasses(M);
 }
 
+/// debugOptimizerCrash - This method is called when some pass crashes on input.
+/// It attempts to prune down the testcase to something reasonable, and figure
+/// out exactly which pass is crashing.
+///
+bool BugDriver::debugOptimizerCrash() {
+  std::cout << "\n*** Debugging optimizer crash!\n";
+
+  // Reduce the list of passes which causes the optimizer to crash...
+  unsigned OldSize = PassesToRun.size();
+  ReducePassList(*this).reduceList(PassesToRun);
+
+  std::cout << "\n*** Found crashing pass"
+            << (PassesToRun.size() == 1 ? ": " : "es: ")
+            << getPassesString(PassesToRun) << "\n";
+
+  EmitProgressBytecode("passinput");
+
+  return DebugACrash(*this, TestForOptimizerCrash);
+}
+
+static bool TestForCodeGenCrash(BugDriver &BD, Module *M) {
+  try {
+    std::cerr << "\n";
+    BD.compileProgram(M);
+    return false;
+  } catch (ToolExecutionError &TEE) {
+    std::cerr << "<crash>\n";
+    return true;  // Tool is still crashing.
+  }
+}
+
+/// debugCodeGeneratorCrash - This method is called when the code generator
+/// crashes on an input.  It attempts to reduce the input as much as possible
+/// while still causing the code generator to crash.
+bool BugDriver::debugCodeGeneratorCrash() {
+  std::cerr << "*** Debugging code generator crash!\n";
+
+  return DebugACrash(*this, TestForCodeGenCrash);
+}


Index: llvm/tools/bugpoint/ExecutionDriver.cpp
diff -u llvm/tools/bugpoint/ExecutionDriver.cpp:1.34 llvm/tools/bugpoint/ExecutionDriver.cpp:1.34.2.1
--- llvm/tools/bugpoint/ExecutionDriver.cpp:1.34	Tue Jan 13 21:38:37 2004
+++ llvm/tools/bugpoint/ExecutionDriver.cpp	Mon Mar  1 17:59:18 2004
@@ -49,6 +49,11 @@
                             0),
                  cl::init(AutoPick));
 
+  cl::opt<bool>
+  CheckProgramExitCode("check-exit-code",
+                       cl::desc("Assume nonzero exit code is failure (default on)"),
+                       cl::init(true));
+
   cl::opt<std::string>
   InputFile("input", cl::init("/dev/null"),
             cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
@@ -79,11 +84,12 @@
 
   // Create an instance of the AbstractInterpreter interface as specified on
   // the command line
+  cbe = 0;
   std::string Message;
   switch (InterpreterSel) {
   case AutoPick:
     InterpreterSel = RunCBE;
-    Interpreter = AbstractInterpreter::createCBE(getToolName(), Message);
+    Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message);
     if (!Interpreter) {
       InterpreterSel = RunJIT;
       Interpreter = AbstractInterpreter::createJIT(getToolName(), Message);
@@ -111,7 +117,7 @@
     Interpreter = AbstractInterpreter::createJIT(getToolName(), Message);
     break;
   case RunCBE:
-    Interpreter = AbstractInterpreter::createCBE(getToolName(), Message);
+    Interpreter = cbe = AbstractInterpreter::createCBE(getToolName(), Message);
     break;
   default:
     Message = "Sorry, this back-end is not supported by bugpoint right now!\n";
@@ -120,8 +126,10 @@
   std::cerr << Message;
 
   // Initialize auxiliary tools for debugging
-  cbe = AbstractInterpreter::createCBE(getToolName(), Message);
-  if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); }
+  if (!cbe) {
+    cbe = AbstractInterpreter::createCBE(getToolName(), Message);
+    if (!cbe) { std::cout << Message << "\nExiting.\n"; exit(1); }
+  }
   gcc = GCC::create(getToolName(), Message);
   if (!gcc) { std::cout << Message << "\nExiting.\n"; exit(1); }
 
@@ -129,6 +137,26 @@
   return Interpreter == 0;
 }
 
+/// compileProgram - Try to compile the specified module, throwing an exception
+/// if an error occurs, or returning normally if not.  This is used for code
+/// generation crash testing.
+///
+void BugDriver::compileProgram(Module *M) {
+  // Emit the program to a bytecode file...
+  std::string BytecodeFile = getUniqueFilename("bugpoint-test-program.bc");
+  if (writeProgramToFile(BytecodeFile, M)) {
+    std::cerr << ToolName << ": Error emitting bytecode to file '"
+              << BytecodeFile << "'!\n";
+    exit(1);
+  }
+
+    // Remove the temporary bytecode file when we are done.
+  FileRemover BytecodeFileRemover(BytecodeFile);
+
+  // Actually compile the program!
+  Interpreter->compileProgram(BytecodeFile);
+}
+
 
 /// executeProgram - This method runs "Program", capturing the output of the
 /// program to a file, returning the filename of the file.  A recommended
@@ -137,7 +165,8 @@
 std::string BugDriver::executeProgram(std::string OutputFile,
                                       std::string BytecodeFile,
                                       const std::string &SharedObj,
-                                      AbstractInterpreter *AI) {
+                                      AbstractInterpreter *AI,
+                                      bool *ProgramExitedNonzero) {
   if (AI == 0) AI = Interpreter;
   assert(AI && "Interpreter should have been created already!");
   bool CreatedBytecode = false;
@@ -153,6 +182,9 @@
     CreatedBytecode = true;
   }
 
+  // Remove the temporary bytecode file when we are done.
+  FileRemover BytecodeFileRemover(BytecodeFile, CreatedBytecode);
+
   if (OutputFile.empty()) OutputFile = "bugpoint-execution-output";
 
   // Check to see if this is a valid output filename...
@@ -167,14 +199,29 @@
   int RetVal = AI->ExecuteProgram(BytecodeFile, InputArgv, InputFile,
                                   OutputFile, SharedObjs);
 
-
-  // Remove the temporary bytecode file.
-  if (CreatedBytecode) removeFile(BytecodeFile);
+  if (ProgramExitedNonzero != 0)
+    *ProgramExitedNonzero = (RetVal != 0);
 
   // Return the filename we captured the output to.
   return OutputFile;
 }
 
+/// executeProgramWithCBE - Used to create reference output with the C
+/// backend, if reference output is not provided.
+///
+std::string BugDriver::executeProgramWithCBE(std::string OutputFile) {
+  bool ProgramExitedNonzero;
+  std::string outFN = executeProgram(OutputFile, "", "",
+                                     (AbstractInterpreter*)cbe,
+                                     &ProgramExitedNonzero);
+  if (ProgramExitedNonzero) {
+    std::cerr
+      << "Warning: While generating reference output, program exited with\n"
+      << "non-zero exit code. This will NOT be treated as a failure.\n";
+    CheckProgramExitCode = false;
+  }
+  return outFN;
+}
 
 std::string BugDriver::compileSharedObject(const std::string &BytecodeFile) {
   assert(Interpreter && "Interpreter should have been created already!");
@@ -211,8 +258,15 @@
 bool BugDriver::diffProgram(const std::string &BytecodeFile,
                             const std::string &SharedObject,
                             bool RemoveBytecode) {
+  bool ProgramExitedNonzero;
+
   // Execute the program, generating an output file...
-  std::string Output = executeProgram("", BytecodeFile, SharedObject);
+  std::string Output = executeProgram("", BytecodeFile, SharedObject, 0,
+                                      &ProgramExitedNonzero);
+
+  // If we're checking the program exit code, assume anything nonzero is bad.
+  if (CheckProgramExitCode && ProgramExitedNonzero)
+    return true;
 
   std::string Error;
   bool FilesDifferent = false;


Index: llvm/tools/bugpoint/ExtractFunction.cpp
diff -u llvm/tools/bugpoint/ExtractFunction.cpp:1.22 llvm/tools/bugpoint/ExtractFunction.cpp:1.22.4.1
--- llvm/tools/bugpoint/ExtractFunction.cpp:1.22	Sat Dec  6 20:31:03 2003
+++ llvm/tools/bugpoint/ExtractFunction.cpp	Mon Mar  1 17:59:18 2004
@@ -48,29 +48,30 @@
 /// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code which
 /// depends on the value.  The modified module is then returned.
 ///
-Module *BugDriver::deleteInstructionFromProgram(Instruction *I,
+Module *BugDriver::deleteInstructionFromProgram(const Instruction *I,
                                                 unsigned Simplification) const {
   Module *Result = CloneModule(Program);
 
-  BasicBlock *PBB = I->getParent();
-  Function *PF = PBB->getParent();
+  const BasicBlock *PBB = I->getParent();
+  const Function *PF = PBB->getParent();
 
   Module::iterator RFI = Result->begin(); // Get iterator to corresponding fn
-  std::advance(RFI, std::distance(Program->begin(), Module::iterator(PF)));
+  std::advance(RFI, std::distance(PF->getParent()->begin(),
+                                  Module::const_iterator(PF)));
 
   Function::iterator RBI = RFI->begin();  // Get iterator to corresponding BB
-  std::advance(RBI, std::distance(PF->begin(), Function::iterator(PBB)));
+  std::advance(RBI, std::distance(PF->begin(), Function::const_iterator(PBB)));
 
   BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst
-  std::advance(RI, std::distance(PBB->begin(), BasicBlock::iterator(I)));
-  I = RI;                                 // Got the corresponding instruction!
+  std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I)));
+  Instruction *TheInst = RI;              // Got the corresponding instruction!
 
   // If this instruction produces a value, replace any users with null values
-  if (I->getType() != Type::VoidTy)
-    I->replaceAllUsesWith(Constant::getNullValue(I->getType()));
+  if (TheInst->getType() != Type::VoidTy)
+    TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType()));
 
   // Remove the instruction from the program.
-  I->getParent()->getInstList().erase(I);
+  TheInst->getParent()->getInstList().erase(TheInst);
 
   // Spiff up the output a little bit.
   PassManager Passes;


Index: llvm/tools/bugpoint/Miscompilation.cpp
diff -u llvm/tools/bugpoint/Miscompilation.cpp:1.26 llvm/tools/bugpoint/Miscompilation.cpp:1.26.2.1
--- llvm/tools/bugpoint/Miscompilation.cpp:1.26	Tue Jan 13 21:38:37 2004
+++ llvm/tools/bugpoint/Miscompilation.cpp	Mon Mar  1 17:59:18 2004
@@ -46,7 +46,7 @@
               << " on the input program!\n";
     BD.setPassesToRun(Suffix);
     BD.EmitProgressBytecode("pass-error",  false);
-    exit(BD.debugCrash());
+    exit(BD.debugOptimizerCrash());
   }
 
   // Check to see if the finished program matches the reference output...
@@ -74,7 +74,7 @@
               << " on the input program!\n";
     BD.setPassesToRun(Prefix);
     BD.EmitProgressBytecode("pass-error",  false);
-    exit(BD.debugCrash());
+    exit(BD.debugOptimizerCrash());
   }
 
   // If the prefix maintains the predicate by itself, only keep the prefix!
@@ -107,7 +107,7 @@
               << " on the input program!\n";
     BD.setPassesToRun(Suffix);
     BD.EmitProgressBytecode("pass-error",  false);
-    exit(BD.debugCrash());
+    exit(BD.debugOptimizerCrash());
   }
 
   // Run the result...
@@ -159,7 +159,7 @@
   }
 
   // First step: clone the module for the two halves of the program we want.
-  Module *ToOptimize = CloneModule(BD.Program);
+  Module *ToOptimize = CloneModule(BD.getProgram());
 
   // Second step: Make sure functions & globals are all external so that linkage
   // between the two modules will work.
@@ -225,13 +225,13 @@
     std::cerr << " Error running this sequence of passes" 
               << " on the input program!\n";
     BD.EmitProgressBytecode("pass-error",  false);
-    exit(BD.debugCrash());
+    exit(BD.debugOptimizerCrash());
   }
 
   if (!EmitBytecode)
     std::cout << "done.\n";
 
-  delete BD.Program;   // Delete the old "ToOptimize" module
+  delete BD.getProgram();   // Delete the old "ToOptimize" module
   BD.Program = BD.ParseInputFile(BytecodeResult);
 
   if (EmitBytecode) {
@@ -270,7 +270,7 @@
   // output, then 'Funcs' are being misoptimized!
   bool Broken = BD.diffProgram();
 
-  delete BD.Program;  // Delete the hacked up program
+  delete BD.Program;         // Delete the hacked up program
   BD.Program = OldProgram;   // Restore the original
 
   std::cout << (Broken ? " nope.\n" : " yup.\n");


Index: llvm/tools/bugpoint/OptimizerDriver.cpp
diff -u llvm/tools/bugpoint/OptimizerDriver.cpp:1.17 llvm/tools/bugpoint/OptimizerDriver.cpp:1.17.2.1
--- llvm/tools/bugpoint/OptimizerDriver.cpp:1.17	Tue Jan 13 21:38:37 2004
+++ llvm/tools/bugpoint/OptimizerDriver.cpp	Mon Mar  1 17:59:18 2004
@@ -53,7 +53,7 @@
   }
 
   std::cout << "Emitted bytecode to '" << Filename << "'\n";
-  if (NoFlyer) return;
+  if (NoFlyer || PassesToRun.empty()) return;
   std::cout << "\n*** You can reproduce the problem with: ";
 
   unsigned PassType = PassesToRun[0]->getPassType();


Index: llvm/tools/bugpoint/bugpoint.cpp
diff -u llvm/tools/bugpoint/bugpoint.cpp:1.11 llvm/tools/bugpoint/bugpoint.cpp:1.11.4.1
--- llvm/tools/bugpoint/bugpoint.cpp:1.11	Tue Nov 11 16:41:34 2003
+++ llvm/tools/bugpoint/bugpoint.cpp	Mon Mar  1 17:59:18 2004
@@ -15,10 +15,11 @@
 
 #include "BugDriver.h"
 #include "llvm/Support/PassNameParser.h"
+#include "llvm/Support/ToolRunner.h"
 #include "Support/CommandLine.h"
+#include "Support/Signals.h"
 #include "Config/unistd.h"
 #include <sys/resource.h>
-
 using namespace llvm;
 
 static cl::list<std::string>
@@ -36,6 +37,7 @@
                               " LLVM automatic testcase reducer. See\nhttp://"
                               "llvm.cs.uiuc.edu/docs/CommandGuide/bugpoint.html"
                               " for more information.\n");
+  PrintStackTraceOnErrorSignal();
 
   BugDriver D(argv[0]);
   if (D.addSources(InputFilenames)) return 1;
@@ -51,5 +53,14 @@
     perror("setrlimit: RLIMIT_CORE");
   }
 
-  return D.run();
+  try {
+    return D.run();
+  } catch (ToolExecutionError &TEE) {
+    std::cerr << "Tool execution error: " << TEE.what() << "\n";
+    return 1;
+  } catch (...) {
+    std::cerr << "Whoops, an exception leaked out of bugpoint.  "
+              << "This is a bug in bugpoint!\n";
+    return 1;
+  }
 }





More information about the llvm-commits mailing list