//===- Miscompilation.cpp - Debug program miscompilations -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file was developed by the LLVM research group and is distributed under
+// the University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
//
// This file implements program miscompilation debugging support.
//
//===----------------------------------------------------------------------===//
#include "BugDriver.h"
-#include "SystemUtils.h"
#include "ListReducer.h"
-#include "llvm/Pass.h"
#include "llvm/Module.h"
+#include "llvm/Pass.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)"));
-}
+#include "Support/FileUtilities.h"
+using namespace llvm;
-class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> {
- BugDriver &BD;
-public:
- ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}
+namespace llvm {
- virtual TestResult doTest(const std::vector<const PassInfo*> &Prefix,
- const std::vector<const PassInfo*> &Kept);
-};
+ class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> {
+ BugDriver &BD;
+ public:
+ ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}
+
+ virtual TestResult doTest(std::vector<const PassInfo*> &Prefix,
+ std::vector<const PassInfo*> &Suffix);
+ };
+}
ReduceMiscompilingPasses::TestResult
-ReduceMiscompilingPasses::doTest(const std::vector<const PassInfo*> &Prefix,
- const std::vector<const PassInfo*> &Kept) {
- // First, run the program with just the Kept passes. If it is still broken
+ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
+ std::vector<const PassInfo*> &Suffix) {
+ // First, run the program with just the Suffix passes. If it is still broken
// with JUST the kept passes, discard the prefix passes.
- std::cout << "Checking to see if '" << getPassesString(Kept)
+ std::cout << "Checking to see if '" << getPassesString(Suffix)
<< "' compile correctly: ";
std::string BytecodeResult;
- if (BD.runPasses(Kept, BytecodeResult, false/*delete*/, true/*quiet*/)) {
- std::cerr << BD.getToolName() << ": Error running this sequence of passes"
+ if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
+ std::cerr << " Error running this sequence of passes"
<< " on the input program!\n";
- exit(1);
+ BD.setPassesToRun(Suffix);
+ BD.EmitProgressBytecode("pass-error", false);
+ exit(BD.debugOptimizerCrash());
}
// 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!
}
if (Prefix.empty()) return NoFailure;
- // First, run the program with just the Kept passes. If it is still broken
- // with JUST the kept passes, discard the prefix passes.
+ // Next, see if the program is broken if we run the "prefix" passes first,
+ // then separately run the "kept" passes.
std::cout << "Checking to see if '" << getPassesString(Prefix)
<< "' compile correctly: ";
// prefix passes, then discard the prefix passes.
//
if (BD.runPasses(Prefix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
- std::cerr << BD.getToolName() << ": Error running this sequence of passes"
+ std::cerr << " Error running this sequence of passes"
<< " on the input program!\n";
- exit(1);
+ BD.setPassesToRun(Prefix);
+ BD.EmitProgressBytecode("pass-error", false);
+ exit(BD.debugOptimizerCrash());
}
// 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;
}
removeFile(BytecodeResult); // No longer need the file on disk
- std::cout << "Checking to see if '" << getPassesString(Kept)
+ std::cout << "Checking to see if '" << getPassesString(Suffix)
<< "' passes compile correctly after the '"
<< getPassesString(Prefix) << "' passes: ";
Module *OriginalInput = BD.Program;
BD.Program = PrefixOutput;
- if (BD.runPasses(Kept, BytecodeResult, false/*delete*/, true/*quiet*/)) {
- std::cerr << BD.getToolName() << ": Error running this sequence of passes"
+ if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
+ std::cerr << " Error running this sequence of passes"
<< " on the input program!\n";
- exit(1);
+ BD.setPassesToRun(Suffix);
+ BD.EmitProgressBytecode("pass-error", false);
+ exit(BD.debugOptimizerCrash());
}
// 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 KeepPrefix;
+ return KeepSuffix;
}
// Otherwise, we must not be running the bad pass anymore.
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:
- ReduceMiscompilingFunctions(BugDriver &bd) : BD(bd) {}
-
- virtual TestResult doTest(const std::vector<Function*> &Prefix,
- const std::vector<Function*> &Kept) {
- if (TestFuncs(Kept, false))
- return KeepSuffix;
- if (!Prefix.empty() && TestFuncs(Prefix, false))
- return KeepPrefix;
- return NoFailure;
- }
-
- bool TestFuncs(const std::vector<Function*> &Prefix, bool EmitBytecode);
-};
-
-// DeleteFunctionBody - "Remove" the function by deleting all of it's basic
-// blocks, making it external.
-//
-static void DeleteFunctionBody(Function *F) {
- // First, break circular use/def chain references...
- for (Function::iterator I = F->begin(), E = F->end(); I != E; ++I)
- I->dropAllReferences();
-
- // Next, delete all of the basic blocks.
- F->getBasicBlockList().clear();
-
- assert(F->isExternal() && "This didn't make the function external!");
+namespace llvm {
+ class ReduceMiscompilingFunctions : public ListReducer<Function*> {
+ BugDriver &BD;
+ public:
+ ReduceMiscompilingFunctions(BugDriver &bd) : BD(bd) {}
+
+ virtual TestResult doTest(std::vector<Function*> &Prefix,
+ std::vector<Function*> &Suffix) {
+ if (!Suffix.empty() && TestFuncs(Suffix, false))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestFuncs(Prefix, false))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestFuncs(const std::vector<Function*> &Prefix, bool EmitBytecode);
+ };
}
-
bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs,
bool EmitBytecode) {
// Test to see if the function is misoptimized if we ONLY run it on the
// functions listed in Funcs.
if (!EmitBytecode) {
- std::cout << "Checking to see if the program is misoptimized when these "
- << "functions are run\nthrough the passes: ";
- PrintFunctionList(Funcs);
+ std::cout << "Checking to see if the program is misoptimized when "
+ << (Funcs.size()==1 ? "this function is" : "these functions are")
+ << " run through the pass"
+ << (BD.PassesToRun.size() == 1 ? "" : "es") << ": ";
+ BD.PrintFunctionList(Funcs);
std::cout << "\n";
} else {
std::cout <<"Outputting reduced bytecode files which expose the problem:\n";
}
// 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.
std::string BytecodeResult;
if (BD.runPasses(BD.PassesToRun, BytecodeResult, false/*delete*/,
true/*quiet*/)) {
- std::cerr << BD.getToolName() << ": Error running this sequence of passes"
+ std::cerr << " Error running this sequence of passes"
<< " on the input program!\n";
- exit(1);
+ BD.EmitProgressBytecode("pass-error", false);
+ 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) {
// 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
+ delete BD.Program; // Delete the hacked up program
BD.Program = OldProgram; // Restore the original
- std::cout << (Broken ? "nope.\n" : "yup.\n");
+ std::cout << (Broken ? " nope.\n" : " yup.\n");
return Broken;
}
/// 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: bugpoint.reference.out.\n";
- } else if (diffProgram(Output)) {
- std::cout << "\n*** Input program does not match reference diff!\n"
- << " Must be problem with input source!\n";
- return false; // Problem found
- }
-
- // Figure out which transformations miscompile the input program.
- unsigned OldSize = PassesToRun.size();
- ReduceMiscompilingPasses(*this).reduceList(PassesToRun);
-
// Make sure something was miscompiled...
- if (PassesToRun.size() == OldSize) {
+ if (!ReduceMiscompilingPasses(*this).reduceList(PassesToRun)) {
std::cerr << "*** Optimized program matches reference output! No problem "
<< "detected...\nbugpoint can't help you with your problem!\n";
return false;
<< 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
// Do the reduction...
ReduceMiscompilingFunctions(*this).reduceList(MiscompiledFunctions);
- std::cout << "\n*** The following functions are being miscompiled: ";
+ std::cout << "\n*** The following function"
+ << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
+ << " being miscompiled: ";
PrintFunctionList(MiscompiledFunctions);
std::cout << "\n";
// Output a bunch of bytecode files for the user...
ReduceMiscompilingFunctions(*this).TestFuncs(MiscompiledFunctions, true);
- if (CreatedOutput) removeFile(Output);
return false;
}
+