Home | History | Annotate | Download | only in assembler
      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     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 
    239 private:
    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