Source code

Revision control

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/mips32/MacroAssembler-mips32.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "jit/Bailouts.h"
#include "jit/BaselineFrame.h"
#include "jit/JitFrames.h"
#include "jit/JitRuntime.h"
#include "jit/MacroAssembler.h"
#include "jit/mips32/Simulator-mips32.h"
#include "jit/MoveEmitter.h"
#include "jit/SharedICRegisters.h"
#include "util/Memory.h"
#include "vm/JitActivation.h" // js::jit::JitActivation
#include "vm/JSContext.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace jit;
using mozilla::Abs;
static const int32_t PAYLOAD_OFFSET = NUNBOX32_PAYLOAD_OFFSET;
static const int32_t TAG_OFFSET = NUNBOX32_TYPE_OFFSET;
static_assert(sizeof(intptr_t) == 4, "Not 64-bit clean.");
void MacroAssemblerMIPSCompat::convertBoolToInt32(Register src, Register dest) {
// Note that C++ bool is only 1 byte, so zero extend it to clear the
// higher-order bits.
ma_and(dest, src, Imm32(0xff));
}
void MacroAssemblerMIPSCompat::convertInt32ToDouble(Register src,
FloatRegister dest) {
as_mtc1(src, dest);
as_cvtdw(dest, dest);
}
void MacroAssemblerMIPSCompat::convertInt32ToDouble(const Address& src,
FloatRegister dest) {
ma_ls(dest, src);
as_cvtdw(dest, dest);
}
void MacroAssemblerMIPSCompat::convertInt32ToDouble(const BaseIndex& src,
FloatRegister dest) {
computeScaledAddress(src, ScratchRegister);
convertInt32ToDouble(Address(ScratchRegister, src.offset), dest);
}
void MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src,
FloatRegister dest) {
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
const uint32_t kExponentShift =
mozilla::FloatingPoint<double>::kExponentShift - 32;
const uint32_t kExponent =
(31 + mozilla::FloatingPoint<double>::kExponentBias);
ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
ma_or(SecondScratchReg, ScratchRegister);
ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
moveToDoubleHi(SecondScratchReg, dest);
moveToDoubleLo(ScratchRegister, dest);
ma_b(&done, ShortJump);
bind(&positive);
convertInt32ToDouble(src, dest);
bind(&done);
}
void MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src,
FloatRegister dest) {
Label positive, done;
ma_b(src, src, &positive, NotSigned, ShortJump);
const uint32_t kExponentShift =
mozilla::FloatingPoint<double>::kExponentShift - 32;
const uint32_t kExponent =
(31 + mozilla::FloatingPoint<double>::kExponentBias);
ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift);
ma_li(ScratchRegister, Imm32(kExponent << kExponentShift));
ma_or(SecondScratchReg, ScratchRegister);
ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1));
FloatRegister destDouble = dest.asDouble();
moveToDoubleHi(SecondScratchReg, destDouble);
moveToDoubleLo(ScratchRegister, destDouble);
convertDoubleToFloat32(destDouble, dest);
ma_b(&done, ShortJump);
bind(&positive);
convertInt32ToFloat32(src, dest);
bind(&done);
}
void MacroAssemblerMIPSCompat::convertDoubleToFloat32(FloatRegister src,
FloatRegister dest) {
as_cvtsd(dest, src);
}
void MacroAssemblerMIPSCompat::convertDoubleToPtr(FloatRegister src,
Register dest, Label* fail,
bool negativeZeroCheck) {
convertDoubleToInt32(src, dest, fail, negativeZeroCheck);
}
const int CauseBitPos = int(Assembler::CauseI);
const int CauseBitCount = 1 + int(Assembler::CauseV) - int(Assembler::CauseI);
const int CauseIOrVMask = ((1 << int(Assembler::CauseI)) |
(1 << int(Assembler::CauseV))) >>
int(Assembler::CauseI);
// Checks whether a double is representable as a 32-bit integer. If so, the
// integer is written to the output register. Otherwise, a bailout is taken to
// the given snapshot. This function overwrites the scratch float register.
void MacroAssemblerMIPSCompat::convertDoubleToInt32(FloatRegister src,
Register dest, Label* fail,
bool negativeZeroCheck) {
if (negativeZeroCheck) {
moveFromDoubleHi(src, dest);
moveFromDoubleLo(src, SecondScratchReg);
ma_xor(dest, Imm32(INT32_MIN));
ma_or(dest, SecondScratchReg);
ma_b(dest, Imm32(0), fail, Assembler::Equal);
}
// Truncate double to int ; if result is inexact or invalid fail.
as_truncwd(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
// Here adding the masking andi instruction just for a precaution.
// For the instruction of trunc.*.*, the Floating Point Exceptions can be
// only Inexact, Invalid Operation, Unimplemented Operation.
// Leaving it maybe is also ok.
as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
// Checks whether a float32 is representable as a 32-bit integer. If so, the
// integer is written to the output register. Otherwise, a bailout is taken to
// the given snapshot. This function overwrites the scratch float register.
void MacroAssemblerMIPSCompat::convertFloat32ToInt32(FloatRegister src,
Register dest, Label* fail,
bool negativeZeroCheck) {
if (negativeZeroCheck) {
moveFromFloat32(src, dest);
ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
}
as_truncws(ScratchFloat32Reg, src);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, dest);
ma_ext(ScratchRegister, ScratchRegister, CauseBitPos, CauseBitCount);
as_andi(ScratchRegister, ScratchRegister, CauseIOrVMask);
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
}
void MacroAssemblerMIPSCompat::convertFloat32ToDouble(FloatRegister src,
FloatRegister dest) {
as_cvtds(dest, src);
}
void MacroAssemblerMIPSCompat::convertInt32ToFloat32(Register src,
FloatRegister dest) {
as_mtc1(src, dest);
as_cvtsw(dest, dest);
}
void MacroAssemblerMIPSCompat::convertInt32ToFloat32(const Address& src,
FloatRegister dest) {
ma_ls(dest, src);
as_cvtsw(dest, dest);
}
void MacroAssemblerMIPS::ma_li(Register dest, CodeLabel* label) {
BufferOffset bo = m_buffer.nextOffset();
ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
label->patchAt()->bind(bo.getOffset());
label->setLinkMode(CodeLabel::MoveImmediate);
}
void MacroAssemblerMIPS::ma_li(Register dest, ImmWord imm) {
ma_li(dest, Imm32(uint32_t(imm.value)));
}
void MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm) {
ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
}
void MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmWord imm) {
ma_liPatchable(dest, Imm32(int32_t(imm.value)));
}
// Arithmetic-based ops.
// Add.
void MacroAssemblerMIPS::ma_add32TestOverflow(Register rd, Register rs,
Register rt, Label* overflow) {
MOZ_ASSERT_IF(rs == rd, rs != rt);
MOZ_ASSERT(rs != ScratchRegister);
MOZ_ASSERT(rt != ScratchRegister);
MOZ_ASSERT(rd != rt);
MOZ_ASSERT(rd != ScratchRegister);
MOZ_ASSERT(rd != SecondScratchReg);
if (rs == rt) {
as_addu(rd, rs, rs);
as_xor(SecondScratchReg, rs, rd);
ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
return;
}
// If different sign, no overflow
as_xor(ScratchRegister, rs, rt);
as_addu(rd, rs, rt);
as_nor(ScratchRegister, ScratchRegister, zero);
// If different sign, then overflow
as_xor(SecondScratchReg, rt, rd);
as_and(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
}
void MacroAssemblerMIPS::ma_add32TestOverflow(Register rd, Register rs,
Imm32 imm, Label* overflow) {
MOZ_ASSERT(rs != ScratchRegister);
MOZ_ASSERT(rs != SecondScratchReg);
MOZ_ASSERT(rd != ScratchRegister);
MOZ_ASSERT(rd != SecondScratchReg);
Register rs_copy = rs;
if (imm.value > 0) {
as_nor(ScratchRegister, rs, zero);
} else if (rs == rd) {
ma_move(ScratchRegister, rs);
rs_copy = ScratchRegister;
}
if (Imm16::IsInSignedRange(imm.value)) {
as_addiu(rd, rs, imm.value);
} else {
ma_li(SecondScratchReg, imm);
as_addu(rd, rs, SecondScratchReg);
}
if (imm.value > 0) {
as_and(ScratchRegister, ScratchRegister, rd);
} else {
as_nor(SecondScratchReg, rd, zero);
as_and(ScratchRegister, rs_copy, SecondScratchReg);
}
ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan);
}
// Subtract.
void MacroAssemblerMIPS::ma_sub32TestOverflow(Register rd, Register rs,
Register rt, Label* overflow) {
// The rs == rt case should probably be folded at MIR stage.
// Happens for Number_isInteger*. Not worth specializing here.
MOZ_ASSERT_IF(rs == rd, rs != rt);
MOZ_ASSERT(rs != SecondScratchReg);
MOZ_ASSERT(rt != SecondScratchReg);
MOZ_ASSERT(rd != rt);
MOZ_ASSERT(rd != ScratchRegister);
MOZ_ASSERT(rd != SecondScratchReg);
Register rs_copy = rs;
if (rs == rd) {
ma_move(SecondScratchReg, rs);
rs_copy = SecondScratchReg;
}
as_subu(rd, rs, rt);
// If same sign, no overflow
as_xor(ScratchRegister, rs_copy, rt);
// If different sign, then overflow
as_xor(SecondScratchReg, rs_copy, rd);
as_and(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_b(SecondScratchReg, Imm32(0), overflow, Assembler::LessThan);
}
// Memory.
void MacroAssemblerMIPS::ma_load(Register dest, Address address,
LoadStoreSize size,
LoadStoreExtension extension) {
int16_t encodedOffset;
Register base;
if (isLoongson() && ZeroExtend != extension &&
!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
base = address.base;
switch (size) {
case SizeByte:
as_gslbx(dest, base, ScratchRegister, 0);
break;
case SizeHalfWord:
as_gslhx(dest, base, ScratchRegister, 0);
break;
case SizeWord:
as_gslwx(dest, base, ScratchRegister, 0);
break;
case SizeDouble:
as_gsldx(dest, base, ScratchRegister, 0);
break;
default:
MOZ_CRASH("Invalid argument for ma_load");
}
return;
}
if (!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
as_addu(ScratchRegister, address.base, ScratchRegister);
base = ScratchRegister;
encodedOffset = Imm16(0).encode();
} else {
encodedOffset = Imm16(address.offset).encode();
base = address.base;
}
switch (size) {
case SizeByte:
if (ZeroExtend == extension) {
as_lbu(dest, base, encodedOffset);
} else {
as_lb(dest, base, encodedOffset);
}
break;
case SizeHalfWord:
if (ZeroExtend == extension) {
as_lhu(dest, base, encodedOffset);
} else {
as_lh(dest, base, encodedOffset);
}
break;
case SizeWord:
as_lw(dest, base, encodedOffset);
break;
default:
MOZ_CRASH("Invalid argument for ma_load");
}
}
void MacroAssemblerMIPS::ma_store(Register data, Address address,
LoadStoreSize size,
LoadStoreExtension extension) {
int16_t encodedOffset;
Register base;
if (isLoongson() && !Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
base = address.base;
switch (size) {
case SizeByte:
as_gssbx(data, base, ScratchRegister, 0);
break;
case SizeHalfWord:
as_gsshx(data, base, ScratchRegister, 0);
break;
case SizeWord:
as_gsswx(data, base, ScratchRegister, 0);
break;
case SizeDouble:
as_gssdx(data, base, ScratchRegister, 0);
break;
default:
MOZ_CRASH("Invalid argument for ma_store");
}
return;
}
if (!Imm16::IsInSignedRange(address.offset)) {
ma_li(ScratchRegister, Imm32(address.offset));
as_addu(ScratchRegister, address.base, ScratchRegister);
base = ScratchRegister;
encodedOffset = Imm16(0).encode();
} else {
encodedOffset = Imm16(address.offset).encode();
base = address.base;
}
switch (size) {
case SizeByte:
as_sb(data, base, encodedOffset);
break;
case SizeHalfWord:
as_sh(data, base, encodedOffset);
break;
case SizeWord:
as_sw(data, base, encodedOffset);
break;
default:
MOZ_CRASH("Invalid argument for ma_store");
}
}
void MacroAssemblerMIPSCompat::computeScaledAddress(const BaseIndex& address,
Register dest) {
int32_t shift = Imm32::ShiftOf(address.scale).value;
if (shift) {
ma_sll(ScratchRegister, address.index, Imm32(shift));
as_addu(dest, address.base, ScratchRegister);
} else {
as_addu(dest, address.base, address.index);
}
}
// Shortcut for when we know we're transferring 32 bits of data.
void MacroAssemblerMIPS::ma_lw(Register data, Address address) {
ma_load(data, address, SizeWord);
}
void MacroAssemblerMIPS::ma_sw(Register data, Address address) {
ma_store(data, address, SizeWord);
}
void MacroAssemblerMIPS::ma_sw(Imm32 imm, Address address) {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, imm);
if (Imm16::IsInSignedRange(address.offset)) {
as_sw(ScratchRegister, address.base, address.offset);
} else {
MOZ_ASSERT(address.base != SecondScratchReg);
ma_li(SecondScratchReg, Imm32(address.offset));
as_addu(SecondScratchReg, address.base, SecondScratchReg);
as_sw(ScratchRegister, SecondScratchReg, 0);
}
}
void MacroAssemblerMIPS::ma_sw(Register data, BaseIndex& address) {
ma_store(data, address, SizeWord);
}
void MacroAssemblerMIPS::ma_pop(Register r) {
as_lw(r, StackPointer, 0);
as_addiu(StackPointer, StackPointer, sizeof(intptr_t));
}
void MacroAssemblerMIPS::ma_push(Register r) {
if (r == sp) {
// Pushing sp requires one more instruction.
ma_move(ScratchRegister, sp);
r = ScratchRegister;
}
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
as_sw(r, StackPointer, 0);
}
// Branches when done from within mips-specific code.
void MacroAssemblerMIPS::ma_b(Register lhs, Address addr, Label* label,
Condition c, JumpKind jumpKind) {
MOZ_ASSERT(lhs != ScratchRegister);
ma_lw(ScratchRegister, addr);
ma_b(lhs, ScratchRegister, label, c, jumpKind);
}
void MacroAssemblerMIPS::ma_b(Address addr, Imm32 imm, Label* label,
Condition c, JumpKind jumpKind) {
ma_lw(SecondScratchReg, addr);
ma_b(SecondScratchReg, imm, label, c, jumpKind);
}
void MacroAssemblerMIPS::ma_b(Address addr, ImmGCPtr imm, Label* label,
Condition c, JumpKind jumpKind) {
ma_lw(SecondScratchReg, addr);
ma_b(SecondScratchReg, imm, label, c, jumpKind);
}
void MacroAssemblerMIPS::ma_bal(Label* label, DelaySlotFill delaySlotFill) {
spew("branch .Llabel %p\n", label);
if (label->bound()) {
// Generate the long jump for calls because return address has to be
// the address after the reserved block.
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
as_jalr(ScratchRegister);
if (delaySlotFill == FillDelaySlot) {
as_nop();
}
return;
}
// Second word holds a pointer to the next branch in label's chain.
uint32_t nextInChain =
label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
// Make the whole branch continous in the buffer.
m_buffer.ensureSpace(4 * sizeof(uint32_t));
spew("bal .Llabel %p\n", label);
BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
// Leave space for long jump.
as_nop();
if (delaySlotFill == FillDelaySlot) {
as_nop();
}
}
void MacroAssemblerMIPS::branchWithCode(InstImm code, Label* label,
JumpKind jumpKind) {
spew("branch .Llabel %p", label);
MOZ_ASSERT(code.encode() !=
InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
if (label->bound()) {
int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();
if (BOffImm16::IsInRange(offset)) {
jumpKind = ShortJump;
}
if (jumpKind == ShortJump) {
MOZ_ASSERT(BOffImm16::IsInRange(offset));
code.setBOffImm16(BOffImm16(offset));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
writeInst(code.encode());
as_nop();
return;
}
if (code.encode() == inst_beq.encode()) {
// Handle long jump
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
as_jr(ScratchRegister);
as_nop();
return;
}
// Handle long conditional branch
spew("invert branch .Llabel %p", label);
InstImm code_r = invertBranch(code, BOffImm16(5 * sizeof(uint32_t)));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code_r);
#endif
writeInst(code_r.encode());
// No need for a "nop" here because we can clobber scratch.
addLongJump(nextOffset(), BufferOffset(label->offset()));
ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET));
as_jr(ScratchRegister);
as_nop();
return;
}
// Generate open jump and link it to a label.
// Second word holds a pointer to the next branch in label's chain.
uint32_t nextInChain =
label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
if (jumpKind == ShortJump) {
// Make the whole branch continous in the buffer.
m_buffer.ensureSpace(2 * sizeof(uint32_t));
// Indicate that this is short jump with offset 4.
code.setBOffImm16(BOffImm16(4));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
BufferOffset bo = writeInst(code.encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
return;
}
bool conditional = code.encode() != inst_beq.encode();
// Make the whole branch continous in the buffer.
m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t));
#ifdef JS_JITSPEW
decodeBranchInstAndSpew(code);
#endif
BufferOffset bo = writeInst(code.encode());
writeInst(nextInChain);
if (!oom()) {
label->use(bo.getOffset());
}
// Leave space for potential long jump.
as_nop();
as_nop();
if (conditional) {
as_nop();
}
}
void MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs,
Imm64 val, Register dest) {
if (val.value == 0) {
switch (cond) {
case Assembler::Equal:
case Assembler::BelowOrEqual:
as_or(dest, lhs.high, lhs.low);
as_sltiu(dest, dest, 1);
break;
case Assembler::NotEqual:
case Assembler::Above:
as_or(dest, lhs.high, lhs.low);
as_sltu(dest, zero, dest);
break;
case Assembler::LessThan:
case Assembler::GreaterThanOrEqual:
as_slt(dest, lhs.high, zero);
if (cond == Assembler::GreaterThanOrEqual) {
as_xori(dest, dest, 1);
}
break;
case Assembler::GreaterThan:
case Assembler::LessThanOrEqual:
as_or(SecondScratchReg, lhs.high, lhs.low);
as_sra(ScratchRegister, lhs.high, 31);
as_sltu(dest, ScratchRegister, SecondScratchReg);
if (cond == Assembler::LessThanOrEqual) {
as_xori(dest, dest, 1);
}
break;
case Assembler::Below:
case Assembler::AboveOrEqual:
as_ori(dest, zero, cond == Assembler::AboveOrEqual ? 1 : 0);
break;
default:
MOZ_CRASH("Condition code not supported");
break;
}
return;
}
Condition c = ma_cmp64(cond, lhs, val, dest);
switch (cond) {
// For Equal/NotEqual cond ma_cmp64 dest holds non boolean result.
case Assembler::Equal:
as_sltiu(dest, dest, 1);
break;
case Assembler::NotEqual:
as_sltu(dest, zero, dest);
break;
default:
if (c == Assembler::Zero) as_xori(dest, dest, 1);
break;
}
}
void MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs,
Register64 rhs, Register dest) {
Condition c = ma_cmp64(cond, lhs, rhs, dest);
switch (cond) {
// For Equal/NotEqual cond ma_cmp64 dest holds non boolean result.
case Assembler::Equal:
as_sltiu(dest, dest, 1);
break;
case Assembler::NotEqual:
as_sltu(dest, zero, dest);
break;
default:
if (c == Assembler::Zero) as_xori(dest, dest, 1);
break;
}
}
Assembler::Condition MacroAssemblerMIPSCompat::ma_cmp64(Condition cond,
Register64 lhs,
Register64 rhs,
Register dest) {
switch (cond) {
case Assembler::Equal:
case Assembler::NotEqual:
as_xor(SecondScratchReg, lhs.high, rhs.high);
as_xor(ScratchRegister, lhs.low, rhs.low);
as_or(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero;
break;
case Assembler::LessThan:
case Assembler::GreaterThanOrEqual:
as_slt(SecondScratchReg, rhs.high, lhs.high);
as_sltu(ScratchRegister, lhs.low, rhs.low);
as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
as_slt(ScratchRegister, lhs.high, rhs.high);
as_or(dest, ScratchRegister, SecondScratchReg);
return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::GreaterThan:
case Assembler::LessThanOrEqual:
as_slt(SecondScratchReg, lhs.high, rhs.high);
as_sltu(ScratchRegister, rhs.low, lhs.low);
as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
as_slt(ScratchRegister, rhs.high, lhs.high);
as_or(dest, ScratchRegister, SecondScratchReg);
return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::Below:
case Assembler::AboveOrEqual:
as_sltu(SecondScratchReg, rhs.high, lhs.high);
as_sltu(ScratchRegister, lhs.low, rhs.low);
as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
as_sltu(ScratchRegister, lhs.high, rhs.high);
as_or(dest, ScratchRegister, SecondScratchReg);
return (cond == Assembler::AboveOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::Above:
case Assembler::BelowOrEqual:
as_sltu(SecondScratchReg, lhs.high, rhs.high);
as_sltu(ScratchRegister, rhs.low, lhs.low);
as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister);
as_sltu(ScratchRegister, rhs.high, lhs.high);
as_or(dest, ScratchRegister, SecondScratchReg);
return (cond == Assembler::BelowOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
default:
MOZ_CRASH("Condition code not supported");
break;
}
}
Assembler::Condition MacroAssemblerMIPSCompat::ma_cmp64(Condition cond,
Register64 lhs,
Imm64 val,
Register dest) {
MOZ_ASSERT(val.value != 0);
switch (cond) {
case Assembler::Equal:
case Assembler::NotEqual:
ma_xor(SecondScratchReg, lhs.high, val.hi());
ma_xor(ScratchRegister, lhs.low, val.low());
as_or(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero;
break;
case Assembler::LessThan:
case Assembler::GreaterThanOrEqual:
ma_li(SecondScratchReg, val.hi());
as_slt(ScratchRegister, lhs.high, SecondScratchReg);
as_slt(SecondScratchReg, SecondScratchReg, lhs.high);
as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_li(ScratchRegister, val.low());
as_sltu(ScratchRegister, lhs.low, ScratchRegister);
as_slt(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::GreaterThan:
case Assembler::LessThanOrEqual:
ma_li(SecondScratchReg, val.hi());
as_slt(ScratchRegister, SecondScratchReg, lhs.high);
as_slt(SecondScratchReg, lhs.high, SecondScratchReg);
as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_li(ScratchRegister, val.low());
as_sltu(ScratchRegister, ScratchRegister, lhs.low);
as_slt(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::Below:
case Assembler::AboveOrEqual:
ma_li(SecondScratchReg, val.hi());
as_sltu(ScratchRegister, lhs.high, SecondScratchReg);
as_sltu(SecondScratchReg, SecondScratchReg, lhs.high);
as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_li(ScratchRegister, val.low());
as_sltu(ScratchRegister, lhs.low, ScratchRegister);
as_slt(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::AboveOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
case Assembler::Above:
case Assembler::BelowOrEqual:
ma_li(SecondScratchReg, val.hi());
as_sltu(ScratchRegister, SecondScratchReg, lhs.high);
as_sltu(SecondScratchReg, lhs.high, SecondScratchReg);
as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister);
ma_li(ScratchRegister, val.low());
as_sltu(ScratchRegister, ScratchRegister, lhs.low);
as_slt(dest, SecondScratchReg, ScratchRegister);
return (cond == Assembler::BelowOrEqual) ? Assembler::Zero
: Assembler::NonZero;
break;
default:
MOZ_CRASH("Condition code not supported");
break;
}
}
// fp instructions
void MacroAssemblerMIPS::ma_lid(FloatRegister dest, double value) {
struct DoubleStruct {
uint32_t lo;
uint32_t hi;
};
DoubleStruct intStruct = mozilla::BitwiseCast<DoubleStruct>(value);
#if MOZ_BIG_ENDIAN()
std::swap(intStruct.hi, intStruct.lo);
#endif
// put hi part of 64 bit value into the odd register
if (intStruct.hi == 0) {
moveToDoubleHi(zero, dest);
} else {
ma_li(ScratchRegister, Imm32(intStruct.hi));
moveToDoubleHi(ScratchRegister, dest);
}
// put low part of 64 bit value into the even register
if (intStruct.lo == 0) {
moveToDoubleLo(zero, dest);
} else {
ma_li(ScratchRegister, Imm32(intStruct.lo));
moveToDoubleLo(ScratchRegister, dest);
}
}
void MacroAssemblerMIPS::ma_mv(FloatRegister src, ValueOperand dest) {
moveFromDoubleLo(src, dest.payloadReg());
moveFromDoubleHi(src, dest.typeReg());
}
void MacroAssemblerMIPS::ma_mv(ValueOperand src, FloatRegister dest) {
moveToDoubleLo(src.payloadReg(), dest);
moveToDoubleHi(src.typeReg(), dest);
}
void MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address) {
if (Imm16::IsInSignedRange(address.offset)) {
as_lwc1(ft, address.base, address.offset);
} else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
as_gslsx(ft, address.base, ScratchRegister, 0);
} else {
as_addu(ScratchRegister, address.base, ScratchRegister);
as_lwc1(ft, ScratchRegister, 0);
}
}
}
void MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address) {
if (Imm16::IsInSignedRange(address.offset)) {
as_ldc1(ft, address.base, address.offset);
} else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
as_gsldx(ft, address.base, ScratchRegister, 0);
} else {
as_addu(ScratchRegister, address.base, ScratchRegister);
as_ldc1(ft, ScratchRegister, 0);
}
}
}
void MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address) {
if (Imm16::IsInSignedRange(address.offset)) {
as_sdc1(ft, address.base, address.offset);
} else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
as_gssdx(ft, address.base, ScratchRegister, 0);
} else {
as_addu(ScratchRegister, address.base, ScratchRegister);
as_sdc1(ft, ScratchRegister, 0);
}
}
}
void MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address) {
if (Imm16::IsInSignedRange(address.offset)) {
as_swc1(ft, address.base, address.offset);
} else {
MOZ_ASSERT(address.base != ScratchRegister);
ma_li(ScratchRegister, Imm32(address.offset));
if (isLoongson()) {
as_gsssx(ft, address.base, ScratchRegister, 0);
} else {
as_addu(ScratchRegister, address.base, ScratchRegister);
as_swc1(ft, ScratchRegister, 0);
}
}
}
void MacroAssemblerMIPS::ma_ldc1WordAligned(FloatRegister ft, Register base,
int32_t off) {
MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
Imm16::IsInSignedRange(off + TAG_OFFSET));
as_lwc1(ft, base, off + PAYLOAD_OFFSET);
as_lwc1(getOddPair(ft), base, off + TAG_OFFSET);
}
void MacroAssemblerMIPS::ma_sdc1WordAligned(FloatRegister ft, Register base,
int32_t off) {
MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
Imm16::IsInSignedRange(off + TAG_OFFSET));
as_swc1(ft, base, off + PAYLOAD_OFFSET);
as_swc1(getOddPair(ft), base, off + TAG_OFFSET);
}
void MacroAssemblerMIPS::ma_pop(FloatRegister f) {
if (f.isDouble()) {
ma_ldc1WordAligned(f, StackPointer, 0);
} else {
as_lwc1(f, StackPointer, 0);
}
as_addiu(StackPointer, StackPointer, f.size());
}
void MacroAssemblerMIPS::ma_push(FloatRegister f) {
as_addiu(StackPointer, StackPointer, -f.size());
if (f.isDouble()) {
ma_sdc1WordAligned(f, StackPointer, 0);
} else {
as_swc1(f, StackPointer, 0);
}
}
bool MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) {
uint32_t descriptor = MakeFrameDescriptor(
asMasm().framePushed(), FrameType::IonJS, ExitFrameLayout::Size());
asMasm().Push(Imm32(descriptor)); // descriptor_
asMasm().Push(ImmPtr(fakeReturnAddr));
return true;
}
void MacroAssemblerMIPSCompat::move32(Imm32 imm, Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPSCompat::move32(Register src, Register dest) {
ma_move(dest, src);
}
void MacroAssemblerMIPSCompat::movePtr(Register src, Register dest) {
ma_move(dest, src);
}
void MacroAssemblerMIPSCompat::movePtr(ImmWord imm, Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPSCompat::movePtr(ImmGCPtr imm, Register dest) {
ma_li(dest, imm);
}
void MacroAssemblerMIPSCompat::movePtr(ImmPtr imm, Register dest) {
movePtr(ImmWord(uintptr_t(imm.value)), dest);
}
void MacroAssemblerMIPSCompat::movePtr(wasm::SymbolicAddress imm,
Register dest) {
append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm));
ma_liPatchable(dest, ImmWord(-1));
}
void MacroAssemblerMIPSCompat::load8ZeroExtend(const Address& address,
Register dest) {
ma_load(dest, address, SizeByte, ZeroExtend);
}
void MacroAssemblerMIPSCompat::load8ZeroExtend(const BaseIndex& src,
Register dest) {
ma_load(dest, src, SizeByte, ZeroExtend);
}
void MacroAssemblerMIPSCompat::load8SignExtend(const Address& address,
Register dest) {
ma_load(dest, address, SizeByte, SignExtend);
}
void MacroAssemblerMIPSCompat::load8SignExtend(const BaseIndex& src,
Register dest) {
ma_load(dest, src, SizeByte, SignExtend);
}
void MacroAssemblerMIPSCompat::load16ZeroExtend(const Address& address,
Register dest) {
ma_load(dest, address, SizeHalfWord, ZeroExtend);
}
void MacroAssemblerMIPSCompat::load16ZeroExtend(const BaseIndex& src,
Register dest) {
ma_load(dest, src, SizeHalfWord, ZeroExtend);
}
void MacroAssemblerMIPSCompat::load16SignExtend(const Address& address,
Register dest) {
ma_load(dest, address, SizeHalfWord, SignExtend);
}
void MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex& src,
Register dest) {
ma_load(dest, src, SizeHalfWord, SignExtend);
}
void MacroAssemblerMIPSCompat::load32(const Address& address, Register dest) {
ma_load(dest, address, SizeWord);
}
void MacroAssemblerMIPSCompat::load32(const BaseIndex& address, Register dest) {
ma_load(dest, address, SizeWord);
}
void MacroAssemblerMIPSCompat::load32(AbsoluteAddress address, Register dest) {
movePtr(ImmPtr(address.addr), ScratchRegister);
load32(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPSCompat::load32(wasm::SymbolicAddress address,
Register dest) {
movePtr(address, ScratchRegister);
load32(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPSCompat::loadPtr(const Address& address, Register dest) {
ma_load(dest, address, SizeWord);
}
void MacroAssemblerMIPSCompat::loadPtr(const BaseIndex& src, Register dest) {
ma_load(dest, src, SizeWord);
}
void MacroAssemblerMIPSCompat::loadPtr(AbsoluteAddress address, Register dest) {
movePtr(ImmPtr(address.addr), ScratchRegister);
loadPtr(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPSCompat::loadPtr(wasm::SymbolicAddress address,
Register dest) {
movePtr(address, ScratchRegister);
loadPtr(Address(ScratchRegister, 0), dest);
}
void MacroAssemblerMIPSCompat::loadPrivate(const Address& address,
Register dest) {
ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::loadUnalignedDouble(
const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
FloatRegister dest) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
computeScaledAddress(src, SecondScratchReg);
BufferOffset load;
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + 7)) {
load = as_lwl(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET + 3);
as_lwr(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET);
append(access, load.getOffset());
moveToDoubleLo(temp, dest);
load = as_lwl(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET + 3);
as_lwr(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET);
append(access, load.getOffset());
moveToDoubleHi(temp, dest);
} else {
ma_li(ScratchRegister, Imm32(src.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
load = as_lwl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
as_lwr(temp, ScratchRegister, INT64LOW_OFFSET);
append(access, load.getOffset());
moveToDoubleLo(temp, dest);
load = as_lwl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
as_lwr(temp, ScratchRegister, INT64HIGH_OFFSET);
append(access, load.getOffset());
moveToDoubleHi(temp, dest);
}
}
void MacroAssemblerMIPSCompat::loadUnalignedFloat32(
const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
FloatRegister dest) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
computeScaledAddress(src, SecondScratchReg);
BufferOffset load;
if (Imm16::IsInSignedRange(src.offset) &&
Imm16::IsInSignedRange(src.offset + 3)) {
load = as_lwl(temp, SecondScratchReg, src.offset + 3);
as_lwr(temp, SecondScratchReg, src.offset);
} else {
ma_li(ScratchRegister, Imm32(src.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
load = as_lwl(temp, ScratchRegister, 3);
as_lwr(temp, ScratchRegister, 0);
}
append(access, load.getOffset());
moveToFloat32(temp, dest);
}
void MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeByte);
}
void MacroAssemblerMIPSCompat::store8(Register src, const Address& address) {
ma_store(src, address, SizeByte);
}
void MacroAssemblerMIPSCompat::store8(Imm32 imm, const BaseIndex& dest) {
ma_store(imm, dest, SizeByte);
}
void MacroAssemblerMIPSCompat::store8(Register src, const BaseIndex& dest) {
ma_store(src, dest, SizeByte);
}
void MacroAssemblerMIPSCompat::store16(Imm32 imm, const Address& address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeHalfWord);
}
void MacroAssemblerMIPSCompat::store16(Register src, const Address& address) {
ma_store(src, address, SizeHalfWord);
}
void MacroAssemblerMIPSCompat::store16(Imm32 imm, const BaseIndex& dest) {
ma_store(imm, dest, SizeHalfWord);
}
void MacroAssemblerMIPSCompat::store16(Register src, const BaseIndex& address) {
ma_store(src, address, SizeHalfWord);
}
void MacroAssemblerMIPSCompat::store32(Register src, AbsoluteAddress address) {
movePtr(ImmPtr(address.addr), ScratchRegister);
store32(src, Address(ScratchRegister, 0));
}
void MacroAssemblerMIPSCompat::store32(Register src, const Address& address) {
ma_store(src, address, SizeWord);
}
void MacroAssemblerMIPSCompat::store32(Imm32 src, const Address& address) {
move32(src, SecondScratchReg);
ma_store(SecondScratchReg, address, SizeWord);
}
void MacroAssemblerMIPSCompat::store32(Imm32 imm, const BaseIndex& dest) {
ma_store(imm, dest, SizeWord);
}
void MacroAssemblerMIPSCompat::store32(Register src, const BaseIndex& dest) {
ma_store(src, dest, SizeWord);
}
template <typename T>
void MacroAssemblerMIPSCompat::storePtr(ImmWord imm, T address) {
ma_li(SecondScratchReg, imm);
ma_store(SecondScratchReg, address, SizeWord);
}
template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmWord imm,
Address address);
template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmWord imm,
BaseIndex address);
template <typename T>
void MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, T address) {
storePtr(ImmWord(uintptr_t(imm.value)), address);
}
template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmPtr imm,
Address address);
template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmPtr imm,
BaseIndex address);
template <typename T>
void MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, T address) {
movePtr(imm, SecondScratchReg);
storePtr(SecondScratchReg, address);
}
template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmGCPtr imm,
Address address);
template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmGCPtr imm,
BaseIndex address);
void MacroAssemblerMIPSCompat::storePtr(Register src, const Address& address) {
ma_store(src, address, SizeWord);
}
void MacroAssemblerMIPSCompat::storePtr(Register src,
const BaseIndex& address) {
ma_store(src, address, SizeWord);
}
void MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest) {
movePtr(ImmPtr(dest.addr), ScratchRegister);
storePtr(src, Address(ScratchRegister, 0));
}
void MacroAssemblerMIPSCompat::storeUnalignedFloat32(
const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
const BaseIndex& dest) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
computeScaledAddress(dest, SecondScratchReg);
moveFromFloat32(src, temp);
BufferOffset store;
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + 3)) {
store = as_swl(temp, SecondScratchReg, dest.offset + 3);
as_swr(temp, SecondScratchReg, dest.offset);
} else {
ma_li(ScratchRegister, Imm32(dest.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
store = as_swl(temp, ScratchRegister, 3);
as_swr(temp, ScratchRegister, 0);
}
append(access, store.getOffset());
}
void MacroAssemblerMIPSCompat::storeUnalignedDouble(
const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
const BaseIndex& dest) {
MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
computeScaledAddress(dest, SecondScratchReg);
BufferOffset store;
if (Imm16::IsInSignedRange(dest.offset) &&
Imm16::IsInSignedRange(dest.offset + 7)) {
moveFromDoubleHi(src, temp);
store = as_swl(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET + 3);
as_swr(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET);
moveFromDoubleLo(src, temp);
as_swl(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET + 3);
as_swr(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET);
} else {
ma_li(ScratchRegister, Imm32(dest.offset));
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
moveFromDoubleHi(src, temp);
store = as_swl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
as_swr(temp, ScratchRegister, INT64HIGH_OFFSET);
moveFromDoubleLo(src, temp);
as_swl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
as_swr(temp, ScratchRegister, INT64LOW_OFFSET);
}
append(access, store.getOffset());
}
void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) {
as_roundwd(ScratchDoubleReg, input);
ma_li(ScratchRegister, Imm32(255));
as_mfc1(output, ScratchDoubleReg);
zeroDouble(ScratchDoubleReg);
as_sltiu(SecondScratchReg, output, 255);
as_colt(DoubleFloat, ScratchDoubleReg, input);
// if res > 255; res = 255;
as_movz(output, ScratchRegister, SecondScratchReg);
// if !(input > 0); res = 0;
as_movf(output, zero);
}
// higher level tag testing code
Operand MacroAssemblerMIPSCompat::ToPayload(Operand base) {
return Operand(Register::FromCode(base.base()), base.disp() + PAYLOAD_OFFSET);
}
Operand MacroAssemblerMIPSCompat::ToType(Operand base) {
return Operand(Register::FromCode(base.base()), base.disp() + TAG_OFFSET);
}
void MacroAssemblerMIPSCompat::testNullSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_NULL), cond);
}
void MacroAssemblerMIPSCompat::testObjectSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_OBJECT), cond);
}
void MacroAssemblerMIPSCompat::testUndefinedSet(Condition cond,
const ValueOperand& value,
Register dest) {
MOZ_ASSERT(cond == Equal || cond == NotEqual);
ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), cond);
}
// unboxing code
void MacroAssemblerMIPSCompat::unboxNonDouble(const ValueOperand& operand,
Register dest, JSValueType) {
if (operand.payloadReg() != dest) {
ma_move(dest, operand.payloadReg());
}
}
void MacroAssemblerMIPSCompat::unboxNonDouble(const Address& src, Register dest,
JSValueType) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxNonDouble(const BaseIndex& src,
Register dest, JSValueType) {
computeScaledAddress(src, SecondScratchReg);
ma_lw(dest, Address(SecondScratchReg, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxInt32(const ValueOperand& operand,
Register dest) {
ma_move(dest, operand.payloadReg());
}
void MacroAssemblerMIPSCompat::unboxInt32(const Address& src, Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxBoolean(const ValueOperand& operand,
Register dest) {
ma_move(dest, operand.payloadReg());
}
void MacroAssemblerMIPSCompat::unboxBoolean(const Address& src, Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxDouble(const ValueOperand& operand,
FloatRegister dest) {
moveToDoubleLo(operand.payloadReg(), dest);
moveToDoubleHi(operand.typeReg(), dest);
}
void MacroAssemblerMIPSCompat::unboxDouble(const Address& src,
FloatRegister dest) {
ma_lw(ScratchRegister, Address(src.base, src.offset + PAYLOAD_OFFSET));
moveToDoubleLo(ScratchRegister, dest);
ma_lw(ScratchRegister, Address(src.base, src.offset + TAG_OFFSET));
moveToDoubleHi(ScratchRegister, dest);
}
void MacroAssemblerMIPSCompat::unboxDouble(const BaseIndex& src,
FloatRegister dest) {
loadDouble(src, dest);
}
void MacroAssemblerMIPSCompat::unboxString(const ValueOperand& operand,
Register dest) {
ma_move(dest, operand.payloadReg());
}
void MacroAssemblerMIPSCompat::unboxString(const Address& src, Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxBigInt(const ValueOperand& operand,
Register dest) {
ma_move(dest, operand.payloadReg());
}
void MacroAssemblerMIPSCompat::unboxBigInt(const Address& src, Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxObject(const ValueOperand& src,
Register dest) {
ma_move(dest, src.payloadReg());
}
void MacroAssemblerMIPSCompat::unboxObject(const Address& src, Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxObjectOrNull(const Address& src,
Register dest) {
ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::unboxValue(const ValueOperand& src,
AnyRegister dest, JSValueType) {
if (dest.isFloat()) {
Label notInt32, end;
asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
convertInt32ToDouble(src.payloadReg(), dest.fpu());
ma_b(&end, ShortJump);
bind(&notInt32);
unboxDouble(src, dest.fpu());
bind(&end);
} else if (src.payloadReg() != dest.gpr()) {
ma_move(dest.gpr(), src.payloadReg());
}
}
void MacroAssemblerMIPSCompat::boxDouble(FloatRegister src,
const ValueOperand& dest,
FloatRegister) {
moveFromDoubleLo(src, dest.payloadReg());
moveFromDoubleHi(src, dest.typeReg());
}
void MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, Register src,
const ValueOperand& dest) {
if (src != dest.payloadReg()) {
ma_move(dest.payloadReg(), src);
}
ma_li(dest.typeReg(), ImmType(type));
}
void MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand& operand,
FloatRegister dest) {
convertBoolToInt32(operand.payloadReg(), ScratchRegister);
convertInt32ToDouble(ScratchRegister, dest);
}
void MacroAssemblerMIPSCompat::int32ValueToDouble(const ValueOperand& operand,
FloatRegister dest) {
convertInt32ToDouble(operand.payloadReg(), dest);
}
void MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand& operand,
FloatRegister dest) {
convertBoolToInt32(operand.payloadReg(), ScratchRegister);
convertInt32ToFloat32(ScratchRegister, dest);
}
void MacroAssemblerMIPSCompat::int32ValueToFloat32(const ValueOperand& operand,
FloatRegister dest) {
convertInt32ToFloat32(operand.payloadReg(), dest);
}
void MacroAssemblerMIPSCompat::loadConstantFloat32(float f,
FloatRegister dest) {
ma_lis(dest, f);
}
void MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src,
FloatRegister dest) {
Label notInt32, end;
// If it's an int, convert it to double.
ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET));
asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET));
convertInt32ToDouble(SecondScratchReg, dest);
ma_b(&end, ShortJump);
// Not an int, just load as double.
bind(&notInt32);
ma_ld(dest, src);
bind(&end);
}
void MacroAssemblerMIPSCompat::loadInt32OrDouble(Register base, Register index,
FloatRegister dest,
int32_t shift) {
Label notInt32, end;
// If it's an int, convert it to double.
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
SecondScratchReg);
// Since we only have one scratch, we need to stomp over it with the tag.
load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg);
asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, &notInt32);
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
SecondScratchReg);
load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg);
convertInt32ToDouble(SecondScratchReg, dest);
ma_b(&end, ShortJump);
// Not an int, just load as double.
bind(&notInt32);
// First, recompute the offset that had been stored in the scratch register
// since the scratch register was overwritten loading in the type.
computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)),
SecondScratchReg);
loadDouble(Address(SecondScratchReg, 0), dest);
bind(&end);
}
void MacroAssemblerMIPSCompat::loadConstantDouble(double dp,
FloatRegister dest) {
ma_lid(dest, dp);
}
Register MacroAssemblerMIPSCompat::extractObject(const Address& address,
Register scratch) {
ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET));
return scratch;
}
Register MacroAssemblerMIPSCompat::extractTag(const Address& address,
Register scratch) {
ma_lw(scratch, Address(address.base, address.offset + TAG_OFFSET));
return scratch;
}
Register MacroAssemblerMIPSCompat::extractTag(const BaseIndex& address,
Register scratch) {
computeScaledAddress(address, scratch);
return extractTag(Address(scratch, address.offset), scratch);
}
uint32_t MacroAssemblerMIPSCompat::getType(const Value& val) {
return val.toNunboxTag();
}
void MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) {
if (val.isGCThing()) {
ma_li(data, ImmGCPtr(val.toGCThing()));
} else {
ma_li(data, Imm32(val.toNunboxPayload()));
}
}
/////////////////////////////////////////////////////////////////
// X86/X64-common/ARM/MIPS interface.
/////////////////////////////////////////////////////////////////
void MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst) {
storeValue(val, Address(Register::FromCode(dst.base()), dst.disp()));
}
void MacroAssemblerMIPSCompat::storeValue(ValueOperand val,
const BaseIndex& dest) {
computeScaledAddress(dest, SecondScratchReg);
storeValue(val, Address(SecondScratchReg, dest.offset));
}
void MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg,
BaseIndex dest) {
computeScaledAddress(dest, ScratchRegister);
// Make sure that ma_sw doesn't clobber ScratchRegister
int32_t offset = dest.offset;
if (!Imm16::IsInSignedRange(offset)) {
ma_li(SecondScratchReg, Imm32(offset));
as_addu(ScratchRegister, ScratchRegister, SecondScratchReg);
offset = 0;
}
storeValue(type, reg, Address(ScratchRegister, offset));
}
void MacroAssemblerMIPSCompat::storeValue(ValueOperand val,
const Address& dest) {
ma_sw(val.payloadReg(), Address(dest.base, dest.offset + PAYLOAD_OFFSET));
ma_sw(val.typeReg(), Address(dest.base, dest.offset + TAG_OFFSET));
}
void MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg,
Address dest) {
MOZ_ASSERT(dest.base != SecondScratchReg);
ma_sw(reg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type)));
ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
}
void MacroAssemblerMIPSCompat::storeValue(const Value& val, Address dest) {
MOZ_ASSERT(dest.base != SecondScratchReg);
ma_li(SecondScratchReg, Imm32(getType(val)));
ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
moveData(val, SecondScratchReg);
ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::storeValue(const Value& val, BaseIndex dest) {
computeScaledAddress(dest, ScratchRegister);
// Make sure that ma_sw doesn't clobber ScratchRegister
int32_t offset = dest.offset;
if (!Imm16::IsInSignedRange(offset)) {
ma_li(SecondScratchReg, Imm32(offset));
as_addu(ScratchRegister, ScratchRegister, SecondScratchReg);
offset = 0;
}
storeValue(val, Address(ScratchRegister, offset));
}
void MacroAssemblerMIPSCompat::loadValue(const BaseIndex& addr,
ValueOperand val) {
computeScaledAddress(addr, SecondScratchReg);
loadValue(Address(SecondScratchReg, addr.offset), val);
}
void MacroAssemblerMIPSCompat::loadValue(Address src, ValueOperand val) {
// Ensure that loading the payload does not erase the pointer to the
// Value in memory.
if (src.base != val.payloadReg()) {
ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET));
ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET));
} else {
ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET));
ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET));
}
}
void MacroAssemblerMIPSCompat::tagValue(JSValueType type, Register payload,
ValueOperand dest) {
MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
if (payload != dest.payloadReg()) {
ma_move(dest.payloadReg(), payload);
}
ma_li(dest.typeReg(), ImmType(type));
}
void MacroAssemblerMIPSCompat::pushValue(ValueOperand val) {
// Allocate stack slots for type and payload. One for each.
asMasm().subPtr(Imm32(sizeof(Value)), StackPointer);
// Store type and payload.
storeValue(val, Address(StackPointer, 0));
}
void MacroAssemblerMIPSCompat::pushValue(const Address& addr) {
// Allocate stack slots for type and payload. One for each.
ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value)));
// If address is based on StackPointer its offset needs to be adjusted
// to accommodate for previous stack allocation.
int32_t offset =
addr.base != StackPointer ? addr.offset : addr.offset + sizeof(Value);
// Store type and payload.
ma_lw(ScratchRegister, Address(addr.base, offset + TAG_OFFSET));
ma_sw(ScratchRegister, Address(StackPointer, TAG_OFFSET));
ma_lw(ScratchRegister, Address(addr.base, offset + PAYLOAD_OFFSET));
ma_sw(ScratchRegister, Address(StackPointer, PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::popValue(ValueOperand val) {
// Load payload and type.
as_lw(val.payloadReg(), StackPointer, PAYLOAD_OFFSET);
as_lw(val.typeReg(), StackPointer, TAG_OFFSET);
// Free stack.
as_addiu(StackPointer, StackPointer, sizeof(Value));
}
void MacroAssemblerMIPSCompat::storePayload(const Value& val, Address dest) {
moveData(val, SecondScratchReg);
ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
}
void MacroAssemblerMIPSCompat::storePayload(Register src, Address dest) {
ma_sw(src, Address(dest.base, dest.offset + PAYLOAD_OFFSET));
return;
}
void MacroAssemblerMIPSCompat::storePayload(const Value& val,
const BaseIndex& dest) {
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
moveData(val, ScratchRegister);
as_sw(ScratchRegister, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET);
}
void MacroAssemblerMIPSCompat::storePayload(Register src,
const BaseIndex& dest) {
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET);
}
void MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest) {
ma_li(SecondScratchReg, tag);
ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET));
}
void MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) {
MOZ_ASSERT(dest.offset == 0);
computeScaledAddress(dest, SecondScratchReg);
ma_li(ScratchRegister, tag);
as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET);
}
void MacroAssemblerMIPSCompat::breakpoint() { as_break(0); }
void MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand& source,
FloatRegister dest,
Label* failure) {
Label isDouble, done;
asMasm().branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble);
asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure);
convertInt32ToDouble(source.payloadReg(), dest);
jump(&done);
bind(&isDouble);
unboxDouble(source, dest);
bind(&done);
}
void MacroAssemblerMIPSCompat::checkStackAlignment() {
#ifdef DEBUG
Label aligned;
as_andi(ScratchRegister, sp, ABIStackAlignment - 1);
ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump);
as_break(BREAK_STACK_UNALIGNED);
bind(&aligned);
#endif
}
void MacroAssemblerMIPSCompat::alignStackPointer() {
movePtr(StackPointer, SecondScratchReg);
asMasm().subPtr(Imm32(sizeof(intptr_t)), StackPointer);
asMasm().andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer);
storePtr(SecondScratchReg, Address(StackPointer, 0));
}
void MacroAssemblerMIPSCompat::restoreStackPointer() {
loadPtr(Address(StackPointer, 0), StackPointer);
}
void MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(
Label* profilerExitTail) {
// Reserve space for exception information.
int size = (sizeof(ResumeFromException) + ABIStackAlignment) &
~(ABIStackAlignment - 1);
asMasm().subPtr(Imm32(size), StackPointer);
ma_move(a0, StackPointer); // Use a0 since it is a first function argument
// Call the handler.
using Fn = void (*)(ResumeFromException * rfe);
asMasm().setupUnalignedABICall(a1);
asMasm().passABIArg(a0);
asMasm().callWithABI<Fn, HandleException>(
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
Label entryFrame;
Label catch_;
Label finally;
Label returnBaseline;
Label returnIon;
Label bailout;
Label wasm;
Label wasmCatch;
// Already clobbered a0, so use it...
load32(Address(StackPointer, ResumeFromException::offsetOfKind()), a0);
asMasm().branch32(Assembler::Equal, a0,
Imm32(ExceptionResumeKind::EntryFrame), &entryFrame);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Catch),
&catch_);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Finally),
&finally);
asMasm().branch32(Assembler::Equal, r0,
Imm32(ExceptionResumeKind::ForcedReturnBaseline),
&returnBaseline);
asMasm().branch32(Assembler::Equal, r0,
Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Bailout),
&bailout);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Wasm),
&wasm);
asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::WasmCatch),
&wasmCatch);
breakpoint(); // Invalid kind.
// No exception handler. Load the error value, load the new stack pointer
// and return from the entry frame.
bind(&entryFrame);
asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
// We're going to be returning by the ion calling convention
ma_pop(ra);
as_jr(ra);
as_nop();
// If we found a catch handler, this must be a baseline frame. Restore
// state and jump to the catch block.
bind(&catch_);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), a0);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
BaselineFrameReg);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
jump(a0);
// If we found a finally block, this must be a baseline frame. Push two
// values expected by the finally block: the exception and BooleanValue(true).
bind(&finally);
ValueOperand exception = ValueOperand(a1, a2);
loadValue(Address(sp, ResumeFromException::offsetOfException()), exception);
loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a0);
loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()),
BaselineFrameReg);
loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp);
pushValue(exception);
pushValue(BooleanValue(true));
jump(a0);
// Return BaselineFrame->returnValue() to the caller.
// Used in debug mode and for GeneratorReturn.
Label profilingInstrumentation;
bind(&returnBaseline);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
BaselineFrameReg);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
loadValue(
Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()),
JSReturnOperand);
ma_move(StackPointer, BaselineFrameReg);
pop(BaselineFrameReg);
jump(&profilingInstrumentation);
// Return the given value to the caller.
bind(&returnIon);
loadValue(Address(StackPointer, ResumeFromException::offsetOfException()),
JSReturnOperand);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
StackPointer);
// If profiling is enabled, then update the lastProfilingFrame to refer to
// caller frame before returning. This code is shared by ForcedReturnIon
// and ForcedReturnBaseline.
bind(&profilingInstrumentation);
{
Label skipProfilingInstrumentation;
// Test if profiler enabled.
AbsoluteAddress addressOfEnabled(
GetJitContext()->runtime->geckoProfiler().addressOfEnabled());
asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
&skipProfilingInstrumentation);
jump(profilerExitTail);
bind(&skipProfilingInstrumentation);
}
ret();
// If we are bailing out to baseline to handle an exception, jump to
// the bailout tail stub. Load 1 (true) in ReturnReg to indicate success.
bind(&bailout);
loadPtr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), a2);
ma_li(ReturnReg, Imm32(1));
loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a1);
jump(a1);
// If we are throwing and the innermost frame was a wasm frame, reset SP and
// FP; SP is pointing to the unwound return address to the wasm entry, so
// we can just ret().
bind(&wasm);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
ret();
// Found a wasm catch handler, restore state and jump to it.
bind(&wasmCatch);
loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a1);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
FramePointer);
loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
StackPointer);
jump(a1);
}
CodeOffset MacroAssemblerMIPSCompat::toggledJump(Label* label) {
CodeOffset ret(nextOffset().getOffset());
ma_b(label);
return ret;
}
CodeOffset MacroAssemblerMIPSCompat::toggledCall(JitCode* target,
bool enabled) {
BufferOffset bo = nextOffset();
CodeOffset offset(bo.getOffset());
addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE);
ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
if (enabled) {
as_jalr(ScratchRegister);
as_nop();
} else {
as_nop();
as_nop();
}
MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() ==
ToggledCallSize(nullptr));
return offset;
}
void MacroAssemblerMIPSCompat::profilerEnterFrame(Register framePtr,
Register scratch) {
asMasm().loadJSContext(scratch);
loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
storePtr(framePtr,
Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
storePtr(ImmPtr(nullptr),
Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
}
void MacroAssemblerMIPSCompat::profilerExitFrame() {
jump(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
}
void MacroAssembler::subFromStackPtr(Imm32 imm32) {
if (imm32.value) {
asMasm().subPtr(imm32, StackPointer);
}
}
//{{{ check_macroassembler_style
// ===============================================================
// Stack manipulation functions.
size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) {
return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes();
}
void MacroAssembler::PushRegsInMask(LiveRegisterSet set) {
int32_t diffF = set.fpus().getPushSizeInBytes();
int32_t diffG = set.gprs().size() * sizeof(intptr_t);
reserveStack(diffG);
for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
diffG -= sizeof(intptr_t);
storePtr(*iter, Address(StackPointer, diffG));
}
MOZ_ASSERT(diffG == 0);
#ifdef ENABLE_WASM_SIMD
# error "Needs more careful logic if SIMD is enabled"
#endif
if (diffF > 0) {
// Double values have to be aligned. We reserve extra space so that we can
// start writing from the first aligned location.
// We reserve a whole extra double so that the buffer has even size.
ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
reserveStack(diffF);
diffF -= sizeof(double);
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
iter.more(); ++iter) {
as_sdc1(*iter, SecondScratchReg, -diffF);
diffF -= sizeof(double);
}
MOZ_ASSERT(diffF == 0);
}
}
void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set,
LiveRegisterSet ignore) {
int32_t diffG = set.gprs().size() * sizeof(intptr_t);
int32_t diffF = set.fpus().getPushSizeInBytes();
const int32_t reservedG = diffG;
const int32_t reservedF = diffF;
#ifdef ENABLE_WASM_SIMD
# error "Needs more careful logic if SIMD is enabled"
#endif
if (reservedF > 0) {
// Read the buffer form the first aligned location.
ma_addu(SecondScratchReg, sp, Imm32(reservedF));
ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
diffF -= sizeof(double);
LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush());
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
iter.more(); ++iter) {
if (!ignore.has(*iter)) {
as_ldc1(*iter, SecondScratchReg, -diffF);
}
diffF -= sizeof(double);
}
freeStack(reservedF);
MOZ_ASSERT(diffF == 0);
}
for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
diffG -= sizeof(intptr_t);
if (!ignore.has(*iter)) {
loadPtr(Address(StackPointer, diffG), *iter);
}
}
freeStack(reservedG);
MOZ_ASSERT(diffG == 0);
}
void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest,
Register scratch) {
int32_t diffF = set.fpus().getPushSizeInBytes();
int32_t diffG = set.gprs().size() * sizeof(intptr_t);
MOZ_ASSERT(dest.offset >= diffG + diffF);
MOZ_ASSERT(dest.base == StackPointer);
for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
diffG -= sizeof(intptr_t);
dest.offset -= sizeof(intptr_t);
storePtr(*iter, dest);
}
MOZ_ASSERT(diffG == 0);
#ifdef ENABLE_WASM_SIMD
# error "Needs more careful logic if SIMD is enabled"
#endif
if (diffF > 0) {
computeEffectiveAddress(dest, scratch);
ma_and(scratch, scratch, Imm32(~(ABIStackAlignment - 1)));
diffF -= sizeof(double);
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush());
iter.more(); ++iter) {
as_sdc1(*iter, scratch, -diffF);
diffF -= sizeof(double);
}
MOZ_ASSERT(diffF == 0);
}
}
// ===============================================================
// ABI function calls.
void MacroAssembler::setupUnalignedABICall(Register scratch) {
MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls");
setupNativeABICall();
dynamicAlignment_ = true;
ma_move(scratch, StackPointer);
// Force sp to be aligned
asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
storePtr(scratch, Address(StackPointer, 0));
}
void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) {
MOZ_ASSERT(inCall_);
uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
// Reserve place for $ra.
stackForCall += sizeof(intptr_t);
if (dynamicAlignment_) {
stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment);
} else {
uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0;
stackForCall += ComputeByteAlignment(
stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment);
}
*stackAdjust = stackForCall;
reserveStack(stackForCall);
// Save $ra because call is going to clobber it. Restore it in
// callWithABIPost. NOTE: This is needed for calls from SharedIC.
// Maybe we can do this differently.
storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t)));
// Position all arguments.
{
enoughMemory_ &= moveResolver_.resolve();
if (!enoughMemory_) {