mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 19:31:58 +00:00
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
This commit is contained in:
parent
b6c29d5512
commit
df5a7daff9
@ -29,13 +29,14 @@ class Constant;
|
||||
class Function;
|
||||
class GlobalVariable;
|
||||
class GlobalValue;
|
||||
class Module;
|
||||
class ModuleProvider;
|
||||
class TargetData;
|
||||
class Type;
|
||||
class MutexGuard;
|
||||
class JITEventListener;
|
||||
class JITMemoryManager;
|
||||
class MachineCodeInfo;
|
||||
class Module;
|
||||
class ModuleProvider;
|
||||
class MutexGuard;
|
||||
class TargetData;
|
||||
class Type;
|
||||
|
||||
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) {
|
||||
|
59
include/llvm/ExecutionEngine/JITEventListener.h
Normal file
59
include/llvm/ExecutionEngine/JITEventListener.h
Normal file
@ -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
|
@ -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() {}
|
||||
|
@ -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();
|
||||
|
@ -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<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);
|
||||
}
|
||||
|
||||
|
173
lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp
Normal file
173
lib/ExecutionEngine/JIT/MacOSJITEventListener.cpp
Normal file
@ -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
|
@ -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();
|
||||
|
||||
|
241
unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp
Normal file
241
unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp
Normal file
@ -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
|
15
unittests/ExecutionEngine/JIT/Makefile
Normal file
15
unittests/ExecutionEngine/JIT/Makefile
Normal file
@ -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
|
19
unittests/ExecutionEngine/Makefile
Normal file
19
unittests/ExecutionEngine/Makefile
Normal file
@ -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
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user