2
* Copyright (C) 2009 University of Szeged
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
27
#ifndef AssemblerBufferWithConstantPool_h
28
#define AssemblerBufferWithConstantPool_h
32
#include "AssemblerBuffer.h"
33
#include <wtf/SegmentedVector.h>
35
#define ASSEMBLER_HAS_CONSTANT_POOL 1
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.
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.
48
The AssemblerBuffer can contain multiple constant pools. Each pool is inserted
49
into the instruction stream - protected by a jump instruction from the
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):
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
59
- barrierSize: size of jump instruction in bytes which protects the
60
constant pool from execution
62
- maxInstructionSize: maximum length of a machine instruction in bytes
64
There are some callbacks which solve the target architecture specific
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.
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).
77
- TYPE placeConstantPoolBarrier(int size):
78
return with a constant pool barrier instruction which jumps over the
81
The 'put*WithConstant*' functions should be used to place a data into the
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;
102
AssemblerBufferWithConstantPool()
105
, m_maxDistance(maxPoolSize)
106
, m_lastConstDelta(0)
108
m_pool = static_cast<uint32_t*>(fastMalloc(maxPoolSize));
109
m_mask = static_cast<char*>(fastMalloc(maxPoolSize / sizeof(uint32_t)));
112
~AssemblerBufferWithConstantPool()
118
void ensureSpace(int space)
120
flushIfNoSpaceFor(space);
121
AssemblerBuffer::ensureSpace(space);
124
void ensureSpace(int insnSpace, int constSpace)
126
flushIfNoSpaceFor(insnSpace, constSpace);
127
AssemblerBuffer::ensureSpace(insnSpace);
130
void ensureSpaceForAnyInstruction(int amount = 1)
132
flushIfNoSpaceFor(amount * maxInstructionSize, amount * sizeof(uint64_t));
135
bool isAligned(int alignment)
137
flushIfNoSpaceFor(alignment);
138
return AssemblerBuffer::isAligned(alignment);
141
void putByteUnchecked(int value)
143
AssemblerBuffer::putByteUnchecked(value);
147
void putByte(int value)
149
flushIfNoSpaceFor(1);
150
AssemblerBuffer::putByte(value);
154
void putShortUnchecked(int value)
156
AssemblerBuffer::putShortUnchecked(value);
160
void putShort(int value)
162
flushIfNoSpaceFor(2);
163
AssemblerBuffer::putShort(value);
167
void putIntUnchecked(int value)
169
AssemblerBuffer::putIntUnchecked(value);
173
void putInt(int value)
175
flushIfNoSpaceFor(4);
176
AssemblerBuffer::putInt(value);
180
void putInt64Unchecked(int64_t value)
182
AssemblerBuffer::putInt64Unchecked(value);
186
void putIntegral(TwoShorts value)
188
putIntegral(value.high);
189
putIntegral(value.low);
192
void putIntegralUnchecked(TwoShorts value)
194
putIntegralUnchecked(value.high);
195
putIntegralUnchecked(value.low);
198
PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort)
200
flushConstantPool(false);
201
return AssemblerBuffer::executableCopy(globalData, ownerUID, effort);
204
void putShortWithConstantInt(uint16_t insn, uint32_t constant, bool isReusable = false)
206
putIntegralWithConstantInt(insn, constant, isReusable);
209
void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)
211
putIntegralWithConstantInt(insn, constant, isReusable);
214
// This flushing mechanism can be called after any unconditional jumps.
215
void flushWithoutBarrier(bool isForced = false)
217
// Flush if constant pool is more than 60% full to avoid overuse of this function.
218
if (isForced || 5 * static_cast<uint32_t>(m_numConsts) > 3 * maxPoolSize / sizeof(uint32_t))
219
flushConstantPool(false);
222
uint32_t* poolAddress()
227
int sizeOfConstantPool()
233
void correctDeltas(int insnSize)
235
m_maxDistance -= insnSize;
236
m_lastConstDelta -= insnSize;
237
if (m_lastConstDelta < 0)
238
m_lastConstDelta = 0;
241
void correctDeltas(int insnSize, int constSize)
243
correctDeltas(insnSize);
245
m_maxDistance -= m_lastConstDelta;
246
m_lastConstDelta = constSize;
249
template<typename IntegralType>
250
void putIntegralWithConstantInt(IntegralType insn, uint32_t constant, bool isReusable)
253
m_maxDistance = maxPoolSize;
254
flushIfNoSpaceFor(sizeof(IntegralType), 4);
256
m_loadOffsets.append(codeSize());
258
for (int i = 0; i < m_numConsts; ++i) {
259
if (m_mask[i] == ReusableConst && m_pool[i] == constant) {
260
putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, i)));
261
correctDeltas(sizeof(IntegralType));
267
m_pool[m_numConsts] = constant;
268
m_mask[m_numConsts] = static_cast<char>(isReusable ? ReusableConst : UniqueConst);
270
putIntegral(static_cast<IntegralType>(AssemblerType::patchConstantPoolLoad(insn, m_numConsts)));
273
correctDeltas(sizeof(IntegralType), 4);
276
void flushConstantPool(bool useBarrier = true)
278
if (m_numConsts == 0)
280
int alignPool = (codeSize() + (useBarrier ? barrierSize : 0)) & (sizeof(uint64_t) - 1);
283
alignPool = sizeof(uint64_t) - alignPool;
285
// Callback to protect the constant pool from execution
287
putIntegral(AssemblerType::placeConstantPoolBarrier(m_numConsts * sizeof(uint32_t) + alignPool));
291
AssemblerBuffer::putByte(AssemblerType::padForAlign8);
293
AssemblerBuffer::putShort(AssemblerType::padForAlign16);
295
AssemblerBuffer::putInt(AssemblerType::padForAlign32);
298
int constPoolOffset = codeSize();
299
append(reinterpret_cast<char*>(m_pool), m_numConsts * sizeof(uint32_t));
301
// Patch each PC relative load
302
for (LoadOffsets::Iterator iter = m_loadOffsets.begin(); iter != m_loadOffsets.end(); ++iter) {
303
void* loadAddr = reinterpret_cast<char*>(data()) + *iter;
304
AssemblerType::patchConstantPoolLoad(loadAddr, reinterpret_cast<char*>(data()) + constPoolOffset);
307
m_loadOffsets.clear();
311
void flushIfNoSpaceFor(int nextInsnSize)
313
if (m_numConsts == 0)
315
int lastConstDelta = m_lastConstDelta > nextInsnSize ? m_lastConstDelta - nextInsnSize : 0;
316
if ((m_maxDistance < nextInsnSize + lastConstDelta + barrierSize + (int)sizeof(uint32_t)))
320
void flushIfNoSpaceFor(int nextInsnSize, int nextConstSize)
322
if (m_numConsts == 0)
324
if ((m_maxDistance < nextInsnSize + m_lastConstDelta + nextConstSize + barrierSize + (int)sizeof(uint32_t)) ||
325
(m_numConsts * sizeof(uint32_t) + nextConstSize >= maxPoolSize))
331
LoadOffsets m_loadOffsets;
335
int m_lastConstDelta;
340
#endif // ENABLE(ASSEMBLER)
342
#endif // AssemblerBufferWithConstantPool_h