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 #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 
     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 
    107     ~AssemblerBufferWithConstantPool()
    108     {
    109         fastFree(m_mask);
    110         fastFree(m_pool);
    111     }
    112 
    113     void ensureSpace(int space)
    114     {
    115         flushIfNoSpaceFor(space);
    116         AssemblerBuffer::ensureSpace(space);
    117     }
    118 
    119     void ensureSpace(int insnSpace, int constSpace)
    120     {
    121         flushIfNoSpaceFor(insnSpace, constSpace);
    122         AssemblerBuffer::ensureSpace(insnSpace);
    123     }
    124 
    125     bool isAligned(int alignment)
    126     {
    127         flushIfNoSpaceFor(alignment);
    128         return AssemblerBuffer::isAligned(alignment);
    129     }
    130 
    131     void putByteUnchecked(int value)
    132     {
    133         AssemblerBuffer::putByteUnchecked(value);
    134         correctDeltas(1);
    135     }
    136 
    137     void putByte(int value)
    138     {
    139         flushIfNoSpaceFor(1);
    140         AssemblerBuffer::putByte(value);
    141         correctDeltas(1);
    142     }
    143 
    144     void putShortUnchecked(int value)
    145     {
    146         AssemblerBuffer::putShortUnchecked(value);
    147         correctDeltas(2);
    148     }
    149 
    150     void putShort(int value)
    151     {
    152         flushIfNoSpaceFor(2);
    153         AssemblerBuffer::putShort(value);
    154         correctDeltas(2);
    155     }
    156 
    157     void putIntUnchecked(int value)
    158     {
    159         AssemblerBuffer::putIntUnchecked(value);
    160         correctDeltas(4);
    161     }
    162 
    163     void putInt(int value)
    164     {
    165         flushIfNoSpaceFor(4);
    166         AssemblerBuffer::putInt(value);
    167         correctDeltas(4);
    168     }
    169 
    170     void putInt64Unchecked(int64_t value)
    171     {
    172         AssemblerBuffer::putInt64Unchecked(value);
    173         correctDeltas(8);
    174     }
    175 
    176     int size()
    177     {
    178         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
    179         return AssemblerBuffer::size();
    180     }
    181 
    182     int uncheckedSize()
    183     {
    184         return AssemblerBuffer::size();
    185     }
    186 
    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 
    224     uint32_t* poolAddress()
    225     {
    226         return m_pool;
    227     }
    228 
    229     int sizeOfConstantPool()
    230     {
    231         return m_numConsts;
    232     }
    233 
    234 private:
    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 
    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 
    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 
    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