/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/x86-shared/MacroAssembler-x86-shared.h"
#include "js/HeapAPI.h"
#include "wasm/WasmBuiltins.h"
namespace js {
namespace jit {
struct ImmShiftedTag :
public ImmWord {
explicit ImmShiftedTag(JSValueType type)
: ImmWord(uintptr_t(JSVAL_TYPE_TO_SHIFTED_TAG(type))) {}
};
struct ImmTag :
public Imm32 {
explicit ImmTag(JSValueTag tag) : Imm32(tag) {}
};
// ScratchTagScope and ScratchTagScopeRelease are used to manage the tag
// register for splitTagForTest(), which has different register management on
// different platforms. On 64-bit platforms it requires a scratch register that
// does not interfere with other operations; on 32-bit platforms it uses a
// register that is already part of the Value.
//
// The ScratchTagScope RAII type acquires the appropriate register; a reference
// to a variable of this type is then passed to splitTagForTest().
//
// On 64-bit platforms ScratchTagScopeRelease makes the owned scratch register
// available in a dynamic scope during compilation. However it is important to
// remember that that does not preserve the register value in any way, so this
// RAII type should only be used along paths that eventually branch past further
// uses of the extracted tag value.
//
// On 32-bit platforms ScratchTagScopeRelease has no effect, since it does not
// manage a register, it only aliases a register in the ValueOperand.
class ScratchTagScope :
public ScratchRegisterScope {
public:
ScratchTagScope(MacroAssembler& masm,
const ValueOperand&)
: ScratchRegisterScope(masm) {}
};
class ScratchTagScopeRelease {
ScratchTagScope* ts_;
public:
explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) {
ts_->release();
}
~ScratchTagScopeRelease() { ts_->reacquire(); }
};
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&);
void vpRiprOpSimd128(
const SimdConstant& v, FloatRegister reg,
JmpSrc (X86Encoding::BaseAssemblerX64::*op)(
X86Encoding::XMMRegisterID id));
void vpRiprOpSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest,
JmpSrc (X86Encoding::BaseAssemblerX64::*op)(
X86Encoding::XMMRegisterID srcId,
X86Encoding::XMMRegisterID destId));
protected:
void flexibleDivMod64(
Register rhs,
Register lhsOutput,
bool isUnsigned,
bool isDiv);
public:
using MacroAssemblerX86Shared::load32;
using MacroAssemblerX86Shared::store16;
using MacroAssemblerX86Shared::store32;
MacroAssemblerX64() =
default;
// 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) {
// Raw GC pointer relocations and Value relocations both end up in
// Assembler::TraceDataRelocations.
if (val.isGCThing()) {
gc::Cell* 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) {
return uint32_t(tag >> 32); }
JSValueShiftedTag GetShiftedTag(JSValueType type) {
return (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
}
/////////////////////////////////////////////////////////////////
// X86/X64-common interface.
/////////////////////////////////////////////////////////////////
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());
if (val.isGCThing()) {
movWithPatch(ImmWord(val.asRawBits()), scratch);
writeDataRelocation(val);
}
else {
mov(ImmWord(val.asRawBits()), scratch);
}
movq(scratch, Operand(dest));
}
void storeValue(ValueOperand val, BaseIndex dest) {
storeValue(val, Operand(dest));
}
void storeValue(
const Address& src,
const Address& dest,
Register temp) {
loadPtr(src, temp);
storePtr(temp, dest);
}
void storePrivateValue(
Register src,
const Address& dest) {
storePtr(src, dest);
}
void storePrivateValue(ImmGCPtr imm,
const Address& dest) {
storePtr(imm, 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 loadUnalignedValue(
const Address& src, ValueOperand dest) {
loadValue(src, dest);
}
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) {
if (val.isGCThing()) {
ScratchRegisterScope scratch(asMasm());
movWithPatch(ImmWord(val.asRawBits()), scratch);
writeDataRelocation(val);
push(scratch);
}
else {
push(ImmWord(val.asRawBits()));
}
}
void pushValue(JSValueType type,
Register reg) {
ScratchRegisterScope scratch(asMasm());
boxValue(type, reg, scratch);
push(scratch);
}
void pushValue(
const Address& addr) { push(Operand(addr)); }
void pushValue(
const BaseIndex& addr,
Register scratch) {
push(Operand(addr));
}
void boxValue(JSValueType type,
Register src,
Register 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 testBigInt(Condition cond,
Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, ImmTag(JSVAL_TAG_BIGINT));
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(JS::detail::ValueUpperInclNumberTag));
return cond == Equal ? BelowOrEqual : Above;
}
Condition testGCThing(Condition cond,
Register tag) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
cmp32(tag, Imm32(JS::detail::ValueLowerInclGCThingTag));
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(JS::detail::ValueUpperExclPrimitiveTag));
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 testBigInt(Condition cond,
const ValueOperand& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBigInt(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) {
cmp32(ToUpper32(src),
Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_UNDEFINED))));
return cond;
}
Condition testInt32(Condition cond,
const Address& src) {
cmp32(ToUpper32(src), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_INT32))));
return cond;
}
Condition testBoolean(Condition cond,
const Address& src) {
cmp32(ToUpper32(src), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_BOOLEAN))));
return cond;
}
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) {
cmp32(ToUpper32(src), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_NULL))));
return cond;
}
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 testBigInt(Condition cond,
const Address& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBigInt(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 testBigInt(Condition cond,
const BaseIndex& src) {
ScratchRegisterScope scratch(asMasm());
splitTag(src, scratch);
return testBigInt(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(
Register lhs, ImmWord rhs) { test64(lhs, Imm64(rhs.value)); }
void testPtr(
const Operand& lhs, Imm32 rhs) { testq(rhs, lhs); }
void test64(
Register lhs,
Register rhs) { testq(rhs, lhs); }
void test64(
Register lhs,
const Imm64 rhs) {
if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) {
testq(Imm32((int32_t)rhs.value), lhs);
}
else {
ScratchRegisterScope scratch(asMasm());
movq(ImmWord(rhs.value), scratch);
testq(scratch, lhs);
}
}
// Compare-then-conditionally-move/load, for integer types
template <size_t CmpSize, size_t MoveSize>
void cmpMove(Condition cond,
Register lhs,
Register rhs,
Register falseVal,
Register trueValAndDest);
template <size_t CmpSize, size_t MoveSize>
void cmpMove(Condition cond,
Register lhs,
const Address& rhs,
Register falseVal,
Register trueValAndDest);
template <size_t CmpSize, size_t LoadSize>
void cmpLoad(Condition cond,
Register lhs,
Register rhs,
const Address& falseVal,
Register trueValAndDest);
template <size_t CmpSize, size_t LoadSize>
void cmpLoad(Condition cond,
Register lhs,
const Address& rhs,
const Address& falseVal,
Register trueValAndDest);
/////////////////////////////////////////////////////////////////
// Common interface.
/////////////////////////////////////////////////////////////////
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 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);
}
}
FaultingCodeOffset loadPtr(
const Address& address,
Register dest) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(Operand(address), dest);
return fco;
}
void load64(
const Address& address,
Register dest) {
movq(Operand(address), dest);
}
void loadPtr(
const Operand& src,
Register dest) { movq(src, dest); }
FaultingCodeOffset loadPtr(
const BaseIndex& src,
Register dest) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(Operand(src), dest);
return fco;
}
void loadPrivate(
const Address& src,
Register dest) { loadPtr(src, 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 Operand& address, Register64 dest) {
movq(address, dest.reg);
}
FaultingCodeOffset load64(
const Address& address, Register64 dest) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(Operand(address), dest.reg);
return fco;
}
FaultingCodeOffset load64(
const BaseIndex& address, Register64 dest) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(Operand(address), dest.reg);
return fco;
}
template <
typename S>
void load64Unaligned(
const S& src, Register64 dest) {
load64(src, dest);
}
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));
}
FaultingCodeOffset storePtr(
Register src,
const Address& address) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(src, Operand(address));
return fco;
}
void store64(
Register src,
const Address& address) {
movq(src, Operand(address));
}
void store64(Register64 src,
const Operand& address) {
movq(src.reg, address);
}
FaultingCodeOffset storePtr(
Register src,
const BaseIndex& address) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
movq(src, Operand(address));
return fco;
}
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 store16(
Register src, AbsoluteAddress address) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movw(src, Operand(address));
}
else {
ScratchRegisterScope scratch(asMasm());
mov(ImmPtr(address.addr), scratch);
store16(src, Address(scratch, 0x0));
}
}
FaultingCodeOffset store64(Register64 src, Address address) {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
storePtr(src.reg, address);
return fco;
}
FaultingCodeOffset store64(Register64 src,
const BaseIndex& address) {
return storePtr(src.reg, address);
}
void store64(Imm64 imm, Address address) {
storePtr(ImmWord(imm.value), address);
}
void store64(Imm64 imm,
const BaseIndex& address) {
storePtr(ImmWord(imm.value), address);
}
template <
typename S,
typename T>
void store64Unaligned(
const S& src,
const T& dest) {
store64(src, dest);
}
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 tag.
void splitTagForTest(
const ValueOperand& value, ScratchTagScope& tag) {
splitTag(value, tag);
}
void cmpTag(
const ValueOperand& operand, ImmTag tag) {
ScratchTagScope reg(asMasm(), operand);
splitTagForTest(operand, reg);
cmp32(reg, tag);
}
Condition testMagic(Condition cond,
const ValueOperand& src) {
ScratchTagScope scratch(asMasm(), src);
splitTagForTest(src, scratch);
return testMagic(cond, scratch);
}
Condition testError(Condition cond,
const ValueOperand& src) {
return testMagic(cond, src);
}
void testNullSet(Condition cond,
const ValueOperand& value,
Register dest) {
bool destIsZero = maybeEmitSetZeroByteRegister(value, dest);
cond = testNull(cond, value);
emitSet(cond, dest, destIsZero);
}
void testObjectSet(Condition cond,
const ValueOperand& value,
Register dest) {
bool destIsZero = maybeEmitSetZeroByteRegister(value, dest);
cond = testObject(cond, value);
emitSet(cond, dest, destIsZero);
}
void testUndefinedSet(Condition cond,
const ValueOperand& value,
Register dest) {
bool destIsZero = maybeEmitSetZeroByteRegister(value, dest);
cond = testUndefined(cond, value);
emitSet(cond, dest, destIsZero);
}
void boxDouble(FloatRegister src,
const ValueOperand& dest, FloatRegister) {
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 unboxInt32(
const BaseIndex& src,
Register dest) {
unboxInt32(Operand(src), dest);
}
template <
typename T>
void unboxDouble(
const T& 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 unboxBoolean(
const BaseIndex& 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 notBoolean(
const ValueOperand& val) { xorq(Imm32(1), val.valueReg()); }
void unboxNonDouble(
const ValueOperand& src,
Register dest,
JSValueType type) {
MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
movl(src.valueReg(), dest);
return;
}
if (src.valueReg() == dest) {
ScratchRegisterScope scratch(asMasm());
mov(ImmShiftedTag(type), scratch);
xorq(scratch, dest);
}
else {
mov(ImmShiftedTag(type), dest);
xorq(src.valueReg(), dest);
}
}
void unboxNonDouble(
const Operand& src,
Register dest, JSValueType type) {
MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
movl(src, dest);
return;
}
// Explicitly permits |dest| to be used in |src|.
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(dest != scratch);
if (src.containsReg(dest)) {
mov(ImmShiftedTag(type), 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);
}
xorq(scratch, dest);
}
else {
mov(ImmShiftedTag(type), dest);
xorq(src, dest);
}
}
void unboxNonDouble(
const Address& src,
Register dest, JSValueType type) {
unboxNonDouble(Operand(src), dest, type);
}
void unboxNonDouble(
const BaseIndex& src,
Register dest, JSValueType type) {
unboxNonDouble(Operand(src), dest, type);
}
void unboxString(
const ValueOperand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}
void unboxString(
const Operand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}
void unboxString(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}
void unboxSymbol(
const ValueOperand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}
void unboxSymbol(
const Operand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}
void unboxBigInt(
const ValueOperand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}
void unboxBigInt(
const Operand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}
void unboxBigInt(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}
void unboxObject(
const ValueOperand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}
void unboxObject(
const Operand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}
void unboxObject(
const Address& src,
Register dest) {
unboxNonDouble(Operand(src), dest, JSVAL_TYPE_OBJECT);
}
void unboxObject(
const BaseIndex& src,
Register dest) {
unboxNonDouble(Operand(src), dest, JSVAL_TYPE_OBJECT);
}
template <
typename T>
void unboxObjectOrNull(
const T& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
ScratchRegisterScope scratch(asMasm());
mov(ImmWord(~JS::detail::ValueObjectOrNullBit), scratch);
andq(scratch, dest);
}
// This should only be used for GC barrier code, to unbox a GC thing Value.
// It's fine there because we don't depend on the actual Value type (all Cells
// are treated the same way). In almost all other cases this would be
// Spectre-unsafe - use unboxNonDouble and friends instead.
void unboxGCThingForGCBarrier(
const Address& src,
Register dest) {
movq(ImmWord(JS::detail::ValueGCThingPayloadMask), dest);
andq(Operand(src), dest);
}
void unboxGCThingForGCBarrier(
const ValueOperand& src,
Register dest) {
MOZ_ASSERT(src.valueReg() != dest);
movq(ImmWord(JS::detail::ValueGCThingPayloadMask), dest);
andq(src.valueReg(), dest);
}
void unboxWasmAnyRefGCThingForGCBarrier(
const Address& src,
Register dest) {
movq(ImmWord(wasm::AnyRef::GCThingMask), dest);
andq(Operand(src), dest);
}
// Like unboxGCThingForGCBarrier, but loads the GC thing's chunk base.
void getGCThingValueChunk(
const Address& src,
Register dest) {
movq(ImmWord(JS::detail::ValueGCThingPayloadChunkMask), dest);
andq(Operand(src), dest);
}
void getGCThingValueChunk(
const ValueOperand& src,
Register dest) {
MOZ_ASSERT(src.valueReg() != dest);
movq(ImmWord(JS::detail::ValueGCThingPayloadChunkMask), dest);
andq(src.valueReg(), dest);
}
void getWasmAnyRefGCThingChunk(
Register src,
Register dest) {
MOZ_ASSERT(src != dest);
movq(ImmWord(wasm::AnyRef::GCThingChunkMask), dest);
andq(src, dest);
}
inline void fallibleUnboxPtrImpl(
const Operand& src,
Register dest,
JSValueType type, Label* fail);
// 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.
[[nodiscard]]
Register extractObject(
const Address& address,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxObject(address, scratch);
return scratch;
}
[[nodiscard]]
Register extractObject(
const ValueOperand& value,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxObject(value, scratch);
return scratch;
}
[[nodiscard]]
Register extractSymbol(
const ValueOperand& value,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxSymbol(value, scratch);
return scratch;
}
[[nodiscard]]
Register extractInt32(
const ValueOperand& value,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxInt32(value, scratch);
return scratch;
}
[[nodiscard]]
Register extractBoolean(
const ValueOperand& value,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
unboxBoolean(value, scratch);
return scratch;
}
[[nodiscard]]
Register extractTag(
const Address& address,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
loadPtr(address, scratch);
splitTag(scratch, scratch);
return scratch;
}
[[nodiscard]]
Register extractTag(
const ValueOperand& value,
Register scratch) {
MOZ_ASSERT(scratch != ScratchReg);
splitTag(value, scratch);
return scratch;
}
inline void unboxValue(
const ValueOperand& src, AnyRegister dest,
JSValueType type);
void loadConstantDouble(
double d, FloatRegister dest);
void loadConstantFloat32(
float f, FloatRegister dest);
void loadConstantSimd128Int(
const SimdConstant& v, FloatRegister dest);
void loadConstantSimd128Float(
const SimdConstant& v, FloatRegister dest);
void vpaddbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpaddwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpadddSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpaddqSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubqSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmullwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmulldSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpaddsbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpaddusbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpaddswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpadduswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubsbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubusbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpsubuswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminsbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminubSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminuwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminsdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpminudSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxsbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxubSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxuwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxsdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaxudSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpandSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpxorSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vporSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vaddpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vaddpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vsubpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vsubpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vdivpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vdivpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vmulpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vmulpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vandpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vminpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpacksswbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpackuswbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpackssdwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpackusdwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpunpckldqSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vunpcklpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpshufbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vptestSimd128(
const SimdConstant& v, FloatRegister lhs);
void vpmaddwdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpeqbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpgtbSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpeqwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpgtwSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpeqdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpcmpgtdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpeqpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpneqpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpltpsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmplepsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpgepsSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpeqpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpneqpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmpltpdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vcmplepdSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmaddubswSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
void vpmuludqSimd128(
const SimdConstant& v, FloatRegister lhs,
FloatRegister dest);
public:
Condition testInt32Truthy(
bool truthy,
const ValueOperand& operand) {
test32(operand.valueReg(), operand.valueReg());
return truthy ? NonZero : Zero;
}
Condition testStringTruthy(
bool truthy,
const ValueOperand& value);
Condition testBigIntTruthy(
bool truthy,
const ValueOperand& value);
template <
typename T>
inline void loadInt32OrDouble(
const T& src, FloatRegister dest);
template <
typename T>
void loadUnboxedValue(
const T& src, MIRType type, AnyRegister dest) {
if (dest.isFloat()) {
loadInt32OrDouble(src, dest.fpu());
}
else {
unboxNonDouble(Operand(src), dest.gpr(), ValueTypeFromMIRType(type));
}
}
template <
typename T>
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes,
JSValueType type) {
switch (nbytes) {
case 8: {
ScratchRegisterScope scratch(asMasm());
unboxNonDouble(value, scratch, type);
storePtr(scratch, address);
if (type == JSVAL_TYPE_OBJECT) {
// Ideally we would call unboxObjectOrNull, but we need an extra
// scratch register for that. So unbox as object, then clear the
// object-or-null bit.
mov(ImmWord(~JS::detail::ValueObjectOrNullBit), scratch);
andq(scratch, Operand(address));
}
return;
}
case 4:
store32(value.valueReg(), address);
return;
case 1:
store8(value.valueReg(), address);
return;
default:
MOZ_CRASH(
"Bad payload width");
}
}
// Checks whether a double is representable as a 64-bit integer. If so, the
// integer is written to the output register. Otherwise, a bailout is taken to
// the given snapshot. This function overwrites the scratch float register.
void convertDoubleToPtr(FloatRegister src,
Register dest, Label* fail,
bool negativeZeroCheck =
true);
void convertUInt32ToDouble(
Register src, FloatRegister dest) {
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(dest);
vcvtsq2sd(src, dest, dest);
}
void convertUInt32ToFloat32(
Register src, FloatRegister dest) {
// Zero the output register to break dependencies, see convertInt32ToDouble.
zeroDouble(dest);
vcvtsq2ss(src, dest, dest);
}
void truncateFloat32ModUint32(FloatRegister src,
Register dest) {
// vcvttss2sq returns 0x8000000000000000 on failure. It fails if
// 1. The input is non-finite (NaN or ±Infinity).
// 2. The input's exponent is at least 63.
//
// In both cases the input is too large for an int32 and the truncated
// result is zero. So unconditionally zeroing the upper 32-bits gives the
// correct result for all inputs.
vcvttss2sq(src, dest);
movl(dest, dest);
// Zero upper 32-bits.
}
inline void incrementInt32Value(
const Address& addr);
public:
void handleFailureWithHandlerTail(Label* profilerExitTail, Label* bailoutTail,
uint32_t* returnValueCheckOffset);
// Instrumentation for entering and leaving the profiler.
void profilerEnterFrame(
Register framePtr,
Register scratch);
void profilerExitFrame();
};
using MacroAssemblerSpecific = MacroAssemblerX64;
}
// namespace jit
}
// namespace js
#endif /* jit_x64_MacroAssembler_x64_h */