• 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 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 WTF::SegmentedVector<uint32_t, 512> LoadOffsets;
88 public:
89     enum {
90         UniqueConst,
91         ReusableConst,
92         UnusedEntry,
93     };
94 
AssemblerBufferWithConstantPool()95     AssemblerBufferWithConstantPool()
96         : AssemblerBuffer()
97         , m_numConsts(0)
98         , m_maxDistance(maxPoolSize)
99         , m_lastConstDelta(0)
100     {
101         m_pool = static_cast<uint32_t*>(fastMalloc(maxPoolSize));
102         m_mask = static_cast<char*>(fastMalloc(maxPoolSize / sizeof(uint32_t)));
103     }
104 
~AssemblerBufferWithConstantPool()105     ~AssemblerBufferWithConstantPool()
106     {
107         fastFree(m_mask);
108         fastFree(m_pool);
109     }
110 
ensureSpace(int space)111     void ensureSpace(int space)
112     {
113         flushIfNoSpaceFor(space);
114         AssemblerBuffer::ensureSpace(space);
115     }
116 
ensureSpace(int insnSpace,int constSpace)117     void ensureSpace(int insnSpace, int constSpace)
118     {
119         flushIfNoSpaceFor(insnSpace, constSpace);
120         AssemblerBuffer::ensureSpace(insnSpace);
121     }
122 
isAligned(int alignment)123     bool isAligned(int alignment)
124     {
125         flushIfNoSpaceFor(alignment);
126         return AssemblerBuffer::isAligned(alignment);
127     }
128 
putByteUnchecked(int value)129     void putByteUnchecked(int value)
130     {
131         AssemblerBuffer::putByteUnchecked(value);
132         correctDeltas(1);
133     }
134 
putByte(int value)135     void putByte(int value)
136     {
137         flushIfNoSpaceFor(1);
138         AssemblerBuffer::putByte(value);
139         correctDeltas(1);
140     }
141 
putShortUnchecked(int value)142     void putShortUnchecked(int value)
143     {
144         AssemblerBuffer::putShortUnchecked(value);
145         correctDeltas(2);
146     }
147 
putShort(int value)148     void putShort(int value)
149     {
150         flushIfNoSpaceFor(2);
151         AssemblerBuffer::putShort(value);
152         correctDeltas(2);
153     }
154 
putIntUnchecked(int value)155     void putIntUnchecked(int value)
156     {
157         AssemblerBuffer::putIntUnchecked(value);
158         correctDeltas(4);
159     }
160 
putInt(int value)161     void putInt(int value)
162     {
163         flushIfNoSpaceFor(4);
164         AssemblerBuffer::putInt(value);
165         correctDeltas(4);
166     }
167 
putInt64Unchecked(int64_t value)168     void putInt64Unchecked(int64_t value)
169     {
170         AssemblerBuffer::putInt64Unchecked(value);
171         correctDeltas(8);
172     }
173 
size()174     int size()
175     {
176         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
177         return AssemblerBuffer::size();
178     }
179 
executableCopy(ExecutablePool * allocator)180     void* executableCopy(ExecutablePool* allocator)
181     {
182         flushConstantPool(false);
183         return AssemblerBuffer::executableCopy(allocator);
184     }
185 
186     void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
187     {
188         flushIfNoSpaceFor(4, 4);
189 
190         m_loadOffsets.append(AssemblerBuffer::size());
191         if (isReusable)
192             for (int i = 0; i < m_numConsts; ++i) {
193                 if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
194                     AssemblerBuffer::putInt(AssemblerType::patchConstantPoolLoad(insn, i));
195                     correctDeltas(4);
196                     return;
197                 }
198             }
199 
200         m_pool[m_numConsts] = constant;
201         m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
202 
203         AssemblerBuffer::putInt(AssemblerType::patchConstantPoolLoad(insn, m_numConsts));
204         ++m_numConsts;
205 
206         correctDeltas(4, 4);
207     }
208 
209     // This flushing mechanism can be called after any unconditional jumps.
flushWithoutBarrier()210     void flushWithoutBarrier()
211     {
212         // Flush if constant pool is more than 60% full to avoid overuse of this function.
213         if (5 * m_numConsts > 3 * maxPoolSize / sizeof(uint32_t))
214             flushConstantPool(false);
215     }
216 
poolAddress()217     uint32_t* poolAddress()
218     {
219         return m_pool;
220     }
221 
222 private:
correctDeltas(int insnSize)223     void correctDeltas(int insnSize)
224     {
225         m_maxDistance -= insnSize;
226         m_lastConstDelta -= insnSize;
227         if (m_lastConstDelta < 0)
228             m_lastConstDelta = 0;
229     }
230 
correctDeltas(int insnSize,int constSize)231     void correctDeltas(int insnSize, int constSize)
232     {
233         correctDeltas(insnSize);
234 
235         m_maxDistance -= m_lastConstDelta;
236         m_lastConstDelta = constSize;
237     }
238 
239     void flushConstantPool(bool useBarrier = true)
240     {
241         if (m_numConsts == 0)
242             return;
243         int alignPool = (AssemblerBuffer::size() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
244 
245         if (alignPool)
246             alignPool = sizeof(uint64_t) - alignPool;
247 
248         // Callback to protect the constant pool from execution
249         if (useBarrier)
250             AssemblerBuffer::putInt(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
251 
252         if (alignPool) {
253             if (alignPool & 1)
254                 AssemblerBuffer::putByte(AssemblerType::padForAlign8);
255             if (alignPool & 2)
256                 AssemblerBuffer::putShort(AssemblerType::padForAlign16);
257             if (alignPool & 4)
258                 AssemblerBuffer::putInt(AssemblerType::padForAlign32);
259         }
260 
261         int constPoolOffset = AssemblerBuffer::size();
262         append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
263 
264         // Patch each PC relative load
265         for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
266             void* loadAddr = reinterpret_cast<void*>(m_buffer + *iter);
267             AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<void*>(m_buffer + constPoolOffset));
268         }
269 
270         m_loadOffsets.clear();
271         m_numConsts = 0;
272         m_maxDistance = maxPoolSize;
273     }
274 
flushIfNoSpaceFor(int nextInsnSize)275     void flushIfNoSpaceFor(int nextInsnSize)
276     {
277         if (m_numConsts == 0)
278             return;
279         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
280             flushConstantPool();
281     }
282 
flushIfNoSpaceFor(int nextInsnSize,int nextConstSize)283     void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
284     {
285         if (m_numConsts == 0)
286             return;
287         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + barrierSize + (int)sizeof(uint32_t)) ||
288             (m_numConsts + nextConstSize / sizeof(uint32_t) >= maxPoolSize))
289             flushConstantPool();
290     }
291 
292     uint32_t* m_pool;
293     char* m_mask;
294     LoadOffsets m_loadOffsets;
295 
296     int m_numConsts;
297     int m_maxDistance;
298     int m_lastConstDelta;
299 };
300 
301 } // namespace JSC
302 
303 #endif // ENABLE(ASSEMBLER)
304 
305 #endif // AssemblerBufferWithConstantPool_h
306