#define DEBUG_TYPE "jit"
#include "JIT.h"
-#include "llvm/Constant.h"
+#include "JITDwarfEmitter.h"
+#include "llvm/Constants.h"
#include "llvm/Module.h"
-#include "llvm/Type.h"
+#include "llvm/DerivedTypes.h"
#include "llvm/CodeGen/MachineCodeEmitter.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/ExecutionEngine/GenericValue.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/System/Memory.h"
+#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/ADT/Statistic.h"
#include <algorithm>
+#include <set>
+#ifndef NDEBUG
+#include <iomanip>
+#endif
using namespace llvm;
STATISTIC(NumBytes, "Number of bytes of machine code compiled");
/// corresponds to.
std::map<void*, Function*> StubToFunctionMap;
- /// GlobalToLazyPtrMap - Keep track of the lazy pointer created for a
+ /// GlobalToNonLazyPtrMap - Keep track of the lazy pointer created for a
/// particular GlobalVariable so that we can reuse them if necessary.
- std::map<GlobalValue*, void*> GlobalToLazyPtrMap;
+ std::map<GlobalValue*, void*> GlobalToNonLazyPtrMap;
public:
std::map<Function*, void*>& getFunctionToStubMap(const MutexGuard& locked) {
}
std::map<GlobalValue*, void*>&
- getGlobalToLazyPtrMap(const MutexGuard& locked) {
+ getGlobalToNonLazyPtrMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
- return GlobalToLazyPtrMap;
+ return GlobalToNonLazyPtrMap;
}
};
static JITResolver *TheJITResolver;
public:
- JITResolver(JIT &jit) : nextGOTIndex(0) {
+ explicit JITResolver(JIT &jit) : nextGOTIndex(0) {
TheJIT = &jit;
LazyResolverFn = jit.getJITInfo().getLazyResolverFunction(JITCompilerFn);
/// specified address, created lazily on demand.
void *getExternalFunctionStub(void *FnAddr);
- /// getGlobalValueLazyPtr - Return a lazy pointer containing the specified
- /// GV address.
- void *getGlobalValueLazyPtr(GlobalValue *V, void *GVAddress);
+ /// getGlobalValueNonLazyPtr - Return a non-lazy pointer containing the
+ /// specified GV address.
+ void *getGlobalValueNonLazyPtr(GlobalValue *V, void *GVAddress);
/// AddCallbackAtLocation - If the target is capable of rewriting an
/// instruction without the use of a stub, record the location of the use so
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) {
return Stub;
}
-/// getGlobalValueLazyPtr - Return a lazy pointer containing the specified
+/// getGlobalValueNonLazyPtr - Return a lazy pointer containing the specified
/// GV address.
-void *JITResolver::getGlobalValueLazyPtr(GlobalValue *GV, void *GVAddress) {
+void *JITResolver::getGlobalValueNonLazyPtr(GlobalValue *GV, void *GVAddress) {
MutexGuard locked(TheJIT->lock);
// If we already have a stub for this global variable, recycle it.
- void *&LazyPtr = state.getGlobalToLazyPtrMap(locked)[GV];
- if (LazyPtr) return LazyPtr;
+ void *&NonLazyPtr = state.getGlobalToNonLazyPtrMap(locked)[GV];
+ if (NonLazyPtr) return NonLazyPtr;
// Otherwise, codegen a new lazy pointer.
- LazyPtr = TheJIT->getJITInfo().emitGlobalValueLazyPtr(GVAddress,
- *TheJIT->getCodeEmitter());
+ NonLazyPtr = TheJIT->getJITInfo().emitGlobalValueNonLazyPtr(GV, GVAddress,
+ *TheJIT->getCodeEmitter());
- DOUT << "JIT: Stub emitted at [" << LazyPtr << "] for GV '"
+ DOUT << "JIT: Stub emitted at [" << NonLazyPtr << "] for GV '"
<< GV->getName() << "'\n";
- return LazyPtr;
+ return NonLazyPtr;
}
/// getExternalFunctionStub - Return a stub for the function at the
void *&Stub = ExternalFnToStubMap[FnAddr];
if (Stub) return Stub;
- Stub = TheJIT->getJITInfo().emitFunctionStub(FnAddr,
+ Stub = TheJIT->getJITInfo().emitFunctionStub(0, FnAddr,
*TheJIT->getCodeEmitter());
DOUT << "JIT: Stub emitted at [" << 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;
}
/// it if necessary, then returns the resultant function pointer.
void *JITResolver::JITCompilerFn(void *Stub) {
JITResolver &JR = *TheJITResolver;
-
- MutexGuard locked(TheJIT->lock);
-
- // The address given to us for the stub may not be exactly right, it might be
- // a little bit after the stub. As such, use upper_bound to find it.
- std::map<void*, Function*>::iterator I =
- JR.state.getStubToFunctionMap(locked).upper_bound(Stub);
- assert(I != JR.state.getStubToFunctionMap(locked).begin() &&
- "This is not a known stub!");
- Function *F = (--I)->second;
+
+ Function* F = 0;
+ void* ActualPtr = 0;
+
+ {
+ // Only lock for getting the Function. The call getPointerToFunction made
+ // in this function might trigger function materializing, which requires
+ // JIT lock to be unlocked.
+ MutexGuard locked(TheJIT->lock);
+
+ // The address given to us for the stub may not be exactly right, it might be
+ // a little bit after the stub. As such, use upper_bound to find it.
+ std::map<void*, Function*>::iterator I =
+ JR.state.getStubToFunctionMap(locked).upper_bound(Stub);
+ assert(I != JR.state.getStubToFunctionMap(locked).begin() &&
+ "This is not a known stub!");
+ F = (--I)->second;
+ ActualPtr = I->first;
+ }
// If we have already code generated the function, just return the address.
void *Result = TheJIT->getPointerToGlobalIfAvailable(F);
DOUT << "JIT: Lazily resolving function '" << F->getName()
<< "' In stub ptr = " << Stub << " actual ptr = "
- << I->first << "\n";
+ << ActualPtr << "\n";
Result = TheJIT->getPointerToFunction(F);
}
+
+ // Reacquire the lock to erase the stub in the map.
+ MutexGuard locked(TheJIT->lock);
// We don't need to reuse this stub in the future, as F is now compiled.
JR.state.getFunctionToStubMap(locked).erase(F);
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;
+
+ // GVSet - a set to keep track of which globals have been seen
+ std::set<const GlobalVariable*> GVSet;
+
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;
}
+
+ /// classof - Methods for support type inquiry through isa, cast, and
+ /// dyn_cast:
+ ///
+ static inline bool classof(const JITEmitter*) { return true; }
+ static inline bool classof(const MachineCodeEmitter*) { return true; }
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);
+
+ /// allocateSpace - Reserves space in the current block if any, or
+ /// allocate a new one of the given size.
+ virtual void *allocateSpace(intptr_t Size, unsigned Alignment);
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);
+ }
+
+ void setMemoryExecutable(void) {
+ MemMgr->setMemoryExecutable();
+ }
+
private:
void *getPointerToGlobal(GlobalValue *GV, void *Reference, bool NoNeedStub);
- void *getPointerToGVLazyPtr(GlobalValue *V, void *Reference,
- bool NoNeedStub);
+ void *getPointerToGVNonLazyPtr(GlobalValue *V, void *Reference,
+ bool NoNeedStub);
+ unsigned addSizeOfGlobal(const GlobalVariable *GV, unsigned Size);
+ unsigned addSizeOfGlobalsInConstantVal(const Constant *C, unsigned Size);
+ unsigned addSizeOfGlobalsInInitializer(const Constant *Init, unsigned Size);
+ unsigned GetSizeOfGlobalsInBytes(MachineFunction &MF);
};
}
/// global immediately instead of queuing it for codegen later!
return TheJIT->getOrEmitGlobalVariable(GV);
}
+ if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V))
+ return TheJIT->getPointerToGlobal(GA->resolveAliasedGlobal(false));
// If we have already compiled the function, return a pointer to its body.
Function *F = cast<Function>(V);
return Resolver.getFunctionStub(F);
}
-void *JITEmitter::getPointerToGVLazyPtr(GlobalValue *V, void *Reference,
+void *JITEmitter::getPointerToGVNonLazyPtr(GlobalValue *V, void *Reference,
bool DoesntNeedStub) {
// Make sure GV is emitted first.
// FIXME: For now, if the GV is an external function we force the JIT to
- // compile it so the lazy pointer will contain the fully resolved address.
+ // compile it so the non-lazy pointer will contain the fully resolved address.
void *GVAddress = getPointerToGlobal(V, Reference, true);
- return Resolver.getGlobalValueLazyPtr(V, GVAddress);
+ return Resolver.getGlobalValueNonLazyPtr(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;
+}
+
+/// addSizeOfGlobal - add the size of the global (plus any alignment padding)
+/// into the running total Size.
+
+unsigned JITEmitter::addSizeOfGlobal(const GlobalVariable *GV, unsigned Size) {
+ const Type *ElTy = GV->getType()->getElementType();
+ size_t GVSize = (size_t)TheJIT->getTargetData()->getABITypeSize(ElTy);
+ size_t GVAlign =
+ (size_t)TheJIT->getTargetData()->getPreferredAlignment(GV);
+ DOUT << "Adding in size " << GVSize << " alignment " << GVAlign;
+ DEBUG(GV->dump());
+ // Assume code section ends with worst possible alignment, so first
+ // variable needs maximal padding.
+ if (Size==0)
+ Size = 1;
+ Size = ((Size+GVAlign-1)/GVAlign)*GVAlign;
+ Size += GVSize;
+ return Size;
}
+/// addSizeOfGlobalsInConstantVal - find any globals that we haven't seen yet
+/// but are referenced from the constant; put them in GVSet and add their
+/// size into the running total Size.
+
+unsigned JITEmitter::addSizeOfGlobalsInConstantVal(const Constant *C,
+ unsigned Size) {
+ // If its undefined, return the garbage.
+ if (isa<UndefValue>(C))
+ return Size;
+
+ // If the value is a ConstantExpr
+ if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
+ Constant *Op0 = CE->getOperand(0);
+ switch (CE->getOpcode()) {
+ case Instruction::GetElementPtr:
+ case Instruction::Trunc:
+ case Instruction::ZExt:
+ case Instruction::SExt:
+ case Instruction::FPTrunc:
+ case Instruction::FPExt:
+ case Instruction::UIToFP:
+ case Instruction::SIToFP:
+ case Instruction::FPToUI:
+ case Instruction::FPToSI:
+ case Instruction::PtrToInt:
+ case Instruction::IntToPtr:
+ case Instruction::BitCast: {
+ Size = addSizeOfGlobalsInConstantVal(Op0, Size);
+ break;
+ }
+ case Instruction::Add:
+ case Instruction::Sub:
+ case Instruction::Mul:
+ case Instruction::UDiv:
+ case Instruction::SDiv:
+ case Instruction::URem:
+ case Instruction::SRem:
+ case Instruction::And:
+ case Instruction::Or:
+ case Instruction::Xor: {
+ Size = addSizeOfGlobalsInConstantVal(Op0, Size);
+ Size = addSizeOfGlobalsInConstantVal(CE->getOperand(1), Size);
+ break;
+ }
+ default: {
+ cerr << "ConstantExpr not handled: " << *CE << "\n";
+ abort();
+ }
+ }
+ }
+
+ if (C->getType()->getTypeID() == Type::PointerTyID)
+ if (const GlobalVariable* GV = dyn_cast<GlobalVariable>(C))
+ if (GVSet.insert(GV).second)
+ Size = addSizeOfGlobal(GV, Size);
+
+ return Size;
+}
+
+/// addSizeOfGLobalsInInitializer - handle any globals that we haven't seen yet
+/// but are referenced from the given initializer.
+
+unsigned JITEmitter::addSizeOfGlobalsInInitializer(const Constant *Init,
+ unsigned Size) {
+ if (!isa<UndefValue>(Init) &&
+ !isa<ConstantVector>(Init) &&
+ !isa<ConstantAggregateZero>(Init) &&
+ !isa<ConstantArray>(Init) &&
+ !isa<ConstantStruct>(Init) &&
+ Init->getType()->isFirstClassType())
+ Size = addSizeOfGlobalsInConstantVal(Init, Size);
+ return Size;
+}
+
+/// GetSizeOfGlobalsInBytes - walk the code for the function, looking for
+/// globals; then walk the initializers of those globals looking for more.
+/// If their size has not been considered yet, add it into the running total
+/// Size.
+
+unsigned JITEmitter::GetSizeOfGlobalsInBytes(MachineFunction &MF) {
+ unsigned Size = 0;
+ GVSet.clear();
+
+ for (MachineFunction::iterator MBB = MF.begin(), E = MF.end();
+ MBB != E; ++MBB) {
+ for (MachineBasicBlock::const_iterator I = MBB->begin(), E = MBB->end();
+ I != E; ++I) {
+ const TargetInstrDesc &Desc = I->getDesc();
+ const MachineInstr &MI = *I;
+ unsigned NumOps = Desc.getNumOperands();
+ for (unsigned CurOp = 0; CurOp < NumOps; CurOp++) {
+ const MachineOperand &MO = MI.getOperand(CurOp);
+ if (MO.isGlobal()) {
+ GlobalValue* V = MO.getGlobal();
+ const GlobalVariable *GV = dyn_cast<const GlobalVariable>(V);
+ if (!GV)
+ continue;
+ // If seen in previous function, it will have an entry here.
+ if (TheJIT->getPointerToGlobalIfAvailable(GV))
+ continue;
+ // If seen earlier in this function, it will have an entry here.
+ // FIXME: it should be possible to combine these tables, by
+ // assuming the addresses of the new globals in this module
+ // start at 0 (or something) and adjusting them after codegen
+ // complete. Another possibility is to grab a marker bit in GV.
+ if (GVSet.insert(GV).second)
+ // A variable as yet unseen. Add in its size.
+ Size = addSizeOfGlobal(GV, Size);
+ }
+ }
+ }
+ }
+ DOUT << "About to look through initializers\n";
+ // Look for more globals that are referenced only from initializers.
+ // GVSet.end is computed each time because the set can grow as we go.
+ for (std::set<const GlobalVariable *>::iterator I = GVSet.begin();
+ I != GVSet.end(); I++) {
+ const GlobalVariable* GV = *I;
+ if (GV->hasInitializer())
+ Size = addSizeOfGlobalsInInitializer(GV->getInitializer(), Size);
+ }
+
+ return Size;
+}
void JITEmitter::startFunction(MachineFunction &F) {
- uintptr_t ActualSize;
+ uintptr_t ActualSize = 0;
+ // Set the memory writable, if it's not already
+ MemMgr->setMemoryWritable();
+ if (MemMgr->NeedsExactSize()) {
+ DOUT << "ExactSize\n";
+ 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);
+
+ DOUT << "ActualSize before globals " << ActualSize << "\n";
+ // Add the size of the globals that will be allocated after this function.
+ // These are all the ones referenced from this function that were not
+ // previously allocated.
+ ActualSize += GetSizeOfGlobalsInBytes(F);
+ DOUT << "ActualSize after globals " << ActualSize << "\n";
+ }
+
BufferBegin = CurBufferPtr = MemMgr->startFunctionBody(F.getFunction(),
ActualSize);
BufferEnd = BufferBegin+ActualSize;
// other per-function data.
unsigned char *FnStart =
(unsigned char *)TheJIT->getPointerToGlobalIfAvailable(F.getFunction());
- unsigned char *FnEnd = CurBufferPtr;
-
- MemMgr->endFunctionBody(F.getFunction(), BufferBegin, FnEnd);
- NumBytes += FnEnd-FnStart;
if (!Relocations.empty()) {
NumRelos += Relocations.size();
// Resolve the relocations to concrete pointers.
for (unsigned i = 0, e = Relocations.size(); i != e; ++i) {
MachineRelocation &MR = Relocations[i];
- void *ResultPtr;
- if (MR.isString()) {
- ResultPtr = TheJIT->getPointerToNamedFunction(MR.getString());
-
- // If the target REALLY wants a stub for this function, emit it now.
- if (!MR.doesntNeedStub())
- ResultPtr = Resolver.getExternalFunctionStub(ResultPtr);
- } else if (MR.isGlobalValue()) {
- ResultPtr = getPointerToGlobal(MR.getGlobalValue(),
- BufferBegin+MR.getMachineCodeOffset(),
- MR.doesntNeedStub());
- } else if (MR.isGlobalValueLazyPtr()) {
- ResultPtr = getPointerToGVLazyPtr(MR.getGlobalValue(),
+ void *ResultPtr = 0;
+ if (!MR.letTargetResolve()) {
+ if (MR.isString()) {
+ ResultPtr = TheJIT->getPointerToNamedFunction(MR.getString());
+
+ // If the target REALLY wants a stub for this function, emit it now.
+ if (!MR.doesntNeedStub())
+ ResultPtr = Resolver.getExternalFunctionStub(ResultPtr);
+ } else if (MR.isGlobalValue()) {
+ ResultPtr = getPointerToGlobal(MR.getGlobalValue(),
+ BufferBegin+MR.getMachineCodeOffset(),
+ MR.doesntNeedStub());
+ } else if (MR.isGlobalValueNonLazyPtr()) {
+ ResultPtr = getPointerToGVNonLazyPtr(MR.getGlobalValue(),
BufferBegin+MR.getMachineCodeOffset(),
MR.doesntNeedStub());
- } else if (MR.isBasicBlock()) {
- ResultPtr = (void*)getMachineBasicBlockAddress(MR.getBasicBlock());
- } else if (MR.isConstantPoolIndex()) {
- ResultPtr=(void*)getConstantPoolEntryAddress(MR.getConstantPoolIndex());
- } else {
- assert(MR.isJumpTableIndex());
- ResultPtr=(void*)getJumpTableEntryAddress(MR.getJumpTableIndex());
- }
+ } else if (MR.isBasicBlock()) {
+ ResultPtr = (void*)getMachineBasicBlockAddress(MR.getBasicBlock());
+ } else if (MR.isConstantPoolIndex()) {
+ ResultPtr = (void*)getConstantPoolEntryAddress(MR.getConstantPoolIndex());
+ } else {
+ assert(MR.isJumpTableIndex());
+ ResultPtr=(void*)getJumpTableEntryAddress(MR.getJumpTableIndex());
+ }
- MR.setResultPointer(ResultPtr);
+ MR.setResultPointer(ResultPtr);
+ }
// if we are managing the GOT and the relocation wants an index,
// give it one
}
}
+ unsigned char *FnEnd = CurBufferPtr;
+
+ MemMgr->endFunctionBody(F.getFunction(), BufferBegin, FnEnd);
+ BufferBegin = CurBufferPtr = 0;
+ NumBytes += FnEnd-FnStart;
+
// Invalidate the icache if necessary.
- synchronizeICache(FnStart, FnEnd-FnStart);
+ 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);
DOUT << "JIT: Finished CodeGen of [" << (void*)FnStart
<< "] Function: " << F.getFunction()->getName()
<< Relocations.size() << " relocations\n";
Relocations.clear();
+ // Mark code region readable and executable if it's not so already.
+ MemMgr->setMemoryExecutable();
+
#ifndef NDEBUG
- if (sys::hasDisassembler())
- DOUT << "Disassembled code:\n"
- << sys::disassembleBuffer(FnStart, FnEnd-FnStart, (uintptr_t)FnStart);
+ {
+ if (sys::hasDisassembler())
+ DOUT << "Disassembled code:\n"
+ << sys::disassembleBuffer(FnStart, FnEnd-FnStart, (uintptr_t)FnStart);
+ else {
+ DOUT << std::hex;
+ int i;
+ unsigned char* q = FnStart;
+ for (i=1; q!=FnEnd; q++, i++) {
+ if (i%8==1)
+ DOUT << "0x" << (long)q << ": ";
+ DOUT<< std::setw(2) << std::setfill('0') << (unsigned short)*q << " ";
+ if (i%8==0)
+ DOUT<<"\n";
+ }
+ DOUT << std::dec;
+ }
+ }
#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);
+ }
+
+ if (MMI)
+ MMI->EndFunction();
+
return false;
}
+void* JITEmitter::allocateSpace(intptr_t Size, unsigned Alignment) {
+ if (BufferBegin)
+ return MachineCodeEmitter::allocateSpace(Size, Alignment);
+
+ // create a new memory block if there is no active one.
+ // care must be taken so that BufferBegin is invalidated when a
+ // block is trimmed
+ BufferBegin = CurBufferPtr = MemMgr->allocateSpace(Size, Alignment);
+ BufferEnd = BufferBegin+Size;
+ return CurBufferPtr;
+}
+
void JITEmitter::emitConstantPool(MachineConstantPool *MCP) {
+ if (TheJIT->getJITInfo().hasCustomConstantPool()) {
+ DOUT << "JIT: Target has custom constant pool handling. Omitting standard "
+ "constant pool\n";
+ return;
+ }
const std::vector<MachineConstantPoolEntry> &Constants = MCP->getConstants();
if (Constants.empty()) return;
? 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();
+
+ // Invalidate the icache if necessary.
+ sys::Memory::InvalidateInstructionCache(BufferBegin, NumBytes);
+
std::swap(SavedBufferBegin, BufferBegin);
BufferEnd = SavedBufferEnd;
CurBufferPtr = SavedCurBufferPtr;
return Addr;
// Get a stub if the target supports it.
- assert(dynamic_cast<JITEmitter*>(MCE) && "Unexpected MCE?");
- JITEmitter *JE = static_cast<JITEmitter*>(getCodeEmitter());
+ assert(isa<JITEmitter>(MCE) && "Unexpected MCE?");
+ JITEmitter *JE = cast<JITEmitter>(getCodeEmitter());
return JE->getJITResolver().getFunctionStub(F);
}
/// 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?");
- static_cast<JITEmitter*>(MCE)->deallocateMemForFunction(F);
+ assert(isa<JITEmitter>(MCE) && "Unexpected MCE?");
+ cast<JITEmitter>(MCE)->deallocateMemForFunction(F);
}