#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/Disassembler-shared.h"
#include "jit/shared/IonAssemblerBufferWithConstantPools.h"
namespace vixl {
using js::jit::BufferOffset;
using js::jit::DisassemblerSpew;
using LabelDoc = DisassemblerSpew::LabelDoc;
using LiteralDoc = DisassemblerSpew::LiteralDoc;
#ifdef JS_DISASM_ARM64
void DisassembleInstruction(char* buffer, size_t bufsize, const Instruction* instr);
#endif
class MozBaseAssembler;
typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, MozBaseAssembler,
NumShortBranchRangeTypes> ARMBuffer;
class MozBaseAssembler : public js::jit::AssemblerShared {
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 = HINT | (NOP << ImmHint_offset);
static const uint32_t BufferNopFillInstruction = HINT | (NOP << ImmHint_offset);
static const unsigned BufferNumDebugNopsToInsert = 0;
#ifdef JS_DISASM_ARM64
static constexpr const char* const InstrIndent = " ";
static constexpr const char* const LabelIndent = " ";
static constexpr const char* const TargetIndent = " ";
#endif
public:
MozBaseAssembler()
: armbuffer_(BufferGuardSize,
BufferHeaderSize,
BufferCodeAlignment,
BufferMaxPoolOffset,
BufferPCBias,
BufferAlignmentFillInstruction,
BufferNopFillInstruction,
BufferNumDebugNopsToInsert)
{
#ifdef JS_DISASM_ARM64
spew_.setLabelIndent(LabelIndent);
spew_.setTargetIndent(TargetIndent);
#endif
}
~MozBaseAssembler()
{
#ifdef JS_DISASM_ARM64
spew_.spewOrphans();
#endif
}
public:
void initWithAllocator() {
armbuffer_.initWithAllocator();
}
Instruction* getInstructionAt(BufferOffset offset) {
return armbuffer_.getInst(offset);
}
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:
BufferOffset nextInstrOffset() {
return armbuffer_.nextInstrOffset();
}
BufferOffset nextOffset() const {
return armbuffer_.nextOffset();
}
BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
uint8_t* inst, uint8_t* data,
const LiteralDoc& doc = LiteralDoc(),
ARMBuffer::PoolEntry* pe = nullptr)
{
MOZ_ASSERT(inst);
MOZ_ASSERT(numInst == 1);
BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst,
data, pe);
propagateOOM(offset.assigned());
#ifdef JS_DISASM_ARM64
Instruction* instruction = armbuffer_.getInstOrNull(offset);
if (instruction)
spewLiteralLoad(reinterpret_cast<vixl::Instruction*>(instruction), doc);
#endif
return offset;
}
#ifdef JS_DISASM_ARM64
DisassemblerSpew spew_;
void spew(const vixl::Instruction* instr) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
spew_.spew("%08" PRIx32 "%s%s", instr->InstructionBits(), InstrIndent, buffer);
}
void spewBranch(const vixl::Instruction* instr, const LabelDoc& target) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
char labelBuf[128];
labelBuf[0] = 0;
bool hasTarget = target.valid;
if (!hasTarget)
SprintfLiteral(labelBuf, "-> (link-time target)");
if (instr->IsImmBranch() && hasTarget) {
size_t i;
const size_t BUFLEN = sizeof(buffer)-1;
for ( i=0 ; i < BUFLEN && buffer[i] && buffer[i] != '#' ; i++ )
;
buffer[i] = 0;
SprintfLiteral(labelBuf, "-> %d%s", target.doc, !target.bound ? "f" : "");
hasTarget = false;
}
spew_.spew("%08" PRIx32 "%s%s%s", instr->InstructionBits(), InstrIndent, buffer, labelBuf);
if (hasTarget)
spew_.spewRef(target);
}
void spewLiteralLoad(const vixl::Instruction* instr, const LiteralDoc& doc) {
if (spew_.isDisabled() || !instr)
return;
char buffer[2048];
DisassembleInstruction(buffer, sizeof(buffer), instr);
char litbuf[2048];
spew_.formatLiteral(doc, litbuf, sizeof(litbuf));
char *probe = strstr(buffer, "pc+0");
if (probe)
*(probe + 4) = 0;
spew_.spew("%08" PRIx32 "%s%s ; .const %s", instr->InstructionBits(), InstrIndent, buffer, litbuf);
}
LabelDoc refLabel(Label* label) {
if (spew_.isDisabled())
return LabelDoc();
return spew_.refLabel(label);
}
#else
LabelDoc refLabel(js::jit::Label*) {
return LabelDoc();
}
#endif
BufferOffset Emit(Instr instruction, bool isBranch = false) {
JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
(void)isBranch;
BufferOffset offs = armbuffer_.putInt(*(uint32_t*)(&instruction));
#ifdef JS_DISASM_ARM64
if (!isBranch)
spew(armbuffer_.getInstOrNull(offs));
#endif
return offs;
}
BufferOffset EmitBranch(Instr instruction, const LabelDoc& doc) {
BufferOffset offs = Emit(instruction, true);
#ifdef JS_DISASM_ARM64
spewBranch(armbuffer_.getInstOrNull(offs), doc);
#endif
return offs;
}
public:
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) {
Emit(at, instruction);
}
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_t SizeOfCodeGenerated() const {
return armbuffer_.size();
}
void flushBuffer() {
armbuffer_.flushPool();
}
void enterNoPool(size_t maxInst) {
armbuffer_.enterNoPool(maxInst);
}
void leaveNoPool() {
armbuffer_.leaveNoPool();
}
void enterNoNops() {
armbuffer_.enterNoNops();
}
void leaveNoNops() {
armbuffer_.leaveNoNops();
}
public:
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:
BufferOffset NextLink(BufferOffset cur);
void SetNextLink(BufferOffset cur, BufferOffset next);
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);
ptrdiff_t LinkAndGetOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
unsigned elementSizeBits, js::jit::Label* label);
protected:
ARMBuffer armbuffer_;
};
}
#endif