mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-15 21:39:58 +00:00
334 lines
10 KiB
C++
334 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2009 University of Szeged
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#if ENABLE(ASSEMBLER)
|
|
|
|
#include "AssemblerBuffer.h"
|
|
#include <wtf/SegmentedVector.h>
|
|
|
|
#define ASSEMBLER_HAS_CONSTANT_POOL 1
|
|
|
|
namespace JSC {
|
|
|
|
/*
|
|
On a constant pool 4 or 8 bytes data can be stored. The values can be
|
|
constants or addresses. The addresses should be 32 or 64 bits. The constants
|
|
should be double-precisions float or integer numbers which are hard to be
|
|
encoded as few machine instructions.
|
|
|
|
TODO: The pool is desinged to handle both 32 and 64 bits values, but
|
|
currently only the 4 bytes constants are implemented and tested.
|
|
|
|
The AssemblerBuffer can contain multiple constant pools. Each pool is inserted
|
|
into the instruction stream - protected by a jump instruction from the
|
|
execution flow.
|
|
|
|
The flush mechanism is called when no space remain to insert the next instruction
|
|
into the pool. Three values are used to determine when the constant pool itself
|
|
have to be inserted into the instruction stream (Assembler Buffer):
|
|
|
|
- maxPoolSize: size of the constant pool in bytes, this value cannot be
|
|
larger than the maximum offset of a PC relative memory load
|
|
|
|
- barrierSize: size of jump instruction in bytes which protects the
|
|
constant pool from execution
|
|
|
|
- maxInstructionSize: maximum length of a machine instruction in bytes
|
|
|
|
There are some callbacks which solve the target architecture specific
|
|
address handling:
|
|
|
|
- TYPE patchConstantPoolLoad(TYPE load, int value):
|
|
patch the 'load' instruction with the index of the constant in the
|
|
constant pool and return the patched instruction.
|
|
|
|
- void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr):
|
|
patch the a PC relative load instruction at 'loadAddr' address with the
|
|
final relative offset. The offset can be computed with help of
|
|
'constPoolAddr' (the address of the constant pool) and index of the
|
|
constant (which is stored previously in the load instruction itself).
|
|
|
|
- TYPE placeConstantPoolBarrier(int size):
|
|
return with a constant pool barrier instruction which jumps over the
|
|
constant pool.
|
|
|
|
The 'put*WithConstant*' functions should be used to place a data into the
|
|
constant pool.
|
|
*/
|
|
|
|
template <int maxPoolSize, int barrierSize, int maxInstructionSize, class AssemblerType>
|
|
class AssemblerBufferWithConstantPool : public AssemblerBuffer {
|
|
typedef SegmentedVector<uint32_t, 512> LoadOffsets;
|
|
using AssemblerBuffer::putIntegral;
|
|
using AssemblerBuffer::putIntegralUnchecked;
|
|
public:
|
|
typedef struct {
|
|
short high;
|
|
short low;
|
|
} TwoShorts;
|
|
|
|
enum {
|
|
UniqueConst,
|
|
ReusableConst,
|
|
UnusedEntry,
|
|
};
|
|
|
|
AssemblerBufferWithConstantPool()
|
|
: AssemblerBuffer()
|
|
, m_numConsts(0)
|
|
, m_maxDistance(maxPoolSize)
|
|
, m_lastConstDelta(0)
|
|
{
|
|
m_pool = static_cast<uint32_t*>(fastMalloc(maxPoolSize));
|
|
m_mask = static_cast<char*>(fastMalloc(maxPoolSize / sizeof(uint32_t)));
|
|
}
|
|
|
|
~AssemblerBufferWithConstantPool()
|
|
{
|
|
fastFree(m_mask);
|
|
fastFree(m_pool);
|
|
}
|
|
|
|
void ensureSpace(int space)
|
|
{
|
|
flushIfNoSpaceFor(space);
|
|
AssemblerBuffer::ensureSpace(space);
|
|
}
|
|
|
|
void ensureSpace(int insnSpace, int constSpace)
|
|
{
|
|
flushIfNoSpaceFor(insnSpace, constSpace);
|
|
AssemblerBuffer::ensureSpace(insnSpace);
|
|
}
|
|
|
|
void ensureSpaceForAnyInstruction(int amount = 1)
|
|
{
|
|
flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t));
|
|
}
|
|
|
|
bool isAligned(int alignment)
|
|
{
|
|
flushIfNoSpaceFor(alignment);
|
|
return AssemblerBuffer::isAligned(alignment);
|
|
}
|
|
|
|
void putByteUnchecked(int value)
|
|
{
|
|
AssemblerBuffer::putByteUnchecked(value);
|
|
correctDeltas(1);
|
|
}
|
|
|
|
void putByte(int value)
|
|
{
|
|
flushIfNoSpaceFor(1);
|
|
AssemblerBuffer::putByte(value);
|
|
correctDeltas(1);
|
|
}
|
|
|
|
void putShortUnchecked(int value)
|
|
{
|
|
AssemblerBuffer::putShortUnchecked(value);
|
|
correctDeltas(2);
|
|
}
|
|
|
|
void putShort(int value)
|
|
{
|
|
flushIfNoSpaceFor(2);
|
|
AssemblerBuffer::putShort(value);
|
|
correctDeltas(2);
|
|
}
|
|
|
|
void putIntUnchecked(int value)
|
|
{
|
|
AssemblerBuffer::putIntUnchecked(value);
|
|
correctDeltas(4);
|
|
}
|
|
|
|
void putInt(int value)
|
|
{
|
|
flushIfNoSpaceFor(4);
|
|
AssemblerBuffer::putInt(value);
|
|
correctDeltas(4);
|
|
}
|
|
|
|
void putInt64Unchecked(int64_t value)
|
|
{
|
|
AssemblerBuffer::putInt64Unchecked(value);
|
|
correctDeltas(8);
|
|
}
|
|
|
|
void putIntegral(TwoShorts value)
|
|
{
|
|
putIntegral(value.high);
|
|
putIntegral(value.low);
|
|
}
|
|
|
|
void putIntegralUnchecked(TwoShorts value)
|
|
{
|
|
putIntegralUnchecked(value.high);
|
|
putIntegralUnchecked(value.low);
|
|
}
|
|
|
|
void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false)
|
|
{
|
|
putIntegralWithConstantInt(insn, constant, isReusable);
|
|
}
|
|
|
|
void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
|
|
{
|
|
putIntegralWithConstantInt(insn, constant, isReusable);
|
|
}
|
|
|
|
// This flushing mechanism can be called after any unconditional jumps.
|
|
void flushWithoutBarrier(bool isForced = false)
|
|
{
|
|
// Flush if constant pool is more than 60% full to avoid overuse of this function.
|
|
if (isForced || 5 * static_cast<uint32_t>(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t))
|
|
flushConstantPool(false);
|
|
}
|
|
|
|
uint32_t* poolAddress()
|
|
{
|
|
return m_pool;
|
|
}
|
|
|
|
int sizeOfConstantPool()
|
|
{
|
|
return m_numConsts;
|
|
}
|
|
|
|
void flushConstantPool(bool useBarrier = true)
|
|
{
|
|
if (!m_numConsts)
|
|
return;
|
|
int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
|
|
|
|
if (alignPool)
|
|
alignPool = sizeof(uint64_t) - alignPool;
|
|
|
|
// Callback to protect the constant pool from execution
|
|
if (useBarrier)
|
|
putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
|
|
|
|
if (alignPool) {
|
|
if (alignPool & 1)
|
|
AssemblerBuffer::putByte(AssemblerType::padForAlign8);
|
|
if (alignPool & 2)
|
|
AssemblerBuffer::putShort(AssemblerType::padForAlign16);
|
|
if (alignPool & 4)
|
|
AssemblerBuffer::putInt(AssemblerType::padForAlign32);
|
|
}
|
|
|
|
int constPoolOffset = codeSize();
|
|
append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
|
|
|
|
// Patch each PC relative load
|
|
for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
|
|
void* loadAddr = reinterpret_cast<char*>(data()) + *iter;
|
|
AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<char*>(data()) + constPoolOffset);
|
|
}
|
|
|
|
m_loadOffsets.clear();
|
|
m_numConsts = 0;
|
|
}
|
|
|
|
private:
|
|
void correctDeltas(int insnSize)
|
|
{
|
|
m_maxDistance -= insnSize;
|
|
m_lastConstDelta -= insnSize;
|
|
if (m_lastConstDelta < 0)
|
|
m_lastConstDelta = 0;
|
|
}
|
|
|
|
void correctDeltas(int insnSize, int constSize)
|
|
{
|
|
correctDeltas(insnSize);
|
|
|
|
m_maxDistance -= m_lastConstDelta;
|
|
m_lastConstDelta = constSize;
|
|
}
|
|
|
|
template<typename IntegralType>
|
|
void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable)
|
|
{
|
|
if (!m_numConsts)
|
|
m_maxDistance = maxPoolSize;
|
|
flushIfNoSpaceFor(sizeof(IntegralType), 4);
|
|
|
|
m_loadOffsets.append(codeSize());
|
|
if (isReusable) {
|
|
for (int i = 0; i < m_numConsts; ++i) {
|
|
if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
|
|
putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, i)));
|
|
correctDeltas(sizeof(IntegralType));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pool[m_numConsts] = constant;
|
|
m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
|
|
|
|
putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, m_numConsts)));
|
|
++m_numConsts;
|
|
|
|
correctDeltas(sizeof(IntegralType), 4);
|
|
}
|
|
|
|
void flushIfNoSpaceFor(int nextInsnSize)
|
|
{
|
|
if (m_numConsts == 0)
|
|
return;
|
|
int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
|
|
if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
|
|
flushConstantPool();
|
|
}
|
|
|
|
void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
|
|
{
|
|
if (m_numConsts == 0)
|
|
return;
|
|
if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
|
|
(m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
|
|
flushConstantPool();
|
|
}
|
|
|
|
uint32_t* m_pool;
|
|
char* m_mask;
|
|
LoadOffsets m_loadOffsets;
|
|
|
|
int m_numConsts;
|
|
int m_maxDistance;
|
|
int m_lastConstDelta;
|
|
};
|
|
|
|
} // namespace JSC
|
|
|
|
#endif // ENABLE(ASSEMBLER)
|