/* -*- 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/. */
#include "jit/mips64/MacroAssembler-mips64.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MathAlgorithms.h"
#include "jit/Bailouts.h"
#include "jit/BaselineFrame.h"
#include "jit/JitFrames.h"
#include "jit/JitRuntime.h"
#include "jit/MacroAssembler.h"
#include "jit/mips64/Simulator-mips64.h"
#include "jit/MoveEmitter.h"
#include "jit/SharedICRegisters.h"
#include "util/Memory.h"
#include "vm/JitActivation.h" // js::jit::JitActivation
#include "vm/JSContext.h"
#include "wasm/WasmStubs.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace jit;
using mozilla::Abs;
static_assert(
sizeof(intptr_t) == 8,
"Not 32-bit clean.");
void MacroAssemblerMIPS64Compat::convertBoolToInt32(
Register src,
Register dest) {
// Note that C++ bool is only 1 byte, so zero extend it to clear the
// higher-order bits.
ma_and(dest, src, Imm32(0xff));
}
void MacroAssemblerMIPS64Compat::convertInt32ToDouble(
Register src,
FloatRegister dest) {
as_mtc1(src, dest);
as_cvtdw(dest, dest);
}
void MacroAssemblerMIPS64Compat::convertInt32ToDouble(
const Address& src,
FloatRegister dest) {
ma_ls(dest, src);
as_cvtdw(dest, dest);
}
void MacroAssemblerMIPS64Compat::convertInt32ToDouble(
const BaseIndex& src,
FloatRegister dest) {
computeScaledAddress(src, ScratchRegister);
convertInt32ToDouble(Address(ScratchRegister, src.offset), dest);
}
void MacroAssemblerMIPS64Compat::convertUInt32ToDouble(
Register src,
FloatRegister dest) {
ma_dext(ScratchRegister, src, Imm32(0), Imm32(32));
asMasm().convertInt64ToDouble(Register64(ScratchRegister), dest);
}
void MacroAssemblerMIPS64Compat::convertUInt64ToDouble(
Register src,
FloatRegister dest) {
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
MOZ_ASSERT(src != ScratchRegister);
MOZ_ASSERT(src != SecondScratchReg);
ma_and(ScratchRegister, src, Imm32(1));
ma_dsrl(SecondScratchReg, src, Imm32(1));
ma_or(ScratchRegister, SecondScratchReg);
as_dmtc1(ScratchRegister, dest);
as_cvtdl(dest, dest);
asMasm().addDouble(dest, dest);
ma_b(&done, ShortJump);
bind(&positive);
as_dmtc1(src, dest);
as_cvtdl(dest, dest);
bind(&done);
}
void MacroAssemblerMIPS64Compat::convertUInt32ToFloat32(
Register src,
FloatRegister dest) {
ma_dext(ScratchRegister, src, Imm32(0), Imm32(32));
asMasm().convertInt64ToFloat32(Register64(ScratchRegister), dest);
}
void MacroAssemblerMIPS64Compat::convertDoubleToFloat32(FloatRegister src,
FloatRegister dest) {
as_cvtsd(dest, src);
}
const int CauseBitPos =
int(Assembler::CauseI);
const int CauseBitCount = 1 +
int(Assembler::CauseV) -
int(Assembler::CauseI);
const int CauseIOrVMask = ((1 <<
int(Assembler::CauseI)) |
(1 <<
int(Assembler::CauseV))) >>
int(Assembler::CauseI);
// Checks whether a double is representable as a 32-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 MacroAssemblerMIPS64Compat::convertDoubleToInt32(FloatRegister src,
Register dest,
Label* fail,
bool negativeZeroCheck) {
if (negativeZeroCheck) {
moveFromDouble(src, dest);
ma_drol(dest, dest, Imm32(1));
ma_b(dest, Imm32(1), fail, Assembler::Equal);
}
// Truncate double to int ; if result is inexact or invalid fail.
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
as_andi(ScratchRegister, ScratchRegister,
CauseIOrVMask);
// masking for Inexact and Invalid flag.
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void MacroAssemblerMIPS64Compat::convertDoubleToPtr(FloatRegister src,
Register dest, Label* fail,
bool negativeZeroCheck) {
if (negativeZeroCheck) {
moveFromDouble(src, dest);
ma_drol(dest, dest, Imm32(1));
ma_b(dest, Imm32(1), fail, Assembler::Equal);
}
as_truncld(ScratchDoubleReg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromDouble(ScratchDoubleReg, dest);
ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
// Checks whether a float32 is representable as a 32-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 MacroAssemblerMIPS64Compat::convertFloat32ToInt32(FloatRegister src,
Register dest,
Label* fail,
bool negativeZeroCheck) {
if (negativeZeroCheck) {
moveFromFloat32(src, dest);
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
}
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void MacroAssemblerMIPS64Compat::convertFloat32ToDouble(FloatRegister src,
FloatRegister dest) {
as_cvtds(dest, src);
}
void MacroAssemblerMIPS64Compat::convertInt32ToFloat32(
Register src,
FloatRegister dest) {
as_mtc1(src, dest);
as_cvtsw(dest, dest);
}
void MacroAssemblerMIPS64Compat::convertInt32ToFloat32(
const Address& src,
FloatRegister dest) {
ma_ls(dest, src);
as_cvtsw(dest, dest);
}
void MacroAssembler::convertIntPtrToDouble(
Register src, FloatRegister dest) {
convertInt64ToDouble(Register64(src), dest);
}
void MacroAssemblerMIPS64Compat::movq(
Register rs,
Register rd) {
ma_move(rd, rs);
}
void MacroAssemblerMIPS64::ma_li(
Register dest, CodeLabel* label) {
BufferOffset bo = m_buffer.nextOffset();
ma_liPatchable(dest, ImmWord(
/* placeholder */ 0));
label->patchAt()->bind(bo.getOffset());
label->setLinkMode(CodeLabel::MoveImmediate);
}
void MacroAssemblerMIPS64::ma_li(
Register dest, ImmWord imm) {
int64_t value = imm.value;
if (-1 == (value >> 15) || 0 == (value >> 15)) {
as_addiu(dest, zero, value);
return;
}
if (0 == (value >> 16)) {
as_ori(dest, zero, value);
return;
}
if (-1 == (value >> 31) || 0 == (value >> 31)) {
as_lui(dest, uint16_t(value >> 16));
}
else if (0 == (value >> 32)) {
as_lui(dest, uint16_t(value >> 16));
as_dinsu(dest, zero, 32, 32);
}
else if (-1 == (value >> 47) || 0 == (value >> 47)) {
as_lui(dest, uint16_t(value >> 32));
if (uint16_t(value >> 16)) {
as_ori(dest, dest, uint16_t(value >> 16));
}
as_dsll(dest, dest, 16);
}
else if (0 == (value >> 48)) {
as_lui(dest, uint16_t(value >> 32));
as_dinsu(dest, zero, 32, 32);
if (uint16_t(value >> 16)) {
as_ori(dest, dest, uint16_t(value >> 16));
}
as_dsll(dest, dest, 16);
}
else {
as_lui(dest, uint16_t(value >> 48));
if (uint16_t(value >> 32)) {
as_ori(dest, dest, uint16_t(value >> 32));
}
if (uint16_t(value >> 16)) {
as_dsll(dest, dest, 16);
as_ori(dest, dest, uint16_t(value >> 16));
as_dsll(dest, dest, 16);
}
else {
as_dsll32(dest, dest, 32);
}
}
if (uint16_t(value)) {
as_ori(dest, dest, uint16_t(value));
}
}
// This method generates lui, dsll and ori instruction block that can be
// modified by UpdateLoad64Value, either during compilation (eg.
// Assembler::bind), or during execution (eg. jit::PatchJump).
void MacroAssemblerMIPS64::ma_liPatchable(
Register dest, ImmPtr imm) {
return ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
}
void MacroAssemblerMIPS64::ma_liPatchable(
Register dest, ImmWord imm,
LiFlags flags) {
if (Li64 == flags) {
m_buffer.ensureSpace(6 *
sizeof(uint32_t));
as_lui(dest, Imm16::Upper(Imm32(imm.value >> 32)).encode());
as_ori(dest, dest, Imm16::Lower(Imm32(imm.value >> 32)).encode());
as_dsll(dest, dest, 16);
as_ori(dest, dest, Imm16::Upper(Imm32(imm.value)).encode());
as_dsll(dest, dest, 16);
as_ori(dest, dest, Imm16::Lower(Imm32(imm.value)).encode());
}
else {
m_buffer.ensureSpace(4 *
sizeof(uint32_t));
as_lui(dest, Imm16::Lower(Imm32(imm.value >> 32)).encode());
as_ori(dest, dest, Imm16::Upper(Imm32(imm.value)).encode());
as_drotr32(dest, dest, 48);
as_ori(dest, dest, Imm16::Lower(Imm32(imm.value)).encode());
}
}
void MacroAssemblerMIPS64::ma_dnegu(
Register rd,
Register rs) {
as_dsubu(rd, zero, rs);
}
// Shifts
void MacroAssemblerMIPS64::ma_dsll(
Register rd,
Register rt, Imm32 shift) {
if (31 < shift.value) {
as_dsll32(rd, rt, shift.value);
}
else {
as_dsll(rd, rt, shift.value);
}
}
void MacroAssemblerMIPS64::ma_dsrl(
Register rd,
Register rt, Imm32 shift) {
if (31 < shift.value) {
as_dsrl32(rd, rt, shift.value);
}
else {
as_dsrl(rd, rt, shift.value);
}
}
void MacroAssemblerMIPS64::ma_dsra(
Register rd,
Register rt, Imm32 shift) {
if (31 < shift.value) {
as_dsra32(rd, rt, shift.value);
}
else {
as_dsra(rd, rt, shift.value);
}
}
void MacroAssemblerMIPS64::ma_dror(
Register rd,
Register rt, Imm32 shift) {
if (31 < shift.value) {
as_drotr32(rd, rt, shift.value);
}
else {
as_drotr(rd, rt, shift.value);
}
}
void MacroAssemblerMIPS64::ma_drol(
Register rd,
Register rt, Imm32 shift) {
uint32_t s = 64 - shift.value;
if (31 < s) {
as_drotr32(rd, rt, s);
}
else {
as_drotr(rd, rt, s);
}
}
void MacroAssemblerMIPS64::ma_dsll(
Register rd,
Register rt,
Register shift) {
as_dsllv(rd, rt, shift);
}
void MacroAssemblerMIPS64::ma_dsrl(
Register rd,
Register rt,
Register shift) {
as_dsrlv(rd, rt, shift);
}
void MacroAssemblerMIPS64::ma_dsra(
Register rd,
Register rt,
Register shift) {
as_dsrav(rd, rt, shift);
}
void MacroAssemblerMIPS64::ma_dror(
Register rd,
Register rt,
Register shift) {
as_drotrv(rd, rt, shift);
}
void MacroAssemblerMIPS64::ma_drol(
Register rd,
Register rt,
Register shift) {
as_dsubu(ScratchRegister, zero, shift);
as_drotrv(rd, rt, ScratchRegister);
}
void MacroAssemblerMIPS64::ma_dins(
Register rt,
Register rs, Imm32 pos,
Imm32 size) {
if (pos.value >= 0 && pos.value < 32) {
if (pos.value + size.value > 32) {
as_dinsm(rt, rs, pos.value, size.value);
}
else {
as_dins(rt, rs, pos.value, size.value);
}
}
else {
as_dinsu(rt, rs, pos.value, size.value);
}
}
void MacroAssemblerMIPS64::ma_dext(
Register rt,
Register rs, Imm32 pos,
Imm32 size) {
if (pos.value >= 0 && pos.value < 32) {
if (size.value > 32) {
as_dextm(rt, rs, pos.value, size.value);
}
else {
as_dext(rt, rs, pos.value, size.value);
}
}
else {
as_dextu(rt, rs, pos.value, size.value);
}
}
void MacroAssemblerMIPS64::ma_dsbh(
Register rd,
Register rt) {
as_dsbh(rd, rt);
}
void MacroAssemblerMIPS64::ma_dshd(
Register rd,
Register rt) {
as_dshd(rd, rt);
}
void MacroAssemblerMIPS64::ma_dctz(
Register rd,
Register rs) {
ma_dnegu(ScratchRegister, rs);
as_and(rd, ScratchRegister, rs);
as_dclz(rd, rd);
ma_dnegu(SecondScratchReg, rd);
ma_daddu(SecondScratchReg, Imm32(0x3f));
#ifdef MIPS64
as_selnez(SecondScratchReg, SecondScratchReg, ScratchRegister);
as_seleqz(rd, rd, ScratchRegister);
as_or(rd, rd, SecondScratchReg);
#else
as_movn(rd, SecondScratchReg, ScratchRegister);
#endif
}
// Arithmetic-based ops.
// Add.
void MacroAssemblerMIPS64::ma_daddu(
Register rd,
Register rs, Imm32 imm) {
if (Imm16::IsInSignedRange(imm.value)) {
as_daddiu(rd, rs, imm.value);
}
else {
ma_li(ScratchRegister, imm);
as_daddu(rd, rs, ScratchRegister);
}
}
void MacroAssemblerMIPS64::ma_daddu(
Register rd,
Register rs) {
as_daddu(rd, rd, rs);
}
void MacroAssemblerMIPS64::ma_daddu(
Register rd, Imm32 imm) {
ma_daddu(rd, rd, imm);
}
void MacroAssemblerMIPS64::ma_add32TestOverflow(
Register rd,
Register rs,
Register rt, Label* overflow) {
as_daddu(SecondScratchReg, rs, rt);
as_addu(rd, rs, rt);
ma_b(rd, SecondScratchReg, overflow, Assembler::NotEqual);
}
void MacroAssemblerMIPS64::ma_add32TestOverflow(
Register rd,
Register rs,
Imm32 imm, Label* overflow) {
// Check for signed range because of as_daddiu
if (Imm16::IsInSignedRange(imm.value)) {
as_daddiu(SecondScratchReg, rs, imm.value);
as_addiu(rd, rs, imm.value);
ma_b(rd, SecondScratchReg, overflow, Assembler::NotEqual);
}
else {
ma_li(ScratchRegister, imm);
ma_add32TestOverflow(rd, rs, ScratchRegister, overflow);
}
}
void MacroAssemblerMIPS64::ma_addPtrTestOverflow(
Register rd,
Register rs,
Register rt, Label* overflow) {
SecondScratchRegisterScope scratch2(asMasm());
MOZ_ASSERT(rd != rt);
MOZ_ASSERT(rd != scratch2);
if (rs == rt) {
as_daddu(rd, rs, rs);
as_xor(scratch2, rs, rd);
}
else {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(rs != scratch2);
MOZ_ASSERT(rt != scratch2);
// If the sign of rs and rt are different, no overflow
as_xor(scratch2, rs, rt);
as_nor(scratch2, scratch2, zero);
as_daddu(rd, rs, rt);
as_xor(scratch, rd, rt);
as_and(scratch, scratch, scratch2);
}
ma_b(scratch2, zero, overflow, Assembler::LessThan);
}
void MacroAssemblerMIPS64::ma_addPtrTestOverflow(
Register rd,
Register rs,
Imm32 imm, Label* overflow) {
ma_li(ScratchRegister, imm);
ma_addPtrTestOverflow(rd, rs, ScratchRegister, overflow);
}
void MacroAssemblerMIPS64::ma_addPtrTestOverflow(
Register rd,
Register rs,
ImmWord imm, Label* overflow) {
ScratchRegisterScope scratch(asMasm());
ma_li(scratch, imm);
ma_addPtrTestOverflow(rd, rs, scratch, overflow);
}
void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond,
Register rd,
Register rs,
Register rt,
Label* overflow) {
SecondScratchRegisterScope scratch2(asMasm());
as_daddu(rd, rs, rt);
as_sltu(scratch2, rd, rt);
ma_b(scratch2, scratch2, overflow,
cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}
void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond,
Register rd,
Register rs, Imm32 imm,
Label* overflow) {
// Check for signed range because of as_daddiu
if (Imm16::IsInSignedRange(imm.value)) {
SecondScratchRegisterScope scratch2(asMasm());
as_daddiu(rd, rs, imm.value);
as_sltiu(scratch2, rd, imm.value);
ma_b(scratch2, scratch2, overflow,
cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}
else {
ma_li(ScratchRegister, imm);
ma_addPtrTestCarry(cond, rd, rs, ScratchRegister, overflow);
}
}
void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond,
Register rd,
Register rs, ImmWord imm,
Label* overflow) {
// Check for signed range because of as_daddiu
if (Imm16::IsInSignedRange(imm.value)) {
SecondScratchRegisterScope scratch2(asMasm());
as_daddiu(rd, rs, imm.value);
as_sltiu(scratch2, rd, imm.value);
ma_b(scratch2, scratch2, overflow,
cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}
else {
ScratchRegisterScope scratch(asMasm());
ma_li(scratch, imm);
ma_addPtrTestCarry(cond, rd, rs, scratch, overflow);
}
}
// Subtract.
void MacroAssemblerMIPS64::ma_dsubu(
Register rd,
Register rs, Imm32 imm) {
if (Imm16::IsInSignedRange(-imm.value)) {
as_daddiu(rd, rs, -imm.value);
}
else {
ma_li(ScratchRegister, imm);
as_dsubu(rd, rs, ScratchRegister);
}
}
void MacroAssemblerMIPS64::ma_dsubu(
Register rd,
Register rs) {
as_dsubu(rd, rd, rs);
}
void MacroAssemblerMIPS64::ma_dsubu(
Register rd, Imm32 imm) {
ma_dsubu(rd, rd, imm);
}
void MacroAssemblerMIPS64::ma_sub32TestOverflow(
Register rd,
Register rs,
Register rt, Label* overflow) {
as_dsubu(SecondScratchReg, rs, rt);
as_subu(rd, rs, rt);
ma_b(rd, SecondScratchReg, overflow, Assembler::NotEqual);
}
void MacroAssemblerMIPS64::ma_subPtrTestOverflow(
Register rd,
Register rs,
Register rt, Label* overflow) {
SecondScratchRegisterScope scratch2(asMasm());
MOZ_ASSERT_IF(rs == rd, rs != rt);
MOZ_ASSERT(rd != rt);
MOZ_ASSERT(rs != scratch2);
MOZ_ASSERT(rt != scratch2);
MOZ_ASSERT(rd != scratch2);
Register rs_copy = rs;
if (rs == rd) {
ma_move(scratch2, rs);
rs_copy = scratch2;
}
{
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(rd != scratch);
as_dsubu(rd, rs, rt);
// If the sign of rs and rt are the same, no overflow
as_xor(scratch, rs_copy, rt);
// Check if the sign of rd and rs are the same
as_xor(scratch2, rd, rs_copy);
as_and(scratch2, scratch2, scratch);
}
ma_b(scratch2, zero, overflow, Assembler::LessThan);
}
void MacroAssemblerMIPS64::ma_subPtrTestOverflow(
Register rd,
Register rs,
Imm32 imm, Label* overflow) {
ma_li(ScratchRegister, imm);
ma_subPtrTestOverflow(rd, rs, ScratchRegister, overflow);
}
void MacroAssemblerMIPS64::ma_dmult(
Register rs, Imm32 imm) {
ma_li(ScratchRegister, imm);
#ifdef MIPSR6
as_dmul(rs, ScratchRegister, SecondScratchReg);
as_dmuh(rs, ScratchRegister, rs);
ma_move(rs, SecondScratchReg);
#else
as_dmult(rs, ScratchRegister);
#endif
}
void MacroAssemblerMIPS64::ma_mulPtrTestOverflow(
Register rd,
Register rs,
Register rt, Label* overflow) {
#ifdef MIPSR6
if (rd == rs) {
ma_move(SecondScratchReg, rs);
rs = SecondScratchReg;
}
as_dmul(rd, rs, rt);
as_dmuh(SecondScratchReg, rs, rt);
#else
as_dmult(rs, rt);
as_mflo(rd);
as_mfhi(SecondScratchReg);
#endif
as_dsra32(ScratchRegister, rd, 63);
ma_b(ScratchRegister, SecondScratchReg, overflow, Assembler::NotEqual);
}
// Memory.
FaultingCodeOffset MacroAssemblerMIPS64::ma_load(
Register dest, Address address,
LoadStoreSize size,
LoadStoreExtension extension) {
int16_t encodedOffset;
Register base;
FaultingCodeOffset fco;
if (isLoongson() && ZeroExtend != extension &&
!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
base = address.base;
fco = FaultingCodeOffset(currentOffset());
switch (size) {
case SizeByte:
as_gslbx(dest, base, ScratchRegister, 0);
break;
case SizeHalfWord:
as_gslhx(dest, base, ScratchRegister, 0);
break;
case SizeWord:
as_gslwx(dest, base, ScratchRegister, 0);
break;
case SizeDouble:
as_gsldx(dest, base, ScratchRegister, 0);
break;
default:
MOZ_CRASH(
"Invalid argument for ma_load");
}
return fco;
}
if (!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
as_daddu(ScratchRegister, address.base, ScratchRegister);
base = ScratchRegister;
encodedOffset = Imm16(0).encode();
}
else {
encodedOffset = Imm16(address.offset).encode();
base = address.base;
}
fco = FaultingCodeOffset(currentOffset());
switch (size) {
case SizeByte:
if (ZeroExtend == extension) {
as_lbu(dest, base, encodedOffset);
}
else {
as_lb(dest, base, encodedOffset);
}
break;
case SizeHalfWord:
if (ZeroExtend == extension) {
as_lhu(dest, base, encodedOffset);
}
else {
as_lh(dest, base, encodedOffset);
}
break;
case SizeWord:
if (ZeroExtend == extension) {
as_lwu(dest, base, encodedOffset);
}
else {
as_lw(dest, base, encodedOffset);
}
break;
case SizeDouble:
as_ld(dest, base, encodedOffset);
break;
default:
MOZ_CRASH(
"Invalid argument for ma_load");
}
return fco;
}
FaultingCodeOffset MacroAssemblerMIPS64::ma_store(
Register data, Address address, LoadStoreSize size,
LoadStoreExtension extension) {
int16_t encodedOffset;
Register base;
FaultingCodeOffset fco;
if (isLoongson() && !Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
base = address.base;
fco = FaultingCodeOffset(currentOffset());
switch (size) {
case SizeByte:
as_gssbx(data, base, ScratchRegister, 0);
break;
case SizeHalfWord:
as_gsshx(data, base, ScratchRegister, 0);
break;
case SizeWord:
as_gsswx(data, base, ScratchRegister, 0);
break;
case SizeDouble:
as_gssdx(data, base, ScratchRegister, 0);
break;
default:
MOZ_CRASH(
"Invalid argument for ma_store");
}
return fco;
}
if (!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
as_daddu(ScratchRegister, address.base, ScratchRegister);
base = ScratchRegister;
encodedOffset = Imm16(0).encode();
}
else {
encodedOffset = Imm16(address.offset).encode();
base = address.base;
}
fco = FaultingCodeOffset(currentOffset());
switch (size) {
case SizeByte:
as_sb(data, base, encodedOffset);
break;
case SizeHalfWord:
as_sh(data, base, encodedOffset);
break;
case SizeWord:
as_sw(data, base, encodedOffset);
break;
case SizeDouble:
as_sd(data, base, encodedOffset);
break;
default:
MOZ_CRASH(
"Invalid argument for ma_store");
}
return fco;
}
void MacroAssemblerMIPS64Compat::computeScaledAddress(
const BaseIndex& address,
Register dest) {
int32_t shift = Imm32::ShiftOf(address.scale).value;
if (shift) {
ma_dsll(ScratchRegister, address.index, Imm32(shift));
as_daddu(dest, address.base, ScratchRegister);
}
else {
as_daddu(dest, address.base, address.index);
}
}
void MacroAssemblerMIPS64Compat::computeEffectiveAddress(
const BaseIndex& address,
Register dest) {
computeScaledAddress(address, dest);
if (address.offset) {
asMasm().addPtr(Imm32(address.offset), dest);
}
}
// Shortcut for when we know we're transferring 32 bits of data.
void MacroAssemblerMIPS64::ma_pop(
Register r) {
as_ld(r, StackPointer, 0);
as_daddiu(StackPointer, StackPointer,
sizeof(intptr_t));
}
void MacroAssemblerMIPS64::ma_push(
Register r) {
if (r == sp) {
// Pushing sp requires one more instruction.
ma_move(ScratchRegister, sp);
r = ScratchRegister;
}
as_daddiu(StackPointer, StackPointer, (int32_t)-
sizeof(intptr_t));
as_sd(r, StackPointer, 0);
}
// Branches when done from within mips-specific code.
void MacroAssemblerMIPS64::ma_b(
Register lhs, ImmWord imm, Label* label,
Condition c, JumpKind jumpKind) {
if (imm.value <= INT32_MAX) {
ma_b(lhs, Imm32(uint32_t(imm.value)), label, c, jumpKind);
}
else {
MOZ_ASSERT(lhs != ScratchRegister);
ma_li(ScratchRegister, imm);
ma_b(lhs, ScratchRegister, label, c, jumpKind);
}
}
void MacroAssemblerMIPS64::ma_b(
Register lhs, Address addr, Label* label,
Condition c, JumpKind jumpKind) {
MOZ_ASSERT(lhs != ScratchRegister);
ma_load(ScratchRegister, addr, SizeDouble);
ma_b(lhs, ScratchRegister, label, c, jumpKind);
}
void MacroAssemblerMIPS64::ma_b(Address addr, Imm32 imm, Label* label,
Condition c, JumpKind jumpKind) {
ma_load(SecondScratchReg, addr, SizeDouble);
ma_b(SecondScratchReg, imm, label, c, jumpKind);
}
void MacroAssemblerMIPS64::ma_b(Address addr, ImmGCPtr imm, Label* label,
Condition c, JumpKind jumpKind) {
ma_load(SecondScratchReg, addr, SizeDouble);
ma_b(SecondScratchReg, imm, label, c, jumpKind);
}
void MacroAssemblerMIPS64::ma_bal(Label* label, DelaySlotFill delaySlotFill) {
spew(
"branch .Llabel %p\n", label);
if (label->bound()) {
// Generate the long jump for calls because return address has to be
// the address after the reserved block.
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, ImmWord(LabelBase::INVALID_OFFSET));
as_jalr(ScratchRegister);
if (delaySlotFill == FillDelaySlot) {
as_nop();
}
return;
}
// Second word holds a pointer to the next branch in label's chain.
uint32_t nextInChain =
label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
// Make the whole branch continous in the buffer. The '6'
// instructions are writing at below (contain delay slot).
m_buffer.ensureSpace(6 *
sizeof(uint32_t));
spew(
"bal .Llabel %p\n", label);
BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
// Leave space for long jump.
as_nop();
as_nop();
as_nop();
if (delaySlotFill == FillDelaySlot) {
as_nop();
}
}
void MacroAssemblerMIPS64::branchWithCode(InstImm code, Label* label,
JumpKind jumpKind) {
// simply output the pointer of one label as its id,
// notice that after one label destructor, the pointer will be reused.
spew(
"branch .Llabel %p", label);
MOZ_ASSERT(code.encode() !=
InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
if (label->bound()) {
int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();
if (BOffImm16::IsInRange(offset)) {
jumpKind = ShortJump;
}
if (jumpKind == ShortJump) {
MOZ_ASSERT(BOffImm16::IsInRange(offset));
code.setBOffImm16(BOffImm16(offset));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
writeInst(code.encode());
as_nop();
return;
}
if (code.encode() == inst_beq.encode()) {
// Handle long jump
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, ImmWord(LabelBase::INVALID_OFFSET));
as_jr(ScratchRegister);
as_nop();
return;
}
// Handle long conditional branch, the target offset is based on self,
// point to next instruction of nop at below.
spew(
"invert branch .Llabel %p", label);
InstImm code_r = invertBranch(code, BOffImm16(7 *
sizeof(uint32_t)));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code_r);
#endif
writeInst(code_r.encode());
// No need for a "nop" here because we can clobber scratch.
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, ImmWord(LabelBase::INVALID_OFFSET));
as_jr(ScratchRegister);
as_nop();
return;
}
// Generate open jump and link it to a label.
// Second word holds a pointer to the next branch in label's chain.
uint32_t nextInChain =
label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
if (jumpKind == ShortJump) {
// Make the whole branch continous in the buffer.
m_buffer.ensureSpace(2 *
sizeof(uint32_t));
// Indicate that this is short jump with offset 4.
code.setBOffImm16(BOffImm16(4));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
BufferOffset bo = writeInst(code.encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
return;
}
bool conditional = code.encode() != inst_beq.encode();
// Make the whole branch continous in the buffer. The '7'
// instructions are writing at below (contain conditional nop).
m_buffer.ensureSpace(7 *
sizeof(uint32_t));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
BufferOffset bo = writeInst(code.encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
// Leave space for potential long jump.
as_nop();
as_nop();
as_nop();
as_nop();
if (conditional) {
as_nop();
}
}
void MacroAssemblerMIPS64::ma_cmp_set(
Register rd,
Register rs, ImmWord imm,
Condition c) {
if (imm.value <= INT32_MAX) {
ma_cmp_set(rd, rs, Imm32(uint32_t(imm.value)), c);
}
else {
ma_li(ScratchRegister, imm);
ma_cmp_set(rd, rs, ScratchRegister, c);
}
}
void MacroAssemblerMIPS64::ma_cmp_set(
Register rd,
Register rs, ImmPtr imm,
Condition c) {
ma_cmp_set(rd, rs, ImmWord(uintptr_t(imm.value)), c);
}
void MacroAssemblerMIPS64::ma_cmp_set(
Register rd, Address address,
Register rt,
Condition c) {
SecondScratchRegisterScope scratch2(asMasm());
ma_load(scratch2, address, SizeDouble);
ma_cmp_set(rd, scratch2, rt, c);
}
void MacroAssemblerMIPS64::ma_cmp_set(
Register rd, Address address, ImmWord imm,
Condition c) {
SecondScratchRegisterScope scratch2(asMasm());
ma_load(scratch2, address, SizeDouble);
ma_cmp_set(rd, scratch2, imm, c);
}
void MacroAssemblerMIPS64::ma_cmp_set(
Register rd, Address address, Imm32 imm,
Condition c) {
SecondScratchRegisterScope scratch2(asMasm());
ma_load(scratch2, address, SizeWord, SignExtend);
ma_cmp_set(rd, scratch2, imm, c);
}
// fp instructions
void MacroAssemblerMIPS64::ma_lid(FloatRegister dest,
double value) {
ImmWord imm(mozilla::BitwiseCast<uint64_t>(value));
if (imm.value != 0) {
ma_li(ScratchRegister, imm);
moveToDouble(ScratchRegister, dest);
}
else {
moveToDouble(zero, dest);
}
}
void MacroAssemblerMIPS64::ma_mv(FloatRegister src, ValueOperand dest) {
as_dmfc1(dest.valueReg(), src);
}
void MacroAssemblerMIPS64::ma_mv(ValueOperand src, FloatRegister dest) {
as_dmtc1(src.valueReg(), dest);
}
FaultingCodeOffset MacroAssemblerMIPS64::ma_ls(FloatRegister ft,
Address address) {
FaultingCodeOffset fco;
if (Imm16::IsInSignedRange(address.offset)) {
fco = FaultingCodeOffset(currentOffset());
as_lwc1(ft, address.base, address.offset);
}
else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
fco = FaultingCodeOffset(currentOffset());
as_gslsx(ft, address.base, ScratchRegister, 0);
}
else {
as_daddu(ScratchRegister, address.base, ScratchRegister);
fco = FaultingCodeOffset(currentOffset());
as_lwc1(ft, ScratchRegister, 0);
}
}
return fco;
}
FaultingCodeOffset MacroAssemblerMIPS64::ma_ld(FloatRegister ft,
Address address) {
FaultingCodeOffset fco;
if (Imm16::IsInSignedRange(address.offset)) {
fco = FaultingCodeOffset(currentOffset());
as_ldc1(ft, address.base, address.offset);
}
else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
fco = FaultingCodeOffset(currentOffset());
as_gsldx(ft, address.base, ScratchRegister, 0);
}
else {
as_daddu(ScratchRegister, address.base, ScratchRegister);
fco = FaultingCodeOffset(currentOffset());
as_ldc1(ft, ScratchRegister, 0);
}
}
return fco;
}
FaultingCodeOffset MacroAssemblerMIPS64::ma_sd(FloatRegister ft,
Address address) {
FaultingCodeOffset fco;
if (Imm16::IsInSignedRange(address.offset)) {
fco = FaultingCodeOffset(currentOffset());
as_sdc1(ft, address.base, address.offset);
}
else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
fco = FaultingCodeOffset(currentOffset());
as_gssdx(ft, address.base, ScratchRegister, 0);
}
else {
as_daddu(ScratchRegister, address.base, ScratchRegister);
fco = FaultingCodeOffset(currentOffset());
as_sdc1(ft, ScratchRegister, 0);
}
}
return fco;
}
FaultingCodeOffset MacroAssemblerMIPS64::ma_ss(FloatRegister ft,
Address address) {
FaultingCodeOffset fco;
if (Imm16::IsInSignedRange(address.offset)) {
fco = FaultingCodeOffset(currentOffset());
as_swc1(ft, address.base, address.offset);
}
else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
fco = FaultingCodeOffset(currentOffset());
as_gsssx(ft, address.base, ScratchRegister, 0);
}
else {
as_daddu(ScratchRegister, address.base, ScratchRegister);
fco = FaultingCodeOffset(currentOffset());
as_swc1(ft, ScratchRegister, 0);
}
}
return fco;
}
void MacroAssemblerMIPS64::ma_pop(FloatRegister f) {
as_ldc1(f, StackPointer, 0);
as_daddiu(StackPointer, StackPointer,
sizeof(
double));
}
void MacroAssemblerMIPS64::ma_push(FloatRegister f) {
as_daddiu(StackPointer, StackPointer, (int32_t)-
sizeof(
double));
as_sdc1(f, StackPointer, 0);
}
bool MacroAssemblerMIPS64Compat::buildOOLFakeExitFrame(
void* fakeReturnAddr) {
asMasm().PushFrameDescriptor(FrameType::IonJS);
// descriptor_
asMasm().Push(ImmPtr(fakeReturnAddr));
asMasm().Push(FramePointer);
return true;
}
void MacroAssemblerMIPS64Compat::move32(Imm32 imm,
Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPS64Compat::move32(
Register src,
Register dest) {
ma_sll(dest, src, Imm32(0));
}
void MacroAssemblerMIPS64Compat::movePtr(
Register src,
Register dest) {
ma_move(dest, src);
}
void MacroAssemblerMIPS64Compat::movePtr(ImmWord imm,
Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPS64Compat::movePtr(ImmGCPtr imm,
Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPS64Compat::movePtr(ImmPtr imm,
Register dest) {
movePtr(ImmWord(uintptr_t(imm.value)), dest);
}
void MacroAssemblerMIPS64Compat::movePtr(wasm::SymbolicAddress imm,
Register dest) {
append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm));
ma_liPatchable(dest, ImmWord(-1));
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load8ZeroExtend(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeByte, ZeroExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load8ZeroExtend(
const BaseIndex& src,
Register dest) {
return ma_load(dest, src, SizeByte, ZeroExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load8SignExtend(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeByte, SignExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load8SignExtend(
const BaseIndex& src,
Register dest) {
return ma_load(dest, src, SizeByte, SignExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load16ZeroExtend(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeHalfWord, ZeroExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load16ZeroExtend(
const BaseIndex& src,
Register dest) {
return ma_load(dest, src, SizeHalfWord, ZeroExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load16SignExtend(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeHalfWord, SignExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load16SignExtend(
const BaseIndex& src,
Register dest) {
return ma_load(dest, src, SizeHalfWord, SignExtend);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load32(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeWord);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::load32(
const BaseIndex& address,
Register dest) {
return ma_load(dest, address, SizeWord);
}
void MacroAssemblerMIPS64Compat::load32(AbsoluteAddress address,
Register dest) {
movePtr(ImmPtr(address.addr), ScratchRegister);
load32(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPS64Compat::load32(wasm::SymbolicAddress address,
Register dest) {
movePtr(address, ScratchRegister);
load32(Address(ScratchRegister, 0), dest);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::loadPtr(
const Address& address,
Register dest) {
return ma_load(dest, address, SizeDouble);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::loadPtr(
const BaseIndex& src,
Register dest) {
return ma_load(dest, src, SizeDouble);
}
void MacroAssemblerMIPS64Compat::loadPtr(AbsoluteAddress address,
Register dest) {
movePtr(ImmPtr(address.addr), ScratchRegister);
loadPtr(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPS64Compat::loadPtr(wasm::SymbolicAddress address,
Register dest) {
movePtr(address, ScratchRegister);
loadPtr(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPS64Compat::loadPrivate(
const Address& address,
Register dest) {
loadPtr(address, dest);
}
void MacroAssemblerMIPS64Compat::loadUnalignedDouble(
const wasm::MemoryAccessDesc& access,
const BaseIndex& src,
Register temp,
FloatRegister dest) {
computeScaledAddress(src, SecondScratchReg);
BufferOffset load;
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + 7)) {
load = as_ldl(temp, SecondScratchReg, src.offset + 7);
as_ldr(temp, SecondScratchReg, src.offset);
}
else {
ma_li(ScratchRegister, Imm32(src.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
load = as_ldl(temp, ScratchRegister, 7);
as_ldr(temp, ScratchRegister, 0);
}
append(access, wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
FaultingCodeOffset(load.getOffset()));
moveToDouble(temp, dest);
}
void MacroAssemblerMIPS64Compat::loadUnalignedFloat32(
const wasm::MemoryAccessDesc& access,
const BaseIndex& src,
Register temp,
FloatRegister dest) {
computeScaledAddress(src, SecondScratchReg);
BufferOffset load;
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + 3)) {
load = as_lwl(temp, SecondScratchReg, src.offset + 3);
as_lwr(temp, SecondScratchReg, src.offset);
}
else {
ma_li(ScratchRegister, Imm32(src.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
load = as_lwl(temp, ScratchRegister, 3);
as_lwr(temp, ScratchRegister, 0);
}
append(access, wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
FaultingCodeOffset(load.getOffset()));
moveToFloat32(temp, dest);
}
void MacroAssemblerMIPS64Compat::store8(Imm32 imm,
const Address& address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeByte);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store8(
Register src,
const Address& address) {
return ma_store(src, address, SizeByte);
}
void MacroAssemblerMIPS64Compat::store8(Imm32 imm,
const BaseIndex& dest) {
ma_store(imm, dest, SizeByte);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store8(
Register src,
const BaseIndex& dest) {
return ma_store(src, dest, SizeByte);
}
void MacroAssemblerMIPS64Compat::store16(Imm32 imm,
const Address& address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeHalfWord);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store16(
Register src,
const Address& address) {
return ma_store(src, address, SizeHalfWord);
}
void MacroAssemblerMIPS64Compat::store16(Imm32 imm,
const BaseIndex& dest) {
ma_store(imm, dest, SizeHalfWord);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store16(
Register src,
const BaseIndex& address) {
return ma_store(src, address, SizeHalfWord);
}
void MacroAssemblerMIPS64Compat::store32(
Register src,
AbsoluteAddress address) {
movePtr(ImmPtr(address.addr), ScratchRegister);
store32(src, Address(ScratchRegister, 0));
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store32(
Register src,
const Address& address) {
return ma_store(src, address, SizeWord);
}
void MacroAssemblerMIPS64Compat::store32(Imm32 src,
const Address& address) {
move32(src, SecondScratchReg);
ma_store(SecondScratchReg, address, SizeWord);
}
void MacroAssemblerMIPS64Compat::store32(Imm32 imm,
const BaseIndex& dest) {
ma_store(imm, dest, SizeWord);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::store32(
Register src,
const BaseIndex& dest) {
return ma_store(src, dest, SizeWord);
}
template <
typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmWord imm, T address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeDouble);
}
template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmWord imm,
Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
ImmWord imm, BaseIndex address);
template <
typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmPtr imm, T address) {
storePtr(ImmWord(uintptr_t(imm.value)), address);
}
template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmPtr imm,
Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
ImmPtr imm, BaseIndex address);
template <
typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmGCPtr imm, T address) {
movePtr(imm, SecondScratchReg);
storePtr(SecondScratchReg, address);
}
template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmGCPtr imm,
Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
ImmGCPtr imm, BaseIndex address);
FaultingCodeOffset MacroAssemblerMIPS64Compat::storePtr(
Register src,
const Address& address) {
return ma_store(src, address, SizeDouble);
}
FaultingCodeOffset MacroAssemblerMIPS64Compat::storePtr(
Register src,
const BaseIndex& address) {
return ma_store(src, address, SizeDouble);
}
void MacroAssemblerMIPS64Compat::storePtr(
Register src, AbsoluteAddress dest) {
movePtr(ImmPtr(dest.addr), ScratchRegister);
storePtr(src, Address(ScratchRegister, 0));
}
void MacroAssemblerMIPS64Compat::storeUnalignedFloat32(
const wasm::MemoryAccessDesc& access, FloatRegister src,
Register temp,
const BaseIndex& dest) {
computeScaledAddress(dest, SecondScratchReg);
moveFromFloat32(src, temp);
BufferOffset store;
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + 3)) {
store = as_swl(temp, SecondScratchReg, dest.offset + 3);
as_swr(temp, SecondScratchReg, dest.offset);
}
else {
ma_li(ScratchRegister, Imm32(dest.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
store = as_swl(temp, ScratchRegister, 3);
as_swr(temp, ScratchRegister, 0);
}
append(access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
FaultingCodeOffset(store.getOffset()));
}
void MacroAssemblerMIPS64Compat::storeUnalignedDouble(
const wasm::MemoryAccessDesc& access, FloatRegister src,
Register temp,
const BaseIndex& dest) {
computeScaledAddress(dest, SecondScratchReg);
moveFromDouble(src, temp);
BufferOffset store;
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + 7)) {
store = as_sdl(temp, SecondScratchReg, dest.offset + 7);
as_sdr(temp, SecondScratchReg, dest.offset);
}
else {
ma_li(ScratchRegister, Imm32(dest.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
store = as_sdl(temp, ScratchRegister, 7);
as_sdr(temp, ScratchRegister, 0);
}
append(access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
FaultingCodeOffset(store.getOffset()));
}
void MacroAssembler::clampDoubleToUint8(FloatRegister input,
Register output) {
as_roundwd(ScratchDoubleReg, input);
ma_li(ScratchRegister, Imm32(255));
as_mfc1(output, ScratchDoubleReg);
#ifdef MIPSR6
as_slti(SecondScratchReg, output, 0);
as_seleqz(output, output, SecondScratchReg);
as_sltiu(SecondScratchReg, output, 255);
as_selnez(output, output, SecondScratchReg);
as_seleqz(ScratchRegister, ScratchRegister, SecondScratchReg);
as_or(output, output, ScratchRegister);
#else
zeroDouble(ScratchDoubleReg);
as_sltiu(SecondScratchReg, output, 255);
as_colt(DoubleFloat, ScratchDoubleReg, input);
// if res > 255; res = 255;
as_movz(output, ScratchRegister, SecondScratchReg);
// if !(input > 0); res = 0;
as_movf(output, zero);
#endif
}
void MacroAssemblerMIPS64Compat::testNullSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
splitTag(value, SecondScratchReg);
ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond);
}
void MacroAssemblerMIPS64Compat::testObjectSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
splitTag(value, SecondScratchReg);
ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond);
}
void MacroAssemblerMIPS64Compat::testUndefinedSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
splitTag(value, SecondScratchReg);
ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond);
}
void MacroAssemblerMIPS64Compat::unboxInt32(
const ValueOperand& operand,
Register dest) {
ma_sll(dest, operand.valueReg(), Imm32(0));
}
void MacroAssemblerMIPS64Compat::unboxInt32(
Register src,
Register dest) {
ma_sll(dest, src, Imm32(0));
}
void MacroAssemblerMIPS64Compat::unboxInt32(
const Address& src,
Register dest) {
load32(Address(src.base, src.offset), dest);
}
void MacroAssemblerMIPS64Compat::unboxInt32(
const BaseIndex& src,
Register dest) {
computeScaledAddress(src, SecondScratchReg);
load32(Address(SecondScratchReg, src.offset), dest);
}
void MacroAssemblerMIPS64Compat::unboxBoolean(
const ValueOperand& operand,
Register dest) {
ma_dext(dest, operand.valueReg(), Imm32(0), Imm32(32));
}
void MacroAssemblerMIPS64Compat::unboxBoolean(
Register src,
Register dest) {
ma_dext(dest, src, Imm32(0), Imm32(32));
}
void MacroAssemblerMIPS64Compat::unboxBoolean(
const Address& src,
Register dest) {
ma_load(dest, Address(src.base, src.offset), SizeWord, ZeroExtend);
}
void MacroAssemblerMIPS64Compat::unboxBoolean(
const BaseIndex& src,
Register dest) {
computeScaledAddress(src, SecondScratchReg);
ma_load(dest, Address(SecondScratchReg, src.offset), SizeWord, ZeroExtend);
}
void MacroAssemblerMIPS64Compat::unboxDouble(
const ValueOperand& operand,
FloatRegister dest) {
as_dmtc1(operand.valueReg(), dest);
}
void MacroAssemblerMIPS64Compat::unboxDouble(
const Address& src,
FloatRegister dest) {
ma_ld(dest, Address(src.base, src.offset));
}
void MacroAssemblerMIPS64Compat::unboxDouble(
const BaseIndex& src,
FloatRegister dest) {
SecondScratchRegisterScope scratch(asMasm());
loadPtr(src, scratch);
unboxDouble(ValueOperand(scratch), dest);
}
void MacroAssemblerMIPS64Compat::unboxString(
const ValueOperand& operand,
Register dest) {
unboxNonDouble(operand, dest, JSVAL_TYPE_STRING);
}
void MacroAssemblerMIPS64Compat::unboxString(
Register src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}
void MacroAssemblerMIPS64Compat::unboxString(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}
void MacroAssemblerMIPS64Compat::unboxSymbol(
const ValueOperand& operand,
Register dest) {
unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL);
}
void MacroAssemblerMIPS64Compat::unboxSymbol(
Register src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}
void MacroAssemblerMIPS64Compat::unboxSymbol(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}
void MacroAssemblerMIPS64Compat::unboxBigInt(
const ValueOperand& operand,
Register dest) {
unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT);
}
void MacroAssemblerMIPS64Compat::unboxBigInt(
Register src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}
void MacroAssemblerMIPS64Compat::unboxBigInt(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}
void MacroAssemblerMIPS64Compat::unboxObject(
const ValueOperand& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}
void MacroAssemblerMIPS64Compat::unboxObject(
Register src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}
void MacroAssemblerMIPS64Compat::unboxObject(
const Address& src,
Register dest) {
unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}
void MacroAssemblerMIPS64Compat::unboxValue(
const ValueOperand& src,
AnyRegister dest,
JSValueType type) {
if (dest.isFloat()) {
Label notInt32, end;
asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32);
convertInt32ToDouble(src.valueReg(), dest.fpu());
ma_b(&end, ShortJump);
bind(¬Int32);
unboxDouble(src, dest.fpu());
bind(&end);
}
else {
unboxNonDouble(src, dest.gpr(), type);
}
}
void MacroAssemblerMIPS64Compat::boxDouble(FloatRegister src,
const ValueOperand& dest,
FloatRegister) {
as_dmfc1(dest.valueReg(), src);
}
void MacroAssemblerMIPS64Compat::boxNonDouble(JSValueType type,
Register src,
const ValueOperand& dest) {
MOZ_ASSERT(src != dest.valueReg());
boxValue(type, src, dest.valueReg());
}
void MacroAssemblerMIPS64Compat::loadConstantFloat32(
float f,
FloatRegister dest) {
ma_lis(dest, f);
}
void MacroAssemblerMIPS64Compat::loadInt32OrDouble(
const Address& src,
FloatRegister dest) {
Label notInt32, end;
// If it's an int, convert it to double.
loadPtr(Address(src.base, src.offset), ScratchRegister);
ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32);
loadPtr(Address(src.base, src.offset), SecondScratchReg);
convertInt32ToDouble(SecondScratchReg, dest);
ma_b(&end, ShortJump);
// Not an int, just load as double.
bind(¬Int32);
unboxDouble(src, dest);
bind(&end);
}
void MacroAssemblerMIPS64Compat::loadInt32OrDouble(
const BaseIndex& addr,
FloatRegister dest) {
Label notInt32, end;
// If it's an int, convert it to double.
computeScaledAddress(addr, SecondScratchReg);
// Since we only have one scratch, we need to stomp over it with the tag.
loadPtr(Address(SecondScratchReg, 0), ScratchRegister);
ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32);
computeScaledAddress(addr, SecondScratchReg);
loadPtr(Address(SecondScratchReg, 0), SecondScratchReg);
convertInt32ToDouble(SecondScratchReg, dest);
ma_b(&end, ShortJump);
// Not an int, just load as double.
bind(¬Int32);
// First, recompute the offset that had been stored in the scratch register
// since the scratch register was overwritten loading in the type.
computeScaledAddress(addr, SecondScratchReg);
unboxDouble(Address(SecondScratchReg, 0), dest);
bind(&end);
}
void MacroAssemblerMIPS64Compat::loadConstantDouble(
double dp,
FloatRegister dest) {
ma_lid(dest, dp);
}
Register MacroAssemblerMIPS64Compat::extractObject(
const Address& address,
Register scratch) {
loadPtr(Address(address.base, address.offset), scratch);
ma_dext(scratch, scratch, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
return scratch;
}
Register MacroAssemblerMIPS64Compat::extractTag(
const Address& address,
Register scratch) {
loadPtr(Address(address.base, address.offset), scratch);
ma_dext(scratch, scratch, Imm32(JSVAL_TAG_SHIFT),
Imm32(64 - JSVAL_TAG_SHIFT));
return scratch;
}
Register MacroAssemblerMIPS64Compat::extractTag(
const BaseIndex& address,
Register scratch) {
computeScaledAddress(address, scratch);
return extractTag(Address(scratch, address.offset), scratch);
}
/////////////////////////////////////////////////////////////////
// X86/X64-common/ARM/MIPS interface.
/////////////////////////////////////////////////////////////////
void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val, Operand dst) {
storeValue(val, Address(
Register::FromCode(dst.base()), dst.disp()));
}
void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val,
const BaseIndex& dest) {
computeScaledAddress(dest, SecondScratchReg);
storeValue(val, Address(SecondScratchReg, dest.offset));
}
void MacroAssemblerMIPS64Compat::storeValue(JSValueType type,
Register reg,
BaseIndex dest) {
computeScaledAddress(dest, ScratchRegister);
int32_t offset = dest.offset;
if (!Imm16::IsInSignedRange(offset)) {
ma_li(SecondScratchReg, Imm32(offset));
as_daddu(ScratchRegister, ScratchRegister, SecondScratchReg);
offset = 0;
}
storeValue(type, reg, Address(ScratchRegister, offset));
}
void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val,
const Address& dest) {
storePtr(val.valueReg(), Address(dest.base, dest.offset));
}
void MacroAssemblerMIPS64Compat::storeValue(JSValueType type,
Register reg,
Address dest) {
MOZ_ASSERT(dest.base != SecondScratchReg);
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
store32(reg, dest);
JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
store32(((Imm64(tag)).hi()), Address(dest.base, dest.offset + 4));
}
else {
ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type)));
ma_dsll(SecondScratchReg, SecondScratchReg, Imm32(JSVAL_TAG_SHIFT));
ma_dins(SecondScratchReg, reg, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
storePtr(SecondScratchReg, Address(dest.base, dest.offset));
}
}
void MacroAssemblerMIPS64Compat::storeValue(
const Value& val, Address dest) {
if (val.isGCThing()) {
writeDataRelocation(val);
movWithPatch(ImmWord(val.asRawBits()), SecondScratchReg);
}
else {
ma_li(SecondScratchReg, ImmWord(val.asRawBits()));
}
storePtr(SecondScratchReg, Address(dest.base, dest.offset));
}
void MacroAssemblerMIPS64Compat::storeValue(
const Value& val, BaseIndex dest) {
computeScaledAddress(dest, ScratchRegister);
int32_t offset = dest.offset;
if (!Imm16::IsInSignedRange(offset)) {
ma_li(SecondScratchReg, Imm32(offset));
as_daddu(ScratchRegister, ScratchRegister, SecondScratchReg);
offset = 0;
}
storeValue(val, Address(ScratchRegister, offset));
}
void MacroAssemblerMIPS64Compat::loadValue(
const BaseIndex& addr,
ValueOperand val) {
computeScaledAddress(addr, SecondScratchReg);
loadValue(Address(SecondScratchReg, addr.offset), val);
}
void MacroAssemblerMIPS64Compat::loadValue(Address src, ValueOperand val) {
loadPtr(Address(src.base, src.offset), val.valueReg());
}
void MacroAssemblerMIPS64Compat::tagValue(JSValueType type,
Register payload,
ValueOperand dest) {
MOZ_ASSERT(dest.valueReg() != ScratchRegister);
if (payload != dest.valueReg()) {
ma_move(dest.valueReg(), payload);
}
ma_li(ScratchRegister, ImmTag(JSVAL_TYPE_TO_TAG(type)));
ma_dins(dest.valueReg(), ScratchRegister, Imm32(JSVAL_TAG_SHIFT),
Imm32(64 - JSVAL_TAG_SHIFT));
if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
ma_dins(dest.valueReg(), zero, Imm32(32), Imm32(JSVAL_TAG_SHIFT - 32));
}
}
void MacroAssemblerMIPS64Compat::pushValue(ValueOperand val) {
// Allocate stack slots for Value. One for each.
asMasm().subPtr(Imm32(
sizeof(Value)), StackPointer);
// Store Value
storeValue(val, Address(StackPointer, 0));
}
void MacroAssemblerMIPS64Compat::pushValue(
const Address& addr) {
// Load value before allocate stack, addr.base may be is sp.
loadPtr(Address(addr.base, addr.offset), ScratchRegister);
ma_dsubu(StackPointer, StackPointer, Imm32(
sizeof(Value)));
storePtr(ScratchRegister, Address(StackPointer, 0));
}
void MacroAssemblerMIPS64Compat::popValue(ValueOperand val) {
as_ld(val.valueReg(), StackPointer, 0);
as_daddiu(StackPointer, StackPointer,
sizeof(Value));
}
void MacroAssemblerMIPS64Compat::breakpoint() { as_break(0); }
void MacroAssemblerMIPS64Compat::checkStackAlignment() {
#ifdef DEBUG
Label aligned;
as_andi(ScratchRegister, sp, ABIStackAlignment - 1);
ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump);
as_break(BREAK_STACK_UNALIGNED);
bind(&aligned);
#endif
}
void MacroAssemblerMIPS64Compat::handleFailureWithHandlerTail(
Label* profilerExitTail, Label* bailoutTail,
uint32_t* returnValueCheckOffset) {
// Reserve space for exception information.
int size = (
sizeof(ResumeFromException) + ABIStackAlignment) &
~(ABIStackAlignment - 1);
asMasm().subPtr(Imm32(size), StackPointer);
ma_move(a0, StackPointer);
// Use a0 since it is a first function argument
// Call the handler.
using Fn =
void (*)(ResumeFromException* rfe);
asMasm().setupUnalignedABICall(a1);
asMasm().passABIArg(a0);
asMasm().callWithABI<Fn, HandleException>(
ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
*returnValueCheckOffset = asMasm().currentOffset();
Label entryFrame;
Label catch_;
Label finally;
Label returnBaseline;
Label returnIon;
Label bailout;
Label wasmInterpEntry;
Label wasmCatch;
// Already clobbered a0, so use it...
load32(Address(StackPointer, ResumeFromException::offsetOfKind()), a0);
asMasm().branch32(Assembler::Equal, a0,
Imm32(ExceptionResumeKind::EntryFrame), &entryFrame);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::
Catch),
&catch_);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Finally),
&finally);
asMasm().branch32(Assembler::Equal, a0,
Imm32(ExceptionResumeKind::ForcedReturnBaseline),
&returnBaseline);
asMasm().branch32(Assembler::Equal, a0,
Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Bailout),
&bailout);
asMasm().branch32(Assembler::Equal, a0,
Imm32(ExceptionResumeKind::WasmInterpEntry),
&wasmInterpEntry);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::WasmCatch),
&wasmCatch);
breakpoint();
// Invalid kind.
// No exception handler. Load the error value, restore state and return from
// the entry frame.
bind(&entryFrame);
asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
// We're going to be returning by the ion calling convention
ma_pop(ra);
as_jr(ra);
as_nop();
// If we found a catch handler, this must be a baseline frame. Restore
// state and jump to the catch block.
bind(&catch_);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), a0);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
jump(a0);
// If we found a finally block, this must be a baseline frame. Push three
// values expected by the finally block: the exception, the exception stack,
// and BooleanValue(true).
bind(&finally);
ValueOperand exception = ValueOperand(a1);
loadValue(Address(sp, ResumeFromException::offsetOfException()), exception);
ValueOperand exceptionStack = ValueOperand(a2);
loadValue(Address(sp, ResumeFromException::offsetOfExceptionStack()),
exceptionStack);
loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a0);
loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp);
pushValue(exception);
pushValue(exceptionStack);
pushValue(BooleanValue(
true));
jump(a0);
// Return BaselineFrame->returnValue() to the caller.
// Used in debug mode and for GeneratorReturn.
Label profilingInstrumentation;
bind(&returnBaseline);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
loadValue(Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()),
JSReturnOperand);
jump(&profilingInstrumentation);
// Return the given value to the caller.
bind(&returnIon);
loadValue(Address(StackPointer, ResumeFromException::offsetOfException()),
JSReturnOperand);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
// If profiling is enabled, then update the lastProfilingFrame to refer to
// caller frame before returning. This code is shared by ForcedReturnIon
// and ForcedReturnBaseline.
bind(&profilingInstrumentation);
{
Label skipProfilingInstrumentation;
--> --------------------
--> maximum size reached
--> --------------------