blob: c3e1e93327153c24e2b6b095dd46dc6abfad7294 [file] [log] [blame]
// Copyright 2013, ARM Limited
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of ARM Limited 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT OWNER 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.
#ifndef jit_arm64_vixl_MozBaseAssembler_vixl_h
#define jit_arm64_vixl_MozBaseAssembler_vixl_h
#include "jit/arm64/vixl/Constants-vixl.h"
#include "jit/arm64/vixl/Instructions-vixl.h"
#include "jit/shared/Assembler-shared.h"
#include "jit/shared/IonAssemblerBufferWithConstantPools.h"
namespace vixl {
using js::jit::BufferOffset;
class MozBaseAssembler;
typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, MozBaseAssembler,
NumShortBranchRangeTypes> ARMBuffer;
// Base class for vixl::Assembler, for isolating Moz-specific changes to VIXL.
class MozBaseAssembler : public js::jit::AssemblerShared {
// Buffer initialization constants.
static const unsigned BufferGuardSize = 1;
static const unsigned BufferHeaderSize = 1;
static const size_t BufferCodeAlignment = 8;
static const size_t BufferMaxPoolOffset = 1024;
static const unsigned BufferPCBias = 0;
static const uint32_t BufferAlignmentFillInstruction = BRK | (0xdead << ImmException_offset);
static const uint32_t BufferNopFillInstruction = HINT | (31 << Rt_offset);
static const unsigned BufferNumDebugNopsToInsert = 0;
public:
MozBaseAssembler()
: armbuffer_(BufferGuardSize,
BufferHeaderSize,
BufferCodeAlignment,
BufferMaxPoolOffset,
BufferPCBias,
BufferAlignmentFillInstruction,
BufferNopFillInstruction,
BufferNumDebugNopsToInsert)
{ }
public:
// Helper function for use with the ARMBuffer.
// The MacroAssembler must create an AutoJitContextAlloc before initializing the buffer.
void initWithAllocator() {
armbuffer_.initWithAllocator();
}
// Return the Instruction at a given byte offset.
Instruction* getInstructionAt(BufferOffset offset) {
return armbuffer_.getInst(offset);
}
// Return the byte offset of a bound label.
template <typename T>
inline T GetLabelByteOffset(const js::jit::Label* label) {
VIXL_ASSERT(label->bound());
JS_STATIC_ASSERT(sizeof(T) >= sizeof(uint32_t));
return reinterpret_cast<T>(label->offset());
}
protected:
// Get the buffer offset of the next inserted instruction. This may flush
// constant pools.
BufferOffset nextInstrOffset() {
return armbuffer_.nextInstrOffset();
}
// Get the next usable buffer offset. Note that a constant pool may be placed
// here before the next instruction is emitted.
BufferOffset nextOffset() const {
return armbuffer_.nextOffset();
}
// Allocate memory in the buffer by forwarding to armbuffer_.
// Propagate OOM errors.
BufferOffset allocEntry(size_t numInst, unsigned numPoolEntries,
uint8_t* inst, uint8_t* data,
ARMBuffer::PoolEntry* pe = nullptr,
bool markAsBranch = false)
{
BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst,
data, pe, markAsBranch);
propagateOOM(offset.assigned());
return offset;
}
// Emit the instruction, returning its offset.
BufferOffset Emit(Instr instruction, bool isBranch = false) {
JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
return armbuffer_.putInt(*(uint32_t*)(&instruction), isBranch);
}
BufferOffset EmitBranch(Instr instruction) {
return Emit(instruction, true);
}
public:
// Emit the instruction at |at|.
static void Emit(Instruction* at, Instr instruction) {
JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
memcpy(at, &instruction, sizeof(instruction));
}
static void EmitBranch(Instruction* at, Instr instruction) {
// TODO: Assert that the buffer already has the instruction marked as a branch.
Emit(at, instruction);
}
// Emit data inline in the instruction stream.
BufferOffset EmitData(void const * data, unsigned size) {
VIXL_ASSERT(size % 4 == 0);
return armbuffer_.allocEntry(size / sizeof(uint32_t), 0, (uint8_t*)(data), nullptr);
}
public:
// Size of the code generated in bytes, including pools.
size_t SizeOfCodeGenerated() const {
return armbuffer_.size();
}
// Move the pool into the instruction stream.
void flushBuffer() {
armbuffer_.flushPool();
}
// Inhibit pool flushing for the given number of instructions.
// Generating more than |maxInst| instructions in a no-pool region
// triggers an assertion within the ARMBuffer.
// Does not nest.
void enterNoPool(size_t maxInst) {
armbuffer_.enterNoPool(maxInst);
}
// Marks the end of a no-pool region.
void leaveNoPool() {
armbuffer_.leaveNoPool();
}
public:
// Static interface used by IonAssemblerBufferWithConstantPools.
static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
static bool PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx, BufferOffset deadline,
BufferOffset veneer);
static uint32_t PlaceConstantPoolBarrier(int offset);
static void WritePoolHeader(uint8_t* start, js::jit::Pool* p, bool isNatural);
static void WritePoolFooter(uint8_t* start, js::jit::Pool* p, bool isNatural);
static void WritePoolGuard(BufferOffset branch, Instruction* inst, BufferOffset dest);
static ptrdiff_t GetBranchOffset(const Instruction* i);
static void RetargetNearBranch(Instruction* i, int offset, Condition cond, bool final = true);
static void RetargetNearBranch(Instruction* i, int offset, bool final = true);
static void RetargetFarBranch(Instruction* i, uint8_t** slot, uint8_t* dest, Condition cond);
protected:
// Functions for managing Labels and linked lists of Label uses.
// Get the next Label user in the linked list of Label uses.
// Return an unassigned BufferOffset when the end of the list is reached.
BufferOffset NextLink(BufferOffset cur);
// Patch the instruction at cur to link to the instruction at next.
void SetNextLink(BufferOffset cur, BufferOffset next);
// Link the current (not-yet-emitted) instruction to the specified label,
// then return a raw offset to be encoded in the instruction.
ptrdiff_t LinkAndGetByteOffsetTo(BufferOffset branch, js::jit::Label* label);
ptrdiff_t LinkAndGetInstructionOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
js::jit::Label* label);
ptrdiff_t LinkAndGetPageOffsetTo(BufferOffset branch, js::jit::Label* label);
// A common implementation for the LinkAndGet<Type>OffsetTo helpers.
ptrdiff_t LinkAndGetOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
unsigned elementSizeBits, js::jit::Label* label);
protected:
// The buffer into which code and relocation info are generated.
ARMBuffer armbuffer_;
js::jit::CompactBufferWriter jumpRelocations_;
js::jit::CompactBufferWriter dataRelocations_;
js::jit::CompactBufferWriter relocations_;
js::jit::CompactBufferWriter preBarriers_;
};
} // namespace vixl
#endif // jit_arm64_vixl_MozBaseAssembler_vixl_h