Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MacroAssembler-mips64.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "jit/mips64/MacroAssembler-mips64.h"

#include "mozilla/DebugOnly.h"
#include "mozilla/MathAlgorithms.h"

#include <bit>

#include "jit/Bailouts.h"
#include "jit/BaselineFrame.h"
#include "jit/JitFrames.h"
#include "jit/JitRuntime.h"
#include "jit/MacroAssembler.h"
#include "jit/MoveEmitter.h"
#include "jit/SharedICRegisters.h"
#include "jit/Simulator.h"
#include "util/Memory.h"
#include "vm/JitActivation.h"  // js::jit::JitActivation
#include "vm/JSContext.h"
#include "wasm/WasmStubs.h"

#include "jit/MacroAssembler-inl.h"

using namespace js;
using namespace jit;

using mozilla::Abs;

static_assert(sizeof(intptr_t) == 8"Not 32-bit clean.");

void MacroAssemblerMIPS64Compat::convertBoolToInt32(Register src,
                                                    Register dest) {
  // Note that C++ bool is only 1 byte, so zero extend it to clear the
  // higher-order bits.
  ma_and(dest, src, Imm32(0xff));
}

void MacroAssemblerMIPS64Compat::convertInt32ToDouble(Register src,
                                                      FloatRegister dest) {
  as_mtc1(src, dest);
  as_cvtdw(dest, dest);
}

void MacroAssemblerMIPS64Compat::convertInt32ToDouble(const Address& src,
                                                      FloatRegister dest) {
  ma_ls(dest, src);
  as_cvtdw(dest, dest);
}

void MacroAssemblerMIPS64Compat::convertInt32ToDouble(const BaseIndex& src,
                                                      FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  computeScaledAddress(src, scratch);
  convertInt32ToDouble(Address(scratch, src.offset), dest);
}

void MacroAssemblerMIPS64Compat::convertUInt32ToDouble(Register src,
                                                       FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_dext(scratch, src, Imm32(0), Imm32(32));
  asMasm().convertInt64ToDouble(Register64(scratch), dest);
}

void MacroAssemblerMIPS64Compat::convertUInt64ToDouble(Register src,
                                                       FloatRegister dest) {
  Label positive, done;
  ma_b(src, src, &positive, NotSigned, ShortJump);

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register scratch2 = temps.Acquire();
  MOZ_ASSERT(src != scratch);
  MOZ_ASSERT(src != scratch2);

  ma_and(scratch, src, Imm32(1));
  ma_dsrl(scratch2, src, Imm32(1));
  ma_or(scratch, scratch2);
  as_dmtc1(scratch, dest);
  as_cvtdl(dest, dest);
  asMasm().addDouble(dest, dest);
  ma_b(&done, ShortJump);

  bind(&positive);
  as_dmtc1(src, dest);
  as_cvtdl(dest, dest);

  bind(&done);
}

void MacroAssemblerMIPS64Compat::convertUInt32ToFloat32(Register src,
                                                        FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_dext(scratch, src, Imm32(0), Imm32(32));
  asMasm().convertInt64ToFloat32(Register64(scratch), dest);
}

void MacroAssemblerMIPS64Compat::convertDoubleToFloat32(FloatRegister src,
                                                        FloatRegister dest) {
  as_cvtsd(dest, src);
}

const int CauseBitPos = int(Assembler::CauseI);
const int CauseBitCount = 1 + int(Assembler::CauseV) - int(Assembler::CauseI);
const int CauseIOrVMask = ((1 << int(Assembler::CauseI)) |
                           (1 << int(Assembler::CauseV))) >>
                          int(Assembler::CauseI);

// Checks whether a double is representable as a 32-bit integer. If so, the
// integer is written to the output register. Otherwise, a bailout is taken to
// the given snapshot. This function overwrites the scratch float register.
void MacroAssemblerMIPS64Compat::convertDoubleToInt32(FloatRegister src,
                                                      Register dest,
                                                      Label* fail,
                                                      bool negativeZeroCheck) {
  UseScratchRegisterScope temps(*this);
  if (negativeZeroCheck) {
    moveFromDouble(src, dest);
    ma_drol(dest, dest, Imm32(1));
    ma_b(dest, Imm32(1), fail, Assembler::Equal);
  }

  Register scratch = temps.Acquire();
  // Truncate double to int ; if result is inexact or invalid fail.
  as_truncwd(ScratchFloat32Reg, src);
  as_cfc1(scratch, Assembler::FCSR);
  moveFromFloat32(ScratchFloat32Reg, dest);
  ma_ext(scratch, scratch, CauseBitPos, CauseBitCount);
  as_andi(scratch, scratch,
          CauseIOrVMask);  // masking for Inexact and Invalid flag.
  ma_b(scratch, Imm32(0), fail, Assembler::NotEqual);
}

void MacroAssemblerMIPS64Compat::convertDoubleToPtr(FloatRegister src,
                                                    Register dest, Label* fail,
                                                    bool negativeZeroCheck) {
  UseScratchRegisterScope temps(*this);
  if (negativeZeroCheck) {
    moveFromDouble(src, dest);
    ma_drol(dest, dest, Imm32(1));
    ma_b(dest, Imm32(1), fail, Assembler::Equal);
  }

  Register scratch = temps.Acquire();
  as_truncld(ScratchDoubleReg, src);
  as_cfc1(scratch, Assembler::FCSR);
  moveFromDouble(ScratchDoubleReg, dest);
  ma_ext(scratch, scratch, CauseBitPos, CauseBitCount);
  as_andi(scratch, scratch, CauseIOrVMask);
  ma_b(scratch, Imm32(0), fail, Assembler::NotEqual);
}

// Checks whether a float32 is representable as a 32-bit integer. If so, the
// integer is written to the output register. Otherwise, a bailout is taken to
// the given snapshot. This function overwrites the scratch float register.
void MacroAssemblerMIPS64Compat::convertFloat32ToInt32(FloatRegister src,
                                                       Register dest,
                                                       Label* fail,
                                                       bool negativeZeroCheck) {
  UseScratchRegisterScope temps(*this);
  if (negativeZeroCheck) {
    moveFromFloat32(src, dest);
    ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal);
  }

  Register scratch = temps.Acquire();
  as_truncws(ScratchFloat32Reg, src);
  as_cfc1(scratch, Assembler::FCSR);
  moveFromFloat32(ScratchFloat32Reg, dest);
  ma_ext(scratch, scratch, CauseBitPos, CauseBitCount);
  as_andi(scratch, scratch, CauseIOrVMask);
  ma_b(scratch, Imm32(0), fail, Assembler::NotEqual);
}

void MacroAssemblerMIPS64Compat::convertFloat32ToDouble(FloatRegister src,
                                                        FloatRegister dest) {
  as_cvtds(dest, src);
}

void MacroAssemblerMIPS64Compat::convertInt32ToFloat32(Register src,
                                                       FloatRegister dest) {
  as_mtc1(src, dest);
  as_cvtsw(dest, dest);
}

void MacroAssemblerMIPS64Compat::convertInt32ToFloat32(const Address& src,
                                                       FloatRegister dest) {
  ma_ls(dest, src);
  as_cvtsw(dest, dest);
}

void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) {
  convertInt64ToDouble(Register64(src), dest);
}

void MacroAssemblerMIPS64::ma_li(Register dest, CodeLabel* label) {
  BufferOffset bo = m_buffer.nextOffset();
  ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
  label->patchAt()->bind(bo.getOffset());
  label->setLinkMode(CodeLabel::MoveImmediate);
}

void MacroAssemblerMIPS64::ma_li(Register dest, ImmWord imm) {
  int64_t value = imm.value;

  if (-1 == (value >> 15) || 0 == (value >> 15)) {
    as_addiu(dest, zero, value);
    return;
  }
  if (0 == (value >> 16)) {
    as_ori(dest, zero, value);
    return;
  }

  if (-1 == (value >> 31) || 0 == (value >> 31)) {
    as_lui(dest, uint16_t(value >> 16));
  } else if (0 == (value >> 32)) {
    as_lui(dest, uint16_t(value >> 16));
    // mips spec recommends dinsu but it is unfriendly to mips3
    ma_dext(dest, dest, Imm32(0), Imm32(32));
  } else if (-1 == (value >> 47) || 0 == (value >> 47)) {
    as_lui(dest, uint16_t(value >> 32));
    if (uint16_t(value >> 16)) {
      as_ori(dest, dest, uint16_t(value >> 16));
    }
    as_dsll(dest, dest, 16);
  } else if (0 == (value >> 48)) {
    as_lui(dest, uint16_t(value >> 32));
    // mips spec recommends dinsu but it is unfriendly to mips3
    ma_dext(dest, dest, Imm32(0), Imm32(32));
    if (uint16_t(value >> 16)) {
      as_ori(dest, dest, uint16_t(value >> 16));
    }
    as_dsll(dest, dest, 16);
  } else {
    uint32_t trailingZeroes = std::countr_zero(uint64_t(value));
    if (Imm16::IsInSignedRange(value >> trailingZeroes)) {
      as_addiu(dest, zero, int32_t(value >> trailingZeroes));
      as_dsll32(dest, dest, trailingZeroes);
      return;
    }

    as_lui(dest, uint16_t(value >> 48));
    if (uint16_t(value >> 32)) {
      as_ori(dest, dest, uint16_t(value >> 32));
    }
    if (uint16_t(value >> 16)) {
      as_dsll(dest, dest, 16);
      as_ori(dest, dest, uint16_t(value >> 16));
      as_dsll(dest, dest, 16);
    } else {
      as_dsll32(dest, dest, 32);
    }
  }
  if (uint16_t(value)) {
    as_ori(dest, dest, uint16_t(value));
  }
}

// This method generates lui, dsll and ori instruction block that can be
// modified by UpdateLoad64Value, either during compilation (eg.
// Assembler::bind), or during execution (eg. jit::PatchJump).
void MacroAssemblerMIPS64::ma_liPatchable(Register dest, ImmPtr imm) {
  return ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
}

void MacroAssemblerMIPS64::ma_liPatchable(Register dest, ImmWord imm,
                                          LiFlags flags) {
  if (Li64 == flags) {
    m_buffer.ensureSpace(6 * sizeof(uint32_t));
    as_lui(dest, Imm16::Upper(Imm32((imm.value >> 32) + 0x8000)).encode());
    as_daddiu(dest, dest, int16_t((imm.value >> 32) & 0xffff));
    as_dsll(dest, dest, 16);
    as_ori(dest, dest, Imm16::Upper(Imm32(imm.value)).encode());
    as_dsll(dest, dest, 16);
    as_ori(dest, dest, Imm16::Lower(Imm32(imm.value)).encode());
  } else {
    m_buffer.ensureSpace(4 * sizeof(uint32_t));
    as_lui(dest, Imm16::Lower(Imm32(imm.value >> 32)).encode());
    as_ori(dest, dest, Imm16::Upper(Imm32(imm.value)).encode());
    as_dsll(dest, dest, 16);
    as_ori(dest, dest, Imm16::Lower(Imm32(imm.value)).encode());
  }
}

void MacroAssemblerMIPS64::ma_dnegu(Register rd, Register rs) {
  as_dsubu(rd, zero, rs);
}

// Shifts
void MacroAssemblerMIPS64::ma_dsll(Register rd, Register rt, Imm32 shift) {
  if (31 < shift.value) {
    as_dsll32(rd, rt, shift.value);
  } else {
    as_dsll(rd, rt, shift.value);
  }
}

void MacroAssemblerMIPS64::ma_dsrl(Register rd, Register rt, Imm32 shift) {
  if (31 < shift.value) {
    as_dsrl32(rd, rt, shift.value);
  } else {
    as_dsrl(rd, rt, shift.value);
  }
}

void MacroAssemblerMIPS64::ma_dsra(Register rd, Register rt, Imm32 shift) {
  if (31 < shift.value) {
    as_dsra32(rd, rt, shift.value);
  } else {
    as_dsra(rd, rt, shift.value);
  }
}

