diff options
author | Jeffrey Yasskin <jyasskin@google.com> | 2009-06-25 02:04:04 +0000 |
---|---|---|
committer | Jeffrey Yasskin <jyasskin@google.com> | 2009-06-25 02:04:04 +0000 |
commit | df5a7daff9c7664bff8b713e8ed5155319bc6041 (patch) | |
tree | d0a7b2a265916b1e0e36ef6423b56749d3429c9a /lib/ExecutionEngine | |
parent | b6c29d55123f6b8c3f9e4d56e4be653a1fd2a472 (diff) | |
download | external_llvm-df5a7daff9c7664bff8b713e8ed5155319bc6041.zip external_llvm-df5a7daff9c7664bff8b713e8ed5155319bc6041.tar.gz external_llvm-df5a7daff9c7664bff8b713e8ed5155319bc6041.tar.bz2 |
Add a JITEventListener interface that gets called back when a new function is
emitted or the machine code for a function is freed. Chris mentioned that we
may also want a notification when a stub is emitted, but that'll be a future
change. I intend to use this to tell oprofile where functions are emitted and
what lines correspond to what addresses.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@74157 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/ExecutionEngine')
-rw-r--r-- | lib/ExecutionEngine/JIT/JIT.cpp | 56 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JIT.h | 17 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITEmitter.cpp | 165 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp | 173 |
4 files changed, 248 insertions, 163 deletions
diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp index 14d8d5b..db5a306 100644 --- a/lib/ExecutionEngine/JIT/JIT.cpp +++ b/lib/ExecutionEngine/JIT/JIT.cpp @@ -20,8 +20,9 @@ #include "llvm/Instructions.h" #include "llvm/ModuleProvider.h" #include "llvm/CodeGen/JITCodeEmitter.h" -#include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/CodeGen/MachineCodeInfo.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetJITInfo.h" @@ -507,6 +508,40 @@ GenericValue JIT::runFunction(Function *F, return runFunction(Stub, std::vector<GenericValue>()); } +void JIT::RegisterJITEventListener(JITEventListener *L) { + if (L == NULL) + return; + MutexGuard locked(lock); + EventListeners.push_back(L); +} +void JIT::UnregisterJITEventListener(JITEventListener *L) { + if (L == NULL) + return; + MutexGuard locked(lock); + std::vector<JITEventListener*>::reverse_iterator I= + std::find(EventListeners.rbegin(), EventListeners.rend(), L); + if (I != EventListeners.rend()) { + std::swap(*I, EventListeners.back()); + EventListeners.pop_back(); + } +} +void JIT::NotifyFunctionEmitted( + const Function &F, + void *Code, size_t Size, + const JITEvent_EmittedFunctionDetails &Details) { + MutexGuard locked(lock); + for (unsigned I = 0, S = EventListeners.size(); I < S; ++I) { + EventListeners[I]->NotifyFunctionEmitted(F, Code, Size, Details); + } +} + +void JIT::NotifyFreeingMachineCode(const Function &F, void *OldPtr) { + MutexGuard locked(lock); + for (unsigned I = 0, S = EventListeners.size(); I < S; ++I) { + EventListeners[I]->NotifyFreeingMachineCode(F, OldPtr); + } +} + /// runJITOnFunction - Run the FunctionPassManager full of /// just-in-time compilation passes on F, hopefully filling in /// GlobalAddress[F] with the address of F's machine code. @@ -514,11 +549,23 @@ GenericValue JIT::runFunction(Function *F, void JIT::runJITOnFunction(Function *F, MachineCodeInfo *MCI) { MutexGuard locked(lock); - registerMachineCodeInfo(MCI); + class MCIListener : public JITEventListener { + MachineCodeInfo *const MCI; + public: + MCIListener(MachineCodeInfo *mci) : MCI(mci) {} + virtual void NotifyFunctionEmitted(const Function &, + void *Code, size_t Size, + const EmittedFunctionDetails &) { + MCI->setAddress(Code); + MCI->setSize(Size); + } + }; + MCIListener MCIL(MCI); + RegisterJITEventListener(&MCIL); runJITOnFunctionUnlocked(F, locked); - registerMachineCodeInfo(0); + UnregisterJITEventListener(&MCIL); } void JIT::runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked) { @@ -709,3 +756,6 @@ void JIT::addPendingFunction(Function *F) { MutexGuard locked(lock); jitstate->getPendingFunctions(locked).push_back(F); } + + +JITEventListener::~JITEventListener() {} diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index 3ccb2dd..66417a7 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -20,10 +20,11 @@ namespace llvm { class Function; -class TargetMachine; -class TargetJITInfo; +class JITEvent_EmittedFunctionDetails; class MachineCodeEmitter; class MachineCodeInfo; +class TargetJITInfo; +class TargetMachine; class JITState { private: @@ -52,6 +53,7 @@ class JIT : public ExecutionEngine { TargetMachine &TM; // The current target we are compiling to TargetJITInfo &TJI; // The JITInfo for the target we are compiling to JITCodeEmitter *JCE; // JCE object + std::vector<JITEventListener*> EventListeners; JITState *jitstate; @@ -157,9 +159,18 @@ public: // Run the JIT on F and return information about the generated code void runJITOnFunction(Function *F, MachineCodeInfo *MCI = 0); + virtual void RegisterJITEventListener(JITEventListener *L); + virtual void UnregisterJITEventListener(JITEventListener *L); + /// These functions correspond to the methods on JITEventListener. They + /// iterate over the registered listeners and call the corresponding method on + /// each. + void NotifyFunctionEmitted( + const Function &F, void *Code, size_t Size, + const JITEvent_EmittedFunctionDetails &Details); + void NotifyFreeingMachineCode(const Function &F, void *OldPtr); + private: static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM); - void registerMachineCodeInfo(MachineCodeInfo *MCI); void runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked); void updateFunctionStub(Function *F); void updateDlsymStubTable(); diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 43f23e4..8fe7ab8 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -24,8 +24,9 @@ #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRelocation.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/CodeGen/MachineCodeInfo.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetJITInfo.h" @@ -411,136 +412,6 @@ void *JITResolver::JITCompilerFn(void *Stub) { } //===----------------------------------------------------------------------===// -// Function Index Support - -// On MacOS we generate an index of currently JIT'd functions so that -// performance tools can determine a symbol name and accurate code range for a -// PC value. Because performance tools are generally asynchronous, the code -// below is written with the hope that it could be interrupted at any time and -// have useful answers. However, we don't go crazy with atomic operations, we -// just do a "reasonable effort". -#ifdef __APPLE__ -#define ENABLE_JIT_SYMBOL_TABLE 0 -#endif - -/// JitSymbolEntry - Each function that is JIT compiled results in one of these -/// being added to an array of symbols. This indicates the name of the function -/// as well as the address range it occupies. This allows the client to map -/// from a PC value to the name of the function. -struct JitSymbolEntry { - const char *FnName; // FnName - a strdup'd string. - void *FnStart; - intptr_t FnSize; -}; - - -struct JitSymbolTable { - /// NextPtr - This forms a linked list of JitSymbolTable entries. This - /// pointer is not used right now, but might be used in the future. Consider - /// it reserved for future use. - JitSymbolTable *NextPtr; - - /// Symbols - This is an array of JitSymbolEntry entries. Only the first - /// 'NumSymbols' symbols are valid. - JitSymbolEntry *Symbols; - - /// NumSymbols - This indicates the number entries in the Symbols array that - /// are valid. - unsigned NumSymbols; - - /// NumAllocated - This indicates the amount of space we have in the Symbols - /// array. This is a private field that should not be read by external tools. - unsigned NumAllocated; -}; - -#if ENABLE_JIT_SYMBOL_TABLE -JitSymbolTable *__jitSymbolTable; -#endif - -static void AddFunctionToSymbolTable(const char *FnName, - void *FnStart, intptr_t FnSize) { - assert(FnName != 0 && FnStart != 0 && "Bad symbol to add"); - JitSymbolTable **SymTabPtrPtr = 0; -#if !ENABLE_JIT_SYMBOL_TABLE - return; -#else - SymTabPtrPtr = &__jitSymbolTable; -#endif - - // If this is the first entry in the symbol table, add the JitSymbolTable - // index. - if (*SymTabPtrPtr == 0) { - JitSymbolTable *New = new JitSymbolTable(); - New->NextPtr = 0; - New->Symbols = 0; - New->NumSymbols = 0; - New->NumAllocated = 0; - *SymTabPtrPtr = New; - } - - JitSymbolTable *SymTabPtr = *SymTabPtrPtr; - - // If we have space in the table, reallocate the table. - if (SymTabPtr->NumSymbols >= SymTabPtr->NumAllocated) { - // If we don't have space, reallocate the table. - unsigned NewSize = std::max(64U, SymTabPtr->NumAllocated*2); - JitSymbolEntry *NewSymbols = new JitSymbolEntry[NewSize]; - JitSymbolEntry *OldSymbols = SymTabPtr->Symbols; - - // Copy the old entries over. - memcpy(NewSymbols, OldSymbols, SymTabPtr->NumSymbols*sizeof(OldSymbols[0])); - - // Swap the new symbols in, delete the old ones. - SymTabPtr->Symbols = NewSymbols; - SymTabPtr->NumAllocated = NewSize; - delete [] OldSymbols; - } - - // 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.FnStart = FnStart; - Entry.FnSize = FnSize; - ++SymTabPtr->NumSymbols; -} - -static void RemoveFunctionFromSymbolTable(void *FnStart) { - assert(FnStart && "Invalid function pointer"); - JitSymbolTable **SymTabPtrPtr = 0; -#if !ENABLE_JIT_SYMBOL_TABLE - return; -#else - SymTabPtrPtr = &__jitSymbolTable; -#endif - - JitSymbolTable *SymTabPtr = *SymTabPtrPtr; - JitSymbolEntry *Symbols = SymTabPtr->Symbols; - - // Scan the table to find its index. The table is not sorted, so do a linear - // scan. - unsigned Index; - for (Index = 0; Symbols[Index].FnStart != FnStart; ++Index) - assert(Index != SymTabPtr->NumSymbols && "Didn't find function!"); - - // Once we have an index, we know to nuke this entry, overwrite it with the - // entry at the end of the array, making the last entry redundant. - const char *OldName = Symbols[Index].FnName; - Symbols[Index] = Symbols[SymTabPtr->NumSymbols-1]; - free((void*)OldName); - - // Drop the number of symbols in the table. - --SymTabPtr->NumSymbols; - - // Finally, if we deleted the final symbol, deallocate the table itself. - if (SymTabPtr->NumSymbols != 0) - return; - - *SymTabPtrPtr = 0; - delete [] Symbols; - delete SymTabPtr; -} - -//===----------------------------------------------------------------------===// // JITEmitter code. // namespace { @@ -616,11 +487,8 @@ namespace { // in the JITResolver's ExternalFnToStubMap. StringMap<void *> ExtFnStubs; - // MCI - A pointer to a MachineCodeInfo object to update with information. - MachineCodeInfo *MCI; - public: - JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit), CurFn(0), MCI(0) { + JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit), CurFn(0) { MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager(); if (jit.getJITInfo().needsGOT()) { MemMgr->AllocateGOT(); @@ -716,10 +584,6 @@ namespace { JITMemoryManager *getMemMgr(void) const { return MemMgr; } - void setMachineCodeInfo(MachineCodeInfo *mci) { - MCI = mci; - } - private: void *getPointerToGlobal(GlobalValue *GV, void *Reference, bool NoNeedStub); void *getPointerToGVIndirectSym(GlobalValue *V, void *Reference, @@ -1157,21 +1021,16 @@ bool JITEmitter::finishFunction(MachineFunction &F) { // Invalidate the icache if necessary. sys::Memory::InvalidateInstructionCache(FnStart, FnEnd-FnStart); - - // Add it to the JIT symbol table if the host wants it. - AddFunctionToSymbolTable(F.getFunction()->getNameStart(), - FnStart, FnEnd-FnStart); + + JITEvent_EmittedFunctionDetails Details; + TheJIT->NotifyFunctionEmitted(*F.getFunction(), FnStart, FnEnd-FnStart, + Details); DOUT << "JIT: Finished CodeGen of [" << (void*)FnStart << "] Function: " << F.getFunction()->getName() << ": " << (FnEnd-FnStart) << " bytes of text, " << Relocations.size() << " relocations\n"; - if (MCI) { - MCI->setAddress(FnStart); - MCI->setSize(FnEnd-FnStart); - } - Relocations.clear(); ConstPoolAddresses.clear(); @@ -1495,13 +1354,6 @@ void *JIT::getPointerToFunctionOrStub(Function *F) { return JE->getJITResolver().getFunctionStub(F); } -void JIT::registerMachineCodeInfo(MachineCodeInfo *mc) { - assert(isa<JITEmitter>(JCE) && "Unexpected MCE?"); - JITEmitter *JE = cast<JITEmitter>(getCodeEmitter()); - - JE->setMachineCodeInfo(mc); -} - void JIT::updateFunctionStub(Function *F) { // Get the empty stub we generated earlier. assert(isa<JITEmitter>(JCE) && "Unexpected MCE?"); @@ -1609,10 +1461,9 @@ void JIT::freeMachineCodeForFunction(Function *F) { void *OldPtr = updateGlobalMapping(F, 0); if (OldPtr) - RemoveFunctionFromSymbolTable(OldPtr); + TheJIT->NotifyFreeingMachineCode(*F, OldPtr); // Free the actual memory for the function body and related stuff. assert(isa<JITEmitter>(JCE) && "Unexpected MCE?"); cast<JITEmitter>(JCE)->deallocateMemForFunction(F); } - diff --git a/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp b/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp new file mode 100644 index 0000000..3b8b84c --- /dev/null +++ b/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp @@ -0,0 +1,173 @@ +//===-- MacOSJITEventListener.cpp - Save symbol table for OSX perf tools --===// +// +// 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 records JITted functions to +// a global __jitSymbolTable linked list. Apple's performance tools use this to +// determine a symbol name and accurate code range for a PC value. Because +// performance tools are generally asynchronous, the code below is written with +// the hope that it could be interrupted at any time and have useful answers. +// However, we don't go crazy with atomic operations, we just do a "reasonable +// effort". +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "macos-jit-event-listener" +#include "llvm/Function.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include <stddef.h> +using namespace llvm; + +#ifdef __APPLE__ +#define ENABLE_JIT_SYMBOL_TABLE 0 +#endif + +#if ENABLE_JIT_SYMBOL_TABLE + +namespace { + +/// JITSymbolEntry - Each function that is JIT compiled results in one of these +/// being added to an array of symbols. This indicates the name of the function +/// as well as the address range it occupies. This allows the client to map +/// from a PC value to the name of the function. +struct JITSymbolEntry { + const char *FnName; // FnName - a strdup'd string. + void *FnStart; + intptr_t FnSize; +}; + + +struct JITSymbolTable { + /// NextPtr - This forms a linked list of JitSymbolTable entries. This + /// pointer is not used right now, but might be used in the future. Consider + /// it reserved for future use. + JITSymbolTable *NextPtr; + + /// Symbols - This is an array of JitSymbolEntry entries. Only the first + /// 'NumSymbols' symbols are valid. + JITSymbolEntry *Symbols; + + /// NumSymbols - This indicates the number entries in the Symbols array that + /// are valid. + unsigned NumSymbols; + + /// NumAllocated - This indicates the amount of space we have in the Symbols + /// array. This is a private field that should not be read by external tools. + unsigned NumAllocated; +}; + +class MacOSJITEventListener : public JITEventListener { +public: + virtual void NotifyFunctionEmitted(const Function &F, + void *FnStart, size_t FnSize, + const EmittedFunctionDetails &Details); + virtual void NotifyFreeingMachineCode(const Function &F, void *OldPtr); +}; + +} // anonymous namespace. + +// This is a public symbol so the performance tools can find it. +JITSymbolTable *__jitSymbolTable; + +namespace llvm { +JITEventListener *createMacOSJITEventListener() { + return new MacOSJITEventListener; +} +} + +// Adds the just-emitted function to the symbol table. +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"); + JITSymbolTable **SymTabPtrPtr = 0; + SymTabPtrPtr = &__jitSymbolTable; + + // If this is the first entry in the symbol table, add the JITSymbolTable + // index. + if (*SymTabPtrPtr == 0) { + JITSymbolTable *New = new JITSymbolTable(); + New->NextPtr = 0; + New->Symbols = 0; + New->NumSymbols = 0; + New->NumAllocated = 0; + *SymTabPtrPtr = New; + } + + JITSymbolTable *SymTabPtr = *SymTabPtrPtr; + + // If we have space in the table, reallocate the table. + if (SymTabPtr->NumSymbols >= SymTabPtr->NumAllocated) { + // If we don't have space, reallocate the table. + unsigned NewSize = std::max(64U, SymTabPtr->NumAllocated*2); + JITSymbolEntry *NewSymbols = new JITSymbolEntry[NewSize]; + JITSymbolEntry *OldSymbols = SymTabPtr->Symbols; + + // Copy the old entries over. + memcpy(NewSymbols, OldSymbols, SymTabPtr->NumSymbols*sizeof(OldSymbols[0])); + + // Swap the new symbols in, delete the old ones. + SymTabPtr->Symbols = NewSymbols; + SymTabPtr->NumAllocated = NewSize; + delete [] OldSymbols; + } + + // 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.FnStart = FnStart; + Entry.FnSize = FnSize; + ++SymTabPtr->NumSymbols; +} + +// Removes the to-be-deleted function from the symbol table. +void MacOSJITEventListener::NotifyFreeingMachineCode( + const Function &, void *FnStart) { + assert(FnStart && "Invalid function pointer"); + JITSymbolTable **SymTabPtrPtr = 0; + SymTabPtrPtr = &__jitSymbolTable; + + JITSymbolTable *SymTabPtr = *SymTabPtrPtr; + JITSymbolEntry *Symbols = SymTabPtr->Symbols; + + // Scan the table to find its index. The table is not sorted, so do a linear + // scan. + unsigned Index; + for (Index = 0; Symbols[Index].FnStart != FnStart; ++Index) + assert(Index != SymTabPtr->NumSymbols && "Didn't find function!"); + + // Once we have an index, we know to nuke this entry, overwrite it with the + // entry at the end of the array, making the last entry redundant. + const char *OldName = Symbols[Index].FnName; + Symbols[Index] = Symbols[SymTabPtr->NumSymbols-1]; + free((void*)OldName); + + // Drop the number of symbols in the table. + --SymTabPtr->NumSymbols; + + // Finally, if we deleted the final symbol, deallocate the table itself. + if (SymTabPtr->NumSymbols != 0) + return; + + *SymTabPtrPtr = 0; + delete [] Symbols; + delete SymTabPtr; +} + +#else // !ENABLE_JIT_SYMBOL_TABLE + +namespace llvm { +// By defining this to return NULL, we can let clients call it unconditionally, +// even if they aren't on an Apple system. +JITEventListener *createMacOSJITEventListener() { + return NULL; +} +} // namespace llvm + +#endif // ENABLE_JIT_SYMBOL_TABLE |