• 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 #include <wtf/Platform.h>
31 
32 #if ENABLE(ASSEMBLER)
33 
34 #include "AssemblerBuffer.h"
35 #include <wtf/SegmentedVector.h>
36 
37 #define ASSEMBLER_HAS_CONSTANT_POOL 1
38 
39 namespace JSC {
40 
41 /*
42     On a constant pool 4 or 8 bytes data can be stored. The values can be
43     constants or addresses. The addresses should be 32 or 64 bits. The constants
44     should be double-precisions float or integer numbers which are hard to be
45     encoded as few machine instructions.
46 
47     TODO: The pool is desinged to handle both 32 and 64 bits values, but
48     currently only the 4 bytes constants are implemented and tested.
49 
50     The AssemblerBuffer can contain multiple constant pools. Each pool is inserted
51     into the instruction stream - protected by a jump instruction from the
52     execution flow.
53 
54     The flush mechanism is called when no space remain to insert the next instruction
55     into the pool. Three values are used to determine when the constant pool itself
56     have to be inserted into the instruction stream (Assembler Buffer):
57 
58     - maxPoolSize: size of the constant pool in bytes, this value cannot be
59         larger than the maximum offset of a PC relative memory load
60 
61     - barrierSize: size of jump instruction in bytes which protects the
62         constant pool from execution
63 
64     - maxInstructionSize: maximum length of a machine instruction in bytes
65 
66     There are some callbacks which solve the target architecture specific
67     address handling:
68 
69     - TYPE patchConstantPoolLoad(TYPE load, int value):
70         patch the 'load' instruction with the index of the constant in the
71         constant pool and return the patched instruction.
72 
73     - void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr):
74         patch the a PC relative load instruction at 'loadAddr' address with the
75         final relative offset. The offset can be computed with help of
76         'constPoolAddr' (the address of the constant pool) and index of the
77         constant (which is stored previously in the load instruction itself).
78 
79     - TYPE placeConstantPoolBarrier(int size):
80         return with a constant pool barrier instruction which jumps over the
81         constant pool.
82 
83     The 'put*WithConstant*' functions should be used to place a data into the
84     constant pool.
85 */
86 
87 template <int maxPoolSize, int barrierSize, int maxInstructionSize, class AssemblerType>
88 class AssemblerBufferWithConstantPool: public AssemblerBuffer {
89     typedef SegmentedVector<uint32_t, 512> LoadOffsets;
90 public:
91     enum {
92         UniqueConst,
93         ReusableConst,
94         UnusedEntry,
95     };
96 
AssemblerBufferWithConstantPool()97     AssemblerBufferWithConstantPool()
98         : AssemblerBuffer()
99         , m_numConsts(0)
100         , m_maxDistance(maxPoolSize)
101         , m_lastConstDelta(0)
102     {
103         m_pool = static_cast<uint32_t*>(fastMalloc(maxPoolSize));
104         m_mask = static_cast<char*>(fastMalloc(maxPoolSize / sizeof(uint32_t)));
105     }
106 
~AssemblerBufferWithConstantPool()107     ~AssemblerBufferWithConstantPool()
108     {
109         fastFree(m_mask);
110         fastFree(m_pool);
111     }
112 
ensureSpace(int space)113     void ensureSpace(int space)
114     {
115         flushIfNoSpaceFor(space);
116         AssemblerBuffer::ensureSpace(space);
117     }
118 
ensureSpace(int insnSpace,int constSpace)119     void ensureSpace(int insnSpace, int constSpace)
120     {
121         flushIfNoSpaceFor(insnSpace, constSpace);
122         AssemblerBuffer::ensureSpace(insnSpace);
123     }
124 
isAligned(int alignment)125     bool isAligned(int alignment)
126     {
127         flushIfNoSpaceFor(alignment);
128         return AssemblerBuffer::isAligned(alignment);
129     }
130 
putByteUnchecked(int value)131     void putByteUnchecked(int value)
132     {
133         AssemblerBuffer::putByteUnchecked(value);
134         correctDeltas(1);
135     }
136 
putByte(int value)137     void putByte(int value)
138     {
139         flushIfNoSpaceFor(1);
140         AssemblerBuffer::putByte(value);
141         correctDeltas(1);
142     }
143 
putShortUnchecked(int value)144     void putShortUnchecked(int value)
145     {
146         AssemblerBuffer::putShortUnchecked(value);
147         correctDeltas(2);
148     }
149 
putShort(int value)150     void putShort(int value)
151     {
152         flushIfNoSpaceFor(2);
153         AssemblerBuffer::putShort(value);
154         correctDeltas(2);
155     }
156 
putIntUnchecked(int value)157     void putIntUnchecked(int value)
158     {
159         AssemblerBuffer::putIntUnchecked(value);
160         correctDeltas(4);
161     }
162 
putInt(int value)163     void putInt(int value)
164     {
165         flushIfNoSpaceFor(4);
166         AssemblerBuffer::putInt(value);
167         correctDeltas(4);
168     }
169 
putInt64Unchecked(int64_t value)170     void putInt64Unchecked(int64_t value)
171     {
172         AssemblerBuffer::putInt64Unchecked(value);
173         correctDeltas(8);
174     }
175 
size()176     int size()
177     {
178         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
179         return AssemblerBuffer::size();
180     }
181 
uncheckedSize()182     int uncheckedSize()
183     {
184         return AssemblerBuffer::size();
185     }
186 
executableCopy(ExecutablePool * allocator)187     void* executableCopy(ExecutablePool* allocator)
188     {
189         flushConstantPool(false);
190         return AssemblerBuffer::executableCopy(allocator);
191     }
192 
193     void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
194     {
195         flushIfNoSpaceFor(4, 4);
196 
197         m_loadOffsets.append(AssemblerBuffer::size());
198         if (isReusable)
199             for (int i = 0; i < m_numConsts; ++i) {
200                 if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
201                     AssemblerBuffer::putInt(AssemblerType::patchConstantPoolLoad(insn, i));
202                     correctDeltas(4);
203                     return;
204                 }
205             }
206 
207         m_pool[m_numConsts] = constant;
208         m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
209 
210         AssemblerBuffer::putInt(AssemblerType::patchConstantPoolLoad(insn, m_numConsts));
211         ++m_numConsts;
212 
213         correctDeltas(4, 4);
214     }
215 
216     // This flushing mechanism can be called after any unconditional jumps.
217     void flushWithoutBarrier(bool isForced = false)
218     {
219         // Flush if constant pool is more than 60% full to avoid overuse of this function.
220         if (isForced || 5 * m_numConsts > 3 * maxPoolSize / sizeof(uint32_t))
221             flushConstantPool(false);
222     }
223 
poolAddress()224     uint32_t* poolAddress()
225     {
226         return m_pool;
227     }
228 
sizeOfConstantPool()229     int sizeOfConstantPool()
230     {
231         return m_numConsts;
232     }
233 
234 private:
correctDeltas(int insnSize)235     void correctDeltas(int insnSize)
236     {
237         m_maxDistance -= insnSize;
238         m_lastConstDelta -= insnSize;
239         if (m_lastConstDelta < 0)
240             m_lastConstDelta = 0;
241     }
242 
correctDeltas(int insnSize,int constSize)243     void correctDeltas(int insnSize, int constSize)
244     {
245         correctDeltas(insnSize);
246 
247         m_maxDistance -= m_lastConstDelta;
248         m_lastConstDelta = constSize;
249     }
250 
251     void flushConstantPool(bool useBarrier = true)
252     {
253         if (m_numConsts == 0)
254             return;
255         int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
256 
257         if (alignPool)
258             alignPool = sizeof(uint64_t) - alignPool;
259 
260         // Callback to protect the constant pool from execution
261         if (useBarrier)
262             AssemblerBuffer::putInt(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
263 
264         if (alignPool) {
265             if (alignPool & 1)
266                 AssemblerBuffer::putByte(AssemblerType::padForAlign8);
267             if (alignPool & 2)
268                 AssemblerBuffer::putShort(AssemblerType::padForAlign16);
269             if (alignPool & 4)
270                 AssemblerBuffer::putInt(AssemblerType::padForAlign32);
271         }
272 
273         int constPoolOffset = AssemblerBuffer::size();
274         append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
275 
276         // Patch each PC relative load
277         for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
278             void* loadAddr = reinterpret_cast<void*>(m_buffer + *iter);
279             AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<void*>(m_buffer + constPoolOffset));
280         }
281 
282         m_loadOffsets.clear();
283         m_numConsts = 0;
284         m_maxDistance = maxPoolSize;
285     }
286 
flushIfNoSpaceFor(int nextInsnSize)287     void flushIfNoSpaceFor(int nextInsnSize)
288     {
289         if (m_numConsts == 0)
290             return;
291         int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
292         if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
293             flushConstantPool();
294     }
295 
flushIfNoSpaceFor(int nextInsnSize,int nextConstSize)296     void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
297     {
298         if (m_numConsts == 0)
299             return;
300         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
301             (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
302             flushConstantPool();
303     }
304 
305     uint32_t* m_pool;
306     char* m_mask;
307     LoadOffsets m_loadOffsets;
308 
309     int m_numConsts;
310     int m_maxDistance;
311     int m_lastConstDelta;
312 };
313 
314 } // namespace JSC
315 
316 #endif // ENABLE(ASSEMBLER)
317 
318 #endif // AssemblerBufferWithConstantPool_h
319