initial import
[vuplus_webkit] / Source / JavaScriptCore / assembler / AssemblerBufferWithConstantPool.h
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
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
112     ~AssemblerBufferWithConstantPool()
113     {
114         fastFree(m_mask);
115         fastFree(m_pool);
116     }
117
118     void ensureSpace(int space)
119     {
120         flushIfNoSpaceFor(space);
121         AssemblerBuffer::ensureSpace(space);
122     }
123
124     void ensureSpace(int insnSpace, int constSpace)
125     {
126         flushIfNoSpaceFor(insnSpace, constSpace);
127         AssemblerBuffer::ensureSpace(insnSpace);
128     }
129
130     void ensureSpaceForAnyOneInstruction()
131     {
132         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
133     }
134
135     bool isAligned(int alignment)
136     {
137         flushIfNoSpaceFor(alignment);
138         return AssemblerBuffer::isAligned(alignment);
139     }
140
141     void putByteUnchecked(int value)
142     {
143         AssemblerBuffer::putByteUnchecked(value);
144         correctDeltas(1);
145     }
146
147     void putByte(int value)
148     {
149         flushIfNoSpaceFor(1);
150         AssemblerBuffer::putByte(value);
151         correctDeltas(1);
152     }
153
154     void putShortUnchecked(int value)
155     {
156         AssemblerBuffer::putShortUnchecked(value);
157         correctDeltas(2);
158     }
159
160     void putShort(int value)
161     {
162         flushIfNoSpaceFor(2);
163         AssemblerBuffer::putShort(value);
164         correctDeltas(2);
165     }
166
167     void putIntUnchecked(int value)
168     {
169         AssemblerBuffer::putIntUnchecked(value);
170         correctDeltas(4);
171     }
172
173     void putInt(int value)
174     {
175         flushIfNoSpaceFor(4);
176         AssemblerBuffer::putInt(value);
177         correctDeltas(4);
178     }
179
180     void putInt64Unchecked(int64_t value)
181     {
182         AssemblerBuffer::putInt64Unchecked(value);
183         correctDeltas(8);
184     }
185
186     void putIntegral(TwoShorts value)
187     {
188         putIntegral(value.high);
189         putIntegral(value.low);
190     }
191
192     void putIntegralUnchecked(TwoShorts value)
193     {
194         putIntegralUnchecked(value.high);
195         putIntegralUnchecked(value.low);
196     }
197
198     PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData)
199     {
200         flushConstantPool(false);
201         return AssemblerBuffer::executableCopy(globalData);
202     }
203
204     void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false)
205     {
206         putIntegralWithConstantInt(insn, constant, isReusable);
207     }
208
209     void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
210     {
211         putIntegralWithConstantInt(insn, constant, isReusable);
212     }
213
214     // This flushing mechanism can be called after any unconditional jumps.
215     void flushWithoutBarrier(bool isForced = false)
216     {
217         // Flush if constant pool is more than 60% full to avoid overuse of this function.
218         if (isForced || 5 * m_numConsts > 3 * maxPoolSize / sizeof(uint32_t))
219             flushConstantPool(false);
220     }
221
222     uint32_t* poolAddress()
223     {
224         return m_pool;
225     }
226
227     int sizeOfConstantPool()
228     {
229         return m_numConsts;
230     }
231
232 private:
233     void correctDeltas(int insnSize)
234     {
235         m_maxDistance -= insnSize;
236         m_lastConstDelta -= insnSize;
237         if (m_lastConstDelta < 0)
238             m_lastConstDelta = 0;
239     }
240
241     void correctDeltas(int insnSize, int constSize)
242     {
243         correctDeltas(insnSize);
244
245         m_maxDistance -= m_lastConstDelta;
246         m_lastConstDelta = constSize;
247     }
248
249     template<typename IntegralType>
250     void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable)
251     {
252         if (!m_numConsts)
253             m_maxDistance = maxPoolSize;
254         flushIfNoSpaceFor(sizeof(IntegralType), 4);
255
256         m_loadOffsets.append(codeSize());
257         if (isReusable) {
258             for (int i = 0; i < m_numConsts; ++i) {
259                 if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
260                     putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, i)));
261                     correctDeltas(sizeof(IntegralType));
262                     return;
263                 }
264             }
265         }
266
267         m_pool[m_numConsts] = constant;
268         m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
269
270         putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, m_numConsts)));
271         ++m_numConsts;
272
273         correctDeltas(sizeof(IntegralType), 4);
274     }
275
276     void flushConstantPool(bool useBarrier = true)
277     {
278         if (m_numConsts == 0)
279             return;
280         int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
281
282         if (alignPool)
283             alignPool = sizeof(uint64_t) - alignPool;
284
285         // Callback to protect the constant pool from execution
286         if (useBarrier)
287             putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
288
289         if (alignPool) {
290             if (alignPool & 1)
291                 AssemblerBuffer::putByte(AssemblerType::padForAlign8);
292             if (alignPool & 2)
293                 AssemblerBuffer::putShort(AssemblerType::padForAlign16);
294             if (alignPool & 4)
295                 AssemblerBuffer::putInt(AssemblerType::padForAlign32);
296         }
297
298         int constPoolOffset = codeSize();
299         append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
300
301         // Patch each PC relative load
302         for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
303             void* loadAddr = reinterpret_cast<char*>(data()) + *iter;
304             AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<char*>(data()) + constPoolOffset);
305         }
306
307         m_loadOffsets.clear();
308         m_numConsts = 0;
309     }
310
311     void flushIfNoSpaceFor(int nextInsnSize)
312     {
313         if (m_numConsts == 0)
314             return;
315         int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
316         if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
317             flushConstantPool();
318     }
319
320     void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
321     {
322         if (m_numConsts == 0)
323             return;
324         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
325             (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
326             flushConstantPool();
327     }
328
329     uint32_t* m_pool;
330     char* m_mask;
331     LoadOffsets m_loadOffsets;
332
333     int m_numConsts;
334     int m_maxDistance;
335     int m_lastConstDelta;
336 };
337
338 } // namespace JSC
339
340 #endif // ENABLE(ASSEMBLER)
341
342 #endif // AssemblerBufferWithConstantPool_h