void MacroAssemblerMIPS64::ma_dror(Register rd, Register rt, Imm32 shift) {
  if (hasR2()) {
    if (31 < shift.value) {
      as_drotr32(rd, rt, shift.value);
    } else {
      as_drotr(rd, rt, shift.value);
    }
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_dsrl(scratch, rt, shift);
    ma_dsll(rd, rt, Imm32(64 - shift.value));
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPS64::ma_drol(Register rd, Register rt, Imm32 shift) {
  ma_dror(rd, rt, Imm32(64 - shift.value));
}

void MacroAssemblerMIPS64::ma_dsll(Register rd, Register rt, Register shift) {
  as_dsllv(rd, rt, shift);
}

void MacroAssemblerMIPS64::ma_dsrl(Register rd, Register rt, Register shift) {
  as_dsrlv(rd, rt, shift);
}

void MacroAssemblerMIPS64::ma_dsra(Register rd, Register rt, Register shift) {
  as_dsrav(rd, rt, shift);
}

void MacroAssemblerMIPS64::ma_dror(Register rd, Register rt, Register shift) {
  if (hasR2()) {
    as_drotrv(rd, rt, shift);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    as_dsubu(scratch, zero, shift);
    as_dsllv(scratch, rt, scratch);
    as_dsrlv(rd, rt, shift);
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPS64::ma_drol(Register rd, Register rt, Register shift) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  as_dsubu(scratch, zero, shift);
  if (hasR2()) {
    as_drotrv(rd, rt, scratch);
  } else {
    as_dsrlv(scratch, rt, scratch);
    as_dsllv(rd, rt, shift);
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPS64::ma_dins(Register rt, Register rs, Imm32 pos,
                                   Imm32 size) {
  if (hasR2()) {
    if (pos.value >= 0 && pos.value < 32) {
      if (pos.value + size.value > 32) {
        as_dinsm(rt, rs, pos.value, size.value);
      } else {
        as_dins(rt, rs, pos.value, size.value);
      }
    } else {
      as_dinsu(rt, rs, pos.value, size.value);
    }
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    // optimize for special positions
    if (pos.value == 0) {
      ma_dext(scratch, rs, Imm32(0), size);
      ma_dsrl(rt, rt, size);
      ma_dsll(rt, rt, size);
      as_or(rt, rt, scratch);
    } else if (pos.value + size.value == 64) {
      ma_dsll(scratch, rs, pos);
      ma_dsll(rt, rt, size);
      ma_dsrl(rt, rt, size);
      as_or(rt, rt, scratch);
    } else {
      Register scratch2 = temps.Acquire();
      ma_dsubu(scratch, zero, Imm32(1));
      ma_dsrl(scratch, scratch, Imm32(64 - size.value));
      as_and(scratch2, rs, scratch);
      ma_dsll(scratch, scratch, pos);
      ma_dsll(scratch2, scratch2, pos);
      as_nor(scratch, scratch, zero);
      as_and(rt, rt, scratch);
      as_or(rt, rt, scratch2);
    }
  }
}

void MacroAssemblerMIPS64::ma_dext(Register rt, Register rs, Imm32 pos,
                                   Imm32 size) {
  if (hasR2()) {
    if (pos.value >= 0 && pos.value < 32) {
      if (size.value > 32) {
        as_dextm(rt, rs, pos.value, size.value);
      } else {
        as_dext(rt, rs, pos.value, size.value);
      }
    } else {
      as_dextu(rt, rs, pos.value, size.value);
    }
  } else {
    ma_dsll(rt, rs, Imm32(64 - pos.value - size.value));
    ma_dsrl(rt, rt, Imm32(64 - size.value));
  }
}

void MacroAssemblerMIPS64::ma_dsbh(Register rd, Register rt) {
  as_dsbh(rd, rt);
}

void MacroAssemblerMIPS64::ma_dshd(Register rd, Register rt) {
  as_dshd(rd, rt);
}

void MacroAssemblerMIPS64::ma_dctz(Register rd, Register rs) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register scratch2 = temps.Acquire();
  ma_dnegu(scratch, rs);
  as_and(rd, scratch, rs);
  as_dclz(rd, rd);
  ma_dnegu(scratch2, rd);
  ma_daddu(scratch2, Imm32(0x3f));
#ifdef MIPS64
  as_selnez(scratch2, scratch2, scratch);
  as_seleqz(rd, rd, scratch);
  as_or(rd, rd, scratch2);
#else
  as_movn(rd, scratch2, scratch);
#endif
}

// Arithmetic-based ops.

// Add.
void MacroAssemblerMIPS64::ma_daddu(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInSignedRange(imm.value)) {
    as_daddiu(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_daddu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPS64::ma_daddu(Register rd, Register rs, ImmWord imm) {
  if (Imm16::IsInSignedRange(int64_t(imm.value))) {
    as_daddiu(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_daddu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPS64::ma_daddu(Register rd, Register rs) {
  as_daddu(rd, rd, rs);
}

void MacroAssemblerMIPS64::ma_daddu(Register rd, Imm32 imm) {
  ma_daddu(rd, rd, imm);
}

void MacroAssemblerMIPS64::ma_add32TestOverflow(Register rd, Register rs,
                                                Register rt, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  as_daddu(scratch2, rs, rt);
  as_addu(rd, rs, rt);
  ma_b(rd, scratch2, overflow, Assembler::NotEqual);
}

void MacroAssemblerMIPS64::ma_add32TestOverflow(Register rd, Register rs,
                                                Imm32 imm, Label* overflow) {
  // Check for signed range because of as_daddiu
  UseScratchRegisterScope temps(*this);
  if (Imm16::IsInSignedRange(imm.value)) {
    Register scratch2 = temps.Acquire();
    as_daddiu(scratch2, rs, imm.value);
    as_addiu(rd, rs, imm.value);
    ma_b(rd, scratch2, overflow, Assembler::NotEqual);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    ma_add32TestOverflow(rd, rs, scratch, overflow);
  }
}

void MacroAssemblerMIPS64::ma_addPtrTestOverflow(Register rd, Register rs,
                                                 Register rt, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  MOZ_ASSERT_IF(rd == rs, rs != rt);
  MOZ_ASSERT(rd != scratch2);

  if (rs == rt) {
    as_daddu(rd, rs, rs);
    as_xor(scratch2, rs, rd);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    MOZ_ASSERT(rs != scratch2);
    MOZ_ASSERT(rt != scratch2);

    // If the sign of rs and rt are different, no overflow
    as_xor(scratch2, rs, rt);
    as_nor(scratch2, scratch2, zero);

    // handle rd == rt
    ma_move(scratch, rt);
    as_daddu(rd, rs, rt);
    as_xor(scratch, rd, scratch);
    as_and(scratch2, scratch, scratch2);
  }

  ma_b(scratch2, zero, overflow, Assembler::LessThan);
}

void MacroAssemblerMIPS64::ma_addPtrTestOverflow(Register rd, Register rs,
                                                 Imm32 imm, Label* overflow) {
  ma_addPtrTestOverflow(rd, rs, ImmWord(imm.value), overflow);
}

void MacroAssemblerMIPS64::ma_addPtrTestOverflow(Register rd, Register rs,
                                                 ImmWord imm, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    // If the sign of rs and rt are different, no overflow
    ma_li(scratch, imm);
    as_xor(scratch2, rs, scratch);
    as_nor(scratch2, scratch2, zero);

    as_daddu(rd, rs, scratch);
    as_xor(scratch, rd, scratch);
    as_and(scratch2, scratch, scratch2);
  }
  ma_b(scratch2, zero, overflow, Assembler::LessThan);
}

void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond, Register rd,
                                              Register rs, Register rt,
                                              Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  as_daddu(rd, rs, rt);
  as_sltu(scratch2, rd, rt);
  ma_b(scratch2, scratch2, overflow,
       cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}

void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond, Register rd,
                                              Register rs, Imm32 imm,
                                              Label* overflow) {
  // Check for signed range because of as_daddiu
  UseScratchRegisterScope temps(*this);
  if (Imm16::IsInSignedRange(imm.value)) {
    Register scratch2 = temps.Acquire();
    as_daddiu(rd, rs, imm.value);
    as_sltiu(scratch2, rd, imm.value);
    ma_b(scratch2, scratch2, overflow,
         cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    ma_addPtrTestCarry(cond, rd, rs, scratch, overflow);
  }
}

void MacroAssemblerMIPS64::ma_addPtrTestCarry(Condition cond, Register rd,
                                              Register rs, ImmWord imm,
                                              Label* overflow) {
  // Check for signed range because of as_daddiu
  UseScratchRegisterScope temps(*this);
  if (Imm16::IsInSignedRange(int64_t(imm.value))) {
    Register scratch2 = temps.Acquire();
    as_daddiu(rd, rs, imm.value);
    as_sltiu(scratch2, rd, imm.value);
    ma_b(scratch2, scratch2, overflow,
         cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    ma_addPtrTestCarry(cond, rd, rs, scratch, overflow);
  }
}

void MacroAssemblerMIPS64::ma_addPtrTestSigned(Condition cond, Register rd,
                                               Register rj, Register rk,
                                               Label* taken) {
  MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned);

  as_daddu(rd, rj, rk);
  ma_b(rd, rd, taken, cond);
}

void MacroAssemblerMIPS64::ma_addPtrTestSigned(Condition cond, Register rd,
                                               Register rj, Imm32 imm,
                                               Label* taken) {
  MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned);

  ma_daddu(rd, rj, imm);
  ma_b(rd, rd, taken, cond);
}

void MacroAssemblerMIPS64::ma_addPtrTestSigned(Condition cond, Register rd,
                                               Register rj, ImmWord imm,
                                               Label* taken) {
  MOZ_ASSERT(cond == Assembler::Signed || cond == Assembler::NotSigned);

  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_li(scratch2, imm);
  ma_addPtrTestSigned(cond, rd, rj, scratch2, taken);
}

// Subtract.
void MacroAssemblerMIPS64::ma_dsubu(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInSignedRange(-imm.value)) {
    as_daddiu(rd, rs, -imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_dsubu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPS64::ma_dsubu(Register rd, Register rs, ImmWord imm) {
  if (Imm16::IsInSignedRange(int64_t(-imm.value))) {
    as_daddiu(rd, rs, -imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_dsubu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPS64::ma_dsubu(Register rd, Register rs) {
  as_dsubu(rd, rd, rs);
}

void MacroAssemblerMIPS64::ma_dsubu(Register rd, Imm32 imm) {
  ma_dsubu(rd, rd, imm);
}

void MacroAssemblerMIPS64::ma_sub32TestOverflow(Register rd, Register rs,
                                                Register rt, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  as_dsubu(scratch2, rs, rt);
  as_subu(rd, rs, rt);
  ma_b(rd, scratch2, overflow, Assembler::NotEqual);
}

void MacroAssemblerMIPS64::ma_subPtrTestOverflow(Register rd, Register rs,
                                                 Register rt, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  MOZ_ASSERT_IF(rs == rd, rs != rt);
  MOZ_ASSERT(rs != scratch2);
  MOZ_ASSERT(rt != scratch2);
  MOZ_ASSERT(rd != scratch2);

  Register rs_copy = rs;

  if (rs == rd) {
    ma_move(scratch2, rs);
    rs_copy = scratch2;
  }

  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    MOZ_ASSERT(rd != scratch);

    ma_move(scratch, rt);
    as_dsubu(rd, rs, rt);
    // If the sign of rs and rt are the same, no overflow
    as_xor(scratch, rs_copy, scratch);
    // Check if the sign of rd and rs are the same
    as_xor(scratch2, rd, rs_copy);
    as_and(scratch2, scratch2, scratch);
  }

  ma_b(scratch2, zero, overflow, Assembler::LessThan);
}

void MacroAssemblerMIPS64::ma_subPtrTestOverflow(Register rd, Register rs,
                                                 Imm32 imm, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  MOZ_ASSERT(rs != scratch2);
  MOZ_ASSERT(rd != scratch2);

  Register rs_copy = rs;

  if (rs == rd) {
    ma_move(scratch2, rs);
    rs_copy = scratch2;
  }

  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    MOZ_ASSERT(rd != scratch);

    ma_li(scratch, imm);
    as_dsubu(rd, rs, scratch);
    // If the sign of rs and rt are the same, no overflow
    as_xor(scratch, rs_copy, scratch);
    // Check if the sign of rd and rs are the same
    as_xor(scratch2, rd, rs_copy);
    as_and(scratch2, scratch2, scratch);
  }

  ma_b(scratch2, zero, overflow, Assembler::LessThan);
}

void MacroAssemblerMIPS64::ma_dmulu(Register rd, Register rs, Register rt) {
#ifdef MIPSR6
  as_dmulu(rd, rs, rt);
#else
  as_dmultu(rs, rt);
  as_mflo(rd);
#endif
}

void MacroAssemblerMIPS64::ma_dmulu(Register rd, Register rs, ImmWord imm) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_li(scratch, imm);
  ma_dmulu(rd, rs, scratch);
}

void MacroAssemblerMIPS64::ma_mulPtrTestOverflow(Register rd, Register rs,
                                                 Register rt, Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register scratch2 = temps.Acquire();
#ifdef MIPSR6
  if (rd == rs) {
    ma_move(scratch2, rs);
    rs = scratch2;
  }
  as_dmul(rd, rs, rt);
  as_dmuh(scratch2, rs, rt);
#else
  as_dmult(rs, rt);
  as_mflo(rd);
  as_mfhi(scratch2);
#endif
  as_dsra32(scratch, rd, 63);
  ma_b(scratch, scratch2, overflow, Assembler::NotEqual);
}

// Memory.
FaultingCodeOffset MacroAssemblerMIPS64::ma_load(Register dest, Address address,
                                                 LoadStoreSize size,
                                                 LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  int16_t encodedOffset;
  Register base;
  FaultingCodeOffset fco;

  if (isLoongson() && ZeroExtend != extension &&
      !Imm16::IsInSignedRange(address.offset)) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(address.offset));
    base = address.base;

    fco = FaultingCodeOffset(currentOffset());
    switch (size) {
      case SizeByte:
        as_gslbx(dest, base, scratch, 0);
        break;
      case SizeHalfWord:
        as_gslhx(dest, base, scratch, 0);
        break;
      case SizeWord:
        as_gslwx(dest, base, scratch, 0);
        break;
      case SizeDouble:
        as_gsldx(dest, base, scratch, 0);
        break;
      default:
        MOZ_CRASH("Invalid argument for ma_load");
    }
    return fco;
  }

  if (!Imm16::IsInSignedRange(address.offset)) {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(address.offset));
    as_daddu(scratch, address.base, scratch);
    base = scratch;
    encodedOffset = Imm16(0).encode();
  } else {
    encodedOffset = Imm16(address.offset).encode();
    base = address.base;
  }

  fco = FaultingCodeOffset(currentOffset());
  switch (size) {
    case SizeByte:
      if (ZeroExtend == extension) {
        as_lbu(dest, base, encodedOffset);
      } else {
        as_lb(dest, base, encodedOffset);
      }
      break;
    case SizeHalfWord:
      if (ZeroExtend == extension) {
        as_lhu(dest, base, encodedOffset);
      } else {
        as_lh(dest, base, encodedOffset);
      }
      break;
    case SizeWord:
      if (ZeroExtend == extension) {
        as_lwu(dest, base, encodedOffset);
      } else {
        as_lw(dest, base, encodedOffset);
      }
      break;
    case SizeDouble:
      as_ld(dest, base, encodedOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_load");
  }
  return fco;
}

void MacroAssemblerMIPS64::ma_store(ImmWord imm, const BaseIndex& dest,
                                    LoadStoreSize size,
                                    LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();

  // Make sure that scratch2 contains absolute address so that
  // offset is 0.
  asMasm().computeEffectiveAddress(dest, scratch2);
  // Scrach register is free now, use it for loading imm value
  Register scratch = temps.Acquire();
  ma_li(scratch, ImmWord(imm.value));

  // with offset=0 scratch will not be used in ma_store()
  // so we can use it as a parameter here
  ma_store(scratch, Address(scratch2, 0), size, extension);
}

void MacroAssemblerMIPS64::ma_store(ImmWord imm, Address address,
                                    LoadStoreSize size,
                                    LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_li(scratch2, imm);
  ma_store(scratch2, address, size, extension);
}

FaultingCodeOffset MacroAssemblerMIPS64::ma_store(
    Register data, Address address, LoadStoreSize size,
    LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  int16_t encodedOffset;
  Register base;
  FaultingCodeOffset fco;

  if (isLoongson() && !Imm16::IsInSignedRange(address.offset)) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(address.offset));
    base = address.base;

    fco = FaultingCodeOffset(currentOffset());
    switch (size) {
      case SizeByte:
        as_gssbx(data, base, scratch, 0);
        break;
      case SizeHalfWord:
        as_gsshx(data, base, scratch, 0);
        break;
      case SizeWord:
        as_gsswx(data, base, scratch, 0);
        break;
      case SizeDouble:
        as_gssdx(data, base, scratch, 0);
        break;
      default:
        MOZ_CRASH("Invalid argument for ma_store");
    }
    return fco;
  }

  if (!Imm16::IsInSignedRange(address.offset)) {
    // assert on scratch ownership
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(address.offset));
    as_daddu(scratch, address.base, scratch);
    base = scratch;
    encodedOffset = Imm16(0).encode();
  } else {
    encodedOffset = Imm16(address.offset).encode();
    base = address.base;
  }

  fco = FaultingCodeOffset(currentOffset());
  switch (size) {
    case SizeByte:
      as_sb(data, base, encodedOffset);
      break;
    case SizeHalfWord:
      as_sh(data, base, encodedOffset);
      break;
    case SizeWord:
      as_sw(data, base, encodedOffset);
      break;
    case SizeDouble:
      as_sd(data, base, encodedOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_store");
  }
  return fco;
}

void MacroAssemblerMIPS64Compat::computeScaledAddress32(
    const BaseIndex& address, Register dest) {
  Register base = address.base;
  Register index = address.index;
  int32_t shift = Imm32::ShiftOf(address.scale).value;
  if (shift && base == zero) {
    MOZ_ASSERT(shift <= 4);
    ma_sll(dest, index, Imm32(shift));
  } else if (shift) {
    UseScratchRegisterScope temps(*this);
    Register tmp = temps.Acquire();
    MOZ_ASSERT(shift <= 4);
    ma_sll(tmp, index, Imm32(shift));
    as_addu(dest, base, tmp);
  } else {
    as_addu(dest, base, index);
  }
}

void MacroAssemblerMIPS64Compat::computeScaledAddress(const BaseIndex& address,
                                                      Register dest) {
  int32_t shift = Imm32::ShiftOf(address.scale).value;
  if (shift) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_dsll(scratch, address.index, Imm32(shift));
    as_daddu(dest, address.base, scratch);
  } else {
    as_daddu(dest, address.base, address.index);
  }
}

void MacroAssemblerMIPS64Compat::computeEffectiveAddress(
    const BaseIndex& address, Register dest) {
  computeScaledAddress(address, dest);
  if (address.offset) {
    asMasm().addPtr(Imm32(address.offset), dest);
  }
}

// Shortcut for when we know we're transferring 32 bits of data.
void MacroAssemblerMIPS64::ma_pop(Register r) {
  as_ld(r, StackPointer, 0);
  as_daddiu(StackPointer, StackPointer, sizeof(intptr_t));
}

void MacroAssemblerMIPS64::ma_push(Register r) {
  UseScratchRegisterScope temps(*this);
  if (r == sp) {
    Register scratch = temps.Acquire();
    // Pushing sp requires one more instruction.
    ma_move(scratch, sp);
    r = scratch;
  }

  as_daddiu(StackPointer, StackPointer, -int32_t(sizeof(intptr_t)));
  as_sd(r, StackPointer, 0);
}

// Branches when done from within mips-specific code.
void MacroAssemblerMIPS64::ma_b(Register lhs, ImmWord imm, Label* label,
                                Condition c, JumpKind jumpKind) {
  if (imm.value <= INT32_MAX) {
    ma_b(lhs, Imm32(uint32_t(imm.value)), label, c, jumpKind);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    ma_b(lhs, scratch, label, c, jumpKind);
  }
}

void MacroAssemblerMIPS64::ma_b(Register lhs, Address addr, Label* label,
                                Condition c, JumpKind jumpKind) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_load(scratch, addr, SizeDouble);
  ma_b(lhs, scratch, label, c, jumpKind);
}

void MacroAssemblerMIPS64::ma_b(Address addr, Imm32 imm, Label* label,
                                Condition c, JumpKind jumpKind) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_load(scratch2, addr, SizeDouble);
  ma_b(scratch2, imm, label, c, jumpKind);
}

void MacroAssemblerMIPS64::ma_b(Address addr, ImmGCPtr imm, Label* label,
                                Condition c, JumpKind jumpKind) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_load(scratch2, addr, SizeDouble);
  ma_b(scratch2, imm, label, c, jumpKind);
}

void MacroAssemblerMIPS64::ma_bal(Label* label, DelaySlotFill delaySlotFill) {
  spew("branch .Llabel %p\n", label);
  if (label->bound()) {
    UseScratchRegisterScope temps(*this);
    // Generate the long jump for calls because return address has to be
    // the address after the reserved block.
    addLongJump(nextOffset(), BufferOffset(label->offset()));
    Register scratch = temps.Acquire();
    ma_liPatchable(scratch, ImmWord(LabelBase::INVALID_OFFSET));
    as_jalr(scratch);
    if (delaySlotFill == FillDelaySlot) {
      as_nop();
    }
    return;
  }

  // Second word holds a pointer to the next branch in label's chain.
  uint32_t nextInChain =
      label->used() ? label->offset() : LabelBase::INVALID_OFFSET;

  // Make the whole branch continous in the buffer. The '6'
  // instructions are writing at below (contain delay slot).
  m_buffer.ensureSpace(6 * sizeof(uint32_t));

  spew("bal .Llabel %p\n", label);
  BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
  writeInst(nextInChain);
  if (!oom()) {
    label->use(bo.getOffset());
  }
  // Leave space for long jump.
  as_nop();
  as_nop();
  as_nop();
  if (delaySlotFill == FillDelaySlot) {
    as_nop();
  }
}

void MacroAssemblerMIPS64::branchWithCode(InstImm code, Label* label,
                                          JumpKind jumpKind,
                                          Register branchCodeScratch) {
  // simply output the pointer of one label as its id,
  // notice that after one label destructor, the pointer will be reused.
  spew("branch .Llabel %p", label);
  MOZ_ASSERT(code.encode() !=
             InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
  InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));

  if (label->bound()) {
    int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();

    if (BOffImm16::IsInRange(offset)) {
      jumpKind = ShortJump;
    }

    if (jumpKind == ShortJump) {
      MOZ_ASSERT(BOffImm16::IsInRange(offset));
      code.setBOffImm16(BOffImm16(offset));
#ifdef JS_JITSPEW
      decodeBranchInstAndSpew(code);
#endif
      writeInst(code.encode());
      as_nop();
      return;
    }

    if (code.encode() == inst_beq.encode()) {
      UseScratchRegisterScope temps(*this);
      // Handle long jump
      addLongJump(nextOffset(), BufferOffset(label->offset()));
      Register scratch = branchCodeScratch;
      if (scratch == InvalidReg) {
        // Request a new scratch register if |branchCodeScratch| is invalid.
        // NB: |branchCodeScratch| must not be used before encoding |code|.
        scratch = temps.Acquire();
      }
      ma_liPatchable(scratch, ImmWord(LabelBase::INVALID_OFFSET));
      as_jr(scratch);
      as_nop();
      return;
    }

    // Handle long conditional branch, the target offset is based on self,
    // point to next instruction of nop at below.
    spew("invert branch .Llabel %p", label);
    InstImm code_r = invertBranch(code, BOffImm16(7 * sizeof(uint32_t)));
#ifdef JS_JITSPEW
    decodeBranchInstAndSpew(code_r);
#endif
    writeInst(code_r.encode());
    UseScratchRegisterScope temps(*this);
    // No need for a "nop" here because we can clobber scratch.
    addLongJump(nextOffset(), BufferOffset(label->offset()));
    Register scratch = branchCodeScratch;
    if (scratch == InvalidReg) {
      // Request a new scratch register if |branchCodeScratch| is invalid.
      // NB: |branchCodeScratch| must not be used before encoding |code|.
      scratch = temps.Acquire();
    }
    ma_liPatchable(scratch, ImmWord(LabelBase::INVALID_OFFSET));
    as_jr(scratch);
    as_nop();
    return;
  }

  // Generate open jump and link it to a label.

  // Second word holds a pointer to the next branch in label's chain.
  uint32_t nextInChain =
      label->used() ? label->offset() : LabelBase::INVALID_OFFSET;

  if (jumpKind == ShortJump) {
    // Make the whole branch continous in the buffer.
    m_buffer.ensureSpace(2 * sizeof(uint32_t));

    // Indicate that this is short jump with offset 4.
    code.setBOffImm16(BOffImm16(4));
#ifdef JS_JITSPEW
    decodeBranchInstAndSpew(code);
#endif
    BufferOffset bo = writeInst(code.encode());
    writeInst(nextInChain);
    if (!oom()) {
      label->use(bo.getOffset());
    }
    return;
  }

  bool conditional = code.encode() != inst_beq.encode();

  // Make the whole branch continous in the buffer. The '7'
  // instructions are writing at below (contain conditional nop).
  m_buffer.ensureSpace(7 * sizeof(uint32_t));

#ifdef JS_JITSPEW
  decodeBranchInstAndSpew(code);
#endif
  BufferOffset bo = writeInst(code.encode());
  writeInst(nextInChain);
  if (!oom()) {
    label->use(bo.getOffset());
  }
  // Leave space for potential long jump.
  as_nop();
  as_nop();
  as_nop();
  as_nop();
  if (conditional) {
    as_nop();
  }
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Register rs, ImmWord imm,
                                      Condition c) {
  if (imm.value <= INT32_MAX) {
    ma_cmp_set(rd, rs, Imm32(uint32_t(imm.value)), c);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    ma_cmp_set(rd, rs, scratch, c);
  }
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Register rs, ImmPtr imm,
                                      Condition c) {
  ma_cmp_set(rd, rs, ImmWord(uintptr_t(imm.value)), c);
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Register rs, ImmGCPtr imm,
                                      Condition c) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_li(scratch, imm);
  ma_cmp_set(rd, rs, scratch, c);
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Address address, Register rt,
                                      Condition c) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_load(scratch2, address, SizeDouble);
  ma_cmp_set(rd, scratch2, rt, c);
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Address address, ImmWord imm,
                                      Condition c) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_load(scratch2, address, SizeDouble);
  ma_cmp_set(rd, scratch2, imm, c);
}

