From: Misha Brukman Date: Mon, 28 Jul 2003 19:16:14 +0000 (+0000) Subject: BugDriver.h: X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=91eabc13d3a456cc4b387d3d7fdb041d976732c7;p=oota-llvm.git BugDriver.h: * Added method to query if BugDriver is executing the JIT currently. This provides the ability in adding code that is conditionally executed in codegen debugging phase. CodeGeneratorBug.cpp: * Delete test functions from the Safe module * Code conditionally added when debugging the JIT: use the lazy resolver function added to Emitter.cpp to get function pointer by name. When compiled into an .so, this is the only way to get a pointer to an external function * Added a symbol disambiguator which will keep symbols uniquely named across modules * Delete generated files by default * The function `main' *must* stay in the .bc file for the JIT, but that prevents debugging it alone. This patch makes the old `main' become `old_main' and adds a new function named `main' which just calls the original with the same parameters, thereby keeping functionality the same. ExecutionDriver.cpp: * Returned to getting unique filenames * Simplified code choosing between using and not using shared library option git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@7364 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 0eb6188bee9..cf2ae58fcdd 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -81,7 +81,6 @@ public: bool debugPassMiscompilation(const PassInfo *ThePass, const std::string &ReferenceOutput); - /// compileSharedObject - This method creates a SharedObject from a given /// BytecodeFile for debugging a code generator. int compileSharedObject(const std::string &BytecodeFile, @@ -92,6 +91,8 @@ public: /// functions that are not under consideration. bool debugCodeGenerator(); + bool isExecutingJIT(); + private: /// ParseInputFile - Given a bytecode or assembly input filename, parse and /// return it, or return null if not possible. diff --git a/tools/bugpoint/CodeGeneratorBug.cpp b/tools/bugpoint/CodeGeneratorBug.cpp index 93ee24ea081..9b8a91e3647 100644 --- a/tools/bugpoint/CodeGeneratorBug.cpp +++ b/tools/bugpoint/CodeGeneratorBug.cpp @@ -7,11 +7,18 @@ #include "BugDriver.h" #include "SystemUtils.h" #include "ListReducer.h" -#include "llvm/Pass.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/GlobalValue.h" +#include "llvm/iMemory.h" +#include "llvm/iTerminators.h" +#include "llvm/iOther.h" #include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Linker.h" -#include "Support/CommandLine.h" #include "Support/Statistic.h" #include "Support/StringExtras.h" #include @@ -31,14 +38,21 @@ public: return NoFailure; } - bool TestFuncs(const std::vector &CodegenTest); + bool TestFuncs(const std::vector &CodegenTest, + bool KeepFiles = false); void DisambiguateGlobalSymbols(Module *M); }; -bool ReduceMisCodegenFunctions::TestFuncs(const std::vector &Funcs) +bool ReduceMisCodegenFunctions::TestFuncs(const std::vector &Funcs, + bool KeepFiles) { + DEBUG(std::cerr << "Test functions are:\n"); + for (std::vector::const_iterator I = Funcs.begin(),E = Funcs.end(); + I != E; ++I) + DEBUG(std::cerr << "\t" << (*I)->getName() << "\n"); + // Clone the module for the two halves of the program we want. Module *SafeModule = CloneModule(BD.Program); @@ -64,8 +78,114 @@ bool ReduceMisCodegenFunctions::TestFuncs(const std::vector &Funcs) DeleteFunctionBody(TNOF); // Function is now external in this module! } + // Remove the Safe functions from the Test module + for (Module::iterator I=TestModule->begin(),E=TestModule->end(); I!=E; ++I) { + bool funcFound = false; + for (std::vector::const_iterator F=Funcs.begin(),Fe=Funcs.end(); + F != Fe; ++F) + if (I->getName() == (*F)->getName()) funcFound = true; + + if (!funcFound && !(BD.isExecutingJIT() && I->getName() == "main")) + DeleteFunctionBody(I); + } + + // This is only applicable if we are debugging the JIT: + // Find all external functions in the Safe modules that are actually used + // (called or taken address of), and make them call the JIT wrapper instead + if (BD.isExecutingJIT()) { + // Must delete `main' from Safe module if it has it + for (Module::iterator I=SafeModule->begin(), E=SafeModule->end();I!=E;++I) + if (I->getName() == "main") DeleteFunctionBody(I); + + // Add an external function "getPointerToNamedFunction" that JIT provides + // Prototype: void *getPointerToNamedFunction(const char* Name) + std::vector Params; + Params.push_back(PointerType::get(Type::SByteTy)); // std::string& + FunctionType *resolverTy = FunctionType::get(PointerType::get(Type::VoidTy), + Params, false /* isVarArg */); + const std::string ResolverFunctionName = "getPointerToNamedFunction"; + Function *resolverFunc = new Function(resolverTy, + GlobalValue::ExternalLinkage, + ResolverFunctionName, + SafeModule); + + // Use the function we just added to get addresses of functions we need + // Iterate over the global declarations in the Safe module + for (Module::iterator F=SafeModule->begin(),E=SafeModule->end(); F!=E; ++F){ + if (F->isExternal() && F->use_begin() != F->use_end() && + F->getName() != ResolverFunctionName) { + // If it has a non-zero use list, + // 1. Add a string constant with its name to the global file + // The correct type is `const [ NUM x sbyte ]' where NUM is length of + // function name + 1 + const std::string &Name = F->getName(); + GlobalVariable *funcName = + new GlobalVariable(ArrayType::get(Type::SByteTy, Name.length()+1), + true /* isConstant */, + GlobalValue::InternalLinkage, + ConstantArray::get(Name), + Name + "_name", + SafeModule); + + // 2. Use `GetElementPtr *funcName, 0, 0' to convert the string to an + // sbyte* so it matches the signature of the resolver function. + Constant *Zero = Constant::getNullValue(Type::LongTy); + std::vector GEPargs; + GEPargs.push_back(Zero); + GEPargs.push_back(Zero); + + // 3. Replace all uses of `func' with calls to resolver by: + // (a) Iterating through the list of uses of this function + // (b) Insert a cast instruction in front of each use + // (c) Replace use of old call with new call + + // Insert code at the beginning of the function + + for (Value::use_iterator i=F->use_begin(), e=F->use_end(); i!=e; ++i) { + if (Instruction* Inst = dyn_cast(*i)) { + // GetElementPtr *funcName, ulong 0, ulong 0 + Value *GEP = + ConstantExpr::getGetElementPtr(ConstantPointerRef::get(funcName), + GEPargs); + std::vector ResolverArgs; + ResolverArgs.push_back(GEP); + // call resolver(GetElementPtr...) + CallInst *resolve = new CallInst(resolverFunc, ResolverArgs, + "resolver", Inst); + // cast the result from the resolver to correctly-typed function + CastInst *castResolver = + new CastInst(resolve, PointerType::get(F->getFunctionType()), + "", Inst); + // actually use the resolved function + Inst->replaceUsesOfWith(F, castResolver); + + //BasicBlock::iterator ii(Inst); + //ReplaceInstWithValue(Inst->getParent()->getInstList(), + // ii, ResolverResult); + } + } + } + } + } + + DEBUG(std::cerr << "Safe module:\n"); + for (Module::iterator I = SafeModule->begin(), E = SafeModule->end();I!=E;++I) + if (!I->isExternal()) DEBUG(std::cerr << "\t" << I->getName() << "\n"); + for (Module::giterator I=SafeModule->gbegin(),E = SafeModule->gend();I!=E;++I) + if (!I->isExternal()) DEBUG(std::cerr << "\t" << I->getName() << "\n"); + + DEBUG(std::cerr << "Test module:\n"); + for (Module::iterator I =TestModule->begin(),E = TestModule->end(); I!=E;++I) + if (!I->isExternal()) DEBUG(std::cerr << "\t" << I->getName() << "\n"); + for (Module::giterator I=TestModule->gbegin(),E = TestModule->gend();I!=E;++I) + if (!I->isExternal()) DEBUG(std::cerr << "\t" << I->getName() << "\n"); + // Write out the bytecode to be sent to CBE - std::string SafeModuleBC = "bugpoint.safe.bc"; + std::string SafeModuleBC = getUniqueFilename("bugpoint.safe.bc"); + if (verifyModule(*SafeModule)) { + std::cerr << "Bytecode file corrupted!\n"; + exit(1); + } if (BD.writeProgramToFile(SafeModuleBC, SafeModule)) { std::cerr << "Error writing bytecode to `" << SafeModuleBC << "'\nExiting."; exit(1); @@ -88,48 +208,78 @@ bool ReduceMisCodegenFunctions::TestFuncs(const std::vector &Funcs) DeleteFunctionBody(I); } - std::string TestModuleBC = "bugpoint.test.bc"; + std::string TestModuleBC = getUniqueFilename("bugpoint.test.bc"); + if (verifyModule(*TestModule)) { + std::cerr << "Bytecode file corrupted!\n"; + exit(1); + } if (BD.writeProgramToFile(TestModuleBC, TestModule)) { std::cerr << "Error writing bytecode to `" << SafeModuleBC << "'\nExiting."; exit(1); } + delete SafeModule; + delete TestModule; + // Run the code generator on the `Test' code, loading the shared library. // The function returns whether or not the new output differs from reference. int Result = BD.diffProgram(TestModuleBC, SharedObject, false); - removeFile(SharedObject); + if (KeepFiles) { + std::cout << "You can reproduce the problem with the command line: \n" + << "lli (or llc) -load " << SharedObject << " " << TestModuleBC + << "\n"; + } else { + removeFile(TestModuleBC); + removeFile(SafeModuleBC); + removeFile(SharedObject); + } return Result; } namespace { struct Disambiguator { - std::set SymbolNames; + std::set SymbolNames; + std::set Symbols; uint64_t uniqueCounter; bool externalOnly; public: Disambiguator() : uniqueCounter(0), externalOnly(true) {} void setExternalOnly(bool value) { externalOnly = value; } void add(GlobalValue &V) { + // If we're only processing externals and this isn't external, bail if (externalOnly && !V.isExternal()) return; + // If we're already processed this symbol, don't add it again + if (Symbols.count(&V) != 0) return; + + std::string SymName = V.getName(); - if (SymbolNames.count(V.getName()) == 0) { - DEBUG(std::cerr << "Disambiguator: adding " << V.getName() + // If the symbol starts with a '.', replace it with 'x' + // This solves the problem of not being able to find symbols in an .so + // file when those symbol names start with '.' + if (SymName[0] == '.') { + SymName[0] = 'x'; + V.setName(SymName); + } + + if (SymbolNames.count(SymName) == 0) { + DEBUG(std::cerr << "Disambiguator: adding " << SymName << ", no conflicts.\n"); - SymbolNames.insert(V.getName()); + SymbolNames.insert(SymName); } else { // Mangle name before adding std::string newName; do { - newName = V.getName() + "_" + utostr(uniqueCounter); + newName = SymName + "_" + utostr(uniqueCounter); if (SymbolNames.count(newName) == 0) break; else ++uniqueCounter; } while (1); //while (SymbolNames.count(V->getName()+utostr(uniqueCounter++))==0); - DEBUG(std::cerr << "Disambiguator: conflict: " << V.getName() + DEBUG(std::cerr << "Disambiguator: conflict: " << SymName << ", adding: " << newName << "\n"); V.setName(newName); SymbolNames.insert(newName); } + Symbols.insert(&V); } }; } @@ -138,13 +288,17 @@ void ReduceMisCodegenFunctions::DisambiguateGlobalSymbols(Module *M) { // First, try not to cause collisions by minimizing chances of renaming an // already-external symbol, so take in external globals and functions as-is. Disambiguator D; + DEBUG(std::cerr << "Disambiguating globals (external-only)\n"); for (Module::giterator I = M->gbegin(), E = M->gend(); I != E; ++I) D.add(*I); + DEBUG(std::cerr << "Disambiguating functions (external-only)\n"); for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) D.add(*I); // Now just rename functions and globals as necessary, keeping what's already // in the set unique. D.setExternalOnly(false); + DEBUG(std::cerr << "Disambiguating globals\n"); for (Module::giterator I = M->gbegin(), E = M->gend(); I != E; ++I) D.add(*I); + DEBUG(std::cerr << "Disambiguating globals\n"); for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) D.add(*I); } @@ -157,6 +311,40 @@ bool BugDriver::debugCodeGenerator() { if (!I->isExternal()) MisCodegenFunctions.push_back(I); + // If we are executing the JIT, we *must* keep the function `main' in the + // module that is passed in, and not the shared library. However, we still + // want to be able to debug the `main' function alone. Thus, we create a new + // function `main' which just calls the old one. + if (isExecutingJIT()) { + // Get the `main' function + Function *oldMain = Program->getNamedFunction("main"); + // Rename it + oldMain->setName("old_main"); + // Create a NEW `main' function with same type + Function *newMain = new Function(oldMain->getFunctionType(), + GlobalValue::InternalLinkage, + "main", Program); + // Call the old main function and return its result + BasicBlock *BB = new BasicBlock("entry", newMain); + std::vector args; + for (Function::aiterator I=newMain->abegin(), E=newMain->aend(); I!=E; ++I) + args.push_back(I); + CallInst *call = new CallInst(oldMain, args); + BB->getInstList().push_back(call); + + // if the type of old function wasn't void, return value of call + ReturnInst *ret; + if (oldMain->getReturnType() != Type::VoidTy) { + ret = new ReturnInst(call); + } else { + ret = new ReturnInst(); + } + + // Add the return instruction to the BasicBlock + BB->getInstList().push_back(ret); + } + + // Do the reduction... ReduceMisCodegenFunctions(*this).reduceList(MisCodegenFunctions); @@ -165,7 +353,7 @@ bool BugDriver::debugCodeGenerator() { std::cout << "\n"; // Output a bunch of bytecode files for the user... - // ReduceMisCodegenFunctions(*this).TestFuncs(MisCodegenFunctions); + ReduceMisCodegenFunctions(*this).TestFuncs(MisCodegenFunctions, true); return false; } diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp index b391b0492f2..adca572075e 100644 --- a/tools/bugpoint/ExecutionDriver.cpp +++ b/tools/bugpoint/ExecutionDriver.cpp @@ -17,6 +17,7 @@ BUGPOINT NOTES: #include "BugDriver.h" #include "SystemUtils.h" #include "Support/CommandLine.h" +#include "Support/Statistic.h" #include #include @@ -101,12 +102,13 @@ int LLI::ExecuteProgram(const std::string &Bytecode, 0 }; + std::cout << ""; return RunProgramWithTimeout(LLIPath, Args, InputFile, OutputFile, OutputFile); } //===----------------------------------------------------------------------===// -// GCC Implementation of AbstractIntepreter interface +// GCC abstraction // // This is not a *real* AbstractInterpreter as it does not accept bytecode // files, but only input acceptable to GCC, i.e. C, C++, and assembly files @@ -145,7 +147,7 @@ int GCC::ExecuteProgram(const std::string &ProgramFile, FileType fileType, const std::string &OutputFile, const std::string &SharedLib) { - std::string OutputBinary = "bugpoint.gcc.exe"; + std::string OutputBinary = getUniqueFilename("bugpoint.gcc.exe"); const char **GCCArgs; const char *ArgsWithoutSO[] = { @@ -159,9 +161,9 @@ int GCC::ExecuteProgram(const std::string &ProgramFile, }; const char *ArgsWithSO[] = { GCCPath.c_str(), + SharedLib.c_str(), // Specify the shared library to link in... "-x", (fileType == AsmFile) ? "assembler" : "c", ProgramFile.c_str(), // Specify the input filename... - SharedLib.c_str(), // Specify the shared library to link in... "-o", OutputBinary.c_str(), // Output to the right filename... "-lm", // Hard-code the math library... "-O2", // Optimize the program a bit... @@ -181,9 +183,8 @@ int GCC::ExecuteProgram(const std::string &ProgramFile, 0 }; - std::cout << ""; - // Now that we have a binary, run it! + std::cout << ""; int ProgramResult = RunProgramWithTimeout(OutputBinary, ProgramArgs, InputFile, OutputFile, OutputFile); std::cout << "\n"; @@ -194,7 +195,7 @@ int GCC::ExecuteProgram(const std::string &ProgramFile, int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType, std::string &OutputFile) { - OutputFile = "./bugpoint.so"; + OutputFile = getUniqueFilename("./bugpoint.so"); // Compile the C/asm file into a shared object const char* GCCArgs[] = { GCCPath.c_str(), @@ -277,7 +278,6 @@ public: int OutputAsm(const std::string &Bytecode, std::string &OutputAsmFile); - }; int LLC::OutputAsm(const std::string &Bytecode, @@ -347,26 +347,25 @@ public: int JIT::ExecuteProgram(const std::string &Bytecode, const std::string &OutputFile, const std::string &SharedLib) { - if (SharedLib.empty()) { - const char* Args[] = { - LLIPath.c_str(), - "-quiet", - "-force-interpreter=false", - Bytecode.c_str(), - 0 - }; - return RunProgramWithTimeout(LLIPath, Args, - InputFile, OutputFile, OutputFile); - } else { - const char* Args[] = { - LLIPath.c_str(), "-quiet", "-force-interpreter=false", - "-load", SharedLib.c_str(), - Bytecode.c_str(), - 0 - }; - return RunProgramWithTimeout(LLIPath, Args, - InputFile, OutputFile, OutputFile); - } + const char* ArgsWithoutSO[] = { + LLIPath.c_str(), "-quiet", "-force-interpreter=false", + Bytecode.c_str(), + 0 + }; + + const char* ArgsWithSO[] = { + LLIPath.c_str(), "-quiet", "-force-interpreter=false", + "-load", SharedLib.c_str(), + Bytecode.c_str(), + 0 + }; + + const char** JITArgs = SharedLib.empty() ? ArgsWithoutSO : ArgsWithSO; + + std::cout << ""; + DEBUG(std::cerr << "\nSending output to " << OutputFile << "\n"); + return RunProgramWithTimeout(LLIPath, JITArgs, + InputFile, OutputFile, OutputFile); } //===----------------------------------------------------------------------===// @@ -591,7 +590,11 @@ bool BugDriver::diffProgram(const std::string &BytecodeFile, if (C1 != C2) { FilesDifferent = true; break; } } while (C1 != EOF); - removeFile(Output); + //removeFile(Output); if (RemoveBytecode) removeFile(BytecodeFile); return FilesDifferent; } + +bool BugDriver::isExecutingJIT() { + return InterpreterSel == RunJIT; +}