blob: 01f8faf240e1674aa6be6b9e3fc14f3ee114027d [file] [log] [blame]
/*
* Copyright (C) 2011 Apple Inc. 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.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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
#include <wtf/Assertions.h>
#include <wtf/FastMalloc.h>
#include <wtf/FastMalloc.h>
#include <wtf/Vector.h>
namespace JSC {
// Gives you a compressed map between between bytecode indices and machine code
// entry points. The compression simply tries to use either 1, 2, or 4 bytes for
// any given offset. The largest offset that can be stored is 2^30.
// Example use:
//
// CompactJITCodeMap::Encoder encoder(map);
// encoder.append(a, b);
// encoder.append(c, d); // preconditions: c >= a, d >= b
// auto map = encoder.finish();
//
// At some later time:
//
// Vector<BytecodeAndMachineOffset> decoded;
// map->decode(decoded);
struct BytecodeAndMachineOffset {
BytecodeAndMachineOffset() { }
BytecodeAndMachineOffset(unsigned bytecodeIndex, unsigned machineCodeOffset)
: m_bytecodeIndex(bytecodeIndex)
, m_machineCodeOffset(machineCodeOffset)
{
}
unsigned m_bytecodeIndex;
unsigned m_machineCodeOffset;
static inline unsigned getBytecodeIndex(BytecodeAndMachineOffset* mapping)
{
return mapping->m_bytecodeIndex;
}
static inline unsigned getMachineCodeOffset(BytecodeAndMachineOffset* mapping)
{
return mapping->m_machineCodeOffset;
}
};
class CompactJITCodeMap {
WTF_MAKE_FAST_ALLOCATED;
public:
CompactJITCodeMap(uint8_t* buffer, unsigned size, unsigned numberOfEntries)
: m_buffer(buffer)
#if !ASSERT_DISABLED
, m_size(size)
#endif
, m_numberOfEntries(numberOfEntries)
{
UNUSED_PARAM(size);
}
~CompactJITCodeMap()
{
if (m_buffer)
fastFree(m_buffer);
}
unsigned numberOfEntries() const
{
return m_numberOfEntries;
}
void decode(Vector<BytecodeAndMachineOffset>& result) const;
private:
uint8_t at(unsigned index) const
{
ASSERT(index < m_size);
return m_buffer[index];
}
unsigned decodeNumber(unsigned& index) const
{
uint8_t headValue = at(index++);
if (!(headValue & 128))
return headValue;
if (!(headValue & 64))
return (static_cast<unsigned>(headValue & ~128) << 8) | at(index++);
unsigned second = at(index++);
unsigned third = at(index++);
unsigned fourth = at(index++);
return (static_cast<unsigned>(headValue & ~(128 + 64)) << 24) | (second << 16) | (third << 8) | fourth;
}
uint8_t* m_buffer;
#if !ASSERT_DISABLED
unsigned m_size;
#endif
unsigned m_numberOfEntries;
public:
class Encoder {
WTF_MAKE_NONCOPYABLE(Encoder);
public:
Encoder();
~Encoder();
void ensureCapacityFor(unsigned numberOfEntriesToAdd);
void append(unsigned bytecodeIndex, unsigned machineCodeOffset);
std::unique_ptr<CompactJITCodeMap> finish();
private:
void appendByte(uint8_t value);
void encodeNumber(uint32_t value);
uint8_t* m_buffer;
unsigned m_size;
unsigned m_capacity;
unsigned m_numberOfEntries;
unsigned m_previousBytecodeIndex;
unsigned m_previousMachineCodeOffset;
};
class Decoder {
WTF_MAKE_NONCOPYABLE(Decoder);
public:
Decoder(const CompactJITCodeMap*);
unsigned numberOfEntriesRemaining() const;
void read(unsigned& bytecodeIndex, unsigned& machineCodeOffset);
private:
const CompactJITCodeMap* m_jitCodeMap;
unsigned m_previousBytecodeIndex;
unsigned m_previousMachineCodeOffset;
unsigned m_numberOfEntriesRemaining;
unsigned m_bufferIndex;
};
private:
friend class Encoder;
friend class Decoder;
};
inline void CompactJITCodeMap::decode(Vector<BytecodeAndMachineOffset>& result) const
{
Decoder decoder(this);
result.resize(decoder.numberOfEntriesRemaining());
for (unsigned i = 0; i < result.size(); ++i)
decoder.read(result[i].m_bytecodeIndex, result[i].m_machineCodeOffset);
ASSERT(!decoder.numberOfEntriesRemaining());
}
inline CompactJITCodeMap::Encoder::Encoder()
: m_buffer(0)
, m_size(0)
, m_capacity(0)
, m_numberOfEntries(0)
, m_previousBytecodeIndex(0)
, m_previousMachineCodeOffset(0)
{
}
inline CompactJITCodeMap::Encoder::~Encoder()
{
if (m_buffer)
fastFree(m_buffer);
}
inline void CompactJITCodeMap::Encoder::append(unsigned bytecodeIndex, unsigned machineCodeOffset)
{
ASSERT(bytecodeIndex >= m_previousBytecodeIndex);
ASSERT(machineCodeOffset >= m_previousMachineCodeOffset);
ensureCapacityFor(1);
encodeNumber(bytecodeIndex - m_previousBytecodeIndex);
encodeNumber(machineCodeOffset - m_previousMachineCodeOffset);
m_previousBytecodeIndex = bytecodeIndex;
m_previousMachineCodeOffset = machineCodeOffset;
m_numberOfEntries++;
}
inline std::unique_ptr<CompactJITCodeMap> CompactJITCodeMap::Encoder::finish()
{
m_capacity = m_size;
m_buffer = static_cast<uint8_t*>(fastRealloc(m_buffer, m_capacity));
auto result = std::make_unique<CompactJITCodeMap>(m_buffer, m_size, m_numberOfEntries);
m_buffer = 0;
m_size = 0;
m_capacity = 0;
m_numberOfEntries = 0;
m_previousBytecodeIndex = 0;
m_previousMachineCodeOffset = 0;
return result;
}
inline void CompactJITCodeMap::Encoder::appendByte(uint8_t value)
{
ASSERT(m_size + 1 <= m_capacity);
m_buffer[m_size++] = value;
}
inline void CompactJITCodeMap::Encoder::encodeNumber(uint32_t value)
{
ASSERT(m_size + 4 <= m_capacity);
ASSERT(value < (1 << 30));
if (value <= 127) {
uint8_t headValue = static_cast<uint8_t>(value);
ASSERT(!(headValue & 128));
appendByte(headValue);
} else if (value <= 16383) {
uint8_t headValue = static_cast<uint8_t>(value >> 8);
ASSERT(!(headValue & 128));
ASSERT(!(headValue & 64));
appendByte(headValue | 128);
appendByte(static_cast<uint8_t>(value));
} else {
uint8_t headValue = static_cast<uint8_t>(value >> 24);
ASSERT(!(headValue & 128));
ASSERT(!(headValue & 64));
appendByte(headValue | 128 | 64);
appendByte(static_cast<uint8_t>(value >> 16));
appendByte(static_cast<uint8_t>(value >> 8));
appendByte(static_cast<uint8_t>(value));
}
}
inline void CompactJITCodeMap::Encoder::ensureCapacityFor(unsigned numberOfEntriesToAdd)
{
unsigned capacityNeeded = m_size + numberOfEntriesToAdd * 2 * 4;
if (capacityNeeded > m_capacity) {
m_capacity = capacityNeeded * 2;
m_buffer = static_cast<uint8_t*>(fastRealloc(m_buffer, m_capacity));
}
}
inline CompactJITCodeMap::Decoder::Decoder(const CompactJITCodeMap* jitCodeMap)
: m_jitCodeMap(jitCodeMap)
, m_previousBytecodeIndex(0)
, m_previousMachineCodeOffset(0)
, m_numberOfEntriesRemaining(jitCodeMap->m_numberOfEntries)
, m_bufferIndex(0)
{
}
inline unsigned CompactJITCodeMap::Decoder::numberOfEntriesRemaining() const
{
ASSERT(m_numberOfEntriesRemaining || m_bufferIndex == m_jitCodeMap->m_size);
return m_numberOfEntriesRemaining;
}
inline void CompactJITCodeMap::Decoder::read(unsigned& bytecodeIndex, unsigned& machineCodeOffset)
{
ASSERT(numberOfEntriesRemaining());
m_previousBytecodeIndex += m_jitCodeMap->decodeNumber(m_bufferIndex);
m_previousMachineCodeOffset += m_jitCodeMap->decodeNumber(m_bufferIndex);
bytecodeIndex = m_previousBytecodeIndex;
machineCodeOffset = m_previousMachineCodeOffset;
m_numberOfEntriesRemaining--;
}
} // namespace JSC