void MacroAssemblerMIPS64::ma_cmp_set(Register rd, Address address, Imm32 imm,
                                      Condition c) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_load(scratch2, address, SizeWord, SignExtend);
  ma_cmp_set(rd, scratch2, imm, c);
}

// fp instructions
void MacroAssemblerMIPS64::ma_lid(FloatRegister dest, double value) {
  ImmWord imm(mozilla::BitwiseCast<uint64_t>(value));

  if (imm.value != 0) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    moveToDouble(scratch, dest);
  } else {
    moveToDouble(zero, dest);
  }
}

void MacroAssemblerMIPS64::ma_mv(FloatRegister src, ValueOperand dest) {
  as_dmfc1(dest.valueReg(), src);
}

void MacroAssemblerMIPS64::ma_mv(ValueOperand src, FloatRegister dest) {
  as_dmtc1(src.valueReg(), dest);
}

FaultingCodeOffset MacroAssemblerMIPS64::ma_ls(FloatRegister ft,
                                               Address address) {
  UseScratchRegisterScope temps(*this);
  FaultingCodeOffset fco;
  if (Imm16::IsInSignedRange(address.offset)) {
    fco = FaultingCodeOffset(currentOffset());
    as_lwc1(ft, address.base, address.offset);
  } else {
    Register scratch = temps.Acquire();
    MOZ_ASSERT(address.base != scratch);
    ma_li(scratch, Imm32(address.offset));
    if (isLoongson()) {
      fco = FaultingCodeOffset(currentOffset());
      as_gslsx(ft, address.base, scratch, 0);
    } else {
      as_daddu(scratch, address.base, scratch);
      fco = FaultingCodeOffset(currentOffset());
      as_lwc1(ft, scratch, 0);
    }
  }
  return fco;
}

