JavaScriptCore/assembler/AssemblerBufferWithConstantPool.h
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 public:
       
    89     enum {
       
    90         UniqueConst,
       
    91         ReusableConst,
       
    92         UnusedEntry,
       
    93     };
       
    94 
       
    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 
       
   105     ~AssemblerBufferWithConstantPool()
       
   106     {
       
   107         fastFree(m_mask);
       
   108         fastFree(m_pool);
       
   109     }
       
   110 
       
   111     void ensureSpace(int space)
       
   112     {
       
   113         flushIfNoSpaceFor(space);
       
   114         AssemblerBuffer::ensureSpace(space);
       
   115     }
       
   116 
       
   117     void ensureSpace(int insnSpace, int constSpace)
       
   118     {
       
   119         flushIfNoSpaceFor(insnSpace, constSpace);
       
   120         AssemblerBuffer::ensureSpace(insnSpace);
       
   121     }
       
   122 
       
   123     bool isAligned(int alignment)
       
   124     {
       
   125         flushIfNoSpaceFor(alignment);
       
   126         return AssemblerBuffer::isAligned(alignment);
       
   127     }
       
   128 
       
   129     void putByteUnchecked(int value)
       
   130     {
       
   131         AssemblerBuffer::putByteUnchecked(value);
       
   132         correctDeltas(1);
       
   133     }
       
   134 
       
   135     void putByte(int value)
       
   136     {
       
   137         flushIfNoSpaceFor(1);
       
   138         AssemblerBuffer::putByte(value);
       
   139         correctDeltas(1);
       
   140     }
       
   141 
       
   142     void putShortUnchecked(int value)
       
   143     {
       
   144         AssemblerBuffer::putShortUnchecked(value);
       
   145         correctDeltas(2);
       
   146     }
       
   147 
       
   148     void putShort(int value)
       
   149     {
       
   150         flushIfNoSpaceFor(2);
       
   151         AssemblerBuffer::putShort(value);
       
   152         correctDeltas(2);
       
   153     }
       
   154 
       
   155     void putIntUnchecked(int value)
       
   156     {
       
   157         AssemblerBuffer::putIntUnchecked(value);
       
   158         correctDeltas(4);
       
   159     }
       
   160 
       
   161     void putInt(int value)
       
   162     {
       
   163         flushIfNoSpaceFor(4);
       
   164         AssemblerBuffer::putInt(value);
       
   165         correctDeltas(4);
       
   166     }
       
   167 
       
   168     void putInt64Unchecked(int64_t value)
       
   169     {
       
   170         AssemblerBuffer::putInt64Unchecked(value);
       
   171         correctDeltas(8);
       
   172     }
       
   173 
       
   174     int size()
       
   175     {
       
   176         flushIfNoSpaceFor(maxInstructionSize, sizeof(uint64_t));
       
   177         return AssemblerBuffer::size();
       
   178     }
       
   179 
       
   180     int uncheckedSize()
       
   181     {
       
   182         return AssemblerBuffer::size();
       
   183     }
       
   184 
       
   185     void* executableCopy(ExecutablePool* allocator)
       
   186     {
       
   187         flushConstantPool(false);
       
   188         return AssemblerBuffer::executableCopy(allocator);
       
   189     }
       
   190 
       
   191     void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
       
   192     {
       
   193         if (!m_numConsts)
       
   194             m_maxDistance = maxPoolSize;
       
   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     }
       
   285 
       
   286     void flushIfNoSpaceFor(int nextInsnSize)
       
   287     {
       
   288         if (m_numConsts == 0)
       
   289             return;
       
   290         int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
       
   291         if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
       
   292             flushConstantPool();
       
   293     }
       
   294 
       
   295     void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
       
   296     {
       
   297         if (m_numConsts == 0)
       
   298             return;
       
   299         if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
       
   300             (m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
       
   301             flushConstantPool();
       
   302     }
       
   303 
       
   304     uint32_t* m_pool;
       
   305     char* m_mask;
       
   306     LoadOffsets m_loadOffsets;
       
   307 
       
   308     int m_numConsts;
       
   309     int m_maxDistance;
       
   310     int m_lastConstDelta;
       
   311 };
       
   312 
       
   313 } // namespace JSC
       
   314 
       
   315 #endif // ENABLE(ASSEMBLER)
       
   316 
       
   317 #endif // AssemblerBufferWithConstantPool_h