forked from OSchip/llvm-project
* Move stub allocation inside the JITEmitter, instead of exposing a
way for each TargetJITInfo subclass to allocate its own stubs. This means stubs aren't as exactly-sized anymore, but it lets us get rid of TargetJITInfo::emitFunctionStubAtAddr(), which lets ARM and PPC support the eager JIT, fixing http://llvm.org/PR4816. * Rename the JITEmitter's stub creation functions to describe the kind of stub they create. So far, all of them create lazy-compilation stubs, but they sometimes get used when far-call stubs are needed. Fixing http://llvm.org/PR5201 will involve fixing this. llvm-svn: 89715
This commit is contained in:
parent
bf2956a88b
commit
f2ad571443
|
@ -18,6 +18,7 @@
|
|||
#define LLVM_TARGET_TARGETJITINFO_H
|
||||
|
||||
#include <cassert>
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/System/DataTypes.h"
|
||||
|
||||
namespace llvm {
|
||||
|
@ -48,23 +49,29 @@ namespace llvm {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// Records the required size and alignment for a call stub in bytes.
|
||||
struct StubLayout {
|
||||
size_t Size;
|
||||
size_t Alignment;
|
||||
};
|
||||
/// Returns the maximum size and alignment for a call stub on this target.
|
||||
virtual StubLayout getStubLayout() {
|
||||
llvm_unreachable("This target doesn't implement getStubLayout!");
|
||||
StubLayout Result = {0, 0};
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// emitFunctionStub - Use the specified JITCodeEmitter object to emit a
|
||||
/// small native function that simply calls the function at the specified
|
||||
/// address. Return the address of the resultant function.
|
||||
virtual void *emitFunctionStub(const Function* F, void *Fn,
|
||||
/// address. The JITCodeEmitter must already have storage allocated for the
|
||||
/// stub. Return the address of the resultant function, which may have been
|
||||
/// aligned from the address the JCE was set up to emit at.
|
||||
virtual void *emitFunctionStub(const Function* F, void *Target,
|
||||
JITCodeEmitter &JCE) {
|
||||
assert(0 && "This target doesn't implement emitFunctionStub!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// emitFunctionStubAtAddr - Use the specified JITCodeEmitter object to
|
||||
/// emit a small native function that simply calls Fn. Emit the stub into
|
||||
/// the supplied buffer.
|
||||
virtual void emitFunctionStubAtAddr(const Function* F, void *Fn,
|
||||
void *Buffer, JITCodeEmitter &JCE) {
|
||||
assert(0 && "This target doesn't implement emitFunctionStubAtAddr!");
|
||||
}
|
||||
|
||||
/// getPICJumpTableEntry - Returns the value of the jumptable entry for the
|
||||
/// specific basic block.
|
||||
virtual uintptr_t getPICJumpTableEntry(uintptr_t BB, uintptr_t JTBase) {
|
||||
|
|
|
@ -83,15 +83,15 @@ namespace {
|
|||
class JITResolverState {
|
||||
public:
|
||||
typedef ValueMap<Function*, void*, NoRAUWValueMapConfig<Function*> >
|
||||
FunctionToStubMapTy;
|
||||
FunctionToLazyStubMapTy;
|
||||
typedef std::map<void*, AssertingVH<Function> > CallSiteToFunctionMapTy;
|
||||
typedef ValueMap<Function *, SmallPtrSet<void*, 1>,
|
||||
CallSiteValueMapConfig> FunctionToCallSitesMapTy;
|
||||
typedef std::map<AssertingVH<GlobalValue>, void*> GlobalToIndirectSymMapTy;
|
||||
private:
|
||||
/// FunctionToStubMap - Keep track of the stub created for a particular
|
||||
/// function so that we can reuse them if necessary.
|
||||
FunctionToStubMapTy FunctionToStubMap;
|
||||
/// FunctionToLazyStubMap - Keep track of the lazy stub created for a
|
||||
/// particular function so that we can reuse them if necessary.
|
||||
FunctionToLazyStubMapTy FunctionToLazyStubMap;
|
||||
|
||||
/// CallSiteToFunctionMap - Keep track of the function that each lazy call
|
||||
/// site corresponds to, and vice versa.
|
||||
|
@ -103,12 +103,13 @@ namespace {
|
|||
GlobalToIndirectSymMapTy GlobalToIndirectSymMap;
|
||||
|
||||
public:
|
||||
JITResolverState() : FunctionToStubMap(this),
|
||||
JITResolverState() : FunctionToLazyStubMap(this),
|
||||
FunctionToCallSitesMap(this) {}
|
||||
|
||||
FunctionToStubMapTy& getFunctionToStubMap(const MutexGuard& locked) {
|
||||
FunctionToLazyStubMapTy& getFunctionToLazyStubMap(
|
||||
const MutexGuard& locked) {
|
||||
assert(locked.holds(TheJIT->lock));
|
||||
return FunctionToStubMap;
|
||||
return FunctionToLazyStubMap;
|
||||
}
|
||||
|
||||
GlobalToIndirectSymMapTy& getGlobalToIndirectSymMap(const MutexGuard& locked) {
|
||||
|
@ -154,11 +155,11 @@ namespace {
|
|||
|
||||
Function *const F = C2F_I->second;
|
||||
#ifndef NDEBUG
|
||||
void *RealStub = FunctionToStubMap.lookup(F);
|
||||
void *RealStub = FunctionToLazyStubMap.lookup(F);
|
||||
assert(RealStub == Stub &&
|
||||
"Call-site that wasn't a stub pass in to EraseStub");
|
||||
#endif
|
||||
FunctionToStubMap.erase(F);
|
||||
FunctionToLazyStubMap.erase(F);
|
||||
CallSiteToFunctionMap.erase(C2F_I);
|
||||
|
||||
// Remove the stub from the function->call-sites map, and remove the whole
|
||||
|
@ -196,7 +197,7 @@ namespace {
|
|||
/// JITResolver - Keep track of, and resolve, call sites for functions that
|
||||
/// have not yet been compiled.
|
||||
class JITResolver {
|
||||
typedef JITResolverState::FunctionToStubMapTy FunctionToStubMapTy;
|
||||
typedef JITResolverState::FunctionToLazyStubMapTy FunctionToLazyStubMapTy;
|
||||
typedef JITResolverState::CallSiteToFunctionMapTy CallSiteToFunctionMapTy;
|
||||
typedef JITResolverState::GlobalToIndirectSymMapTy GlobalToIndirectSymMapTy;
|
||||
|
||||
|
@ -206,8 +207,11 @@ namespace {
|
|||
|
||||
JITResolverState state;
|
||||
|
||||
/// ExternalFnToStubMap - This is the equivalent of FunctionToStubMap for
|
||||
/// external functions.
|
||||
/// ExternalFnToStubMap - This is the equivalent of FunctionToLazyStubMap
|
||||
/// for external functions. TODO: Of course, external functions don't need
|
||||
/// a lazy stub. It's actually here to make it more likely that far calls
|
||||
/// succeed, but no single stub can guarantee that. I'll remove this in a
|
||||
/// subsequent checkin when I actually fix far calls.
|
||||
std::map<void*, void*> ExternalFnToStubMap;
|
||||
|
||||
/// revGOTMap - map addresses to indexes in the GOT
|
||||
|
@ -230,14 +234,13 @@ namespace {
|
|||
TheJITResolver = 0;
|
||||
}
|
||||
|
||||
/// getFunctionStubIfAvailable - This returns a pointer to a function stub
|
||||
/// if it has already been created.
|
||||
void *getFunctionStubIfAvailable(Function *F);
|
||||
/// getLazyFunctionStubIfAvailable - This returns a pointer to a function's
|
||||
/// lazy-compilation stub if it has already been created.
|
||||
void *getLazyFunctionStubIfAvailable(Function *F);
|
||||
|
||||
/// getFunctionStub - This returns a pointer to a function stub, creating
|
||||
/// one on demand as needed. If empty is true, create a function stub
|
||||
/// pointing at address 0, to be filled in later.
|
||||
void *getFunctionStub(Function *F);
|
||||
/// getLazyFunctionStub - This returns a pointer to a function's
|
||||
/// lazy-compilation stub, creating one on demand as needed.
|
||||
void *getLazyFunctionStub(Function *F);
|
||||
|
||||
/// getExternalFunctionStub - Return a stub for the function at the
|
||||
/// specified address, created lazily on demand.
|
||||
|
@ -485,22 +488,22 @@ void CallSiteValueMapConfig::onDelete(JITResolverState *JRS, Function *F) {
|
|||
JRS->EraseAllCallSitesPrelocked(F);
|
||||
}
|
||||
|
||||
/// getFunctionStubIfAvailable - This returns a pointer to a function stub
|
||||
/// getLazyFunctionStubIfAvailable - This returns a pointer to a function stub
|
||||
/// if it has already been created.
|
||||
void *JITResolver::getFunctionStubIfAvailable(Function *F) {
|
||||
void *JITResolver::getLazyFunctionStubIfAvailable(Function *F) {
|
||||
MutexGuard locked(TheJIT->lock);
|
||||
|
||||
// If we already have a stub for this function, recycle it.
|
||||
return state.getFunctionToStubMap(locked).lookup(F);
|
||||
return state.getFunctionToLazyStubMap(locked).lookup(F);
|
||||
}
|
||||
|
||||
/// getFunctionStub - This returns a pointer to a function stub, creating
|
||||
/// one on demand as needed.
|
||||
void *JITResolver::getFunctionStub(Function *F) {
|
||||
void *JITResolver::getLazyFunctionStub(Function *F) {
|
||||
MutexGuard locked(TheJIT->lock);
|
||||
|
||||
// If we already have a stub for this function, recycle it.
|
||||
void *&Stub = state.getFunctionToStubMap(locked)[F];
|
||||
// If we already have a lazy stub for this function, recycle it.
|
||||
void *&Stub = state.getFunctionToLazyStubMap(locked)[F];
|
||||
if (Stub) return Stub;
|
||||
|
||||
// Call the lazy resolver function if we are JIT'ing lazily. Otherwise we
|
||||
|
@ -518,9 +521,13 @@ void *JITResolver::getFunctionStub(Function *F) {
|
|||
if (!Actual) return 0;
|
||||
}
|
||||
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
TargetJITInfo::StubLayout SL = TheJIT->getJITInfo().getStubLayout();
|
||||
JE.startGVStub(BS, F, SL.Size, SL.Alignment);
|
||||
// Codegen a new stub, calling the lazy resolver or the actual address of the
|
||||
// external function, if it was resolved.
|
||||
Stub = TheJIT->getJITInfo().emitFunctionStub(F, Actual, JE);
|
||||
JE.finishGVStub(BS);
|
||||
|
||||
if (Actual != (void*)(intptr_t)LazyResolverFn) {
|
||||
// If we are getting the stub for an external function, we really want the
|
||||
|
@ -529,7 +536,7 @@ void *JITResolver::getFunctionStub(Function *F) {
|
|||
TheJIT->updateGlobalMapping(F, Stub);
|
||||
}
|
||||
|
||||
DEBUG(errs() << "JIT: Stub emitted at [" << Stub << "] for function '"
|
||||
DEBUG(errs() << "JIT: Lazy stub emitted at [" << Stub << "] for function '"
|
||||
<< F->getName() << "'\n");
|
||||
|
||||
// Finally, keep track of the stub-to-Function mapping so that the
|
||||
|
@ -572,7 +579,11 @@ void *JITResolver::getExternalFunctionStub(void *FnAddr) {
|
|||
void *&Stub = ExternalFnToStubMap[FnAddr];
|
||||
if (Stub) return Stub;
|
||||
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
TargetJITInfo::StubLayout SL = TheJIT->getJITInfo().getStubLayout();
|
||||
JE.startGVStub(BS, 0, SL.Size, SL.Alignment);
|
||||
Stub = TheJIT->getJITInfo().emitFunctionStub(0, FnAddr, JE);
|
||||
JE.finishGVStub(BS);
|
||||
|
||||
DEBUG(errs() << "JIT: Stub emitted at [" << Stub
|
||||
<< "] for external function at '" << FnAddr << "'\n");
|
||||
|
@ -594,10 +605,10 @@ void JITResolver::getRelocatableGVs(SmallVectorImpl<GlobalValue*> &GVs,
|
|||
SmallVectorImpl<void*> &Ptrs) {
|
||||
MutexGuard locked(TheJIT->lock);
|
||||
|
||||
const FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
|
||||
const FunctionToLazyStubMapTy &FM = state.getFunctionToLazyStubMap(locked);
|
||||
GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(locked);
|
||||
|
||||
for (FunctionToStubMapTy::const_iterator i = FM.begin(), e = FM.end();
|
||||
for (FunctionToLazyStubMapTy::const_iterator i = FM.begin(), e = FM.end();
|
||||
i != e; ++i){
|
||||
Function *F = i->first;
|
||||
if (F->isDeclaration() && F->hasExternalLinkage()) {
|
||||
|
@ -723,11 +734,12 @@ void *JITEmitter::getPointerToGlobal(GlobalValue *V, void *Reference,
|
|||
// If we have already compiled the function, return a pointer to its body.
|
||||
Function *F = cast<Function>(V);
|
||||
|
||||
void *FnStub = Resolver.getFunctionStubIfAvailable(F);
|
||||
void *FnStub = Resolver.getLazyFunctionStubIfAvailable(F);
|
||||
if (FnStub) {
|
||||
// Return the function stub if it's already created. We do this first
|
||||
// so that we're returning the same address for the function as any
|
||||
// previous call.
|
||||
// Return the function stub if it's already created. We do this first so
|
||||
// that we're returning the same address for the function as any previous
|
||||
// call. TODO: Yes, this is wrong. The lazy stub isn't guaranteed to be
|
||||
// close enough to call.
|
||||
AddStubToCurrentFunction(FnStub);
|
||||
return FnStub;
|
||||
}
|
||||
|
@ -747,12 +759,12 @@ void *JITEmitter::getPointerToGlobal(GlobalValue *V, void *Reference,
|
|||
|
||||
// Otherwise, we may need a to emit a stub, and, conservatively, we
|
||||
// always do so.
|
||||
void *StubAddr = Resolver.getFunctionStub(F);
|
||||
void *StubAddr = Resolver.getLazyFunctionStub(F);
|
||||
|
||||
// Add the stub to the current function's list of referenced stubs, so we can
|
||||
// deallocate them if the current function is ever freed. It's possible to
|
||||
// return null from getFunctionStub in the case of a weak extern that fails
|
||||
// to resolve.
|
||||
// return null from getLazyFunctionStub in the case of a weak extern that
|
||||
// fails to resolve.
|
||||
if (StubAddr)
|
||||
AddStubToCurrentFunction(StubAddr);
|
||||
|
||||
|
@ -1442,6 +1454,7 @@ void JITEmitter::startGVStub(BufferState &BS, void *Buffer, unsigned StubSize) {
|
|||
}
|
||||
|
||||
void *JITEmitter::finishGVStub(BufferState &BS) {
|
||||
assert(CurBufferPtr != BufferEnd && "Stub overflowed allocated space.");
|
||||
NumBytes += getCurrentPCOffset();
|
||||
void *Result = BufferBegin;
|
||||
RestoreStateFrom(BS);
|
||||
|
@ -1521,19 +1534,23 @@ void *JIT::getPointerToFunctionOrStub(Function *F) {
|
|||
// Get a stub if the target supports it.
|
||||
assert(isa<JITEmitter>(JCE) && "Unexpected MCE?");
|
||||
JITEmitter *JE = cast<JITEmitter>(getCodeEmitter());
|
||||
return JE->getJITResolver().getFunctionStub(F);
|
||||
return JE->getJITResolver().getLazyFunctionStub(F);
|
||||
}
|
||||
|
||||
void JIT::updateFunctionStub(Function *F) {
|
||||
// Get the empty stub we generated earlier.
|
||||
assert(isa<JITEmitter>(JCE) && "Unexpected MCE?");
|
||||
JITEmitter *JE = cast<JITEmitter>(getCodeEmitter());
|
||||
void *Stub = JE->getJITResolver().getFunctionStub(F);
|
||||
void *Stub = JE->getJITResolver().getLazyFunctionStub(F);
|
||||
void *Addr = getPointerToGlobalIfAvailable(F);
|
||||
|
||||
// Tell the target jit info to rewrite the stub at the specified address,
|
||||
// rather than creating a new one.
|
||||
void *Addr = getPointerToGlobalIfAvailable(F);
|
||||
getJITInfo().emitFunctionStubAtAddr(F, Addr, Stub, *getCodeEmitter());
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
TargetJITInfo::StubLayout layout = getJITInfo().getStubLayout();
|
||||
JE->startGVStub(BS, Stub, layout.Size);
|
||||
getJITInfo().emitFunctionStub(F, Addr, *getCodeEmitter());
|
||||
JE->finishGVStub(BS);
|
||||
}
|
||||
|
||||
/// freeMachineCodeForFunction - release machine code memory for given Function.
|
||||
|
|
|
@ -154,15 +154,22 @@ void *ARMJITInfo::emitGlobalValueIndirectSym(const GlobalValue *GV, void *Ptr,
|
|||
return PtrAddr;
|
||||
}
|
||||
|
||||
TargetJITInfo::StubLayout ARMJITInfo::getStubLayout() {
|
||||
// The stub contains up to 3 4-byte instructions, aligned at 4 bytes, and a
|
||||
// 4-byte address. See emitFunctionStub for details.
|
||||
StubLayout Result = {16, 4};
|
||||
return Result;
|
||||
}
|
||||
|
||||
void *ARMJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
||||
JITCodeEmitter &JCE) {
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
void *Addr;
|
||||
// If this is just a call to an external function, emit a branch instead of a
|
||||
// call. The code is the same except for one bit of the last instruction.
|
||||
if (Fn != (void*)(intptr_t)ARMCompilationCallback) {
|
||||
// Branch to the corresponding function addr.
|
||||
if (IsPIC) {
|
||||
// The stub is 8-byte size and 4-aligned.
|
||||
// The stub is 16-byte size and 4-aligned.
|
||||
intptr_t LazyPtr = getIndirectSymAddr(Fn);
|
||||
if (!LazyPtr) {
|
||||
// In PIC mode, the function stub is loading a lazy-ptr.
|
||||
|
@ -174,30 +181,30 @@ void *ARMJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
errs() << "JIT: Stub emitted at [" << LazyPtr
|
||||
<< "] for external function at '" << Fn << "'\n");
|
||||
}
|
||||
JCE.startGVStub(BS, F, 16, 4);
|
||||
intptr_t Addr = (intptr_t)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable((void*)Addr, 16)) {
|
||||
JCE.emitAlignment(4);
|
||||
Addr = (void*)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable(Addr, 16)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub writable");
|
||||
}
|
||||
JCE.emitWordLE(0xe59fc004); // ldr ip, [pc, #+4]
|
||||
JCE.emitWordLE(0xe08fc00c); // L_func$scv: add ip, pc, ip
|
||||
JCE.emitWordLE(0xe59cf000); // ldr pc, [ip]
|
||||
JCE.emitWordLE(LazyPtr - (Addr+4+8)); // func - (L_func$scv+8)
|
||||
sys::Memory::InvalidateInstructionCache((void*)Addr, 16);
|
||||
if (!sys::Memory::setRangeExecutable((void*)Addr, 16)) {
|
||||
JCE.emitWordLE(LazyPtr - (intptr_t(Addr)+4+8)); // func - (L_func$scv+8)
|
||||
sys::Memory::InvalidateInstructionCache(Addr, 16);
|
||||
if (!sys::Memory::setRangeExecutable(Addr, 16)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub executable");
|
||||
}
|
||||
} else {
|
||||
// The stub is 8-byte size and 4-aligned.
|
||||
JCE.startGVStub(BS, F, 8, 4);
|
||||
intptr_t Addr = (intptr_t)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable((void*)Addr, 8)) {
|
||||
JCE.emitAlignment(4);
|
||||
Addr = (void*)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable(Addr, 8)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub writable");
|
||||
}
|
||||
JCE.emitWordLE(0xe51ff004); // ldr pc, [pc, #-4]
|
||||
JCE.emitWordLE((intptr_t)Fn); // addr of function
|
||||
sys::Memory::InvalidateInstructionCache((void*)Addr, 8);
|
||||
if (!sys::Memory::setRangeExecutable((void*)Addr, 8)) {
|
||||
sys::Memory::InvalidateInstructionCache(Addr, 8);
|
||||
if (!sys::Memory::setRangeExecutable(Addr, 8)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub executable");
|
||||
}
|
||||
}
|
||||
|
@ -209,9 +216,9 @@ void *ARMJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
//
|
||||
// Branch and link to the compilation callback.
|
||||
// The stub is 16-byte size and 4-byte aligned.
|
||||
JCE.startGVStub(BS, F, 16, 4);
|
||||
intptr_t Addr = (intptr_t)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable((void*)Addr, 16)) {
|
||||
JCE.emitAlignment(4);
|
||||
Addr = (void*)JCE.getCurrentPCValue();
|
||||
if (!sys::Memory::setRangeWritable(Addr, 16)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub writable");
|
||||
}
|
||||
// Save LR so the callback can determine which stub called it.
|
||||
|
@ -224,13 +231,13 @@ void *ARMJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
JCE.emitWordLE(0xe51ff004); // ldr pc, [pc, #-4]
|
||||
// The address of the compilation callback.
|
||||
JCE.emitWordLE((intptr_t)ARMCompilationCallback);
|
||||
sys::Memory::InvalidateInstructionCache((void*)Addr, 16);
|
||||
if (!sys::Memory::setRangeExecutable((void*)Addr, 16)) {
|
||||
sys::Memory::InvalidateInstructionCache(Addr, 16);
|
||||
if (!sys::Memory::setRangeExecutable(Addr, 16)) {
|
||||
llvm_unreachable("ERROR: Unable to mark stub executable");
|
||||
}
|
||||
}
|
||||
|
||||
return JCE.finishGVStub(BS);
|
||||
return Addr;
|
||||
}
|
||||
|
||||
intptr_t ARMJITInfo::resolveRelocDestAddr(MachineRelocation *MR) const {
|
||||
|
|
|
@ -61,6 +61,10 @@ namespace llvm {
|
|||
virtual void *emitGlobalValueIndirectSym(const GlobalValue* GV, void *ptr,
|
||||
JITCodeEmitter &JCE);
|
||||
|
||||
// getStubLayout - Returns the size and alignment of the largest call stub
|
||||
// on ARM.
|
||||
virtual StubLayout getStubLayout();
|
||||
|
||||
/// emitFunctionStub - Use the specified JITCodeEmitter object to emit a
|
||||
/// small native function that simply calls the function at the specified
|
||||
/// address.
|
||||
|
|
|
@ -190,18 +190,27 @@ extern "C" {
|
|||
#endif
|
||||
}
|
||||
|
||||
TargetJITInfo::StubLayout AlphaJITInfo::getStubLayout() {
|
||||
// The stub contains 19 4-byte instructions, aligned at 4 bytes:
|
||||
// R0 = R27
|
||||
// 8 x "R27 <<= 8; R27 |= 8-bits-of-Target" == 16 instructions
|
||||
// JMP R27
|
||||
// Magic number so the compilation callback can recognize the stub.
|
||||
StubLayout Result = {19 * 4, 4};
|
||||
return Result;
|
||||
}
|
||||
|
||||
void *AlphaJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
||||
JITCodeEmitter &JCE) {
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
//assert(Fn == AlphaCompilationCallback && "Where are you going?\n");
|
||||
//Do things in a stupid slow way!
|
||||
JCE.startGVStub(BS, F, 19*4);
|
||||
void* Addr = (void*)(intptr_t)JCE.getCurrentPCValue();
|
||||
for (int x = 0; x < 19; ++ x)
|
||||
JCE.emitWordLE(0);
|
||||
EmitBranchToAt(Addr, Fn);
|
||||
DEBUG(errs() << "Emitting Stub to " << Fn << " at [" << Addr << "]\n");
|
||||
return JCE.finishGVStub(BS);
|
||||
return Addr;
|
||||
}
|
||||
|
||||
TargetJITInfo::LazyResolverFn
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace llvm {
|
|||
explicit AlphaJITInfo(TargetMachine &tm) : TM(tm)
|
||||
{ useGOT = true; }
|
||||
|
||||
virtual StubLayout getStubLayout();
|
||||
virtual void *emitFunctionStub(const Function* F, void *Fn,
|
||||
JITCodeEmitter &JCE);
|
||||
virtual LazyResolverFn getLazyResolverFunction(JITCompilerFn);
|
||||
|
|
|
@ -323,6 +323,15 @@ PPCJITInfo::getLazyResolverFunction(JITCompilerFn Fn) {
|
|||
return is64Bit ? PPC64CompilationCallback : PPC32CompilationCallback;
|
||||
}
|
||||
|
||||
TargetJITInfo::StubLayout PPCJITInfo::getStubLayout() {
|
||||
// The stub contains up to 10 4-byte instructions, aligned at 4 bytes: 3
|
||||
// instructions to save the caller's address if this is a lazy-compilation
|
||||
// stub, plus a 1-, 4-, or 7-instruction sequence to load an arbitrary address
|
||||
// into a register and jump through it.
|
||||
StubLayout Result = {10*4, 4};
|
||||
return Result;
|
||||
}
|
||||
|
||||
#if (defined(__POWERPC__) || defined (__ppc__) || defined(_POWER)) && \
|
||||
defined(__APPLE__)
|
||||
extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
|
||||
|
@ -335,8 +344,7 @@ void *PPCJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
// call. The code is the same except for one bit of the last instruction.
|
||||
if (Fn != (void*)(intptr_t)PPC32CompilationCallback &&
|
||||
Fn != (void*)(intptr_t)PPC64CompilationCallback) {
|
||||
JCE.startGVStub(BS, F, 7*4);
|
||||
intptr_t Addr = (intptr_t)JCE.getCurrentPCValue();
|
||||
void *Addr = (void*)JCE.getCurrentPCValue();
|
||||
JCE.emitWordBE(0);
|
||||
JCE.emitWordBE(0);
|
||||
JCE.emitWordBE(0);
|
||||
|
@ -344,13 +352,12 @@ void *PPCJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
JCE.emitWordBE(0);
|
||||
JCE.emitWordBE(0);
|
||||
JCE.emitWordBE(0);
|
||||
EmitBranchToAt(Addr, (intptr_t)Fn, false, is64Bit);
|
||||
sys::Memory::InvalidateInstructionCache((void*)Addr, 7*4);
|
||||
return JCE.finishGVStub(BS);
|
||||
EmitBranchToAt((intptr_t)Addr, (intptr_t)Fn, false, is64Bit);
|
||||
sys::Memory::InvalidateInstructionCache(Addr, 7*4);
|
||||
return Addr;
|
||||
}
|
||||
|
||||
JCE.startGVStub(BS, F, 10*4);
|
||||
intptr_t Addr = (intptr_t)JCE.getCurrentPCValue();
|
||||
void *Addr = (void*)JCE.getCurrentPCValue();
|
||||
if (is64Bit) {
|
||||
JCE.emitWordBE(0xf821ffb1); // stdu r1,-80(r1)
|
||||
JCE.emitWordBE(0x7d6802a6); // mflr r11
|
||||
|
@ -373,8 +380,8 @@ void *PPCJITInfo::emitFunctionStub(const Function* F, void *Fn,
|
|||
JCE.emitWordBE(0);
|
||||
JCE.emitWordBE(0);
|
||||
EmitBranchToAt(BranchAddr, (intptr_t)Fn, true, is64Bit);
|
||||
sys::Memory::InvalidateInstructionCache((void*)Addr, 10*4);
|
||||
return JCE.finishGVStub(BS);
|
||||
sys::Memory::InvalidateInstructionCache(Addr, 10*4);
|
||||
return Addr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace llvm {
|
|||
is64Bit = tmIs64Bit;
|
||||
}
|
||||
|
||||
virtual StubLayout getStubLayout();
|
||||
virtual void *emitFunctionStub(const Function* F, void *Fn,
|
||||
JITCodeEmitter &JCE);
|
||||
virtual LazyResolverFn getLazyResolverFunction(JITCompilerFn);
|
||||
|
|
|
@ -438,74 +438,65 @@ void *X86JITInfo::emitGlobalValueIndirectSym(const GlobalValue* GV, void *ptr,
|
|||
return JCE.finishGVStub(BS);
|
||||
}
|
||||
|
||||
void *X86JITInfo::emitFunctionStub(const Function* F, void *Fn,
|
||||
TargetJITInfo::StubLayout X86JITInfo::getStubLayout() {
|
||||
// The 64-bit stub contains:
|
||||
// movabs r10 <- 8-byte-target-address # 10 bytes
|
||||
// call|jmp *r10 # 3 bytes
|
||||
// The 32-bit stub contains a 5-byte call|jmp.
|
||||
// If the stub is a call to the compilation callback, an extra byte is added
|
||||
// to mark it as a stub.
|
||||
StubLayout Result = {14, 4};
|
||||
return Result;
|
||||
}
|
||||
|
||||
void *X86JITInfo::emitFunctionStub(const Function* F, void *Target,
|
||||
JITCodeEmitter &JCE) {
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
// Note, we cast to intptr_t here to silence a -pedantic warning that
|
||||
// complains about casting a function pointer to a normal pointer.
|
||||
#if defined (X86_32_JIT) && !defined (_MSC_VER)
|
||||
bool NotCC = (Fn != (void*)(intptr_t)X86CompilationCallback &&
|
||||
Fn != (void*)(intptr_t)X86CompilationCallback_SSE);
|
||||
bool NotCC = (Target != (void*)(intptr_t)X86CompilationCallback &&
|
||||
Target != (void*)(intptr_t)X86CompilationCallback_SSE);
|
||||
#else
|
||||
bool NotCC = Fn != (void*)(intptr_t)X86CompilationCallback;
|
||||
bool NotCC = Target != (void*)(intptr_t)X86CompilationCallback;
|
||||
#endif
|
||||
JCE.emitAlignment(4);
|
||||
void *Result = (void*)JCE.getCurrentPCValue();
|
||||
if (NotCC) {
|
||||
#if defined (X86_64_JIT)
|
||||
JCE.startGVStub(BS, F, 13, 4);
|
||||
JCE.emitByte(0x49); // REX prefix
|
||||
JCE.emitByte(0xB8+2); // movabsq r10
|
||||
JCE.emitWordLE((unsigned)(intptr_t)Fn);
|
||||
JCE.emitWordLE((unsigned)(((intptr_t)Fn) >> 32));
|
||||
JCE.emitWordLE((unsigned)(intptr_t)Target);
|
||||
JCE.emitWordLE((unsigned)(((intptr_t)Target) >> 32));
|
||||
JCE.emitByte(0x41); // REX prefix
|
||||
JCE.emitByte(0xFF); // jmpq *r10
|
||||
JCE.emitByte(2 | (4 << 3) | (3 << 6));
|
||||
#else
|
||||
JCE.startGVStub(BS, F, 5, 4);
|
||||
JCE.emitByte(0xE9);
|
||||
JCE.emitWordLE((intptr_t)Fn-JCE.getCurrentPCValue()-4);
|
||||
JCE.emitWordLE((intptr_t)Target-JCE.getCurrentPCValue()-4);
|
||||
#endif
|
||||
return JCE.finishGVStub(BS);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#if defined (X86_64_JIT)
|
||||
JCE.startGVStub(BS, F, 14, 4);
|
||||
JCE.emitByte(0x49); // REX prefix
|
||||
JCE.emitByte(0xB8+2); // movabsq r10
|
||||
JCE.emitWordLE((unsigned)(intptr_t)Fn);
|
||||
JCE.emitWordLE((unsigned)(((intptr_t)Fn) >> 32));
|
||||
JCE.emitWordLE((unsigned)(intptr_t)Target);
|
||||
JCE.emitWordLE((unsigned)(((intptr_t)Target) >> 32));
|
||||
JCE.emitByte(0x41); // REX prefix
|
||||
JCE.emitByte(0xFF); // callq *r10
|
||||
JCE.emitByte(2 | (2 << 3) | (3 << 6));
|
||||
#else
|
||||
JCE.startGVStub(BS, F, 6, 4);
|
||||
JCE.emitByte(0xE8); // Call with 32 bit pc-rel destination...
|
||||
|
||||
JCE.emitWordLE((intptr_t)Fn-JCE.getCurrentPCValue()-4);
|
||||
JCE.emitWordLE((intptr_t)Target-JCE.getCurrentPCValue()-4);
|
||||
#endif
|
||||
|
||||
// This used to use 0xCD, but that value is used by JITMemoryManager to
|
||||
// initialize the buffer with garbage, which means it may follow a
|
||||
// noreturn function call, confusing X86CompilationCallback2. PR 4929.
|
||||
JCE.emitByte(0xCE); // Interrupt - Just a marker identifying the stub!
|
||||
return JCE.finishGVStub(BS);
|
||||
}
|
||||
|
||||
void X86JITInfo::emitFunctionStubAtAddr(const Function* F, void *Fn, void *Stub,
|
||||
JITCodeEmitter &JCE) {
|
||||
MachineCodeEmitter::BufferState BS;
|
||||
// Note, we cast to intptr_t here to silence a -pedantic warning that
|
||||
// complains about casting a function pointer to a normal pointer.
|
||||
JCE.startGVStub(BS, Stub, 5);
|
||||
JCE.emitByte(0xE9);
|
||||
#if defined (X86_64_JIT) && !defined (NDEBUG)
|
||||
// Yes, we need both of these casts, or some broken versions of GCC (4.2.4)
|
||||
// get the signed-ness of the expression wrong. Go figure.
|
||||
intptr_t Displacement = (intptr_t)Fn - (intptr_t)JCE.getCurrentPCValue() - 5;
|
||||
assert(((Displacement << 32) >> 32) == Displacement
|
||||
&& "PIC displacement does not fit in displacement field!");
|
||||
#endif
|
||||
JCE.emitWordLE((intptr_t)Fn-JCE.getCurrentPCValue()-4);
|
||||
JCE.finishGVStub(BS);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// getPICJumpTableEntry - Returns the value of the jumptable entry for the
|
||||
|
|
|
@ -43,18 +43,16 @@ namespace llvm {
|
|||
virtual void *emitGlobalValueIndirectSym(const GlobalValue* GV, void *ptr,
|
||||
JITCodeEmitter &JCE);
|
||||
|
||||
// getStubLayout - Returns the size and alignment of the largest call stub
|
||||
// on X86.
|
||||
virtual StubLayout getStubLayout();
|
||||
|
||||
/// emitFunctionStub - Use the specified JITCodeEmitter object to emit a
|
||||
/// small native function that simply calls the function at the specified
|
||||
/// address.
|
||||
virtual void *emitFunctionStub(const Function* F, void *Fn,
|
||||
virtual void *emitFunctionStub(const Function* F, void *Target,
|
||||
JITCodeEmitter &JCE);
|
||||
|
||||
/// emitFunctionStubAtAddr - Use the specified JITCodeEmitter object to
|
||||
/// emit a small native function that simply calls Fn. Emit the stub into
|
||||
/// the supplied buffer.
|
||||
virtual void emitFunctionStubAtAddr(const Function* F, void *Fn,
|
||||
void *Buffer, JITCodeEmitter &JCE);
|
||||
|
||||
/// getPICJumpTableEntry - Returns the value of the jumptable entry for the
|
||||
/// specific basic block.
|
||||
virtual uintptr_t getPICJumpTableEntry(uintptr_t BB, uintptr_t JTBase);
|
||||
|
|
|
@ -183,6 +183,7 @@ class JITTest : public testing::Test {
|
|||
M = new Module("<main>", Context);
|
||||
MP = new ExistingModuleProvider(M);
|
||||
RJMM = new RecordingJITMemoryManager;
|
||||
RJMM->setPoisonMemory(true);
|
||||
std::string Error;
|
||||
TheJIT.reset(EngineBuilder(MP).setEngineKind(EngineKind::JIT)
|
||||
.setJITMemoryManager(RJMM)
|
||||
|
@ -311,7 +312,6 @@ TEST_F(JITTest, FarCallToKnownFunction) {
|
|||
EXPECT_EQ(8, TestFunctionPtr());
|
||||
}
|
||||
|
||||
#if !defined(__arm__) && !defined(__powerpc__) && !defined(__ppc__)
|
||||
// Test a function C which calls A and B which call each other.
|
||||
TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) {
|
||||
TheJIT->DisableLazyCompilation(true);
|
||||
|
@ -407,7 +407,6 @@ TEST_F(JITTest, NonLazyLeaksNoStubs) {
|
|||
EXPECT_EQ(Func2->getNumUses(), 0u);
|
||||
Func2->eraseFromParent();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(JITTest, ModuleDeletion) {
|
||||
TheJIT->DisableLazyCompilation(false);
|
||||
|
@ -458,7 +457,6 @@ TEST_F(JITTest, ModuleDeletion) {
|
|||
NumTablesDeallocated);
|
||||
}
|
||||
|
||||
#if !defined(__arm__) && !defined(__powerpc__) && !defined(__ppc__)
|
||||
typedef int (*FooPtr) ();
|
||||
|
||||
TEST_F(JITTest, NoStubs) {
|
||||
|
@ -496,7 +494,40 @@ TEST_F(JITTest, NoStubs) {
|
|||
|
||||
ASSERT_EQ(stubsBefore, RJMM->stubsAllocated);
|
||||
}
|
||||
|
||||
TEST_F(JITTest, FunctionPointersOutliveTheirCreator) {
|
||||
TheJIT->DisableLazyCompilation(true);
|
||||
LoadAssembly("define i8()* @get_foo_addr() { "
|
||||
" ret i8()* @foo "
|
||||
"} "
|
||||
" "
|
||||
"define i8 @foo() { "
|
||||
" ret i8 42 "
|
||||
"} ");
|
||||
Function *F_get_foo_addr = M->getFunction("get_foo_addr");
|
||||
|
||||
typedef char(*fooT)();
|
||||
fooT (*get_foo_addr)() = reinterpret_cast<fooT(*)()>(
|
||||
(intptr_t)TheJIT->getPointerToFunction(F_get_foo_addr));
|
||||
fooT foo_addr = get_foo_addr();
|
||||
|
||||
// Now free get_foo_addr. This should not free the machine code for foo or
|
||||
// any call stub returned as foo's canonical address.
|
||||
TheJIT->freeMachineCodeForFunction(F_get_foo_addr);
|
||||
|
||||
// Check by calling the reported address of foo.
|
||||
EXPECT_EQ(42, foo_addr());
|
||||
|
||||
// The reported address should also be the same as the result of a subsequent
|
||||
// getPointerToFunction(foo).
|
||||
#if 0
|
||||
// Fails until PR5126 is fixed:
|
||||
Function *F_foo = M->getFunction("foo");
|
||||
fooT foo = reinterpret_cast<fooT>(
|
||||
(intptr_t)TheJIT->getPointerToFunction(F_foo));
|
||||
EXPECT_EQ((intptr_t)foo, (intptr_t)foo_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This code is copied from JITEventListenerTest, but it only runs once for all
|
||||
// the tests in this directory. Everything seems fine, but that's strange
|
||||
|
|
Loading…
Reference in New Issue