FaultingCodeOffset MacroAssemblerMIPS64::ma_ld(FloatRegister ft,
                                               Address address) {
  UseScratchRegisterScope temps(*this);
  FaultingCodeOffset fco;
  if (Imm16::IsInSignedRange(address.offset)) {
    fco = FaultingCodeOffset(currentOffset());
    as_ldc1(ft, address.base, address.offset);
  } else {
    Register scratch = temps.Acquire();
    MOZ_ASSERT(address.base != scratch);
    ma_li(scratch, Imm32(address.offset));
    if (isLoongson()) {
      fco = FaultingCodeOffset(currentOffset());
      as_gsldx(ft, address.base, scratch, 0);
    } else {
      as_daddu(scratch, address.base, scratch);
      fco = FaultingCodeOffset(currentOffset());
      as_ldc1(ft, scratch, 0);
    }
  }
  return fco;
}

FaultingCodeOffset MacroAssemblerMIPS64::ma_sd(FloatRegister ft,
                                               Address address) {
  UseScratchRegisterScope temps(*this);
  FaultingCodeOffset fco;
  if (Imm16::IsInSignedRange(address.offset)) {
    fco = FaultingCodeOffset(currentOffset());
    as_sdc1(ft, address.base, address.offset);
  } else {
    Register scratch = temps.Acquire();
    MOZ_ASSERT(address.base != scratch);
    ma_li(scratch, Imm32(address.offset));
    if (isLoongson()) {
      fco = FaultingCodeOffset(currentOffset());
      as_gssdx(ft, address.base, scratch, 0);
    } else {
      as_daddu(scratch, address.base, scratch);
      fco = FaultingCodeOffset(currentOffset());
      as_sdc1(ft, scratch, 0);
    }
  }
  return fco;
}

FaultingCodeOffset MacroAssemblerMIPS64::ma_ss(FloatRegister ft,
                                               Address address) {
  UseScratchRegisterScope temps(*this);
  FaultingCodeOffset fco;
  if (Imm16::IsInSignedRange(address.offset)) {
    fco = FaultingCodeOffset(currentOffset());
    as_swc1(ft, address.base, address.offset);
  } else {
    Register scratch = temps.Acquire();
    MOZ_ASSERT(address.base != scratch);
    ma_li(scratch, Imm32(address.offset));
    if (isLoongson()) {
      fco = FaultingCodeOffset(currentOffset());
      as_gsssx(ft, address.base, scratch, 0);
    } else {
      as_daddu(scratch, address.base, scratch);
      fco = FaultingCodeOffset(currentOffset());
      as_swc1(ft, scratch, 0);
    }
  }
  return fco;
}

void MacroAssemblerMIPS64::ma_pop(FloatRegister f) {
  if (f.isDouble()) {
    as_ldc1(f, StackPointer, 0);
  } else {
    MOZ_ASSERT(f.isSingle(), "simd128 not supported");
    as_lwc1(f, StackPointer, 0);
  }
  as_daddiu(StackPointer, StackPointer, sizeof(double));
}

void MacroAssemblerMIPS64::ma_push(FloatRegister f) {
  as_daddiu(StackPointer, StackPointer, -int32_t(sizeof(double)));
  if (f.isDouble()) {
    as_sdc1(f, StackPointer, 0);
  } else {
    MOZ_ASSERT(f.isSingle(), "simd128 not supported");
    as_swc1(f, StackPointer, 0);
  }
}

bool MacroAssemblerMIPS64Compat::buildOOLFakeExitFrame(void* fakeReturnAddr) {
  asMasm().Push(FrameDescriptor(FrameType::IonJS));  // descriptor_
  asMasm().Push(ImmPtr(fakeReturnAddr));
  asMasm().Push(FramePointer);
  return true;
}

void MacroAssemblerMIPS64Compat::move32(Imm32 imm, Register dest) {
  ma_li(dest, imm);
}

void MacroAssemblerMIPS64Compat::move32(Register src, Register dest) {
  ma_sll(dest, src, Imm32(0));
}

void MacroAssemblerMIPS64Compat::movePtr(Register src, Register dest) {
  ma_move(dest, src);
}
void MacroAssemblerMIPS64Compat::movePtr(ImmWord imm, Register dest) {
  ma_li(dest, imm);
}

void MacroAssemblerMIPS64Compat::movePtr(ImmGCPtr imm, Register dest) {
  ma_li(dest, imm);
}

