blob: e59d29bf30005146c77945aaf6aad94458e84a7f [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:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_x64_MacroAssembler_x64_h
#define jit_x64_MacroAssembler_x64_h
#include "jit/JitFrames.h"
#include "jit/MoveResolver.h"
#include "jit/x86-shared/MacroAssembler-x86-shared.h"
namespace js {
namespace jit {
struct ImmShiftedTag : public ImmWord
{
explicit ImmShiftedTag(JSValueShiftedTag shtag)
: ImmWord((uintptr_t)shtag)
{ }
explicit ImmShiftedTag(JSValueType type)
: ImmWord(uintptr_t(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))))
{ }
};
struct ImmTag : public Imm32
{
explicit ImmTag(JSValueTag tag)
: Imm32(tag)
{ }
};
class MacroAssemblerX64 : public MacroAssemblerX86Shared
{
private:
// Perform a downcast. Should be removed by Bug 996602.
MacroAssembler& asMasm();
const MacroAssembler& asMasm() const;
void bindOffsets(const MacroAssemblerX86Shared::UsesVector&);
public:
using MacroAssemblerX86Shared::branch32;
using MacroAssemblerX86Shared::branchTest32;
using MacroAssemblerX86Shared::load32;
using MacroAssemblerX86Shared::store32;
MacroAssemblerX64()
{
}
// The buffer is about to be linked, make sure any constant pools or excess
// bookkeeping has been flushed to the instruction stream.
void finish();
/////////////////////////////////////////////////////////////////
// X64 helpers.
/////////////////////////////////////////////////////////////////
void writeDataRelocation(const Value& val) {
if (val.isMarkable()) {
gc::Cell* cell = reinterpret_cast<gc::Cell*>(val.toGCThing());
if (cell && gc::IsInsideNursery(cell))
embedsNurseryPointers_ = true;
dataRelocations_.writeUnsigned(masm.currentOffset());
}
}
// Refers to the upper 32 bits of a 64-bit Value operand.
// On x86_64, the upper 32 bits do not necessarily only contain the type.
Operand ToUpper32(Operand base) {
switch (base.kind()) {
case Operand::MEM_REG_DISP:
return Operand(Register::FromCode(base.base()), base.disp() + 4);
case Operand::MEM_SCALE:
return Operand(Register::FromCode(base.base()), Register::FromCode(base.index()),
base.scale(), base.disp() + 4);
default:
MOZ_CRASH("unexpected operand kind");
}
}
static inline Operand ToUpper32(const Address& address) {
return Operand(address.base, address.offset + 4);
}
static inline Operand ToUpper32(const BaseIndex& address) {
return Operand(address.base, address.index, address.scale, address.offset + 4);
}
uint32_t Upper32Of(JSValueShiftedTag tag) {
union { // Implemented in this way to appease MSVC++.
uint64_t tag;
struct {
uint32_t lo32;
uint32_t hi32;
} s;
} e;
e.tag = tag;
return e.s.hi32;
}
JSValueShiftedTag GetShiftedTag(JSValueType type) {
return (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
}
/////////////////////////////////////////////////////////////////
// X86/X64-common interface.
/////////////////////////////////////////////////////////////////
Address ToPayload(Address value) {
return value;
}
void storeValue(ValueOperand val, Operand dest) {
movq(val.valueReg(), dest);
}
void storeValue(ValueOperand val, const Address& dest) {
storeValue(val, Operand(dest));
}
template <typename T>
void storeValue(JSValueType type, Register reg, const T& dest) {
// Value types with 32-bit payloads can be emitted as two 32-bit moves.
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
movl(reg, Operand(dest));
movl(Imm32(Upper32Of(GetShiftedTag(type))), ToUpper32(Operand(dest)));
} else {
ScratchRegisterScope scratch(asMasm());
boxValue(type, reg, scratch);
movq(scratch, Operand(dest));
}
}
template <typename T>
void storeValue(const Value& val, const T& dest) {
ScratchRegisterScope scratch(asMasm());
jsval_layout jv = JSVAL_TO_IMPL(val);
if (val.isMarkable()) {
movWithPatch(ImmWord(jv.asBits), scratch);
writeDataRelocation(val);
} else {
mov(ImmWord(jv.asBits), scratch);
}
movq(scratch, Operand(dest));
}
void storeValue(ValueOperand val, BaseIndex dest) {
storeValue(val, Operand(dest));
}
void loadValue(Operand src, ValueOperand val) {
movq(src, val.valueReg());
}
void loadValue(Address src, ValueOperand val) {
loadValue(Operand(src), val);
}
void loadValue(const BaseIndex& src, ValueOperand val) {
loadValue(Operand(src), val);
}
void tagValue(JSValueType type, Register payload, ValueOperand dest) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(dest.valueReg() != scratch);
if (payload != dest.valueReg())
movq(payload, dest.valueReg());
mov(ImmShiftedTag(type), scratch);
orq(scratch, dest.valueReg());
}
void pushValue(ValueOperand val) {
push(val.valueReg());
}
void popValue(ValueOperand val) {
pop(val.valueReg());
}
void pushValue(const Value& val) {
jsval_layout jv = JSVAL_TO_IMPL(val);
if (val.isMarkable()) {
ScratchRegisterScope scratch(asMasm());
movWithPatch(ImmWord(jv.asBits), scratch);
writeDataRelocation(val);
push(scratch);
} else {
push(ImmWord(jv.asBits));
}
}
void pushValue(JSValueType type, Register reg) {
ScratchRegisterScope scratch(asMasm());
boxValue(type, reg, scratch);
push(scratch);
}
void pushValue(const Address& addr) {
push(Operand(addr));
}
void moveValue(const Value& val, Register dest) {
jsval_layout jv = JSVAL_TO_IMPL(val);
movWithPatch(ImmWord(jv.asBits), dest);
writeDataRelocation(val);
}
void moveValue(const Value& src, const ValueOperand& dest) {
moveValue(src, dest.valueReg());
}
void moveValue(const ValueOperand& src, const ValueOperand& dest) {
if (src.valueReg() != dest.valueReg())
movq(src.valueReg(), dest.valueReg());
}
void boxValue(JSValueType type, Register src, Register dest) {
MOZ_ASSERT(src != dest);
JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
#ifdef DEBUG
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
Label upper32BitsZeroed;
movePtr(ImmWord(UINT32_MAX), dest);
branchPtr(Assembler::BelowOrEqual, src, dest, &upper32BitsZeroed);
breakpoint();
bind(&upper32BitsZeroed);
}
#endif
mov(ImmShiftedTag(tag), dest);
orq(src, dest);
}
Condition testUndefined(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_UNDEFINED));
return cond;
}
Condition testInt32(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_INT32));
return cond;
}
Condition testBoolean(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_BOOLEAN));
return cond;
}
Condition testNull(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_NULL));
return cond;
}
Condition testString(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_STRING));
return cond;
}
Condition testSymbol(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_SYMBOL));
return cond;
}
Condition testObject(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_OBJECT));
return cond;
}
Condition testDouble(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, Imm32(JSVAL_TAG_MAX_DOUBLE));
return cond == Equal ? BelowOrEqual : Above;
}
Condition testNumber(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, Imm32(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
return cond == Equal ? BelowOrEqual : Above;
}
Condition testGCThing(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, Imm32(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
return cond == Equal ? AboveOrEqual : Below;
}
Condition testMagic(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_MAGIC));
return cond;
}
Condition testError(Condition cond, Register tag) {
return testMagic(cond, tag);
}
Condition testPrimitive(Condition cond, Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET));
return cond == Equal ? Below : AboveOrEqual;
}
Condition testUndefined(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testUndefined(cond, scratch);
}
Condition testInt32(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testInt32(cond, scratch);
}
Condition testBoolean(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBoolean(cond, scratch);
}
Condition testDouble(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testDouble(cond, scratch);
}
Condition testNumber(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testNumber(cond, scratch);
}
Condition testNull(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testNull(cond, scratch);
}
Condition testString(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testString(cond, scratch);
}
Condition testSymbol(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testSymbol(cond, scratch);
}
Condition testObject(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testObject(cond, scratch);
}
Condition testGCThing(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testGCThing(cond, scratch);
}
Condition testPrimitive(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testPrimitive(cond, scratch);
}
Condition testUndefined(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testUndefined(cond, scratch);
}
Condition testInt32(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testInt32(cond, scratch);
}
Condition testBoolean(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBoolean(cond, scratch);
}
Condition testDouble(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testDouble(cond, scratch);
}
Condition testNumber(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testNumber(cond, scratch);
}
Condition testNull(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testNull(cond, scratch);
}
Condition testString(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testString(cond, scratch);
}
Condition testSymbol(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testSymbol(cond, scratch);
}
Condition testObject(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testObject(cond, scratch);
}
Condition testPrimitive(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testPrimitive(cond, scratch);
}
Condition testGCThing(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testGCThing(cond, scratch);
}
Condition testMagic(Condition cond, const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testMagic(cond, scratch);
}
Condition testUndefined(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testUndefined(cond, scratch);
}
Condition testNull(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testNull(cond, scratch);
}
Condition testBoolean(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBoolean(cond, scratch);
}
Condition testString(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testString(cond, scratch);
}
Condition testSymbol(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testSymbol(cond, scratch);
}
Condition testInt32(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testInt32(cond, scratch);
}
Condition testObject(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testObject(cond, scratch);
}
Condition testDouble(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testDouble(cond, scratch);
}
Condition testMagic(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testMagic(cond, scratch);
}
Condition testGCThing(Condition cond, const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testGCThing(cond, scratch);
}
Condition isMagic(Condition cond, const ValueOperand& src, JSWhyMagic why) {
uint64_t magic = MagicValue(why).asRawBits();
cmpPtr(src.valueReg(), ImmWord(magic));
return cond;
}
void cmpPtr(Register lhs, const ImmWord rhs) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(lhs != scratch);
if (intptr_t(rhs.value) <= INT32_MAX && intptr_t(rhs.value) >= INT32_MIN) {
cmpPtr(lhs, Imm32(int32_t(rhs.value)));
} else {
movePtr(rhs, scratch);
cmpPtr(lhs, scratch);
}
}
void cmpPtr(Register lhs, const ImmPtr rhs) {
cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
}
void cmpPtr(Register lhs, const ImmGCPtr rhs) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(lhs != scratch);
movePtr(rhs, scratch);
cmpPtr(lhs, scratch);
}
void cmpPtr(Register lhs, const Imm32 rhs) {
cmpq(rhs, lhs);
}
void cmpPtr(const Operand& lhs, const ImmGCPtr rhs) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(!lhs.containsReg(scratch));
movePtr(rhs, scratch);
cmpPtr(lhs, scratch);
}
void cmpPtr(const Operand& lhs, const ImmWord rhs) {
if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) {
cmpPtr(lhs, Imm32((int32_t)rhs.value));
} else {
ScratchRegisterScope scratch(asMasm());
movePtr(rhs, scratch);
cmpPtr(lhs, scratch);
}
}
void cmpPtr(const Operand& lhs, const ImmPtr rhs) {
cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
}
void cmpPtr(const Address& lhs, const ImmGCPtr rhs) {
cmpPtr(Operand(lhs), rhs);
}
void cmpPtr(const Address& lhs, const ImmWord rhs) {
cmpPtr(Operand(lhs), rhs);
}
void cmpPtr(const Address& lhs, const ImmPtr rhs) {
cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
}
void cmpPtr(const Operand& lhs, Register rhs) {
cmpq(rhs, lhs);
}
void cmpPtr(Register lhs, const Operand& rhs) {
cmpq(rhs, lhs);
}
void cmpPtr(const Operand& lhs, const Imm32 rhs) {
cmpq(rhs, lhs);
}
void cmpPtr(const Address& lhs, Register rhs) {
cmpPtr(Operand(lhs), rhs);
}
void cmpPtr(Register lhs, Register rhs) {
cmpq(rhs, lhs);
}
void testPtr(Register lhs, Register rhs) {
testq(rhs, lhs);
}
void testPtr(Register lhs, Imm32 rhs) {
testq(rhs, lhs);
}
void testPtr(const Operand& lhs, Imm32 rhs) {
testq(rhs, lhs);
}
template <typename T1, typename T2>
void cmpPtrSet(Assembler::Condition cond, T1 lhs, T2 rhs, Register dest)
{
cmpPtr(lhs, rhs);
emitSet(cond, dest);
}
/////////////////////////////////////////////////////////////////
// Common interface.
/////////////////////////////////////////////////////////////////
void addPtr(Register src, Register dest) {
addq(src, dest);
}
void addPtr(Imm32 imm, Register dest) {
addq(imm, dest);
}
void addPtr(Imm32 imm, const Address& dest) {
addq(imm, Operand(dest));
}
void addPtr(Imm32 imm, const Operand& dest) {
addq(imm, dest);
}
void addPtr(ImmWord imm, Register dest) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(dest != scratch);
if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
addq(Imm32((int32_t)imm.value), dest);
} else {
mov(imm, scratch);
addq(scratch, dest);
}
}
void addPtr(ImmPtr imm, Register dest) {
addPtr(ImmWord(uintptr_t(imm.value)), dest);
}
void addPtr(const Address& src, Register dest) {
addq(Operand(src), dest);
}
void add64(Imm32 imm, Register64 dest) {
addq(imm, dest.reg);
}
void subPtr(Imm32 imm, Register dest) {
subq(imm, dest);
}
void subPtr(Register src, Register dest) {
subq(src, dest);
}
void subPtr(const Address& addr, Register dest) {
subq(Operand(addr), dest);
}
void subPtr(Register src, const Address& dest) {
subq(src, Operand(dest));
}
void mulBy3(const Register& src, const Register& dest) {
lea(Operand(src, src, TimesTwo), dest);
}
void mul64(Imm64 imm, const Register64& dest) {
movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
imulq(ScratchReg, dest.reg);
}
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
if (X86Encoding::IsAddressImmediate(lhs.addr)) {
branch32(cond, Operand(lhs), rhs, label);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(lhs.addr), scratch);
branch32(cond, Address(scratch, 0), rhs, label);
}
}
void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label) {
ScratchRegisterScope scratch(asMasm());
mov(lhs, scratch);
branch32(cond, Address(scratch, 0), rhs, label);
}
void branch32(Condition cond, AbsoluteAddress lhs, Register rhs, Label* label) {
if (X86Encoding::IsAddressImmediate(lhs.addr)) {
branch32(cond, Operand(lhs), rhs, label);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(lhs.addr), scratch);
branch32(cond, Address(scratch, 0), rhs, label);
}
}
void branchTest32(Condition cond, AbsoluteAddress address, Imm32 imm, Label* label) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
test32(Operand(address), imm);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
test32(Operand(scratch, 0), imm);
}
j(cond, label);
}
// Specialization for AbsoluteAddress.
void branchPtr(Condition cond, AbsoluteAddress addr, Register ptr, Label* label) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(ptr != scratch);
if (X86Encoding::IsAddressImmediate(addr.addr)) {
branchPtr(cond, Operand(addr), ptr, label);
} else {
mov(ImmPtr(addr.addr), scratch);
branchPtr(cond, Operand(scratch, 0x0), ptr, label);
}
}
void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label* label) {
if (X86Encoding::IsAddressImmediate(addr.addr)) {
branchPtr(cond, Operand(addr), ptr, label);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(addr.addr), scratch);
branchPtr(cond, Operand(scratch, 0x0), ptr, label);
}
}
void branchPtr(Condition cond, wasm::SymbolicAddress addr, Register ptr, Label* label) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(ptr != scratch);
mov(addr, scratch);
branchPtr(cond, Operand(scratch, 0x0), ptr, label);
}
void branchPrivatePtr(Condition cond, Address lhs, ImmPtr ptr, Label* label) {
branchPtr(cond, lhs, ImmWord(uintptr_t(ptr.value) >> 1), label);
}
void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label);
template <typename T, typename S>
void branchPtr(Condition cond, const T& lhs, const S& ptr, Label* label) {
cmpPtr(Operand(lhs), ptr);
j(cond, label);
}
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Label* documentation = nullptr) {
JmpSrc src = jmpSrc(label);
return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED));
}
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond,
Label* documentation = nullptr)
{
JmpSrc src = jSrc(cond, label);
return CodeOffsetJump(size(), addPatchableJump(src, Relocation::HARDCODED));
}
CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation = nullptr) {
return jumpWithPatch(label);
}
template <typename S, typename T>
CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel* label) {
cmpPtr(lhs, ptr);
return jumpWithPatch(label, cond);
}
void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) {
cmpPtr(lhs, rhs);
j(cond, label);
}
void branchTestPtr(Condition cond, Register lhs, Register rhs, Label* label) {
testPtr(lhs, rhs);
j(cond, label);
}
void branchTestPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
testPtr(lhs, imm);
j(cond, label);
}
void branchTestPtr(Condition cond, const Address& lhs, Imm32 imm, Label* label) {
testPtr(Operand(lhs), imm);
j(cond, label);
}
void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
subPtr(imm, lhs);
j(cond, label);
}
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
branchTestPtr(cond, lhs.reg, rhs.reg, label);
}
void movePtr(Register src, Register dest) {
movq(src, dest);
}
void movePtr(Register src, const Operand& dest) {
movq(src, dest);
}
void movePtr(ImmWord imm, Register dest) {
mov(imm, dest);
}
void movePtr(ImmPtr imm, Register dest) {
mov(imm, dest);
}
void movePtr(wasm::SymbolicAddress imm, Register dest) {
mov(imm, dest);
}
void movePtr(ImmGCPtr imm, Register dest) {
movq(imm, dest);
}
void move64(Register64 src, Register64 dest) {
movq(src.reg, dest.reg);
}
void loadPtr(AbsoluteAddress address, Register dest) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movq(Operand(address), dest);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
loadPtr(Address(scratch, 0x0), dest);
}
}
void loadPtr(const Address& address, Register dest) {
movq(Operand(address), dest);
}
void loadPtr(const Operand& src, Register dest) {
movq(src, dest);
}
void loadPtr(const BaseIndex& src, Register dest) {
movq(Operand(src), dest);
}
void loadPrivate(const Address& src, Register dest) {
loadPtr(src, dest);
shlq(Imm32(1), dest);
}
void load32(AbsoluteAddress address, Register dest) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movl(Operand(address), dest);
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
load32(Address(scratch, 0x0), dest);
}
}
void load64(const Address& address, Register64 dest) {
movq(Operand(address), dest.reg);
}
void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) {
MOZ_ASSERT(cond == Assembler::NotEqual,
"other condition codes not supported");
branchPtr(cond, lhs, ImmWord(val.value), label);
}
void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
Label* label)
{
MOZ_ASSERT(cond == Assembler::NotEqual,
"other condition codes not supported");
MOZ_ASSERT(lhs.base != scratch);
MOZ_ASSERT(rhs.base != scratch);
loadPtr(rhs, scratch);
branchPtr(cond, lhs, scratch, label);
}
template <typename T>
void storePtr(ImmWord imm, T address) {
if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
movq(Imm32((int32_t)imm.value), Operand(address));
} else {
ScratchRegisterScope scratch(asMasm());
mov(imm, scratch);
movq(scratch, Operand(address));
}
}
template <typename T>
void storePtr(ImmPtr imm, T address) {
storePtr(ImmWord(uintptr_t(imm.value)), address);
}
template <typename T>
void storePtr(ImmGCPtr imm, T address) {
ScratchRegisterScope scratch(asMasm());
movq(imm, scratch);
movq(scratch, Operand(address));
}
void storePtr(Register src, const Address& address) {
movq(src, Operand(address));
}
void storePtr(Register src, const BaseIndex& address) {
movq(src, Operand(address));
}
void storePtr(Register src, const Operand& dest) {
movq(src, dest);
}
void storePtr(Register src, AbsoluteAddress address) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movq(src, Operand(address));
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
storePtr(src, Address(scratch, 0x0));
}
}
void store32(Register src, AbsoluteAddress address) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movl(src, Operand(address));
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
store32(src, Address(scratch, 0x0));
}
}
void store64(Register64 src, Address address) {
movq(src.reg, Operand(address));
}
void splitTag(Register src, Register dest) {
if (src != dest)
movq(src, dest);
shrq(Imm32(JSVAL_TAG_SHIFT), dest);
}
void splitTag(const ValueOperand& operand, Register dest) {
splitTag(operand.valueReg(), dest);
}
void splitTag(const Operand& operand, Register dest) {
movq(operand, dest);
shrq(Imm32(JSVAL_TAG_SHIFT), dest);
}
void splitTag(const Address& operand, Register dest) {
splitTag(Operand(operand), dest);
}
void splitTag(const BaseIndex& operand, Register dest) {
splitTag(Operand(operand), dest);
}
// Extracts the tag of a value and places it in ScratchReg.
Register splitTagForTest(const ValueOperand& value) {
splitTag(value, ScratchReg);
return ScratchReg;
}
void cmpTag(const ValueOperand& operand, ImmTag tag) {
Register reg = splitTagForTest(operand);
cmp32(reg, tag);
}
void branchTestUndefined(Condition cond, Register tag, Label* label) {
cond = testUndefined(cond, tag);
j(cond, label);
}
void branchTestInt32(Condition cond, Register tag, Label* label) {
cond = testInt32(cond, tag);
j(cond, label);
}
void branchTestDouble(Condition cond, Register tag, Label* label) {
cond = testDouble(cond, tag);
j(cond, label);
}
void branchTestBoolean(Condition cond, Register tag, Label* label) {
cond = testBoolean(cond, tag);
j(cond, label);
}
void branchTestNull(Condition cond, Register tag, Label* label) {
cond = testNull(cond, tag);
j(cond, label);
}
void branchTestString(Condition cond, Register tag, Label* label) {
cond = testString(cond, tag);
j(cond, label);
}
void branchTestSymbol(Condition cond, Register tag, Label* label) {
cond = testSymbol(cond, tag);
j(cond, label);
}
void branchTestObject(Condition cond, Register tag, Label* label) {
cond = testObject(cond, tag);
j(cond, label);
}
void branchTestNumber(Condition cond, Register tag, Label* label) {
cond = testNumber(cond, tag);
j(cond, label);
}
// x64 can test for certain types directly from memory when the payload
// of the type is limited to 32 bits. This avoids loading into a register,
// accesses half as much memory, and removes a right-shift.
void branchTestUndefined(Condition cond, const Operand& operand, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_UNDEFINED))));
j(cond, label);
}
void branchTestUndefined(Condition cond, const Address& address, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestUndefined(cond, Operand(address), label);
}
void branchTestInt32(Condition cond, const Operand& operand, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_INT32))));
j(cond, label);
}
void branchTestInt32(Condition cond, const Address& address, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestInt32(cond, Operand(address), label);
}
void branchTestDouble(Condition cond, const Operand& operand, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
ScratchRegisterScope scratch(asMasm());
splitTag(operand, scratch);
branchTestDouble(cond, scratch, label);
}
void branchTestDouble(Condition cond, const Address& address, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestDouble(cond, Operand(address), label);
}
void branchTestBoolean(Condition cond, const Operand& operand, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_BOOLEAN))));
j(cond, label);
}
void branchTestBoolean(Condition cond, const Address& address, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestBoolean(cond, Operand(address), label);
}
void branchTestNull(Condition cond, const Operand& operand, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_NULL))));
j(cond, label);
}
void branchTestNull(Condition cond, const Address& address, Label* label) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestNull(cond, Operand(address), label);
}
// This one, though, clobbers the ScratchReg.
void branchTestObject(Condition cond, const Address& src, Label* label) {
cond = testObject(cond, src);
j(cond, label);
}
// Perform a type-test on a full Value loaded into a register.
// Clobbers the ScratchReg.
void branchTestUndefined(Condition cond, const ValueOperand& src, Label* label) {
cond = testUndefined(cond, src);
j(cond, label);
}
void branchTestInt32(Condition cond, const ValueOperand& src, Label* label) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
branchTestInt32(cond, scratch, label);
}
void branchTestBoolean(Condition cond, const ValueOperand& src, Label* label) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
branchTestBoolean(cond, scratch, label);
}
void branchTestDouble(Condition cond, const ValueOperand& src, Label* label) {
cond = testDouble(cond, src);
j(cond, label);
}
void branchTestNull(Condition cond, const ValueOperand& src, Label* label) {
cond = testNull(cond, src);
j(cond, label);
}
void branchTestString(Condition cond, const ValueOperand& src, Label* label) {
cond = testString(cond, src);
j(cond, label);
}
void branchTestSymbol(Condition cond, const ValueOperand& src, Label* label) {
cond = testSymbol(cond, src);
j(cond, label);
}
void branchTestObject(Condition cond, const ValueOperand& src, Label* label) {
cond = testObject(cond, src);
j(cond, label);
}
void branchTestNumber(Condition cond, const ValueOperand& src, Label* label) {
cond = testNumber(cond, src);
j(cond, label);
}
// Perform a type-test on a Value addressed by BaseIndex.
// Clobbers the ScratchReg.
void branchTestUndefined(Condition cond, const BaseIndex& address, Label* label) {
cond = testUndefined(cond, address);
j(cond, label);
}
void branchTestInt32(Condition cond, const BaseIndex& address, Label* label) {
ScratchRegisterScope scratch(asMasm());
splitTag(address, scratch);
branchTestInt32(cond, scratch, label);
}
void branchTestBoolean(Condition cond, const BaseIndex& address, Label* label) {
ScratchRegisterScope scratch(asMasm());
splitTag(address, scratch);
branchTestBoolean(cond, scratch, label);
}
void branchTestDouble(Condition cond, const BaseIndex& address, Label* label) {
cond = testDouble(cond, address);
j(cond, label);
}
void branchTestNull(Condition cond, const BaseIndex& address, Label* label) {
cond = testNull(cond, address);
j(cond, label);
}
void branchTestString(Condition cond, const BaseIndex& address, Label* label) {
cond = testString(cond, address);
j(cond, label);
}
void branchTestSymbol(Condition cond, const BaseIndex& address, Label* label) {
cond = testSymbol(cond, address);
j(cond, label);
}
void branchTestObject(Condition cond, const BaseIndex& address, Label* label) {
cond = testObject(cond, address);
j(cond, label);
}
template <typename T>
void branchTestGCThing(Condition cond, const T& src, Label* label) {
cond = testGCThing(cond, src);
j(cond, label);
}
template <typename T>
void branchTestPrimitive(Condition cond, const T& t, Label* label) {
cond = testPrimitive(cond, t);
j(cond, label);
}
template <typename T>
void branchTestMagic(Condition cond, const T& t, Label* label) {
cond = testMagic(cond, t);
j(cond, label);
}
void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
Label* label)
{
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchTestValue(cond, val, MagicValue(why), label);
}
Condition testMagic(Condition cond, const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testMagic(cond, scratch);
}
Condition testError(Condition cond, const ValueOperand& src) {
return testMagic(cond, src);
}
void branchTestValue(Condition cond, const ValueOperand& value, const Value& v, Label* label) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(value.valueReg() != scratch);
moveValue(v, scratch);
cmpPtr(value.valueReg(), scratch);
j(cond, label);
}
void branchTestValue(Condition cond, const Address& valaddr, const ValueOperand& value,
Label* label)
{
MOZ_ASSERT(cond == Equal || cond == NotEqual);
branchPtr(cond, valaddr, value.valueReg(), label);
}
void testNullSet(Condition cond, const ValueOperand& value, Register dest) {
cond = testNull(cond, value);
emitSet(cond, dest);
}
void testObjectSet(Condition cond, const ValueOperand& value, Register dest) {
cond = testObject(cond, value);
emitSet(cond, dest);
}
void testUndefinedSet(Condition cond, const ValueOperand& value, Register dest) {
cond = testUndefined(cond, value);
emitSet(cond, dest);
}
void boxDouble(FloatRegister src, const ValueOperand& dest) {
vmovq(src, dest.valueReg());
}
void boxNonDouble(JSValueType type, Register src, const ValueOperand& dest) {
MOZ_ASSERT(src != dest.valueReg());
boxValue(type, src, dest.valueReg());
}
// Note that the |dest| register here may be ScratchReg, so we shouldn't
// use it.
void unboxInt32(const ValueOperand& src, Register dest) {
movl(src.valueReg(), dest);
}
void unboxInt32(const Operand& src, Register dest) {
movl(src, dest);
}
void unboxInt32(const Address& src, Register dest) {
unboxInt32(Operand(src), dest);
}
void unboxDouble(const Address& src, FloatRegister dest) {
loadDouble(Operand(src), dest);
}
void unboxArgObjMagic(const ValueOperand& src, Register dest) {
unboxArgObjMagic(Operand(src.valueReg()), dest);
}
void unboxArgObjMagic(const Operand& src, Register dest) {
mov(ImmWord(0), dest);
}
void unboxArgObjMagic(const Address& src, Register dest) {
unboxArgObjMagic(Operand(src), dest);
}
void unboxBoolean(const ValueOperand& src, Register dest) {
movl(src.valueReg(), dest);
}
void unboxBoolean(const Operand& src, Register dest) {
movl(src, dest);
}
void unboxBoolean(const Address& src, Register dest) {
unboxBoolean(Operand(src), dest);
}
void unboxMagic(const ValueOperand& src, Register dest) {
movl(src.valueReg(), dest);
}
void unboxDouble(const ValueOperand& src, FloatRegister dest) {
vmovq(src.valueReg(), dest);
}
void unboxPrivate(const ValueOperand& src, const Register dest) {
movq(src.valueReg(), dest);
shlq(Imm32(1), dest);
}
void notBoolean(const ValueOperand& val) {
xorq(Imm32(1), val.valueReg());
}
// Unbox any non-double value into dest. Prefer unboxInt32 or unboxBoolean
// instead if the source type is known.
void unboxNonDouble(const ValueOperand& src, Register dest) {
if (src.valueReg() == dest) {
ScratchRegisterScope scratch(asMasm());
mov(ImmWord(JSVAL_PAYLOAD_MASK), scratch);
andq(scratch, dest);
} else {
mov(ImmWord(JSVAL_PAYLOAD_MASK), dest);
andq(src.valueReg(), dest);
}
}
void unboxNonDouble(const Operand& src, Register dest) {
// Explicitly permits |dest| to be used in |src|.
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(dest != scratch);
if (src.containsReg(dest)) {
mov(ImmWord(JSVAL_PAYLOAD_MASK), scratch);
// If src is already a register, then src and dest are the same
// thing and we don't need to move anything into dest.
if (src.kind() != Operand::REG)
movq(src, dest);
andq(scratch, dest);
} else {
mov(ImmWord(JSVAL_PAYLOAD_MASK), dest);
andq(src, dest);
}
}
void unboxString(const ValueOperand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxString(const Operand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxSymbol(const ValueOperand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxSymbol(const Operand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxObject(const ValueOperand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxObject(const Operand& src, Register dest) { unboxNonDouble(src, dest); }
void unboxObject(const Address& src, Register dest) { unboxNonDouble(Operand(src), dest); }
void unboxObject(const BaseIndex& src, Register dest) { unboxNonDouble(Operand(src), dest); }
// Extended unboxing API. If the payload is already in a register, returns
// that register. Otherwise, provides a move to the given scratch register,
// and returns that.
Register extractObject(const Address& address, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxObject(address, scratch);
return scratch;
}
Register extractObject(const ValueOperand& value, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxObject(value, scratch);
return scratch;
}
Register extractInt32(const ValueOperand& value, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxInt32(value, scratch);
return scratch;
}
Register extractBoolean(const ValueOperand& value, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxBoolean(value, scratch);
return scratch;
}
Register extractTag(const Address& address, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
loadPtr(address, scratch);
splitTag(scratch, scratch);
return scratch;
}
Register extractTag(const ValueOperand& value, Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
splitTag(value, scratch);
return scratch;
}
void unboxValue(const ValueOperand& src, AnyRegister dest) {
if (dest.isFloat()) {
Label notInt32, end;
branchTestInt32(Assembler::NotEqual, src, &notInt32);
convertInt32ToDouble(src.valueReg(), dest.fpu());
jump(&end);
bind(&notInt32);
unboxDouble(src, dest.fpu());
bind(&end);
} else {
unboxNonDouble(src, dest.gpr());
}
}
// These two functions use the low 32-bits of the full value register.
void boolValueToDouble(const ValueOperand& operand, FloatRegister dest) {
convertInt32ToDouble(operand.valueReg(), dest);
}
void int32ValueToDouble(const ValueOperand& operand, FloatRegister dest) {
convertInt32ToDouble(operand.valueReg(), dest);
}
void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest) {
convertInt32ToFloat32(operand.valueReg(), dest);
}
void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest) {
convertInt32ToFloat32(operand.valueReg(), dest);
}
void loadConstantDouble(double d, FloatRegister dest);
void loadConstantFloat32(float f, FloatRegister dest);
void loadConstantInt32x4(const SimdConstant& v, FloatRegister dest);
void loadConstantFloat32x4(const SimdConstant& v, FloatRegister dest);
void branchTruncateDouble(FloatRegister src, Register dest, Label* fail) {
vcvttsd2sq(src, dest);
// vcvttsd2sq returns 0x8000000000000000 on failure. Test for it by
// subtracting 1 and testing overflow (this avoids the need to
// materialize that value in a register).
cmpPtr(dest, Imm32(1));
j(Assembler::Overflow, fail);
movl(dest, dest); // Zero upper 32-bits.
}
void branchTruncateFloat32(FloatRegister src, Register dest, Label* fail) {
vcvttss2sq(src, dest);
// Same trick as for Doubles
cmpPtr(dest, Imm32(1));
j(Assembler::Overflow, fail);
movl(dest, dest); // Zero upper 32-bits.
}
Condition testInt32Truthy(bool truthy, const ValueOperand& operand) {
test32(operand.valueReg(), operand.valueReg());
return truthy ? NonZero : Zero;
}
void branchTestInt32Truthy(bool truthy, const ValueOperand& operand, Label* label) {
Condition cond = testInt32Truthy(truthy, operand);
j(cond, label);
}
void branchTestBooleanTruthy(bool truthy, const ValueOperand& operand, Label* label) {
test32(operand.valueReg(), operand.valueReg());
j(truthy ? NonZero : Zero, label);
}
Condition testStringTruthy(bool truthy, const ValueOperand& value) {
ScratchRegisterScope scratch(asMasm());
unboxString(value, scratch);
cmp32(Operand(scratch, JSString::offsetOfLength()), Imm32(0));
return truthy ? Assembler::NotEqual : Assembler::Equal;
}
void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) {
Condition cond = testStringTruthy(truthy, value);
j(cond, label);
}
void loadInt32OrDouble(const Operand& operand, FloatRegister dest) {
Label notInt32, end;
branchTestInt32(Assembler::NotEqual, operand, &notInt32);
convertInt32ToDouble(operand, dest);
jump(&end);
bind(&notInt32);
loadDouble(operand, dest);
bind(&end);
}
template <typename T>
void loadUnboxedValue(const T& src, MIRType type, AnyRegister dest) {
if (dest.isFloat())
loadInt32OrDouble(Operand(src), dest.fpu());
else if (type == MIRType_Int32 || type == MIRType_Boolean)
movl(Operand(src), dest.gpr());
else
unboxNonDouble(Operand(src), dest.gpr());
}
template <typename T>
void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, MIRType slotType);
template <typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
switch (nbytes) {
case 8: {
ScratchRegisterScope scratch(asMasm());
unboxNonDouble(value, scratch);
storePtr(scratch, address);
return;
}
case 4:
store32(value.valueReg(), address);
return;
case 1:
store8(value.valueReg(), address);
return;
default: MOZ_CRASH("Bad payload width");
}
}
void loadInstructionPointerAfterCall(Register dest) {
loadPtr(Address(StackPointer, 0x0), dest);
}
void convertUInt32ToDouble(Register src, FloatRegister dest) {
vcvtsq2sd(src, dest, dest);
}
void convertUInt32ToFloat32(Register src, FloatRegister dest) {
vcvtsq2ss(src, dest, dest);
}
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
vcvtsi2sdq(src.reg, dest);
}
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
movq(imm, ScratchReg);
vmulsd(Operand(ScratchReg, 0), dest, dest);
}
void inc64(AbsoluteAddress dest) {
if (X86Encoding::IsAddressImmediate(dest.addr)) {
addPtr(Imm32(1), Operand(dest));
} else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(dest.addr), scratch);
addPtr(Imm32(1), Address(scratch, 0));
}
}
void incrementInt32Value(const Address& addr) {
addPtr(Imm32(1), addr);
}
// If source is a double, load it into dest. If source is int32,
// convert it to double. Else, branch to failure.
void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure) {
Label isDouble, done;
Register tag = splitTagForTest(source);
branchTestDouble(Assembler::Equal, tag, &isDouble);
branchTestInt32(Assembler::NotEqual, tag, failure);
ScratchRegisterScope scratch(asMasm());
unboxInt32(source, scratch);
convertInt32ToDouble(scratch, dest);
jump(&done);
bind(&isDouble);
unboxDouble(source, dest);
bind(&done);
}
public:
void handleFailureWithHandlerTail(void* handler);
// See CodeGeneratorX64 calls to noteAsmJSGlobalAccess.
void patchAsmJSGlobalAccess(CodeOffset patchAt, uint8_t* code, uint8_t* globalData,
unsigned globalDataOffset)
{
uint8_t* nextInsn = code + patchAt.offset();
MOZ_ASSERT(nextInsn <= globalData);
uint8_t* target = globalData + globalDataOffset;
((int32_t*)nextInsn)[-1] = target - nextInsn;
}
void memIntToValue(Address Source, Address Dest) {
ScratchRegisterScope scratch(asMasm());
load32(Source, scratch);
storeValue(JSVAL_TYPE_INT32, scratch, Dest);
}
void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
// Instrumentation for entering and leaving the profiler.
void profilerEnterFrame(Register framePtr, Register scratch);
void profilerExitFrame();
};
typedef MacroAssemblerX64 MacroAssemblerSpecific;
} // namespace jit
} // namespace js
#endif /* jit_x64_MacroAssembler_x64_h */