From df5a7daff9c7664bff8b713e8ed5155319bc6041 Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Thu, 25 Jun 2009 02:04:04 +0000 Subject: [PATCH] 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 --- .../llvm/ExecutionEngine/ExecutionEngine.h | 16 +- .../llvm/ExecutionEngine/JITEventListener.h | 59 +++++ lib/ExecutionEngine/JIT/JIT.cpp | 56 +++- lib/ExecutionEngine/JIT/JIT.h | 17 +- lib/ExecutionEngine/JIT/JITEmitter.cpp | 165 +----------- .../JIT/MacOSJITEventListener.cpp | 173 +++++++++++++ tools/lli/lli.cpp | 7 +- .../JIT/JITEventListenerTest.cpp | 241 ++++++++++++++++++ unittests/ExecutionEngine/JIT/Makefile | 15 ++ unittests/ExecutionEngine/Makefile | 19 ++ unittests/Makefile | 2 +- 11 files changed, 600 insertions(+), 170 deletions(-) create mode 100644 include/llvm/ExecutionEngine/JITEventListener.h create mode 100644 lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp create mode 100644 unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp create mode 100644 unittests/ExecutionEngine/JIT/Makefile create mode 100644 unittests/ExecutionEngine/Makefile diff --git a/include/llvm/ExecutionEngine/ExecutionEngine.h b/include/llvm/ExecutionEngine/ExecutionEngine.h index 170e18477a7..613adb574e3 100644 --- a/include/llvm/ExecutionEngine/ExecutionEngine.h +++ b/include/llvm/ExecutionEngine/ExecutionEngine.h @@ -29,13 +29,14 @@ class Constant; class Function; class GlobalVariable; class GlobalValue; +class JITEventListener; +class JITMemoryManager; +class MachineCodeInfo; class Module; class ModuleProvider; +class MutexGuard; class TargetData; class Type; -class MutexGuard; -class JITMemoryManager; -class MachineCodeInfo; class ExecutionEngineState { private: @@ -276,7 +277,14 @@ public: virtual void *getOrEmitGlobalVariable(const GlobalVariable *GV) { return getPointerToGlobal((GlobalValue*)GV); } - + + /// Registers a listener to be called back on various events within + /// the JIT. See JITEventListener.h for more details. Does not + /// take ownership of the argument. The argument may be NULL, in + /// which case these functions do nothing. + virtual void RegisterJITEventListener(JITEventListener *L) {} + virtual void UnregisterJITEventListener(JITEventListener *L) {} + /// DisableLazyCompilation - If called, the JIT will abort if lazy compilation /// is ever attempted. void DisableLazyCompilation(bool Disabled = true) { diff --git a/include/llvm/ExecutionEngine/JITEventListener.h b/include/llvm/ExecutionEngine/JITEventListener.h new file mode 100644 index 00000000000..dd76f26c877 --- /dev/null +++ b/include/llvm/ExecutionEngine/JITEventListener.h @@ -0,0 +1,59 @@ +//===- JITEventListener.h - Exposes events from JIT compilation -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the JITEventListener interface, which lets users get +// callbacks when significant events happen during the JIT compilation process. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTION_ENGINE_JIT_EVENTLISTENER_H +#define LLVM_EXECUTION_ENGINE_JIT_EVENTLISTENER_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { +class Function; + +/// Empty for now, but this object will contain all details about the +/// generated machine code that a Listener might care about. +struct JITEvent_EmittedFunctionDetails { +}; + +/// JITEventListener - This interface is used by the JIT to notify clients about +/// significant events during compilation. For example, we could have +/// implementations for profilers and debuggers that need to know where +/// functions have been emitted. +/// +/// Each method defaults to doing nothing, so you only need to override the ones +/// you care about. +class JITEventListener { +public: + JITEventListener() {} + virtual ~JITEventListener(); // Defined in JIT.cpp. + + typedef JITEvent_EmittedFunctionDetails EmittedFunctionDetails; + /// NotifyFunctionEmitted - Called after a function has been successfully + /// emitted to memory. The function still has its MachineFunction attached, + /// if you should happen to need that. + virtual void NotifyFunctionEmitted(const Function &F, + void *Code, size_t Size, + const EmittedFunctionDetails &Details) {} + + /// NotifyFreeingMachineCode - This is called inside of + /// freeMachineCodeForFunction(), after the global mapping is removed, but + /// before the machine code is returned to the allocator. OldPtr is the + /// address of the machine code. + virtual void NotifyFreeingMachineCode(const Function &F, void *OldPtr) {} +}; + +JITEventListener *createMacOSJITEventListener(); + +} // end namespace llvm. + +#endif diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp index 14d8d5b12ae..db5a306badc 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()); } +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::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 3ccb2dd8126..66417a71b2c 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 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 43f23e44342..8fe7ab848b7 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" @@ -410,136 +411,6 @@ void *JITResolver::JITCompilerFn(void *Stub) { return Result; } -//===----------------------------------------------------------------------===// -// 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. // @@ -616,11 +487,8 @@ namespace { // in the JITResolver's ExternalFnToStubMap. StringMap 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(JCE) && "Unexpected MCE?"); - JITEmitter *JE = cast(getCodeEmitter()); - - JE->setMachineCodeInfo(mc); -} - void JIT::updateFunctionStub(Function *F) { // Get the empty stub we generated earlier. assert(isa(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(JCE) && "Unexpected MCE?"); cast(JCE)->deallocateMemForFunction(F); } - diff --git a/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp b/lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp new file mode 100644 index 00000000000..3b8b84ce5bc --- /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 +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 diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index afd3c5a71fa..25536746dad 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -18,9 +18,10 @@ #include "llvm/Type.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/LinkAllCodegenComponents.h" -#include "llvm/ExecutionEngine/JIT.h" -#include "llvm/ExecutionEngine/Interpreter.h" #include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" @@ -149,6 +150,8 @@ int main(int argc, char **argv, char * const *envp) { exit(1); } + EE->RegisterJITEventListener(createMacOSJITEventListener()); + if (NoLazyCompilation) EE->DisableLazyCompilation(); diff --git a/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp b/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp new file mode 100644 index 00000000000..3c9beebba65 --- /dev/null +++ b/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp @@ -0,0 +1,241 @@ +//===- JITEventListenerTest.cpp - Unit tests for JITEventListeners --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITEventListener.h" + +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/CodeGen/MachineCodeInfo.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/Support/TypeBuilder.h" +#include "llvm/Target/TargetSelect.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +namespace { + +struct FunctionEmittedEvent { + // Indices are local to the RecordingJITEventListener, since the + // JITEventListener interface makes no guarantees about the order of + // calls between Listeners. + unsigned Index; + const Function *F; + void *Code; + size_t Size; + JITEvent_EmittedFunctionDetails Details; +}; +struct FunctionFreedEvent { + unsigned Index; + const Function *F; + void *Code; +}; + +struct RecordingJITEventListener : public JITEventListener { + std::vector EmittedEvents; + std::vector FreedEvents; + + int NextIndex; + + RecordingJITEventListener() : NextIndex(0) {} + + virtual void NotifyFunctionEmitted(const Function &F, + void *Code, size_t Size, + const EmittedFunctionDetails &Details) { + FunctionEmittedEvent Event = {NextIndex++, &F, Code, Size, Details}; + EmittedEvents.push_back(Event); + } + + virtual void NotifyFreeingMachineCode(const Function &F, void *OldPtr) { + FunctionFreedEvent Event = {NextIndex++, &F, OldPtr}; + FreedEvents.push_back(Event); + } +}; + +class JITEventListenerTest : public testing::Test { + protected: + JITEventListenerTest() + : M(new Module("module")), + EE(ExecutionEngine::createJIT(new ExistingModuleProvider(M))) { + } + + Module *M; + const OwningPtr EE; +}; + +Function *buildFunction(Module *M) { + Function *Result = Function::Create( + TypeBuilder::get(), + GlobalValue::ExternalLinkage, "id", M); + Value *Arg = Result->arg_begin(); + BasicBlock *BB = BasicBlock::Create("entry", Result); + ReturnInst::Create(Arg, BB); + return Result; +} + +// Tests that a single JITEventListener follows JIT events accurately. +TEST_F(JITEventListenerTest, Simple) { + RecordingJITEventListener Listener; + EE->RegisterJITEventListener(&Listener); + Function *F1 = buildFunction(M); + Function *F2 = buildFunction(M); + + void *F1_addr = EE->getPointerToFunction(F1); + void *F2_addr = EE->getPointerToFunction(F2); + EE->getPointerToFunction(F1); // Should do nothing. + EE->freeMachineCodeForFunction(F1); + EE->freeMachineCodeForFunction(F2); + + ASSERT_EQ(2U, Listener.EmittedEvents.size()); + ASSERT_EQ(2U, Listener.FreedEvents.size()); + + EXPECT_EQ(0U, Listener.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener.EmittedEvents[1].Index); + EXPECT_EQ(F2, Listener.EmittedEvents[1].F); + EXPECT_EQ(F2_addr, Listener.EmittedEvents[1].Code); + EXPECT_LT(0U, Listener.EmittedEvents[1].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(2U, Listener.FreedEvents[0].Index); + EXPECT_EQ(F1, Listener.FreedEvents[0].F); + EXPECT_EQ(F1_addr, Listener.FreedEvents[0].Code); + + EXPECT_EQ(3U, Listener.FreedEvents[1].Index); + EXPECT_EQ(F2, Listener.FreedEvents[1].F); + EXPECT_EQ(F2_addr, Listener.FreedEvents[1].Code); + + F1->eraseFromParent(); + F2->eraseFromParent(); +} + +// Tests that a single JITEventListener follows JIT events accurately. +TEST_F(JITEventListenerTest, MultipleListenersDontInterfere) { + RecordingJITEventListener Listener1; + RecordingJITEventListener Listener2; + RecordingJITEventListener Listener3; + Function *F1 = buildFunction(M); + Function *F2 = buildFunction(M); + + EE->RegisterJITEventListener(&Listener1); + EE->RegisterJITEventListener(&Listener2); + void *F1_addr = EE->getPointerToFunction(F1); + EE->RegisterJITEventListener(&Listener3); + EE->UnregisterJITEventListener(&Listener1); + void *F2_addr = EE->getPointerToFunction(F2); + EE->UnregisterJITEventListener(&Listener2); + EE->UnregisterJITEventListener(&Listener3); + EE->freeMachineCodeForFunction(F1); + EE->RegisterJITEventListener(&Listener2); + EE->RegisterJITEventListener(&Listener3); + EE->RegisterJITEventListener(&Listener1); + EE->freeMachineCodeForFunction(F2); + EE->UnregisterJITEventListener(&Listener1); + EE->UnregisterJITEventListener(&Listener2); + EE->UnregisterJITEventListener(&Listener3); + + // Listener 1. + ASSERT_EQ(1U, Listener1.EmittedEvents.size()); + ASSERT_EQ(1U, Listener1.FreedEvents.size()); + + EXPECT_EQ(0U, Listener1.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener1.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener1.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener1.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener1.FreedEvents[0].Index); + EXPECT_EQ(F2, Listener1.FreedEvents[0].F); + EXPECT_EQ(F2_addr, Listener1.FreedEvents[0].Code); + + // Listener 2. + ASSERT_EQ(2U, Listener2.EmittedEvents.size()); + ASSERT_EQ(1U, Listener2.FreedEvents.size()); + + EXPECT_EQ(0U, Listener2.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener2.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener2.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener2.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener2.EmittedEvents[1].Index); + EXPECT_EQ(F2, Listener2.EmittedEvents[1].F); + EXPECT_EQ(F2_addr, Listener2.EmittedEvents[1].Code); + EXPECT_LT(0U, Listener2.EmittedEvents[1].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(2U, Listener2.FreedEvents[0].Index); + EXPECT_EQ(F2, Listener2.FreedEvents[0].F); + EXPECT_EQ(F2_addr, Listener2.FreedEvents[0].Code); + + // Listener 3. + ASSERT_EQ(1U, Listener3.EmittedEvents.size()); + ASSERT_EQ(1U, Listener3.FreedEvents.size()); + + EXPECT_EQ(0U, Listener3.EmittedEvents[0].Index); + EXPECT_EQ(F2, Listener3.EmittedEvents[0].F); + EXPECT_EQ(F2_addr, Listener3.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener3.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener3.FreedEvents[0].Index); + EXPECT_EQ(F2, Listener3.FreedEvents[0].F); + EXPECT_EQ(F2_addr, Listener3.FreedEvents[0].Code); + + F1->eraseFromParent(); + F2->eraseFromParent(); +} + +TEST_F(JITEventListenerTest, MatchesMachineCodeInfo) { + RecordingJITEventListener Listener; + MachineCodeInfo MCI; + Function *F = buildFunction(M); + + EE->RegisterJITEventListener(&Listener); + EE->runJITOnFunction(F, &MCI); + void *F_addr = EE->getPointerToFunction(F); + EE->freeMachineCodeForFunction(F); + + ASSERT_EQ(1U, Listener.EmittedEvents.size()); + ASSERT_EQ(1U, Listener.FreedEvents.size()); + + EXPECT_EQ(0U, Listener.EmittedEvents[0].Index); + EXPECT_EQ(F, Listener.EmittedEvents[0].F); + EXPECT_EQ(F_addr, Listener.EmittedEvents[0].Code); + EXPECT_EQ(MCI.address(), Listener.EmittedEvents[0].Code); + EXPECT_EQ(MCI.size(), Listener.EmittedEvents[0].Size); + + EXPECT_EQ(1U, Listener.FreedEvents[0].Index); + EXPECT_EQ(F, Listener.FreedEvents[0].F); + EXPECT_EQ(F_addr, Listener.FreedEvents[0].Code); +} + +class JITEnvironment : public testing::Environment { + virtual void SetUp() { + // Required for ExecutionEngine::createJIT to create a JIT. + InitializeNativeTarget(); + } +}; +testing::Environment* const jit_env = + testing::AddGlobalTestEnvironment(new JITEnvironment); + +} // anonymous namespace diff --git a/unittests/ExecutionEngine/JIT/Makefile b/unittests/ExecutionEngine/JIT/Makefile new file mode 100644 index 00000000000..0069c7687a0 --- /dev/null +++ b/unittests/ExecutionEngine/JIT/Makefile @@ -0,0 +1,15 @@ +##===- unittests/ExecutionEngine/JIT/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +TESTNAME = JIT +LINK_COMPONENTS := core support jit native + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile new file mode 100644 index 00000000000..e837a7d4fd3 --- /dev/null +++ b/unittests/ExecutionEngine/Makefile @@ -0,0 +1,19 @@ +##===- unittests/ExecutionEngine/Makefile ------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. + +include $(LEVEL)/Makefile.config + +PARALLEL_DIRS = JIT + +include $(LEVEL)/Makefile.common + +clean:: + $(Verb) $(RM) -f *Tests diff --git a/unittests/Makefile b/unittests/Makefile index 04d12e01ad7..1eb69abbc89 100644 --- a/unittests/Makefile +++ b/unittests/Makefile @@ -16,7 +16,7 @@ BUILD_ARCHIVE = 1 CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include/ CPP.Flags += -Wno-variadic-macros -PARALLEL_DIRS = ADT Support VMCore MC +PARALLEL_DIRS = ADT ExecutionEngine Support VMCore MC include $(LEVEL)/Makefile.common -- 2.34.1