void MacroAssemblerMIPS64Compat::movePtr(ImmPtr imm, Register dest) {
  movePtr(ImmWord(uintptr_t(imm.value)), dest);
}
void MacroAssemblerMIPS64Compat::movePtr(wasm::SymbolicAddress imm,
                                         Register dest) {
  append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm));
  ma_liPatchable(dest, ImmWord(-1));
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load8ZeroExtend(
    const Address& address, Register dest) {
  return ma_load(dest, address, SizeByte, ZeroExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load8ZeroExtend(
    const BaseIndex& src, Register dest) {
  return ma_load(dest, src, SizeByte, ZeroExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load8SignExtend(
    const Address& address, Register dest) {
  return ma_load(dest, address, SizeByte, SignExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load8SignExtend(
    const BaseIndex& src, Register dest) {
  return ma_load(dest, src, SizeByte, SignExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load16ZeroExtend(
    const Address& address, Register dest) {
  return ma_load(dest, address, SizeHalfWord, ZeroExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load16ZeroExtend(
    const BaseIndex& src, Register dest) {
  return ma_load(dest, src, SizeHalfWord, ZeroExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load16SignExtend(
    const Address& address, Register dest) {
  return ma_load(dest, address, SizeHalfWord, SignExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load16SignExtend(
    const BaseIndex& src, Register dest) {
  return ma_load(dest, src, SizeHalfWord, SignExtend);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load32(const Address& address,
                                                      Register dest) {
  return ma_load(dest, address, SizeWord);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::load32(const BaseIndex& address,
                                                      Register dest) {
  return ma_load(dest, address, SizeWord);
}

void MacroAssemblerMIPS64Compat::load32(AbsoluteAddress address,
                                        Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(ImmPtr(address.addr), scratch);
  load32(Address(scratch, 0), dest);
}

void MacroAssemblerMIPS64Compat::load32(wasm::SymbolicAddress address,
                                        Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(address, scratch);
  load32(Address(scratch, 0), dest);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::loadPtr(const Address& address,
                                                       Register dest) {
  return ma_load(dest, address, SizeDouble);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::loadPtr(const BaseIndex& src,
                                                       Register dest) {
  return ma_load(dest, src, SizeDouble);
}

void MacroAssemblerMIPS64Compat::loadPtr(AbsoluteAddress address,
                                         Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(ImmPtr(address.addr), scratch);
  loadPtr(Address(scratch, 0), dest);
}

void MacroAssemblerMIPS64Compat::loadPtr(wasm::SymbolicAddress address,
                                         Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(address, scratch);
  loadPtr(Address(scratch, 0), dest);
}

void MacroAssemblerMIPS64Compat::loadPrivate(const Address& address,
                                             Register dest) {
  loadPtr(address, dest);
}

void MacroAssemblerMIPS64Compat::loadUnalignedDouble(
    const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
    FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(src, scratch2);
  BufferOffset load;
  if (Imm16::IsInSignedRange(src.offset) &&
      Imm16::IsInSignedRange(src.offset + 7)) {
    load = as_ldl(temp, scratch2, src.offset + 7);
    as_ldr(temp, scratch2, src.offset);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(src.offset));
    as_daddu(scratch, scratch2, scratch);
    load = as_ldl(temp, scratch, 7);
    as_ldr(temp, scratch, 0);
  }
  append(access, wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
         FaultingCodeOffset(load.getOffset()));
  moveToDouble(temp, dest);
}

void MacroAssemblerMIPS64Compat::loadUnalignedFloat32(
    const wasm::MemoryAccessDesc& access, const BaseIndex& src, Register temp,
    FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(src, scratch2);
  BufferOffset load;
  if (Imm16::IsInSignedRange(src.offset) &&
      Imm16::IsInSignedRange(src.offset + 3)) {
    load = as_lwl(temp, scratch2, src.offset + 3);
    as_lwr(temp, scratch2, src.offset);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(src.offset));
    as_daddu(scratch, scratch2, scratch);
    load = as_lwl(temp, scratch, 3);
    as_lwr(temp, scratch, 0);
  }
  append(access, wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
         FaultingCodeOffset(load.getOffset()));
  moveToFloat32(temp, dest);
}

void MacroAssemblerMIPS64Compat::store8(Imm32 imm, const Address& address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_li(scratch2, imm);
  ma_store(scratch2, address, SizeByte);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store8(Register src,
                                                      const Address& address) {
  return ma_store(src, address, SizeByte);
}

void MacroAssemblerMIPS64Compat::store8(Imm32 imm, const BaseIndex& dest) {
  ma_store(imm, dest, SizeByte);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store8(Register src,
                                                      const BaseIndex& dest) {
  return ma_store(src, dest, SizeByte);
}

void MacroAssemblerMIPS64Compat::store16(Imm32 imm, const Address& address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  ma_li(scratch2, imm);
  ma_store(scratch2, address, SizeHalfWord);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store16(Register src,
                                                       const Address& address) {
  return ma_store(src, address, SizeHalfWord);
}

void MacroAssemblerMIPS64Compat::store16(Imm32 imm, const BaseIndex& dest) {
  ma_store(imm, dest, SizeHalfWord);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store16(
    Register src, const BaseIndex& address) {
  return ma_store(src, address, SizeHalfWord);
}

void MacroAssemblerMIPS64Compat::store32(Register src,
                                         AbsoluteAddress address) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(ImmPtr(address.addr), scratch);
  store32(src, Address(scratch, 0));
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store32(Register src,
                                                       const Address& address) {
  return ma_store(src, address, SizeWord);
}

void MacroAssemblerMIPS64Compat::store32(Imm32 src, const Address& address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  move32(src, scratch2);
  ma_store(scratch2, address, SizeWord);
}

void MacroAssemblerMIPS64Compat::store32(Imm32 imm, const BaseIndex& dest) {
  ma_store(imm, dest, SizeWord);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::store32(Register src,
                                                       const BaseIndex& dest) {
  return ma_store(src, dest, SizeWord);
}

template <typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmWord imm, T address) {
  ma_store(imm, address, SizeDouble);
}

template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmWord imm,
                                                            Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
    ImmWord imm, BaseIndex address);

template <typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmPtr imm, T address) {
  storePtr(ImmWord(uintptr_t(imm.value)), address);
}

template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmPtr imm,
                                                            Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
    ImmPtr imm, BaseIndex address);

template <typename T>
void MacroAssemblerMIPS64Compat::storePtr(ImmGCPtr imm, T address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  movePtr(imm, scratch2);
  storePtr(scratch2, address);
}

template void MacroAssemblerMIPS64Compat::storePtr<Address>(ImmGCPtr imm,
                                                            Address address);
template void MacroAssemblerMIPS64Compat::storePtr<BaseIndex>(
    ImmGCPtr imm, BaseIndex address);

FaultingCodeOffset MacroAssemblerMIPS64Compat::storePtr(
    Register src, const Address& address) {
  return ma_store(src, address, SizeDouble);
}

FaultingCodeOffset MacroAssemblerMIPS64Compat::storePtr(
    Register src, const BaseIndex& address) {
  return ma_store(src, address, SizeDouble);
}

void MacroAssemblerMIPS64Compat::storePtr(Register src, AbsoluteAddress dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  movePtr(ImmPtr(dest.addr), scratch);
  storePtr(src, Address(scratch, 0));
}

void MacroAssemblerMIPS64Compat::storeUnalignedFloat32(
    const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
    const BaseIndex& dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(dest, scratch2);
  moveFromFloat32(src, temp);
  BufferOffset store;
  if (Imm16::IsInSignedRange(dest.offset) &&
      Imm16::IsInSignedRange(dest.offset + 3)) {
    store = as_swl(temp, scratch2, dest.offset + 3);
    as_swr(temp, scratch2, dest.offset);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(dest.offset));
    as_daddu(scratch, scratch2, scratch);
    store = as_swl(temp, scratch, 3);
    as_swr(temp, scratch, 0);
  }
  append(access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
         FaultingCodeOffset(store.getOffset()));
}

void MacroAssemblerMIPS64Compat::storeUnalignedDouble(
    const wasm::MemoryAccessDesc& access, FloatRegister src, Register temp,
    const BaseIndex& dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(dest, scratch2);
  moveFromDouble(src, temp);

  BufferOffset store;
  if (Imm16::IsInSignedRange(dest.offset) &&
      Imm16::IsInSignedRange(dest.offset + 7)) {
    store = as_sdl(temp, scratch2, dest.offset + 7);
    as_sdr(temp, scratch2, dest.offset);
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(dest.offset));
    as_daddu(scratch, scratch2, scratch);
    store = as_sdl(temp, scratch, 7);
    as_sdr(temp, scratch, 0);
  }
  append(access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
         FaultingCodeOffset(store.getOffset()));
}

void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register scratch2 = temps.Acquire();
  as_roundwd(ScratchDoubleReg, input);
  ma_li(scratch, Imm32(255));
  as_mfc1(output, ScratchDoubleReg);
#ifdef MIPSR6
  as_slti(scratch2, output, 0);
  as_seleqz(output, output, scratch2);
  as_sltiu(scratch2, output, 255);
  as_selnez(output, output, scratch2);
  as_seleqz(scratch, scratch, scratch2);
  as_or(output, output, scratch);
#else
  zeroDouble(ScratchDoubleReg);
  as_sltiu(scratch2, output, 255);
  as_colt(DoubleFloat, ScratchDoubleReg, input);
  // if res > 255; res = 255;
  as_movz(output, scratch, scratch2);
  // if !(input > 0); res = 0;
  as_movf(output, zero);
#endif
}

void MacroAssemblerMIPS64Compat::testNullSet(Condition cond,
                                             const ValueOperand& value,
                                             Register dest) {
  MOZ_ASSERT(cond == Equal || cond == NotEqual);
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  splitTag(value, scratch2);
  ma_cmp_set(dest, scratch2, ImmTag(JSVAL_TAG_NULL), cond);
}

void MacroAssemblerMIPS64Compat::testObjectSet(Condition cond,
                                               const ValueOperand& value,
                                               Register dest) {
  MOZ_ASSERT(cond == Equal || cond == NotEqual);
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  splitTag(value, scratch2);
  ma_cmp_set(dest, scratch2, ImmTag(JSVAL_TAG_OBJECT), cond);
}

void MacroAssemblerMIPS64Compat::testUndefinedSet(Condition cond,
                                                  const ValueOperand& value,
                                                  Register dest) {
  MOZ_ASSERT(cond == Equal || cond == NotEqual);
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  splitTag(value, scratch2);
  ma_cmp_set(dest, scratch2, ImmTag(JSVAL_TAG_UNDEFINED), cond);
}

void MacroAssemblerMIPS64Compat::unboxInt32(const ValueOperand& operand,
                                            Register dest) {
  ma_sll(dest, operand.valueReg(), Imm32(0));
}

void MacroAssemblerMIPS64Compat::unboxInt32(Register src, Register dest) {
  ma_sll(dest, src, Imm32(0));
}

void MacroAssemblerMIPS64Compat::unboxInt32(const Address& src, Register dest) {
  load32(Address(src.base, src.offset), dest);
}

void MacroAssemblerMIPS64Compat::unboxInt32(const BaseIndex& src,
                                            Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(src, scratch2);
  load32(Address(scratch2, src.offset), dest);
}

void MacroAssemblerMIPS64Compat::unboxBoolean(const ValueOperand& operand,
                                              Register dest) {
  ma_dext(dest, operand.valueReg(), Imm32(0), Imm32(32));
}

void MacroAssemblerMIPS64Compat::unboxBoolean(Register src, Register dest) {
  ma_dext(dest, src, Imm32(0), Imm32(32));
}

void MacroAssemblerMIPS64Compat::unboxBoolean(const Address& src,
                                              Register dest) {
  ma_load(dest, Address(src.base, src.offset), SizeWord, ZeroExtend);
}

void MacroAssemblerMIPS64Compat::unboxBoolean(const BaseIndex& src,
                                              Register dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(src, scratch2);
  ma_load(dest, Address(scratch2, src.offset), SizeWord, ZeroExtend);
}

void MacroAssemblerMIPS64Compat::unboxDouble(const ValueOperand& operand,
                                             FloatRegister dest) {
  as_dmtc1(operand.valueReg(), dest);
}

void MacroAssemblerMIPS64Compat::unboxDouble(const Address& src,
                                             FloatRegister dest) {
  ma_ld(dest, Address(src.base, src.offset));
}
void MacroAssemblerMIPS64Compat::unboxDouble(const BaseIndex& src,
                                             FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  loadPtr(src, scratch);
  unboxDouble(ValueOperand(scratch), dest);
}

void MacroAssemblerMIPS64Compat::unboxString(const ValueOperand& operand,
                                             Register dest) {
  unboxNonDouble(operand, dest, JSVAL_TYPE_STRING);
}

void MacroAssemblerMIPS64Compat::unboxString(Register src, Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}

void MacroAssemblerMIPS64Compat::unboxString(const Address& src,
                                             Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
}

void MacroAssemblerMIPS64Compat::unboxSymbol(const ValueOperand& operand,
                                             Register dest) {
  unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL);
}

void MacroAssemblerMIPS64Compat::unboxSymbol(Register src, Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}

void MacroAssemblerMIPS64Compat::unboxSymbol(const Address& src,
                                             Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
}

void MacroAssemblerMIPS64Compat::unboxBigInt(const ValueOperand& operand,
                                             Register dest) {
  unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT);
}

void MacroAssemblerMIPS64Compat::unboxBigInt(Register src, Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}

void MacroAssemblerMIPS64Compat::unboxBigInt(const Address& src,
                                             Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
}

void MacroAssemblerMIPS64Compat::unboxObject(const ValueOperand& src,
                                             Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}

void MacroAssemblerMIPS64Compat::unboxObject(Register src, Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}

void MacroAssemblerMIPS64Compat::unboxObject(const Address& src,
                                             Register dest) {
  unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
}

void MacroAssemblerMIPS64Compat::unboxValue(const ValueOperand& src,
                                            AnyRegister dest,
                                            JSValueType type) {
  if (dest.isFloat()) {
    Label notInt32, end;
    asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32);
    convertInt32ToDouble(src.valueReg(), dest.fpu());
    ma_b(&end, ShortJump);
    bind(¬Int32);
    unboxDouble(src, dest.fpu());
    bind(&end);
  } else {
    unboxNonDouble(src, dest.gpr(), type);
  }
}

void MacroAssemblerMIPS64Compat::boxDouble(FloatRegister src,
                                           const ValueOperand& dest,
                                           FloatRegister) {
  as_dmfc1(dest.valueReg(), src);
}

#ifdef DEBUG
static constexpr int32_t PayloadSize(JSValueType type) {
  switch (type) {
    case JSVAL_TYPE_UNDEFINED:
    case JSVAL_TYPE_NULL:
      return 0;
    case JSVAL_TYPE_BOOLEAN:
      return 1;
    case JSVAL_TYPE_INT32:
    case JSVAL_TYPE_MAGIC:
      return 32;
    case JSVAL_TYPE_STRING:
    case JSVAL_TYPE_SYMBOL:
    case JSVAL_TYPE_PRIVATE_GCTHING:
    case JSVAL_TYPE_BIGINT:
    case JSVAL_TYPE_OBJECT:
      return JSVAL_TAG_SHIFT;
    case JSVAL_TYPE_DOUBLE:
    case JSVAL_TYPE_UNKNOWN:
      break;
  }
  MOZ_CRASH("bad value type");
}
#endif

static void AssertValidPayload(MacroAssemblerMIPS64Compat& masm,
                               JSValueType type, Register payload,
                               Register scratch) {
#ifdef DEBUG
  if (type == JSVAL_TYPE_INT32) {
    // Ensure the payload is a properly sign-extended int32.
    Label signExtended;
    masm.ma_sll(scratch, payload, Imm32(0));
    masm.ma_b(payload, scratch, &signExtended, Assembler::Equal, ShortJump);
    masm.breakpoint();
    masm.bind(&signExtended);
  } else {
    // All bits above the payload must be zeroed.
    Label zeroed;
    masm.ma_dsrl(scratch, payload, Imm32(PayloadSize(type)));
    masm.ma_b(scratch, scratch, &zeroed, Assembler::Zero, ShortJump);
    masm.breakpoint();
    masm.bind(&zeroed);
  }
#endif
}

void MacroAssemblerMIPS64Compat::boxValue(JSValueType type, Register src,
                                          Register dest) {
  MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL);
  MOZ_ASSERT(src != dest);

  AssertValidPayload(*this, type, src, dest);

  ma_li(dest, ImmShiftedTag(type));

  if (type == JSVAL_TYPE_INT32) {
    ma_dins(dest, src, Imm32(0), Imm32(32));
  } else {
    as_or(dest, dest, src);
  }
}

void MacroAssemblerMIPS64Compat::boxValue(Register type, Register src,
                                          Register dest) {
  MOZ_ASSERT(src != dest);

#ifdef DEBUG
  Label done, isNullOrUndefined, isBoolean, isInt32OrMagic, isPointerSized;

  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_NULL),
                    &isNullOrUndefined);
  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_UNDEFINED),
                    &isNullOrUndefined);
  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_BOOLEAN),
                    &isBoolean);
  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_INT32),
                    &isInt32OrMagic);
  asMasm().branch32(Assembler::Equal, type, Imm32(JSVAL_TYPE_MAGIC),
                    &isInt32OrMagic);
  // GCThing types aren't currently supported, because ma_dins truncates
  // payloads above UINT32_MAX.
  breakpoint();
  {
    bind(&isNullOrUndefined);

    // Ensure no payload for null and undefined.
    ma_b(src, src, &done, Assembler::Zero, ShortJump);
    breakpoint();
  }
  {
    bind(&isBoolean);

    // Ensure boolean values are either 0 or 1.
    ma_b(src, Imm32(1), &done, Assembler::BelowOrEqual, ShortJump);
    breakpoint();
  }
  {
    bind(&isInt32OrMagic);

    // Ensure |src| is sign-extended.
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_sll(scratch, src, Imm32(0));
    ma_b(src, scratch, &done, Assembler::Equal, ShortJump);
    breakpoint();
  }
  bind(&done);
#endif

  ma_or(dest, type, Imm32(JSVAL_TAG_MAX_DOUBLE));
  ma_dsll(dest, dest, Imm32(JSVAL_TAG_SHIFT));
  ma_dins(dest, src, Imm32(0), Imm32(32));
}

void MacroAssemblerMIPS64Compat::loadConstantFloat32(float f,
                                                     FloatRegister dest) {
  ma_lis(dest, f);
}

void MacroAssemblerMIPS64Compat::loadInt32OrDouble(const Address& src,
                                                   FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  Label notInt32, end;
  {
    // Inlined |branchTestInt32| to use a short-jump.
    Register tag = extractTag(src, scratch);
    ma_b(tag, ImmTag(JSVAL_TAG_INT32), ¬Int32, Assembler::NotEqual,
         ShortJump);
  }
  {
    // If it's an int, convert it to double.
    unboxInt32(src, scratch);
    convertInt32ToDouble(scratch, dest);
    ma_b(&end, ShortJump);
  }
  bind(¬Int32);
  {
    // Not an int, just load as double.
    unboxDouble(src, dest);
  }
  bind(&end);
}

void MacroAssemblerMIPS64Compat::loadInt32OrDouble(const BaseIndex& addr,
                                                   FloatRegister dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  computeScaledAddress(addr, scratch);
  loadInt32OrDouble(Address(scratch, addr.offset), dest);
}

void MacroAssemblerMIPS64Compat::loadConstantDouble(double dp,
                                                    FloatRegister dest) {
  ma_lid(dest, dp);
}

Register MacroAssemblerMIPS64Compat::extractObject(const Address& address,
                                                   Register scratch) {
  loadPtr(address, scratch);
  ma_dext(scratch, scratch, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
  return scratch;
}

Register MacroAssemblerMIPS64Compat::extractTag(const Address& address,
                                                Register scratch) {
  loadPtr(address, scratch);
  ma_dext(scratch, scratch, Imm32(JSVAL_TAG_SHIFT),
          Imm32(64 - JSVAL_TAG_SHIFT));
  return scratch;
}

Register MacroAssemblerMIPS64Compat::extractTag(const BaseIndex& address,
                                                Register scratch) {
  computeScaledAddress(address, scratch);
  return extractTag(Address(scratch, address.offset), scratch);
}

/////////////////////////////////////////////////////////////////
// X86/X64-common/ARM/MIPS interface.
/////////////////////////////////////////////////////////////////
void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val, Operand dst) {
  storeValue(val, Address(Register::FromCode(dst.base()), dst.disp()));
}

void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val,
                                            const BaseIndex& dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(dest, scratch2);
  storeValue(val, Address(scratch2, dest.offset));
}

void MacroAssemblerMIPS64Compat::storeValue(JSValueType type, Register reg,
                                            BaseIndex dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  computeScaledAddress(dest, scratch);

  int32_t offset = dest.offset;
  if (!Imm16::IsInSignedRange(offset)) {
    Register scratch2 = temps.Acquire();
    ma_li(scratch2, Imm32(offset));
    as_daddu(scratch, scratch, scratch2);
    offset = 0;
  }

  storeValue(type, reg, Address(scratch, offset));
}

void MacroAssemblerMIPS64Compat::storeValue(ValueOperand val,
                                            const Address& dest) {
  storePtr(val.valueReg(), Address(dest.base, dest.offset));
}

void MacroAssemblerMIPS64Compat::storeValue(JSValueType type, Register reg,
                                            Address dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  MOZ_ASSERT(dest.base != scratch2);

  if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
    AssertValidPayload(*this, type, reg, scratch2);

    store32(reg, dest);
    JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type);
    move32(Imm64(tag).hi(), scratch2);
    store32(scratch2, Address(dest.base, dest.offset + 4));
  } else {
    boxValue(type, reg, scratch2);
    storePtr(scratch2, Address(dest.base, dest.offset));
  }
}

