aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Yasskin <jyasskin@google.com>2009-06-25 02:04:04 +0000
committerJeffrey Yasskin <jyasskin@google.com>2009-06-25 02:04:04 +0000
commitdf5a7daff9c7664bff8b713e8ed5155319bc6041 (patch)
treed0a7b2a265916b1e0e36ef6423b56749d3429c9a
parentb6c29d55123f6b8c3f9e4d56e4be653a1fd2a472 (diff)
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
-rw-r--r--include/llvm/ExecutionEngine/ExecutionEngine.h16
-rw-r--r--include/llvm/ExecutionEngine/JITEventListener.h59
-rw-r--r--lib/ExecutionEngine/JIT/JIT.cpp56
-rw-r--r--lib/ExecutionEngine/JIT/JIT.h17
-rw-r--r--lib/ExecutionEngine/JIT/JITEmitter.cpp165
-rw-r--r--lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp173
-rw-r--r--tools/lli/lli.cpp7
-rw-r--r--unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp241
-rw-r--r--unittests/ExecutionEngine/JIT/Makefile15
-rw-r--r--unittests/ExecutionEngine/Makefile19
-rw-r--r--unittests/Makefile2
11 files changed, 600 insertions, 170 deletions
diff --git a/include/llvm/ExecutionEngine/ExecutionEngine.h b/include/llvm/ExecutionEngine/ExecutionEngine.h
index 170e18477a..613adb574e 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 0000000000..dd76f26c87
--- /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 14d8d5b12a..db5a306bad 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 3ccb2dd812..66417a71b2 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 43f23e4434..8fe7ab848b 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 0000000000..3b8b84ce5b
--- /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
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index afd3c5a71f..25536746da 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 0000000000..3c9beebba6
--- /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 <vector>
+
+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<FunctionEmittedEvent> EmittedEvents;
+ std::vector<FunctionFreedEvent> 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<ExecutionEngine> EE;
+};
+
+Function *buildFunction(Module *M) {
+ Function *Result = Function::Create(
+ TypeBuilder<int32_t(int32_t), false>::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 0000000000..0069c7687a
--- /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 0000000000..e837a7d4fd
--- /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 04d12e01ad..1eb69abbc8 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