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
37namespace 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
85template <int maxPoolSize, int barrierSize, int maxInstructionSize, class AssemblerType>
86class AssemblerBufferWithConstantPool: public AssemblerBuffer {
87    typedef SegmentedVector<uint32_t, 512> LoadOffsets;
88    using AssemblerBuffer::putIntegral;
89    using AssemblerBuffer::putIntegralUnchecked;
90public:
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    bool isAligned(int alignment)
131    {
132        flushIfNoSpaceFor(alignment);
133        return AssemblerBuffer::isAligned(alignment);
134    }
135
136    void putByteUnchecked(int value)
137    {
138        AssemblerBuffer::putByteUnchecked(value);
139        correctDeltas(1);
140    }
141
142    void putByte(int value)
143    {
144        flushIfNoSpaceFor(1);
145        AssemblerBuffer::putByte(value);
146        correctDeltas(1);
147    }
148
149    void putShortUnchecked(int value)
150    {
151        AssemblerBuffer::putShortUnchecked(value);
152        correctDeltas(2);
153    }
154
155    void putShort(int value)
156    {
157        flushIfNoSpaceFor(2);
158        AssemblerBuffer::putShort(value);
159        correctDeltas(2);
160    }
161
162    void putIntUnchecked(int value)
163    {
164        AssemblerBuffer::putIntUnchecked(value);
165        correctDeltas(4);
166    }
167
168    void putInt(int value)
169    {
170        flushIfNoSpaceFor(4);
171        AssemblerBuffer::putInt(value);
172        correctDeltas(4);
173    }
174
175    void putInt64Unchecked(int64_t value)
176    {
177        AssemblerBuffer::putInt64Unchecked(value);
178        correctDeltas(8);
179    }
180
181    void putIntegral(TwoShorts value)
182    {
183        if (m_size > m_capacity - sizeof(TwoShorts))
184            grow();
185        putIntegralUnchecked(value);
186    }
187
188    void putIntegralUnchecked(TwoShorts value)
189    {
190        putIntegralUnchecked(value.high);
191        putIntegralUnchecked(value.low);
192    }
193
194    int size()
195    {
196        flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
197        return AssemblerBuffer::size();
198    }
199
200    int uncheckedSize()
201    {
202        return AssemblerBuffer::size();
203    }
204
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
229    uint32_t* poolAddress()
230    {
231        return m_pool;
232    }
233
234    int sizeOfConstantPool()
235    {
236        return m_numConsts;
237    }
238
239private:
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
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>
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
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
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