void MacroAssemblerMIPS64Compat::storeValue(const Value& val, Address dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  if (val.isGCThing()) {
    writeDataRelocation(val);
    movWithPatch(ImmWord(val.asRawBits()), scratch2);
  } else {
    ma_li(scratch2, ImmWord(val.asRawBits()));
  }
  storePtr(scratch2, Address(dest.base, dest.offset));
}

void MacroAssemblerMIPS64Compat::storeValue(const Value& val, BaseIndex dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  computeScaledAddress(dest, scratch);

  int32_t offset = dest.offset;
  if (!Imm16::IsInSignedRange(offset)) {
    Register scratch2 = temps.Acquire();
    ma_li(scratch2, Imm32(offset));
    as_daddu(scratch, scratch, scratch2);
    offset = 0;
  }
  storeValue(val, Address(scratch, offset));
}

void MacroAssemblerMIPS64Compat::loadValue(const BaseIndex& addr,
                                           ValueOperand val) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  computeScaledAddress(addr, scratch2);
  loadValue(Address(scratch2, addr.offset), val);
}

void MacroAssemblerMIPS64Compat::loadValue(Address src, ValueOperand val) {
  loadPtr(Address(src.base, src.offset), val.valueReg());
}

void MacroAssemblerMIPS64Compat::tagValue(JSValueType type, Register payload,
                                          ValueOperand dest) {
  MOZ_ASSERT(type != JSVAL_TYPE_UNDEFINED && type != JSVAL_TYPE_NULL);

  if (payload == dest.valueReg()) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    MOZ_ASSERT(dest.valueReg() != scratch);

    AssertValidPayload(*this, type, payload, scratch);

    switch (type) {
      case JSVAL_TYPE_BOOLEAN:
      case JSVAL_TYPE_INT32:
      case JSVAL_TYPE_MAGIC: {
        int64_t shifted = int64_t(JSVAL_TYPE_TO_SHIFTED_TAG(type)) >> 32;

        // Load upper 32 bits of shifted tag into scratch register.
        ma_li(scratch, Imm32(shifted));

        // Insert tag into the result.
        ma_dins(dest.valueReg(), scratch, Imm32(32), Imm32(32));
        return;
      }
      case JSVAL_TYPE_STRING:
      case JSVAL_TYPE_SYMBOL:
      case JSVAL_TYPE_PRIVATE_GCTHING:
      case JSVAL_TYPE_BIGINT:
      case JSVAL_TYPE_OBJECT: {
        int64_t signExtendedShiftedTag =
            int64_t(JSVAL_TYPE_TO_SHIFTED_TAG(type)) >> JSVAL_TAG_SHIFT;
        MOZ_ASSERT(Imm16::IsInSignedRange(signExtendedShiftedTag),
                   "sign-extended shifted tag can be materialised in a single "
                   "daddiu instruction");

        // Store sign-extended tag into lower 17 bits of the scratch register.
        as_daddiu(scratch, zero, signExtendedShiftedTag);

        // Insert tag into the result.
        ma_dins(dest.valueReg(), scratch, Imm32(JSVAL_TAG_SHIFT),
                Imm32(64 - JSVAL_TAG_SHIFT));
        return;
      }
      case JSVAL_TYPE_DOUBLE:
      case JSVAL_TYPE_UNDEFINED:
      case JSVAL_TYPE_NULL:
      case JSVAL_TYPE_UNKNOWN:
        break;
    }
    MOZ_CRASH("bad value type");
  } else {
    boxNonDouble(type, payload, dest);
  }
}

void MacroAssemblerMIPS64Compat::pushValue(ValueOperand val) {
  // Allocate stack slots for Value. One for each.
  asMasm().subPtr(Imm32(sizeof(Value)), StackPointer);
  // Store Value
  storeValue(val, Address(StackPointer, 0));
}

void MacroAssemblerMIPS64Compat::pushValue(const Address& addr) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  // Load value before allocate stack, addr.base may be is sp.
  loadPtr(Address(addr.base, addr.offset), scratch);
  ma_dsubu(StackPointer, StackPointer, Imm32(sizeof(Value)));
  storePtr(scratch, Address(StackPointer, 0));
}

void MacroAssemblerMIPS64Compat::popValue(ValueOperand val) {
  as_ld(val.valueReg(), StackPointer, 0);
  as_daddiu(StackPointer, StackPointer, sizeof(Value));
}

void MacroAssemblerMIPS64Compat::breakpoint() { as_break(0); }

void MacroAssemblerMIPS64Compat::checkStackAlignment() {
#ifdef DEBUG
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Label aligned;
  as_andi(scratch, sp, ABIStackAlignment - 1);
  ma_b(scratch, zero, &aligned, Equal, ShortJump);
  as_break(BREAK_STACK_UNALIGNED);
  bind(&aligned);
#endif
}

void MacroAssemblerMIPS64Compat::handleFailureWithHandlerTail(
    Label* profilerExitTail, Label* bailoutTail,
    uint32_t* returnValueCheckOffset) {
  // Reserve space for exception information.
  int size = (sizeof(ResumeFromException) + ABIStackAlignment) &
             ~(ABIStackAlignment - 1);
  asMasm().subPtr(Imm32(size), StackPointer);
  ma_move(a0, StackPointer);  // Use a0 since it is a first function argument

  // Call the handler.
  using Fn = void (*)(ResumeFromException* rfe);
  asMasm().setupUnalignedABICall(a1);
  asMasm().passABIArg(a0);
  asMasm().callWithABI<Fn, HandleException>(
      ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);

  *returnValueCheckOffset = asMasm().currentOffset();

  Label entryFrame;
  Label catch_;
  Label finally;
  Label returnBaseline;
  Label returnIon;
  Label bailout;
  Label wasmInterpEntry;
  Label wasmCatch;

  // Already clobbered a0, so use it...
  load32(Address(StackPointer, ResumeFromException::offsetOfKind()), a0);
  asMasm().branch32(Assembler::Equal, a0,
                    Imm32(ExceptionResumeKind::EntryFrame), &entryFrame);
  asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Catch),
                    &catch_);
  asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Finally),
                    &finally);
  asMasm().branch32(Assembler::Equal, a0,
                    Imm32(ExceptionResumeKind::ForcedReturnBaseline),
                    &returnBaseline);
  asMasm().branch32(Assembler::Equal, a0,
                    Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon);
  asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Bailout),
                    &bailout);
  asMasm().branch32(Assembler::Equal, a0,
                    Imm32(ExceptionResumeKind::WasmInterpEntry),
                    &wasmInterpEntry);
  asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::WasmCatch),
                    &wasmCatch);

  breakpoint();  // Invalid kind.

  // No exception handler. Load the error value, restore state and return from
  // the entry frame.
  bind(&entryFrame);
  asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);

  // We're going to be returning by the ion calling convention
  ma_pop(ra);
  as_jr(ra);
  as_nop();

  // If we found a catch handler, this must be a baseline frame. Restore
  // state and jump to the catch block.
  bind(&catch_);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), a0);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);
  jump(a0);

  // If we found a finally block, this must be a baseline frame. Push three
  // values expected by the finally block: the exception, the exception stack,
  // and BooleanValue(true).
  bind(&finally);
  ValueOperand exception = ValueOperand(a1);
  loadValue(Address(sp, ResumeFromException::offsetOfException()), exception);

  ValueOperand exceptionStack = ValueOperand(a2);
  loadValue(Address(sp, ResumeFromException::offsetOfExceptionStack()),
            exceptionStack);

  loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a0);
  loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp);

  pushValue(exception);
  pushValue(exceptionStack);
  pushValue(BooleanValue(true));
  jump(a0);

  // Return BaselineFrame->returnValue() to the caller.
  // Used in debug mode and for GeneratorReturn.
  Label profilingInstrumentation;
  bind(&returnBaseline);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);
  loadValue(Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()),
            JSReturnOperand);
  jump(&profilingInstrumentation);

  // Return the given value to the caller.
  bind(&returnIon);
  loadValue(Address(StackPointer, ResumeFromException::offsetOfException()),
            JSReturnOperand);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);

  // If profiling is enabled, then update the lastProfilingFrame to refer to
  // caller frame before returning. This code is shared by ForcedReturnIon
  // and ForcedReturnBaseline.
  bind(&profilingInstrumentation);
  {
    Label skipProfilingInstrumentation;
    // Test if profiler enabled.
    AbsoluteAddress addressOfEnabled(
        asMasm().runtime()->geckoProfiler().addressOfEnabled());
    asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
                      &skipProfilingInstrumentation);
    jump(profilerExitTail);
    bind(&skipProfilingInstrumentation);
  }

  ma_move(StackPointer, FramePointer);
  pop(FramePointer);
  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);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);
  ma_li(ReturnReg, Imm32(1));
  jump(bailoutTail);

  // Reset SP and FP; SP is pointing to the unwound return address to the wasm
  // interpreter entry, so we can just ret().
  bind(&wasmInterpEntry);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
          FramePointer);
  loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
          StackPointer);
  ma_li(InstanceReg, ImmWord(wasm::InterpFailInstanceReg));
  ret();

  // Found a wasm catch handler, restore state and jump to it.
  bind(&wasmCatch);
  wasm::GenerateJumpToCatchHandler(asMasm(), sp, a1, a2, a3);
}

CodeOffset MacroAssemblerMIPS64Compat::toggledJump(Label* label) {
  CodeOffset ret(nextOffset().getOffset());
  ma_b(label);
  return ret;
}

CodeOffset MacroAssemblerMIPS64Compat::toggledCall(JitCode* target,
                                                   bool enabled) {
  UseScratchRegisterScope temps(*this);
  BufferOffset bo = nextOffset();
  CodeOffset offset(bo.getOffset());
  addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE);
  Register scratch = temps.Acquire();
  ma_liPatchable(scratch, ImmPtr(target->raw()));
  if (enabled) {
    as_jalr(scratch);
    as_nop();
  } else {
    as_nop();
    as_nop();
  }
  MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() ==
                            ToggledCallSize(nullptr));
  return offset;
}

void MacroAssemblerMIPS64Compat::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 MacroAssemblerMIPS64Compat::profilerExitFrame() {
  jump(asMasm().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 diff =
      set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes();
  const int32_t reserved = diff;

  reserveStack(reserved);
  for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
    diff -= sizeof(intptr_t);
    storePtr(*iter, Address(StackPointer, diff));
  }

#ifdef ENABLE_WASM_SIMD
#  error "Needs more careful logic if SIMD is enabled"
#endif

  for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush());
       iter.more(); ++iter) {
    diff -= sizeof(double);
    storeDouble(*iter, Address(StackPointer, diff));
  }
  MOZ_ASSERT(diff == 0);
}

