blob: 95a8556ef24f77e7a9dcde666727aa7fdc121343 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Copyright (C) 2008 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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.
*
* ***** END LICENSE BLOCK ***** */
#ifndef assembler_assembler_AssemblerBuffer_h
#define assembler_assembler_AssemblerBuffer_h
#include "assembler/wtf/Platform.h"
#if ENABLE_ASSEMBLER
#include <string.h>
#include <limits.h>
#include "assembler/jit/ExecutableAllocator.h"
#include "assembler/wtf/Assertions.h"
#include <stdarg.h>
#include "jsfriendapi.h"
#include "jsopcode.h"
#include "jit/IonSpewer.h"
#include "js/RootingAPI.h"
#define PRETTY_PRINT_OFFSET(os) (((os)<0)?"-":""), (((os)<0)?-(os):(os))
#define FIXME_INSN_PRINTING \
do { \
spew("FIXME insn printing %s:%d", \
__FILE__, __LINE__); \
} while (0)
namespace JSC {
class AssemblerBuffer {
static const int inlineCapacity = 256;
public:
AssemblerBuffer()
: m_buffer(m_inlineBuffer)
, m_capacity(inlineCapacity)
, m_size(0)
, m_oom(false)
#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
, m_skipInline(js::TlsPerThreadData.get(), &m_inlineBuffer)
#endif
{
}
~AssemblerBuffer()
{
if (m_buffer != m_inlineBuffer)
js_free(m_buffer);
}
void ensureSpace(int space)
{
if (m_size > m_capacity - space)
grow();
}
bool isAligned(int alignment) const
{
return !(m_size & (alignment - 1));
}
void putByteUnchecked(int value)
{
ASSERT(!(m_size > m_capacity - 4));
m_buffer[m_size] = char(value);
m_size++;
}
void putByte(int value)
{
if (m_size > m_capacity - 4)
grow();
putByteUnchecked(value);
}
void putShortUnchecked(int value)
{
ASSERT(!(m_size > m_capacity - 4));
*reinterpret_cast<short*>(&m_buffer[m_size]) = short(value);
m_size += 2;
}
void putShort(int value)
{
if (m_size > m_capacity - 4)
grow();
putShortUnchecked(value);
}
void putIntUnchecked(int value)
{
ASSERT(!(m_size > m_capacity - 4));
*reinterpret_cast<int*>(&m_buffer[m_size]) = value;
m_size += 4;
}
void putInt64Unchecked(int64_t value)
{
ASSERT(!(m_size > m_capacity - 8));
*reinterpret_cast<int64_t*>(&m_buffer[m_size]) = value;
m_size += 8;
}
void putInt(int value)
{
if (m_size > m_capacity - 4)
grow();
putIntUnchecked(value);
}
void* data() const
{
return m_buffer;
}
int size() const
{
return m_size;
}
bool oom() const
{
return m_oom;
}
/*
* The user must check for a NULL return value, which means
* no code was generated, or there was an OOM.
*/
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp, CodeKind kind)
{
if (m_oom || m_size == 0) {
*poolp = NULL;
return 0;
}
void* result = allocator->alloc(m_size, poolp, kind);
if (!result) {
*poolp = NULL;
return 0;
}
JS_ASSERT(*poolp);
ExecutableAllocator::makeWritable(result, m_size);
return memcpy(result, m_buffer, m_size);
}
unsigned char *buffer() const {
ASSERT(!m_oom);
return reinterpret_cast<unsigned char *>(m_buffer);
}
protected:
void append(const char* data, int size)
{
if (m_size > m_capacity - size)
grow(size);
// If we OOM and size > inlineCapacity, this would crash.
if (m_oom)
return;
memcpy(m_buffer + m_size, data, size);
m_size += size;
}
/*
* OOM handling: This class can OOM in the grow() method trying to
* allocate a new buffer. In response to an OOM, we need to avoid
* crashing and report the error. We also want to make it so that
* users of this class need to check for OOM only at certain points
* and not after every operation.
*
* Our strategy for handling an OOM is to set m_oom, and then set
* m_size to 0, preserving the current buffer. This way, the user
* can continue assembling into the buffer, deferring OOM checking
* until the user wants to read code out of the buffer.
*
* See also the |executableAllocAndCopy| and |buffer| methods.
*/
void grow(int extraCapacity = 0)
{
/*
* If |extraCapacity| is zero (as it almost always is) this is an
* allocator-friendly doubling growth strategy.
*/
int newCapacity = m_capacity + m_capacity + extraCapacity;
char* newBuffer;
// Do not allow offsets to grow beyond INT_MAX / 2. This mirrors
// Assembler-shared.h.
if (newCapacity >= INT_MAX / 2) {
m_size = 0;
m_oom = true;
return;
}
if (m_buffer == m_inlineBuffer) {
newBuffer = static_cast<char*>(js_malloc(newCapacity));
if (!newBuffer) {
m_size = 0;
m_oom = true;
return;
}
memcpy(newBuffer, m_buffer, m_size);
} else {
newBuffer = static_cast<char*>(js_realloc(m_buffer, newCapacity));
if (!newBuffer) {
m_size = 0;
m_oom = true;
return;
}
}
m_buffer = newBuffer;
m_capacity = newCapacity;
}
char m_inlineBuffer[inlineCapacity];
char* m_buffer;
int m_capacity;
int m_size;
bool m_oom;
#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
/*
* GC Pointers baked into the code can get stored on the stack here
* through the inline assembler buffer. We need to protect these from
* being poisoned by the rooting analysis, however, they do not need to
* actually be traced: the compiler is only allowed to bake in
* non-nursery-allocated pointers, such as Shapes.
*/
js::SkipRoot m_skipInline;
#endif
};
class GenericAssembler
{
js::Sprinter *printer;
public:
bool isOOLPath;
GenericAssembler()
: printer(NULL)
, isOOLPath(false)
{}
void setPrinter(js::Sprinter *sp) {
printer = sp;
}
void spew(const char *fmt, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
{
if (printer
#ifdef JS_ION
|| js::jit::IonSpewEnabled(js::jit::IonSpew_Codegen)
#endif
)
{
// Buffer to hold the formatted string. Note that this may contain
// '%' characters, so do not pass it directly to printf functions.
char buf[200];
va_list va;
va_start(va, fmt);
int i = vsnprintf(buf, sizeof(buf), fmt, va);
va_end(va);
if (i > -1) {
if (printer)
printer->printf("%s\n", buf);
#ifdef JS_ION
js::jit::IonSpew(js::jit::IonSpew_Codegen, "%s", buf);
#endif
}
}
}
static void staticSpew(const char *fmt, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
{
#ifdef JS_ION
if (js::jit::IonSpewEnabled(js::jit::IonSpew_Codegen)) {
char buf[200];
va_list va;
va_start(va, fmt);
int i = vsnprintf(buf, sizeof(buf), fmt, va);
va_end(va);
if (i > -1)
js::jit::IonSpew(js::jit::IonSpew_Codegen, "%s", buf);
}
#endif
}
};
} // namespace JSC
#endif // ENABLE(ASSEMBLER)
#endif /* assembler_assembler_AssemblerBuffer_h */