diff options
Diffstat (limited to 'lib/ExecutionEngine/JIT')
-rw-r--r-- | lib/ExecutionEngine/JIT/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/Intercept.cpp | 11 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JIT.cpp | 172 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JIT.h | 47 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp | 208 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITDebugRegisterer.h | 116 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp | 182 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITDwarfEmitter.h | 4 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITEmitter.cpp | 292 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITMemoryManager.cpp | 387 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp | 5 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/OProfileJITEventListener.cpp | 178 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/TargetSelect.cpp | 85 |
13 files changed, 1282 insertions, 407 deletions
diff --git a/lib/ExecutionEngine/JIT/CMakeLists.txt b/lib/ExecutionEngine/JIT/CMakeLists.txt index bf915f7..41b3b4e 100644 --- a/lib/ExecutionEngine/JIT/CMakeLists.txt +++ b/lib/ExecutionEngine/JIT/CMakeLists.txt @@ -4,9 +4,11 @@ add_definitions(-DENABLE_X86_JIT) add_llvm_library(LLVMJIT Intercept.cpp JIT.cpp + JITDebugRegisterer.cpp JITDwarfEmitter.cpp JITEmitter.cpp JITMemoryManager.cpp MacOSJITEventListener.cpp + OProfileJITEventListener.cpp TargetSelect.cpp ) diff --git a/lib/ExecutionEngine/JIT/Intercept.cpp b/lib/ExecutionEngine/JIT/Intercept.cpp index 3dcc462..c00b60a 100644 --- a/lib/ExecutionEngine/JIT/Intercept.cpp +++ b/lib/ExecutionEngine/JIT/Intercept.cpp @@ -16,7 +16,7 @@ //===----------------------------------------------------------------------===// #include "JIT.h" -#include "llvm/Support/Streams.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/System/DynamicLibrary.h" #include "llvm/Config/config.h" using namespace llvm; @@ -56,6 +56,7 @@ static void runAtExitHandlers() { * linking with libc_nonshared.a and -Wl,--export-dynamic doesn't make 'stat' * available as an exported symbol, so we have to add it explicitly. */ +namespace { class StatSymbols { public: StatSymbols() { @@ -72,6 +73,7 @@ public: sys::DynamicLibrary::AddSymbol("mknod", (void*)(intptr_t)mknod); } }; +} static StatSymbols initStatSymbols; #endif // __linux__ @@ -82,7 +84,7 @@ static void jit_exit(int Status) { } // jit_atexit - Used to intercept the "atexit" library call. -static int jit_atexit(void (*Fn)(void)) { +static int jit_atexit(void (*Fn)()) { AtExitHandlers.push_back(Fn); // Take note of atexit handler... return 0; // Always successful } @@ -140,9 +142,8 @@ void *JIT::getPointerToNamedFunction(const std::string &Name, return RP; if (AbortOnFailure) { - cerr << "ERROR: Program used external function '" << Name - << "' which could not be resolved!\n"; - abort(); + llvm_report_error("Program used external function '"+Name+ + "' which could not be resolved!"); } return 0; } diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp index 1d8312f..b2a268b 100644 --- a/lib/ExecutionEngine/JIT/JIT.cpp +++ b/lib/ExecutionEngine/JIT/JIT.cpp @@ -27,6 +27,7 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetJITInfo.h" #include "llvm/Support/Dwarf.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MutexGuard.h" #include "llvm/System/DynamicLibrary.h" #include "llvm/Config/config.h" @@ -196,25 +197,44 @@ void DarwinRegisterFrame(void* FrameBegin) { ExecutionEngine *ExecutionEngine::createJIT(ModuleProvider *MP, std::string *ErrorStr, JITMemoryManager *JMM, - CodeGenOpt::Level OptLevel) { - ExecutionEngine *EE = JIT::createJIT(MP, ErrorStr, JMM, OptLevel); - if (!EE) return 0; - + CodeGenOpt::Level OptLevel, + bool GVsWithCode) { + return JIT::createJIT(MP, ErrorStr, JMM, OptLevel, GVsWithCode); +} + +ExecutionEngine *JIT::createJIT(ModuleProvider *MP, + std::string *ErrorStr, + JITMemoryManager *JMM, + CodeGenOpt::Level OptLevel, + bool GVsWithCode) { // Make sure we can resolve symbols in the program as well. The zero arg // to the function tells DynamicLibrary to load the program, not a library. - sys::DynamicLibrary::LoadLibraryPermanently(0, ErrorStr); - return EE; + if (sys::DynamicLibrary::LoadLibraryPermanently(0, ErrorStr)) + return 0; + + // Pick a target either via -march or by guessing the native arch. + TargetMachine *TM = JIT::selectTarget(MP, ErrorStr); + if (!TM || (ErrorStr && ErrorStr->length() > 0)) return 0; + + // If the target supports JIT code generation, create a the JIT. + if (TargetJITInfo *TJ = TM->getJITInfo()) { + return new JIT(MP, *TM, *TJ, JMM, OptLevel, GVsWithCode); + } else { + if (ErrorStr) + *ErrorStr = "target does not support JIT code generation"; + return 0; + } } JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji, - JITMemoryManager *JMM, CodeGenOpt::Level OptLevel) - : ExecutionEngine(MP), TM(tm), TJI(tji) { + JITMemoryManager *JMM, CodeGenOpt::Level OptLevel, bool GVsWithCode) + : ExecutionEngine(MP), TM(tm), TJI(tji), AllocateGVsWithCode(GVsWithCode) { setTargetData(TM.getTargetData()); jitstate = new JITState(MP); // Initialize JCE - JCE = createEmitter(*this, JMM); + JCE = createEmitter(*this, JMM, TM); // Add target data MutexGuard locked(lock); @@ -224,8 +244,7 @@ JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji, // Turn the machine code intermediate representation into bytes in memory that // may be executed. if (TM.addPassesToEmitMachineCode(PM, *JCE, OptLevel)) { - cerr << "Target does not support machine code emission!\n"; - abort(); + llvm_report_error("Target does not support machine code emission!"); } // Register routine for informing unwinding runtime about new EH frames @@ -273,8 +292,7 @@ void JIT::addModuleProvider(ModuleProvider *MP) { // Turn the machine code intermediate representation into bytes in memory // that may be executed. if (TM.addPassesToEmitMachineCode(PM, *JCE, CodeGenOpt::Default)) { - cerr << "Target does not support machine code emission!\n"; - abort(); + llvm_report_error("Target does not support machine code emission!"); } // Initialize passes. @@ -306,8 +324,7 @@ Module *JIT::removeModuleProvider(ModuleProvider *MP, std::string *E) { // Turn the machine code intermediate representation into bytes in memory // that may be executed. if (TM.addPassesToEmitMachineCode(PM, *JCE, CodeGenOpt::Default)) { - cerr << "Target does not support machine code emission!\n"; - abort(); + llvm_report_error("Target does not support machine code emission!"); } // Initialize passes. @@ -338,8 +355,7 @@ void JIT::deleteModuleProvider(ModuleProvider *MP, std::string *E) { // Turn the machine code intermediate representation into bytes in memory // that may be executed. if (TM.addPassesToEmitMachineCode(PM, *JCE, CodeGenOpt::Default)) { - cerr << "Target does not support machine code emission!\n"; - abort(); + llvm_report_error("Target does not support machine code emission!"); } // Initialize passes. @@ -366,10 +382,11 @@ GenericValue JIT::runFunction(Function *F, // Handle some common cases first. These cases correspond to common `main' // prototypes. - if (RetTy == Type::Int32Ty || RetTy == Type::VoidTy) { + if (RetTy == Type::getInt32Ty(F->getContext()) || + RetTy == Type::getVoidTy(F->getContext())) { switch (ArgValues.size()) { case 3: - if (FTy->getParamType(0) == Type::Int32Ty && + if (FTy->getParamType(0) == Type::getInt32Ty(F->getContext()) && isa<PointerType>(FTy->getParamType(1)) && isa<PointerType>(FTy->getParamType(2))) { int (*PF)(int, char **, const char **) = @@ -384,7 +401,7 @@ GenericValue JIT::runFunction(Function *F, } break; case 2: - if (FTy->getParamType(0) == Type::Int32Ty && + if (FTy->getParamType(0) == Type::getInt32Ty(F->getContext()) && isa<PointerType>(FTy->getParamType(1))) { int (*PF)(int, char **) = (int(*)(int, char **))(intptr_t)FPtr; @@ -397,7 +414,7 @@ GenericValue JIT::runFunction(Function *F, break; case 1: if (FTy->getNumParams() == 1 && - FTy->getParamType(0) == Type::Int32Ty) { + FTy->getParamType(0) == Type::getInt32Ty(F->getContext())) { GenericValue rv; int (*PF)(int) = (int(*)(int))(intptr_t)FPtr; rv.IntVal = APInt(32, PF(ArgValues[0].IntVal.getZExtValue())); @@ -411,7 +428,7 @@ GenericValue JIT::runFunction(Function *F, if (ArgValues.empty()) { GenericValue rv; switch (RetTy->getTypeID()) { - default: assert(0 && "Unknown return type for function call!"); + default: llvm_unreachable("Unknown return type for function call!"); case Type::IntegerTyID: { unsigned BitWidth = cast<IntegerType>(RetTy)->getBitWidth(); if (BitWidth == 1) @@ -425,7 +442,7 @@ GenericValue JIT::runFunction(Function *F, else if (BitWidth <= 64) rv.IntVal = APInt(BitWidth, ((int64_t(*)())(intptr_t)FPtr)()); else - assert(0 && "Integer types > 64 bits not supported"); + llvm_unreachable("Integer types > 64 bits not supported"); return rv; } case Type::VoidTyID: @@ -440,7 +457,7 @@ GenericValue JIT::runFunction(Function *F, case Type::X86_FP80TyID: case Type::FP128TyID: case Type::PPC_FP128TyID: - assert(0 && "long double not supported yet"); + llvm_unreachable("long double not supported yet"); return rv; case Type::PointerTyID: return PTOGV(((void*(*)())(intptr_t)FPtr)()); @@ -458,7 +475,7 @@ GenericValue JIT::runFunction(Function *F, F->getParent()); // Insert a basic block. - BasicBlock *StubBB = BasicBlock::Create("", Stub); + BasicBlock *StubBB = BasicBlock::Create(F->getContext(), "", Stub); // Convert all of the GenericValue arguments over to constants. Note that we // currently don't support varargs. @@ -468,28 +485,31 @@ GenericValue JIT::runFunction(Function *F, const Type *ArgTy = FTy->getParamType(i); const GenericValue &AV = ArgValues[i]; switch (ArgTy->getTypeID()) { - default: assert(0 && "Unknown argument type for function call!"); + default: llvm_unreachable("Unknown argument type for function call!"); case Type::IntegerTyID: - C = ConstantInt::get(AV.IntVal); + C = ConstantInt::get(F->getContext(), AV.IntVal); break; case Type::FloatTyID: - C = ConstantFP::get(APFloat(AV.FloatVal)); + C = ConstantFP::get(F->getContext(), APFloat(AV.FloatVal)); break; case Type::DoubleTyID: - C = ConstantFP::get(APFloat(AV.DoubleVal)); + C = ConstantFP::get(F->getContext(), APFloat(AV.DoubleVal)); break; case Type::PPC_FP128TyID: case Type::X86_FP80TyID: case Type::FP128TyID: - C = ConstantFP::get(APFloat(AV.IntVal)); + C = ConstantFP::get(F->getContext(), APFloat(AV.IntVal)); break; case Type::PointerTyID: void *ArgPtr = GVTOP(AV); if (sizeof(void*) == 4) - C = ConstantInt::get(Type::Int32Ty, (int)(intptr_t)ArgPtr); + C = ConstantInt::get(Type::getInt32Ty(F->getContext()), + (int)(intptr_t)ArgPtr); else - C = ConstantInt::get(Type::Int64Ty, (intptr_t)ArgPtr); - C = ConstantExpr::getIntToPtr(C, ArgTy); // Cast the integer to pointer + C = ConstantInt::get(Type::getInt64Ty(F->getContext()), + (intptr_t)ArgPtr); + // Cast the integer to pointer + C = ConstantExpr::getIntToPtr(C, ArgTy); break; } Args.push_back(C); @@ -499,10 +519,11 @@ GenericValue JIT::runFunction(Function *F, "", StubBB); TheCall->setCallingConv(F->getCallingConv()); TheCall->setTailCall(); - if (TheCall->getType() != Type::VoidTy) - ReturnInst::Create(TheCall, StubBB); // Return result of the call. + if (TheCall->getType() != Type::getVoidTy(F->getContext())) + // Return result of the call. + ReturnInst::Create(F->getContext(), TheCall, StubBB); else - ReturnInst::Create(StubBB); // Just return void. + ReturnInst::Create(F->getContext(), StubBB); // Just return void. // Finally, return the value returned by our nullary stub function. return runFunction(Stub, std::vector<GenericValue>()); @@ -629,9 +650,8 @@ void *JIT::getPointerToFunction(Function *F) { std::string ErrorMsg; if (MP->materializeFunction(F, &ErrorMsg)) { - cerr << "Error reading function '" << F->getName() - << "' from bitcode file: " << ErrorMsg << "\n"; - abort(); + llvm_report_error("Error reading function '" + F->getName()+ + "' from bitcode file: " + ErrorMsg); } // Now retry to get the address. @@ -669,45 +689,18 @@ void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) { if (GV->getName() == "__dso_handle") return (void*)&__dso_handle; #endif - Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(GV->getName().c_str()); + Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(GV->getName()); if (Ptr == 0 && !areDlsymStubsEnabled()) { - cerr << "Could not resolve external global address: " - << GV->getName() << "\n"; - abort(); + llvm_report_error("Could not resolve external global address: " + +GV->getName()); } addGlobalMapping(GV, Ptr); } else { - // GlobalVariable's which are not "constant" will cause trouble in a server - // situation. It's returned in the same block of memory as code which may - // not be writable. - if (isGVCompilationDisabled() && !GV->isConstant()) { - cerr << "Compilation of non-internal GlobalValue is disabled!\n"; - abort(); - } // If the global hasn't been emitted to memory yet, allocate space and - // emit it into memory. It goes in the same array as the generated - // code, jump tables, etc. - const Type *GlobalType = GV->getType()->getElementType(); - size_t S = getTargetData()->getTypeAllocSize(GlobalType); - size_t A = getTargetData()->getPreferredAlignment(GV); - if (GV->isThreadLocal()) { - MutexGuard locked(lock); - Ptr = TJI.allocateThreadLocalMemory(S); - } else if (TJI.allocateSeparateGVMemory()) { - if (A <= 8) { - Ptr = malloc(S); - } else { - // Allocate S+A bytes of memory, then use an aligned pointer within that - // space. - Ptr = malloc(S+A); - unsigned MisAligned = ((intptr_t)Ptr & (A-1)); - Ptr = (char*)Ptr + (MisAligned ? (A-MisAligned) : 0); - } - } else { - Ptr = JCE->allocateSpace(S, A); - } + // emit it into memory. + Ptr = getMemoryForGV(GV); addGlobalMapping(GV, Ptr); - EmitGlobalVariable(GV); + EmitGlobalVariable(GV); // Initialize the variable. } return Ptr; } @@ -742,14 +735,41 @@ void *JIT::recompileAndRelinkFunction(Function *F) { /// on the target. /// char* JIT::getMemoryForGV(const GlobalVariable* GV) { - const Type *ElTy = GV->getType()->getElementType(); - size_t GVSize = (size_t)getTargetData()->getTypeAllocSize(ElTy); + char *Ptr; + + // GlobalVariable's which are not "constant" will cause trouble in a server + // situation. It's returned in the same block of memory as code which may + // not be writable. + if (isGVCompilationDisabled() && !GV->isConstant()) { + llvm_report_error("Compilation of non-internal GlobalValue is disabled!"); + } + + // Some applications require globals and code to live together, so they may + // be allocated into the same buffer, but in general globals are allocated + // through the memory manager which puts them near the code but not in the + // same buffer. + const Type *GlobalType = GV->getType()->getElementType(); + size_t S = getTargetData()->getTypeAllocSize(GlobalType); + size_t A = getTargetData()->getPreferredAlignment(GV); if (GV->isThreadLocal()) { MutexGuard locked(lock); - return TJI.allocateThreadLocalMemory(GVSize); + Ptr = TJI.allocateThreadLocalMemory(S); + } else if (TJI.allocateSeparateGVMemory()) { + if (A <= 8) { + Ptr = (char*)malloc(S); + } else { + // Allocate S+A bytes of memory, then use an aligned pointer within that + // space. + Ptr = (char*)malloc(S+A); + unsigned MisAligned = ((intptr_t)Ptr & (A-1)); + Ptr = Ptr + (MisAligned ? (A-MisAligned) : 0); + } + } else if (AllocateGVsWithCode) { + Ptr = (char*)JCE->allocateSpace(S, A); } else { - return new char[GVSize]; + Ptr = (char*)JCE->allocateGlobal(S, A); } + return Ptr; } void JIT::addPendingFunction(Function *F) { diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index 66417a7..525cc84 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -16,11 +16,12 @@ #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/PassManager.h" +#include "llvm/Support/ValueHandle.h" namespace llvm { class Function; -class JITEvent_EmittedFunctionDetails; +struct JITEvent_EmittedFunctionDetails; class MachineCodeEmitter; class MachineCodeInfo; class TargetJITInfo; @@ -33,7 +34,7 @@ private: /// PendingFunctions - Functions which have not been code generated yet, but /// were called from a function being code generated. - std::vector<Function*> PendingFunctions; + std::vector<AssertingVH<Function> > PendingFunctions; public: explicit JITState(ModuleProvider *MP) : PM(MP), MP(MP) {} @@ -43,7 +44,7 @@ public: } ModuleProvider *getMP() const { return MP; } - std::vector<Function*> &getPendingFunctions(const MutexGuard &L) { + std::vector<AssertingVH<Function> > &getPendingFunctions(const MutexGuard &L){ return PendingFunctions; } }; @@ -55,10 +56,16 @@ class JIT : public ExecutionEngine { JITCodeEmitter *JCE; // JCE object std::vector<JITEventListener*> EventListeners; + /// AllocateGVsWithCode - Some applications require that global variables and + /// code be allocated into the same region of memory, in which case this flag + /// should be set to true. Doing so breaks freeMachineCodeForFunction. + bool AllocateGVsWithCode; + JITState *jitstate; - JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji, - JITMemoryManager *JMM, CodeGenOpt::Level OptLevel); + JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji, + JITMemoryManager *JMM, CodeGenOpt::Level OptLevel, + bool AllocateGVsWithCode); public: ~JIT(); @@ -73,10 +80,13 @@ public: /// create - Create an return a new JIT compiler if there is one available /// for the current target. Otherwise, return null. /// - static ExecutionEngine *create(ModuleProvider *MP, std::string *Err, + static ExecutionEngine *create(ModuleProvider *MP, + std::string *Err, + JITMemoryManager *JMM, CodeGenOpt::Level OptLevel = - CodeGenOpt::Default) { - return createJIT(MP, Err, 0, OptLevel); + CodeGenOpt::Default, + bool GVsWithCode = true) { + return ExecutionEngine::createJIT(MP, Err, JMM, OptLevel, GVsWithCode); } virtual void addModuleProvider(ModuleProvider *MP); @@ -145,16 +155,22 @@ public: /// addPendingFunction - while jitting non-lazily, a called but non-codegen'd /// function was encountered. Add it to a pending list to be processed after /// the current function. - /// + /// void addPendingFunction(Function *F); - + /// getCodeEmitter - Return the code emitter this JIT is emitting into. + /// JITCodeEmitter *getCodeEmitter() const { return JCE; } - - static ExecutionEngine *createJIT(ModuleProvider *MP, std::string *Err, - JITMemoryManager *JMM, - CodeGenOpt::Level OptLevel); + /// selectTarget - Pick a target either via -march or by guessing the native + /// arch. Add any CPU features specified via -mcpu or -mattr. + static TargetMachine *selectTarget(ModuleProvider *MP, std::string *Err); + + static ExecutionEngine *createJIT(ModuleProvider *MP, + std::string *ErrorStr, + JITMemoryManager *JMM, + CodeGenOpt::Level OptLevel, + bool GVsWithCode); // Run the JIT on F and return information about the generated code void runJITOnFunction(Function *F, MachineCodeInfo *MCI = 0); @@ -170,7 +186,8 @@ public: void NotifyFreeingMachineCode(const Function &F, void *OldPtr); private: - static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM); + static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM, + TargetMachine &tm); void runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked); void updateFunctionStub(Function *F); void updateDlsymStubTable(); diff --git a/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp b/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp new file mode 100644 index 0000000..fa64010 --- /dev/null +++ b/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp @@ -0,0 +1,208 @@ +//===-- JITDebugRegisterer.cpp - Register debug symbols for JIT -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITDebugRegisterer object that is used by the JIT to +// register debug info with debuggers like GDB. +// +//===----------------------------------------------------------------------===// + +#include "JITDebugRegisterer.h" +#include "../../CodeGen/ELF.h" +#include "../../CodeGen/ELFWriter.h" +#include "llvm/LLVMContext.h" +#include "llvm/Function.h" +#include "llvm/Module.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Mutex.h" +#include <string> +#include <vector> + +namespace llvm { + +// This must be kept in sync with gdb/gdb/jit.h . +extern "C" { + + // Debuggers puts a breakpoint in this function. + void DISABLE_INLINE __jit_debug_register_code() { } + + // We put information about the JITed function in this global, which the + // debugger reads. Make sure to specify the version statically, because the + // debugger checks the version before we can set it during runtime. + struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +} + +namespace { + + /// JITDebugLock - Used to serialize all code registration events, since they + /// modify global variables. + sys::Mutex JITDebugLock; + +} + +JITDebugRegisterer::JITDebugRegisterer(TargetMachine &tm) : TM(tm), FnMap() { } + +JITDebugRegisterer::~JITDebugRegisterer() { + // Free all ELF memory. + for (RegisteredFunctionsMap::iterator I = FnMap.begin(), E = FnMap.end(); + I != E; ++I) { + // Call the private method that doesn't update the map so our iterator + // doesn't break. + UnregisterFunctionInternal(I); + } + FnMap.clear(); +} + +std::string JITDebugRegisterer::MakeELF(const Function *F, DebugInfo &I) { + // Stack allocate an empty module with an empty LLVMContext for the ELFWriter + // API. We don't use the real module because then the ELFWriter would write + // out unnecessary GlobalValues during finalization. + LLVMContext Context; + Module M("", Context); + + // Make a buffer for the ELF in memory. + std::string Buffer; + raw_string_ostream O(Buffer); + ELFWriter EW(O, TM); + EW.doInitialization(M); + + // Copy the binary into the .text section. This isn't necessary, but it's + // useful to be able to disassemble the ELF by hand. + ELFSection &Text = EW.getTextSection((Function *)F); + Text.Addr = (uint64_t)I.FnStart; + // TODO: We could eliminate this copy if we somehow used a pointer/size pair + // instead of a vector. + Text.getData().assign(I.FnStart, I.FnEnd); + + // Copy the exception handling call frame information into the .eh_frame + // section. This allows GDB to get a good stack trace, particularly on + // linux x86_64. Mark this as a PROGBITS section that needs to be loaded + // into memory at runtime. + ELFSection &EH = EW.getSection(".eh_frame", ELFSection::SHT_PROGBITS, + ELFSection::SHF_ALLOC); + // Pointers in the DWARF EH info are all relative to the EH frame start, + // which is stored here. + EH.Addr = (uint64_t)I.EhStart; + // TODO: We could eliminate this copy if we somehow used a pointer/size pair + // instead of a vector. + EH.getData().assign(I.EhStart, I.EhEnd); + + // Add this single function to the symbol table, so the debugger prints the + // name instead of '???'. We give the symbol default global visibility. + ELFSym *FnSym = ELFSym::getGV(F, + ELFSym::STB_GLOBAL, + ELFSym::STT_FUNC, + ELFSym::STV_DEFAULT); + FnSym->SectionIdx = Text.SectionIdx; + FnSym->Size = I.FnEnd - I.FnStart; + FnSym->Value = 0; // Offset from start of section. + EW.SymbolList.push_back(FnSym); + + EW.doFinalization(M); + O.flush(); + + // When trying to debug why GDB isn't getting the debug info right, it's + // awfully helpful to write the object file to disk so that it can be + // inspected with readelf and objdump. + if (JITEmitDebugInfoToDisk) { + std::string Filename; + raw_string_ostream O2(Filename); + O2 << "/tmp/llvm_function_" << I.FnStart << "_" << F->getNameStr() << ".o"; + O2.flush(); + std::string Errors; + raw_fd_ostream O3(Filename.c_str(), Errors); + O3 << Buffer; + O3.close(); + } + + return Buffer; +} + +void JITDebugRegisterer::RegisterFunction(const Function *F, DebugInfo &I) { + // TODO: Support non-ELF platforms. + if (!TM.getELFWriterInfo()) + return; + + std::string Buffer = MakeELF(F, I); + + jit_code_entry *JITCodeEntry = new jit_code_entry(); + JITCodeEntry->symfile_addr = Buffer.c_str(); + JITCodeEntry->symfile_size = Buffer.size(); + + // Add a mapping from F to the entry and buffer, so we can delete this + // info later. + FnMap[F] = std::make_pair<std::string, jit_code_entry*>(Buffer, JITCodeEntry); + + // Acquire the lock and do the registration. + { + MutexGuard locked(JITDebugLock); + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + + // Insert this entry at the head of the list. + JITCodeEntry->prev_entry = NULL; + jit_code_entry *NextEntry = __jit_debug_descriptor.first_entry; + JITCodeEntry->next_entry = NextEntry; + if (NextEntry != NULL) { + NextEntry->prev_entry = JITCodeEntry; + } + __jit_debug_descriptor.first_entry = JITCodeEntry; + __jit_debug_descriptor.relevant_entry = JITCodeEntry; + __jit_debug_register_code(); + } +} + +void JITDebugRegisterer::UnregisterFunctionInternal( + RegisteredFunctionsMap::iterator I) { + jit_code_entry *JITCodeEntry = I->second.second; + + // Acquire the lock and do the unregistration. + { + MutexGuard locked(JITDebugLock); + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + + // Remove the jit_code_entry from the linked list. + jit_code_entry *PrevEntry = JITCodeEntry->prev_entry; + jit_code_entry *NextEntry = JITCodeEntry->next_entry; + if (NextEntry) { + NextEntry->prev_entry = PrevEntry; + } + if (PrevEntry) { + PrevEntry->next_entry = NextEntry; + } else { + assert(__jit_debug_descriptor.first_entry == JITCodeEntry); + __jit_debug_descriptor.first_entry = NextEntry; + } + + // Tell GDB which entry we removed, and unregister the code. + __jit_debug_descriptor.relevant_entry = JITCodeEntry; + __jit_debug_register_code(); + } + + // Free the ELF file in memory. + std::string &Buffer = I->second.first; + Buffer.clear(); +} + +void JITDebugRegisterer::UnregisterFunction(const Function *F) { + // TODO: Support non-ELF platforms. + if (!TM.getELFWriterInfo()) + return; + + RegisteredFunctionsMap::iterator I = FnMap.find(F); + if (I == FnMap.end()) return; + UnregisterFunctionInternal(I); + FnMap.erase(I); +} + +} // end namespace llvm diff --git a/lib/ExecutionEngine/JIT/JITDebugRegisterer.h b/lib/ExecutionEngine/JIT/JITDebugRegisterer.h new file mode 100644 index 0000000..dce506b --- /dev/null +++ b/lib/ExecutionEngine/JIT/JITDebugRegisterer.h @@ -0,0 +1,116 @@ +//===-- JITDebugRegisterer.h - Register debug symbols for JIT -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITDebugRegisterer object that is used by the JIT to +// register debug info with debuggers like GDB. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H +#define LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/DataTypes.h" +#include <string> + +// This must be kept in sync with gdb/gdb/jit.h . +extern "C" { + + typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN + } jit_actions_t; + + struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; + }; + + struct jit_descriptor { + uint32_t version; + // This should be jit_actions_t, but we want to be specific about the + // bit-width. + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; + }; + +} + +namespace llvm { + +class ELFSection; +class Function; +class TargetMachine; + + +/// This class encapsulates information we want to send to the debugger. +/// +struct DebugInfo { + uint8_t *FnStart; + uint8_t *FnEnd; + uint8_t *EhStart; + uint8_t *EhEnd; + + DebugInfo() : FnStart(0), FnEnd(0), EhStart(0), EhEnd(0) {} +}; + +typedef DenseMap< const Function*, std::pair<std::string, jit_code_entry*> > + RegisteredFunctionsMap; + +/// This class registers debug info for JITed code with an attached debugger. +/// Without proper debug info, GDB can't do things like source level debugging +/// or even produce a proper stack trace on linux-x86_64. To use this class, +/// whenever a function is JITed, create a DebugInfo struct and pass it to the +/// RegisterFunction method. The method will then do whatever is necessary to +/// inform the debugger about the JITed function. +class JITDebugRegisterer { + + TargetMachine &TM; + + /// FnMap - A map of functions that have been registered to the associated + /// temporary files. Used for cleanup. + RegisteredFunctionsMap FnMap; + + /// MakeELF - Builds the ELF file in memory and returns a std::string that + /// contains the ELF. + std::string MakeELF(const Function *F, DebugInfo &I); + +public: + JITDebugRegisterer(TargetMachine &tm); + + /// ~JITDebugRegisterer - Unregisters all code and frees symbol files. + /// + ~JITDebugRegisterer(); + + /// RegisterFunction - Register debug info for the given function with an + /// attached debugger. Clients must call UnregisterFunction on all + /// registered functions before deleting them to free the associated symbol + /// file and unregister it from the debugger. + void RegisterFunction(const Function *F, DebugInfo &I); + + /// UnregisterFunction - Unregister the debug info for the given function + /// from the debugger and free associated memory. + void UnregisterFunction(const Function *F); + +private: + /// UnregisterFunctionInternal - Unregister the debug info for the given + /// function from the debugger and delete any temporary files. The private + /// version of this method does not remove the function from FnMap so that it + /// can be called while iterating over FnMap. + void UnregisterFunctionInternal(RegisteredFunctionsMap::iterator I); + +}; + +} // end namespace llvm + +#endif // LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H diff --git a/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp b/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp index e101ef3..f2b28ad 100644 --- a/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITDwarfEmitter.cpp @@ -21,25 +21,27 @@ #include "llvm/CodeGen/MachineLocation.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/Target/TargetAsmInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetInstrInfo.h" #include "llvm/Target/TargetFrameInfo.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetRegisterInfo.h" - using namespace llvm; -JITDwarfEmitter::JITDwarfEmitter(JIT& theJit) : Jit(theJit) {} +JITDwarfEmitter::JITDwarfEmitter(JIT& theJit) : MMI(0), Jit(theJit) {} unsigned char* JITDwarfEmitter::EmitDwarfTable(MachineFunction& F, JITCodeEmitter& jce, unsigned char* StartFunction, - unsigned char* EndFunction) { + unsigned char* EndFunction, + unsigned char* &EHFramePtr) { + assert(MMI && "MachineModuleInfo not registered!"); + const TargetMachine& TM = F.getTarget(); TD = TM.getTargetData(); - needsIndirectEncoding = TM.getTargetAsmInfo()->getNeedsIndirectEncoding(); stackGrowthDirection = TM.getFrameInfo()->getStackGrowthDirection(); RI = TM.getRegisterInfo(); JCE = &jce; @@ -48,14 +50,13 @@ unsigned char* JITDwarfEmitter::EmitDwarfTable(MachineFunction& F, EndFunction); unsigned char* Result = 0; - unsigned char* EHFramePtr = 0; const std::vector<Function *> Personalities = MMI->getPersonalities(); EHFramePtr = EmitCommonEHFrame(Personalities[MMI->getPersonalityIndex()]); Result = EmitEHFrame(Personalities[MMI->getPersonalityIndex()], EHFramePtr, StartFunction, EndFunction, ExceptionTable); - + return Result; } @@ -106,11 +107,9 @@ JITDwarfEmitter::EmitFrameMoves(intptr_t BaseLabelPtr, JCE->emitULEB128Bytes(RI->getDwarfRegNum(Src.getReg(), true)); } - int Offset = -Src.getOffset(); - - JCE->emitULEB128Bytes(Offset); + JCE->emitULEB128Bytes(-Src.getOffset()); } else { - assert(0 && "Machine move no supported yet."); + llvm_unreachable("Machine move not supported yet."); } } else if (Src.isReg() && Src.getReg() == MachineLocation::VirtualFP) { @@ -118,7 +117,7 @@ JITDwarfEmitter::EmitFrameMoves(intptr_t BaseLabelPtr, JCE->emitByte(dwarf::DW_CFA_def_cfa_register); JCE->emitULEB128Bytes(RI->getDwarfRegNum(Dst.getReg(), true)); } else { - assert(0 && "Machine move no supported yet."); + llvm_unreachable("Machine move not supported yet."); } } else { unsigned Reg = RI->getDwarfRegNum(Src.getReg(), true); @@ -209,6 +208,8 @@ struct CallSiteEntry { unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, unsigned char* StartFunction, unsigned char* EndFunction) const { + assert(MMI && "MachineModuleInfo not registered!"); + // Map all labels and get rid of any dead landing pads. MMI->TidyLandingPads(); @@ -241,7 +242,7 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, for(std::vector<unsigned>::const_iterator I = FilterIds.begin(), E = FilterIds.end(); I != E; ++I) { FilterOffsets.push_back(Offset); - Offset -= TargetAsmInfo::getULEB128Size(*I); + Offset -= MCAsmInfo::getULEB128Size(*I); } // Compute the actions table and gather the first action index for each @@ -266,10 +267,10 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, const unsigned SizePrevIds = LandingPads[i-1]->TypeIds.size(); assert(Actions.size()); PrevAction = &Actions.back(); - SizeAction = TargetAsmInfo::getSLEB128Size(PrevAction->NextAction) + - TargetAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); + SizeAction = MCAsmInfo::getSLEB128Size(PrevAction->NextAction) + + MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); for (unsigned j = NumShared; j != SizePrevIds; ++j) { - SizeAction -= TargetAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); + SizeAction -= MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); SizeAction += -PrevAction->NextAction; PrevAction = PrevAction->Previous; } @@ -280,10 +281,10 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, int TypeID = TypeIds[I]; assert(-1-TypeID < (int)FilterOffsets.size() && "Unknown filter id!"); int ValueForTypeID = TypeID < 0 ? FilterOffsets[-1 - TypeID] : TypeID; - unsigned SizeTypeID = TargetAsmInfo::getSLEB128Size(ValueForTypeID); + unsigned SizeTypeID = MCAsmInfo::getSLEB128Size(ValueForTypeID); int NextAction = SizeAction ? -(SizeAction + SizeTypeID) : 0; - SizeAction = SizeTypeID + TargetAsmInfo::getSLEB128Size(NextAction); + SizeAction = SizeTypeID + MCAsmInfo::getSLEB128Size(NextAction); SizeSiteActions += SizeAction; ActionEntry Action = {ValueForTypeID, NextAction, PrevAction}; @@ -386,29 +387,19 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, sizeof(int32_t) + // Site length. sizeof(int32_t)); // Landing pad. for (unsigned i = 0, e = CallSites.size(); i < e; ++i) - SizeSites += TargetAsmInfo::getULEB128Size(CallSites[i].Action); + SizeSites += MCAsmInfo::getULEB128Size(CallSites[i].Action); unsigned SizeTypes = TypeInfos.size() * TD->getPointerSize(); unsigned TypeOffset = sizeof(int8_t) + // Call site format // Call-site table length - TargetAsmInfo::getULEB128Size(SizeSites) + + MCAsmInfo::getULEB128Size(SizeSites) + SizeSites + SizeActions + SizeTypes; - unsigned TotalSize = sizeof(int8_t) + // LPStart format - sizeof(int8_t) + // TType format - TargetAsmInfo::getULEB128Size(TypeOffset) + // TType base offset - TypeOffset; - - unsigned SizeAlign = (4 - TotalSize) & 3; - // Begin the exception table. - JCE->emitAlignment(4); - for (unsigned i = 0; i != SizeAlign; ++i) { - JCE->emitByte(0); - // Asm->EOL("Padding"); - } - + JCE->emitAlignmentWithFill(4, 0); + // Asm->EOL("Padding"); + unsigned char* DwarfExceptionTable = (unsigned char*)JCE->getCurrentPCValue(); // Emit the header. @@ -475,11 +466,10 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, GlobalVariable *GV = TypeInfos[M - 1]; if (GV) { - if (TD->getPointerSize() == sizeof(int32_t)) { + if (TD->getPointerSize() == sizeof(int32_t)) JCE->emitInt32((intptr_t)Jit.getOrEmitGlobalVariable(GV)); - } else { + else JCE->emitInt64((intptr_t)Jit.getOrEmitGlobalVariable(GV)); - } } else { if (TD->getPointerSize() == sizeof(int32_t)) JCE->emitInt32(0); @@ -495,8 +485,8 @@ unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, JCE->emitULEB128Bytes(TypeID); //Asm->EOL("Filter TypeInfo index"); } - - JCE->emitAlignment(4); + + JCE->emitAlignmentWithFill(4, 0); return DwarfExceptionTable; } @@ -517,7 +507,7 @@ JITDwarfEmitter::EmitCommonEHFrame(const Function* Personality) const { JCE->emitULEB128Bytes(1); JCE->emitSLEB128Bytes(stackGrowth); JCE->emitByte(RI->getDwarfRegNum(RI->getRARegister(), true)); - + if (Personality) { // Augmentation Size: 3 small ULEBs of one byte each, and the personality // function which size is PointerSize. @@ -533,10 +523,9 @@ JITDwarfEmitter::EmitCommonEHFrame(const Function* Personality) const { JCE->emitByte(dwarf::DW_EH_PE_sdata8); JCE->emitInt64(((intptr_t)Jit.getPointerToGlobal(Personality))); } - + JCE->emitULEB128Bytes(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4); JCE->emitULEB128Bytes(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4); - } else { JCE->emitULEB128Bytes(1); JCE->emitULEB128Bytes(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4); @@ -545,11 +534,12 @@ JITDwarfEmitter::EmitCommonEHFrame(const Function* Personality) const { std::vector<MachineMove> Moves; RI->getInitialFrameState(Moves); EmitFrameMoves(0, Moves); - JCE->emitAlignment(PointerSize); - - JCE->emitInt32At((uintptr_t*)StartCommonPtr, - (uintptr_t)((unsigned char*)JCE->getCurrentPCValue() - - FrameCommonBeginPtr)); + + JCE->emitAlignmentWithFill(PointerSize, dwarf::DW_CFA_nop); + + JCE->emitInt32At((uintptr_t*)StartCommonPtr, + (uintptr_t)((unsigned char*)JCE->getCurrentPCValue() - + FrameCommonBeginPtr)); return StartCommonPtr; } @@ -574,13 +564,19 @@ JITDwarfEmitter::EmitEHFrame(const Function* Personality, // If there is a personality and landing pads then point to the language // specific data area in the exception table. - if (MMI->getPersonalityIndex()) { - JCE->emitULEB128Bytes(4); + if (Personality) { + JCE->emitULEB128Bytes(PointerSize == 4 ? 4 : 8); - if (!MMI->getLandingPads().empty()) { - JCE->emitInt32(ExceptionTable - (unsigned char*)JCE->getCurrentPCValue()); + if (PointerSize == 4) { + if (!MMI->getLandingPads().empty()) + JCE->emitInt32(ExceptionTable-(unsigned char*)JCE->getCurrentPCValue()); + else + JCE->emitInt32((int)0); } else { - JCE->emitInt32((int)0); + if (!MMI->getLandingPads().empty()) + JCE->emitInt64(ExceptionTable-(unsigned char*)JCE->getCurrentPCValue()); + else + JCE->emitInt64((int)0); } } else { JCE->emitULEB128Bytes(0); @@ -589,14 +585,14 @@ JITDwarfEmitter::EmitEHFrame(const Function* Personality, // Indicate locations of function specific callee saved registers in // frame. EmitFrameMoves((intptr_t)StartFunction, MMI->getFrameMoves()); - - JCE->emitAlignment(PointerSize); - + + JCE->emitAlignmentWithFill(PointerSize, dwarf::DW_CFA_nop); + // Indicate the size of the table - JCE->emitInt32At((uintptr_t*)StartEHPtr, - (uintptr_t)((unsigned char*)JCE->getCurrentPCValue() - - StartEHPtr)); - + JCE->emitInt32At((uintptr_t*)StartEHPtr, + (uintptr_t)((unsigned char*)JCE->getCurrentPCValue() - + StartEHPtr)); + // Double zeroes for the unwind runtime if (PointerSize == 8) { JCE->emitInt64(0); @@ -605,7 +601,6 @@ JITDwarfEmitter::EmitEHFrame(const Function* Personality, JCE->emitInt32(0); JCE->emitInt32(0); } - return StartEHPtr; } @@ -616,7 +611,6 @@ unsigned JITDwarfEmitter::GetDwarfTableSizeInBytes(MachineFunction& F, unsigned char* EndFunction) { const TargetMachine& TM = F.getTarget(); TD = TM.getTargetData(); - needsIndirectEncoding = TM.getTargetAsmInfo()->getNeedsIndirectEncoding(); stackGrowthDirection = TM.getFrameInfo()->getStackGrowthDirection(); RI = TM.getRegisterInfo(); JCE = &jce; @@ -630,7 +624,7 @@ unsigned JITDwarfEmitter::GetDwarfTableSizeInBytes(MachineFunction& F, FinalSize += GetEHFrameSizeInBytes(Personalities[MMI->getPersonalityIndex()], StartFunction); - + return FinalSize; } @@ -653,11 +647,11 @@ JITDwarfEmitter::GetEHFrameSizeInBytes(const Function* Personality, FinalSize += 3 * PointerSize; // If there is a personality and landing pads then point to the language // specific data area in the exception table. - if (MMI->getPersonalityIndex()) { - FinalSize += TargetAsmInfo::getULEB128Size(4); + if (Personality) { + FinalSize += MCAsmInfo::getULEB128Size(4); FinalSize += PointerSize; } else { - FinalSize += TargetAsmInfo::getULEB128Size(0); + FinalSize += MCAsmInfo::getULEB128Size(0); } // Indicate locations of function specific callee saved registers in @@ -685,24 +679,24 @@ unsigned JITDwarfEmitter::GetCommonEHFrameSizeInBytes(const Function* Personalit FinalSize += 4; FinalSize += 1; FinalSize += Personality ? 5 : 3; // "zPLR" or "zR" - FinalSize += TargetAsmInfo::getULEB128Size(1); - FinalSize += TargetAsmInfo::getSLEB128Size(stackGrowth); + FinalSize += MCAsmInfo::getULEB128Size(1); + FinalSize += MCAsmInfo::getSLEB128Size(stackGrowth); FinalSize += 1; if (Personality) { - FinalSize += TargetAsmInfo::getULEB128Size(7); + FinalSize += MCAsmInfo::getULEB128Size(7); // Encoding FinalSize+= 1; //Personality FinalSize += PointerSize; - FinalSize += TargetAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); - FinalSize += TargetAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); + FinalSize += MCAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); + FinalSize += MCAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); } else { - FinalSize += TargetAsmInfo::getULEB128Size(1); - FinalSize += TargetAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); + FinalSize += MCAsmInfo::getULEB128Size(1); + FinalSize += MCAsmInfo::getULEB128Size(dwarf::DW_EH_PE_pcrel); } std::vector<MachineMove> Moves; @@ -754,23 +748,23 @@ JITDwarfEmitter::GetFrameMovesSizeInBytes(intptr_t BaseLabelPtr, } else { ++FinalSize; unsigned RegNum = RI->getDwarfRegNum(Src.getReg(), true); - FinalSize += TargetAsmInfo::getULEB128Size(RegNum); + FinalSize += MCAsmInfo::getULEB128Size(RegNum); } int Offset = -Src.getOffset(); - FinalSize += TargetAsmInfo::getULEB128Size(Offset); + FinalSize += MCAsmInfo::getULEB128Size(Offset); } else { - assert(0 && "Machine move no supported yet."); + llvm_unreachable("Machine move no supported yet."); } } else if (Src.isReg() && Src.getReg() == MachineLocation::VirtualFP) { if (Dst.isReg()) { ++FinalSize; unsigned RegNum = RI->getDwarfRegNum(Dst.getReg(), true); - FinalSize += TargetAsmInfo::getULEB128Size(RegNum); + FinalSize += MCAsmInfo::getULEB128Size(RegNum); } else { - assert(0 && "Machine move no supported yet."); + llvm_unreachable("Machine move no supported yet."); } } else { unsigned Reg = RI->getDwarfRegNum(Src.getReg(), true); @@ -778,15 +772,15 @@ JITDwarfEmitter::GetFrameMovesSizeInBytes(intptr_t BaseLabelPtr, if (Offset < 0) { ++FinalSize; - FinalSize += TargetAsmInfo::getULEB128Size(Reg); - FinalSize += TargetAsmInfo::getSLEB128Size(Offset); + FinalSize += MCAsmInfo::getULEB128Size(Reg); + FinalSize += MCAsmInfo::getSLEB128Size(Offset); } else if (Reg < 64) { ++FinalSize; - FinalSize += TargetAsmInfo::getULEB128Size(Offset); + FinalSize += MCAsmInfo::getULEB128Size(Offset); } else { ++FinalSize; - FinalSize += TargetAsmInfo::getULEB128Size(Reg); - FinalSize += TargetAsmInfo::getULEB128Size(Offset); + FinalSize += MCAsmInfo::getULEB128Size(Reg); + FinalSize += MCAsmInfo::getULEB128Size(Offset); } } } @@ -829,7 +823,7 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { for(std::vector<unsigned>::const_iterator I = FilterIds.begin(), E = FilterIds.end(); I != E; ++I) { FilterOffsets.push_back(Offset); - Offset -= TargetAsmInfo::getULEB128Size(*I); + Offset -= MCAsmInfo::getULEB128Size(*I); } // Compute the actions table and gather the first action index for each @@ -854,10 +848,10 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { const unsigned SizePrevIds = LandingPads[i-1]->TypeIds.size(); assert(Actions.size()); PrevAction = &Actions.back(); - SizeAction = TargetAsmInfo::getSLEB128Size(PrevAction->NextAction) + - TargetAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); + SizeAction = MCAsmInfo::getSLEB128Size(PrevAction->NextAction) + + MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); for (unsigned j = NumShared; j != SizePrevIds; ++j) { - SizeAction -= TargetAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); + SizeAction -= MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); SizeAction += -PrevAction->NextAction; PrevAction = PrevAction->Previous; } @@ -868,10 +862,10 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { int TypeID = TypeIds[I]; assert(-1-TypeID < (int)FilterOffsets.size() && "Unknown filter id!"); int ValueForTypeID = TypeID < 0 ? FilterOffsets[-1 - TypeID] : TypeID; - unsigned SizeTypeID = TargetAsmInfo::getSLEB128Size(ValueForTypeID); + unsigned SizeTypeID = MCAsmInfo::getSLEB128Size(ValueForTypeID); int NextAction = SizeAction ? -(SizeAction + SizeTypeID) : 0; - SizeAction = SizeTypeID + TargetAsmInfo::getSLEB128Size(NextAction); + SizeAction = SizeTypeID + MCAsmInfo::getSLEB128Size(NextAction); SizeSiteActions += SizeAction; ActionEntry Action = {ValueForTypeID, NextAction, PrevAction}; @@ -974,18 +968,18 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { sizeof(int32_t) + // Site length. sizeof(int32_t)); // Landing pad. for (unsigned i = 0, e = CallSites.size(); i < e; ++i) - SizeSites += TargetAsmInfo::getULEB128Size(CallSites[i].Action); + SizeSites += MCAsmInfo::getULEB128Size(CallSites[i].Action); unsigned SizeTypes = TypeInfos.size() * TD->getPointerSize(); unsigned TypeOffset = sizeof(int8_t) + // Call site format // Call-site table length - TargetAsmInfo::getULEB128Size(SizeSites) + + MCAsmInfo::getULEB128Size(SizeSites) + SizeSites + SizeActions + SizeTypes; unsigned TotalSize = sizeof(int8_t) + // LPStart format sizeof(int8_t) + // TType format - TargetAsmInfo::getULEB128Size(TypeOffset) + // TType base offset + MCAsmInfo::getULEB128Size(TypeOffset) + // TType base offset TypeOffset; unsigned SizeAlign = (4 - TotalSize) & 3; @@ -1023,7 +1017,7 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { // Asm->EOL("Landing pad"); FinalSize += PointerSize; - FinalSize += TargetAsmInfo::getULEB128Size(S.Action); + FinalSize += MCAsmInfo::getULEB128Size(S.Action); // Asm->EOL("Action"); } @@ -1032,9 +1026,9 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { ActionEntry &Action = Actions[I]; //Asm->EOL("TypeInfo index"); - FinalSize += TargetAsmInfo::getSLEB128Size(Action.ValueForTypeID); + FinalSize += MCAsmInfo::getSLEB128Size(Action.ValueForTypeID); //Asm->EOL("Next action"); - FinalSize += TargetAsmInfo::getSLEB128Size(Action.NextAction); + FinalSize += MCAsmInfo::getSLEB128Size(Action.NextAction); } // Emit the type ids. @@ -1046,7 +1040,7 @@ JITDwarfEmitter::GetExceptionTableSizeInBytes(MachineFunction* MF) const { // Emit the filter typeids. for (unsigned j = 0, M = FilterIds.size(); j < M; ++j) { unsigned TypeID = FilterIds[j]; - FinalSize += TargetAsmInfo::getULEB128Size(TypeID); + FinalSize += MCAsmInfo::getULEB128Size(TypeID); //Asm->EOL("Filter TypeInfo index"); } diff --git a/lib/ExecutionEngine/JIT/JITDwarfEmitter.h b/lib/ExecutionEngine/JIT/JITDwarfEmitter.h index 9120ed4..e627550 100644 --- a/lib/ExecutionEngine/JIT/JITDwarfEmitter.h +++ b/lib/ExecutionEngine/JIT/JITDwarfEmitter.h @@ -32,7 +32,6 @@ class JITDwarfEmitter { const TargetRegisterInfo* RI; MachineModuleInfo* MMI; JIT& Jit; - bool needsIndirectEncoding; bool stackGrowthDirection; unsigned char* EmitExceptionTable(MachineFunction* MF, @@ -68,7 +67,8 @@ public: unsigned char* EmitDwarfTable(MachineFunction& F, JITCodeEmitter& JCE, unsigned char* StartFunction, - unsigned char* EndFunction); + unsigned char* EndFunction, + unsigned char* &EHFramePtr); unsigned GetDwarfTableSizeInBytes(MachineFunction& F, diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 8fe7ab8..eacd9f9 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -14,7 +14,9 @@ #define DEBUG_TYPE "jit" #include "JIT.h" +#include "JITDebugRegisterer.h" #include "JITDwarfEmitter.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/Constants.h" #include "llvm/Module.h" #include "llvm/DerivedTypes.h" @@ -33,8 +35,10 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MutexGuard.h" #include "llvm/Support/ValueHandle.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/System/Disassembler.h" #include "llvm/System/Memory.h" #include "llvm/Target/TargetInstrInfo.h" @@ -49,6 +53,7 @@ using namespace llvm; STATISTIC(NumBytes, "Number of bytes of machine code compiled"); STATISTIC(NumRelos, "Number of relocations applied"); +STATISTIC(NumRetries, "Number of retries with more memory"); static JIT *TheJIT = 0; @@ -59,7 +64,7 @@ namespace { class JITResolverState { public: typedef std::map<AssertingVH<Function>, void*> FunctionToStubMapTy; - typedef std::map<void*, Function*> StubToFunctionMapTy; + typedef std::map<void*, AssertingVH<Function> > StubToFunctionMapTy; typedef std::map<AssertingVH<GlobalValue>, void*> GlobalToIndirectSymMapTy; private: /// FunctionToStubMap - Keep track of the stub created for a particular @@ -193,9 +198,9 @@ void *JITResolver::getFunctionStub(Function *F) { // Call the lazy resolver function unless we are JIT'ing non-lazily, in which // case we must resolve the symbol now. - void *Actual = TheJIT->isLazyCompilationDisabled() + void *Actual = TheJIT->isLazyCompilationDisabled() ? (void *)0 : (void *)(intptr_t)LazyResolverFn; - + // If this is an external declaration, attempt to resolve the address now // to place in the stub. if (F->isDeclaration() && !F->hasNotBeenReadFromBitcode()) { @@ -220,20 +225,20 @@ void *JITResolver::getFunctionStub(Function *F) { TheJIT->updateGlobalMapping(F, Stub); } - DOUT << "JIT: Stub emitted at [" << Stub << "] for function '" - << F->getName() << "'\n"; + DEBUG(errs() << "JIT: Stub emitted at [" << Stub << "] for function '" + << F->getName() << "'\n"); // Finally, keep track of the stub-to-Function mapping so that the // JITCompilerFn knows which function to compile! state.getStubToFunctionMap(locked)[Stub] = F; - + // If we are JIT'ing non-lazily but need to call a function that does not // exist yet, add it to the JIT's work list so that we can fill in the stub // address later. if (!Actual && TheJIT->isLazyCompilationDisabled()) if (!F->isDeclaration() || F->hasNotBeenReadFromBitcode()) TheJIT->addPendingFunction(F); - + return Stub; } @@ -250,8 +255,8 @@ void *JITResolver::getGlobalValueIndirectSym(GlobalValue *GV, void *GVAddress) { IndirectSym = TheJIT->getJITInfo().emitGlobalValueIndirectSym(GV, GVAddress, *TheJIT->getCodeEmitter()); - DOUT << "JIT: Indirect symbol emitted at [" << IndirectSym << "] for GV '" - << GV->getName() << "'\n"; + DEBUG(errs() << "JIT: Indirect symbol emitted at [" << IndirectSym + << "] for GV '" << GV->getName() << "'\n"); return IndirectSym; } @@ -266,8 +271,8 @@ void *JITResolver::getExternalFunctionStub(void *FnAddr) { Stub = TheJIT->getJITInfo().emitFunctionStub(0, FnAddr, *TheJIT->getCodeEmitter()); - DOUT << "JIT: Stub emitted at [" << Stub - << "] for external function at '" << FnAddr << "'\n"; + DEBUG(errs() << "JIT: Stub emitted at [" << Stub + << "] for external function at '" << FnAddr << "'\n"); return Stub; } @@ -276,7 +281,8 @@ unsigned JITResolver::getGOTIndexForAddr(void* addr) { if (!idx) { idx = ++nextGOTIndex; revGOTMap[addr] = idx; - DOUT << "JIT: Adding GOT entry " << idx << " for addr [" << addr << "]\n"; + DEBUG(errs() << "JIT: Adding GOT entry " << idx << " for addr [" + << addr << "]\n"); } return idx; } @@ -373,9 +379,8 @@ void *JITResolver::JITCompilerFn(void *Stub) { // If lazy compilation is disabled, emit a useful error message and abort. if (TheJIT->isLazyCompilationDisabled()) { - cerr << "LLVM JIT requested to do lazy compilation of function '" - << F->getName() << "' when lazy compiles are disabled!\n"; - abort(); + llvm_report_error("LLVM JIT requested to do lazy compilation of function '" + + F->getName() + "' when lazy compiles are disabled!"); } // We might like to remove the stub from the StubToFunction map. @@ -385,9 +390,9 @@ void *JITResolver::JITCompilerFn(void *Stub) { // it needs to call. //JR.state.getStubToFunctionMap(locked).erase(I); - DOUT << "JIT: Lazily resolving function '" << F->getName() - << "' In stub ptr = " << Stub << " actual ptr = " - << ActualPtr << "\n"; + DEBUG(errs() << "JIT: Lazily resolving function '" << F->getName() + << "' In stub ptr = " << Stub << " actual ptr = " + << ActualPtr << "\n"); Result = TheJIT->getPointerToFunction(F); } @@ -424,6 +429,12 @@ namespace { // save BufferBegin/BufferEnd/CurBufferPtr here. uint8_t *SavedBufferBegin, *SavedBufferEnd, *SavedCurBufferPtr; + // When reattempting to JIT a function after running out of space, we store + // the estimated size of the function we're trying to JIT here, so we can + // ask the memory manager for at least this much space. When we + // successfully emit the function, we reset this back to zero. + uintptr_t SizeEstimate; + /// Relocations - These are the relocations that the function needs, as /// emitted. std::vector<MachineRelocation> Relocations; @@ -455,9 +466,12 @@ namespace { /// Resolver - This contains info about the currently resolved functions. JITResolver Resolver; - + /// DE - The dwarf emitter for the jit. - JITDwarfEmitter *DE; + OwningPtr<JITDwarfEmitter> DE; + + /// DR - The debug registerer for the jit. + OwningPtr<JITDebugRegisterer> DR; /// LabelLocations - This vector is a mapping from Label ID's to their /// address. @@ -472,7 +486,12 @@ namespace { // CurFn - The llvm function being emitted. Only valid during // finishFunction(). const Function *CurFn; - + + /// Information about emitted code, which is passed to the + /// JITEventListeners. This is reset in startFunction and used in + /// finishFunction. + JITEvent_EmittedFunctionDetails EmissionDetails; + // CurFnStubUses - For a given Function, a vector of stubs that it // references. This facilitates the JIT detecting that a stub is no // longer used, so that it may be deallocated. @@ -487,19 +506,26 @@ namespace { // in the JITResolver's ExternalFnToStubMap. StringMap<void *> ExtFnStubs; + DebugLocTuple PrevDLT; + public: - JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit), CurFn(0) { + JITEmitter(JIT &jit, JITMemoryManager *JMM, TargetMachine &TM) + : SizeEstimate(0), Resolver(jit), MMI(0), CurFn(0) { MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager(); if (jit.getJITInfo().needsGOT()) { MemMgr->AllocateGOT(); - DOUT << "JIT is managing a GOT\n"; + DEBUG(errs() << "JIT is managing a GOT\n"); } - if (ExceptionHandling) DE = new JITDwarfEmitter(jit); + if (DwarfExceptionHandling || JITEmitDebugInfo) { + DE.reset(new JITDwarfEmitter(jit)); + } + if (JITEmitDebugInfo) { + DR.reset(new JITDebugRegisterer(TM)); + } } ~JITEmitter() { delete MemMgr; - if (ExceptionHandling) delete DE; } /// classof - Methods for support type inquiry through isa, cast, and @@ -527,6 +553,11 @@ namespace { /// allocate a new one of the given size. virtual void *allocateSpace(uintptr_t Size, unsigned Alignment); + /// allocateGlobal - Allocate memory for a global. Unlike allocateSpace, + /// this method does not allocate memory in the current output buffer, + /// because a global may live longer than the current function. + virtual void *allocateGlobal(uintptr_t Size, unsigned Alignment); + virtual void addRelocation(const MachineRelocation &MR) { Relocations.push_back(MR); } @@ -535,8 +566,8 @@ namespace { if (MBBLocations.size() <= (unsigned)MBB->getNumber()) MBBLocations.resize((MBB->getNumber()+1)*2); MBBLocations[MBB->getNumber()] = getCurrentPCValue(); - DOUT << "JIT: Emitting BB" << MBB->getNumber() << " at [" - << (void*) getCurrentPCValue() << "]\n"; + DEBUG(errs() << "JIT: Emitting BB" << MBB->getNumber() << " at [" + << (void*) getCurrentPCValue() << "]\n"); } virtual uintptr_t getConstantPoolEntryAddress(unsigned Entry) const; @@ -548,9 +579,14 @@ namespace { return MBBLocations[MBB->getNumber()]; } + /// retryWithMoreMemory - Log a retry and deallocate all memory for the + /// given function. Increase the minimum allocation size so that we get + /// more memory next time. + void retryWithMoreMemory(MachineFunction &F); + /// deallocateMemForFunction - Deallocate all memory for the specified /// function body. - void deallocateMemForFunction(Function *F); + void deallocateMemForFunction(const Function *F); /// AddStubToCurrentFunction - Mark the current function being JIT'd as /// using the stub at the specified address. Allows @@ -561,6 +597,8 @@ namespace { /// MachineRelocations that reference external functions by name. const StringMap<void*> &getExternalFnStubs() const { return ExtFnStubs; } + virtual void processDebugLoc(DebugLoc DL, bool BeforePrintingInsn); + virtual void emitLabel(uint64_t LabelID) { if (LabelLocations.size() <= LabelID) LabelLocations.resize((LabelID+1)*2); @@ -575,14 +613,14 @@ namespace { virtual void setModuleInfo(MachineModuleInfo* Info) { MMI = Info; - if (ExceptionHandling) DE->setModuleInfo(Info); + if (DE.get()) DE->setModuleInfo(Info); } - void setMemoryExecutable(void) { + void setMemoryExecutable() { MemMgr->setMemoryExecutable(); } - JITMemoryManager *getMemMgr(void) const { return MemMgr; } + JITMemoryManager *getMemMgr() const { return MemMgr; } private: void *getPointerToGlobal(GlobalValue *GV, void *Reference, bool NoNeedStub); @@ -606,7 +644,7 @@ void *JITEmitter::getPointerToGlobal(GlobalValue *V, void *Reference, // If we have already compiled the function, return a pointer to its body. Function *F = cast<Function>(V); void *ResultPtr; - if (!DoesntNeedStub && !TheJIT->isLazyCompilationDisabled()) { + if (!DoesntNeedStub) { // Return the function stub if it's already created. ResultPtr = Resolver.getFunctionStubIfAvailable(F); if (ResultPtr) @@ -658,11 +696,8 @@ void *JITEmitter::getPointerToGVIndirectSym(GlobalValue *V, void *Reference, } void JITEmitter::AddStubToCurrentFunction(void *StubAddr) { - if (!TheJIT->areDlsymStubsEnabled()) - return; - assert(CurFn && "Stub added to current function, but current function is 0!"); - + SmallVectorImpl<void*> &StubsUsed = CurFnStubUses[CurFn]; StubsUsed.push_back(StubAddr); @@ -670,6 +705,23 @@ void JITEmitter::AddStubToCurrentFunction(void *StubAddr) { FnRefs.insert(CurFn); } +void JITEmitter::processDebugLoc(DebugLoc DL, bool BeforePrintingInsn) { + if (!DL.isUnknown()) { + DebugLocTuple CurDLT = EmissionDetails.MF->getDebugLocTuple(DL); + + if (BeforePrintingInsn) { + if (CurDLT.Scope != 0 && PrevDLT != CurDLT) { + JITEvent_EmittedFunctionDetails::LineStart NextLine; + NextLine.Address = getCurrentPCValue(); + NextLine.Loc = DL; + EmissionDetails.LineStarts.push_back(NextLine); + } + + PrevDLT = CurDLT; + } + } +} + static unsigned GetConstantPoolSizeInBytes(MachineConstantPool *MCP, const TargetData *TD) { const std::vector<MachineConstantPoolEntry> &Constants = MCP->getConstants(); @@ -713,7 +765,7 @@ unsigned JITEmitter::addSizeOfGlobal(const GlobalVariable *GV, unsigned Size) { size_t GVSize = (size_t)TheJIT->getTargetData()->getTypeAllocSize(ElTy); size_t GVAlign = (size_t)TheJIT->getTargetData()->getPreferredAlignment(GV); - DOUT << "JIT: Adding in size " << GVSize << " alignment " << GVAlign; + DEBUG(errs() << "JIT: Adding in size " << GVSize << " alignment " << GVAlign); DEBUG(GV->dump()); // Assume code section ends with worst possible alignment, so first // variable needs maximal padding. @@ -772,8 +824,10 @@ unsigned JITEmitter::addSizeOfGlobalsInConstantVal(const Constant *C, break; } default: { - cerr << "ConstantExpr not handled: " << *CE << "\n"; - abort(); + std::string msg; + raw_string_ostream Msg(msg); + Msg << "ConstantExpr not handled: " << *CE; + llvm_report_error(Msg.str()); } } } @@ -839,7 +893,7 @@ unsigned JITEmitter::GetSizeOfGlobalsInBytes(MachineFunction &MF) { } } } - DOUT << "JIT: About to look through initializers\n"; + DEBUG(errs() << "JIT: About to look through initializers\n"); // Look for more globals that are referenced only from initializers. // GVSet.end is computed each time because the set can grow as we go. for (SmallPtrSet<const GlobalVariable *, 8>::iterator I = GVSet.begin(); @@ -853,14 +907,14 @@ unsigned JITEmitter::GetSizeOfGlobalsInBytes(MachineFunction &MF) { } void JITEmitter::startFunction(MachineFunction &F) { - DOUT << "JIT: Starting CodeGen of Function " - << F.getFunction()->getName() << "\n"; + DEBUG(errs() << "JIT: Starting CodeGen of Function " + << F.getFunction()->getName() << "\n"); uintptr_t ActualSize = 0; // Set the memory writable, if it's not already MemMgr->setMemoryWritable(); if (MemMgr->NeedsExactSize()) { - DOUT << "JIT: ExactSize\n"; + DEBUG(errs() << "JIT: ExactSize\n"); const TargetInstrInfo* TII = F.getTarget().getInstrInfo(); MachineJumpTableInfo *MJTI = F.getJumpTableInfo(); MachineConstantPool *MCP = F.getConstantPool(); @@ -887,12 +941,15 @@ void JITEmitter::startFunction(MachineFunction &F) { // Add the function size ActualSize += TII->GetFunctionSizeInBytes(F); - DOUT << "JIT: ActualSize before globals " << ActualSize << "\n"; + DEBUG(errs() << "JIT: ActualSize before globals " << ActualSize << "\n"); // Add the size of the globals that will be allocated after this function. // These are all the ones referenced from this function that were not // previously allocated. ActualSize += GetSizeOfGlobalsInBytes(F); - DOUT << "JIT: ActualSize after globals " << ActualSize << "\n"; + DEBUG(errs() << "JIT: ActualSize after globals " << ActualSize << "\n"); + } else if (SizeEstimate > 0) { + // SizeEstimate will be non-zero on reallocation attempts. + ActualSize = SizeEstimate; } BufferBegin = CurBufferPtr = MemMgr->startFunctionBody(F.getFunction(), @@ -910,17 +967,22 @@ void JITEmitter::startFunction(MachineFunction &F) { TheJIT->updateGlobalMapping(F.getFunction(), CurBufferPtr); MBBLocations.clear(); + + EmissionDetails.MF = &F; + EmissionDetails.LineStarts.clear(); } bool JITEmitter::finishFunction(MachineFunction &F) { if (CurBufferPtr == BufferEnd) { - // FIXME: Allocate more space, then try again. - cerr << "JIT: Ran out of space for generated machine code!\n"; - abort(); + // We must call endFunctionBody before retrying, because + // deallocateMemForFunction requires it. + MemMgr->endFunctionBody(F.getFunction(), BufferBegin, CurBufferPtr); + retryWithMoreMemory(F); + return true; } - + emitJumpTableInfo(F.getJumpTableInfo()); - + // FnStart is the start of the text, not the start of the constant pool and // other per-function data. uint8_t *FnStart = @@ -941,8 +1003,8 @@ bool JITEmitter::finishFunction(MachineFunction &F) { if (MR.isExternalSymbol()) { ResultPtr = TheJIT->getPointerToNamedFunction(MR.getExternalSymbol(), false); - DOUT << "JIT: Map \'" << MR.getExternalSymbol() << "\' to [" - << ResultPtr << "]\n"; + DEBUG(errs() << "JIT: Map \'" << MR.getExternalSymbol() << "\' to [" + << ResultPtr << "]\n"); // If the target REALLY wants a stub for this function, emit it now. if (!MR.doesntNeedStub()) { @@ -983,9 +1045,9 @@ bool JITEmitter::finishFunction(MachineFunction &F) { unsigned idx = Resolver.getGOTIndexForAddr(ResultPtr); MR.setGOTIndex(idx); if (((void**)MemMgr->getGOTBase())[idx] != ResultPtr) { - DOUT << "JIT: GOT was out of date for " << ResultPtr - << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] - << "\n"; + DEBUG(errs() << "JIT: GOT was out of date for " << ResultPtr + << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] + << "\n"); ((void**)MemMgr->getGOTBase())[idx] = ResultPtr; } } @@ -1000,8 +1062,9 @@ bool JITEmitter::finishFunction(MachineFunction &F) { if (MemMgr->isManagingGOT()) { unsigned idx = Resolver.getGOTIndexForAddr((void*)BufferBegin); if (((void**)MemMgr->getGOTBase())[idx] != (void*)BufferBegin) { - DOUT << "JIT: GOT was out of date for " << (void*)BufferBegin - << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] << "\n"; + DEBUG(errs() << "JIT: GOT was out of date for " << (void*)BufferBegin + << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] + << "\n"); ((void**)MemMgr->getGOTBase())[idx] = (void*)BufferBegin; } } @@ -1011,9 +1074,12 @@ bool JITEmitter::finishFunction(MachineFunction &F) { MemMgr->endFunctionBody(F.getFunction(), BufferBegin, CurBufferPtr); if (CurBufferPtr == BufferEnd) { - // FIXME: Allocate more space, then try again. - cerr << "JIT: Ran out of space for generated machine code!\n"; - abort(); + retryWithMoreMemory(F); + return true; + } else { + // Now that we've succeeded in emitting the function, reset the + // SizeEstimate back down to zero. + SizeEstimate = 0; } BufferBegin = CurBufferPtr = 0; @@ -1022,14 +1088,13 @@ bool JITEmitter::finishFunction(MachineFunction &F) { // Invalidate the icache if necessary. sys::Memory::InvalidateInstructionCache(FnStart, FnEnd-FnStart); - JITEvent_EmittedFunctionDetails Details; TheJIT->NotifyFunctionEmitted(*F.getFunction(), FnStart, FnEnd-FnStart, - Details); + EmissionDetails); - DOUT << "JIT: Finished CodeGen of [" << (void*)FnStart - << "] Function: " << F.getFunction()->getName() - << ": " << (FnEnd-FnStart) << " bytes of text, " - << Relocations.size() << " relocations\n"; + DEBUG(errs() << "JIT: Finished CodeGen of [" << (void*)FnStart + << "] Function: " << F.getFunction()->getName() + << ": " << (FnEnd-FnStart) << " bytes of text, " + << Relocations.size() << " relocations\n"); Relocations.clear(); ConstPoolAddresses.clear(); @@ -1037,45 +1102,42 @@ bool JITEmitter::finishFunction(MachineFunction &F) { // Mark code region readable and executable if it's not so already. MemMgr->setMemoryExecutable(); -#ifndef NDEBUG - { + DEBUG( if (sys::hasDisassembler()) { - DOUT << "JIT: Disassembled code:\n"; - DOUT << sys::disassembleBuffer(FnStart, FnEnd-FnStart, (uintptr_t)FnStart); + errs() << "JIT: Disassembled code:\n"; + errs() << sys::disassembleBuffer(FnStart, FnEnd-FnStart, + (uintptr_t)FnStart); } else { - DOUT << "JIT: Binary code:\n"; - DOUT << std::hex; + errs() << "JIT: Binary code:\n"; uint8_t* q = FnStart; for (int i = 0; q < FnEnd; q += 4, ++i) { if (i == 4) i = 0; if (i == 0) - DOUT << "JIT: " << std::setw(8) << std::setfill('0') - << (long)(q - FnStart) << ": "; + errs() << "JIT: " << (long)(q - FnStart) << ": "; bool Done = false; for (int j = 3; j >= 0; --j) { if (q + j >= FnEnd) Done = true; else - DOUT << std::setw(2) << std::setfill('0') << (unsigned short)q[j]; + errs() << (unsigned short)q[j]; } if (Done) break; - DOUT << ' '; + errs() << ' '; if (i == 3) - DOUT << '\n'; + errs() << '\n'; } - DOUT << std::dec; - DOUT<< '\n'; + errs()<< '\n'; } - } -#endif - if (ExceptionHandling) { + ); + + if (DwarfExceptionHandling || JITEmitDebugInfo) { uintptr_t ActualSize = 0; SavedBufferBegin = BufferBegin; SavedBufferEnd = BufferEnd; SavedCurBufferPtr = CurBufferPtr; - + if (MemMgr->NeedsExactSize()) { ActualSize = DE->GetDwarfTableSizeInBytes(F, *this, FnStart, FnEnd); } @@ -1083,14 +1145,28 @@ bool JITEmitter::finishFunction(MachineFunction &F) { BufferBegin = CurBufferPtr = MemMgr->startExceptionTable(F.getFunction(), ActualSize); BufferEnd = BufferBegin+ActualSize; - uint8_t* FrameRegister = DE->EmitDwarfTable(F, *this, FnStart, FnEnd); + uint8_t *EhStart; + uint8_t *FrameRegister = DE->EmitDwarfTable(F, *this, FnStart, FnEnd, + EhStart); MemMgr->endExceptionTable(F.getFunction(), BufferBegin, CurBufferPtr, FrameRegister); + uint8_t *EhEnd = CurBufferPtr; BufferBegin = SavedBufferBegin; BufferEnd = SavedBufferEnd; CurBufferPtr = SavedCurBufferPtr; - TheJIT->RegisterTable(FrameRegister); + if (DwarfExceptionHandling) { + TheJIT->RegisterTable(FrameRegister); + } + + if (JITEmitDebugInfo) { + DebugInfo I; + I.FnStart = FnStart; + I.FnEnd = FnEnd; + I.EhStart = EhStart; + I.EhEnd = EhEnd; + DR->RegisterFunction(F.getFunction(), I); + } } if (MMI) @@ -1099,11 +1175,28 @@ bool JITEmitter::finishFunction(MachineFunction &F) { return false; } +void JITEmitter::retryWithMoreMemory(MachineFunction &F) { + DEBUG(errs() << "JIT: Ran out of space for native code. Reattempting.\n"); + Relocations.clear(); // Clear the old relocations or we'll reapply them. + ConstPoolAddresses.clear(); + ++NumRetries; + deallocateMemForFunction(F.getFunction()); + // Try again with at least twice as much free space. + SizeEstimate = (uintptr_t)(2 * (BufferEnd - BufferBegin)); +} + /// deallocateMemForFunction - Deallocate all memory for the specified /// function body. Also drop any references the function has to stubs. -void JITEmitter::deallocateMemForFunction(Function *F) { +void JITEmitter::deallocateMemForFunction(const Function *F) { MemMgr->deallocateMemForFunction(F); + // TODO: Do we need to unregister exception handling information from libgcc + // here? + + if (JITEmitDebugInfo) { + DR->UnregisterFunction(F); + } + // If the function did not reference any stubs, return. if (CurFnStubUses.find(F) == CurFnStubUses.end()) return; @@ -1125,7 +1218,7 @@ void JITEmitter::deallocateMemForFunction(Function *F) { // in the JITResolver. Were there a memory manager deallocateStub routine, // we could call that at this point too. if (FnRefs.empty()) { - DOUT << "\nJIT: Invalidated Stub at [" << Stub << "]\n"; + DEBUG(errs() << "\nJIT: Invalidated Stub at [" << Stub << "]\n"); StubFnRefs.erase(Stub); // Invalidate the stub. If it is a GV stub, update the JIT's global @@ -1161,6 +1254,11 @@ void* JITEmitter::allocateSpace(uintptr_t Size, unsigned Alignment) { return CurBufferPtr; } +void* JITEmitter::allocateGlobal(uintptr_t Size, unsigned Alignment) { + // Delegate this call through the memory manager. + return MemMgr->allocateGlobal(Size, Alignment); +} + void JITEmitter::emitConstantPool(MachineConstantPool *MCP) { if (TheJIT->getJITInfo().hasCustomConstantPool()) return; @@ -1175,8 +1273,8 @@ void JITEmitter::emitConstantPool(MachineConstantPool *MCP) { if (ConstantPoolBase == 0) return; // Buffer overflow. - DOUT << "JIT: Emitted constant pool at [" << ConstantPoolBase - << "] (size: " << Size << ", alignment: " << Align << ")\n"; + DEBUG(errs() << "JIT: Emitted constant pool at [" << ConstantPoolBase + << "] (size: " << Size << ", alignment: " << Align << ")\n"); // Initialize the memory for all of the constant pool entries. unsigned Offset = 0; @@ -1189,13 +1287,12 @@ void JITEmitter::emitConstantPool(MachineConstantPool *MCP) { ConstPoolAddresses.push_back(CAddr); if (CPE.isMachineConstantPoolEntry()) { // FIXME: add support to lower machine constant pool values into bytes! - cerr << "Initialize memory with machine specific constant pool entry" - << " has not been implemented!\n"; - abort(); + llvm_report_error("Initialize memory with machine specific constant pool" + "entry has not been implemented!"); } TheJIT->InitializeMemory(CPE.Val.ConstVal, (void*)CAddr); - DOUT << "JIT: CP" << i << " at [0x" - << std::hex << CAddr << std::dec << "]\n"; + DEBUG(errs() << "JIT: CP" << i << " at [0x"; + errs().write_hex(CAddr) << "]\n"); const Type *Ty = CPE.Val.ConstVal->getType(); Offset += TheJIT->getTargetData()->getTypeAllocSize(Ty); @@ -1322,8 +1419,9 @@ uintptr_t JITEmitter::getJumpTableEntryAddress(unsigned Index) const { // Public interface to this file //===----------------------------------------------------------------------===// -JITCodeEmitter *JIT::createEmitter(JIT &jit, JITMemoryManager *JMM) { - return new JITEmitter(jit, JMM); +JITCodeEmitter *JIT::createEmitter(JIT &jit, JITMemoryManager *JMM, + TargetMachine &tm) { + return new JITEmitter(jit, JMM, tm); } // getPointerToNamedFunction - This function is used as a global wrapper to @@ -1396,7 +1494,7 @@ void JIT::updateDlsymStubTable() { SmallVector<unsigned, 8> Offsets; for (unsigned i = 0; i != GVs.size(); ++i) { Offsets.push_back(offset); - offset += GVs[i]->getName().length() + 1; + offset += GVs[i]->getName().size() + 1; } for (StringMapConstIterator<void*> i = ExtFns.begin(), e = ExtFns.end(); i != e; ++i) { diff --git a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp index 70ccdcc..474843f 100644 --- a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp +++ b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp @@ -11,9 +11,16 @@ // //===----------------------------------------------------------------------===// -#include "llvm/GlobalValue.h" +#define DEBUG_TYPE "jit" #include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/GlobalValue.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/System/Memory.h" #include <map> #include <vector> @@ -24,6 +31,7 @@ #include <cstring> using namespace llvm; +STATISTIC(NumSlabs, "Number of slabs of memory allocated by the JIT"); JITMemoryManager::~JITMemoryManager() {} @@ -140,7 +148,7 @@ FreeRangeHeader *FreeRangeHeader::AllocateBlock() { /// FreeRangeHeader to allocate from. FreeRangeHeader *MemoryRangeHeader::FreeBlock(FreeRangeHeader *FreeList) { MemoryRangeHeader *FollowingBlock = &getBlockAfter(); - assert(ThisAllocated && "This block is already allocated!"); + assert(ThisAllocated && "This block is already free!"); assert(FollowingBlock->PrevAllocated && "Flags out of sync!"); FreeRangeHeader *FreeListToReturn = FreeList; @@ -243,67 +251,160 @@ TrimAllocationToSize(FreeRangeHeader *FreeList, uint64_t NewSize) { // Memory Block Implementation. //===----------------------------------------------------------------------===// -namespace { +namespace { + + class DefaultJITMemoryManager; + + class JITSlabAllocator : public SlabAllocator { + DefaultJITMemoryManager &JMM; + public: + JITSlabAllocator(DefaultJITMemoryManager &jmm) : JMM(jmm) { } + virtual ~JITSlabAllocator() { } + virtual MemSlab *Allocate(size_t Size); + virtual void Deallocate(MemSlab *Slab); + }; + /// DefaultJITMemoryManager - Manage memory for the JIT code generation. /// This splits a large block of MAP_NORESERVE'd memory into two /// sections, one for function stubs, one for the functions themselves. We /// have to do this because we may need to emit a function stub while in the /// middle of emitting a function, and we don't know how large the function we /// are emitting is. - class VISIBILITY_HIDDEN DefaultJITMemoryManager : public JITMemoryManager { - std::vector<sys::MemoryBlock> Blocks; // Memory blocks allocated by the JIT - FreeRangeHeader *FreeMemoryList; // Circular list of free blocks. - + class DefaultJITMemoryManager : public JITMemoryManager { + + // Whether to poison freed memory. + bool PoisonMemory; + + /// LastSlab - This points to the last slab allocated and is used as the + /// NearBlock parameter to AllocateRWX so that we can attempt to lay out all + /// stubs, data, and code contiguously in memory. In general, however, this + /// is not possible because the NearBlock parameter is ignored on Windows + /// platforms and even on Unix it works on a best-effort pasis. + sys::MemoryBlock LastSlab; + + // Memory slabs allocated by the JIT. We refer to them as slabs so we don't + // confuse them with the blocks of memory descibed above. + std::vector<sys::MemoryBlock> CodeSlabs; + JITSlabAllocator BumpSlabAllocator; + BumpPtrAllocator StubAllocator; + BumpPtrAllocator DataAllocator; + + // Circular list of free blocks. + FreeRangeHeader *FreeMemoryList; + // When emitting code into a memory block, this is the block. MemoryRangeHeader *CurBlock; - - uint8_t *CurStubPtr, *StubBase; + uint8_t *GOTBase; // Target Specific reserved memory void *DlsymTable; // Stub external symbol information - // Centralize memory block allocation. - sys::MemoryBlock getNewMemoryBlock(unsigned size); - std::map<const Function*, MemoryRangeHeader*> FunctionBlocks; std::map<const Function*, MemoryRangeHeader*> TableBlocks; public: DefaultJITMemoryManager(); ~DefaultJITMemoryManager(); + /// allocateNewSlab - Allocates a new MemoryBlock and remembers it as the + /// last slab it allocated, so that subsequent allocations follow it. + sys::MemoryBlock allocateNewSlab(size_t size); + + /// DefaultCodeSlabSize - When we have to go map more memory, we allocate at + /// least this much unless more is requested. + static const size_t DefaultCodeSlabSize; + + /// DefaultSlabSize - Allocate data into slabs of this size unless we get + /// an allocation above SizeThreshold. + static const size_t DefaultSlabSize; + + /// DefaultSizeThreshold - For any allocation larger than this threshold, we + /// should allocate a separate slab. + static const size_t DefaultSizeThreshold; + void AllocateGOT(); void SetDlsymTable(void *); - - uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment); - + + // Testing methods. + virtual bool CheckInvariants(std::string &ErrorStr); + size_t GetDefaultCodeSlabSize() { return DefaultCodeSlabSize; } + size_t GetDefaultDataSlabSize() { return DefaultSlabSize; } + size_t GetDefaultStubSlabSize() { return DefaultSlabSize; } + unsigned GetNumCodeSlabs() { return CodeSlabs.size(); } + unsigned GetNumDataSlabs() { return DataAllocator.GetNumSlabs(); } + unsigned GetNumStubSlabs() { return StubAllocator.GetNumSlabs(); } + /// startFunctionBody - When a function starts, allocate a block of free /// executable memory, returning a pointer to it and its actual size. uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) { - + FreeRangeHeader* candidateBlock = FreeMemoryList; FreeRangeHeader* head = FreeMemoryList; FreeRangeHeader* iter = head->Next; uintptr_t largest = candidateBlock->BlockSize; - + // Search for the largest free block while (iter != head) { - if (iter->BlockSize > largest) { - largest = iter->BlockSize; - candidateBlock = iter; - } - iter = iter->Next; + if (iter->BlockSize > largest) { + largest = iter->BlockSize; + candidateBlock = iter; + } + iter = iter->Next; } + + largest = largest - sizeof(MemoryRangeHeader); + // If this block isn't big enough for the allocation desired, allocate + // another block of memory and add it to the free list. + if (largest < ActualSize || + largest <= FreeRangeHeader::getMinBlockSize()) { + DEBUG(errs() << "JIT: Allocating another slab of memory for function."); + candidateBlock = allocateNewCodeSlab((size_t)ActualSize); + } + // Select this candidate block for allocation CurBlock = candidateBlock; // Allocate the entire memory block. FreeMemoryList = candidateBlock->AllocateBlock(); - ActualSize = CurBlock->BlockSize-sizeof(MemoryRangeHeader); - return (uint8_t *)(CurBlock+1); + ActualSize = CurBlock->BlockSize - sizeof(MemoryRangeHeader); + return (uint8_t *)(CurBlock + 1); } - + + /// allocateNewCodeSlab - Helper method to allocate a new slab of code + /// memory from the OS and add it to the free list. Returns the new + /// FreeRangeHeader at the base of the slab. + FreeRangeHeader *allocateNewCodeSlab(size_t MinSize) { + // If the user needs at least MinSize free memory, then we account for + // two MemoryRangeHeaders: the one in the user's block, and the one at the + // end of the slab. + size_t PaddedMin = MinSize + 2 * sizeof(MemoryRangeHeader); + size_t SlabSize = std::max(DefaultCodeSlabSize, PaddedMin); + sys::MemoryBlock B = allocateNewSlab(SlabSize); + CodeSlabs.push_back(B); + char *MemBase = (char*)(B.base()); + + // Put a tiny allocated block at the end of the memory chunk, so when + // FreeBlock calls getBlockAfter it doesn't fall off the end. + MemoryRangeHeader *EndBlock = + (MemoryRangeHeader*)(MemBase + B.size()) - 1; + EndBlock->ThisAllocated = 1; + EndBlock->PrevAllocated = 0; + EndBlock->BlockSize = sizeof(MemoryRangeHeader); + + // Start out with a vast new block of free memory. + FreeRangeHeader *NewBlock = (FreeRangeHeader*)MemBase; + NewBlock->ThisAllocated = 0; + // Make sure getFreeBlockBefore doesn't look into unmapped memory. + NewBlock->PrevAllocated = 1; + NewBlock->BlockSize = (uintptr_t)EndBlock - (uintptr_t)NewBlock; + NewBlock->SetEndOfBlockSizeMarker(); + NewBlock->AddToFreeList(FreeMemoryList); + + assert(NewBlock->BlockSize - sizeof(MemoryRangeHeader) >= MinSize && + "The block was too small!"); + return NewBlock; + } + /// endFunctionBody - The function F is now allocated, and takes the memory /// in the range [FunctionStart,FunctionEnd). void endFunctionBody(const Function *F, uint8_t *FunctionStart, @@ -319,12 +420,13 @@ namespace { FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize); } - /// allocateSpace - Allocate a memory block of the given size. + /// allocateSpace - Allocate a memory block of the given size. This method + /// cannot be called between calls to startFunctionBody and endFunctionBody. uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { CurBlock = FreeMemoryList; FreeMemoryList = FreeMemoryList->AllocateBlock(); - uint8_t *result = (uint8_t *)CurBlock+1; + uint8_t *result = (uint8_t *)(CurBlock + 1); if (Alignment == 0) Alignment = 1; result = (uint8_t*)(((intptr_t)result+Alignment-1) & @@ -336,6 +438,17 @@ namespace { return result; } + /// allocateStub - Allocate memory for a function stub. + uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment) { + return (uint8_t*)StubAllocator.Allocate(StubSize, Alignment); + } + + /// allocateGlobal - Allocate memory for a global. + uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { + return (uint8_t*)DataAllocator.Allocate(Size, Alignment); + } + /// startExceptionTable - Use startFunctionBody to allocate memory for the /// function's exception table. uint8_t* startExceptionTable(const Function* F, uintptr_t &ActualSize) { @@ -375,12 +488,12 @@ namespace { // Find the block that is allocated for this function. MemoryRangeHeader *MemRange = I->second; assert(MemRange->ThisAllocated && "Block isn't allocated!"); - + // Fill the buffer with garbage! -#ifndef NDEBUG - memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange)); -#endif - + if (PoisonMemory) { + memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange)); + } + // Free the memory. FreeMemoryList = MemRange->FreeBlock(FreeMemoryList); @@ -393,12 +506,12 @@ namespace { // Find the block that is allocated for this function. MemRange = I->second; assert(MemRange->ThisAllocated && "Block isn't allocated!"); - + // Fill the buffer with garbage! -#ifndef NDEBUG - memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange)); -#endif - + if (PoisonMemory) { + memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange)); + } + // Free the memory. FreeMemoryList = MemRange->FreeBlock(FreeMemoryList); @@ -408,36 +521,57 @@ namespace { /// setMemoryWritable - When code generation is in progress, /// the code pages may need permissions changed. - void setMemoryWritable(void) + void setMemoryWritable() { - for (unsigned i = 0, e = Blocks.size(); i != e; ++i) - sys::Memory::setWritable(Blocks[i]); + for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) + sys::Memory::setWritable(CodeSlabs[i]); } /// setMemoryExecutable - When code generation is done and we're ready to /// start execution, the code pages may need permissions changed. - void setMemoryExecutable(void) + void setMemoryExecutable() { - for (unsigned i = 0, e = Blocks.size(); i != e; ++i) - sys::Memory::setExecutable(Blocks[i]); + for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) + sys::Memory::setExecutable(CodeSlabs[i]); + } + + /// setPoisonMemory - Controls whether we write garbage over freed memory. + /// + void setPoisonMemory(bool poison) { + PoisonMemory = poison; } }; } -DefaultJITMemoryManager::DefaultJITMemoryManager() { - // Allocate a 16M block of memory for functions. -#if defined(__APPLE__) && defined(__arm__) - sys::MemoryBlock MemBlock = getNewMemoryBlock(4 << 20); +MemSlab *JITSlabAllocator::Allocate(size_t Size) { + sys::MemoryBlock B = JMM.allocateNewSlab(Size); + MemSlab *Slab = (MemSlab*)B.base(); + Slab->Size = B.size(); + Slab->NextPtr = 0; + return Slab; +} + +void JITSlabAllocator::Deallocate(MemSlab *Slab) { + sys::MemoryBlock B(Slab, Slab->Size); + sys::Memory::ReleaseRWX(B); +} + +DefaultJITMemoryManager::DefaultJITMemoryManager() + : +#ifdef NDEBUG + PoisonMemory(false), #else - sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20); + PoisonMemory(true), #endif + LastSlab(0, 0), + BumpSlabAllocator(*this), + StubAllocator(DefaultSlabSize, DefaultSizeThreshold, BumpSlabAllocator), + DataAllocator(DefaultSlabSize, DefaultSizeThreshold, BumpSlabAllocator) { - uint8_t *MemBase = static_cast<uint8_t*>(MemBlock.base()); + // Allocate space for code. + sys::MemoryBlock MemBlock = allocateNewSlab(DefaultCodeSlabSize); + CodeSlabs.push_back(MemBlock); + uint8_t *MemBase = (uint8_t*)MemBlock.base(); - // Allocate stubs backwards from the base, allocate functions forward - // from the base. - StubBase = MemBase; - CurStubPtr = MemBase + 512*1024; // Use 512k for stubs, working backwards. - // We set up the memory chunk with 4 mem regions, like this: // [ START // [ Free #0 ] -> Large space to allocate functions from. @@ -453,7 +587,7 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() { MemoryRangeHeader *Mem3 = (MemoryRangeHeader*)(MemBase+MemBlock.size())-1; Mem3->ThisAllocated = 1; Mem3->PrevAllocated = 0; - Mem3->BlockSize = 0; + Mem3->BlockSize = sizeof(MemoryRangeHeader); /// Add a tiny free region so that the free list always has one entry. FreeRangeHeader *Mem2 = @@ -469,12 +603,12 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() { MemoryRangeHeader *Mem1 = (MemoryRangeHeader*)Mem2-1; Mem1->ThisAllocated = 1; Mem1->PrevAllocated = 0; - Mem1->BlockSize = (char*)Mem2 - (char*)Mem1; + Mem1->BlockSize = sizeof(MemoryRangeHeader); // Add a FreeRangeHeader to the start of the function body region, indicating // that the space is free. Mark the previous block allocated so we never look // at it. - FreeRangeHeader *Mem0 = (FreeRangeHeader*)CurStubPtr; + FreeRangeHeader *Mem0 = (FreeRangeHeader*)MemBase; Mem0->ThisAllocated = 0; Mem0->PrevAllocated = 1; Mem0->BlockSize = (char*)Mem1-(char*)Mem0; @@ -499,43 +633,128 @@ void DefaultJITMemoryManager::SetDlsymTable(void *ptr) { } DefaultJITMemoryManager::~DefaultJITMemoryManager() { - for (unsigned i = 0, e = Blocks.size(); i != e; ++i) - sys::Memory::ReleaseRWX(Blocks[i]); - - delete[] GOTBase; - Blocks.clear(); -} + for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) + sys::Memory::ReleaseRWX(CodeSlabs[i]); -uint8_t *DefaultJITMemoryManager::allocateStub(const GlobalValue* F, - unsigned StubSize, - unsigned Alignment) { - CurStubPtr -= StubSize; - CurStubPtr = (uint8_t*)(((intptr_t)CurStubPtr) & - ~(intptr_t)(Alignment-1)); - if (CurStubPtr < StubBase) { - // FIXME: allocate a new block - fprintf(stderr, "JIT ran out of memory for function stubs!\n"); - abort(); - } - return CurStubPtr; + delete[] GOTBase; } -sys::MemoryBlock DefaultJITMemoryManager::getNewMemoryBlock(unsigned size) { +sys::MemoryBlock DefaultJITMemoryManager::allocateNewSlab(size_t size) { // Allocate a new block close to the last one. - const sys::MemoryBlock *BOld = Blocks.empty() ? 0 : &Blocks.front(); std::string ErrMsg; - sys::MemoryBlock B = sys::Memory::AllocateRWX(size, BOld, &ErrMsg); + sys::MemoryBlock *LastSlabPtr = LastSlab.base() ? &LastSlab : 0; + sys::MemoryBlock B = sys::Memory::AllocateRWX(size, LastSlabPtr, &ErrMsg); if (B.base() == 0) { - fprintf(stderr, - "Allocation failed when allocating new memory in the JIT\n%s\n", - ErrMsg.c_str()); - abort(); + llvm_report_error("Allocation failed when allocating new memory in the" + " JIT\n" + ErrMsg); + } + LastSlab = B; + ++NumSlabs; + // Initialize the slab to garbage when debugging. + if (PoisonMemory) { + memset(B.base(), 0xCD, B.size()); } - Blocks.push_back(B); return B; } +/// CheckInvariants - For testing only. Return "" if all internal invariants +/// are preserved, and a helpful error message otherwise. For free and +/// allocated blocks, make sure that adding BlockSize gives a valid block. +/// For free blocks, make sure they're in the free list and that their end of +/// block size marker is correct. This function should return an error before +/// accessing bad memory. This function is defined here instead of in +/// JITMemoryManagerTest.cpp so that we don't have to expose all of the +/// implementation details of DefaultJITMemoryManager. +bool DefaultJITMemoryManager::CheckInvariants(std::string &ErrorStr) { + raw_string_ostream Err(ErrorStr); + + // Construct a the set of FreeRangeHeader pointers so we can query it + // efficiently. + llvm::SmallPtrSet<MemoryRangeHeader*, 16> FreeHdrSet; + FreeRangeHeader* FreeHead = FreeMemoryList; + FreeRangeHeader* FreeRange = FreeHead; + + do { + // Check that the free range pointer is in the blocks we've allocated. + bool Found = false; + for (std::vector<sys::MemoryBlock>::iterator I = CodeSlabs.begin(), + E = CodeSlabs.end(); I != E && !Found; ++I) { + char *Start = (char*)I->base(); + char *End = Start + I->size(); + Found = (Start <= (char*)FreeRange && (char*)FreeRange < End); + } + if (!Found) { + Err << "Corrupt free list; points to " << FreeRange; + return false; + } + + if (FreeRange->Next->Prev != FreeRange) { + Err << "Next and Prev pointers do not match."; + return false; + } + + // Otherwise, add it to the set. + FreeHdrSet.insert(FreeRange); + FreeRange = FreeRange->Next; + } while (FreeRange != FreeHead); + + // Go over each block, and look at each MemoryRangeHeader. + for (std::vector<sys::MemoryBlock>::iterator I = CodeSlabs.begin(), + E = CodeSlabs.end(); I != E; ++I) { + char *Start = (char*)I->base(); + char *End = Start + I->size(); + + // Check each memory range. + for (MemoryRangeHeader *Hdr = (MemoryRangeHeader*)Start, *LastHdr = NULL; + Start <= (char*)Hdr && (char*)Hdr < End; + Hdr = &Hdr->getBlockAfter()) { + if (Hdr->ThisAllocated == 0) { + // Check that this range is in the free list. + if (!FreeHdrSet.count(Hdr)) { + Err << "Found free header at " << Hdr << " that is not in free list."; + return false; + } + + // Now make sure the size marker at the end of the block is correct. + uintptr_t *Marker = ((uintptr_t*)&Hdr->getBlockAfter()) - 1; + if (!(Start <= (char*)Marker && (char*)Marker < End)) { + Err << "Block size in header points out of current MemoryBlock."; + return false; + } + if (Hdr->BlockSize != *Marker) { + Err << "End of block size marker (" << *Marker << ") " + << "and BlockSize (" << Hdr->BlockSize << ") don't match."; + return false; + } + } + + if (LastHdr && LastHdr->ThisAllocated != Hdr->PrevAllocated) { + Err << "Hdr->PrevAllocated (" << Hdr->PrevAllocated << ") != " + << "LastHdr->ThisAllocated (" << LastHdr->ThisAllocated << ")"; + return false; + } else if (!LastHdr && !Hdr->PrevAllocated) { + Err << "The first header should have PrevAllocated true."; + return false; + } + + // Remember the last header. + LastHdr = Hdr; + } + } + + // All invariants are preserved. + return true; +} JITMemoryManager *JITMemoryManager::CreateDefaultMemManager() { return new DefaultJITMemoryManager(); } + +// Allocate memory for code in 512K slabs. +const size_t DefaultJITMemoryManager::DefaultCodeSlabSize = 512 * 1024; + +// Allocate globals and stubs in slabs of 64K. (probably 16 pages) +const size_t DefaultJITMemoryManager::DefaultSlabSize = 64 * 1024; + +// Waste at most 16K at the end of each bump slab. (probably 4 pages) +const size_t DefaultJITMemoryManager::DefaultSizeThreshold = 16 * 1024; diff --git a/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp b/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp index 3b8b84c..53585b8 100644 --- a/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp +++ b/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp @@ -84,8 +84,7 @@ JITEventListener *createMacOSJITEventListener() { void MacOSJITEventListener::NotifyFunctionEmitted( const Function &F, void *FnStart, size_t FnSize, const EmittedFunctionDetails &) { - const char *const FnName = F.getNameStart(); - assert(FnName != 0 && FnStart != 0 && "Bad symbol to add"); + assert(F.hasName() && FnStart != 0 && "Bad symbol to add"); JITSymbolTable **SymTabPtrPtr = 0; SymTabPtrPtr = &__jitSymbolTable; @@ -120,7 +119,7 @@ void MacOSJITEventListener::NotifyFunctionEmitted( // Otherwise, we have enough space, just tack it onto the end of the array. JITSymbolEntry &Entry = SymTabPtr->Symbols[SymTabPtr->NumSymbols]; - Entry.FnName = strdup(FnName); + Entry.FnName = strdup(F.getName().data()); Entry.FnStart = FnStart; Entry.FnSize = FnSize; ++SymTabPtr->NumSymbols; diff --git a/lib/ExecutionEngine/JIT/OProfileJITEventListener.cpp b/lib/ExecutionEngine/JIT/OProfileJITEventListener.cpp new file mode 100644 index 0000000..69398be --- /dev/null +++ b/lib/ExecutionEngine/JIT/OProfileJITEventListener.cpp @@ -0,0 +1,178 @@ +//===-- OProfileJITEventListener.cpp - Tell OProfile about JITted code ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a JITEventListener object that calls into OProfile to tell +// it about JITted functions. For now, we only record function names and sizes, +// but eventually we'll also record line number information. +// +// See http://oprofile.sourceforge.net/doc/devel/jit-interface.html for the +// definition of the interface we're using. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "oprofile-jit-event-listener" +#include "llvm/Function.h" +#include "llvm/Analysis/DebugInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Errno.h" +#include "llvm/Config/config.h" +#include <stddef.h> +using namespace llvm; + +#if USE_OPROFILE + +#include <opagent.h> + +namespace { + +class OProfileJITEventListener : public JITEventListener { + op_agent_t Agent; +public: + OProfileJITEventListener(); + ~OProfileJITEventListener(); + + virtual void NotifyFunctionEmitted(const Function &F, + void *FnStart, size_t FnSize, + const EmittedFunctionDetails &Details); + virtual void NotifyFreeingMachineCode(const Function &F, void *OldPtr); +}; + +OProfileJITEventListener::OProfileJITEventListener() + : Agent(op_open_agent()) { + if (Agent == NULL) { + const std::string err_str = sys::StrError(); + DEBUG(errs() << "Failed to connect to OProfile agent: " << err_str << "\n"); + } else { + DEBUG(errs() << "Connected to OProfile agent.\n"); + } +} + +OProfileJITEventListener::~OProfileJITEventListener() { + if (Agent != NULL) { + if (op_close_agent(Agent) == -1) { + const std::string err_str = sys::StrError(); + DEBUG(errs() << "Failed to disconnect from OProfile agent: " + << err_str << "\n"); + } else { + DEBUG(errs() << "Disconnected from OProfile agent.\n"); + } + } +} + +class FilenameCache { + // Holds the filename of each CompileUnit, so that we can pass the + // pointer into oprofile. These char*s are freed in the destructor. + DenseMap<MDNode*, char*> Filenames; + + public: + const char *getFilename(MDNode *CompileUnit) { + char *&Filename = Filenames[CompileUnit]; + if (Filename == NULL) { + DICompileUnit CU(CompileUnit); + Filename = strdup(CU.getFilename()); + } + return Filename; + } + ~FilenameCache() { + for (DenseMap<MDNode*, char*>::iterator + I = Filenames.begin(), E = Filenames.end(); I != E; ++I) { + free(I->second); + } + } +}; + +static debug_line_info LineStartToOProfileFormat( + const MachineFunction &MF, FilenameCache &Filenames, + uintptr_t Address, DebugLoc Loc) { + debug_line_info Result; + Result.vma = Address; + const DebugLocTuple &tuple = MF.getDebugLocTuple(Loc); + Result.lineno = tuple.Line; + Result.filename = Filenames.getFilename(tuple.CompileUnit); + DEBUG(errs() << "Mapping " << reinterpret_cast<void*>(Result.vma) << " to " + << Result.filename << ":" << Result.lineno << "\n"); + return Result; +} + +// Adds the just-emitted function to the symbol table. +void OProfileJITEventListener::NotifyFunctionEmitted( + const Function &F, void *FnStart, size_t FnSize, + const EmittedFunctionDetails &Details) { + assert(F.hasName() && FnStart != 0 && "Bad symbol to add"); + if (op_write_native_code(Agent, F.getName().data(), + reinterpret_cast<uint64_t>(FnStart), + FnStart, FnSize) == -1) { + DEBUG(errs() << "Failed to tell OProfile about native function " + << F.getName() << " at [" + << FnStart << "-" << ((char*)FnStart + FnSize) << "]\n"); + return; + } + + // Now we convert the line number information from the address/DebugLoc format + // in Details to the address/filename/lineno format that OProfile expects. + // OProfile 0.9.4 (and maybe later versions) has a bug that causes it to + // ignore line numbers for addresses above 4G. + FilenameCache Filenames; + std::vector<debug_line_info> LineInfo; + LineInfo.reserve(1 + Details.LineStarts.size()); + if (!Details.MF->getDefaultDebugLoc().isUnknown()) { + LineInfo.push_back(LineStartToOProfileFormat( + *Details.MF, Filenames, + reinterpret_cast<uintptr_t>(FnStart), + Details.MF->getDefaultDebugLoc())); + } + for (std::vector<EmittedFunctionDetails::LineStart>::const_iterator + I = Details.LineStarts.begin(), E = Details.LineStarts.end(); + I != E; ++I) { + LineInfo.push_back(LineStartToOProfileFormat( + *Details.MF, Filenames, I->Address, I->Loc)); + } + if (!LineInfo.empty()) { + if (op_write_debug_line_info(Agent, FnStart, + LineInfo.size(), &*LineInfo.begin()) == -1) { + DEBUG(errs() + << "Failed to tell OProfile about line numbers for native function " + << F.getName() << " at [" + << FnStart << "-" << ((char*)FnStart + FnSize) << "]\n"); + } + } +} + +// Removes the to-be-deleted function from the symbol table. +void OProfileJITEventListener::NotifyFreeingMachineCode( + const Function &F, void *FnStart) { + assert(FnStart && "Invalid function pointer"); + if (op_unload_native_code(Agent, reinterpret_cast<uint64_t>(FnStart)) == -1) { + DEBUG(errs() << "Failed to tell OProfile about unload of native function " + << F.getName() << " at " << FnStart << "\n"); + } +} + +} // anonymous namespace. + +namespace llvm { +JITEventListener *createOProfileJITEventListener() { + return new OProfileJITEventListener; +} +} + +#else // USE_OPROFILE + +namespace llvm { +// By defining this to return NULL, we can let clients call it unconditionally, +// even if they haven't configured with the OProfile libraries. +JITEventListener *createOProfileJITEventListener() { + return NULL; +} +} // namespace llvm + +#endif // USE_OPROFILE diff --git a/lib/ExecutionEngine/JIT/TargetSelect.cpp b/lib/ExecutionEngine/JIT/TargetSelect.cpp index 0f20819..8bed33b 100644 --- a/lib/ExecutionEngine/JIT/TargetSelect.cpp +++ b/lib/ExecutionEngine/JIT/TargetSelect.cpp @@ -7,24 +7,27 @@ // //===----------------------------------------------------------------------===// // -// This just asks the TargetMachineRegistry for the appropriate JIT to use, and -// allows the user to specify a specific one on the commandline with -march=x. +// This just asks the TargetRegistry for the appropriate JIT to use, and allows +// the user to specify a specific one on the commandline with -march=x. Clients +// should initialize targets prior to calling createJIT. // //===----------------------------------------------------------------------===// #include "JIT.h" #include "llvm/Module.h" #include "llvm/ModuleProvider.h" -#include "llvm/Support/RegistryParser.h" -#include "llvm/Support/Streams.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Host.h" #include "llvm/Target/SubtargetFeature.h" #include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetMachineRegistry.h" +#include "llvm/Target/TargetRegistry.h" using namespace llvm; -static cl::opt<const TargetMachineRegistry::entry*, false, - RegistryParser<TargetMachine> > -MArch("march", cl::desc("Architecture to generate assembly for:")); +static cl::opt<std::string> +MArch("march", + cl::desc("Architecture to generate assembly for (see --version)")); static cl::opt<std::string> MCPU("mcpu", @@ -38,25 +41,51 @@ MAttrs("mattr", cl::desc("Target specific attributes (-mattr=help for details)"), cl::value_desc("a1,+a2,-a3,...")); -/// createInternal - Create an return a new JIT compiler if there is one -/// available for the current target. Otherwise, return null. -/// -ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr, - JITMemoryManager *JMM, - CodeGenOpt::Level OptLevel) { - const TargetMachineRegistry::entry *TheArch = MArch; - if (TheArch == 0) { +/// selectTarget - Pick a target either via -march or by guessing the native +/// arch. Add any CPU features specified via -mcpu or -mattr. +TargetMachine *JIT::selectTarget(ModuleProvider *MP, std::string *ErrorStr) { + Module &Mod = *MP->getModule(); + + Triple TheTriple(Mod.getTargetTriple()); + if (TheTriple.getTriple().empty()) + TheTriple.setTriple(sys::getHostTriple()); + + // Adjust the triple to match what the user requested. + const Target *TheTarget = 0; + if (!MArch.empty()) { + for (TargetRegistry::iterator it = TargetRegistry::begin(), + ie = TargetRegistry::end(); it != ie; ++it) { + if (MArch == it->getName()) { + TheTarget = &*it; + break; + } + } + + if (!TheTarget) { + *ErrorStr = "No available targets are compatible with this -march, " + "see -version for the available targets.\n"; + return 0; + } + + // Adjust the triple to match (if known), otherwise stick with the + // module/host triple. + Triple::ArchType Type = Triple::getArchTypeForLLVMName(MArch); + if (Type != Triple::UnknownArch) + TheTriple.setArch(Type); + } else { std::string Error; - TheArch = TargetMachineRegistry::getClosestTargetForJIT(Error); - if (TheArch == 0) { + TheTarget = TargetRegistry::lookupTarget(TheTriple.getTriple(), Error); + if (TheTarget == 0) { if (ErrorStr) *ErrorStr = Error; return 0; } - } else if (TheArch->JITMatchQualityFn() == 0) { - cerr << "WARNING: This target JIT is not designed for the host you are" - << " running. If bad things happen, please choose a different " - << "-march switch.\n"; + } + + if (!TheTarget->hasJIT()) { + errs() << "WARNING: This target JIT is not designed for the host you are" + << " running. If bad things happen, please choose a different " + << "-march switch.\n"; } // Package up features to be passed to target/subtarget @@ -70,14 +99,8 @@ ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr, } // Allocate a target... - TargetMachine *Target = TheArch->CtorFn(*MP->getModule(), FeaturesStr); + TargetMachine *Target = + TheTarget->createTargetMachine(TheTriple.getTriple(), FeaturesStr); assert(Target && "Could not allocate target machine!"); - - // If the target supports JIT code generation, return a new JIT now. - if (TargetJITInfo *TJ = Target->getJITInfo()) - return new JIT(MP, *Target, *TJ, JMM, OptLevel); - - if (ErrorStr) - *ErrorStr = "target does not support JIT code generation"; - return 0; + return Target; } |