void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set,
                                         LiveRegisterSet ignore) {
  int32_t diff =
      set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes();
  const int32_t reserved = diff;

  for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
    diff -= sizeof(intptr_t);
    if (!ignore.has(*iter)) {
      loadPtr(Address(StackPointer, diff), *iter);
    }
  }

#ifdef ENABLE_WASM_SIMD
#  error "Needs more careful logic if SIMD is enabled"
#endif

  for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush());
       iter.more(); ++iter) {
    diff -= sizeof(double);
    if (!ignore.has(*iter)) {
      loadDouble(Address(StackPointer, diff), *iter);
    }
  }
  MOZ_ASSERT(diff == 0);
  freeStack(reserved);
}

void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest,
                                     Register) {
  FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
  mozilla::DebugOnly<unsigned> numFpu = fpuSet.size();
  int32_t diffF = fpuSet.getPushSizeInBytes();
  mozilla::DebugOnly<int32_t> diffG = set.gprs().size() * sizeof(intptr_t);

  MOZ_ASSERT(dest.offset >= diffG + diffF);

  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

  for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {
    FloatRegister reg = *iter;
    diffF -= reg.size();
    numFpu -= 1;
    dest.offset -= reg.size();
    if (reg.isDouble()) {
      storeDouble(reg, dest);
    } else if (reg.isSingle()) {
      storeFloat32(reg, dest);
    } else {
      MOZ_CRASH("Unknown register type.");
    }
  }
  MOZ_ASSERT(numFpu == 0);

  diffF -= diffF % sizeof(uintptr_t);
  MOZ_ASSERT(diffF == 0);
}

void MacroAssembler::freeStackTo(uint32_t framePushed) {
  MOZ_ASSERT(framePushed <= framePushed_);
  ma_dsubu(StackPointer, FramePointer, Imm32(framePushed));
  framePushed_ = framePushed;
}

// ===============================================================
// 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_) {
      return;
    }

    MoveEmitter emitter(*this);
    emitter.emit(moveResolver_);
    emitter.finish();
  }

  assertStackAlignment(ABIStackAlignment);
}

void MacroAssembler::callWithABIPost(uint32_t stackAdjust, ABIType result) {
  // Restore ra value (as stored in callWithABIPre()).
  loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra);

  if (dynamicAlignment_) {
    // Restore sp value from stack (as stored in setupUnalignedABICall()).
    loadPtr(Address(StackPointer, stackAdjust), StackPointer);
    // Use adjustFrame instead of freeStack because we already restored sp.
    adjustFrame(-stackAdjust);
  } else {
    freeStack(stackAdjust);
  }

#ifdef DEBUG
  MOZ_ASSERT(inCall_);
  inCall_ = false;
#endif
}

void MacroAssembler::callWithABINoProfiler(Register fun, ABIType result) {
  // Load the callee in t9, no instruction between the lw and call
  // should clobber it. Note that we can't use fun.base because it may
  // be one of the IntArg registers clobbered before the call.
  ma_move(CallReg, fun);
  uint32_t stackAdjust;
  callWithABIPre(&stackAdjust);
  call(CallReg);
  callWithABIPost(stackAdjust, result);
}

void MacroAssembler::callWithABINoProfiler(const Address& fun, ABIType result) {
  // Load the callee in t9, as above.
  loadPtr(Address(fun.base, fun.offset), CallReg);
  uint32_t stackAdjust;
  callWithABIPre(&stackAdjust);
  call(CallReg);
  callWithABIPost(stackAdjust, result);
}

// ===============================================================
// Move

void MacroAssembler::moveValue(const ValueOperand& src,
                               const ValueOperand& dest) {
  if (src == dest) {
    return;
  }
  movePtr(src.valueReg(), dest.valueReg());
}

void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) {
  if (!src.isGCThing()) {
    ma_li(dest.valueReg(), ImmWord(src.asRawBits()));
    return;
  }

  writeDataRelocation(src);
  movWithPatch(ImmWord(src.asRawBits()), dest.valueReg());
}

// ===============================================================
// Branch functions

void MacroAssembler::branchValueIsNurseryCell(Condition cond,
                                              const Address& address,
                                              Register temp, Label* label) {
  branchValueIsNurseryCellImpl(cond, address, temp, label);
}

void MacroAssembler::branchValueIsNurseryCell(Condition cond,
                                              ValueOperand value, Register temp,
                                              Label* label) {
  branchValueIsNurseryCellImpl(cond, value, temp, label);
}

template <typename T>
void MacroAssembler::branchValueIsNurseryCellImpl(Condition cond,
                                                  const T& value, Register temp,
                                                  Label* label) {
  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
  Label done;
  branchTestGCThing(Assembler::NotEqual, value,
                    cond == Assembler::Equal ? &done : label);

  // temp may be InvalidReg, use scratch2 instead.
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();

  getGCThingValueChunk(value, scratch2);
  loadPtr(Address(scratch2, gc::ChunkStoreBufferOffset), scratch2);
  branchPtr(InvertCondition(cond), scratch2, ImmWord(0), label);

  bind(&done);
}

void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
                                     const Value& rhs, Label* label) {
  MOZ_ASSERT(cond == Equal || cond == NotEqual);
  MOZ_ASSERT(!rhs.isNaN());

  if (!rhs.isGCThing()) {
    ma_b(lhs.valueReg(), ImmWord(rhs.asRawBits()), label, cond);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    MOZ_ASSERT(lhs.valueReg() != scratch);
    moveValue(rhs, ValueOperand(scratch));
    ma_b(lhs.valueReg(), scratch, label, cond);
  }
}

void MacroAssembler::branchTestNaNValue(Condition cond, const ValueOperand& val,
                                        Register temp, Label* label) {
  MOZ_ASSERT(cond == Equal || cond == NotEqual);
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  MOZ_ASSERT(val.valueReg() != scratch);

  // When testing for NaN, we want to ignore the sign bit.
  if (hasR2()) {
    // Clear the sign bit by extracting the lower 63 bits.
    ma_dext(temp, val.valueReg(), Imm32(0), Imm32(63));
  } else {
    // Clear the sign bit by shifting left and then right.
    ma_dsll(temp, val.valueReg(), Imm32(1));
    ma_dsrl(temp, temp, Imm32(1));
  }

  // Compare against a NaN with sign bit 0.
  static_assert(JS::detail::CanonicalizedNaNSignBit == 0);
  moveValue(DoubleValue(JS::GenericNaN()), ValueOperand(scratch));
  ma_b(temp, scratch, label, cond);
}

// ========================================================================
// Memory access primitives.
template <typename T>
void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
                                       MIRType valueType, const T& dest) {
  MOZ_ASSERT(valueType < MIRType::Value);

  if (valueType == MIRType::Double) {
    boxDouble(value.reg().typedReg().fpu(), dest);
    return;
  }

  if (value.constant()) {
    storeValue(value.value(), dest);
  } else {
    storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(),
               dest);
  }
}

template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
                                                MIRType valueType,
                                                const Address& dest);
template void MacroAssembler::storeUnboxedValue(
    const ConstantOrRegister& value, MIRType valueType,
    const BaseObjectElementIndex& dest);

void MacroAssembler::PushBoxed(FloatRegister reg) {
  subFromStackPtr(Imm32(sizeof(double)));
  boxDouble(reg, Address(getStackPointer(), 0));
  adjustFrame(sizeof(double));
}

void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
                                       Register boundsCheckLimit,
                                       Label* label) {
  ma_b(index, boundsCheckLimit, label, cond);
}

void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
                                       Address boundsCheckLimit, Label* label) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  load32(boundsCheckLimit, scratch2);
  ma_b(index, scratch2, label, cond);
}

void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
                                       Register64 boundsCheckLimit,
                                       Label* label) {
  ma_b(index.reg, boundsCheckLimit.reg, label, cond);
}

void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
                                       Address boundsCheckLimit, Label* label) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  loadPtr(boundsCheckLimit, scratch2);
  ma_b(index.reg, scratch2, label, cond);
}

void MacroAssembler::widenInt32(Register r) {
  // I *think* this is correct.  It may be redundant.
  move32To64SignExtend(r, Register64(r));
}

void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input,
                                                Register output,
                                                bool isSaturating,
                                                Label* oolEntry) {
  UseScratchRegisterScope temps(*this);
  branchDouble(Assembler::DoubleUnordered, input, input, oolEntry);
  as_truncld(ScratchDoubleReg, input);
  moveFromDouble(ScratchDoubleReg, output);
  Register scratch = temps.Acquire();
  ma_dsrl(scratch, output, Imm32(32));
  as_sll(output, output, 0);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);
}

void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input,
                                                 Register output,
                                                 bool isSaturating,
                                                 Label* oolEntry) {
  UseScratchRegisterScope temps(*this);
  branchFloat(Assembler::DoubleUnordered, input, input, oolEntry);
  as_truncls(ScratchDoubleReg, input);
  moveFromDouble(ScratchDoubleReg, output);
  Register scratch = temps.Acquire();
  ma_dsrl(scratch, output, Imm32(32));
  as_sll(output, output, 0);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);
}

void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access,
                                 Register memoryBase, Register ptr,
                                 Register ptrScratch, Register64 output) {
  wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
}

void MacroAssembler::wasmUnalignedLoadI64(const wasm::MemoryAccessDesc& access,
                                          Register memoryBase, Register ptr,
                                          Register ptrScratch,
                                          Register64 output, Register tmp) {
  wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, tmp);
}

void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access,
                                  Register64 value, Register memoryBase,
                                  Register ptr, Register ptrScratch) {
  wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
}

void MacroAssembler::wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access,
                                           Register64 value,
                                           Register memoryBase, Register ptr,
                                           Register ptrScratch, Register tmp) {
  wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, tmp);
}

void MacroAssembler::wasmTruncateDoubleToInt64(
    FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry,
    Label* oolRejoin, FloatRegister tempDouble) {
  MOZ_ASSERT(tempDouble.isInvalid());
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  as_truncld(ScratchDoubleReg, input);
  as_cfc1(scratch, Assembler::FCSR);
  moveFromDouble(ScratchDoubleReg, output.reg);
  ma_ext(scratch, scratch, Assembler::CauseV, 1);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);

  if (isSaturating) {
    bind(oolRejoin);
  }
}

void MacroAssembler::wasmTruncateDoubleToUInt64(
    FloatRegister input, Register64 output_, bool isSaturating, Label* oolEntry,
    Label* oolRejoin, FloatRegister tempDouble) {
  MOZ_ASSERT(tempDouble.isInvalid());
  Register output = output_.reg;

  Label done;

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  // Guard against NaN.
  branchDouble(Assembler::DoubleUnordered, input, input, oolEntry);

  as_truncld(ScratchDoubleReg, input);
  // ma_li INT64_MAX
  ma_li(scratch, Imm32(-1));
  ma_dext(scratch, scratch, Imm32(0), Imm32(63));
  moveFromDouble(ScratchDoubleReg, output);
  // For numbers in  -1.[ : ]INT64_MAX range do nothing more
  ma_b(output, scratch, &done, Assembler::Below, ShortJump);

  loadConstantDouble(double(INT64_MAX + 1ULL), ScratchDoubleReg);
  {
    UseScratchRegisterScope temps(*this);
    Register scratch2 = temps.Acquire();
    // ma_li INT64_MIN
    ma_daddu(scratch2, scratch, Imm32(1));
    as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
    as_truncld(ScratchDoubleReg, ScratchDoubleReg);
    as_cfc1(scratch, Assembler::FCSR);
    moveFromDouble(ScratchDoubleReg, output);
    ma_ext(scratch, scratch, Assembler::CauseV, 1);
    ma_daddu(output, scratch2);

    // Guard against negative values that result in 0 due the precision loss.
    as_sltiu(scratch2, output, 1);
    ma_or(scratch, scratch2);
  }
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);

  bind(&done);

  if (isSaturating) {
    bind(oolRejoin);
  }
}

void MacroAssembler::wasmTruncateFloat32ToInt64(
    FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry,
    Label* oolRejoin, FloatRegister tempFloat) {
  MOZ_ASSERT(tempFloat.isInvalid());
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  as_truncls(ScratchDoubleReg, input);
  as_cfc1(scratch, Assembler::FCSR);
  moveFromDouble(ScratchDoubleReg, output.reg);
  ma_ext(scratch, scratch, Assembler::CauseV, 1);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);

  if (isSaturating) {
    bind(oolRejoin);
  }
}

