• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 University of Szeged
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #ifndef AssemblerBufferWithConstantPool_h
28 #define AssemblerBufferWithConstantPool_h
29 
30 #if ENABLE(ASSEMBLER)
31 
32 #include "AssemblerBuffer.h"
33 #include <wtf/SegmentedVector.h>
34 
35 #define ASSEMBLER_HAS_CONSTANT_POOL 1
36 
37 namespace JSC {
38 
39 /*
40     On a constant pool 4 or 8 bytes data can be stored. The values can be
41     constants or addresses. The addresses should be 32 or 64 bits. The constants
42     should be double-precisions float or integer numbers which are hard to be
43     encoded as few machine instructions.
44 
45     TODO: The pool is desinged to handle both 32 and 64 bits values, but
46     currently only the 4 bytes constants are implemented and tested.
47 
48     The AssemblerBuffer can contain multiple constant pools. Each pool is inserted
49     into the instruction stream - protected by a jump instruction from the
50     execution flow.
51 
52     The flush mechanism is called when no space remain to insert the next instruction
53     into the pool. Three values are used to determine when the constant pool itself
54     have to be inserted into the instruction stream (Assembler Buffer):
55 
56     - maxPoolSize: size of the constant pool in bytes, this value cannot be
57         larger than the maximum offset of a PC relative memory load
58 
59     - barrierSize: size of jump instruction in bytes which protects the
60         constant pool from execution
61 
62     - maxInstructionSize: maximum length of a machine instruction in bytes
63 
64     There are some callbacks which solve the target architecture specific
65     address handling:
66 
67     - TYPE patchConstantPoolLoad(TYPE load, int value):
68         patch the 'load' instruction with the index of the constant in the
69         constant pool and return the patched instruction.
70 
71     - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr):
72         patch the a PC relative load instruction at 'loadAddr' address with the
73         final relative offset. The offset can be computed with help of
74         'constPoolAddr' (the address of the constant pool) and index of the
75         constant (which is stored previously in the load instruction itself).
76 
77     - TYPE placeConstantPoolBarrier(int size):
78         return with a constant pool barrier instruction which jumps over the
79         constant pool.
80 
81     The 'put*WithConstant*' functions should be used to place a data into the
82     constant pool.
83 */
84 
85 template <int maxPoolSize, int barrierSize, int maxInstructionSize, class AssemblerType>
86 class AssemblerBufferWithConstantPool: public AssemblerBuffer {
87     typedef SegmentedVector<uint32_t, 512> LoadOffsets;
88     using AssemblerBuffer::putIntegral;
89     using AssemblerBuffer::putIntegralUnchecked;
90 public:
91     typedef struct {
92         short high;
93         short low;
94     } TwoShorts;
95 
96     enum {
97         UniqueConst,
98         ReusableConst,
99         UnusedEntry,
100     };
101 
AssemblerBufferWithConstantPool()102     AssemblerBufferWithConstantPool()
103         : AssemblerBuffer()
104         , m_numConsts(0)
105         , m_maxDistance(maxPoolSize)
106         , m_lastConstDelta(0)
107     {
108         m_pool = static_cast<uint32_t*>(fastMalloc(maxPoolSize));
109         m_mask = static_cast<char*>(fastMalloc(maxPoolSize / sizeof(uint32_t)));
110     }
111 
~AssemblerBufferWithConstantPool()112     ~AssemblerBufferWithConstantPool()
113     {
114         fastFree(m_mask);
115         fastFree(m_pool);
116     }
117 
ensureSpace(int space)118     void ensureSpace(int space)
119     {
120         flushIfNoSpaceFor(space);
121         AssemblerBuffer::ensureSpace(space);
122     }
123 
ensureSpace(int insnSpace,int constSpace)124     void ensureSpace(int insnSpace, int constSpace)
125     {
126         flushIfNoSpaceFor(insnSpace, constSpace);
127         AssemblerBuffer::ensureSpace(insnSpace);
128     }
129 
isAligned(int alignment)130     bool isAligned(int alignment)
131     {
132         flushIfNoSpaceFor(alignment);
133         return AssemblerBuffer::isAligned(alignment);
134     }
135 
putByteUnchecked(int value)136     void putByteUnchecked(int value)
137     {
138         AssemblerBuffer::putByteUnchecked(value);
139         correctDeltas(1);
140     }
141 
putByte(int value)142     void putByte(int value)
143     {
144         flushIfNoSpaceFor(1);
145         AssemblerBuffer::putByte(value);
146         correctDeltas(1);
147     }
148 
putShortUnchecked(int value)149     void putShortUnchecked(int value)
150     {
151         AssemblerBuffer::putShortUnchecked(value);
152         correctDeltas(2);
153     }
154 
putShort(int value)155     void putShort(int value)
156     {
157         flushIfNoSpaceFor(2);
158         AssemblerBuffer::putShort(value);
159         correctDeltas(2);
160     }
161 
putIntUnchecked(int value)162     void putIntUnchecked(int value)
163     {
164         AssemblerBuffer::putIntUnchecked(value);
165         correctDeltas(4);
166     }
167 
putInt(int value)168     void putInt(int value)
169     {
170         flushIfNoSpaceFor(4);
171         AssemblerBuffer::putInt(value);
172         correctDeltas(4);
173     }
174 
putInt64Unchecked(int64_t value)175     void putInt64Unchecked(int64_t value)
176     {
177         AssemblerBuffer::putInt64Unchecked(value);
178         correctDeltas(8);
179     }
180 
putIntegral(TwoShorts value)181     void putIntegral(TwoShorts value)
182     {
183         if (m_size > m_capacity - sizeof(TwoShorts))
184             grow();
185         putIntegralUnchecked(value);
186     }
187 
putIntegralUnchecked(TwoShorts value)188     void putIntegralUnchecked(TwoShorts value)
189     {
190         putIntegralUnchecked(value.high);
191         putIntegralUnchecked(value.low);
192     }
193 
size()194     int size()
195     {
196         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
197         return AssemblerBuffer::size();
198     }
199 
uncheckedSize()200     int uncheckedSize()
201     {
202         return AssemblerBuffer::size();
203     }
204 
executableCopy(ExecutablePool * allocator)205     void* executableCopy(ExecutablePool* allocator)
206     {
207         flushConstantPool(false);
208         return AssemblerBuffer::executableCopy(allocator);
209     }
210 
211     void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false)
212     {
213         putIntegralWithConstantInt(insn, constant, isReusable);
214     }
215 
216     void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
217     {
218         putIntegralWithConstantInt(insn, constant, isReusable);
219     }
220 
221     // This flushing mechanism can be called after any unconditional jumps.
222     void flushWithoutBarrier(bool isForced = false)
223     {
224         // Flush if constant pool is more than 60% full to avoid overuse of this function.
225         if (isForced || 5 * m_numConsts > 3 * maxPoolSize / sizeof(uint32_t))
226             flushConstantPool(false);
227     }
228 
poolAddress()229     uint32_t* poolAddress()
230     {
231         return m_pool;
232     }
233 
sizeOfConstantPool()234     int sizeOfConstantPool()
235     {
236         return m_numConsts;
237     }
238 
239 private:
correctDeltas(int insnSize)240     void correctDeltas(int insnSize)
241     {
242         m_maxDistance -= insnSize;
243         m_lastConstDelta -= insnSize;
244         if (m_lastConstDelta < 0)
245             m_lastConstDelta = 0;
246     }
247 
correctDeltas(int insnSize,int constSize)248     void correctDeltas(int insnSize, int constSize)
249     {
250         correctDeltas(insnSize);
251 
252         m_maxDistance -= m_lastConstDelta;
253         m_lastConstDelta = constSize;
254     }
255 
256     template<typename IntegralType>
putIntegralWithConstantInt(IntegralType insn,uint32_t constant,bool isReusable)257     void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable)
258     {
259         if (!m_numConsts)
260             m_maxDistance = maxPoolSize;
261         flushIfNoSpaceFor(sizeof(IntegralType), 4);
262 
263         m_loadOffsets.append(AssemblerBuffer::size());
264         if (isReusable) {
265             for (int i = 0; i < m_numConsts; ++i) {
266                 if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
267                     putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, i)));
268                     correctDeltas(sizeof(IntegralType));
269                     return;
270                 }
271             }
272         }
273 
274         m_pool[m_numConsts] = constant;
275         m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
276 
277         putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, m_numConsts)));
278         ++m_numConsts;
279 
280         correctDeltas(sizeof(IntegralType), 4);
281     }
282 
283     void flushConstantPool(bool useBarrier = true)
284     {
285         if (m_numConsts == 0)
286             return;
287         int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
288 
289         if (alignPool)
290             alignPool = sizeof(uint64_t) - alignPool;
291 
292         // Callback to protect the constant pool from execution
293         if (useBarrier)
294             putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
295 
296         if (alignPool) {
297             if (alignPool & 1)
298                 AssemblerBuffer::putByte(AssemblerType::padForAlign8);
299             if (alignPool & 2)
300                 AssemblerBuffer::putShort(AssemblerType::padForAlign16);
301             if (alignPool & 4)
302                 AssemblerBuffer::putInt(AssemblerType::padForAlign32);
303         }
304 
305         int constPoolOffset = AssemblerBuffer::size();
306         append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
307 
308         // Patch each PC relative load
309         for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
310             void* loadAddr = reinterpret_cast<void*>(m_buffer + *iter);
311             AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<void*>(m_buffer + constPoolOffset));
312         }
313 
314         m_loadOffsets.clear();
315         m_numConsts = 0;
316     }
317 
flushIfNoSpaceFor(int nextInsnSize)318     void flushIfNoSpaceFor(int nextInsnSize)
319     {
320         if (m_numConsts == 0)
321             return;
322         int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
323         if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
324             flushConstantPool();
325     }
326 
flushIfNoSpaceFor(int nextInsnSize,int nextConstSize)327     void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
328     {
329         if (m_numConsts == 0)
330             return;
331         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
332             (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
333             flushConstantPool();
334     }
335 
336     uint32_t* m_pool;
337     char* m_mask;
338     LoadOffsets m_loadOffsets;
339 
340     int m_numConsts;
341     int m_maxDistance;
342     int m_lastConstDelta;
343 };
344 
345 } // namespace JSC
346 
347 #endif // ENABLE(ASSEMBLER)
348 
349 #endif // AssemblerBufferWithConstantPool_h
350