diff options
author | Eli Bendersky <eliben@chromium.org> | 2012-12-04 10:56:28 -0800 |
---|---|---|
committer | Eli Bendersky <eliben@chromium.org> | 2012-12-04 10:56:28 -0800 |
commit | c5e7f14509f0ac87449e997ccf938f726e490882 (patch) | |
tree | 6e2de3e17faf42ccdaf649bab98c91de0c5ba980 | |
parent | 411e665e7c7b288760015c670177c8493ccd3e0c (diff) |
Revert the LOCALMODs implementing Nacl for the legacy JIT
BUG= http://code.google.com/p/nativeclient/issues/detail?id=3178
Review URL: https://codereview.chromium.org/11428121
-rw-r--r-- | include/llvm/ExecutionEngine/NaClJITMemoryManager.h | 240 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JIT.h | 2 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITEmitter.cpp | 390 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/NaClJITMemoryManager.cpp | 431 | ||||
-rw-r--r-- | lib/Target/X86/X86NaClJITInfo.cpp | 393 | ||||
-rw-r--r-- | lib/Target/X86/X86NaClJITInfo.h | 75 | ||||
-rw-r--r-- | lib/Target/X86/X86TargetMachine.h | 4 |
7 files changed, 9 insertions, 1526 deletions
diff --git a/include/llvm/ExecutionEngine/NaClJITMemoryManager.h b/include/llvm/ExecutionEngine/NaClJITMemoryManager.h deleted file mode 100644 index 535d64b133..0000000000 --- a/include/llvm/ExecutionEngine/NaClJITMemoryManager.h +++ /dev/null @@ -1,240 +0,0 @@ -//=-- NaClJITMemoryManager.h - Interface JIT uses to Allocate Mem -*- C++ -*-=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - - -#ifndef LLVM_EXECUTION_ENGINE_NACL_JIT_MEMMANAGER_H -#define LLVM_EXECUTION_ENGINE_NACL_JIT_MEMMANAGER_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/Support/Allocator.h" - -namespace llvm { - -class Function; -class GlobalValue; - -struct SimpleSlab { - uint8_t *address; - size_t size; - uint8_t *next_free; -}; - -struct FreeListNode { - uint8_t *address; - uintptr_t size; - FreeListNode *Prev; - FreeListNode *Next; - FreeListNode *RemoveFromFreeList() { - assert(Next->Prev == this && Prev->Next == this && "Freelist broken!"); - Next->Prev = Prev; - return Prev->Next = Next; - } - void AddToFreeList(FreeListNode *FreeList) { - Next = FreeList; - Prev = FreeList->Prev; - Prev->Next = this; - Next->Prev = this; - } -}; - -class NaClJITMemoryManager : public JITMemoryManager { - // NaCl disallows writing into any code region, and disallows executing any - // data region. Thus we can never get any RWX memory and the the strategy - // used by the other allocators of colocation of allocation metadata - // with the allocated code won't work. - // Currently with NaCl we have one single pool of usable space between the - // text and rodata segments, defined by the linker - // so to support stub allocation in the middle of a function, we allocate - // them in slabs interspersed with the functions. - - static const size_t kStubSlabSize = 16 * 1024; - static const size_t kDataSlabSize = 16 * 1024; - static const size_t kCodeSlabSize = 64 * 1024; - - typedef DenseMap<uint8_t *, size_t> AllocationTable; - - uint8_t *AllocatableRegionStart; - uint8_t *AllocatableRegionLimit; - uint8_t *NextCode; - SimpleSlab CurrentStubSlab; - - // Allocation metadata must be kept separate from code, so the free list is - // allocated with new rather than being a header in the code blocks - FreeListNode *CodeFreeListHead; - FreeListNode *CurrentCodeBlock; - // Mapping from pointer to allocated function, to size of allocation - AllocationTable AllocatedFunctions; - - // Since Exception tables are allocated like functions (i.e. we don't know - // ahead of time how large they are) we use the same allocation method for - // simplicity even though it's not strictly necessary to separate the - // allocation metadata from the allocated data. - FreeListNode *DataFreeListHead; - FreeListNode *CurrentDataBlock; - AllocationTable AllocatedTables; - BumpPtrAllocator DataAllocator; - - uint8_t *GOTBase; // Target Specific reserved memory - - FreeListNode *allocateCodeSlab(size_t MinSize); - FreeListNode *allocateDataSlab(size_t MinSize); - SimpleSlab allocateStubSlab(size_t MinSize); - - // Functions for allocations using one of the free lists - void InitFreeList(FreeListNode **Head); - void DestroyFreeList(FreeListNode *Head); - FreeListNode *FreeListAllocate(uintptr_t &ActualSize, FreeListNode *Head, - FreeListNode * (NaClJITMemoryManager::*allocate)(size_t)); - void FreeListFinishAllocation(FreeListNode *Block, FreeListNode *Head, - uint8_t *AllocationStart, uint8_t *AllocationEnd, AllocationTable &table); - void FreeListDeallocate(FreeListNode *Head, AllocationTable &Table, - void *Body); - public: - // TODO(dschuff): how to find the real value? is it a flag? - static const int kBundleSize = 32; - static const intptr_t kJumpMask = -32; - NaClJITMemoryManager(); - virtual ~NaClJITMemoryManager(); - static inline bool classof(const JITMemoryManager*) { return true; } - - /// setMemoryWritable - No-op on NaCl - code is never writable - virtual void setMemoryWritable() {} - - /// setMemoryExecutable - No-op on NaCl - data is never executable - virtual void setMemoryExecutable() {} - - /// setPoisonMemory - No-op on NaCl - nothing unvalidated is ever executable - virtual void setPoisonMemory(bool poison) {} - - /// getPointerToNamedFunction - This method returns the address of the - /// specified function. As such it is only useful for resolving library - /// symbols, not code generated symbols. - /// - /// If AbortOnFailure is false and no function with the given name is - /// found, this function silently returns a null pointer. Otherwise, - /// it prints a message to stderr and aborts. - /// - virtual void *getPointerToNamedFunction(const std::string &Name, - bool AbortOnFailure = true) ; - - //===--------------------------------------------------------------------===// - // Global Offset Table Management - //===--------------------------------------------------------------------===// - - /// AllocateGOT - If the current table requires a Global Offset Table, this - /// method is invoked to allocate it. This method is required to set HasGOT - /// to true. - virtual void AllocateGOT(); - - /// getGOTBase - If this is managing a Global Offset Table, this method should - /// return a pointer to its base. - virtual uint8_t *getGOTBase() const { - return GOTBase; - } - - //===--------------------------------------------------------------------===// - // Main Allocation Functions - //===--------------------------------------------------------------------===// - - /// startFunctionBody - When we start JITing a function, the JIT calls this - /// method to allocate a block of free RWX memory, which returns a pointer to - /// it. If the JIT wants to request a block of memory of at least a certain - /// size, it passes that value as ActualSize, and this method returns a block - /// with at least that much space. If the JIT doesn't know ahead of time how - /// much space it will need to emit the function, it passes 0 for the - /// ActualSize. In either case, this method is required to pass back the size - /// of the allocated block through ActualSize. The JIT will be careful to - /// not write more than the returned ActualSize bytes of memory. - virtual uint8_t *startFunctionBody(const Function *F, - uintptr_t &ActualSize); - - /// allocateStub - This method is called by the JIT to allocate space for a - /// function stub (used to handle limited branch displacements) while it is - /// JIT compiling a function. For example, if foo calls bar, and if bar - /// either needs to be lazily compiled or is a native function that exists too - /// far away from the call site to work, this method will be used to make a - /// thunk for it. The stub should be "close" to the current function body, - /// but should not be included in the 'actualsize' returned by - /// startFunctionBody. - virtual uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment); - - /// endFunctionBody - This method is called when the JIT is done codegen'ing - /// the specified function. At this point we know the size of the JIT - /// compiled function. This passes in FunctionStart (which was returned by - /// the startFunctionBody method) and FunctionEnd which is a pointer to the - /// actual end of the function. This method should mark the space allocated - /// and remember where it is in case the client wants to deallocate it. - virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, - uint8_t *FunctionEnd); - - /// allocateCodeSection - Allocate a memory block of (at least) the given - /// size suitable for executable code. The SectionID is a unique identifier - /// assigned by the JIT and passed through to the memory manager for - /// the instance class to use if it needs to communicate to the JIT about - /// a given section after the fact. - virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); - - /// allocateDataSection - Allocate a memory block of (at least) the given - /// size suitable for data. The SectionID is a unique identifier - /// assigned by the JIT and passed through to the memory manager for - /// the instance class to use if it needs to communicate to the JIT about - /// a given section after the fact. - virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, bool IsReadOnly); - - /// Ignored. - virtual bool applyPermissions(std::string *ErrMsg = 0) {} - - /// allocateSpace - Allocate a memory block of the given size. This method - /// cannot be called between calls to startFunctionBody and endFunctionBody. - virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); - - /// allocateGlobal - Allocate memory for a global. - virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); - - /// deallocateFunctionBody - Free the specified function body. The argument - /// must be the return value from a call to startFunctionBody() that hasn't - /// been deallocated yet. This is never called when the JIT is currently - /// emitting a function. - virtual void deallocateFunctionBody(void *Body); - - /// startExceptionTable - When we finished JITing the function, if exception - /// handling is set, we emit the exception table. - virtual uint8_t* startExceptionTable(const Function* F, - uintptr_t &ActualSize); - - /// endExceptionTable - This method is called when the JIT is done emitting - /// the exception table. - virtual void endExceptionTable(const Function *F, uint8_t *TableStart, - uint8_t *TableEnd, uint8_t* FrameRegister); - - /// deallocateExceptionTable - Free the specified exception table's memory. - /// The argument must be the return value from a call to startExceptionTable() - /// that hasn't been deallocated yet. This is never called when the JIT is - /// currently emitting an exception table. - virtual void deallocateExceptionTable(void *ET); - - virtual size_t GetDefaultCodeSlabSize() { - return kCodeSlabSize; - } - virtual size_t GetDefaultDataSlabSize() { - return kDataSlabSize; - } - virtual size_t GetDefaultStubSlabSize() { - return kStubSlabSize; - } - -}; - -} - -#endif // LLVM_EXECUTION_ENGINE_NACL_JIT_MEMMANAGER_H diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index 338db8f454..2ae155bebf 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -210,8 +210,6 @@ public: private: static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM, TargetMachine &tm); - // Native client needs its own memory manager, so custom ones are unsupported - static JITCodeEmitter *createNaClEmitter(JIT &J, TargetMachine &tm); void runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked); void updateFunctionStub(Function *F); void jitTheFunction(Function *F, const MutexGuard &locked); diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 1c5abf751d..ecafda7286 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -30,7 +30,6 @@ #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/ExecutionEngine/NaClJITMemoryManager.h" #include "llvm/DataLayout.h" #include "llvm/Target/TargetInstrInfo.h" #include "llvm/Target/TargetJITInfo.h" @@ -53,15 +52,12 @@ #ifndef NDEBUG #include <iomanip> #endif -#ifdef __native_client__ -#include <nacl/nacl_dyncode.h> -#endif using namespace llvm; STATISTIC(NumBytes, "Number of bytes of machine code compiled"); STATISTIC(NumRelos, "Number of relocations applied"); STATISTIC(NumRetries, "Number of retries with more memory"); -STATISTIC(NumNopBytes, "Number of bytes of NOPs emitted"); + // A declaration may stop being a declaration once it's fully read from bitcode. // This function returns true if F is fully read and is still a declaration. @@ -285,6 +281,8 @@ namespace { /// JITEmitter - The JIT implementation of the MachineCodeEmitter, which is /// used to output functions to memory for execution. class JITEmitter : public JITCodeEmitter { + JITMemoryManager *MemMgr; + // When outputting a function stub in the context of some other function, we // save BufferBegin/BufferEnd/CurBufferPtr here. uint8_t *SavedBufferBegin, *SavedBufferEnd, *SavedCurBufferPtr; @@ -294,13 +292,11 @@ namespace { // ask the memory manager for at least this much space. When we // successfully emit the function, we reset this back to zero. uintptr_t SizeEstimate; -protected: //TODO:(dschuff): fix/move this once we do validation and are sure - // which functions/data we need in NaClJITEmitter. also add LOCALMOD - JITMemoryManager *MemMgr; + /// Relocations - These are the relocations that the function needs, as /// emitted. std::vector<MachineRelocation> Relocations; -private: + /// MBBLocations - This vector is a mapping from MBB ID's to their address. /// It is filled in by the StartMachineBasicBlock callback and queried by /// the getMachineBasicBlockAddress callback. @@ -384,7 +380,7 @@ private: DE.reset(new JITDwarfEmitter(jit)); } } - virtual ~JITEmitter() { // @LOCALMOD + ~JITEmitter() { delete MemMgr; } @@ -397,10 +393,10 @@ private: void initJumpTableInfo(MachineJumpTableInfo *MJTI); void emitJumpTableInfo(MachineJumpTableInfo *MJTI); - virtual void startGVStub(const GlobalValue* GV, + void startGVStub(const GlobalValue* GV, unsigned StubSize, unsigned Alignment = 1); - virtual void startGVStub(void *Buffer, unsigned StubSize); - virtual void finishGVStub(); + void startGVStub(void *Buffer, unsigned StubSize); + void finishGVStub(); virtual void *allocIndirectGV(const GlobalValue *GV, const uint8_t *Buffer, size_t Size, unsigned Alignment); @@ -472,360 +468,6 @@ private: bool MayNeedFarStub); void *getPointerToGVIndirectSym(GlobalValue *V, void *Reference); }; - - // @LOCALMOD-START - class NaClJITEmitter : public JITEmitter { - /* There are two Nacl-specific requirements that must be dealt with: the - * first is that the data and code spaces are strictly separated, and code - * must be copied (by the service runtime/validator)to its destination - * after emission and relocation have finished. - * The second is bundle alignment: neither instructions nor multi- - * instruction pseudoinstruction groups may cross bundle boundaries. - * - * Requirement 1 is dealt with jointly by NaClJITMemoryManager and - * and NaClJITEmitter. NaClJITMemoryManager separates metadata from - * code and returns pointers in the proper space - * for code (startFunctionBody, allocateStub) and data (allocateSpace, - * startExceptionTable, etc). NaClJITEmitter emits code into a separate - * memory buffer (EmissionBuffer). After startFunction allocates the - * function's memory, NaClJITEmitter's startFunction points BufferBegin, - * CurBufferPtr and BufferEnd at the EmissionBuffer (this avoids having to - * override all of the actual emission methods from JITCodeEmitter) - * JITEmitter already uses this trick for emitting a stub in the middle - * of emitting a function so it doesn't seem so terrible to do our own - * similar swapping of the pointers. - * - * Requirement 2 is bundle alignment. - * X86CodeEmitter makes several calls into JITCodeEmitter per instruction, - * to add the various bytes, constants, etc. To implement bundle alignment, - * we add methods to start and end a bundle-locked group - * (the group can include just one instruction or several). - * The X86CodeEmitter will pass-through any such markers created by the - * rewriting passes (which surround multiple-instruction groups), - * and will also generate them surrounding each individual instruction - * (there should never be more than two-deep nesting). - * When beginBundleLock is called, the CurBufferPtr is marked. When - * endBundleLock is called, it checks that the group does not cross a - * bundle boundary; if it does, it inserts nop padding as necessary. - * If padding is added, the relocations must also be fixed up; this also - * happens in endBundleLock. - * - */ - public: - NaClJITEmitter(JIT &jit, TargetMachine &TM) : - JITEmitter(jit, new NaClJITMemoryManager(), TM), - BundleLockSavedCurBufferPtr(NULL), - BundleNestCount(0), - AlignNextGroup(kNone), - GroupRelocationCount(0), - JITInfo(&jit.getJITInfo()), - kBundleSize(jit.getJITInfo().getBundleSize()), - kJumpMask(jit.getJITInfo().getJumpMask()) { - uintptr_t CodeSlabSize = MemMgr->GetDefaultCodeSlabSize(); - EmissionBuffer = MemMgr->allocateSpace(CodeSlabSize, kBundleSize); - EmissionBufferSize = CodeSlabSize; - DEBUG(dbgs() << "EmissionBuffer " << EmissionBuffer << " size " - << EmissionBufferSize << "\n"); - StubEmissionBuffer = MemMgr->allocateSpace(kBundleSize, kBundleSize); - StubEmissionBufferSize = kBundleSize; - DEBUG(dbgs() << "StubEmissionBuffer " << StubEmissionBuffer << " size " - << StubEmissionBufferSize << "\n"); - JITInfo = &jit.getJITInfo(); - } - - virtual ~NaClJITEmitter() { - } - - static inline bool classof(const JITEmitter*) { return true; } - - virtual void startFunction(MachineFunction &F) { - JITEmitter::startFunction(F); - // Make sure the emission buffer is at least as big as the allocated - // function - if (BufferEnd - BufferBegin > (intptr_t)EmissionBufferSize) { - EmissionBufferSize = std::max((uintptr_t)(BufferEnd - BufferBegin), - 2 * EmissionBufferSize); - // BumpPtrAllocator doesn't do anything when you call Deallocate. it - // will be freed on destruction - EmissionBuffer = MemMgr->allocateSpace(EmissionBufferSize, - kBundleSize); - DEBUG(dbgs() << "new EmissionBuffer " << EmissionBuffer << " size " - << EmissionBufferSize << "\n"); - } - // We ensure that the emission buffer is bundle-aligned, and constant - // pool emission should not go into code space - assert((CurBufferPtr == BufferBegin || - (int)F.getFunction()->getAlignment() > kBundleSize) && - "Pre-function data should not be emitted into code space"); - if (CurBufferPtr > BufferBegin) { - // If CurBufferPtr has been bumped forward for alignment, we need to - // pad the space with nops - memcpy(EmissionBuffer, - JITInfo->getNopSequence(CurBufferPtr - BufferBegin), - CurBufferPtr - BufferBegin); - NumNopBytes += CurBufferPtr - BufferBegin; - } - FunctionDestination = BufferBegin; - setBufferPtrs(EmissionBuffer); - } - - virtual bool finishFunction(MachineFunction &F) { - uint8_t *end = CurBufferPtr; - emitAlignment(kBundleSize); - memcpy(end, JITInfo->getNopSequence(CurBufferPtr - end), - CurBufferPtr - end); - NumNopBytes += CurBufferPtr - end; - JITInfo->setRelocationBuffer(BufferBegin); - assert(BufferBegin == EmissionBuffer); - int FunctionSize = CurBufferPtr - BufferBegin; - setBufferPtrs(FunctionDestination); - bool result = JITEmitter::finishFunction(F); - // If we ran out of memory, don't bother validating, we'll just retry - if (result) return result; - - DEBUG({ - dbgs() << "Validating " << FunctionDestination << "-" << - FunctionDestination + FunctionSize << "\n"; - if (sys::hasDisassembler()) { - dbgs() << "Disassembled code:\n"; - dbgs() << sys::disassembleBuffer(EmissionBuffer, - FunctionSize, - (uintptr_t)FunctionDestination); - } else { - dbgs() << "Binary code:\n"; - uint8_t* q = BufferBegin; - for (int i = 0; q < CurBufferPtr; q += 4, ++i) { - if (i == 4) - i = 0; - if (i == 0) - dbgs() << "JIT: " << (long)(q - BufferBegin) << ": "; - bool Done = false; - for (int j = 3; j >= 0; --j) { - if (q + j >= CurBufferPtr) - Done = true; - else - dbgs() << (unsigned short)q[j]; - } - if (Done) - break; - dbgs() << ' '; - if (i == 3) - dbgs() << '\n'; - } - dbgs()<< '\n'; - } - }); -#ifdef __native_client__ - if(nacl_dyncode_create(FunctionDestination, EmissionBuffer, - FunctionSize) != 0) { - report_fatal_error("NaCl validation failed"); - } -#endif - return result; - } - - virtual void startGVStub(const GlobalValue* GV, - unsigned StubSize, unsigned Alignment = 1) { - JITEmitter::startGVStub(GV, StubSize, Alignment); - ReusedStub = false; - assert(StubSize <= StubEmissionBufferSize); - StubDestination = BufferBegin; - setBufferPtrs(StubEmissionBuffer); - } - virtual void startGVStub(void *Buffer, unsigned StubSize) { - JITEmitter::startGVStub(Buffer, StubSize); - ReusedStub = true; - assert(StubSize <= StubEmissionBufferSize); - StubDestination = BufferBegin; - setBufferPtrs(StubEmissionBuffer); - } - virtual void finishGVStub() { - assert(CurBufferPtr - BufferBegin == kBundleSize); - - DEBUG(dbgs() << "Validating "<< BufferBegin<<"-"<<StubDestination<<"\n"); - int ValidationResult; -#ifdef __native_client__ - if (!ReusedStub) { - ValidationResult = nacl_dyncode_create(StubDestination, BufferBegin, - CurBufferPtr - BufferBegin); - } else { - // This is not a thread-safe modification because it updates the whole - // stub rather than just a jump target. However it is only used by - // eager compilation to replace a stub which is not in use yet - // (it jumps to 0). - ValidationResult = nacl_dyncode_modify(StubDestination, BufferBegin, - CurBufferPtr - BufferBegin); - } -#endif - if (ValidationResult) { - dbgs() << "NaCl stub validation failed:\n"; - if (sys::hasDisassembler()) { - dbgs() << "Disassembled code:\n"; - dbgs() << sys::disassembleBuffer(BufferBegin, - CurBufferPtr-BufferBegin, - (uintptr_t)StubDestination); - } - report_fatal_error("Stub validation failed"); - } - setBufferPtrs(StubDestination); - JITEmitter::finishGVStub(); - } - - /// allocateSpace - Allocates *data* space, rather than space in the - // current code block. - virtual void *allocateSpace(uintptr_t Size, unsigned Alignment) { - return MemMgr->allocateSpace(Size, Alignment); - } - - virtual void StartMachineBasicBlock(MachineBasicBlock *MBB) { - uint8_t *end = CurBufferPtr; - emitAlignment(MBB->getAlignment()); - memcpy(end, JITInfo->getNopSequence(CurBufferPtr - end), - CurBufferPtr - end); - NumNopBytes += CurBufferPtr - end; - JITEmitter::StartMachineBasicBlock(MBB); - } - - /// beginBundleLock - Save the current location of CurBufferPtr so we can - // tell if the block crosses a bundle boundary - virtual void beginBundleLock() { - assert(BundleNestCount <= 2 && "Bundle-locked groups can't be nested"); - if (++BundleNestCount == 2) return; - DEBUG(dbgs() << "begin lock, buffer begin:end:cur "<<BufferBegin<<" "<< - BufferEnd<< " "<<CurBufferPtr << "\n"); - BundleLockSavedCurBufferPtr = CurBufferPtr; - GroupRelocationCount = 0; - } - - /// endBundleLock - Check if the group crosses a bundle boundary. If so - // (or if the group must be aligned to the end of a bundle), move the - // group and add appropriate padding - virtual void endBundleLock() { - assert(BundleNestCount > 0 && "mismatched bundle-lock start/end"); - if (--BundleNestCount > 0) return; - DEBUG(dbgs() <<"end lock, buffer begin:end:cur:savd "<<BufferBegin<<" "<< - BufferEnd<< " "<<CurBufferPtr <<" "<< - BundleLockSavedCurBufferPtr<<"\n"); - - int GroupLen = CurBufferPtr - BundleLockSavedCurBufferPtr; - if (BufferEnd - CurBufferPtr < - GroupLen + kBundleSize) { - // Added padding can be no more than kBundleSize. Retry if there's any - // possibility of overflow - CurBufferPtr = BufferEnd; - AlignNextGroup = kNone; - return; - } - // Space left in the current bundle - int SpaceLeft = (((intptr_t)BundleLockSavedCurBufferPtr + kBundleSize) - & kJumpMask) - (intptr_t)BundleLockSavedCurBufferPtr; - int TotalPadding = 0; - if (SpaceLeft < GroupLen || AlignNextGroup == kBegin) { - DEBUG(dbgs() << "space " << SpaceLeft <<" len "<<GroupLen<<"\n"); - memmove(BundleLockSavedCurBufferPtr + SpaceLeft, - BundleLockSavedCurBufferPtr, GroupLen); - memcpy(BundleLockSavedCurBufferPtr, JITInfo->getNopSequence(SpaceLeft), - SpaceLeft); - NumNopBytes += SpaceLeft; - assert(CurBufferPtr == BundleLockSavedCurBufferPtr + GroupLen); - CurBufferPtr += SpaceLeft; - BundleLockSavedCurBufferPtr += SpaceLeft; - TotalPadding = SpaceLeft; - SpaceLeft = kBundleSize; - } - - if (AlignNextGroup == kEnd) { - DEBUG(dbgs() << "alignend, space len "<<SpaceLeft<<" "<<GroupLen<<"\n"); - int MoveDistance = SpaceLeft - GroupLen; - memmove(BundleLockSavedCurBufferPtr + MoveDistance, - BundleLockSavedCurBufferPtr, GroupLen); - memcpy(BundleLockSavedCurBufferPtr, - JITInfo->getNopSequence(MoveDistance), MoveDistance); - NumNopBytes += MoveDistance; - CurBufferPtr += MoveDistance; - TotalPadding += MoveDistance; - } - - AlignNextGroup = kNone; - - assert(CurBufferPtr <= BufferEnd && "Bundled group caused buf overflow"); - if (TotalPadding && GroupRelocationCount) { - assert(Relocations.size() >= GroupRelocationCount && - "Too many relocations recorded for this group"); - for(std::vector<MachineRelocation>::reverse_iterator I = - Relocations.rbegin(); GroupRelocationCount > 0; - ++I, GroupRelocationCount--) { - int NewOffset = I->getMachineCodeOffset() - + TotalPadding; - I->setMachineCodeOffset(NewOffset); - } - } - } - - virtual void alignToBundleBeginning() { - // mark that the next locked group must be aligned to bundle start - // (e.g. an indirect branch target) - assert(AlignNextGroup == kNone && "Conflicting group alignments"); - AlignNextGroup = kBegin; - } - - virtual void alignToBundleEnd() { - // mark that the next locked group must be aligned to bundle end (e.g. a - // call) - assert(AlignNextGroup == kNone && "Conflicting group alignments"); - AlignNextGroup = kEnd; - } - - virtual uintptr_t getCurrentPCValue() const { - // return destination PC value rather than generating location - if (BufferBegin == EmissionBuffer) { - return (uintptr_t)(FunctionDestination + (CurBufferPtr - BufferBegin)); - } else if (BufferBegin == StubEmissionBuffer) { - return (uintptr_t)(StubDestination + (CurBufferPtr - BufferBegin)); - } else { - return (uintptr_t)CurBufferPtr; - } - } - - // addRelocation gets called in the middle of emitting an instruction, and - // creates the relocation based on the instruction's current position in - // the emission buffer; however it could get moved if it crosses the bundle - // boundary. so we intercept relocation creation and adjust newly-created - // relocations if necessary - virtual void addRelocation(const MachineRelocation &MR) { - GroupRelocationCount++; - JITEmitter::addRelocation(MR); - } - - private: - typedef enum _GroupAlign { kNone, kBegin, kEnd } GroupAlign; - // FunctionDestination points to the final destination for the function - // (i.e. where it will be copied after validation) - uint8_t *FunctionDestination; - uint8_t *BundleLockSavedCurBufferPtr; - int BundleNestCount; // should not exceed 2 - GroupAlign AlignNextGroup; - unsigned GroupRelocationCount; - uint8_t *EmissionBuffer; - uintptr_t EmissionBufferSize; - - bool ReusedStub; - uint8_t *StubDestination; - uint8_t *StubEmissionBuffer; - uintptr_t StubEmissionBufferSize; - - TargetJITInfo *JITInfo; - const int kBundleSize; - const int32_t kJumpMask; - - // Set the buffer pointers (begin, cur, end) so they point into the buffer - // at dest, preserving their relative positions - void setBufferPtrs(uint8_t* dest) { - BufferEnd = dest + (BufferEnd - BufferBegin); - CurBufferPtr = dest + (CurBufferPtr - BufferBegin); - BufferBegin = dest; - } -}; } void CallSiteValueMapConfig::onDelete(JITResolverState *JRS, Function *F) { @@ -1292,12 +934,6 @@ bool JITEmitter::finishFunction(MachineFunction &F) { // Mark code region readable and executable if it's not so already. MemMgr->setMemoryExecutable(); - // @LOCALMOD-START -#ifndef __native_client__ - // In NaCl, we haven't yet validated and copied the function code to the - // destination yet, so there is nothing to disassemble. Furthermore we can't - // touch the destination because it may not even be mapped yet - // @LOCALMOD-END DEBUG({ if (sys::hasDisassembler()) { dbgs() << "JIT: Disassembled code:\n"; @@ -1327,7 +963,6 @@ bool JITEmitter::finishFunction(MachineFunction &F) { dbgs()<< '\n'; } }); -#endif // @LOCALMOD if (JITExceptionHandling) { uintptr_t ActualSize = 0; @@ -1612,14 +1247,7 @@ void JITEmitter::EmittedFunctionConfig::onRAUW( JITCodeEmitter *JIT::createEmitter(JIT &jit, JITMemoryManager *JMM, TargetMachine &tm) { -// @LOCALMOD-START -#ifndef __native_client__ return new JITEmitter(jit, JMM, tm); -#else - assert(!JMM && "NaCl does not support custom memory managers"); - return new NaClJITEmitter(jit, tm); -#endif -// @LOCALMOD-END } // getPointerToFunctionOrStub - If the specified function has been diff --git a/lib/ExecutionEngine/JIT/NaClJITMemoryManager.cpp b/lib/ExecutionEngine/JIT/NaClJITMemoryManager.cpp deleted file mode 100644 index d1b5ee704a..0000000000 --- a/lib/ExecutionEngine/JIT/NaClJITMemoryManager.cpp +++ /dev/null @@ -1,431 +0,0 @@ -//===-- NaClJITMemoryManager.cpp - Memory Allocator for JIT'd code --------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the NaClJITMemoryManager class. -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "jit" -#include "llvm/ExecutionEngine/NaClJITMemoryManager.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Config/config.h" -#include <vector> - -#if defined(__linux__) || defined(__native_client__) -#if defined(HAVE_SYS_STAT_H) -#include <sys/stat.h> -#endif -#include <fcntl.h> -#include <unistd.h> -#endif - -using namespace llvm; - -#ifdef __native_client__ -// etext is guarded by ifdef so the code still compiles on non-ELF platforms -extern char etext; -#endif - -// The way NaCl linking is currently setup, there is a gap between the text -// segment and the rodata segment where we can fill dyncode. The text ends -// at etext, but there's no symbol for the start of rodata. Currently the -// linker script puts it at 0x11000000 -// If we run out of space there, we can also allocate below the text segment -// and keep going downward until we run into code loaded by the dynamic -// linker. (TODO(dschuff): make that work) -// For now, just start at etext and go until we hit rodata - -// It's an open issue that lazy jitting is not thread safe (PR5184). However -// NaCl's dyncode_create solves exactly this problem, so in the future -// this allocator could (should?) be made thread safe - -const size_t NaClJITMemoryManager::kStubSlabSize; -const size_t NaClJITMemoryManager::kDataSlabSize; -const size_t NaClJITMemoryManager::kCodeSlabSize; - -// TODO(dschuff) fix allocation start (etext + 64M is hopefully after where -// glibc is loaded) and limit (maybe need a linker-provide symbol for the start -// of the IRT or end of the segment gap) -// (also fix allocateCodeSlab and maybe allocateStubSlab at that time) -// what we really need is a usable nacl_dyncode_alloc(), but this could still -// be improved upon using dl_iterate_phdr -const static intptr_t kNaClSegmentGapEnd = 0x11000000; - -NaClJITMemoryManager::NaClJITMemoryManager() : - AllocatableRegionLimit((uint8_t *)kNaClSegmentGapEnd), - NextCode(AllocatableRegionStart), GOTBase(NULL) { -#ifdef __native_client__ - AllocatableRegionStart = (uint8_t *)&etext + 1024*1024*64; -#else - assert(false && "NaClJITMemoryManager will not work outside NaCl sandbox"); -#endif - AllocatableRegionStart = - (uint8_t *)RoundUpToAlignment((uint64_t)AllocatableRegionStart, - kBundleSize); - NextCode = AllocatableRegionStart; - - // Allocate 1 stub slab to get us started - CurrentStubSlab = allocateStubSlab(0); - InitFreeList(&CodeFreeListHead); - InitFreeList(&DataFreeListHead); - - DEBUG(dbgs() << "NaClJITMemoryManager: AllocatableRegionStart " << - AllocatableRegionStart << " Limit " << AllocatableRegionLimit << "\n"); -} - -NaClJITMemoryManager::~NaClJITMemoryManager() { - delete [] GOTBase; - DestroyFreeList(CodeFreeListHead); - DestroyFreeList(DataFreeListHead); -} - -FreeListNode *NaClJITMemoryManager::allocateCodeSlab(size_t MinSize) { - FreeListNode *node = new FreeListNode(); - if (AllocatableRegionLimit - NextCode < (int)kCodeSlabSize) { - // TODO(dschuff): might be possible to try the space below text segment? - report_fatal_error("Ran out of code space"); - } - node->address = NextCode; - node->size = std::max(kCodeSlabSize, MinSize); - NextCode += node->size; - DEBUG(dbgs() << "allocated code slab " << NextCode - node->size << "-" << - NextCode << "\n"); - return node; -} - -SimpleSlab NaClJITMemoryManager::allocateStubSlab(size_t MinSize) { - SimpleSlab s; - DEBUG(dbgs() << "allocateStubSlab: "); - // It's a little weird to just allocate and throw away the FreeListNode, but - // since code region allocation is still a bit ugly and magical, I decided - // it's better to reuse allocateCodeSlab than duplicate the logic. - FreeListNode *n = allocateCodeSlab(MinSize); - s.address = n->address; - s.size = n->size; - s.next_free = n->address; - delete n; - return s; -} - -FreeListNode *NaClJITMemoryManager::allocateDataSlab(size_t MinSize) { - FreeListNode *node = new FreeListNode; - size_t size = std::max(kDataSlabSize, MinSize); - node->address = (uint8_t*)DataAllocator.Allocate(size, kBundleSize); - node->size = size; - return node; -} - -void NaClJITMemoryManager::InitFreeList(FreeListNode **Head) { - // Make sure there is always at least one entry in the free list - *Head = new FreeListNode; - (*Head)->Next = (*Head)->Prev = *Head; - (*Head)->size = 0; -} - -void NaClJITMemoryManager::DestroyFreeList(FreeListNode *Head) { - FreeListNode *n = Head->Next; - while(n != Head) { - FreeListNode *next = n->Next; - delete n; - n = next; - } - delete Head; -} - -FreeListNode *NaClJITMemoryManager::FreeListAllocate(uintptr_t &ActualSize, - FreeListNode *Head, - FreeListNode * (NaClJITMemoryManager::*allocate)(size_t)) { - FreeListNode *candidateBlock = Head; - FreeListNode *iter = Head->Next; - - uintptr_t largest = candidateBlock->size; - // Search for the largest free block - while (iter != Head) { - if (iter->size > largest) { - largest = iter->size; - candidateBlock = iter; - } - iter = iter->Next; - } - - if (largest < ActualSize || largest == 0) { - candidateBlock = (this->*allocate)(ActualSize); - } else { - candidateBlock->RemoveFromFreeList(); - } - return candidateBlock; -} - -void NaClJITMemoryManager::FreeListFinishAllocation(FreeListNode *Block, - FreeListNode *Head, uint8_t *AllocationStart, uint8_t *AllocationEnd, - AllocationTable &Table) { - assert(AllocationEnd > AllocationStart); - assert(Block->address == AllocationStart); - uint8_t *End = (uint8_t *)RoundUpToAlignment((uint64_t)AllocationEnd, - kBundleSize); - assert(End <= Block->address + Block->size); - int AllocationSize = End - Block->address; - Table[AllocationStart] = AllocationSize; - - Block->size -= AllocationSize; - if (Block->size >= kBundleSize * 2) {//TODO(dschuff): better heuristic? - Block->address = End; - Block->AddToFreeList(Head); - } else { - delete Block; - } - DEBUG(dbgs()<<"FinishAllocation size "<< AllocationSize <<" end "<<End<<"\n"); -} - -void NaClJITMemoryManager::FreeListDeallocate(FreeListNode *Head, - AllocationTable &Table, - void *Body) { - uint8_t *Allocation = (uint8_t *)Body; - DEBUG(dbgs() << "deallocating "<<Body<<" "); - assert(Table.count(Allocation) && "FreeList Deallocation not found in table"); - FreeListNode *Block = new FreeListNode; - Block->address = Allocation; - Block->size = Table[Allocation]; - Block->AddToFreeList(Head); - DEBUG(dbgs() << "deallocated "<< Allocation<< " size " << Block->size <<"\n"); -} - -uint8_t *NaClJITMemoryManager::startFunctionBody(const Function *F, - uintptr_t &ActualSize) { - CurrentCodeBlock = FreeListAllocate(ActualSize, CodeFreeListHead, - &NaClJITMemoryManager::allocateCodeSlab); - DEBUG(dbgs() << "startFunctionBody CurrentBlock " << CurrentCodeBlock << - " addr " << CurrentCodeBlock->address << "\n"); - ActualSize = CurrentCodeBlock->size; - return CurrentCodeBlock->address; -} - -void NaClJITMemoryManager::endFunctionBody(const Function *F, - uint8_t *FunctionStart, - uint8_t *FunctionEnd) { - DEBUG(dbgs() << "endFunctionBody "); - FreeListFinishAllocation(CurrentCodeBlock, CodeFreeListHead, - FunctionStart, FunctionEnd, AllocatedFunctions); - -} - -uint8_t *NaClJITMemoryManager::allocateCodeSection(uintptr_t Size, - unsigned Alignment, - unsigned SectionID) { - llvm_unreachable("Implement me! (or don't.)"); -} - -uint8_t *NaClJITMemoryManager::allocateDataSection(uintptr_t Size, - unsigned Alignment, - unsigned SectionID, - bool IsReadOnly) { - return (uint8_t *)DataAllocator.Allocate(Size, Alignment); -} - -void NaClJITMemoryManager::deallocateFunctionBody(void *Body) { - DEBUG(dbgs() << "deallocateFunctionBody, "); - if (Body) FreeListDeallocate(CodeFreeListHead, AllocatedFunctions, Body); -} - -uint8_t *NaClJITMemoryManager::allocateStub(const GlobalValue* F, - unsigned StubSize, - unsigned Alignment) { - uint8_t *StartAddress = (uint8_t *)(uintptr_t) - RoundUpToAlignment((uintptr_t)CurrentStubSlab.next_free, Alignment); - if (StartAddress + StubSize > - CurrentStubSlab.address + CurrentStubSlab.size) { - CurrentStubSlab = allocateStubSlab(kStubSlabSize); - StartAddress = (uint8_t *)(uintptr_t) - RoundUpToAlignment((uintptr_t)CurrentStubSlab.next_free, Alignment); - } - CurrentStubSlab.next_free = StartAddress + StubSize; - DEBUG(dbgs() <<"allocated stub "<<StartAddress<< " size "<<StubSize<<"\n"); - return StartAddress; -} - -uint8_t *NaClJITMemoryManager::allocateSpace(intptr_t Size, - unsigned Alignment) { - uint8_t *r = (uint8_t*)DataAllocator.Allocate(Size, Alignment); - DEBUG(dbgs() << "allocateSpace " << Size <<"/"<<Alignment<<" ret "<<r<<"\n"); - return r; -} - -uint8_t *NaClJITMemoryManager::allocateGlobal(uintptr_t Size, - unsigned Alignment) { - uint8_t *r = (uint8_t*)DataAllocator.Allocate(Size, Alignment); - DEBUG(dbgs() << "allocateGlobal " << Size <<"/"<<Alignment<<" ret "<<r<<"\n"); - return r; -} - -uint8_t* NaClJITMemoryManager::startExceptionTable(const Function* F, - uintptr_t &ActualSize) { - CurrentDataBlock = FreeListAllocate(ActualSize, DataFreeListHead, - &NaClJITMemoryManager::allocateDataSlab); - DEBUG(dbgs() << "startExceptionTable CurrentBlock " << CurrentDataBlock << - " addr " << CurrentDataBlock->address << "\n"); - ActualSize = CurrentDataBlock->size; - return CurrentDataBlock->address; -} - -void NaClJITMemoryManager::endExceptionTable(const Function *F, - uint8_t *TableStart, - uint8_t *TableEnd, uint8_t* FrameRegister) { - DEBUG(dbgs() << "endExceptionTable "); - FreeListFinishAllocation(CurrentDataBlock, DataFreeListHead, - TableStart, TableEnd, AllocatedTables); -} - -void NaClJITMemoryManager::deallocateExceptionTable(void *ET) { - DEBUG(dbgs() << "deallocateExceptionTable, "); - if (ET) FreeListDeallocate(DataFreeListHead, AllocatedTables, ET); -} - -// Copy of DefaultJITMemoryManager's implementation -void NaClJITMemoryManager::AllocateGOT() { - assert(GOTBase == 0 && "Cannot allocate the got multiple times"); - GOTBase = new uint8_t[sizeof(void*) * 8192]; - HasGOT = true; -} - -//===----------------------------------------------------------------------===// -// getPointerToNamedFunction() implementation. -// This code is pasted directly from r153607 of JITMemoryManager.cpp and has -// never been tested. It most likely doesn't work inside the sandbox. -//===----------------------------------------------------------------------===// - -// AtExitHandlers - List of functions to call when the program exits, -// registered with the atexit() library function. -static std::vector<void (*)()> AtExitHandlers; - -/// runAtExitHandlers - Run any functions registered by the program's -/// calls to atexit(3), which we intercept and store in -/// AtExitHandlers. -/// -static void runAtExitHandlers() { - while (!AtExitHandlers.empty()) { - void (*Fn)() = AtExitHandlers.back(); - AtExitHandlers.pop_back(); - Fn(); - } -} - -//===----------------------------------------------------------------------===// -// Function stubs that are invoked instead of certain library calls -// -// Force the following functions to be linked in to anything that uses the -// JIT. This is a hack designed to work around the all-too-clever Glibc -// strategy of making these functions work differently when inlined vs. when -// not inlined, and hiding their real definitions in a separate archive file -// that the dynamic linker can't see. For more info, search for -// 'libc_nonshared.a' on Google, or read http://llvm.org/PR274. -#if defined(__linux__) -/* stat functions are redirecting to __xstat with a version number. On x86-64 - * linking with libc_nonshared.a and -Wl,--export-dynamic doesn't make 'stat' - * available as an exported symbol, so we have to add it explicitly. - */ -namespace { -class StatSymbols { -public: - StatSymbols() { - sys::DynamicLibrary::AddSymbol("stat", (void*)(intptr_t)stat); - sys::DynamicLibrary::AddSymbol("fstat", (void*)(intptr_t)fstat); - sys::DynamicLibrary::AddSymbol("lstat", (void*)(intptr_t)lstat); - sys::DynamicLibrary::AddSymbol("stat64", (void*)(intptr_t)stat64); - sys::DynamicLibrary::AddSymbol("\x1stat64", (void*)(intptr_t)stat64); - sys::DynamicLibrary::AddSymbol("\x1open64", (void*)(intptr_t)open64); - sys::DynamicLibrary::AddSymbol("\x1lseek64", (void*)(intptr_t)lseek64); - sys::DynamicLibrary::AddSymbol("fstat64", (void*)(intptr_t)fstat64); - sys::DynamicLibrary::AddSymbol("lstat64", (void*)(intptr_t)lstat64); - sys::DynamicLibrary::AddSymbol("atexit", (void*)(intptr_t)atexit); - sys::DynamicLibrary::AddSymbol("mknod", (void*)(intptr_t)mknod); - } -}; -} -static StatSymbols initStatSymbols; -#endif // __linux__ - -// jit_exit - Used to intercept the "exit" library call. -static void jit_exit(int Status) { - runAtExitHandlers(); // Run atexit handlers... - exit(Status); -} - -// jit_atexit - Used to intercept the "atexit" library call. -static int jit_atexit(void (*Fn)()) { - AtExitHandlers.push_back(Fn); // Take note of atexit handler... - return 0; // Always successful -} - -static int jit_noop() { - return 0; -} - -//===----------------------------------------------------------------------===// -// -/// getPointerToNamedFunction - This method returns the address of the specified -/// function by using the dynamic loader interface. As such it is only useful -/// for resolving library symbols, not code generated symbols. -/// -void *NaClJITMemoryManager::getPointerToNamedFunction(const std::string &Name, - bool AbortOnFailure) { - // Check to see if this is one of the functions we want to intercept. Note, - // we cast to intptr_t here to silence a -pedantic warning that complains - // about casting a function pointer to a normal pointer. - if (Name == "exit") return (void*)(intptr_t)&jit_exit; - if (Name == "atexit") return (void*)(intptr_t)&jit_atexit; - - // We should not invoke parent's ctors/dtors from generated main()! - // On Mingw and Cygwin, the symbol __main is resolved to - // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors - // (and register wrong callee's dtors with atexit(3)). - // We expect ExecutionEngine::runStaticConstructorsDestructors() - // is called before ExecutionEngine::runFunctionAsMain() is called. - if (Name == "__main") return (void*)(intptr_t)&jit_noop; - - const char *NameStr = Name.c_str(); - // If this is an asm specifier, skip the sentinal. - if (NameStr[0] == 1) ++NameStr; - - // If it's an external function, look it up in the process image... - void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); - if (Ptr) return Ptr; - - // If it wasn't found and if it starts with an underscore ('_') character, - // try again without the underscore. - if (NameStr[0] == '_') { - Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1); - if (Ptr) return Ptr; - } - - // Darwin/PPC adds $LDBLStub suffixes to various symbols like printf. These - // are references to hidden visibility symbols that dlsym cannot resolve. - // If we have one of these, strip off $LDBLStub and try again. -#if defined(__APPLE__) && defined(__ppc__) - if (Name.size() > 9 && Name[Name.size()-9] == '$' && - memcmp(&Name[Name.size()-8], "LDBLStub", 8) == 0) { - // First try turning $LDBLStub into $LDBL128. If that fails, strip it off. - // This mirrors logic in libSystemStubs.a. - std::string Prefix = std::string(Name.begin(), Name.end()-9); - if (void *Ptr = getPointerToNamedFunction(Prefix+"$LDBL128", false)) - return Ptr; - if (void *Ptr = getPointerToNamedFunction(Prefix, false)) - return Ptr; - } -#endif - - if (AbortOnFailure) { - report_fatal_error("Program used external function '"+Name+ - "' which could not be resolved!"); - } - return 0; -} diff --git a/lib/Target/X86/X86NaClJITInfo.cpp b/lib/Target/X86/X86NaClJITInfo.cpp deleted file mode 100644 index e5ccbf960d..0000000000 --- a/lib/Target/X86/X86NaClJITInfo.cpp +++ /dev/null @@ -1,393 +0,0 @@ -//===-- X86JITInfo.cpp - Implement the JIT interfaces for the X86 target --===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the JIT interfaces for the X86 target on Native Client -// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "jit" -#include "X86NaClJITInfo.h" -#include "X86Relocations.h" -#include "X86Subtarget.h" -#include "X86TargetMachine.h" -#include <cstdlib> -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Disassembler.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Valgrind.h" -#ifdef __native_client__ -#include <nacl/nacl_dyncode.h> -#endif - -using namespace llvm; - -extern cl::opt<int> FlagSfiX86JmpMask; - -// Determine the platform we're running on -#if defined (__x86_64__) || defined (_M_AMD64) || defined (_M_X64) -# define X86_64_JIT -#elif defined(__i386__) || defined(i386) || defined(_M_IX86) -# define X86_32_JIT -#elif defined(__pnacl__) -#warning "PNaCl does not yet have JIT support" -#else -#error "Should not be building X86NaClJITInfo on non-x86" -// TODO(dschuff): make this work under pnacl self-build? -#endif - -// Get the ASMPREFIX for the current host. This is often '_'. -#ifndef __USER_LABEL_PREFIX__ -#define __USER_LABEL_PREFIX__ -#endif -#define GETASMPREFIX2(X) #X -#define GETASMPREFIX(X) GETASMPREFIX2(X) -#define ASMPREFIX GETASMPREFIX(__USER_LABEL_PREFIX__) - -# define SIZE(sym) ".size " #sym ", . - " #sym "\n" -# define TYPE_FUNCTION(sym) ".type " #sym ", @function\n" - -void X86NaClJITInfo::replaceMachineCodeForFunction(void *Old, void *New) { - // We don't know the original instruction boundaries, so we replace the - // whole bundle. - uint8_t buf[kBundleSize]; - buf[0] = 0xE9; // Emit JMP opcode. - intptr_t OldAddr = ((uintptr_t)Old + 1); - uint32_t NewOffset = (intptr_t)New - OldAddr - 4;// PC-relative offset of new - *((uint32_t*)(buf + 1)) = NewOffset; - memcpy(buf + 5, getNopSequence(kBundleSize - 5), kBundleSize - 5); - -#ifdef __native_client__ - if(nacl_dyncode_create(Old, buf, kBundleSize)) { - report_fatal_error("machine code replacement failed"); - } -#endif - - // X86 doesn't need to invalidate the processor cache, so just invalidate - // Valgrind's cache directly. - sys::ValgrindDiscardTranslations(Old, 5); -} - -/// JITCompilerFunction - This contains the address of the JIT function used to -/// compile a function lazily. -static TargetJITInfo::JITCompilerFn JITCompilerFunction; - -extern "C" { -#if defined(X86_64_JIT) || defined(__pnacl__) || !defined(__native_client__) -void X86NaClCompilationCallback(void) { -//TODO(dschuff): implement for X86-64 -} -void X86NaClCompilationCallback_fastcc(void) { -//TODO(dschuff): implement for X86-64 -} -#else -// Chrome system requirements include PIII, So SSE is present. -// For now this is the same as X86CompilationCallback_SSE -// In the future we could emit this rather than defining it with asm, for -// compatibility with pnacl self-build -// Also omit CFI junk (which is #defined away) - -// The difference between the 2 wrapper variants is that the first returns -// through ecx and the 2nd returns through eax. The fastcc calling convention -// uses ecx to pass arguments, and the C calling convention uses eax to pass -// arguments with the 'inreg' attribute, so we make sure not to clobber it. -// Returning through eax for fastcc and ecx for C clobbers the 'nest' parameter -// breaking nested functions (which are not supported by clang in any case). - -void X86NaClCompilationCallback(void); -asm( - ".text\n" - ".align 32\n" - ".globl " ASMPREFIX "X86NaClCompilationCallback\n" - TYPE_FUNCTION(X86NaClCompilationCallback) - ASMPREFIX "X86NaClCompilationCallback:\n" - "pushl %ebp\n" - "movl %esp, %ebp\n" // Standard prologue - "pushl %eax\n" - "pushl %edx\n" // Save EAX/EDX/ECX - "pushl %ecx\n" - "andl $-16, %esp\n" // Align ESP on 16-byte boundary - // Save all XMM arg registers - "subl $64, %esp\n" - // FIXME: provide frame move information for xmm registers. - // This can be tricky, because CFA register is ebp (unaligned) - // and we need to produce offsets relative to it. - "movaps %xmm0, (%esp)\n" - "movaps %xmm1, 16(%esp)\n" - "movaps %xmm2, 32(%esp)\n" - "movaps %xmm3, 48(%esp)\n" - "subl $16, %esp\n" - "movl 4(%ebp), %eax\n" // Pass prev frame and return address - "movl %eax, 4(%esp)\n" - "movl %ebp, (%esp)\n" - "call " ASMPREFIX "X86NaClCompilationCallback2\n" - "addl $16, %esp\n" - "movaps 48(%esp), %xmm3\n" - "movaps 32(%esp), %xmm2\n" - "movaps 16(%esp), %xmm1\n" - "movaps (%esp), %xmm0\n" - "movl %ebp, %esp\n" // Restore ESP - "subl $12, %esp\n" - "popl %ecx\n" - "popl %edx\n" - "popl %eax\n" - "popl %ebp\n" - "popl %ecx\n" - "nacljmp %ecx\n" - SIZE(X86NaClCompilationCallback) -); - - - -void X86NaClCompilationCallback_fastcc(void); -asm( - ".text\n" - ".align 32\n" - ".globl " ASMPREFIX "X86NaClCompilationCallback_fastcc\n" - TYPE_FUNCTION(X86NaClCompilationCallback_fastcc) - ASMPREFIX "X86NaClCompilationCallback_fastcc:\n" - "pushl %ebp\n" - "movl %esp, %ebp\n" // Standard prologue - "pushl %eax\n" - "pushl %edx\n" // Save EAX/EDX/ECX - "pushl %ecx\n" - "andl $-16, %esp\n" // Align ESP on 16-byte boundary - // Save all XMM arg registers - "subl $64, %esp\n" - // FIXME: provide frame move information for xmm registers. - // This can be tricky, because CFA register is ebp (unaligned) - // and we need to produce offsets relative to it. - "movaps %xmm0, (%esp)\n" - "movaps %xmm1, 16(%esp)\n" - "movaps %xmm2, 32(%esp)\n" - "movaps %xmm3, 48(%esp)\n" - "subl $16, %esp\n" - "movl 4(%ebp), %eax\n" // Pass prev frame and return address - "movl %eax, 4(%esp)\n" - "movl %ebp, (%esp)\n" - "call " ASMPREFIX "X86NaClCompilationCallback2\n" - "addl $16, %esp\n" - "movaps 48(%esp), %xmm3\n" - "movaps 32(%esp), %xmm2\n" - "movaps 16(%esp), %xmm1\n" - "movaps (%esp), %xmm0\n" - "movl %ebp, %esp\n" // Restore ESP - "subl $12, %esp\n" - "popl %ecx\n" - "popl %edx\n" - "popl %eax\n" - "popl %ebp\n" - "popl %eax\n" - "nacljmp %eax\n" - SIZE(X86NaClCompilationCallback_fastcc) -); -#endif - -/// X86CompilationCallback2 - This is the target-specific function invoked by the -/// function stub when we did not know the real target of a call. This function -/// must locate the start of the stub or call site and pass it into the JIT -/// compiler function. - -// A stub has the following format: -// | Jump opcode (1 byte) | Jump target +22 bytes | 3 bytes of NOPs -// | 18 bytes of NOPs | 1 halt | Call opcode (1 byte) | call target -// The jump targets the call at the end of the bundle, which targets the -// compilation callback. Once the compilation callback JITed the target -// function it replaces the first 8 bytes of the stub in a single atomic -// operation, retargeting the jump at the JITed function. - -static uint8_t *BundleRewriteBuffer; - -static void LLVM_ATTRIBUTE_USED -X86NaClCompilationCallback2(intptr_t *StackPtr, intptr_t RetAddr) { - // Get the return address from where the call instruction left it - intptr_t *RetAddrLoc = &StackPtr[1]; - assert(*RetAddrLoc == RetAddr && - "Could not find return address on the stack!"); - - // TODO: take a lock here. figure out whether it has to be the JIT lock or - // can be our own lock (or however we handle thread safety) -#if 0 - DEBUG(dbgs() << "In callback! Addr=" << (void*)RetAddr - << " ESP=" << (void*)StackPtr << "\n"); -#endif - - intptr_t StubStart = RetAddr - 32; - // This probably isn't necessary. I believe the corresponding code in - // X86JITInfo is vestigial, and AFAICT no non-stub calls to the compilation - // callback are generated anywhere. Still it doesn't hurt as a sanity check - bool isStub = *((unsigned char*)StubStart) == 0xE9 && - *((int32_t*)(StubStart + 1)) == 22 && - *((unsigned char*)(StubStart + 26)) == 0xF4; - - assert(isStub && "NaCl doesn't support rewriting non-stub callsites yet"); - - // Backtrack so RetAddr points inside the stub (so JITResolver can find - // which function to compile) - RetAddr -= 4; - - intptr_t NewVal = (intptr_t)JITCompilerFunction((void*)RetAddr); - - // Rewrite the stub's call target, so that we don't end up here every time we - // execute the call. - - // Get the first 8 bytes of the stub - memcpy(BundleRewriteBuffer, (void *)(StubStart), 8); - // Point the jump at the newly-JITed code - *((intptr_t *)(BundleRewriteBuffer + 1)) = NewVal - (StubStart + 5); - - // Copy the new code -#ifdef __native_client__ - if(nacl_dyncode_modify((void *)StubStart, BundleRewriteBuffer, 8)) { - report_fatal_error("dyncode_modify failed"); - } -#endif - // TODO: release the lock - - // Change our return address to execute the new jump - *RetAddrLoc = StubStart; -} - -} - -const int X86NaClJITInfo::kBundleSize; - -TargetJITInfo::LazyResolverFn -X86NaClJITInfo::getLazyResolverFunction(JITCompilerFn F) { - JITCompilerFunction = F; - return X86NaClCompilationCallback; -} - -X86NaClJITInfo::X86NaClJITInfo(X86TargetMachine &tm) : X86JITInfo(tm) { - // FIXME: does LLVM have some way of doing static initialization? -#ifndef __pnacl__ - if(posix_memalign((void **)&BundleRewriteBuffer, kBundleSize, kBundleSize)) - report_fatal_error("Could not allocate aligned memory"); -#else - BundleRewriteBuffer = NULL; -#endif - - NopString = new uint8_t[kBundleSize]; - for (int i = 0; i < kBundleSize; i++) NopString[i] = 0x90; - X86Hlt.ins = new uint8_t[1]; - X86Hlt.ins[0] = 0xf4; - X86Hlt.len = 1; -} - -X86NaClJITInfo::~X86NaClJITInfo() { - delete [] NopString; - delete [] X86Hlt.ins; -} - -TargetJITInfo::StubLayout X86NaClJITInfo::getStubLayout() { - // NaCl stubs must be full bundles because calls still have to be aligned - // even if they don't return - StubLayout Result = {kBundleSize, kBundleSize}; - return Result; -} - - -void *X86NaClJITInfo::emitFunctionStub(const Function* F, void *Target, - JITCodeEmitter &JCE) { - bool TargetsCC = Target == (void *)(intptr_t)X86NaClCompilationCallback; - - // If we target the compilation callback, swap it for a different one for - // functions using the fastcc calling convention - if(TargetsCC && F->getCallingConv() == CallingConv::Fast) { - Target = (void *)(intptr_t)X86NaClCompilationCallback_fastcc; - } - - void *Result = (void *)JCE.getCurrentPCValue(); - assert(RoundUpToAlignment((uintptr_t)Result, kBundleSize) == (uintptr_t)Result - && "Unaligned function stub"); - if (!TargetsCC) { - // Jump to the target - JCE.emitByte(0xE9); - JCE.emitWordLE((intptr_t)Target - JCE.getCurrentPCValue() - 4); - // Fill with Nops. - emitNopPadding(JCE, 27); - } else { - // Jump over 22 bytes - JCE.emitByte(0xE9); - JCE.emitWordLE(22); - // emit 3-bytes of nop to ensure an instruction boundary at 8 bytes - emitNopPadding(JCE, 3); - // emit 18 bytes of nop - emitNopPadding(JCE, 18); - // emit 1 byte of halt. This helps CompilationCallback tell whether - // we came from a stub or not - JCE.emitByte(X86Hlt.ins[0]); - // emit a call to the compilation callback - JCE.emitByte(0xE8); - JCE.emitWordLE((intptr_t)Target - JCE.getCurrentPCValue() - 4); - } - return Result; -} - -// Relocations are the same as in X86, but the address being written -// not the same as the address that the offset is relative to (see comment on -// setRelocationBuffer in X86NaClJITInfo.h -void X86NaClJITInfo::relocate(void *Function, MachineRelocation *MR, - unsigned NumRelocs, unsigned char* GOTBase) { - for (unsigned i = 0; i != NumRelocs; ++i, ++MR) { - void *RelocPos = RelocationBuffer + MR->getMachineCodeOffset(); - void *RelocTargetPos = (char*)Function + MR->getMachineCodeOffset(); - intptr_t ResultPtr = (intptr_t)MR->getResultPointer(); - switch ((X86::RelocationType)MR->getRelocationType()) { - case X86::reloc_pcrel_word: { - // PC relative relocation, add the relocated value to the value already in - // memory, after we adjust it for where the PC is. - ResultPtr = ResultPtr -(intptr_t)RelocTargetPos - 4 - MR->getConstantVal(); - *((unsigned*)RelocPos) += (unsigned)ResultPtr; - break; - } - case X86::reloc_picrel_word: { - // PIC base relative relocation, add the relocated value to the value - // already in memory, after we adjust it for where the PIC base is. - ResultPtr = ResultPtr - ((intptr_t)Function + MR->getConstantVal()); - *((unsigned*)RelocPos) += (unsigned)ResultPtr; - break; - } - case X86::reloc_absolute_word: - case X86::reloc_absolute_word_sext: - // Absolute relocation, just add the relocated value to the value already - // in memory. - *((unsigned*)RelocPos) += (unsigned)ResultPtr; - break; - case X86::reloc_absolute_dword: - *((intptr_t*)RelocPos) += ResultPtr; - break; - } - } -} - -const uint8_t *X86NaClJITInfo::getNopSequence(size_t len) const { - // TODO(dschuff): use more efficient NOPs. - // Update emitNopPadding when it happens - assert((int)len <= kBundleSize && - "Nop sequence can't be more than bundle size"); - return NopString; -} - -void X86NaClJITInfo::emitNopPadding(JITCodeEmitter &JCE, size_t len) { - for (size_t i = 0; i < len; i++) JCE.emitByte(NopString[i]); -} - -const TargetJITInfo::HaltInstruction *X86NaClJITInfo::getHalt() const { - return &X86Hlt; -} - -int X86NaClJITInfo::getBundleSize() const { - return kBundleSize; -} - -int32_t X86NaClJITInfo::getJumpMask() const { - return FlagSfiX86JmpMask; -} diff --git a/lib/Target/X86/X86NaClJITInfo.h b/lib/Target/X86/X86NaClJITInfo.h deleted file mode 100644 index 9416efeff1..0000000000 --- a/lib/Target/X86/X86NaClJITInfo.h +++ /dev/null @@ -1,75 +0,0 @@ -//=- X86NaClJITInfo.h - X86 implementation of the JIT interface --*- C++ -*-=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains the X86 implementation of the TargetJITInfo class for -// Native Client -// -//===----------------------------------------------------------------------===// - -#ifndef X86NACLJITINFO_H -#define X86NACLJITINFO_H - -#include "X86JITInfo.h" -#include "llvm/Function.h" -#include "llvm/CodeGen/JITCodeEmitter.h" -#include "llvm/Target/TargetJITInfo.h" - -namespace llvm { - class X86NaClJITInfo : public X86JITInfo { - void emitNopPadding(JITCodeEmitter &JCE, size_t len); - const X86Subtarget *Subtarget; - uintptr_t PICBase; - uint8_t *NopString; - HaltInstruction X86Hlt; - uint8_t *RelocationBuffer; - public: - static const int kBundleSize = 32; - explicit X86NaClJITInfo(X86TargetMachine &tm); - virtual ~X86NaClJITInfo(); - - virtual void replaceMachineCodeForFunction(void *Old, void *New); - - // getStubLayout - Returns the size and alignment of the largest call stub - // on X86 NaCl. - virtual StubLayout getStubLayout(); - - // Note: the emission and functions MUST NOT touch the target memory - virtual void *emitFunctionStub(const Function* F, void *Target, - JITCodeEmitter &JCE); - /// getLazyResolverFunction - Expose the lazy resolver to the JIT. - virtual LazyResolverFn getLazyResolverFunction(JITCompilerFn); - /// relocate - Before the JIT can run a block of code that has been emitted, - /// it must rewrite the code to contain the actual addresses of any - /// referenced global symbols. - virtual void relocate(void *Function, MachineRelocation *MR, - unsigned NumRelocs, unsigned char* GOTBase); - - virtual char* allocateThreadLocalMemory(size_t size) { - //TODO(dschuff) Implement TLS or decide whether X86 TLS works - assert(0 && "This target does not implement thread local storage!"); - return 0; - } - /// Return a string containing a sequence of NOPs which is valid for - /// the given length - virtual const uint8_t *getNopSequence(size_t len) const; - virtual const HaltInstruction *getHalt() const; - virtual int getBundleSize() const; - virtual int getJumpMask() const; - /// Relocations cannot happen in-place in NaCl because we can't write to - /// code. This function takes a pointer to where the code has been emitted, - /// before it is copied to the code region. The subsequent call to - /// relocate takes pointers to the target code location, but rewrites the - /// code in the relocation buffer rather than at the target - virtual void setRelocationBuffer(unsigned char * BufferBegin) { - RelocationBuffer = BufferBegin; - } - }; -} - -#endif diff --git a/lib/Target/X86/X86TargetMachine.h b/lib/Target/X86/X86TargetMachine.h index 967ce95d10..209a416c83 100644 --- a/lib/Target/X86/X86TargetMachine.h +++ b/lib/Target/X86/X86TargetMachine.h @@ -83,11 +83,7 @@ class X86_32TargetMachine : public X86TargetMachine { X86InstrInfo InstrInfo; X86SelectionDAGInfo TSInfo; X86TargetLowering TLInfo; -#ifdef __native_client__ - X86NaClJITInfo JITInfo; -#else X86JITInfo JITInfo; -#endif ScalarTargetTransformImpl STTI; X86VectorTargetTransformInfo VTTI; public: |