1 //===-- CodeMemoryManager.h - CodeMemoryManager Class -----------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See external/llvm/LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines the CodeMemoryManager class. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef BCC_CODEMEMORYMANAGER_H 15 #define BCC_CODEMEMORYMANAGER_H 16 17 #include "ExecutionEngine/Compiler.h" 18 19 #include "llvm/ExecutionEngine/JITMemoryManager.h" 20 21 #include <bcc/bcc_assert.h> 22 23 #include <map> 24 #include <utility> 25 26 #include <stddef.h> 27 #include <stdint.h> 28 29 30 namespace llvm { 31 // Forward Declaration 32 class Function; 33 class GlobalValue; 34 }; 35 36 37 namespace bcc { 38 39 ////////////////////////////////////////////////////////////////////////////// 40 // Memory manager for the code reside in memory 41 // 42 // The memory for our code emitter is very simple and is conforming to the 43 // design decisions of Android RenderScript's Exection Environment: 44 // The code, data, and symbol sizes are limited (currently 100KB.) 45 // 46 // It's very different from typical compiler, which has no limitation 47 // on the code size. How does code emitter know the size of the code 48 // it is about to emit? It does not know beforehand. We want to solve 49 // this without complicating the code emitter too much. 50 // 51 // We solve this by pre-allocating a certain amount of memory, 52 // and then start the code emission. Once the buffer overflows, the emitter 53 // simply discards all the subsequent emission but still has a counter 54 // on how many bytes have been emitted. 55 // 56 // So once the whole emission is done, if there's a buffer overflow, 57 // it re-allocates the buffer with enough size (based on the 58 // counter from previous emission) and re-emit again. 59 60 extern const unsigned int MaxCodeSize; 61 extern const unsigned int MaxGOTSize; 62 extern const unsigned int MaxGlobalVarSize; 63 64 65 class CodeMemoryManager : public llvm::JITMemoryManager { 66 private: 67 typedef std::map<const llvm::Function*, 68 std::pair<void * /* start address */, 69 void * /* end address */> > FunctionMapTy; 70 71 72 private: 73 // 74 // Our memory layout is as follows: 75 // 76 // The direction of arrows (-> and <-) shows memory's growth direction 77 // when more space is needed. 78 // 79 // @mpCodeMem: 80 // +--------------------------------------------------------------+ 81 // | Function Memory ... -> <- ... Stub/GOT | 82 // +--------------------------------------------------------------+ 83 // |<------------------ Total: @MaxCodeSize KiB ----------------->| 84 // 85 // Where size of GOT is @MaxGOTSize KiB. 86 // 87 // @mpGVMem: 88 // +--------------------------------------------------------------+ 89 // | Global variable ... -> | 90 // +--------------------------------------------------------------+ 91 // |<--------------- Total: @MaxGlobalVarSize KiB --------------->| 92 // 93 // 94 // @mCurFuncMemIdx: The current index (starting from 0) of the last byte 95 // of function code's memory usage 96 // @mCurSGMemIdx: The current index (starting from tail) of the last byte 97 // of stub/GOT's memory usage 98 // @mCurGVMemIdx: The current index (starting from tail) of the last byte 99 // of global variable's memory usage 100 // 101 uintptr_t mCurFuncMemIdx; 102 uintptr_t mCurSGMemIdx; 103 uintptr_t mCurGVMemIdx; 104 char *mpCodeMem; 105 char *mpGVMem; 106 107 // GOT Base 108 uint8_t *mpGOTBase; 109 110 FunctionMapTy mFunctionMap; 111 112 113 public: 114 CodeMemoryManager(); 115 116 virtual ~CodeMemoryManager(); 117 118 uint8_t *getCodeMemBase() const { 119 return reinterpret_cast<uint8_t*>(mpCodeMem); 120 } 121 122 // setMemoryWritable - When code generation is in progress, the code pages 123 // may need permissions changed. 124 virtual void setMemoryWritable(); 125 126 // When code generation is done and we're ready to start execution, the 127 // code pages may need permissions changed. 128 virtual void setMemoryExecutable(); 129 130 // Setting this flag to true makes the memory manager garbage values over 131 // freed memory. This is useful for testing and debugging, and is to be 132 // turned on by default in debug mode. 133 virtual void setPoisonMemory(bool poison); 134 135 136 // Global Offset Table Management 137 138 // If the current table requires a Global Offset Table, this method is 139 // invoked to allocate it. This method is required to set HasGOT to true. 140 virtual void AllocateGOT(); 141 142 // If this is managing a Global Offset Table, this method should return a 143 // pointer to its base. 144 virtual uint8_t *getGOTBase() const { 145 return mpGOTBase; 146 } 147 148 // Main Allocation Functions 149 150 // When we start JITing a function, the JIT calls this method to allocate a 151 // block of free RWX memory, which returns a pointer to it. If the JIT wants 152 // to request a block of memory of at least a certain size, it passes that 153 // value as ActualSize, and this method returns a block with at least that 154 // much space. If the JIT doesn't know ahead of time how much space it will 155 // need to emit the function, it passes 0 for the ActualSize. In either 156 // case, this method is required to pass back the size of the allocated 157 // block through ActualSize. The JIT will be careful to not write more than 158 // the returned ActualSize bytes of memory. 159 virtual uint8_t *startFunctionBody(const llvm::Function *F, 160 uintptr_t &ActualSize); 161 162 // This method is called by the JIT to allocate space for a function stub 163 // (used to handle limited branch displacements) while it is JIT compiling a 164 // function. For example, if foo calls bar, and if bar either needs to be 165 // lazily compiled or is a native function that exists too far away from the 166 // call site to work, this method will be used to make a thunk for it. The 167 // stub should be "close" to the current function body, but should not be 168 // included in the 'actualsize' returned by startFunctionBody. 169 virtual uint8_t *allocateStub(const llvm::GlobalValue *F, 170 unsigned StubSize, 171 unsigned Alignment) { 172 return allocateSGMemory(StubSize, Alignment); 173 } 174 175 // This method is called when the JIT is done codegen'ing the specified 176 // function. At this point we know the size of the JIT compiled function. 177 // This passes in FunctionStart (which was returned by the startFunctionBody 178 // method) and FunctionEnd which is a pointer to the actual end of the 179 // function. This method should mark the space allocated and remember where 180 // it is in case the client wants to deallocate it. 181 virtual void endFunctionBody(const llvm::Function *F, 182 uint8_t *FunctionStart, 183 uint8_t *FunctionEnd); 184 185 // Allocate a (function code) memory block of the given size. This method 186 // cannot be called between calls to startFunctionBody and endFunctionBody. 187 virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); 188 189 // Allocate memory for a global variable. 190 virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); 191 192 // Free the specified function body. The argument must be the return value 193 // from a call to startFunctionBody() that hasn't been deallocated yet. This 194 // is never called when the JIT is currently emitting a function. 195 virtual void deallocateFunctionBody(void *Body); 196 197 // When we finished JITing the function, if exception handling is set, we 198 // emit the exception table. 199 virtual uint8_t *startExceptionTable(const llvm::Function *F, 200 uintptr_t &ActualSize) { 201 bccAssert(false && 202 "Exception is not allowed in our language specification"); 203 return NULL; 204 } 205 206 // This method is called when the JIT is done emitting the exception table. 207 virtual void endExceptionTable(const llvm::Function *F, uint8_t *TableStart, 208 uint8_t *TableEnd, uint8_t *FrameRegister) { 209 bccAssert(false && 210 "Exception is not allowed in our language specification"); 211 } 212 213 // Free the specified exception table's memory. The argument must be the 214 // return value from a call to startExceptionTable() that hasn't been 215 // deallocated yet. This is never called when the JIT is currently emitting 216 // an exception table. 217 virtual void deallocateExceptionTable(void *ET) { 218 bccAssert(false && 219 "Exception is not allowed in our language specification"); 220 } 221 222 // Below are the methods we create 223 void reset(); 224 225 226 private: 227 intptr_t getFreeCodeMemSize() const { 228 return mCurSGMemIdx - mCurFuncMemIdx; 229 } 230 231 uint8_t *allocateSGMemory(uintptr_t Size, 232 unsigned Alignment = 1 /* no alignment */); 233 234 uintptr_t getFreeGVMemSize() const { 235 return MaxGlobalVarSize - mCurGVMemIdx; 236 } 237 238 uint8_t *getGVMemBase() const { 239 return reinterpret_cast<uint8_t*>(mpGVMem); 240 } 241 242 }; 243 244 } // namespace bcc 245 246 #endif // BCC_CODEMEMORYMANAGER_H 247