1079 lines
29 KiB
C++
1079 lines
29 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef jit_mips32_MacroAssembler_mips32_inl_h
|
|
#define jit_mips32_MacroAssembler_mips32_inl_h
|
|
|
|
#include "jit/mips32/MacroAssembler-mips32.h"
|
|
|
|
#include "jit/mips-shared/MacroAssembler-mips-shared-inl.h"
|
|
|
|
namespace js {
|
|
namespace jit {
|
|
|
|
//{{{ check_macroassembler_style
|
|
|
|
void
|
|
MacroAssembler::move64(Register64 src, Register64 dest)
|
|
{
|
|
move32(src.low, dest.low);
|
|
move32(src.high, dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::move64(Imm64 imm, Register64 dest)
|
|
{
|
|
move32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
|
|
move32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Logical instructions
|
|
|
|
void
|
|
MacroAssembler::andPtr(Register src, Register dest)
|
|
{
|
|
ma_and(dest, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::andPtr(Imm32 imm, Register dest)
|
|
{
|
|
ma_and(dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
|
{
|
|
if (imm.low().value != int32_t(0xFFFFFFFF))
|
|
and32(imm.low(), dest.low);
|
|
if (imm.hi().value != int32_t(0xFFFFFFFF))
|
|
and32(imm.hi(), dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::and64(Register64 src, Register64 dest)
|
|
{
|
|
and32(src.low, dest.low);
|
|
and32(src.high, dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
|
{
|
|
if (imm.low().value)
|
|
or32(imm.low(), dest.low);
|
|
if (imm.hi().value)
|
|
or32(imm.hi(), dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::xor64(Imm64 imm, Register64 dest)
|
|
{
|
|
if (imm.low().value)
|
|
xor32(imm.low(), dest.low);
|
|
if (imm.hi().value)
|
|
xor32(imm.hi(), dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::orPtr(Register src, Register dest)
|
|
{
|
|
ma_or(dest, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::orPtr(Imm32 imm, Register dest)
|
|
{
|
|
ma_or(dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::or64(Register64 src, Register64 dest)
|
|
{
|
|
or32(src.low, dest.low);
|
|
or32(src.high, dest.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::xor64(Register64 src, Register64 dest)
|
|
{
|
|
ma_xor(dest.low, src.low);
|
|
ma_xor(dest.high, src.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::xorPtr(Register src, Register dest)
|
|
{
|
|
ma_xor(dest, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
|
{
|
|
ma_xor(dest, imm);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Arithmetic functions
|
|
|
|
void
|
|
MacroAssembler::addPtr(Register src, Register dest)
|
|
{
|
|
ma_addu(dest, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::addPtr(Imm32 imm, Register dest)
|
|
{
|
|
ma_addu(dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::addPtr(ImmWord imm, Register dest)
|
|
{
|
|
addPtr(Imm32(imm.value), dest);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::add64(Register64 src, Register64 dest)
|
|
{
|
|
as_addu(dest.low, dest.low, src.low);
|
|
as_sltu(ScratchRegister, dest.low, src.low);
|
|
as_addu(dest.high, dest.high, src.high);
|
|
as_addu(dest.high, dest.high, ScratchRegister);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
|
{
|
|
ma_li(ScratchRegister, imm);
|
|
as_addu(dest.low, dest.low, ScratchRegister);
|
|
as_sltu(ScratchRegister, dest.low, ScratchRegister);
|
|
as_addu(dest.high, dest.high, ScratchRegister);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::add64(Imm64 imm, Register64 dest)
|
|
{
|
|
add64(imm.low(), dest);
|
|
ma_addu(dest.high, dest.high, imm.hi());
|
|
}
|
|
|
|
void
|
|
MacroAssembler::subPtr(Register src, Register dest)
|
|
{
|
|
as_subu(dest, dest, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::subPtr(Imm32 imm, Register dest)
|
|
{
|
|
ma_subu(dest, dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::sub64(Register64 src, Register64 dest)
|
|
{
|
|
as_sltu(ScratchRegister, dest.low, src.low);
|
|
as_subu(dest.high, dest.high, ScratchRegister);
|
|
as_subu(dest.low, dest.low, src.low);
|
|
as_subu(dest.high, dest.high, src.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::sub64(Imm64 imm, Register64 dest)
|
|
{
|
|
ma_li(ScratchRegister, imm.low());
|
|
as_sltu(ScratchRegister, dest.low, ScratchRegister);
|
|
as_subu(dest.high, dest.high, ScratchRegister);
|
|
ma_subu(dest.low, dest.low, imm.low());
|
|
ma_subu(dest.high, dest.high, imm.hi());
|
|
}
|
|
|
|
void
|
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
|
{
|
|
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
|
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
|
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
|
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
|
|
|
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
|
ma_li(ScratchRegister, Imm32(imm.value & LOW_32_MASK));
|
|
as_multu(dest.high, ScratchRegister);
|
|
as_mflo(dest.high);
|
|
|
|
// mfhi:mflo = LOW(dest) * LOW(imm);
|
|
as_multu(dest.low, ScratchRegister);
|
|
|
|
// HIGH(dest) += mfhi;
|
|
as_mfhi(ScratchRegister);
|
|
as_addu(dest.high, dest.high, ScratchRegister);
|
|
|
|
if (((imm.value >> 32) & LOW_32_MASK) == 5) {
|
|
// Optimized case for Math.random().
|
|
|
|
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
|
as_sll(ScratchRegister, dest.low, 2);
|
|
as_addu(ScratchRegister, ScratchRegister, dest.low);
|
|
as_addu(dest.high, dest.high, ScratchRegister);
|
|
|
|
// LOW(dest) = mflo;
|
|
as_mflo(dest.low);
|
|
} else {
|
|
// tmp = mflo
|
|
as_mflo(SecondScratchReg);
|
|
|
|
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
|
ma_li(ScratchRegister, Imm32((imm.value >> 32) & LOW_32_MASK));
|
|
as_multu(dest.low, ScratchRegister);
|
|
as_mflo(ScratchRegister);
|
|
as_addu(dest.high, dest.high, ScratchRegister);
|
|
|
|
// LOW(dest) = tmp;
|
|
ma_move(dest.low, SecondScratchReg);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
|
|
{
|
|
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
|
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
|
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
|
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
|
|
|
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
|
MOZ_ASSERT(temp != dest.high && temp != dest.low);
|
|
|
|
ma_li(ScratchRegister, imm.firstHalf());
|
|
as_multu(dest.high, ScratchRegister);
|
|
as_mflo(dest.high);
|
|
|
|
ma_li(ScratchRegister, imm.secondHalf());
|
|
as_multu(dest.low, ScratchRegister);
|
|
as_mflo(temp);
|
|
as_addu(temp, dest.high, temp);
|
|
|
|
ma_li(ScratchRegister, imm.firstHalf());
|
|
as_multu(dest.low, ScratchRegister);
|
|
as_mfhi(dest.high);
|
|
as_mflo(dest.low);
|
|
as_addu(dest.high, dest.high, temp);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
|
|
{
|
|
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
|
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
|
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
|
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
|
|
|
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
|
MOZ_ASSERT(dest != src);
|
|
MOZ_ASSERT(dest.low != src.high && dest.high != src.low);
|
|
|
|
as_multu(dest.high, src.low); // (2)
|
|
as_mflo(dest.high);
|
|
as_multu(dest.low, src.high); // (3)
|
|
as_mflo(temp);
|
|
as_addu(temp, dest.high, temp);
|
|
as_multu(dest.low, src.low); // (4) + (1)
|
|
as_mfhi(dest.high);
|
|
as_mflo(dest.low);
|
|
as_addu(dest.high, dest.high, temp);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::neg64(Register64 reg)
|
|
{
|
|
ma_li(ScratchRegister, Imm32(1));
|
|
as_movz(ScratchRegister, zero, reg.low);
|
|
ma_negu(reg.low, reg.low);
|
|
as_addu(reg.high, reg.high, ScratchRegister);
|
|
ma_negu(reg.high, reg.high);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::mulBy3(Register src, Register dest)
|
|
{
|
|
MOZ_ASSERT(src != ScratchRegister);
|
|
as_addu(ScratchRegister, src, src);
|
|
as_addu(dest, ScratchRegister, src);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::inc64(AbsoluteAddress dest)
|
|
{
|
|
ma_li(ScratchRegister, Imm32((int32_t)dest.addr));
|
|
as_lw(SecondScratchReg, ScratchRegister, 0);
|
|
|
|
as_addiu(SecondScratchReg, SecondScratchReg, 1);
|
|
as_sw(SecondScratchReg, ScratchRegister, 0);
|
|
|
|
as_sltiu(SecondScratchReg, SecondScratchReg, 1);
|
|
as_lw(ScratchRegister, ScratchRegister, 4);
|
|
|
|
as_addu(SecondScratchReg, ScratchRegister, SecondScratchReg);
|
|
|
|
ma_li(ScratchRegister, Imm32((int32_t)dest.addr));
|
|
as_sw(SecondScratchReg, ScratchRegister, 4);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Shift functions
|
|
|
|
void
|
|
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 32);
|
|
ma_sll(dest, dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
|
ScratchRegisterScope scratch(*this);
|
|
|
|
if (imm.value == 0) {
|
|
return;
|
|
} else if (imm.value < 32) {
|
|
as_sll(dest.high, dest.high, imm.value);
|
|
as_srl(scratch, dest.low, 32 - imm.value);
|
|
as_or(dest.high, dest.high, scratch);
|
|
as_sll(dest.low, dest.low, imm.value);
|
|
} else {
|
|
as_sll(dest.high, dest.low, imm.value - 32);
|
|
move32(Imm32(0), dest.low);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::lshift64(Register unmaskedShift, Register64 dest)
|
|
{
|
|
Label done, less;
|
|
ScratchRegisterScope shift(*this);
|
|
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
ma_b(shift, Imm32(0), &done, Equal);
|
|
|
|
ma_sll(dest.high, dest.high, shift);
|
|
ma_subu(shift, shift, Imm32(32));
|
|
ma_b(shift, Imm32(0), &less, LessThan);
|
|
|
|
ma_sll(dest.high, dest.low, shift);
|
|
move32(Imm32(0), dest.low);
|
|
ma_b(&done);
|
|
|
|
bind(&less);
|
|
ma_li(SecondScratchReg, Imm32(0));
|
|
as_subu(shift, SecondScratchReg, shift);
|
|
ma_srl(SecondScratchReg, dest.low, shift);
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
ma_sll(dest.low, dest.low, shift);
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 32);
|
|
ma_srl(dest, dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 32);
|
|
ma_sra(dest, dest, imm);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
|
ScratchRegisterScope scratch(*this);
|
|
|
|
if (imm.value < 32) {
|
|
as_srl(dest.low, dest.low, imm.value);
|
|
as_sll(scratch, dest.high, 32 - imm.value);
|
|
as_or(dest.low, dest.low, scratch);
|
|
as_srl(dest.high, dest.high, imm.value);
|
|
} else if (imm.value == 32) {
|
|
ma_move(dest.low, dest.high);
|
|
move32(Imm32(0), dest.high);
|
|
} else {
|
|
ma_srl(dest.low, dest.high, Imm32(imm.value - 32));
|
|
move32(Imm32(0), dest.high);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshift64(Register unmaskedShift, Register64 dest)
|
|
{
|
|
Label done, less;
|
|
ScratchRegisterScope shift(*this);
|
|
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
ma_srl(dest.low, dest.low, shift);
|
|
ma_subu(shift, shift, Imm32(32));
|
|
ma_b(shift, Imm32(0), &less, LessThan);
|
|
|
|
ma_srl(dest.low, dest.high, shift);
|
|
move32(Imm32(0), dest.high);
|
|
ma_b(&done);
|
|
|
|
bind(&less);
|
|
ma_li(SecondScratchReg, Imm32(0));
|
|
as_subu(shift, SecondScratchReg, shift);
|
|
ma_sll(SecondScratchReg, dest.high, shift);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
ma_srl(dest.high, dest.high, shift);
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
|
{
|
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
|
ScratchRegisterScope scratch(*this);
|
|
|
|
if (imm.value < 32) {
|
|
as_srl(dest.low, dest.low, imm.value);
|
|
as_sll(scratch, dest.high, 32 - imm.value);
|
|
as_or(dest.low, dest.low, scratch);
|
|
as_sra(dest.high, dest.high, imm.value);
|
|
} else if (imm.value == 32) {
|
|
ma_move(dest.low, dest.high);
|
|
as_sra(dest.high, dest.high, 31);
|
|
} else {
|
|
as_sra(dest.low, dest.high, imm.value - 32);
|
|
as_sra(dest.high, dest.high, 31);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rshift64Arithmetic(Register unmaskedShift, Register64 dest)
|
|
{
|
|
Label done, less;
|
|
|
|
ScratchRegisterScope shift(*this);
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
|
|
ma_srl(dest.low, dest.low, shift);
|
|
ma_subu(shift, shift, Imm32(32));
|
|
ma_b(shift, Imm32(0), &less, LessThan);
|
|
|
|
ma_sra(dest.low, dest.high, shift);
|
|
as_sra(dest.high, dest.high, 31);
|
|
ma_b(&done);
|
|
|
|
bind(&less);
|
|
ma_li(SecondScratchReg, Imm32(0));
|
|
as_subu(shift, SecondScratchReg, shift);
|
|
ma_sll(SecondScratchReg, dest.high, shift);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
|
ma_sra(dest.high, dest.high, shift);
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Rotation functions
|
|
|
|
void
|
|
MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
|
{
|
|
MOZ_ASSERT(temp == InvalidReg);
|
|
MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
|
|
|
|
int32_t amount = count.value & 0x3f;
|
|
if (amount > 32) {
|
|
rotateRight64(Imm32(64 - amount), input, dest, temp);
|
|
} else {
|
|
ScratchRegisterScope scratch(*this);
|
|
if (amount == 0) {
|
|
ma_move(dest.low, input.low);
|
|
ma_move(dest.high, input.high);
|
|
} else if (amount == 32) {
|
|
ma_move(scratch, input.low);
|
|
ma_move(dest.low, input.high);
|
|
ma_move(dest.high, scratch);
|
|
} else {
|
|
MOZ_ASSERT(0 < amount && amount < 32);
|
|
ma_move(scratch, input.high);
|
|
ma_sll(dest.high, input.high, Imm32(amount));
|
|
ma_srl(SecondScratchReg, input.low, Imm32(32 - amount));
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
ma_sll(dest.low, input.low, Imm32(amount));
|
|
ma_srl(SecondScratchReg, scratch, Imm32(32 - amount));
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rotateLeft64(Register shift, Register64 src, Register64 dest, Register temp)
|
|
{
|
|
MOZ_ASSERT(temp != src.low && temp != src.high);
|
|
MOZ_ASSERT(shift != src.low && shift != src.high);
|
|
MOZ_ASSERT(temp != InvalidReg);
|
|
|
|
ScratchRegisterScope shift_value(*this);
|
|
Label high, done, zero;
|
|
|
|
ma_and(temp, shift, Imm32(0x3f));
|
|
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
|
|
|
|
// high = high << shift | low >> 32 - shift
|
|
// low = low << shift | high >> 32 - shift
|
|
ma_sll(dest.high, src.high, temp);
|
|
ma_b(temp, Imm32(0), &zero, Equal);
|
|
ma_li(SecondScratchReg, Imm32(32));
|
|
as_subu(shift_value, SecondScratchReg, temp);
|
|
|
|
ma_srl(SecondScratchReg, src.low, shift_value);
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
|
|
ma_sll(dest.low, src.low, temp);
|
|
ma_srl(SecondScratchReg, src.high, shift_value);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
ma_b(&done);
|
|
|
|
bind(&zero);
|
|
ma_move(dest.low, src.low);
|
|
ma_move(dest.high, src.high);
|
|
ma_b(&done);
|
|
|
|
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
|
|
bind(&high);
|
|
ma_and(shift, shift, Imm32(0x3f));
|
|
ma_li(SecondScratchReg, Imm32(64));
|
|
as_subu(temp, SecondScratchReg, shift);
|
|
|
|
ma_srl(dest.high, src.high, temp);
|
|
ma_li(SecondScratchReg, Imm32(32));
|
|
as_subu(shift_value, SecondScratchReg, temp);
|
|
ma_sll(SecondScratchReg, src.low, shift_value);
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
|
|
ma_srl(dest.low, src.low, temp);
|
|
ma_sll(SecondScratchReg, src.high, shift_value);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
|
{
|
|
MOZ_ASSERT(temp == InvalidReg);
|
|
MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
|
|
|
|
int32_t amount = count.value & 0x3f;
|
|
if (amount > 32) {
|
|
rotateLeft64(Imm32(64 - amount), input, dest, temp);
|
|
} else {
|
|
ScratchRegisterScope scratch(*this);
|
|
if (amount == 0) {
|
|
ma_move(dest.low, input.low);
|
|
ma_move(dest.high, input.high);
|
|
} else if (amount == 32) {
|
|
ma_move(scratch, input.low);
|
|
ma_move(dest.low, input.high);
|
|
ma_move(dest.high, scratch);
|
|
} else {
|
|
MOZ_ASSERT(0 < amount && amount < 32);
|
|
ma_move(scratch, input.high);
|
|
ma_srl(dest.high, input.high, Imm32(amount));
|
|
ma_sll(SecondScratchReg, input.low, Imm32(32 - amount));
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
ma_srl(dest.low, input.low, Imm32(amount));
|
|
ma_sll(SecondScratchReg, scratch, Imm32(32 - amount));
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::rotateRight64(Register shift, Register64 src, Register64 dest, Register temp)
|
|
{
|
|
MOZ_ASSERT(temp != src.low && temp != src.high);
|
|
MOZ_ASSERT(shift != src.low && shift != src.high);
|
|
MOZ_ASSERT(temp != InvalidReg);
|
|
|
|
ScratchRegisterScope shift_value(*this);
|
|
Label high, done, zero;
|
|
|
|
ma_and(temp, shift, Imm32(0x3f));
|
|
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
|
|
|
|
// high = high >> shift | low << 32 - shift
|
|
// low = low >> shift | high << 32 - shift
|
|
ma_srl(dest.high, src.high, temp);
|
|
ma_b(temp, Imm32(0), &zero, Equal);
|
|
ma_li(SecondScratchReg, Imm32(32));
|
|
as_subu(shift_value, SecondScratchReg, temp);
|
|
|
|
ma_sll(SecondScratchReg, src.low, shift_value);
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
|
|
ma_srl(dest.low, src.low, temp);
|
|
|
|
//ma_li(SecondScratchReg, Imm32(32));
|
|
//as_subu(shift_value, SecondScratchReg, shift_value);
|
|
ma_sll(SecondScratchReg, src.high, shift_value);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
|
|
ma_b(&done);
|
|
|
|
bind(&zero);
|
|
ma_move(dest.low, src.low);
|
|
ma_move(dest.high, src.high);
|
|
ma_b(&done);
|
|
|
|
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
|
|
bind(&high);
|
|
ma_and(shift, shift, Imm32(0x3f));
|
|
ma_li(SecondScratchReg, Imm32(64));
|
|
as_subu(temp, SecondScratchReg, shift);
|
|
|
|
ma_sll(dest.high, src.high, temp);
|
|
ma_li(SecondScratchReg, Imm32(32));
|
|
as_subu(shift_value, SecondScratchReg, temp);
|
|
|
|
ma_srl(SecondScratchReg, src.low, shift_value);
|
|
as_or(dest.high, dest.high, SecondScratchReg);
|
|
|
|
ma_sll(dest.low, src.low, temp);
|
|
ma_srl(SecondScratchReg, src.high, shift_value);
|
|
as_or(dest.low, dest.low, SecondScratchReg);
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
void
|
|
MacroAssembler::cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest)
|
|
{
|
|
ma_cmp_set(dest, lhs, rhs, cond);
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
void
|
|
MacroAssembler::cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest)
|
|
{
|
|
ma_cmp_set(dest, lhs, rhs, cond);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Bit counting functions
|
|
|
|
void
|
|
MacroAssembler::clz64(Register64 src, Register dest)
|
|
{
|
|
Label done, low;
|
|
|
|
ma_b(src.high, Imm32(0), &low, Equal);
|
|
as_clz(dest, src.high);
|
|
ma_b(&done);
|
|
|
|
bind(&low);
|
|
as_clz(dest, src.low);
|
|
ma_addu(dest, Imm32(32));
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::ctz64(Register64 src, Register dest)
|
|
{
|
|
Label done, high;
|
|
|
|
ma_b(src.low, Imm32(0), &high, Equal);
|
|
|
|
ma_ctz(dest, src.low);
|
|
ma_b(&done);
|
|
|
|
bind(&high);
|
|
ma_ctz(dest, src.high);
|
|
ma_addu(dest, Imm32(32));
|
|
|
|
bind(&done);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::popcnt64(Register64 src, Register64 dest, Register tmp)
|
|
{
|
|
MOZ_ASSERT(dest.low != tmp);
|
|
MOZ_ASSERT(dest.high != tmp);
|
|
MOZ_ASSERT(dest.low != dest.high);
|
|
|
|
if (dest.low != src.high) {
|
|
popcnt32(src.low, dest.low, tmp);
|
|
popcnt32(src.high, dest.high, tmp);
|
|
} else {
|
|
MOZ_ASSERT(dest.high != src.high);
|
|
popcnt32(src.low, dest.high, tmp);
|
|
popcnt32(src.high, dest.low, tmp);
|
|
}
|
|
|
|
ma_addu(dest.low, dest.high);
|
|
move32(Imm32(0), dest.high);
|
|
}
|
|
|
|
// ===============================================================
|
|
// Branch functions
|
|
|
|
void
|
|
MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
|
|
{
|
|
MOZ_ASSERT(cond == Assembler::NotEqual,
|
|
"other condition codes not supported");
|
|
|
|
branch32(cond, lhs, val.firstHalf(), label);
|
|
branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), val.secondHalf(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
|
|
Label* label)
|
|
{
|
|
MOZ_ASSERT(cond == Assembler::NotEqual,
|
|
"other condition codes not supported");
|
|
MOZ_ASSERT(lhs.base != scratch);
|
|
MOZ_ASSERT(rhs.base != scratch);
|
|
|
|
load32(rhs, scratch);
|
|
branch32(cond, lhs, scratch, label);
|
|
|
|
load32(Address(rhs.base, rhs.offset + sizeof(uint32_t)), scratch);
|
|
branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, Label* success, Label* fail)
|
|
{
|
|
bool fallthrough = false;
|
|
Label fallthroughLabel;
|
|
|
|
if (!fail) {
|
|
fail = &fallthroughLabel;
|
|
fallthrough = true;
|
|
}
|
|
|
|
switch(cond) {
|
|
case Assembler::Equal:
|
|
branch32(Assembler::NotEqual, lhs.low, val.low(), fail);
|
|
branch32(Assembler::Equal, lhs.high, val.hi(), success);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
case Assembler::NotEqual:
|
|
branch32(Assembler::NotEqual, lhs.low, val.low(), success);
|
|
branch32(Assembler::NotEqual, lhs.high, val.hi(), success);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
case Assembler::LessThan:
|
|
case Assembler::LessThanOrEqual:
|
|
case Assembler::GreaterThan:
|
|
case Assembler::GreaterThanOrEqual:
|
|
case Assembler::Below:
|
|
case Assembler::BelowOrEqual:
|
|
case Assembler::Above:
|
|
case Assembler::AboveOrEqual: {
|
|
Assembler::Condition invert_cond = Assembler::InvertCondition(cond);
|
|
Assembler::Condition cond1 = Assembler::ConditionWithoutEqual(cond);
|
|
Assembler::Condition cond2 = Assembler::ConditionWithoutEqual(invert_cond);
|
|
Assembler::Condition cond3 = Assembler::UnsignedCondition(cond);
|
|
|
|
ma_b(lhs.high, val.hi(), success, cond1);
|
|
ma_b(lhs.high, val.hi(), fail, cond2);
|
|
ma_b(lhs.low, val.low(), success, cond3);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH("Condition code not supported");
|
|
break;
|
|
}
|
|
|
|
if (fallthrough)
|
|
bind(fail);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success, Label* fail)
|
|
{
|
|
bool fallthrough = false;
|
|
Label fallthroughLabel;
|
|
|
|
if (!fail) {
|
|
fail = &fallthroughLabel;
|
|
fallthrough = true;
|
|
}
|
|
|
|
switch(cond) {
|
|
case Assembler::Equal:
|
|
branch32(Assembler::NotEqual, lhs.low, rhs.low, fail);
|
|
branch32(Assembler::Equal, lhs.high, rhs.high, success);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
case Assembler::NotEqual:
|
|
branch32(Assembler::NotEqual, lhs.low, rhs.low, success);
|
|
branch32(Assembler::NotEqual, lhs.high, rhs.high, success);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
case Assembler::LessThan:
|
|
case Assembler::LessThanOrEqual:
|
|
case Assembler::GreaterThan:
|
|
case Assembler::GreaterThanOrEqual:
|
|
case Assembler::Below:
|
|
case Assembler::BelowOrEqual:
|
|
case Assembler::Above:
|
|
case Assembler::AboveOrEqual: {
|
|
Assembler::Condition invert_cond = Assembler::InvertCondition(cond);
|
|
Assembler::Condition cond1 = Assembler::ConditionWithoutEqual(cond);
|
|
Assembler::Condition cond2 = Assembler::ConditionWithoutEqual(invert_cond);
|
|
Assembler::Condition cond3 = Assembler::UnsignedCondition(cond);
|
|
|
|
ma_b(lhs.high, rhs.high, success, cond1);
|
|
ma_b(lhs.high, rhs.high, fail, cond2);
|
|
ma_b(lhs.low, rhs.low, success, cond3);
|
|
if (!fallthrough)
|
|
jump(fail);
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH("Condition code not supported");
|
|
break;
|
|
}
|
|
|
|
if (fallthrough)
|
|
bind(fail);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
|
|
{
|
|
branchPtr(cond, lhs, rhs, label);
|
|
}
|
|
|
|
template <class L>
|
|
void
|
|
MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
|
|
L label)
|
|
{
|
|
if (cond == Assembler::Zero) {
|
|
MOZ_ASSERT(lhs.low == rhs.low);
|
|
MOZ_ASSERT(lhs.high == rhs.high);
|
|
as_or(ScratchRegister, lhs.low, lhs.high);
|
|
branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
|
|
} else {
|
|
MOZ_CRASH("Unsupported condition");
|
|
}
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestUndefined(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestUndefined(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestInt32(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
|
|
{
|
|
ScratchRegisterScope scratch(*this);
|
|
as_and(scratch, value.payloadReg(), value.payloadReg());
|
|
ma_b(scratch, scratch, label, b ? NonZero : Zero);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestDouble(Condition cond, Register tag, Label* label)
|
|
{
|
|
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
Condition actual = (cond == Equal) ? Below : AboveOrEqual;
|
|
ma_b(tag, ImmTag(JSVAL_TAG_CLEAR), label, actual);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestDouble(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestDouble(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestNumber(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestNumber(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestBoolean(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
ma_b(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), label, cond);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestBooleanTruthy(bool b, const ValueOperand& value, Label* label)
|
|
{
|
|
ma_b(value.payloadReg(), value.payloadReg(), label, b ? NonZero : Zero);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestString(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestString(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestStringTruthy(bool b, const ValueOperand& value, Label* label)
|
|
{
|
|
Register string = value.payloadReg();
|
|
SecondScratchRegisterScope scratch2(*this);
|
|
ma_lw(scratch2, Address(string, JSString::offsetOfLength()));
|
|
ma_b(scratch2, Imm32(0), label, b ? NotEqual : Equal);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestSymbol(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestSymbol(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestNull(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestNull(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestObject(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestObject(cond, value.typeReg(), label);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestPrimitive(Condition cond, const ValueOperand& value, Label* label)
|
|
{
|
|
branchTestPrimitive(cond, value.typeReg(), label);
|
|
}
|
|
|
|
template <class L>
|
|
void
|
|
MacroAssembler::branchTestMagic(Condition cond, const ValueOperand& value, L label)
|
|
{
|
|
ma_b(value.typeReg(), ImmTag(JSVAL_TAG_MAGIC), label, cond);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label)
|
|
{
|
|
branchTestMagic(cond, valaddr, label);
|
|
branch32(cond, ToPayload(valaddr), Imm32(why), label);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Memory access primitives.
|
|
void
|
|
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
|
|
{
|
|
ma_sd(src, addr);
|
|
}
|
|
void
|
|
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
|
|
{
|
|
MOZ_ASSERT(addr.offset == 0);
|
|
ma_sd(src, addr);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
|
|
{
|
|
ma_ss(src, addr);
|
|
}
|
|
void
|
|
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
|
|
{
|
|
MOZ_ASSERT(addr.offset == 0);
|
|
ma_ss(src, addr);
|
|
}
|
|
|
|
// ========================================================================
|
|
// wasm support
|
|
|
|
template <class L>
|
|
void
|
|
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
|
|
{
|
|
BufferOffset bo = ma_BoundsCheck(ScratchRegister);
|
|
append(wasm::BoundsCheck(bo.getOffset()));
|
|
|
|
ma_b(index, ScratchRegister, label, cond);
|
|
}
|
|
|
|
void
|
|
MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
|
|
{
|
|
Instruction* inst = (Instruction*) patchAt;
|
|
InstImm* i0 = (InstImm*) inst;
|
|
InstImm* i1 = (InstImm*) i0->next();
|
|
|
|
// Replace with new value
|
|
AssemblerMIPSShared::UpdateLuiOriValue(i0, i1, limit);
|
|
}
|
|
|
|
//}}} check_macroassembler_style
|
|
// ===============================================================
|
|
|
|
void
|
|
MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr)
|
|
{
|
|
asMasm().add32(Imm32(1), ToPayload(addr));
|
|
}
|
|
|
|
void
|
|
MacroAssemblerMIPSCompat::computeEffectiveAddress(const BaseIndex& address, Register dest)
|
|
{
|
|
computeScaledAddress(address, dest);
|
|
if (address.offset)
|
|
asMasm().addPtr(Imm32(address.offset), dest);
|
|
}
|
|
|
|
void
|
|
MacroAssemblerMIPSCompat::retn(Imm32 n) {
|
|
// pc <- [sp]; sp += n
|
|
loadPtr(Address(StackPointer, 0), ra);
|
|
asMasm().addPtr(n, StackPointer);
|
|
as_jr(ra);
|
|
as_nop();
|
|
}
|
|
|
|
} // namespace jit
|
|
} // namespace js
|
|
|
|
#endif /* jit_mips32_MacroAssembler_mips32_inl_h */
|