Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/mips-shared/MacroAssembler-mips-shared.h"
#include "mozilla/EndianUtils.h"
#include "jsmath.h"
#include "jit/MacroAssembler.h"
using namespace js;
using namespace jit;
void MacroAssemblerMIPSShared::ma_move(Register rd, Register rs) {
as_or(rd, rs, zero);
}
void MacroAssemblerMIPSShared::ma_li(Register dest, ImmGCPtr ptr) {
writeDataRelocation(ptr);
asMasm().ma_liPatchable(dest, ImmPtr(ptr.value));
}
void MacroAssemblerMIPSShared::ma_li(Register dest, Imm32 imm) {
if (Imm16::IsInSignedRange(imm.value)) {
as_addiu(dest, zero, imm.value);
} else if (Imm16::IsInUnsignedRange(imm.value)) {
as_ori(dest, zero, Imm16::Lower(imm).encode());
} else if (Imm16::Lower(imm).encode() == 0) {
as_lui(dest, Imm16::Upper(imm).encode());
} else {
as_lui(dest, Imm16::Upper(imm).encode());
as_ori(dest, dest, Imm16::Lower(imm).encode());
}
}
// This method generates lui and ori instruction pair that can be modified by
// UpdateLuiOriValue, either during compilation (eg. Assembler::bind), or
// during execution (eg. jit::PatchJump).
void MacroAssemblerMIPSShared::ma_liPatchable(Register dest, Imm32 imm) {
m_buffer.ensureSpace(2 * sizeof(uint32_t));
as_lui(dest, Imm16::Upper(imm).encode());
as_ori(dest, dest, Imm16::Lower(imm).encode());
}
// Shifts
void MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt, Imm32 shift) {
as_sll(rd, rt, shift.value % 32);
}
void MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt, Imm32 shift) {
as_srl(rd, rt, shift.value % 32);
}
void MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt, Imm32 shift) {
as_sra(rd, rt, shift.value % 32);
}
void MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt, Imm32 shift) {
if (hasR2()) {
as_rotr(rd, rt, shift.value % 32);
} else {
ScratchRegisterScope scratch(asMasm());
as_srl(scratch, rt, shift.value % 32);
as_sll(rd, rt, (32 - (shift.value % 32)) % 32);
as_or(rd, rd, scratch);
}
}
void MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt, Imm32 shift) {
if (hasR2()) {
as_rotr(rd, rt, (32 - (shift.value % 32)) % 32);
} else {
ScratchRegisterScope scratch(asMasm());
as_srl(scratch, rt, (32 - (shift.value % 32)) % 32);
as_sll(rd, rt, shift.value % 32);
as_or(rd, rd, scratch);
}
}
void MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt,
Register shift) {
as_sllv(rd, rt, shift);
}
void MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt,
Register shift) {
as_srlv(rd, rt, shift);
}
void MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt,
Register shift) {
as_srav(rd, rt, shift);
}
void MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt,
Register shift) {
if (hasR2()) {
as_rotrv(rd, rt, shift);
} else {
ScratchRegisterScope scratch(asMasm());
ma_negu(scratch, shift);
as_sllv(scratch, rt, scratch);
as_srlv(rd, rt, shift);
as_or(rd, rd, scratch);
}
}
void MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt,
Register shift) {
ScratchRegisterScope scratch(asMasm());
ma_negu(scratch, shift);
if (hasR2()) {
as_rotrv(rd, rt, scratch);
} else {
as_srlv(rd, rt, scratch);
as_sllv(scratch, rt, shift);
as_or(rd, rd, scratch);
}
}
void MacroAssemblerMIPSShared::ma_negu(Register rd, Register rs) {
as_subu(rd, zero, rs);
}
void MacroAssemblerMIPSShared::ma_not(Register rd, Register rs) {
as_nor(rd, rs, zero);
}
// Bit extract/insert
void MacroAssemblerMIPSShared::ma_ext(Register rt, Register rs, uint16_t pos,
uint16_t size) {
MOZ_ASSERT(pos < 32);
MOZ_ASSERT(pos + size < 33);
if (hasR2()) {
as_ext(rt, rs, pos, size);
} else {
int shift_left = 32 - (pos + size);
as_sll(rt, rs, shift_left);
int shift_right = 32 - size;
if (shift_right > 0) {
as_srl(rt, rt, shift_right);
}
}
}
void MacroAssemblerMIPSShared::ma_ins(Register rt, Register rs, uint16_t pos,
uint16_t size) {
MOZ_ASSERT(pos < 32);
MOZ_ASSERT(pos + size <= 32);
MOZ_ASSERT(size != 0);
if (hasR2()) {
as_ins(rt, rs, pos, size);
} else {
ScratchRegisterScope scratch(asMasm());
SecondScratchRegisterScope scratch2(asMasm());
ma_subu(scratch, zero, Imm32(1));
as_srl(scratch, scratch, 32 - size);
as_and(scratch2, rs, scratch);
as_sll(scratch2, scratch2, pos);
as_sll(scratch, scratch, pos);
as_nor(scratch, scratch, zero);
as_and(scratch, rt, scratch);
as_or(rt, scratch2, scratch);
}
}
// Sign extend
void MacroAssemblerMIPSShared::ma_seb(Register rd, Register rt) {
if (hasR2()) {
as_seb(rd, rt);
} else {
as_sll(rd, rt, 24);
as_sra(rd, rd, 24);
}
}
void MacroAssemblerMIPSShared::ma_seh(Register rd, Register rt) {
if (hasR2()) {
as_seh(rd, rt);
} else {
as_sll(rd, rt, 16);
as_sra(rd, rd, 16);
}
}
// And.
void MacroAssemblerMIPSShared::ma_and(Register rd, Register rs) {
as_and(rd, rd, rs);
}
void MacroAssemblerMIPSShared::ma_and(Register rd, Imm32 imm) {
ma_and(rd, rd, imm);
}
void MacroAssemblerMIPSShared::ma_and(Register rd, Register rs, Imm32 imm) {
if (Imm16::IsInUnsignedRange(imm.value)) {
as_andi(rd, rs, imm.value);
} else {
ma_li(ScratchRegister, imm);
as_and(rd, rs, ScratchRegister);
}
}
// Or.
void MacroAssemblerMIPSShared::ma_or(Register rd, Register rs) {
as_or(rd, rd, rs);
}
void MacroAssemblerMIPSShared::ma_or(Register rd, Imm32 imm) {
ma_or(rd, rd, imm);
}
void MacroAssemblerMIPSShared::ma_or(Register rd, Register rs, Imm32 imm) {
if (Imm16::IsInUnsignedRange(imm.value)) {
as_ori(rd, rs, imm.value);
} else {
ma_li(ScratchRegister, imm);
as_or(rd, rs, ScratchRegister);
}
}
// xor
void MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs) {
as_xor(rd, rd, rs);
}
void MacroAssemblerMIPSShared::ma_xor(Register rd, Imm32 imm) {
ma_xor(rd, rd, imm);
}
void MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs, Imm32 imm) {
if (Imm16::IsInUnsignedRange(imm.value)) {
as_xori(rd, rs, imm.value);
} else {
ma_li(ScratchRegister, imm);
as_xor(rd, rs, ScratchRegister);
}
}
// word swap bytes within halfwords
void MacroAssemblerMIPSShared::ma_wsbh(Register rd, Register rt) {
as_wsbh(rd, rt);
}
void MacroAssemblerMIPSShared::ma_ctz(Register rd, Register rs) {
as_addiu(ScratchRegister, rs, -1);
as_xor(rd, ScratchRegister, rs);
as_and(rd, rd, ScratchRegister);
as_clz(rd, rd);
ma_li(ScratchRegister, Imm32(0x20));
as_subu(rd, ScratchRegister, rd);
}
// Arithmetic-based ops.
// Add.
void MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs, Imm32 imm) {
if (Imm16::IsInSignedRange(imm.value)) {
as_addiu(rd, rs, imm.value);
} else {
ma_li(ScratchRegister, imm);
as_addu(rd, rs, ScratchRegister);
}
}
void MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs) {
as_addu(rd, rd, rs);
}
void MacroAssemblerMIPSShared::ma_addu(Register rd, Imm32 imm) {
ma_addu(rd, rd, imm);
}
void MacroAssemblerMIPSShared::ma_add32TestCarry(Condition cond, Register rd,
Register rs, Register rt,
Label* overflow) {
MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear);
MOZ_ASSERT_IF(rd == rs, rt != rd);
as_addu(rd, rs, rt);
as_sltu(SecondScratchReg, rd, rd == rs ? rt : rs);
ma_b(SecondScratchReg, SecondScratchReg, overflow,
cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}
void MacroAssemblerMIPSShared::ma_add32TestCarry(Condition cond, Register rd,
Register rs, Imm32 imm,
Label* overflow) {
ma_li(ScratchRegister, imm);
ma_add32TestCarry(cond, rd, rs, ScratchRegister, overflow);
}
// Subtract.
void MacroAssemblerMIPSShared::ma_subu(Register rd, Register rs, Imm32 imm) {
if (Imm16::IsInSignedRange(-imm.value)) {
as_addiu(rd, rs, -imm.value);
} else {
ma_li(ScratchRegister, imm);
as_subu(rd, rs, ScratchRegister);
}
}
void MacroAssemblerMIPSShared::ma_subu(Register rd, Imm32 imm) {
ma_subu(rd, rd, imm);
}
void MacroAssemblerMIPSShared::ma_subu(Register rd, Register rs) {
as_subu(rd, rd, rs);
}
void MacroAssemblerMIPSShared::ma_sub32TestOverflow(Register rd, Register rs,
Imm32 imm,
Label* overflow) {
if (imm.value != INT32_MIN) {
asMasm().ma_add32TestOverflow(rd, rs, Imm32(-imm.value), overflow);
} else {
ma_li(ScratchRegister, Imm32(imm.value));
asMasm().ma_sub32TestOverflow(rd, rs, ScratchRegister, overflow);
}
}
void MacroAssemblerMIPSShared::ma_mul(Register rd, Register rs, Imm32 imm) {
ma_li(ScratchRegister, imm);
as_mul(rd, rs, ScratchRegister);
}
void MacroAssemblerMIPSShared::ma_mul32TestOverflow(Register rd, Register rs,
Register rt,
Label* overflow) {
#ifdef MIPSR6
if (rd == rs) {
ma_move(SecondScratchReg, rs);
rs = SecondScratchReg;
}
as_mul(rd, rs, rt);
as_muh(SecondScratchReg, rs, rt);
#else
as_mult(rs, rt);
as_mflo(rd);
as_mfhi(SecondScratchReg);
#endif
as_sra(ScratchRegister, rd, 31);
ma_b(ScratchRegister, SecondScratchReg, overflow, Assembler::NotEqual);
}
void MacroAssemblerMIPSShared::ma_mul32TestOverflow(Register rd, Register rs,
Imm32 imm,
Label* overflow) {
ma_li(ScratchRegister, imm);
ma_mul32TestOverflow(rd, rs, ScratchRegister, overflow);
}
void MacroAssemblerMIPSShared::ma_div_branch_overflow(Register rd, Register rs,
Register rt,
Label* overflow) {
#ifdef MIPSR6
if (rd == rs) {
ma_move(SecondScratchReg, rs);
rs = SecondScratchReg;
}
as_mod(ScratchRegister, rs, rt);
#else
as_div(rs, rt);
as_mfhi(ScratchRegister);
#endif
ma_b(ScratchRegister, ScratchRegister, overflow, Assembler::NonZero);
#ifdef MIPSR6
as_div(rd, rs, rt);
#else
as_mflo(rd);
#endif
}
void MacroAssemblerMIPSShared::ma_div_branch_overflow(Register rd, Register rs,
Imm32 imm,
Label* overflow) {
ma_li(ScratchRegister, imm);
ma_div_branch_overflow(rd, rs, ScratchRegister, overflow);
}
void MacroAssemblerMIPSShared::ma_mod_mask(Register src, Register dest,
Register hold, Register remain,
int32_t shift, Label* negZero) {
// MATH:
// We wish to compute x % (1<<y) - 1 for a known constant, y.
// First, let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit
// dividend as a number in base b, namely
// c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
// now, since both addition and multiplication commute with modulus,
// x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
// (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)...
// now, since b == C + 1, b % C == 1, and b^n % C == 1
// this means that the whole thing simplifies to:
// c_0 + c_1 + c_2 ... c_n % C
// each c_n can easily be computed by a shift/bitextract, and the modulus
// can be maintained by simply subtracting by C whenever the number gets
// over C.
int32_t mask = (1 << shift) - 1;
Label head, negative, sumSigned, done;
// hold holds -1 if the value was negative, 1 otherwise.
// remain holds the remaining bits that have not been processed
// SecondScratchReg serves as a temporary location to store extracted bits
// into as well as holding the trial subtraction as a temp value dest is
// the accumulator (and holds the final result)
// move the whole value into the remain.
ma_move(remain, src);
// Zero out the dest.
ma_li(dest, Imm32(0));
// Set the hold appropriately.
ma_b(remain, remain, &negative, Signed, ShortJump);
ma_li(hold, Imm32(1));
ma_b(&head, ShortJump);
bind(&negative);
ma_li(hold, Imm32(-1));
ma_negu(remain, remain);
// Begin the main loop.
bind(&head);
// Extract the bottom bits into SecondScratchReg.
ma_and(SecondScratchReg, remain, Imm32(mask));
// Add those bits to the accumulator.
as_addu(dest, dest, SecondScratchReg);
// Do a trial subtraction
ma_subu(SecondScratchReg, dest, Imm32(mask));
// If (sum - C) > 0, store sum - C back into sum, thus performing a
// modulus.
ma_b(SecondScratchReg, SecondScratchReg, &sumSigned, Signed, ShortJump);
ma_move(dest, SecondScratchReg);
bind(&sumSigned);
// Get rid of the bits that we extracted before.
as_srl(remain, remain, shift);
// If the shift produced zero, finish, otherwise, continue in the loop.
ma_b(remain, remain, &head, NonZero, ShortJump);
// Check the hold to see if we need to negate the result.
ma_b(hold, hold, &done, NotSigned, ShortJump);
// If the hold was non-zero, negate the result to be in line with
// what JS wants
if (negZero != nullptr) {
// Jump out in case of negative zero.
ma_b(hold, hold, negZero, Zero);
ma_negu(dest, dest);
} else {
ma_negu(dest, dest);
}
bind(&done);
}
// Memory.
void MacroAssemblerMIPSShared::ma_load(Register dest, const BaseIndex& src,
LoadStoreSize size,
LoadStoreExtension extension) {
if (isLoongson() && ZeroExtend != extension &&
Imm8::IsInSignedRange(src.offset)) {
Register index = src.index;
if (src.scale != TimesOne) {
int32_t shift = Imm32::ShiftOf(src.scale).value;
MOZ_ASSERT(SecondScratchReg != src.base);
index = SecondScratchReg;
#ifdef JS_CODEGEN_MIPS64
asMasm().ma_dsll(index, src.index, Imm32(shift));
#else
asMasm().ma_sll(index, src.index, Imm32(shift));
#endif
}
switch (size) {
case SizeByte:
as_gslbx(dest, src.base, index, src.offset);
break;
case SizeHalfWord:
as_gslhx(dest, src.base, index, src.offset);
break;
case SizeWord:
as_gslwx(dest, src.base, index, src.offset);
break;
case SizeDouble:
as_gsldx(dest, src.base, index, src.offset);
break;
default:
MOZ_CRASH("Invalid argument for ma_load");
}
return;
}
asMasm().computeScaledAddress(src, SecondScratchReg);
asMasm().ma_load(dest, Address(SecondScratchReg, src.offset), size,
extension);
}
void MacroAssemblerMIPSShared::ma_load_unaligned(Register dest,
const BaseIndex& src,
LoadStoreSize size,
LoadStoreExtension extension) {
int16_t lowOffset, hiOffset;
SecondScratchRegisterScope base(asMasm());
asMasm().computeScaledAddress(src, base);
ScratchRegisterScope scratch(asMasm());
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
lowOffset = Imm16(src.offset).encode();
hiOffset = Imm16(src.offset + size / 8 - 1).encode();
} else {
ma_li(scratch, Imm32(src.offset));
asMasm().addPtr(scratch, base);
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
switch (size) {
case SizeHalfWord:
MOZ_ASSERT(dest != scratch);
if (extension == ZeroExtend) {
as_lbu(scratch, base, hiOffset);
} else {
as_lb(scratch, base, hiOffset);
}
as_lbu(dest, base, lowOffset);
ma_ins(dest, scratch, 8, 24);
break;
case SizeWord:
MOZ_ASSERT(dest != base);
as_lwl(dest, base, hiOffset);
as_lwr(dest, base, lowOffset);
#ifdef JS_CODEGEN_MIPS64
if (extension == ZeroExtend) {
as_dext(dest, dest, 0, 32);
}
#endif
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
MOZ_ASSERT(dest != base);
as_ldl(dest, base, hiOffset);
as_ldr(dest, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_load_unaligned");
}
}
void MacroAssemblerMIPSShared::ma_load_unaligned(Register dest,
const Address& address,
LoadStoreSize size,
LoadStoreExtension extension) {
int16_t lowOffset, hiOffset;
ScratchRegisterScope scratch1(asMasm());
SecondScratchRegisterScope scratch2(asMasm());
Register base;
if (Imm16::IsInSignedRange(address.offset) &&
Imm16::IsInSignedRange(address.offset + size / 8 - 1)) {
base = address.base;
lowOffset = Imm16(address.offset).encode();
hiOffset = Imm16(address.offset + size / 8 - 1).encode();
} else {
ma_li(scratch1, Imm32(address.offset));
asMasm().addPtr(address.base, scratch1);
base = scratch1;
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
switch (size) {
case SizeHalfWord:
MOZ_ASSERT(base != scratch2 && dest != scratch2);
if (extension == ZeroExtend) {
as_lbu(scratch2, base, hiOffset);
} else {
as_lb(scratch2, base, hiOffset);
}
as_lbu(dest, base, lowOffset);
ma_ins(dest, scratch2, 8, 24);
break;
case SizeWord:
MOZ_ASSERT(dest != base);
as_lwl(dest, base, hiOffset);
as_lwr(dest, base, lowOffset);
#ifdef JS_CODEGEN_MIPS64
if (extension == ZeroExtend) {
as_dext(dest, dest, 0, 32);
}
#endif
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
MOZ_ASSERT(dest != base);
as_ldl(dest, base, hiOffset);
as_ldr(dest, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_load_unaligned");
}
}
void MacroAssemblerMIPSShared::ma_load_unaligned(
const wasm::MemoryAccessDesc& access, Register dest, const BaseIndex& src,
Register temp, LoadStoreSize size, LoadStoreExtension extension) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
int16_t lowOffset, hiOffset;
Register base;
asMasm().computeScaledAddress(src, SecondScratchReg);
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
base = SecondScratchReg;
lowOffset = Imm16(src.offset).encode();
hiOffset = Imm16(src.offset + size / 8 - 1).encode();
} else {
ma_li(ScratchRegister, Imm32(src.offset));
asMasm().addPtr(SecondScratchReg, ScratchRegister);
base = ScratchRegister;
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
BufferOffset load;
switch (size) {
case SizeHalfWord:
if (extension == ZeroExtend) {
load = as_lbu(temp, base, hiOffset);
} else {
load = as_lb(temp, base, hiOffset);
}
as_lbu(dest, base, lowOffset);
ma_ins(dest, temp, 8, 24);
break;
case SizeWord:
load = as_lwl(dest, base, hiOffset);
as_lwr(dest, base, lowOffset);
#ifdef JS_CODEGEN_MIPS64
if (extension == ZeroExtend) {
as_dext(dest, dest, 0, 32);
}
#endif
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
load = as_ldl(dest, base, hiOffset);
as_ldr(dest, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_load");
}
append(access, load.getOffset());
}
void MacroAssemblerMIPSShared::ma_store(Register data, const BaseIndex& dest,
LoadStoreSize size,
LoadStoreExtension extension) {
if (isLoongson() && Imm8::IsInSignedRange(dest.offset)) {
Register index = dest.index;
if (dest.scale != TimesOne) {
int32_t shift = Imm32::ShiftOf(dest.scale).value;
MOZ_ASSERT(SecondScratchReg != dest.base);
index = SecondScratchReg;
#ifdef JS_CODEGEN_MIPS64
asMasm().ma_dsll(index, dest.index, Imm32(shift));
#else
asMasm().ma_sll(index, dest.index, Imm32(shift));
#endif
}
switch (size) {
case SizeByte:
as_gssbx(data, dest.base, index, dest.offset);
break;
case SizeHalfWord:
as_gsshx(data, dest.base, index, dest.offset);
break;
case SizeWord:
as_gsswx(data, dest.base, index, dest.offset);
break;
case SizeDouble:
as_gssdx(data, dest.base, index, dest.offset);
break;
default:
MOZ_CRASH("Invalid argument for ma_store");
}
return;
}
asMasm().computeScaledAddress(dest, SecondScratchReg);
asMasm().ma_store(data, Address(SecondScratchReg, dest.offset), size,
extension);
}
void MacroAssemblerMIPSShared::ma_store(Imm32 imm, const BaseIndex& dest,
LoadStoreSize size,
LoadStoreExtension extension) {
if (isLoongson() && Imm8::IsInSignedRange(dest.offset)) {
Register data = zero;
Register index = dest.index;
if (imm.value) {
MOZ_ASSERT(ScratchRegister != dest.base);
MOZ_ASSERT(ScratchRegister != dest.index);
data = ScratchRegister;
ma_li(data, imm);
}
if (dest.scale != TimesOne) {
int32_t shift = Imm32::ShiftOf(dest.scale).value;
MOZ_ASSERT(SecondScratchReg != dest.base);
index = SecondScratchReg;
#ifdef JS_CODEGEN_MIPS64
asMasm().ma_dsll(index, dest.index, Imm32(shift));
#else
asMasm().ma_sll(index, dest.index, Imm32(shift));
#endif
}
switch (size) {
case SizeByte:
as_gssbx(data, dest.base, index, dest.offset);
break;
case SizeHalfWord:
as_gsshx(data, dest.base, index, dest.offset);
break;
case SizeWord:
as_gsswx(data, dest.base, index, dest.offset);
break;
case SizeDouble:
as_gssdx(data, dest.base, index, dest.offset);
break;
default:
MOZ_CRASH("Invalid argument for ma_store");
}
return;
}
// Make sure that SecondScratchReg contains absolute address so that
// offset is 0.
asMasm().computeEffectiveAddress(dest, SecondScratchReg);
// Scrach register is free now, use it for loading imm value
ma_li(ScratchRegister, imm);
// with offset=0 ScratchRegister will not be used in ma_store()
// so we can use it as a parameter here
asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size,
extension);
}
void MacroAssemblerMIPSShared::ma_store_unaligned(Register data,
const Address& address,
LoadStoreSize size) {
int16_t lowOffset, hiOffset;
ScratchRegisterScope scratch(asMasm());
Register base;
if (Imm16::IsInSignedRange(address.offset) &&
Imm16::IsInSignedRange(address.offset + size / 8 - 1)) {
base = address.base;
lowOffset = Imm16(address.offset).encode();
hiOffset = Imm16(address.offset + size / 8 - 1).encode();
} else {
ma_li(scratch, Imm32(address.offset));
asMasm().addPtr(address.base, scratch);
base = scratch;
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
switch (size) {
case SizeHalfWord: {
SecondScratchRegisterScope scratch2(asMasm());
MOZ_ASSERT(base != scratch2);
as_sb(data, base, lowOffset);
ma_ext(scratch2, data, 8, 8);
as_sb(scratch2, base, hiOffset);
break;
}
case SizeWord:
as_swl(data, base, hiOffset);
as_swr(data, base, lowOffset);
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
as_sdl(data, base, hiOffset);
as_sdr(data, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_store_unaligned");
}
}
void MacroAssemblerMIPSShared::ma_store_unaligned(Register data,
const BaseIndex& dest,
LoadStoreSize size) {
int16_t lowOffset, hiOffset;
SecondScratchRegisterScope base(asMasm());
asMasm().computeScaledAddress(dest, base);
ScratchRegisterScope scratch(asMasm());
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
lowOffset = Imm16(dest.offset).encode();
hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
} else {
ma_li(scratch, Imm32(dest.offset));
asMasm().addPtr(scratch, base);
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
switch (size) {
case SizeHalfWord:
MOZ_ASSERT(base != scratch);
as_sb(data, base, lowOffset);
ma_ext(scratch, data, 8, 8);
as_sb(scratch, base, hiOffset);
break;
case SizeWord:
as_swl(data, base, hiOffset);
as_swr(data, base, lowOffset);
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
as_sdl(data, base, hiOffset);
as_sdr(data, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_store_unaligned");
}
}
void MacroAssemblerMIPSShared::ma_store_unaligned(
const wasm::MemoryAccessDesc& access, Register data, const BaseIndex& dest,
Register temp, LoadStoreSize size, LoadStoreExtension extension) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
int16_t lowOffset, hiOffset;
Register base;
asMasm().computeScaledAddress(dest, SecondScratchReg);
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
base = SecondScratchReg;
lowOffset = Imm16(dest.offset).encode();
hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
} else {
ma_li(ScratchRegister, Imm32(dest.offset));
asMasm().addPtr(SecondScratchReg, ScratchRegister);
base = ScratchRegister;
lowOffset = Imm16(0).encode();
hiOffset = Imm16(size / 8 - 1).encode();
}
BufferOffset store;
switch (size) {
case SizeHalfWord:
ma_ext(temp, data, 8, 8);
store = as_sb(temp, base, hiOffset);
as_sb(data, base, lowOffset);
break;
case SizeWord:
store = as_swl(data, base, hiOffset);
as_swr(data, base, lowOffset);
break;
#ifdef JS_CODEGEN_MIPS64
case SizeDouble:
store = as_sdl(data, base, hiOffset);
as_sdr(data, base, lowOffset);
break;
#endif
default:
MOZ_CRASH("Invalid argument for ma_store");
}
append(access, store.getOffset());
}
// Branches when done from within mips-specific code.
void MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, Label* label,
Condition c, JumpKind jumpKind) {
switch (c) {
case Equal:
case NotEqual:
asMasm().branchWithCode(getBranchCode(lhs, rhs, c), label, jumpKind);
break;
case Always:
ma_b(label, jumpKind);
break;
case Zero:
case NonZero:
case Signed:
case NotSigned:
MOZ_ASSERT(lhs == rhs);
asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
break;
default:
Condition cond = ma_cmp(ScratchRegister, lhs, rhs, c);
asMasm().branchWithCode(getBranchCode(ScratchRegister, cond), label,
jumpKind);
break;
}
}
void MacroAssemblerMIPSShared::ma_b(Register lhs, Imm32 imm, Label* label,
Condition c, JumpKind jumpKind) {
MOZ_ASSERT(c != Overflow);
if (imm.value == 0) {
if (c == Always || c == AboveOrEqual) {
ma_b(label, jumpKind);
} else if (c == Below) {
; // This condition is always false. No branch required.
} else {
asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
}
} else {
switch (c) {
case Equal:
case NotEqual:
MOZ_ASSERT(lhs != ScratchRegister);
ma_li(ScratchRegister, imm);
ma_b(lhs, ScratchRegister, label, c, jumpKind);
break;
default:
Condition cond = ma_cmp(ScratchRegister, lhs, imm, c);
asMasm().branchWithCode(getBranchCode(ScratchRegister, cond), label,
jumpKind);
}
}
}
void MacroAssemblerMIPSShared::ma_b(Register lhs, ImmPtr imm, Label* l,
Condition c, JumpKind jumpKind) {
asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
}
void MacroAssemblerMIPSShared::ma_b(Label* label, JumpKind jumpKind) {
asMasm().branchWithCode(getBranchCode(BranchIsJump), label, jumpKind);
}
Assembler::Condition MacroAssemblerMIPSShared::ma_cmp(Register dest,
Register lhs,
Register rhs,
Condition c) {
switch (c) {
case Above:
// bgtu s,t,label =>
// sltu at,t,s
// bne at,$zero,offs
as_sltu(dest, rhs, lhs);
return NotEqual;
case AboveOrEqual:
// bgeu s,t,label =>
// sltu at,s,t
// beq at,$zero,offs
as_sltu(dest, lhs, rhs);
return Equal;
case Below:
// bltu s,t,label =>
// sltu at,s,t
// bne at,$zero,offs
as_sltu(dest, lhs, rhs);
return NotEqual;
case BelowOrEqual:
// bleu s,t,label =>
// sltu at,t,s
// beq at,$zero,offs
as_sltu(dest, rhs, lhs);
return Equal;
case GreaterThan:
// bgt s,t,label =>
// slt at,t,s
// bne at,$zero,offs
as_slt(dest, rhs, lhs);
return NotEqual;
case GreaterThanOrEqual:
// bge s,t,label =>
// slt at,s,t
// beq at,$zero,offs
as_slt(dest, lhs, rhs);
return Equal;
case LessThan:
// blt s,t,label =>
// slt at,s,t
// bne at,$zero,offs
as_slt(dest, lhs, rhs);
return NotEqual;
case LessThanOrEqual:
// ble s,t,label =>
// slt at,t,s
// beq at,$zero,offs
as_slt(dest, rhs, lhs);
return Equal;
default:
MOZ_CRASH("Invalid condition.");
}
return Always;
}
Assembler::Condition MacroAssemblerMIPSShared::ma_cmp(Register dest,
Register lhs, Imm32 imm,
Condition c) {
ScratchRegisterScope scratch(asMasm());
MOZ_ASSERT(lhs != scratch);
switch (c) {
case Above:
case BelowOrEqual:
if (Imm16::IsInSignedRange(imm.value + 1) && imm.value != -1) {
// lhs <= rhs via lhs < rhs + 1 if rhs + 1 does not overflow
as_sltiu(dest, lhs, imm.value + 1);
return (c == BelowOrEqual ? NotEqual : Equal);
} else {
ma_li(scratch, imm);
as_sltu(dest, scratch, lhs);
return (c == BelowOrEqual ? Equal : NotEqual);
}
case AboveOrEqual:
case Below:
if (Imm16::IsInSignedRange(imm.value)) {
as_sltiu(dest, lhs, imm.value);
} else {
ma_li(scratch, imm);
as_sltu(dest, lhs, scratch);
}
return (c == AboveOrEqual ? Equal : NotEqual);
case GreaterThan:
case LessThanOrEqual:
if (Imm16::IsInSignedRange(imm.value + 1)) {
// lhs <= rhs via lhs < rhs + 1.
as_slti(dest, lhs, imm.value + 1);
return (c == LessThanOrEqual ? NotEqual : Equal);
} else {
ma_li(scratch, imm);
as_slt(dest, scratch, lhs);
return (c == LessThanOrEqual ? Equal : NotEqual);
}
case GreaterThanOrEqual:
case LessThan:
if (Imm16::IsInSignedRange(imm.value)) {
as_slti(dest, lhs, imm.value);
} else {
ma_li(scratch, imm);
as_slt(dest, lhs, scratch);
}
return (c == GreaterThanOrEqual ? Equal : NotEqual);
default:
MOZ_CRASH("Invalid condition.");
}
return Always;
}
void MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Register rt,
Condition c) {
switch (c) {
case Equal:
// seq d,s,t =>
// xor d,s,t
// sltiu d,d,1
as_xor(rd, rs, rt);
as_sltiu(rd, rd, 1);
break;
case NotEqual:
// sne d,s,t =>
// xor d,s,t
// sltu d,$zero,d
as_xor(rd, rs, rt);
as_sltu(rd, zero, rd);
break;
case Above:
// sgtu d,s,t =>
// sltu d,t,s
as_sltu(rd, rt, rs);
break;
case AboveOrEqual:
// sgeu d,s,t =>
// sltu d,s,t
// xori d,d,1
as_sltu(rd, rs, rt);
as_xori(rd, rd, 1);
break;
case Below:
// sltu d,s,t
as_sltu(rd, rs, rt);
break;
case BelowOrEqual:
// sleu d,s,t =>
// sltu d,t,s
// xori d,d,1
as_sltu(rd, rt, rs);
as_xori(rd, rd, 1);
break;
case GreaterThan:
// sgt d,s,t =>
// slt d,t,s
as_slt(rd, rt, rs);
break;
case GreaterThanOrEqual:
// sge d,s,t =>
// slt d,s,t
// xori d,d,1
as_slt(rd, rs, rt);
as_xori(rd, rd, 1);
break;
case LessThan:
// slt d,s,t
as_slt(rd, rs, rt);
break;
case LessThanOrEqual:
// sle d,s,t =>
// slt d,t,s
// xori d,d,1
as_slt(rd, rt, rs);
as_xori(rd, rd, 1);
break;
case Zero:
MOZ_ASSERT(rs == rt);
// seq d,s,$zero =>
// sltiu d,s,1
as_sltiu(rd, rs, 1);
break;
case NonZero:
MOZ_ASSERT(rs == rt);
// sne d,s,$zero =>
// sltu d,$zero,s
as_sltu(rd, zero, rs);
break;
case Signed:
MOZ_ASSERT(rs == rt);
as_slt(rd, rs, zero);
break;
case NotSigned:
MOZ_ASSERT(rs == rt);
// sge d,s,$zero =>
// slt d,s,$zero
// xori d,d,1
as_slt(rd, rs, zero);
as_xori(rd, rd, 1);
break;
default:
MOZ_CRASH("Invalid condition.");
}
}
void MacroAssemblerMIPSShared::compareFloatingPoint(
FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, DoubleCondition c,
FloatTestKind* testKind, FPConditionBit fcc) {
switch (c) {
case DoubleOrdered:
as_cun(fmt, lhs, rhs, fcc);
*testKind = TestForFalse;
break;
case DoubleEqual:
as_ceq(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleNotEqual:
as_cueq(fmt, lhs, rhs, fcc);
*testKind = TestForFalse;
break;
case DoubleGreaterThan:
as_colt(fmt, rhs, lhs, fcc);
*testKind = TestForTrue;
break;
case DoubleGreaterThanOrEqual:
as_cole(fmt, rhs, lhs, fcc);
*testKind = TestForTrue;
break;
case DoubleLessThan:
as_colt(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleLessThanOrEqual:
as_cole(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleUnordered:
as_cun(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleEqualOrUnordered:
as_cueq(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleNotEqualOrUnordered:
as_ceq(fmt, lhs, rhs, fcc);
*testKind = TestForFalse;
break;
case DoubleGreaterThanOrUnordered:
as_cult(fmt, rhs, lhs, fcc);
*testKind = TestForTrue;
break;
case DoubleGreaterThanOrEqualOrUnordered:
as_cule(fmt, rhs, lhs, fcc);
*testKind = TestForTrue;
break;
case DoubleLessThanOrUnordered:
as_cult(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
case DoubleLessThanOrEqualOrUnordered:
as_cule(fmt, lhs, rhs, fcc);
*testKind = TestForTrue;
break;
default:
MOZ_CRASH("Invalid DoubleCondition.");
}
}
void MacroAssemblerMIPSShared::ma_cmp_set_double(Register dest,
FloatRegister lhs,
FloatRegister rhs,
DoubleCondition c) {
FloatTestKind moveCondition;
compareFloatingPoint(DoubleFloat, lhs, rhs, c, &moveCondition);
#ifdef MIPSR6
as_mfc1(dest, FloatRegisters::f24);
if (moveCondition == TestForTrue) {
as_andi(dest, dest, 0x1);
} else {
as_addiu(dest, dest, 0x1);
}
#else
ma_li(dest, Imm32(1));
if (moveCondition == TestForTrue) {
as_movf(dest, zero);
} else {
as_movt(dest, zero);
}
#endif
}
void MacroAssemblerMIPSShared::ma_cmp_set_float32(Register dest,
FloatRegister lhs,
FloatRegister rhs,
DoubleCondition c) {
FloatTestKind moveCondition;
compareFloatingPoint(SingleFloat, lhs, rhs, c, &moveCondition);
#ifdef MIPSR6
as_mfc1(dest, FloatRegisters::f24);
if (moveCondition == TestForTrue) {
as_andi(dest, dest, 0x1);
} else {
as_addiu(dest, dest, 0x1);
}
#else
ma_li(dest, Imm32(1));
if (moveCondition == TestForTrue) {
as_movf(dest, zero);
} else {
as_movt(dest, zero);
}
#endif
}
void MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Imm32 imm,
Condition c) {
if (imm.value == 0) {
switch (c) {
case Equal:
case BelowOrEqual:
as_sltiu(rd, rs, 1);
break;
case NotEqual:
case Above:
as_sltu(rd, zero, rs);
break;
case AboveOrEqual:
case Below:
as_ori(rd, zero, c == AboveOrEqual ? 1 : 0);
break;
case GreaterThan:
case LessThanOrEqual:
as_slt(rd, zero, rs);
if (c == LessThanOrEqual) {
as_xori(rd, rd, 1);
}
break;
case LessThan:
case GreaterThanOrEqual:
as_slt(rd, rs, zero);
if (c == GreaterThanOrEqual) {
as_xori(rd, rd, 1);
}
break;
case Zero:
as_sltiu(rd, rs, 1);
break;
case NonZero:
as_sltu(rd, zero, rs);
break;
case Signed:
as_slt(rd, rs, zero);
break;
case NotSigned:
as_slt(rd, rs, zero);
as_xori(rd, rd, 1);
break;
default:
MOZ_CRASH("Invalid condition.");
}
return;
}
switch (c) {
case Equal:
case NotEqual:
MOZ_ASSERT(rs != ScratchRegister);
ma_xor(rd, rs, imm);
if (c == Equal) {
as_sltiu(rd, rd, 1);
} else {
as_sltu(rd, zero, rd);
}
break;
case Zero:
case NonZero:
case Signed:
case NotSigned:
MOZ_CRASH("Invalid condition.");
default:
Condition cond = ma_cmp(rd, rs, imm, c);
MOZ_ASSERT(cond == Equal || cond == NotEqual);
if (cond == Equal) as_xori(rd, rd, 1);
}
}
// fp instructions
void MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value) {
Imm32 imm(mozilla::BitwiseCast<uint32_t>(value));
if (imm.value != 0) {
ma_li(ScratchRegister, imm);
moveToFloat32(ScratchRegister, dest);
} else {
moveToFloat32(zero, dest);
}
}
void MacroAssemblerMIPSShared::ma_sd(FloatRegister ft, BaseIndex address) {
if (isLoongson() && Imm8::IsInSignedRange(address.offset)) {
Register index = address.index;
if (address.scale != TimesOne) {
int32_t shift = Imm32::ShiftOf(address.scale).value;
MOZ_ASSERT(SecondScratchReg != address.base);
index = SecondScratchReg;
#ifdef JS_CODEGEN_MIPS64
asMasm().ma_dsll(index, address.index, Imm32(shift));
#else
asMasm().ma_sll(index, address.index, Imm32(shift));
#endif
}
as_gssdx(ft, address.base, index, address.offset);
return;
}
asMasm().computeScaledAddress(address, SecondScratchReg);
asMasm().ma_sd(ft, Address(SecondScratchReg, address.offset));
}
void MacroAssemblerMIPSShared::ma_ss(FloatRegister ft, BaseIndex address) {
if (isLoongson() && Imm8::IsInSignedRange(address.offset)) {
Register index = address.index;
if (address.scale != TimesOne) {
int32_t shift = Imm32::ShiftOf(address.scale).value;
MOZ_ASSERT(SecondScratchReg != address.base);
index = SecondScratchReg;
#ifdef JS_CODEGEN_MIPS64
asMasm().ma_dsll(index, address.index, Imm32(shift));
#else
asMasm().ma_sll(index, address.index, Imm32(shift));
#endif
}
as_gsssx(ft, address.base, index, address.offset);
return;
}
asMasm().computeScaledAddress(address, SecondScratchReg);
asMasm().ma_ss(ft, Address(SecondScratchReg, address.offset));
}
void MacroAssemblerMIPSShared::ma_ld(FloatRegister ft, const BaseIndex& src) {
asMasm().computeScaledAddress(src, SecondScratchReg);
asMasm().ma_ld(ft, Address(SecondScratchReg, src.offset));
}
void MacroAssemblerMIPSShared::ma_ls(FloatRegister ft, const BaseIndex& src) {
asMasm().computeScaledAddress(src, SecondScratchReg);
asMasm().ma_ls(ft, Address(SecondScratchReg, src.offset));
}
void MacroAssemblerMIPSShared::ma_bc1s(FloatRegister lhs, FloatRegister rhs,
Label* label, DoubleCondition c,
JumpKind jumpKind, FPConditionBit fcc) {
FloatTestKind testKind;
compareFloatingPoint(SingleFloat, lhs, rhs, c, &testKind, fcc);
asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
}
void MacroAssemblerMIPSShared::ma_bc1d(FloatRegister lhs, FloatRegister rhs,
Label* label, DoubleCondition c,
JumpKind jumpKind, FPConditionBit fcc) {
FloatTestKind testKind;
compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc);
asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
}
void MacroAssemblerMIPSShared::minMaxDouble(FloatRegister srcDest,
FloatRegister second,
bool handleNaN, bool isMax) {
FloatRegister first = srcDest;
Assembler::DoubleCondition cond = isMax ? Assembler::DoubleLessThanOrEqual
: Assembler::DoubleGreaterThanOrEqual;
Label nan, equal, done;
FloatTestKind moveCondition;
// First or second is NaN, result is NaN.
ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump);
#ifdef MIPSR6
if (isMax) {
as_max(DoubleFloat, srcDest, first, second);
} else {
as_min(DoubleFloat, srcDest, first, second);
}
#else
// Make sure we handle -0 and 0 right.
ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump);
compareFloatingPoint(DoubleFloat, first, second, cond, &moveCondition);
MOZ_ASSERT(TestForTrue == moveCondition);
as_movt(DoubleFloat, first, second);
ma_b(&done, ShortJump);
// Check for zero.
bind(&equal);
asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
compareFloatingPoint(DoubleFloat, first, ScratchDoubleReg,
Assembler::DoubleEqual, &moveCondition);
// So now both operands are either -0 or 0.
if (isMax) {
// -0 + -0 = -0 and -0 + 0 = 0.
as_addd(ScratchDoubleReg, first, second);
} else {
as_negd(ScratchDoubleReg, first);
as_subd(ScratchDoubleReg, ScratchDoubleReg, second);
as_negd(ScratchDoubleReg, ScratchDoubleReg);
}
MOZ_ASSERT(TestForTrue == moveCondition);
// First is 0 or -0, move max/min to it, else just return it.
as_movt(DoubleFloat, first, ScratchDoubleReg);
#endif
ma_b(&done, ShortJump);
bind(&nan);
asMasm().loadConstantDouble(JS::GenericNaN(), srcDest);
bind(&done);
}
void MacroAssemblerMIPSShared::minMaxFloat32(FloatRegister srcDest,
FloatRegister second,
bool handleNaN, bool isMax) {
FloatRegister first = srcDest;
Assembler::DoubleCondition cond = isMax ? Assembler::DoubleLessThanOrEqual
: Assembler::DoubleGreaterThanOrEqual;
Label nan, equal, done;
FloatTestKind moveCondition;
// First or second is NaN, result is NaN.
ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump);
#ifdef MIPSR6
if (isMax) {
as_max(SingleFloat, srcDest, first, second);
} else {
as_min(SingleFloat, srcDest, first, second);
}
#else
// Make sure we handle -0 and 0 right.
ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump);
compareFloatingPoint(SingleFloat, first, second, cond, &moveCondition);
MOZ_ASSERT(TestForTrue == moveCondition);
as_movt(SingleFloat, first, second);
ma_b(&done, ShortJump);
// Check for zero.
bind(&equal);
asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
compareFloatingPoint(SingleFloat, first, ScratchFloat32Reg,
Assembler::DoubleEqual, &moveCondition);
// So now both operands are either -0 or 0.
if (isMax) {
// -0 + -0 = -0 and -0 + 0 = 0.
as_adds(ScratchFloat32Reg, first, second);
} else {
as_negs(ScratchFloat32Reg, first);
as_subs(ScratchFloat32Reg, ScratchFloat32Reg, second);
as_negs(ScratchFloat32Reg, ScratchFloat32Reg);
}
MOZ_ASSERT(TestForTrue == moveCondition);
// First is 0 or -0, move max/min to it, else just return it.
as_movt(SingleFloat, first, ScratchFloat32Reg);
#endif
ma_b(&done, ShortJump);
bind(&nan);
asMasm().loadConstantFloat32(JS::GenericNaN(), srcDest);
bind(&done);
}
void MacroAssemblerMIPSShared::loadDouble(const Address& address,
FloatRegister dest) {
asMasm().ma_ld(dest, address);
}
void MacroAssemblerMIPSShared::loadDouble(const BaseIndex& src,
FloatRegister dest) {
asMasm().ma_ld(dest, src);
}
void MacroAssemblerMIPSShared::loadFloatAsDouble(const Address& address,
FloatRegister dest) {
asMasm().ma_ls(dest, address);
as_cvtds(dest, dest);
}
void MacroAssemblerMIPSShared::loadFloatAsDouble(const BaseIndex& src,
FloatRegister dest) {
asMasm().loadFloat32(src, dest);
as_cvtds(dest, dest);
}
void MacroAssemblerMIPSShared::loadFloat32(const Address& address,
FloatRegister dest) {
asMasm().ma_ls(dest, address);
}
void MacroAssemblerMIPSShared::loadFloat32(const BaseIndex& src,
FloatRegister dest) {
asMasm().ma_ls(dest, src);
}
void MacroAssemblerMIPSShared::ma_call(ImmPtr dest) {
asMasm().ma_liPatchable(CallReg, dest);
as_jalr(CallReg);
as_nop();
}
void MacroAssemblerMIPSShared::ma_jump(ImmPtr dest) {
asMasm().ma_liPatchable(ScratchRegister, dest);
as_jr(ScratchRegister);
as_nop();
}
MacroAssembler& MacroAssemblerMIPSShared::asMasm() {
return *static_cast<MacroAssembler*>(this);
}
const MacroAssembler& MacroAssemblerMIPSShared::asMasm() const {
return *static_cast<const MacroAssembler*>(this);
}
//{{{ check_macroassembler_style
// ===============================================================
// MacroAssembler high-level usage.
void MacroAssembler::flush() {}
// ===============================================================
// Stack manipulation functions.
void MacroAssembler::Push(Register reg) {
ma_push(reg);
adjustFrame(int32_t(sizeof(intptr_t)));
}
void MacroAssembler::Push(const Imm32 imm) {
ma_li(ScratchRegister, imm);
ma_push(ScratchRegister);
adjustFrame(int32_t(sizeof(intptr_t)));
}
void MacroAssembler::Push(const ImmWord imm) {
ma_li(ScratchRegister, imm);
ma_push(ScratchRegister);
adjustFrame(int32_t(sizeof(intptr_t)));
}
void MacroAssembler::Push(const ImmPtr imm) {
Push(ImmWord(uintptr_t(imm.value)));
}
void MacroAssembler::Push(const ImmGCPtr ptr) {
ma_li(ScratchRegister, ptr);
ma_push(ScratchRegister);
adjustFrame(int32_t(sizeof(intptr_t)));
}
void MacroAssembler::Push(FloatRegister f) {
ma_push(f);
adjustFrame(int32_t(f.pushSize()));
}
void MacroAssembler::Pop(Register reg) {
ma_pop(reg);
adjustFrame(-int32_t(sizeof(intptr_t)));
}
void MacroAssembler::Pop(FloatRegister f) {
ma_pop(f);
adjustFrame(-int32_t(f.pushSize()));
}
void MacroAssembler::Pop(const ValueOperand& val) {
popValue(val);
adjustFrame(-int32_t(sizeof(Value)));
}
void MacroAssembler::PopStackPtr() {
loadPtr(Address(StackPointer, 0), StackPointer);
adjustFrame(-int32_t(sizeof(intptr_t)));
}
// ===============================================================
// Simple call functions.
CodeOffset MacroAssembler::call(Register reg) {
as_jalr(reg);
as_nop();
return CodeOffset(currentOffset());
}
CodeOffset MacroAssembler::call(Label* label) {
ma_bal(label);
return CodeOffset(currentOffset());
}
CodeOffset MacroAssembler::callWithPatch() {
as_bal(BOffImm16(3 * sizeof(uint32_t)));
addPtr(Imm32(5 * sizeof(uint32_t)), ra);
// Allocate space which will be patched by patchCall().
spew(".space 32bit initValue 0xffff ffff");
writeInst(UINT32_MAX);
as_lw(ScratchRegister, ra, -(int32_t)(5 * sizeof(uint32_t)));
addPtr(ra, ScratchRegister);
as_jr(ScratchRegister);
as_nop();
return CodeOffset(currentOffset());
}
void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
BufferOffset call(callerOffset - 7 * sizeof(uint32_t));
BOffImm16 offset = BufferOffset(calleeOffset).diffB<BOffImm16>(call);
if (!offset.isInvalid()) {
InstImm* bal = (InstImm*)editSrc(call);
bal->setBOffImm16(offset);
} else {
uint32_t u32Offset = callerOffset - 5 * sizeof(uint32_t);
uint32_t* u32 =
reinterpret_cast<uint32_t*>(editSrc(BufferOffset(u32Offset)));
*u32 = calleeOffset - callerOffset;
}
}
CodeOffset MacroAssembler::farJumpWithPatch() {
ma_move(SecondScratchReg, ra);
as_bal(BOffImm16(3 * sizeof(uint32_t)));
as_lw(ScratchRegister, ra, 0);
// Allocate space which will be patched by patchFarJump().
CodeOffset farJump(currentOffset());
spew(".space 32bit initValue 0xffff ffff");
writeInst(UINT32_MAX);
addPtr(ra, ScratchRegister);
as_jr(ScratchRegister);
ma_move(ra, SecondScratchReg);
return farJump;
}
void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) {
uint32_t* u32 =
reinterpret_cast<uint32_t*>(editSrc(BufferOffset(farJump.offset())));
MOZ_ASSERT(*u32 == UINT32_MAX);
*u32 = targetOffset - farJump.offset();
}
CodeOffset MacroAssembler::call(wasm::SymbolicAddress target) {
movePtr(target, CallReg);
return call(CallReg);
}
void MacroAssembler::call(const Address& addr) {
loadPtr(addr, CallReg);
call(CallReg);
}
void MacroAssembler::call(ImmWord target) { call(ImmPtr((void*)target.value)); }
void MacroAssembler::call(ImmPtr target) {
BufferOffset bo = m_buffer.nextOffset();
addPendingJump(bo, target, RelocationKind::HARDCODED);
ma_call(target);
}
void MacroAssembler::call(JitCode* c) {
BufferOffset bo = m_buffer.nextOffset();
addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE);
ma_liPatchable(ScratchRegister, ImmPtr(c->raw()));
callJitNoProfiler(ScratchRegister);
}
CodeOffset MacroAssembler::nopPatchableToCall() {
// MIPS32 //MIPS64
as_nop(); // lui // lui
as_nop(); // ori // ori
as_nop(); // jalr // drotr32
as_nop(); // ori
#ifdef JS_CODEGEN_MIPS64
as_nop(); // jalr
as_nop();
#endif
return CodeOffset(currentOffset());
}
void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) {
#ifdef JS_CODEGEN_MIPS64
Instruction* inst = (Instruction*)call - 6 /* six nops */;
Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)target);
inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
#else
Instruction* inst = (Instruction*)call - 4 /* four nops */;
Assembler::WriteLuiOriInstructions(inst, &inst[1], ScratchRegister,
(uint32_t)target);
inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
#endif
}
void MacroAssembler::patchCallToNop(uint8_t* call) {
#ifdef JS_CODEGEN_MIPS64
Instruction* inst = (Instruction*)call - 6 /* six nops */;
#else
Instruction* inst = (Instruction*)call - 4 /* four nops */;
#endif
inst[0].makeNop();
inst[1].makeNop();
inst[2].makeNop();
inst[3].makeNop();
#ifdef JS_CODEGEN_MIPS64
inst[4].makeNop();
inst[5].makeNop();
#endif
}
void MacroAssembler::pushReturnAddress() { push(ra); }
void MacroAssembler::popReturnAddress() { pop(ra); }
// ===============================================================
// Jit Frames.
uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) {
CodeLabel cl;
ma_li(scratch, &cl);
Push(scratch);
bind(&cl);
uint32_t retAddr = currentOffset();
addCodeLabel(cl);
return retAddr;
}
void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) {
ma_and(buffer, ptr, Imm32(int32_t(~gc::ChunkMask)));
loadPtr(Address(buffer, gc::ChunkStoreBufferOffset), buffer);
}
void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr,
Register temp, Label* label) {
MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
MOZ_ASSERT(ptr != temp);
MOZ_ASSERT(ptr != SecondScratchReg);
ma_and(SecondScratchReg, ptr, Imm32(int32_t(~gc::ChunkMask)));
branchPtr(InvertCondition(cond),
Address(SecondScratchReg, gc::ChunkStoreBufferOffset), ImmWord(0),
label);
}
void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); }
// ===============================================================
// WebAssembly
FaultingCodeOffset MacroAssembler::wasmTrapInstruction() {
FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
as_teq(zero, zero, WASM_TRAP);
return fco;
}
void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input,
Register output,
bool isSaturating,
Label* oolEntry) {
as_truncwd(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input,
Register output,
bool isSaturating,
Label* oolEntry) {
as_truncws(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void MacroAssembler::oolWasmTruncateCheckF32ToI32(FloatRegister input,
Register output,
TruncFlags flags,
wasm::BytecodeOffset off,
Label* rejoin) {
outOfLineWasmTruncateToInt32Check(input, output, MIRType::Float32, flags,
rejoin, off);
}
void MacroAssembler::oolWasmTruncateCheckF64ToI32(FloatRegister input,
Register output,
TruncFlags flags,
wasm::BytecodeOffset off,
Label* rejoin) {
outOfLineWasmTruncateToInt32Check(input, output, MIRType::Double, flags,
rejoin, off);
}
void MacroAssembler::oolWasmTruncateCheckF32ToI64(FloatRegister input,
Register64 output,
TruncFlags flags,
wasm::BytecodeOffset off,
Label* rejoin) {
outOfLineWasmTruncateToInt64Check(input, output, MIRType::Float32, flags,
rejoin, off);
}
void MacroAssembler::oolWasmTruncateCheckF64ToI64(FloatRegister input,
Register64 output,
TruncFlags flags,
wasm::BytecodeOffset off,
Label* rejoin) {
outOfLineWasmTruncateToInt64Check(input, output, MIRType::Double, flags,
rejoin, off);
}
void MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt32Check(
FloatRegister input, Register output, MIRType fromType, TruncFlags flags,
Label* rejoin, wasm::BytecodeOffset trapOffset) {
bool isUnsigned = flags & TRUNC_UNSIGNED;
bool isSaturating = flags & TRUNC_SATURATING;
if (isSaturating) {
if (fromType == MIRType::Double) {
asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
} else {
asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
}
if (isUnsigned) {
ma_li(output, Imm32(UINT32_MAX));
FloatTestKind moveCondition;
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThanOrUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
} else {
// Positive overflow is already saturated to INT32_MAX, so we only have
// to handle NaN and negative overflow here.
FloatTestKind moveCondition;
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input, input,
Assembler::DoubleUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThan, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
ma_li(ScratchRegister, Imm32(INT32_MIN));
as_movt(output, ScratchRegister);
}
MOZ_ASSERT(rejoin->bound());
asMasm().jump(rejoin);
return;
}
Label inputIsNaN;
if (fromType == MIRType::Double) {
asMasm().branchDouble(Assembler::DoubleUnordered, input, input,
&inputIsNaN);
} else if (fromType == MIRType::Float32) {
asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
}
asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
asMasm().bind(&inputIsNaN);
asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);
}
void MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt64Check(
FloatRegister input, Register64 output_, MIRType fromType, TruncFlags flags,
Label* rejoin, wasm::BytecodeOffset trapOffset) {
bool isUnsigned = flags & TRUNC_UNSIGNED;
bool isSaturating = flags & TRUNC_SATURATING;
if (isSaturating) {
#if defined(JS_CODEGEN_MIPS32)
// Saturating callouts don't use ool path.
return;
#else
Register output = output_.reg;
if (fromType == MIRType::Double) {
asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
} else {
asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
}
if (isUnsigned) {
asMasm().ma_li(output, ImmWord(UINT64_MAX));
FloatTestKind moveCondition;
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThanOrUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
} else {
// Positive overflow is already saturated to INT64_MAX, so we only have
// to handle NaN and negative overflow here.
FloatTestKind moveCondition;
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input, input,
Assembler::DoubleUnordered, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
as_movt(output, zero);
compareFloatingPoint(
fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
Assembler::DoubleLessThan, &moveCondition);
MOZ_ASSERT(moveCondition == TestForTrue);
asMasm().ma_li(ScratchRegister, ImmWord(INT64_MIN));
as_movt(output, ScratchRegister);
}
MOZ_ASSERT(rejoin->bound());
asMasm().jump(rejoin);
return;
#endif
}
Label inputIsNaN;
if (fromType == MIRType::Double) {
asMasm().branchDouble(Assembler::DoubleUnordered, input, input,
&inputIsNaN);
} else if (fromType == MIRType::Float32) {
asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
}
#if defined(JS_CODEGEN_MIPS32)
// Only possible valid input that produces INT64_MIN result.
double validInput =
isUnsigned ? double(uint64_t(INT64_MIN)) : double(int64_t(INT64_MIN));
if (fromType == MIRType::Double) {
asMasm().loadConstantDouble(validInput, ScratchDoubleReg);
asMasm().branchDouble(Assembler::DoubleEqual, input, ScratchDoubleReg,
rejoin);
} else {
asMasm().loadConstantFloat32(float(validInput), ScratchFloat32Reg);
asMasm().branchFloat(Assembler::DoubleEqual, input, ScratchDoubleReg,
rejoin);
}
#endif
asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
asMasm().bind(&inputIsNaN);
asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);
}
void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access,
Register memoryBase, Register ptr,
Register ptrScratch, AnyRegister output) {
wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
}
void MacroAssembler::wasmUnalignedLoad(const wasm::MemoryAccessDesc& access,
Register memoryBase, Register ptr,
Register ptrScratch, Register output,
Register tmp) {
wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp);
}
void MacroAssembler::wasmUnalignedLoadFP(const wasm::MemoryAccessDesc& access,
Register memoryBase, Register ptr,
Register ptrScratch,
FloatRegister output, Register tmp1) {
wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp1);
}
void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access,
AnyRegister value, Register memoryBase,
Register ptr, Register ptrScratch) {
wasmStoreImpl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
}
void MacroAssembler::wasmUnalignedStore(const wasm::MemoryAccessDesc& access,
Register value, Register memoryBase,
Register ptr, Register ptrScratch,
Register tmp) {
wasmStoreImpl(access, AnyRegister(value), memoryBase, ptr, ptrScratch, tmp);
}
void MacroAssembler::wasmUnalignedStoreFP(const wasm::MemoryAccessDesc& access,
FloatRegister floatValue,
Register memoryBase, Register ptr,
Register ptrScratch, Register tmp) {
wasmStoreImpl(access, AnyRegister(floatValue), memoryBase, ptr, ptrScratch,
tmp);
}
void MacroAssemblerMIPSShared::wasmLoadImpl(
const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
Register ptrScratch, AnyRegister output, Register tmp) {
access.assertOffsetInGuardPages();
uint32_t offset = access.offset();
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
// Maybe add the offset.
if (offset) {
asMasm().addPtr(ImmWord(offset), ptrScratch);
ptr = ptrScratch;
}
unsigned byteSize = access.byteSize();
bool isSigned;
bool isFloat = false;
MOZ_ASSERT(!access.isZeroExtendSimd128Load());
MOZ_ASSERT(!access.isSplatSimd128Load());
MOZ_ASSERT(!access.isWidenSimd128Load());
switch (access.type()) {
case Scalar::Int8:
isSigned = true;
break;
case Scalar::Uint8:
isSigned = false;
break;
case Scalar::Int16:
isSigned = true;
break;
case Scalar::Uint16:
isSigned = false;
break;
case Scalar::Int32:
isSigned = true;
break;
case Scalar::Uint32:
isSigned = false;
break;
case Scalar::Float64:
isFloat = true;
break;
case Scalar::Float32:
isFloat = true;
break;
default:
MOZ_CRASH("unexpected array type");
}
BaseIndex address(memoryBase, ptr, TimesOne);
if (IsUnaligned(access)) {
MOZ_ASSERT(tmp != InvalidReg);
if (isFloat) {
if (byteSize == 4) {
asMasm().loadUnalignedFloat32(access, address, tmp, output.fpu());
} else {
asMasm().loadUnalignedDouble(access, address, tmp, output.fpu());
}
} else {
asMasm().ma_load_unaligned(access, output.gpr(), address, tmp,
static_cast<LoadStoreSize>(8 * byteSize),
isSigned ? SignExtend : ZeroExtend);
}
return;
}
asMasm().memoryBarrierBefore(access.sync());
if (isFloat) {
if (byteSize == 4) {
asMasm().ma_ls(output.fpu(), address);
} else {
asMasm().ma_ld(output.fpu(), address);
}
} else {
asMasm().ma_load(output.gpr(), address,
static_cast<LoadStoreSize>(8 * byteSize),
isSigned ? SignExtend : ZeroExtend);
}
asMasm().append(access, asMasm().size() - 4);
asMasm().memoryBarrierAfter(access.sync());
}
void MacroAssemblerMIPSShared::wasmStoreImpl(
const wasm::MemoryAccessDesc& access, AnyRegister value,
Register memoryBase, Register ptr, Register ptrScratch, Register tmp) {
access.assertOffsetInGuardPages();
uint32_t offset = access.offset();
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
// Maybe add the offset.
if (offset) {
asMasm().addPtr(ImmWord(offset), ptrScratch);
ptr = ptrScratch;
}
unsigned byteSize = access.byteSize();
bool isSigned;
bool isFloat = false;
switch (access.type()) {
case Scalar::Int8:
isSigned = true;
break;
case Scalar::Uint8:
isSigned = false;
break;
case Scalar::Int16:
isSigned = true;
break;
case Scalar::Uint16:
isSigned = false;
break;
case Scalar::Int32:
isSigned = true;
break;
case Scalar::Uint32:
isSigned = false;
break;
case Scalar::Int64:
isSigned = true;
break;
case Scalar::Float64:
isFloat = true;
break;
case Scalar::Float32:
isFloat = true;
break;
default:
MOZ_CRASH("unexpected array type");
}
BaseIndex address(memoryBase, ptr, TimesOne);
if (IsUnaligned(access)) {
MOZ_ASSERT(tmp != InvalidReg);
if (isFloat) {
if (byteSize == 4) {
asMasm().storeUnalignedFloat32(access, value.fpu(), tmp, address);
} else {
asMasm().storeUnalignedDouble(access, value.fpu(), tmp, address);
}
} else {
asMasm().ma_store_unaligned(access, value.gpr(), address, tmp,
static_cast<LoadStoreSize>(8 * byteSize),
isSigned ? SignExtend : ZeroExtend);
}
return;
}
asMasm().memoryBarrierBefore(access.sync());
if (isFloat) {
if (byteSize == 4) {
asMasm().ma_ss(value.fpu(), address);
} else {
asMasm().ma_sd(value.fpu(), address);
}
} else {
asMasm().ma_store(value.gpr(), address,
static_cast<LoadStoreSize>(8 * byteSize),
isSigned ? SignExtend : ZeroExtend);
}
// Only the last emitted instruction is a memory access.
asMasm().append(access, asMasm().size() - 4);
asMasm().memoryBarrierAfter(access.sync());
}
void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch,
ExitFrameType type) {
enterFakeExitFrame(cxreg, scratch, type);
}
// ========================================================================
// Primitive atomic operations.
template <typename T>
static void CompareExchange(MacroAssembler& masm,
const wasm::MemoryAccessDesc* access,
Scalar::Type type, Synchronization sync,
const T& mem, Register oldval, Register newval,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register output) {
bool signExtend = Scalar::isSignedIntType(type);
unsigned nbytes = Scalar::byteSize(type);
switch (nbytes) {
case 1:
case 2:
break;
case 4:
MOZ_ASSERT(valueTemp == InvalidReg);
MOZ_ASSERT(offsetTemp == InvalidReg);
MOZ_ASSERT(maskTemp == InvalidReg);
break;
default:
MOZ_CRASH();
}
Label again, end;
masm.computeEffectiveAddress(mem, SecondScratchReg);
if (nbytes == 4) {
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(output, SecondScratchReg, 0);
masm.ma_b(output, oldval, &end, Assembler::NotEqual, ShortJump);
masm.ma_move(ScratchRegister, newval);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
masm.bind(&end);
return;
}
masm.as_andi(offsetTemp, SecondScratchReg, 3);
masm.subPtr(offsetTemp, SecondScratchReg);
#if !MOZ_LITTLE_ENDIAN()
masm.as_xori(offsetTemp, offsetTemp, 3);
#endif
masm.as_sll(offsetTemp, offsetTemp, 3);
masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
masm.as_sllv(maskTemp, maskTemp, offsetTemp);
masm.as_nor(maskTemp, zero, maskTemp);
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(ScratchRegister, SecondScratchReg, 0);
masm.as_srlv(output, ScratchRegister, offsetTemp);
switch (nbytes) {
case 1:
if (signExtend) {
masm.ma_seb(valueTemp, oldval);
masm.ma_seb(output, output);
} else {
masm.as_andi(valueTemp, oldval, 0xff);
masm.as_andi(output, output, 0xff);
}
break;
case 2:
if (signExtend) {
masm.ma_seh(valueTemp, oldval);
masm.ma_seh(output, output);
} else {
masm.as_andi(valueTemp, oldval, 0xffff);
masm.as_andi(output, output, 0xffff);
}
break;
}
masm.ma_b(output, valueTemp, &end, Assembler::NotEqual, ShortJump);
masm.as_sllv(valueTemp, newval, offsetTemp);
masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
masm.bind(&end);
}
void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
const Address& mem, Register oldval,
Register newval, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, output);
}
void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
const BaseIndex& mem, Register oldval,
Register newval, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, output);
}
void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
const Address& mem, Register oldval,
Register newval, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
newval, valueTemp, offsetTemp, maskTemp, output);
}
void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
const BaseIndex& mem, Register oldval,
Register newval, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
newval, valueTemp, offsetTemp, maskTemp, output);
}
template <typename T>
static void AtomicExchange(MacroAssembler& masm,
const wasm::MemoryAccessDesc* access,
Scalar::Type type, Synchronization sync,
const T& mem, Register value, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
bool signExtend = Scalar::isSignedIntType(type);
unsigned nbytes = Scalar::byteSize(type);
switch (nbytes) {
case 1:
case 2:
break;
case 4:
MOZ_ASSERT(valueTemp == InvalidReg);
MOZ_ASSERT(offsetTemp == InvalidReg);
MOZ_ASSERT(maskTemp == InvalidReg);
break;
default:
MOZ_CRASH();
}
Label again;
masm.computeEffectiveAddress(mem, SecondScratchReg);
if (nbytes == 4) {
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(output, SecondScratchReg, 0);
masm.ma_move(ScratchRegister, value);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
return;
}
masm.as_andi(offsetTemp, SecondScratchReg, 3);
masm.subPtr(offsetTemp, SecondScratchReg);
#if !MOZ_LITTLE_ENDIAN()
masm.as_xori(offsetTemp, offsetTemp, 3);
#endif
masm.as_sll(offsetTemp, offsetTemp, 3);
masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
masm.as_sllv(maskTemp, maskTemp, offsetTemp);
masm.as_nor(maskTemp, zero, maskTemp);
switch (nbytes) {
case 1:
masm.as_andi(valueTemp, value, 0xff);
break;
case 2:
masm.as_andi(valueTemp, value, 0xffff);
break;
}
masm.as_sllv(valueTemp, valueTemp, offsetTemp);
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(output, SecondScratchReg, 0);
masm.as_and(ScratchRegister, output, maskTemp);
masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.as_srlv(output, output, offsetTemp);
switch (nbytes) {
case 1:
if (signExtend) {
masm.ma_seb(output, output);
} else {
masm.as_andi(output, output, 0xff);
}
break;
case 2:
if (signExtend) {
masm.ma_seh(output, output);
} else {
masm.as_andi(output, output, 0xffff);
}
break;
}
masm.memoryBarrierAfter(sync);
}
void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
const Address& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register output) {
AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp,
maskTemp, output);
}
void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
const BaseIndex& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register output) {
AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp,
maskTemp, output);
}
void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
const Address& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register output) {
AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
valueTemp, offsetTemp, maskTemp, output);
}
void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
const BaseIndex& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register output) {
AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
valueTemp, offsetTemp, maskTemp, output);
}
template <typename T>
static void AtomicFetchOp(MacroAssembler& masm,
const wasm::MemoryAccessDesc* access,
Scalar::Type type, Synchronization sync, AtomicOp op,
const T& mem, Register value, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
bool signExtend = Scalar::isSignedIntType(type);
unsigned nbytes = Scalar::byteSize(type);
switch (nbytes) {
case 1:
case 2:
break;
case 4:
MOZ_ASSERT(valueTemp == InvalidReg);
MOZ_ASSERT(offsetTemp == InvalidReg);
MOZ_ASSERT(maskTemp == InvalidReg);
break;
default:
MOZ_CRASH();
}
Label again;
masm.computeEffectiveAddress(mem, SecondScratchReg);
if (nbytes == 4) {
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(output, SecondScratchReg, 0);
switch (op) {
case AtomicOp::Add:
masm.as_addu(ScratchRegister, output, value);
break;
case AtomicOp::Sub:
masm.as_subu(ScratchRegister, output, value);
break;
case AtomicOp::And:
masm.as_and(ScratchRegister, output, value);
break;
case AtomicOp::Or:
masm.as_or(ScratchRegister, output, value);
break;
case AtomicOp::Xor:
masm.as_xor(ScratchRegister, output, value);
break;
default:
MOZ_CRASH();
}
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
return;
}
masm.as_andi(offsetTemp, SecondScratchReg, 3);
masm.subPtr(offsetTemp, SecondScratchReg);
#if !MOZ_LITTLE_ENDIAN()
masm.as_xori(offsetTemp, offsetTemp, 3);
#endif
masm.as_sll(offsetTemp, offsetTemp, 3);
masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
masm.as_sllv(maskTemp, maskTemp, offsetTemp);
masm.as_nor(maskTemp, zero, maskTemp);
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(ScratchRegister, SecondScratchReg, 0);
masm.as_srlv(output, ScratchRegister, offsetTemp);
switch (op) {
case AtomicOp::Add:
masm.as_addu(valueTemp, output, value);
break;
case AtomicOp::Sub:
masm.as_subu(valueTemp, output, value);
break;
case AtomicOp::And:
masm.as_and(valueTemp, output, value);
break;
case AtomicOp::Or:
masm.as_or(valueTemp, output, value);
break;
case AtomicOp::Xor:
masm.as_xor(valueTemp, output, value);
break;
default:
MOZ_CRASH();
}
switch (nbytes) {
case 1:
masm.as_andi(valueTemp, valueTemp, 0xff);
break;
case 2:
masm.as_andi(valueTemp, valueTemp, 0xffff);
break;
}
masm.as_sllv(valueTemp, valueTemp, offsetTemp);
masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
switch (nbytes) {
case 1:
if (signExtend) {
masm.ma_seb(output, output);
} else {
masm.as_andi(output, output, 0xff);
}
break;
case 2:
if (signExtend) {
masm.ma_seh(output, output);
} else {
masm.as_andi(output, output, 0xffff);
}
break;
}
masm.memoryBarrierAfter(sync);
}
void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
AtomicOp op, Register value,
const Address& mem, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp,
offsetTemp, maskTemp, output);
}
void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
AtomicOp op, Register value,
const BaseIndex& mem, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp,
offsetTemp, maskTemp, output);
}
void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
AtomicOp op, Register value,
const Address& mem, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value,
valueTemp, offsetTemp, maskTemp, output);
}
void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
AtomicOp op, Register value,
const BaseIndex& mem, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register output) {
AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value,
valueTemp, offsetTemp, maskTemp, output);
}
template <typename T>
static void AtomicEffectOp(MacroAssembler& masm,
const wasm::MemoryAccessDesc* access,
Scalar::Type type, Synchronization sync, AtomicOp op,
const T& mem, Register value, Register valueTemp,
Register offsetTemp, Register maskTemp) {
unsigned nbytes = Scalar::byteSize(type);
switch (nbytes) {
case 1:
case 2:
break;
case 4:
MOZ_ASSERT(valueTemp == InvalidReg);
MOZ_ASSERT(offsetTemp == InvalidReg);
MOZ_ASSERT(maskTemp == InvalidReg);
break;
default:
MOZ_CRASH();
}
Label again;
masm.computeEffectiveAddress(mem, SecondScratchReg);
if (nbytes == 4) {
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(ScratchRegister, SecondScratchReg, 0);
switch (op) {
case AtomicOp::Add:
masm.as_addu(ScratchRegister, ScratchRegister, value);
break;
case AtomicOp::Sub:
masm.as_subu(ScratchRegister, ScratchRegister, value);
break;
case AtomicOp::And:
masm.as_and(ScratchRegister, ScratchRegister, value);
break;
case AtomicOp::Or:
masm.as_or(ScratchRegister, ScratchRegister, value);
break;
case AtomicOp::Xor:
masm.as_xor(ScratchRegister, ScratchRegister, value);
break;
default:
MOZ_CRASH();
}
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
return;
}
masm.as_andi(offsetTemp, SecondScratchReg, 3);
masm.subPtr(offsetTemp, SecondScratchReg);
#if !MOZ_LITTLE_ENDIAN()
masm.as_xori(offsetTemp, offsetTemp, 3);
#endif
masm.as_sll(offsetTemp, offsetTemp, 3);
masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
masm.as_sllv(maskTemp, maskTemp, offsetTemp);
masm.as_nor(maskTemp, zero, maskTemp);
masm.memoryBarrierBefore(sync);
masm.bind(&again);
if (access) {
masm.append(*access, masm.size());
}
masm.as_ll(ScratchRegister, SecondScratchReg, 0);
masm.as_srlv(valueTemp, ScratchRegister, offsetTemp);
switch (op) {
case AtomicOp::Add:
masm.as_addu(valueTemp, valueTemp, value);
break;
case AtomicOp::Sub:
masm.as_subu(valueTemp, valueTemp, value);
break;
case AtomicOp::And:
masm.as_and(valueTemp, valueTemp, value);
break;
case AtomicOp::Or:
masm.as_or(valueTemp, valueTemp, value);
break;
case AtomicOp::Xor:
masm.as_xor(valueTemp, valueTemp, value);
break;
default:
MOZ_CRASH();
}
switch (nbytes) {
case 1:
masm.as_andi(valueTemp, valueTemp, 0xff);
break;
case 2:
masm.as_andi(valueTemp, valueTemp, 0xffff);
break;
}
masm.as_sllv(valueTemp, valueTemp, offsetTemp);
masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
masm.as_sc(ScratchRegister, SecondScratchReg, 0);
masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero,
ShortJump);
masm.memoryBarrierAfter(sync);
}
void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
AtomicOp op, Register value,
const Address& mem, Register valueTemp,
Register offsetTemp,
Register maskTemp) {
AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value,
valueTemp, offsetTemp, maskTemp);
}
void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
AtomicOp op, Register value,
const BaseIndex& mem,
Register valueTemp, Register offsetTemp,
Register maskTemp) {
AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value,
valueTemp, offsetTemp, maskTemp);
}
// ========================================================================
// JS atomic operations.
template <typename T>
static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
Synchronization sync, const T& mem,
Register oldval, Register newval,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
if (arrayType == Scalar::Uint32) {
masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, temp);
masm.convertUInt32ToDouble(temp, output.fpu());
} else {
masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, output.gpr());
}
}
void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
Synchronization sync, const Address& mem,
Register oldval, Register newval,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, temp, output);
}
void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
Synchronization sync,
const BaseIndex& mem, Register oldval,
Register newval, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register temp, AnyRegister output) {
CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp,
offsetTemp, maskTemp, temp, output);
}
template <typename T>
static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
Synchronization sync, const T& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
if (arrayType == Scalar::Uint32) {
masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp,
maskTemp, temp);
masm.convertUInt32ToDouble(temp, output.fpu());
} else {
masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp,
maskTemp, output.gpr());
}
}
void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
Synchronization sync, const Address& mem,
Register value, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register temp, AnyRegister output) {
AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp,
maskTemp, temp, output);
}
void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
Synchronization sync,
const BaseIndex& mem, Register value,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp,
maskTemp, temp, output);
}
template <typename T>
static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType,
Synchronization sync, AtomicOp op, Register value,
const T& mem, Register valueTemp,
Register offsetTemp, Register maskTemp,
Register temp, AnyRegister output) {
if (arrayType == Scalar::Uint32) {
masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp,
maskTemp, temp);
masm.convertUInt32ToDouble(temp, output.fpu());
} else {
masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp,
maskTemp, output.gpr());
}
}
void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
Synchronization sync, AtomicOp op,
Register value, const Address& mem,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp,
maskTemp, temp, output);
}
void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
Synchronization sync, AtomicOp op,
Register value, const BaseIndex& mem,
Register valueTemp, Register offsetTemp,
Register maskTemp, Register temp,
AnyRegister output) {
AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp,
maskTemp, temp, output);
}
void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
Synchronization sync, AtomicOp op,
Register value, const BaseIndex& mem,
Register valueTemp, Register offsetTemp,
Register maskTemp) {
AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp,
offsetTemp, maskTemp);
}
void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
Synchronization sync, AtomicOp op,
Register value, const Address& mem,
Register valueTemp, Register offsetTemp,
Register maskTemp) {
AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp,
offsetTemp, maskTemp);
}
void MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest,
bool isUnsigned,
const LiveRegisterSet&) {
quotient32(rhs, srcDest, isUnsigned);
}
void MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest,
bool isUnsigned,
const LiveRegisterSet&) {
remainder32(rhs, srcDest, isUnsigned);
}
void MacroAssembler::flexibleDivMod32(Register rhs, Register srcDest,
Register remOutput, bool isUnsigned,
const LiveRegisterSet&) {
if (isUnsigned) {
#ifdef MIPSR6
as_divu(ScratchRegister, srcDest, rhs);
as_modu(remOutput, srcDest, rhs);
ma_move(srcDest, ScratchRegister);
#else
as_divu(srcDest, rhs);
#endif
} else {
#ifdef MIPSR6
as_div(ScratchRegister, srcDest, rhs);
as_mod(remOutput, srcDest, rhs);
ma_move(srcDest, ScratchRegister);
#else
as_div(srcDest, rhs);
#endif
}
#ifndef MIPSR6
as_mfhi(remOutput);
as_mflo(srcDest);
#endif
}
CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) {
return movWithPatch(ImmPtr(nullptr), dest);
}
void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc,
CodeLocationLabel target) {
PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr));
}
// ========================================================================
// Spectre Mitigations.
void MacroAssembler::speculationBarrier() { MOZ_CRASH(); }
void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest,
Label* fail) {
ScratchFloat32Scope scratch(*this);
Label skipCheck, done;
// If Nan, 0 or -0 check for bailout
loadConstantFloat32(0.0f, scratch);
ma_bc1s(src, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
// If binary value is not zero, it is NaN or -0, so we bail.
moveFromDoubleLo(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&done, ShortJump);
bind(&skipCheck);
as_floorws(scratch, src);
moveFromDoubleLo(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branch32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
bind(&done);
}
void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest,
Label* fail) {
ScratchDoubleScope scratch(*this);
Label skipCheck, done;
// If Nan, 0 or -0 check for bailout
loadConstantDouble(0.0, scratch);
ma_bc1d(src, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
// If high part is not zero, it is NaN or -0, so we bail.
moveFromDoubleHi(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&done, ShortJump);
bind(&skipCheck);
as_floorwd(scratch, src);
moveFromDoubleLo(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branch32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
bind(&done);
}
void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest,
Label* fail) {
ScratchFloat32Scope scratch(*this);
Label performCeil, done;
// If x < -1 or x > 0 then perform ceil.
loadConstantFloat32(0.0f, scratch);
branchFloat(Assembler::DoubleGreaterThan, src, scratch, &performCeil);
loadConstantFloat32(-1.0f, scratch);
branchFloat(Assembler::DoubleLessThanOrEqual, src, scratch, &performCeil);
// If binary value is not zero, the input was not 0, so we bail.
moveFromFloat32(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&done, ShortJump);
bind(&performCeil);
as_ceilws(scratch, src);
moveFromFloat32(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branch32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
bind(&done);
}
void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest,
Label* fail) {
ScratchDoubleScope scratch(*this);
Label performCeil, done;
// If x < -1 or x > 0 then perform ceil.
loadConstantDouble(0, scratch);
branchDouble(Assembler::DoubleGreaterThan, src, scratch, &performCeil);
loadConstantDouble(-1, scratch);
branchDouble(Assembler::DoubleLessThanOrEqual, src, scratch, &performCeil);
// If high part is not zero, the input was not 0, so we bail.
moveFromDoubleHi(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&done, ShortJump);
bind(&performCeil);
as_ceilwd(scratch, src);
moveFromDoubleLo(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branch32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
bind(&done);
}
void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest,
FloatRegister temp, Label* fail) {
ScratchFloat32Scope scratch(*this);
Label negative, end, skipCheck;
// Load biggest number less than 0.5 in the temp register.
loadConstantFloat32(GetBiggestNumberLessThan(0.5f), temp);
// Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
loadConstantFloat32(0.0f, scratch);
ma_bc1s(src, scratch, &negative, Assembler::DoubleLessThan, ShortJump);
// If Nan, 0 or -0 check for bailout
ma_bc1s(src, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
// If binary value is not zero, it is NaN or -0, so we bail.
moveFromFloat32(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&end, ShortJump);
bind(&skipCheck);
as_adds(scratch, src, temp);
as_floorws(scratch, scratch);
moveFromFloat32(scratch, dest);
branchTest32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branchTest32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
jump(&end);
// Input is negative, but isn't -0.
bind(&negative);
// Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to
// be added the biggest double less than 0.5.
Label loadJoin;
loadConstantFloat32(-0.5f, scratch);
branchFloat(Assembler::DoubleLessThan, src, scratch, &loadJoin);
loadConstantFloat32(0.5f, temp);
bind(&loadJoin);
as_adds(temp, src, temp);
// If input + 0.5 >= 0, input is a negative number >= -0.5 and the
// result is -0.
branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, fail);
// Truncate and round toward zero.
// This is off-by-one for everything but integer-valued inputs.
as_floorws(scratch, temp);
moveFromFloat32(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
bind(&end);
}
void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest,
FloatRegister temp, Label* fail) {
ScratchDoubleScope scratch(*this);
Label negative, end, skipCheck;
// Load biggest number less than 0.5 in the temp register.
loadConstantDouble(GetBiggestNumberLessThan(0.5), temp);
// Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
loadConstantDouble(0.0, scratch);
ma_bc1d(src, scratch, &negative, Assembler::DoubleLessThan, ShortJump);
// If Nan, 0 or -0 check for bailout
ma_bc1d(src, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
// If high part is not zero, it is NaN or -0, so we bail.
moveFromDoubleHi(src, SecondScratchReg);
branch32(Assembler::NotEqual, SecondScratchReg, Imm32(0), fail);
// Input was zero, so return zero.
move32(Imm32(0), dest);
ma_b(&end, ShortJump);
bind(&skipCheck);
as_addd(scratch, src, temp);
as_floorwd(scratch, scratch);
moveFromDoubleLo(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
branch32(Assembler::Equal, dest, Imm32(INT_MAX), fail);
jump(&end);
// Input is negative, but isn't -0.
bind(&negative);
// Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to
// be added the biggest double less than 0.5.
Label loadJoin;
loadConstantDouble(-0.5, scratch);
branchDouble(Assembler::DoubleLessThan, src, scratch, &loadJoin);
loadConstantDouble(0.5, temp);
bind(&loadJoin);
addDouble(src, temp);
// If input + 0.5 >= 0, input is a negative number >= -0.5 and the
// result is -0.
branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, fail);
// Truncate and round toward zero.
// This is off-by-one for everything but integer-valued inputs.
as_floorwd(scratch, temp);
moveFromDoubleLo(scratch, dest);
branch32(Assembler::Equal, dest, Imm32(INT_MIN), fail);
bind(&end);
}
void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest,
Label* fail) {
Label notZero;
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(dest, Imm32(0), &notZero, Assembler::NotEqual, ShortJump);
moveFromFloat32(src, ScratchRegister);
// Check if src is in ]-1; -0] range by checking the sign bit.
as_slt(ScratchRegister, ScratchRegister, zero);
bind(&notZero);
branch32(Assembler::NotEqual, ScratchRegister, Imm32(0), fail);
}
void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest,
Label* fail) {
Label notZero;
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseV, 1);
ma_b(dest, Imm32(0), &notZero, Assembler::NotEqual, ShortJump);
moveFromDoubleHi(src, ScratchRegister);
// Check if src is in ]-1; -0] range by checking the sign bit.
as_slt(ScratchRegister, ScratchRegister, zero);
bind(&notZero);
branch32(Assembler::NotEqual, ScratchRegister, Imm32(0), fail);
}
void MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src,
FloatRegister dest) {
MOZ_CRASH("not supported on this platform");
}
void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src,
FloatRegister dest) {
MOZ_CRASH("not supported on this platform");
}
void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs,
FloatRegister output) {
MOZ_CRASH("not supported on this platform");
}
void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift,
Register pointer) {
if (IsShiftInScaleRange(shift)) {
computeEffectiveAddress(
BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer);
return;
}
lshift32(Imm32(shift), indexTemp32);
addPtr(indexTemp32, pointer);
}
//}}} check_macroassembler_style