#define DEBUG_TYPE "jit"
#include "JIT.h"
+#include "JITDwarfEmitter.h"
#include "llvm/Constant.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRelocation.h"
#include "llvm/ExecutionEngine/JITMemoryManager.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetJITInfo.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MutexGuard.h"
#include "llvm/System/Disassembler.h"
+#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/ADT/Statistic.h"
#include <algorithm>
using namespace llvm;
static JITResolver *TheJITResolver;
public:
- JITResolver(JIT &jit) : nextGOTIndex(0) {
+ explicit JITResolver(JIT &jit) : nextGOTIndex(0) {
TheJIT = &jit;
LazyResolverFn = jit.getJITInfo().getLazyResolverFunction(JITCompilerFn);
JITResolver *JITResolver::TheJITResolver = 0;
-#if (defined(__POWERPC__) || defined (__ppc__) || defined(_POWER)) && \
- defined(__APPLE__)
-extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
-#endif
-
-/// synchronizeICache - On some targets, the JIT emitted code must be
-/// explicitly refetched to ensure correct execution.
-static void synchronizeICache(const void *Addr, size_t len) {
-#if (defined(__POWERPC__) || defined (__ppc__) || defined(_POWER)) && \
- defined(__APPLE__)
- sys_icache_invalidate(Addr, len);
-#endif
-}
-
/// getFunctionStub - This returns a pointer to a function stub, creating
/// one on demand as needed.
void *JITResolver::getFunctionStub(Function *F) {
// Otherwise, codegen a new stub. For now, the stub will call the lazy
// resolver function.
- Stub = TheJIT->getJITInfo().emitFunctionStub(Actual,
+ Stub = TheJIT->getJITInfo().emitFunctionStub(F, Actual,
*TheJIT->getCodeEmitter());
if (Actual != (void*)(intptr_t)LazyResolverFn) {
TheJIT->updateGlobalMapping(F, Stub);
}
- // Invalidate the icache if necessary.
- synchronizeICache(Stub, TheJIT->getCodeEmitter()->getCurrentPCValue() -
- (intptr_t)Stub);
-
DOUT << "JIT: Stub emitted at [" << Stub << "] for function '"
<< F->getName() << "'\n";
if (LazyPtr) return LazyPtr;
// Otherwise, codegen a new lazy pointer.
- LazyPtr = TheJIT->getJITInfo().emitGlobalValueLazyPtr(GVAddress,
+ LazyPtr = TheJIT->getJITInfo().emitGlobalValueLazyPtr(GV, GVAddress,
*TheJIT->getCodeEmitter());
DOUT << "JIT: Stub emitted at [" << LazyPtr << "] for GV '"
void *&Stub = ExternalFnToStubMap[FnAddr];
if (Stub) return Stub;
- Stub = TheJIT->getJITInfo().emitFunctionStub(FnAddr,
+ Stub = TheJIT->getJITInfo().emitFunctionStub(0, FnAddr,
*TheJIT->getCodeEmitter());
- // Invalidate the icache if necessary.
- synchronizeICache(Stub, TheJIT->getCodeEmitter()->getCurrentPCValue() -
- (intptr_t)Stub);
-
DOUT << "JIT: Stub emitted at [" << Stub
<< "] for external function at '" << FnAddr << "'\n";
return Stub;
if (!idx) {
idx = ++nextGOTIndex;
revGOTMap[addr] = idx;
- DOUT << "Adding GOT entry " << idx
- << " for addr " << addr << "\n";
+ DOUT << "Adding GOT entry " << idx << " for addr " << addr << "\n";
}
return idx;
}
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.
/// Resolver - This contains info about the currently resolved functions.
JITResolver Resolver;
+
+ /// DE - The dwarf emitter for the jit.
+ JITDwarfEmitter *DE;
+
+ /// LabelLocations - This vector is a mapping from Label ID's to their
+ /// address.
+ std::vector<intptr_t> LabelLocations;
+
+ /// MMI - Machine module info for exception informations
+ MachineModuleInfo* MMI;
+
public:
JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit) {
MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager();
MemMgr->AllocateGOT();
DOUT << "JIT is managing a GOT\n";
}
+
+ if (ExceptionHandling) DE = new JITDwarfEmitter(jit);
}
~JITEmitter() {
delete MemMgr;
+ if (ExceptionHandling) delete DE;
}
JITResolver &getJITResolver() { return Resolver; }
void initJumpTableInfo(MachineJumpTableInfo *MJTI);
void emitJumpTableInfo(MachineJumpTableInfo *MJTI);
- virtual void startFunctionStub(unsigned StubSize, unsigned Alignment = 1);
- virtual void* finishFunctionStub(const Function *F);
+ virtual void startFunctionStub(const GlobalValue* F, unsigned StubSize,
+ unsigned Alignment = 1);
+ virtual void* finishFunctionStub(const GlobalValue *F);
virtual void addRelocation(const MachineRelocation &MR) {
Relocations.push_back(MR);
void deallocateMemForFunction(Function *F) {
MemMgr->deallocateMemForFunction(F);
}
+
+ virtual void emitLabel(uint64_t LabelID) {
+ if (LabelLocations.size() <= LabelID)
+ LabelLocations.resize((LabelID+1)*2);
+ LabelLocations[LabelID] = getCurrentPCValue();
+ }
+
+ virtual intptr_t getLabelAddress(uint64_t LabelID) const {
+ assert(LabelLocations.size() > (unsigned)LabelID &&
+ LabelLocations[LabelID] && "Label not emitted!");
+ return LabelLocations[LabelID];
+ }
+
+ virtual void setModuleInfo(MachineModuleInfo* Info) {
+ MMI = Info;
+ if (ExceptionHandling) DE->setModuleInfo(Info);
+ }
+
private:
void *getPointerToGlobal(GlobalValue *GV, void *Reference, bool NoNeedStub);
void *getPointerToGVLazyPtr(GlobalValue *V, void *Reference,
return Resolver.getGlobalValueLazyPtr(V, GVAddress);
}
+static unsigned GetConstantPoolSizeInBytes(MachineConstantPool *MCP) {
+ const std::vector<MachineConstantPoolEntry> &Constants = MCP->getConstants();
+ if (Constants.empty()) return 0;
+
+ MachineConstantPoolEntry CPE = Constants.back();
+ unsigned Size = CPE.Offset;
+ const Type *Ty = CPE.isMachineConstantPoolEntry()
+ ? CPE.Val.MachineCPVal->getType() : CPE.Val.ConstVal->getType();
+ Size += TheJIT->getTargetData()->getABITypeSize(Ty);
+ return Size;
+}
+
+static unsigned GetJumpTableSizeInBytes(MachineJumpTableInfo *MJTI) {
+ const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables();
+ if (JT.empty()) return 0;
+
+ unsigned NumEntries = 0;
+ for (unsigned i = 0, e = JT.size(); i != e; ++i)
+ NumEntries += JT[i].MBBs.size();
+
+ unsigned EntrySize = MJTI->getEntrySize();
+
+ return NumEntries * EntrySize;
+}
+
+static uintptr_t RoundUpToAlign(uintptr_t Size, unsigned Alignment) {
+ if (Alignment == 0) Alignment = 1;
+ // Since we do not know where the buffer will be allocated, be pessimistic.
+ return Size + Alignment;
+}
void JITEmitter::startFunction(MachineFunction &F) {
- uintptr_t ActualSize;
+ uintptr_t ActualSize = 0;
+ if (MemMgr->NeedsExactSize()) {
+ const TargetInstrInfo* TII = F.getTarget().getInstrInfo();
+ MachineJumpTableInfo *MJTI = F.getJumpTableInfo();
+ MachineConstantPool *MCP = F.getConstantPool();
+
+ // Ensure the constant pool/jump table info is at least 4-byte aligned.
+ ActualSize = RoundUpToAlign(ActualSize, 16);
+
+ // Add the alignment of the constant pool
+ ActualSize = RoundUpToAlign(ActualSize,
+ 1 << MCP->getConstantPoolAlignment());
+
+ // Add the constant pool size
+ ActualSize += GetConstantPoolSizeInBytes(MCP);
+
+ // Add the aligment of the jump table info
+ ActualSize = RoundUpToAlign(ActualSize, MJTI->getAlignment());
+
+ // Add the jump table size
+ ActualSize += GetJumpTableSizeInBytes(MJTI);
+
+ // Add the alignment for the function
+ ActualSize = RoundUpToAlign(ActualSize,
+ std::max(F.getFunction()->getAlignment(), 8U));
+
+ // Add the function size
+ ActualSize += TII->GetFunctionSizeInBytes(F);
+ }
+
BufferBegin = CurBufferPtr = MemMgr->startFunctionBody(F.getFunction(),
ActualSize);
BufferEnd = BufferBegin+ActualSize;
}
// Invalidate the icache if necessary.
- synchronizeICache(FnStart, FnEnd-FnStart);
+ TheJIT->getJITInfo().InvalidateInstructionCache(FnStart, FnEnd-FnStart);
+
+ // Add it to the JIT symbol table if the host wants it.
+ AddFunctionToSymbolTable(F.getFunction()->getNameStart(),
+ FnStart, FnEnd-FnStart);
DOUT << "JIT: Finished CodeGen of [" << (void*)FnStart
<< "] Function: " << F.getFunction()->getName()
DOUT << "Disassembled code:\n"
<< sys::disassembleBuffer(FnStart, FnEnd-FnStart, (uintptr_t)FnStart);
#endif
-
+ if (ExceptionHandling) {
+ uintptr_t ActualSize = 0;
+ SavedBufferBegin = BufferBegin;
+ SavedBufferEnd = BufferEnd;
+ SavedCurBufferPtr = CurBufferPtr;
+
+ if (MemMgr->NeedsExactSize()) {
+ ActualSize = DE->GetDwarfTableSizeInBytes(F, *this, FnStart, FnEnd);
+ }
+
+ BufferBegin = CurBufferPtr = MemMgr->startExceptionTable(F.getFunction(),
+ ActualSize);
+ BufferEnd = BufferBegin+ActualSize;
+ unsigned char* FrameRegister = DE->EmitDwarfTable(F, *this, FnStart, FnEnd);
+ MemMgr->endExceptionTable(F.getFunction(), BufferBegin, CurBufferPtr,
+ FrameRegister);
+ BufferBegin = SavedBufferBegin;
+ BufferEnd = SavedBufferEnd;
+ CurBufferPtr = SavedCurBufferPtr;
+
+ TheJIT->RegisterTable(FrameRegister);
+ }
+ MMI->EndFunction();
+
return false;
}
? CPE.Val.MachineCPVal->getType() : CPE.Val.ConstVal->getType();
Size += TheJIT->getTargetData()->getABITypeSize(Ty);
- ConstantPoolBase = allocateSpace(Size, 1 << MCP->getConstantPoolAlignment());
+ unsigned Align = 1 << MCP->getConstantPoolAlignment();
+ ConstantPoolBase = allocateSpace(Size, Align);
ConstantPool = MCP;
if (ConstantPoolBase == 0) return; // Buffer overflow.
+ DOUT << "JIT: Emitted constant pool at [" << ConstantPoolBase
+ << "] (size: " << Size << ", alignment: " << Align << ")\n";
+
// Initialize the memory for all of the constant pool entries.
for (unsigned i = 0, e = Constants.size(); i != e; ++i) {
void *CAddr = (char*)ConstantPoolBase+Constants[i].Offset;
abort();
}
TheJIT->InitializeMemory(Constants[i].Val.ConstVal, CAddr);
+ DOUT << "JIT: CP" << i << " at [" << CAddr << "]\n";
}
}
}
}
-void JITEmitter::startFunctionStub(unsigned StubSize, unsigned Alignment) {
+void JITEmitter::startFunctionStub(const GlobalValue* F, unsigned StubSize,
+ unsigned Alignment) {
SavedBufferBegin = BufferBegin;
SavedBufferEnd = BufferEnd;
SavedCurBufferPtr = CurBufferPtr;
- BufferBegin = CurBufferPtr = MemMgr->allocateStub(StubSize, Alignment);
+ BufferBegin = CurBufferPtr = MemMgr->allocateStub(F, StubSize, Alignment);
BufferEnd = BufferBegin+StubSize+1;
}
-void *JITEmitter::finishFunctionStub(const Function *F) {
+void *JITEmitter::finishFunctionStub(const GlobalValue* F) {
NumBytes += getCurrentPCOffset();
std::swap(SavedBufferBegin, BufferBegin);
BufferEnd = SavedBufferEnd;
/// freeMachineCodeForFunction - release machine code memory for given Function.
///
void JIT::freeMachineCodeForFunction(Function *F) {
+
// Delete translation for this from the ExecutionEngine, so it will get
// retranslated next time it is used.
- updateGlobalMapping(F, 0);
+ void *OldPtr = updateGlobalMapping(F, 0);
+
+ if (OldPtr)
+ RemoveFunctionFromSymbolTable(OldPtr);
// Free the actual memory for the function body and related stuff.
assert(dynamic_cast<JITEmitter*>(MCE) && "Unexpected MCE?");