void MacroAssembler::wasmTruncateFloat32ToUInt64(
    FloatRegister input, Register64 output_, bool isSaturating, Label* oolEntry,
    Label* oolRejoin, FloatRegister tempFloat) {
  MOZ_ASSERT(tempFloat.isInvalid());
  Register output = output_.reg;

  Label done;

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  // Guard against NaN.
  branchFloat(Assembler::DoubleUnordered, input, input, oolEntry);

  as_truncls(ScratchDoubleReg, input);
  // ma_li INT64_MAX
  ma_li(scratch, Imm32(-1));
  ma_dext(scratch, scratch, Imm32(0), Imm32(63));
  moveFromDouble(ScratchDoubleReg, output);
  // For numbers in  -1.[ : ]INT64_MAX range do nothing more
  ma_b(output, scratch, &done, Assembler::Below, ShortJump);

  loadConstantFloat32(float(INT64_MAX + 1ULL), ScratchFloat32Reg);
  {
    // ma_li INT64_MIN
    UseScratchRegisterScope temps(*this);
    Register scratch2 = temps.Acquire();
    ma_daddu(scratch2, scratch, Imm32(1));
    as_subs(ScratchFloat32Reg, input, ScratchFloat32Reg);
    as_truncls(ScratchDoubleReg, ScratchFloat32Reg);
    as_cfc1(scratch, Assembler::FCSR);
    moveFromDouble(ScratchDoubleReg, output);
    ma_ext(scratch, scratch, Assembler::CauseV, 1);
    ma_daddu(output, scratch2);

    // Guard against negative values that result in 0 due the precision loss.
    as_sltiu(scratch2, output, 1);
    ma_or(scratch, scratch2);
  }
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);

  bind(&done);

  if (isSaturating) {
    bind(oolRejoin);
  }
}

void MacroAssemblerMIPS64Compat::wasmLoadI64Impl(
    const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
    Register ptrScratch, Register64 output, Register tmp) {
  access.assertOffsetInGuardPages();
  uint32_t offset = access.offset32();
  MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);

  MOZ_ASSERT(!access.isZeroExtendSimd128Load());
  MOZ_ASSERT(!access.isSplatSimd128Load());
  MOZ_ASSERT(!access.isWidenSimd128Load());

  // Maybe add the offset.
  if (offset) {
    asMasm().addPtr(ImmWord(offset), ptrScratch);
    ptr = ptrScratch;
  }

  unsigned byteSize = access.byteSize();
  bool isSigned = Scalar::isSignedIntType(access.type());

  BaseIndex address(memoryBase, ptr, TimesOne);
  if (IsUnaligned(access)) {
    MOZ_ASSERT(tmp != InvalidReg);
    asMasm().ma_load_unaligned(access, output.reg, address, tmp,
                               static_cast<LoadStoreSize>(8 * byteSize),
                               isSigned ? SignExtend : ZeroExtend);
    return;
  }

  asMasm().memoryBarrierBefore(access.sync());
  FaultingCodeOffset fco = asMasm().ma_load(
      output.reg, address, static_cast<LoadStoreSize>(8 * byteSize),
      isSigned ? SignExtend : ZeroExtend);
  asMasm().append(access,
                  wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
                  fco);
  asMasm().memoryBarrierAfter(access.sync());
}

void MacroAssemblerMIPS64Compat::wasmStoreI64Impl(
    const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
    Register ptr, Register ptrScratch, Register tmp) {
  access.assertOffsetInGuardPages();
  uint32_t offset = access.offset32();
  MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);

  // Maybe add the offset.
  if (offset) {
    asMasm().addPtr(ImmWord(offset), ptrScratch);
    ptr = ptrScratch;
  }

  unsigned byteSize = access.byteSize();
  bool isSigned = Scalar::isSignedIntType(access.type());

  BaseIndex address(memoryBase, ptr, TimesOne);

  if (IsUnaligned(access)) {
    MOZ_ASSERT(tmp != InvalidReg);
    asMasm().ma_store_unaligned(access, value.reg, address, tmp,
                                static_cast<LoadStoreSize>(8 * byteSize),
                                isSigned ? SignExtend : ZeroExtend);
    return;
  }

  asMasm().memoryBarrierBefore(access.sync());
  FaultingCodeOffset fco = asMasm().ma_store(
      value.reg, address, static_cast<LoadStoreSize>(8 * byteSize),
      isSigned ? SignExtend : ZeroExtend);
  asMasm().append(
      access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
      fco);
  asMasm().memoryBarrierAfter(access.sync());
}

template <typename T>
static void CompareExchange64(MacroAssembler& masm,
                              const wasm::MemoryAccessDesc* access,
                              Synchronization sync, const T& mem,
                              Register64 expect, Register64 replace,
                              Register64 output) {
  MOZ_ASSERT(expect != output && replace != output);

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();

  masm.computeEffectiveAddress(mem, scratch2);

  Label tryAgain;
  Label exit;

  masm.memoryBarrierBefore(sync);

  masm.bind(&tryAgain);

  if (access) {
    masm.append(*access, wasm::TrapMachineInsn::Load64,
                FaultingCodeOffset(masm.currentOffset()));
  }

  Register scratch = temps.Acquire();
  masm.as_lld(output.reg, scratch2, 0);
  masm.ma_b(output.reg, expect.reg, &exit, Assembler::NotEqual, ShortJump);
  masm.movePtr(replace.reg, scratch);
  masm.as_scd(scratch, scratch2, 0);
  masm.ma_b(scratch, scratch, &tryAgain, Assembler::Zero, ShortJump);

  masm.memoryBarrierAfter(sync);

  masm.bind(&exit);
}

void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
                                           const Address& mem,
                                           Register64 expect,
                                           Register64 replace,
                                           Register64 output) {
  CompareExchange64(*this, &access, access.sync(), mem, expect, replace,
                    output);
}

void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
                                           const BaseIndex& mem,
                                           Register64 expect,
                                           Register64 replace,
                                           Register64 output) {
  CompareExchange64(*this, &access, access.sync(), mem, expect, replace,
                    output);
}

void MacroAssembler::compareExchange64(Synchronization sync, const Address& mem,
                                       Register64 expect, Register64 replace,
                                       Register64 output) {
  CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
}

void MacroAssembler::compareExchange64(Synchronization sync,
                                       const BaseIndex& mem, Register64 expect,
                                       Register64 replace, Register64 output) {
  CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
}

template <typename T>
static void AtomicExchange64(MacroAssembler& masm,
                             const wasm::MemoryAccessDesc* access,
                             Synchronization sync, const T& mem,
                             Register64 value, Register64 output) {
  MOZ_ASSERT(value != output);

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();

  masm.computeEffectiveAddress(mem, scratch2);

  Label tryAgain;

  masm.memoryBarrierBefore(sync);

  masm.bind(&tryAgain);

  if (access) {
    masm.append(*access, wasm::TrapMachineInsn::Load64,
                FaultingCodeOffset(masm.currentOffset()));
  }

  Register scratch = temps.Acquire();
  masm.as_lld(output.reg, scratch2, 0);
  masm.movePtr(value.reg, scratch);
  masm.as_scd(scratch, scratch2, 0);
  masm.ma_b(scratch, scratch, &tryAgain, Assembler::Zero, ShortJump);

  masm.memoryBarrierAfter(sync);
}

template <typename T>
static void WasmAtomicExchange64(MacroAssembler& masm,
                                 const wasm::MemoryAccessDesc& access,
                                 const T& mem, Register64 value,
                                 Register64 output) {
  AtomicExchange64(masm, &access, access.sync(), mem, value, output);
}

void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
                                          const Address& mem, Register64 src,
                                          Register64 output) {
  WasmAtomicExchange64(*this, access, mem, src, output);
}

void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
                                          const BaseIndex& mem, Register64 src,
                                          Register64 output) {
  WasmAtomicExchange64(*this, access, mem, src, output);
}

void MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem,
                                      Register64 value, Register64 output) {
  AtomicExchange64(*this, nullptr, sync, mem, value, output);
}

void MacroAssembler::atomicExchange64(Synchronization sync,
                                      const BaseIndex& mem, Register64 value,
                                      Register64 output) {
  AtomicExchange64(*this, nullptr, sync, mem, value, output);
}

template <typename T>
static void AtomicFetchOp64(MacroAssembler& masm,
                            const wasm::MemoryAccessDesc* access,
                            Synchronization sync, AtomicOp op, Register64 value,
                            const T& mem, Register64 temp, Register64 output) {
  MOZ_ASSERT(value != output);
  MOZ_ASSERT(value != temp);

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();

  masm.computeEffectiveAddress(mem, scratch2);

  Label tryAgain;

  masm.memoryBarrierBefore(sync);

  masm.bind(&tryAgain);
  if (access) {
    masm.append(*access, wasm::TrapMachineInsn::Load64,
                FaultingCodeOffset(masm.currentOffset()));
  }

  masm.as_lld(output.reg, scratch2, 0);

  switch (op) {
    case AtomicOp::Add:
      masm.as_daddu(temp.reg, output.reg, value.reg);
      break;
    case AtomicOp::Sub:
      masm.as_dsubu(temp.reg, output.reg, value.reg);
      break;
    case AtomicOp::And:
      masm.as_and(temp.reg, output.reg, value.reg);
      break;
    case AtomicOp::Or:
      masm.as_or(temp.reg, output.reg, value.reg);
      break;
    case AtomicOp::Xor:
      masm.as_xor(temp.reg, output.reg, value.reg);
      break;
    default:
      MOZ_CRASH();
  }

  masm.as_scd(temp.reg, scratch2, 0);
  masm.ma_b(temp.reg, temp.reg, &tryAgain, Assembler::Zero, ShortJump);

  masm.memoryBarrierAfter(sync);
}

void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
                                         AtomicOp op, Register64 value,
                                         const Address& mem, Register64 temp,
                                         Register64 output) {
  AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output);
}

void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
                                         AtomicOp op, Register64 value,
                                         const BaseIndex& mem, Register64 temp,
                                         Register64 output) {
  AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output);
}

void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op,
                                     Register64 value, const Address& mem,
                                     Register64 temp, Register64 output) {
  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
}

void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op,
                                     Register64 value, const BaseIndex& mem,
                                     Register64 temp, Register64 output) {
  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
}

void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
                                      Register64 value, const Address& mem,
                                      Register64 temp) {
  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
}

void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
                                      Register64 value, const BaseIndex& mem,
                                      Register64 temp) {
  AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
}

// ========================================================================
// Convert floating point.

void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) {
  as_dmtc1(src.reg, dest);
  as_cvtdl(dest, dest);
}

void MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest) {
  as_dmtc1(src.reg, dest);
  as_cvtsl(dest, dest);
}

bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; }

void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest,
                                           Register temp) {
  MOZ_ASSERT(temp == Register::Invalid());
  MacroAssemblerSpecific::convertUInt64ToDouble(src.reg, dest);
}

void MacroAssembler::convertUInt64ToFloat32(Register64 src_, FloatRegister dest,
                                            Register temp) {
  MOZ_ASSERT(temp == Register::Invalid());

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register scratch2 = temps.Acquire();

  Register src = src_.reg;
  Label positive, done;
  ma_b(src, src, &positive, NotSigned, ShortJump);

  MOZ_ASSERT(src != scratch);
  MOZ_ASSERT(src != scratch2);

  ma_and(scratch, src, Imm32(1));
  ma_dsrl(scratch2, src, Imm32(1));
  ma_or(scratch, scratch2);
  as_dmtc1(scratch, dest);
  as_cvtsl(dest, dest);
  addFloat32(dest, dest);
  ma_b(&done, ShortJump);

  bind(&positive);
  as_dmtc1(src, dest);
  as_cvtsl(dest, dest);

  bind(&done);
}

void MacroAssembler::flexibleQuotientPtr(
    Register lhs, Register rhs, Register dest, bool isUnsigned,
    const LiveRegisterSet& volatileLiveRegs) {
  quotient64(lhs, rhs, dest, isUnsigned);
}

void MacroAssembler::flexibleRemainderPtr(
    Register lhs, Register rhs, Register dest, bool isUnsigned,
    const LiveRegisterSet& volatileLiveRegs) {
  remainder64(lhs, rhs, dest, isUnsigned);
}

void MacroAssembler::wasmMarkCallAsSlow() { mov(ra, ra); }

const int32_t SlowCallMarker = 0x37ff0000;  // ori ra, ra, 0

void MacroAssembler::wasmCheckSlowCallsite(Register ra_, Label* notSlow,
                                           Register temp1, Register temp2) {
  MOZ_ASSERT(ra_ != temp2);
  load32(Address(ra_, 0), temp2);
  branch32(Assembler::NotEqual, temp2, Imm32(SlowCallMarker), notSlow);
}

CodeOffset MacroAssembler::wasmMarkedSlowCall(const wasm::CallSiteDesc& desc,
                                              const Register reg) {
  CodeOffset offset = call(desc, reg);
  wasmMarkCallAsSlow();
  return offset;
}

//}}} check_macroassembler_style

Messung V0.5 in Prozent
C=97 H=99 G=97

¤ Dauer der Verarbeitung: 0.94 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002