//===- 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 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 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 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; }