//===- LTOBugPoint.cpp - Top-Level LTO BugPoint class ---------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This class contains all of the shared state and information that is used by // the LTO BugPoint tool to track down bit code files that cause errors. // //===----------------------------------------------------------------------===// #include "LTOBugPoint.h" #include "llvm/PassManager.h" #include "llvm/ModuleProvider.h" #include "llvm/CodeGen/FileWriters.h" #include "llvm/Target/SubtargetFeature.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetAsmInfo.h" #include "llvm/Target/TargetMachineRegistry.h" #include "llvm/Support/SystemUtils.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Config/config.h" #include #include using namespace llvm; using namespace Reloc; /// printBitVector - Helper function. static void printBitVector(BitVector &BV, const char *Title) { std::cerr << Title; for (unsigned i = 0, e = BV.size(); i < e; i++) { if (BV[i]) std::cerr << " " << i; } std::cerr << "\n"; } /// printBitVector - Helper function. static void printBitVectorFiles(BitVector &BV, const char *Title, SmallVector &InFiles) { std::cerr << Title << "\n"; for (unsigned i = 0, e = BV.size(); i < e; i++) { if (BV[i]) std::cerr << "\t" << InFiles[i] << "\n"; } } /// LTOBugPoint -- Constructor. Popuate list of linker options and /// list of linker input files. LTOBugPoint::LTOBugPoint(std::istream &args, std::istream &ins) { // Read linker options. Order is important here. std::string option; while (getline(args, option)) LinkerOptions.push_back(option); // Read linker input files. Order is important here. std::string inFile; while(getline(ins, inFile)) LinkerInputFiles.push_back(inFile); TempDir = sys::Path::GetTemporaryDirectory(); // FIXME - Use command line option to set this. findLinkingFailure = true; } LTOBugPoint::~LTOBugPoint() { TempDir.eraseFromDisk(true); } /// findTroubleMakers - Find minimum set of input files that causes error /// identified by the script. bool LTOBugPoint::findTroubleMakers(SmallVector &TroubleMakers, std::string &Script) { // Reproduce original error. if (!relinkProgram(LinkerInputFiles) && !findLinkingFailure) { ErrMsg = " Unable to reproduce original error!"; return false; } if (!findLinkingFailure && !reproduceProgramError(Script)) { ErrMsg = " Unable to reproduce original error!"; return false; } // Build native object files set. unsigned Size = LinkerInputFiles.size(); BCFiles.resize(Size); ConfirmedClean.resize(Size); ConfirmedGuilty.resize(Size); for (unsigned I = 0; I < Size; ++I) { std::string &FileName = LinkerInputFiles[I]; sys::Path InputFile(FileName.c_str()); if (InputFile.isDynamicLibrary() || InputFile.isArchive()) { ErrMsg = "Unable to handle input file " + FileName; return false; } else if (InputFile.isBitcodeFile()) { BCFiles.set(I); if (getNativeObjectFile(FileName) == false) return false; } else { // Original native object input files are always clean. ConfirmedClean.set(I); NativeInputFiles.push_back(FileName); } } if (BCFiles.none()) { ErrMsg = "Unable to help!"; ErrMsg = " Need at least one input file that contains llvm bitcode"; return false; } // Try to reproduce error using native object files first. If the error // occurs then this is not a LTO error. if (!relinkProgram(NativeInputFiles)) { ErrMsg = " Unable to link the program using all native object files!"; return false; } if (!findLinkingFailure && reproduceProgramError(Script) == true) { ErrMsg = " Unable to fix program error using all native object files!"; return false; } printBitVector(BCFiles, "Initial set of llvm bitcode files"); identifyTroubleMakers(BCFiles); printBitVectorFiles(ConfirmedGuilty, "Identified minimal set of bitcode files!", LinkerInputFiles); return true; } /// getFeatureString - Return a string listing the features associated with the /// target triple. /// /// FIXME: This is an inelegant way of specifying the features of a /// subtarget. It would be better if we could encode this information into the /// IR. std::string LTOBugPoint::getFeatureString(const char *TargetTriple) { SubtargetFeatures Features; if (strncmp(TargetTriple, "powerpc-apple-", 14) == 0) { Features.AddFeature("altivec", true); } else if (strncmp(TargetTriple, "powerpc64-apple-", 16) == 0) { Features.AddFeature("64bit", true); Features.AddFeature("altivec", true); } return Features.getString(); } /// assembleBitcode - Generate assembly code from the module. Return false /// in case of an error. bool LTOBugPoint::assembleBitcode(llvm::Module *M, const char *AsmFileName) { std::string TargetTriple = M->getTargetTriple(); std::string FeatureStr = getFeatureString(TargetTriple.c_str()); const TargetMachineRegistry::entry* Registry = TargetMachineRegistry::getClosestStaticTargetForModule( *M, ErrMsg); if ( Registry == NULL ) return false; TargetMachine *Target = Registry->CtorFn(*M, FeatureStr.c_str()); // If target supports exception handling then enable it now. if (Target->getTargetAsmInfo()->doesSupportExceptionHandling()) ExceptionHandling = true; // FIXME Target->setRelocationModel(Reloc::PIC_); FunctionPassManager* CGPasses = new FunctionPassManager(new ExistingModuleProvider(M)); CGPasses->add(new TargetData(*Target->getTargetData())); MachineCodeEmitter* mce = NULL; std::ofstream *Out = new std::ofstream(AsmFileName, std::ios::out); switch (Target->addPassesToEmitFile(*CGPasses, *Out, TargetMachine::AssemblyFile, true)) { case FileModel::MachOFile: mce = AddMachOWriter(*CGPasses, *Out, *Target); break; case FileModel::ElfFile: mce = AddELFWriter(*CGPasses, *Out, *Target); break; case FileModel::AsmFile: break; case FileModel::Error: case FileModel::None: ErrMsg = "target file type not supported"; return false; } if (Target->addPassesToEmitFileFinish(*CGPasses, mce, true)) { ErrMsg = "target does not support generation of this file type"; return false; } CGPasses->doInitialization(); for (Module::iterator it = M->begin(), e = M->end(); it != e; ++it) if (!it->isDeclaration()) CGPasses->run(*it); CGPasses->doFinalization(); delete Out; return true; } /// getNativeObjectFile - Generate native object file based from llvm /// bitcode file. Return false in case of an error. bool LTOBugPoint::getNativeObjectFile(std::string &FileName) { std::auto_ptr M; MemoryBuffer *Buffer = MemoryBuffer::getFile(FileName.c_str(), &ErrMsg); if (!Buffer) { ErrMsg = "Unable to read " + FileName; return false; } M.reset(ParseBitcodeFile(Buffer, &ErrMsg)); std::string TargetTriple = M->getTargetTriple(); sys::Path AsmFile(TempDir); if(AsmFile.createTemporaryFileOnDisk(false, &ErrMsg)) return false; if (assembleBitcode(M.get(), AsmFile.c_str()) == false) { AsmFile.eraseFromDisk(); return false; } sys::Path NativeFile(TempDir); if(NativeFile.createTemporaryFileOnDisk(false, &ErrMsg)) { AsmFile.eraseFromDisk(); return false; } // find compiler driver const sys::Path gcc = sys::Program::FindProgramByName("gcc"); if ( gcc.isEmpty() ) { ErrMsg = "can't locate gcc"; AsmFile.eraseFromDisk(); NativeFile.eraseFromDisk(); return false; } // build argument list std::vector args; args.push_back(gcc.c_str()); if ( TargetTriple.find("darwin") != TargetTriple.size() ) { if (strncmp(TargetTriple.c_str(), "i686-apple-", 11) == 0) { args.push_back("-arch"); args.push_back("i386"); } else if (strncmp(TargetTriple.c_str(), "x86_64-apple-", 13) == 0) { args.push_back("-arch"); args.push_back("x86_64"); } else if (strncmp(TargetTriple.c_str(), "powerpc-apple-", 14) == 0) { args.push_back("-arch"); args.push_back("ppc"); } else if (strncmp(TargetTriple.c_str(), "powerpc64-apple-", 16) == 0) { args.push_back("-arch"); args.push_back("ppc64"); } } args.push_back("-c"); args.push_back("-x"); args.push_back("assembler"); args.push_back("-o"); args.push_back(NativeFile.c_str()); args.push_back(AsmFile.c_str()); args.push_back(0); // invoke assembler if (sys::Program::ExecuteAndWait(gcc, &args[0], 0, 0, 0, 0, &ErrMsg)) { ErrMsg = "error in assembly"; AsmFile.eraseFromDisk(); NativeFile.eraseFromDisk(); return false; } AsmFile.eraseFromDisk(); NativeInputFiles.push_back(NativeFile.c_str()); return true; } /// relinkProgram - Relink program. Return false if linking fails. bool LTOBugPoint::relinkProgram(llvm::SmallVector &InFiles) { if (InFiles.empty()) return false; // Atleast three options: linker path, -o and output file name. if (LinkerOptions.size() < 3) return false; const sys::Path linker = sys::Program::FindProgramByName(LinkerOptions[0]); if (linker.isEmpty()) { ErrMsg = "can't locate linker"; return false; } std::vector Args; for (unsigned i = 0, e = LinkerOptions.size(); i < e; ++i) Args.push_back(LinkerOptions[i].c_str()); for (unsigned i = 0, e = InFiles.size(); i < e; ++i) Args.push_back(InFiles[i].c_str()); Args.push_back(0); if (sys::Program::ExecuteAndWait(linker, &Args[0], 0, 0, 0, 0, &ErrMsg)) { ErrMsg = "error while linking program"; return false; } return true; } /// reproduceProgramError - Validate program using user provided script. /// Return true if program error is reproduced. bool LTOBugPoint::reproduceProgramError(std::string &Script) { const sys::Path validator = sys::Program::FindProgramByName(Script); if (validator.isEmpty()) { ErrMsg = "can't locate validation script"; return false; } std::vector Args; Args.push_back(Script.c_str()); Args.push_back(0); int result = sys::Program::ExecuteAndWait(validator, &Args[0], 0, 0, 0, 0, &ErrMsg); // Validation scrip returns non-zero if the error is reproduced. if (result > 0) // Able to reproduce program error. return true; else if (result < 0) // error occured while running validation script. ErrMsg contains error // description. return false; return false; } /// identifyTroubleMakers - Identify set of bit code files that are causing /// the error. This is a recursive function. void LTOBugPoint::identifyTroubleMakers(llvm::BitVector &In) { assert (In.size() == LinkerInputFiles.size() && "Invalid identifyTroubleMakers input!\n"); printBitVector(In, "Processing files "); BitVector CandidateVector; CandidateVector.resize(LinkerInputFiles.size()); // Process first half unsigned count = 0; for (unsigned i = 0, e = In.size(); i < e; ++i) { if (!ConfirmedClean[i]) { count++; CandidateVector.set(i); } if (count >= In.count()/2) break; } if (CandidateVector.none()) return; printBitVector(CandidateVector, "Candidate vector "); // Reproduce the error using native object files for candidate files. SmallVector CandidateFiles; for (unsigned i = 0, e = CandidateVector.size(); i < e; ++i) { if (CandidateVector[i] || ConfirmedClean[i]) CandidateFiles.push_back(NativeInputFiles[i]); else CandidateFiles.push_back(LinkerInputFiles[i]); } bool result = relinkProgram(CandidateFiles); if (findLinkingFailure) { if (result == true) { // Candidate files are suspected. if (CandidateVector.count() == 1) { ConfirmedGuilty.set(CandidateVector.find_first()); return; } else identifyTroubleMakers(CandidateVector); } else { // Candidate files are not causing this error. for (unsigned i = 0, e = CandidateVector.size(); i < e; ++i) { if (CandidateVector[i]) ConfirmedClean.set(i); } } } else { std::cerr << "FIXME : Not yet implemented!\n"; } // Process remaining cadidates CandidateVector.clear(); CandidateVector.resize(LinkerInputFiles.size()); for (unsigned i = 0, e = LinkerInputFiles.size(); i < e; ++i) { if (!ConfirmedClean[i] && !ConfirmedGuilty[i]) CandidateVector.set(i); } identifyTroubleMakers(CandidateVector); }