From 108c838093704650378b194fe9afc5ebb9e91455 Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Mon, 23 Nov 2009 23:35:19 +0000 Subject: [PATCH] * 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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@89715 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Target/TargetJITInfo.h | 27 ++++--- lib/ExecutionEngine/JIT/JITEmitter.cpp | 95 +++++++++++++---------- lib/Target/ARM/ARMJITInfo.cpp | 45 ++++++----- lib/Target/ARM/ARMJITInfo.h | 4 + lib/Target/Alpha/AlphaJITInfo.cpp | 13 +++- lib/Target/Alpha/AlphaJITInfo.h | 1 + lib/Target/PowerPC/PPCJITInfo.cpp | 25 +++--- lib/Target/PowerPC/PPCJITInfo.h | 1 + lib/Target/X86/X86JITInfo.cpp | 59 ++++++-------- lib/Target/X86/X86JITInfo.h | 12 ++- unittests/ExecutionEngine/JIT/JITTest.cpp | 37 ++++++++- 11 files changed, 196 insertions(+), 123 deletions(-) diff --git a/include/llvm/Target/TargetJITInfo.h b/include/llvm/Target/TargetJITInfo.h index 809f1830abf..7208a8dc446 100644 --- a/include/llvm/Target/TargetJITInfo.h +++ b/include/llvm/Target/TargetJITInfo.h @@ -18,6 +18,7 @@ #define LLVM_TARGET_TARGETJITINFO_H #include +#include "llvm/Support/ErrorHandling.h" #include "llvm/System/DataTypes.h" namespace llvm { @@ -48,22 +49,28 @@ 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. diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index f34ae00f8bc..bbac762b459 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -83,15 +83,15 @@ namespace { class JITResolverState { public: typedef ValueMap > - FunctionToStubMapTy; + FunctionToLazyStubMapTy; typedef std::map > CallSiteToFunctionMapTy; typedef ValueMap, CallSiteValueMapConfig> FunctionToCallSitesMapTy; typedef std::map, 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 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 &GVs, SmallVectorImpl &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(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(JCE) && "Unexpected MCE?"); JITEmitter *JE = cast(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(JCE) && "Unexpected MCE?"); JITEmitter *JE = cast(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. diff --git a/lib/Target/ARM/ARMJITInfo.cpp b/lib/Target/ARM/ARMJITInfo.cpp index 7031640471f..aa50cfd3074 100644 --- a/lib/Target/ARM/ARMJITInfo.cpp +++ b/lib/Target/ARM/ARMJITInfo.cpp @@ -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 { diff --git a/lib/Target/ARM/ARMJITInfo.h b/lib/Target/ARM/ARMJITInfo.h index 7dfeed8b7bf..ff332b7ee15 100644 --- a/lib/Target/ARM/ARMJITInfo.h +++ b/lib/Target/ARM/ARMJITInfo.h @@ -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. diff --git a/lib/Target/Alpha/AlphaJITInfo.cpp b/lib/Target/Alpha/AlphaJITInfo.cpp index 4e59833953b..b3b711eea98 100644 --- a/lib/Target/Alpha/AlphaJITInfo.cpp +++ b/lib/Target/Alpha/AlphaJITInfo.cpp @@ -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 diff --git a/lib/Target/Alpha/AlphaJITInfo.h b/lib/Target/Alpha/AlphaJITInfo.h index ecb467fbc5e..bd358a41312 100644 --- a/lib/Target/Alpha/AlphaJITInfo.h +++ b/lib/Target/Alpha/AlphaJITInfo.h @@ -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); diff --git a/lib/Target/PowerPC/PPCJITInfo.cpp b/lib/Target/PowerPC/PPCJITInfo.cpp index ddbb3265700..c679bcdf58b 100644 --- a/lib/Target/PowerPC/PPCJITInfo.cpp +++ b/lib/Target/PowerPC/PPCJITInfo.cpp @@ -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; } diff --git a/lib/Target/PowerPC/PPCJITInfo.h b/lib/Target/PowerPC/PPCJITInfo.h index 2e25b295f43..47ead59b587 100644 --- a/lib/Target/PowerPC/PPCJITInfo.h +++ b/lib/Target/PowerPC/PPCJITInfo.h @@ -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); diff --git a/lib/Target/X86/X86JITInfo.cpp b/lib/Target/X86/X86JITInfo.cpp index a14c155f174..ce06f0fdebe 100644 --- a/lib/Target/X86/X86JITInfo.cpp +++ b/lib/Target/X86/X86JITInfo.cpp @@ -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 diff --git a/lib/Target/X86/X86JITInfo.h b/lib/Target/X86/X86JITInfo.h index c381433bf35..238420c236b 100644 --- a/lib/Target/X86/X86JITInfo.h +++ b/lib/Target/X86/X86JITInfo.h @@ -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); diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp index 98b292244fe..b676ee41b27 100644 --- a/unittests/ExecutionEngine/JIT/JITTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -183,6 +183,7 @@ class JITTest : public testing::Test { M = new Module("
", 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( + (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( + (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 -- 2.34.1