[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