• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getCodeMemBase()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.
getGOTBase()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.
allocateStub(const llvm::GlobalValue * F,unsigned StubSize,unsigned Alignment)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.
startExceptionTable(const llvm::Function * F,uintptr_t & ActualSize)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.
endExceptionTable(const llvm::Function * F,uint8_t * TableStart,uint8_t * TableEnd,uint8_t * FrameRegister)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.
deallocateExceptionTable(void * ET)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:
getFreeCodeMemSize()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 
getFreeGVMemSize()234     uintptr_t getFreeGVMemSize() const {
235       return MaxGlobalVarSize - mCurGVMemIdx;
236     }
237 
getGVMemBase()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