mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-13 23:18:51 +00:00
4a10645c70
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@5120 91177308-0d34-0410-b5e6-96231b3b80d8
142 lines
5.1 KiB
C++
142 lines
5.1 KiB
C++
//===- Miscompilation.cpp - Debug program miscompilations -----------------===//
|
|
//
|
|
// This file implements program miscompilation debugging support.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BugDriver.h"
|
|
#include "SystemUtils.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Module.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)"));
|
|
}
|
|
|
|
/// debugMiscompilation - This method is used when the passes selected are not
|
|
/// crashing, but the generated output is semantically different from the
|
|
/// 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!\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 transformation is the first to miscompile the input
|
|
// program. We do a binary search here in case there are a large number of
|
|
// passes involved.
|
|
//
|
|
unsigned LastGood = 0, LastBad = PassesToRun.size();
|
|
while (LastGood != LastBad) {
|
|
unsigned Mid = (LastBad+LastGood+1) / 2;
|
|
std::vector<const PassInfo*> P(PassesToRun.begin(),
|
|
PassesToRun.begin()+Mid);
|
|
std::cout << "Checking to see if the first " << Mid << " passes are ok: ";
|
|
|
|
std::string BytecodeResult;
|
|
if (runPasses(P, BytecodeResult, false, true)) {
|
|
std::cerr << ToolName << ": Error running this sequence of passes"
|
|
<< " on the input program!\n";
|
|
exit(1);
|
|
}
|
|
|
|
// Check to see if the finished program matches the reference output...
|
|
if (diffProgram(Output, BytecodeResult)) {
|
|
std::cout << "nope.\n";
|
|
LastBad = Mid-1; // Miscompilation detected!
|
|
} else {
|
|
std::cout << "yup.\n";
|
|
LastGood = Mid; // No miscompilation!
|
|
}
|
|
|
|
// We are now done with the optimized output... so remove it.
|
|
removeFile(BytecodeResult);
|
|
}
|
|
|
|
// Make sure something was miscompiled...
|
|
if (LastBad >= PassesToRun.size()) {
|
|
std::cerr << "*** Optimized program matches reference output! No problem "
|
|
<< "detected...\nbugpoint can't help you with your problem!\n";
|
|
return false;
|
|
}
|
|
|
|
// Calculate which pass it is that miscompiles...
|
|
const PassInfo *ThePass = PassesToRun[LastBad];
|
|
|
|
std::cout << "\n*** Found miscompiling pass '-" << ThePass->getPassArgument()
|
|
<< "': " << ThePass->getPassName() << "\n";
|
|
|
|
if (LastGood != 0) {
|
|
std::vector<const PassInfo*> P(PassesToRun.begin(),
|
|
PassesToRun.begin()+LastGood);
|
|
std::string Filename;
|
|
std::cout << "Running good passes to get input for pass:";
|
|
if (runPasses(P, Filename, false, true)) {
|
|
std::cerr << "ERROR: Running the first " << LastGood
|
|
<< " passes crashed!\n";
|
|
return true;
|
|
}
|
|
std::cout << " done!\n";
|
|
|
|
// Assuming everything was successful, we now have a valid bytecode file in
|
|
// OutputName. Use it for "Program" Instead.
|
|
delete Program;
|
|
Program = ParseInputFile(Filename);
|
|
|
|
// Delete the file now.
|
|
removeFile(Filename);
|
|
}
|
|
|
|
bool Result = debugPassMiscompilation(ThePass, Output);
|
|
|
|
if (CreatedOutput) removeFile(Output);
|
|
return Result;
|
|
}
|
|
|
|
/// debugPassMiscompilation - This method is called when the specified pass
|
|
/// miscompiles Program as input. It tries to reduce the testcase to something
|
|
/// that smaller that still miscompiles the program. ReferenceOutput contains
|
|
/// the filename of the file containing the output we are to match.
|
|
///
|
|
bool BugDriver::debugPassMiscompilation(const PassInfo *Pass,
|
|
const std::string &ReferenceOutput) {
|
|
EmitProgressBytecode(Pass, "passinput");
|
|
|
|
// Loop over all of the functions in the program, attempting to find one that
|
|
// is being miscompiled. We do this by extracting the function into a module,
|
|
// running the "bad" optimization on that module, then linking it back into
|
|
// the program. If the program fails the diff, the function got misoptimized.
|
|
//
|
|
|
|
|
|
return false;
|
|
}
|