Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/jit/mips-shared/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 109 kB image not shown  

Quelle  MacroAssembler-mips-shared.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/mips-shared/MacroAssembler-mips-shared.h"

#include <bit>

#include "jit/MacroAssembler.h"
#include "util/PortableMath.h"

using namespace js;
using namespace jit;

void MacroAssemblerMIPSShared::ma_move(Register rd, Register rs) {
  as_or(rd, rs, zero);
}

void MacroAssemblerMIPSShared::ma_li(Register dest, ImmGCPtr ptr) {
  writeDataRelocation(ptr);
  asMasm().ma_liPatchable(dest, ImmPtr(ptr.value));
}

void MacroAssemblerMIPSShared::ma_li(Register dest, Imm32 imm) {
  if (Imm16::IsInSignedRange(imm.value)) {
    as_addiu(dest, zero, imm.value);
  } else if (Imm16::IsInUnsignedRange(imm.value)) {
    as_ori(dest, zero, Imm16::Lower(imm).encode());
  } else if (Imm16::Lower(imm).encode() == 0) {
    as_lui(dest, Imm16::Upper(imm).encode());
  } else {
    as_lui(dest, Imm16::Upper(imm).encode());
    as_ori(dest, dest, Imm16::Lower(imm).encode());
  }
}

// This method generates lui and ori instruction pair that can be modified by
// UpdateLuiOriValue, either during compilation (eg. Assembler::bind), or
// during execution (eg. jit::PatchJump).
void MacroAssemblerMIPSShared::ma_liPatchable(Register dest, Imm32 imm) {
  m_buffer.ensureSpace(2 * sizeof(uint32_t));
  as_lui(dest, Imm16::Upper(imm).encode());
  as_ori(dest, dest, Imm16::Lower(imm).encode());
}

// Shifts
void MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt, Imm32 shift) {
  as_sll(rd, rt, shift.value & 0x1f);
}
void MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt, Imm32 shift) {
  as_srl(rd, rt, shift.value & 0x1f);
}

void MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt, Imm32 shift) {
  as_sra(rd, rt, shift.value & 0x1f);
}

void MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt, Imm32 shift) {
  if (hasR2()) {
    as_rotr(rd, rt, shift.value & 0x1f);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    as_srl(scratch, rt, shift.value & 0x1f);
    as_sll(rd, rt, 32 - (shift.value & 0x1f));
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt, Imm32 shift) {
  if (hasR2()) {
    as_rotr(rd, rt, 32 - (shift.value & 0x1f));
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    as_srl(scratch, rt, 32 - (shift.value & 0x1f));
    as_sll(rd, rt, shift.value & 0x1f);
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt,
                                      Register shift) {
  as_sllv(rd, rt, shift);
}

void MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt,
                                      Register shift) {
  as_srlv(rd, rt, shift);
}

void MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt,
                                      Register shift) {
  as_srav(rd, rt, shift);
}

void MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt,
                                      Register shift) {
  if (hasR2()) {
    as_rotrv(rd, rt, shift);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_negu(scratch, shift);
    as_sllv(scratch, rt, scratch);
    as_srlv(rd, rt, shift);
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt,
                                      Register shift) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_negu(scratch, shift);
  if (hasR2()) {
    as_rotrv(rd, rt, scratch);
  } else {
    as_srlv(scratch, rt, scratch);
    as_sllv(rd, rt, shift);
    as_or(rd, rd, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_negu(Register rd, Register rs) {
  as_subu(rd, zero, rs);
}

void MacroAssemblerMIPSShared::ma_not(Register rd, Register rs) {
  as_nor(rd, rs, zero);
}

// Bit extract/insert
void MacroAssemblerMIPSShared::ma_ext(Register rt, Register rs, uint16_t pos,
                                      uint16_t size) {
  MOZ_ASSERT(pos < 32);
  MOZ_ASSERT(pos + size < 33);

  if (hasR2()) {
    as_ext(rt, rs, pos, size);
  } else {
    int shift_left = 32 - (pos + size);
    as_sll(rt, rs, shift_left);
    int shift_right = 32 - size;
    if (shift_right > 0) {
      as_srl(rt, rt, shift_right);
    }
  }
}

void MacroAssemblerMIPSShared::ma_ins(Register rt, Register rs, uint16_t pos,
                                      uint16_t size) {
  MOZ_ASSERT(pos < 32);
  MOZ_ASSERT(pos + size <= 32);
  MOZ_ASSERT(size != 0);

  if (hasR2()) {
    as_ins(rt, rs, pos, size);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    if (pos == 0) {
      ma_ext(scratch, rs, 0, size);
      as_srl(rt, rt, size);
      as_sll(rt, rt, size);
      as_or(rt, rt, scratch);
    } else if (pos + size == 32) {
      as_sll(scratch, rs, pos);
      as_sll(rt, rt, size);
      as_srl(rt, rt, size);
      as_or(rt, rt, scratch);
    } else {
      Register scratch2 = temps.Acquire();
      ma_subu(scratch, zero, Imm32(1));
      as_srl(scratch, scratch, 32 - size);
      as_and(scratch2, rs, scratch);
      as_sll(scratch2, scratch2, pos);
      as_sll(scratch, scratch, pos);
      as_nor(scratch, scratch, zero);
      as_and(scratch, rt, scratch);
      as_or(rt, scratch2, scratch);
    }
  }
}

// Sign extend
void MacroAssemblerMIPSShared::ma_seb(Register rd, Register rt) {
  if (hasR2()) {
    as_seb(rd, rt);
  } else {
    as_sll(rd, rt, 24);
    as_sra(rd, rd, 24);
  }
}

void MacroAssemblerMIPSShared::ma_seh(Register rd, Register rt) {
  if (hasR2()) {
    as_seh(rd, rt);
  } else {
    as_sll(rd, rt, 16);
    as_sra(rd, rd, 16);
  }
}

// And.
void MacroAssemblerMIPSShared::ma_and(Register rd, Register rs) {
  as_and(rd, rd, rs);
}

void MacroAssemblerMIPSShared::ma_and(Register rd, Imm32 imm) {
  ma_and(rd, rd, imm);
}

void MacroAssemblerMIPSShared::ma_and(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInUnsignedRange(imm.value)) {
    as_andi(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_and(rd, rs, scratch);
  }
}

// Or.
void MacroAssemblerMIPSShared::ma_or(Register rd, Register rs) {
  as_or(rd, rd, rs);
}

void MacroAssemblerMIPSShared::ma_or(Register rd, Imm32 imm) {
  ma_or(rd, rd, imm);
}

void MacroAssemblerMIPSShared::ma_or(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInUnsignedRange(imm.value)) {
    as_ori(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_or(rd, rs, scratch);
  }
}

// xor
void MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs) {
  as_xor(rd, rd, rs);
}

void MacroAssemblerMIPSShared::ma_xor(Register rd, Imm32 imm) {
  ma_xor(rd, rd, imm);
}

void MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInUnsignedRange(imm.value)) {
    as_xori(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_xor(rd, rs, scratch);
  }
}

// word swap bytes within halfwords
void MacroAssemblerMIPSShared::ma_wsbh(Register rd, Register rt) {
  as_wsbh(rd, rt);
}

void MacroAssemblerMIPSShared::ma_ctz(Register rd, Register rs) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  as_addiu(scratch, rs, -1);
  as_xor(rd, scratch, rs);
  as_and(rd, rd, scratch);
  as_clz(rd, rd);
  ma_li(scratch, Imm32(0x20));
  as_subu(rd, scratch, rd);
}

// Arithmetic-based ops.

// Add.
void MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInSignedRange(imm.value)) {
    as_addiu(rd, rs, imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_addu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs) {
  as_addu(rd, rd, rs);
}

void MacroAssemblerMIPSShared::ma_addu(Register rd, Imm32 imm) {
  ma_addu(rd, rd, imm);
}

void MacroAssemblerMIPSShared::ma_add32TestCarry(Condition cond, Register rd,
                                                 Register rs, Register rt,
                                                 Label* overflow) {
  MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear);
  MOZ_ASSERT_IF(rd == rs, rt != rd);
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  as_addu(rd, rs, rt);
  as_sltu(scratch2, rd, rd == rs ? rt : rs);
  ma_b(scratch2, scratch2, overflow,
       cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero);
}

void MacroAssemblerMIPSShared::ma_add32TestCarry(Condition cond, Register rd,
                                                 Register rs, Imm32 imm,
                                                 Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_li(scratch, imm);
  ma_add32TestCarry(cond, rd, rs, scratch, overflow);
}

// Subtract.
void MacroAssemblerMIPSShared::ma_subu(Register rd, Register rs, Imm32 imm) {
  if (Imm16::IsInSignedRange(-imm.value)) {
    as_addiu(rd, rs, -imm.value);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, imm);
    as_subu(rd, rs, scratch);
  }
}

void MacroAssemblerMIPSShared::ma_subu(Register rd, Imm32 imm) {
  ma_subu(rd, rd, imm);
}

void MacroAssemblerMIPSShared::ma_subu(Register rd, Register rs) {
  as_subu(rd, rd, rs);
}

void MacroAssemblerMIPSShared::ma_sub32TestOverflow(Register rd, Register rs,
                                                    Imm32 imm,
                                                    Label* overflow) {
  if (imm.value != INT32_MIN) {
    asMasm().ma_add32TestOverflow(rd, rs, Imm32(-imm.value), overflow);
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(imm.value));
    asMasm().ma_sub32TestOverflow(rd, rs, scratch, overflow);
  }
}

void MacroAssemblerMIPSShared::ma_mul(Register rd, Register rs, Imm32 imm) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_li(scratch, imm);
  as_mul(rd, rs, scratch);
}

void MacroAssemblerMIPSShared::ma_mul32TestOverflow(Register rd, Register rs,
                                                    Register rt,
                                                    Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

#ifdef MIPSR6
  as_dmul(rd, rs, rt);
#else
  as_dmult(rs, rt);
  as_mflo(rd);
#endif
  ma_sll(scratch, rd, Imm32(0));
  ma_b(rd, scratch, overflow, Assembler::NotEqual);
}

void MacroAssemblerMIPSShared::ma_mul32TestOverflow(Register rd, Register rs,
                                                    Imm32 imm,
                                                    Label* overflow) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();

  ma_li(scratch, imm);
#ifdef MIPSR6
  as_dmul(rd, rs, scratch);
#else
  as_dmult(rs, scratch);
  as_mflo(rd);
#endif
  ma_sll(scratch, rd, Imm32(0));
  ma_b(rd, scratch, overflow, Assembler::NotEqual);
}

void MacroAssemblerMIPSShared::ma_mod_mask(Register src, Register dest,
                                           Register hold, Register remain,
                                           int32_t shift, Label* negZero) {
  UseScratchRegisterScope temps(*this);

  // MATH:
  // We wish to compute x % (1<<y) - 1 for a known constant, y.
  // First, let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit
  // dividend as a number in base b, namely
  // c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
  // now, since both addition and multiplication commute with modulus,
  // x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
  // (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)...
  // now, since b == C + 1, b % C == 1, and b^n % C == 1
  // this means that the whole thing simplifies to:
  // c_0 + c_1 + c_2 ... c_n % C
  // each c_n can easily be computed by a shift/bitextract, and the modulus
  // can be maintained by simply subtracting by C whenever the number gets
  // over C.
  int32_t mask = (1 << shift) - 1;
  Label head, negative, sumSigned, done;

  // hold holds -1 if the value was negative, 1 otherwise.
  // remain holds the remaining bits that have not been processed
  // scratch2 serves as a temporary location to store extracted bits
  // into as well as holding the trial subtraction as a temp value dest is
  // the accumulator (and holds the final result)

  // move the whole value into the remain.
  ma_move(remain, src);
  // Zero out the dest.
  ma_li(dest, Imm32(0));
  // Set the hold appropriately.
  ma_b(remain, remain, &negative, Signed, ShortJump);
  ma_li(hold, Imm32(1));
  ma_b(&head, ShortJump);

  bind(&negative);
  ma_li(hold, Imm32(-1));
  ma_negu(remain, remain);

  // Begin the main loop.
  bind(&head);

  Register scratch2 = temps.Acquire();
  // Extract the bottom bits into scratch2.
  ma_and(scratch2, remain, Imm32(mask));
  // Add those bits to the accumulator.
  as_addu(dest, dest, scratch2);
  // Do a trial subtraction
  ma_subu(scratch2, dest, Imm32(mask));
  // If (sum - C) > 0, store sum - C back into sum, thus performing a
  // modulus.
  ma_b(scratch2, scratch2, &sumSigned, Signed, ShortJump);
  ma_move(dest, scratch2);
  bind(&sumSigned);
  // Get rid of the bits that we extracted before.
  as_srl(remain, remain, shift);
  // If the shift produced zero, finish, otherwise, continue in the loop.
  ma_b(remain, remain, &head, NonZero, ShortJump);
  // Check the hold to see if we need to negate the result.
  ma_b(hold, hold, &done, NotSigned, ShortJump);

  if (negZero != nullptr) {
    // Jump out in case of negative zero.
    ma_b(dest, dest, negZero, Zero);
  }
  // If the hold was non-zero, negate the result to be in line with
  // what JS wants
  ma_negu(dest, dest);

  bind(&done);
}

// Memory.

FaultingCodeOffset MacroAssemblerMIPSShared::ma_load(
    Register dest, const BaseIndex& src, LoadStoreSize size,
    LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  FaultingCodeOffset fco;
  if (isLoongson() && ZeroExtend != extension &&
      Imm8::IsInSignedRange(src.offset)) {
    UseScratchRegisterScope temps(*this);
    Register index = src.index;

    if (src.scale != TimesOne) {
      int32_t shift = Imm32::ShiftOf(src.scale).value;

      Register scratch2 = temps.Acquire();
      MOZ_ASSERT(scratch2 != src.base);
      index = scratch2;
      asMasm().ma_dsll(index, src.index, Imm32(shift));
    }

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

  // dest will be overwritten anyway
  asMasm().computeEffectiveAddress(src, dest);
  return asMasm().ma_load(dest, Address(dest, 0), size, extension);
}

void MacroAssemblerMIPSShared::ma_load_unaligned(Register dest,
                                                 const BaseIndex& src,
                                                 LoadStoreSize size,
                                                 LoadStoreExtension extension) {
  int16_t lowOffset, hiOffset;
  UseScratchRegisterScope temps(*this);
  Register base = temps.Acquire();
  asMasm().computeScaledAddress(src, base);
  Register scratch = temps.Acquire();

  if (Imm16::IsInSignedRange(src.offset) &&
      Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
    lowOffset = Imm16(src.offset).encode();
    hiOffset = Imm16(src.offset + size / 8 - 1).encode();
  } else {
    ma_li(scratch, Imm32(src.offset));
    asMasm().addPtr(scratch, base);
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  switch (size) {
    case SizeHalfWord:
      MOZ_ASSERT(dest != scratch);
      if (extension == ZeroExtend) {
        as_lbu(scratch, base, hiOffset);
      } else {
        as_lb(scratch, base, hiOffset);
      }
      as_lbu(dest, base, lowOffset);
      if (hasR2()) {
        as_ins(dest, scratch, 824);
      } else {
        as_sll(scratch, scratch, 8);
        as_or(dest, dest, scratch);
      }
      break;
    case SizeWord:
      MOZ_ASSERT(dest != base);
      as_lwl(dest, base, hiOffset);
      as_lwr(dest, base, lowOffset);
      if (extension == ZeroExtend) {
        asMasm().ma_dext(dest, dest, Imm32(0), Imm32(32));
      }
      break;
    case SizeDouble:
      MOZ_ASSERT(dest != base);
      as_ldl(dest, base, hiOffset);
      as_ldr(dest, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_load_unaligned");
  }
}

void MacroAssemblerMIPSShared::ma_load_unaligned(Register dest,
                                                 const Address& address,
                                                 LoadStoreSize size,
                                                 LoadStoreExtension extension) {
  int16_t lowOffset, hiOffset;
  UseScratchRegisterScope temps(*this);
  Register scratch1 = temps.Acquire();
  Register scratch2 = temps.Acquire();
  Register base;

  if (Imm16::IsInSignedRange(address.offset) &&
      Imm16::IsInSignedRange(address.offset + size / 8 - 1)) {
    base = address.base;
    lowOffset = Imm16(address.offset).encode();
    hiOffset = Imm16(address.offset + size / 8 - 1).encode();
  } else {
    ma_li(scratch1, Imm32(address.offset));
    asMasm().addPtr(address.base, scratch1);
    base = scratch1;
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  switch (size) {
    case SizeHalfWord:
      MOZ_ASSERT(base != scratch2 && dest != scratch2);
      if (extension == ZeroExtend) {
        as_lbu(scratch2, base, hiOffset);
      } else {
        as_lb(scratch2, base, hiOffset);
      }
      as_lbu(dest, base, lowOffset);
      if (hasR2()) {
        as_ins(dest, scratch2, 824);
      } else {
        as_sll(scratch2, scratch2, 8);
        as_or(dest, dest, scratch2);
      }
      break;
    case SizeWord:
      MOZ_ASSERT(dest != base);
      as_lwl(dest, base, hiOffset);
      as_lwr(dest, base, lowOffset);
      if (extension == ZeroExtend) {
        as_dext(dest, dest, 032);
      }
      break;
    case SizeDouble:
      MOZ_ASSERT(dest != base);
      as_ldl(dest, base, hiOffset);
      as_ldr(dest, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_load_unaligned");
  }
}

void MacroAssemblerMIPSShared::ma_load_unaligned(
    const wasm::MemoryAccessDesc& access, Register dest, const BaseIndex& src,
    Register temp, LoadStoreSize size, LoadStoreExtension extension) {
  MOZ_ASSERT(std::endian::native == std::endian::little,
             "Wasm-only; wasm is disabled on big-endian.");
  int16_t lowOffset, hiOffset;
  Register base;

  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  asMasm().computeScaledAddress(src, scratch2);

  if (Imm16::IsInSignedRange(src.offset) &&
      Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
    base = scratch2;
    lowOffset = Imm16(src.offset).encode();
    hiOffset = Imm16(src.offset + size / 8 - 1).encode();
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(src.offset));
    asMasm().addPtr(scratch2, scratch);
    base = scratch;
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  BufferOffset load;
  unsigned byteSize = access.byteSize();
  switch (size) {
    case SizeHalfWord:
      // begins with 1-byte load
      byteSize = 1;
      if (extension == ZeroExtend) {
        load = as_lbu(temp, base, hiOffset);
      } else {
        load = as_lb(temp, base, hiOffset);
      }
      as_lbu(dest, base, lowOffset);
      if (hasR2()) {
        as_ins(dest, temp, 824);
      } else {
        as_sll(temp, temp, 8);
        as_or(dest, dest, temp);
      }
      break;
    case SizeWord:
      load = as_lwl(dest, base, hiOffset);
      as_lwr(dest, base, lowOffset);
      if (extension == ZeroExtend) {
        asMasm().ma_dext(dest, dest, Imm32(0), Imm32(32));
      }
      break;
    case SizeDouble:
      load = as_ldl(dest, base, hiOffset);
      as_ldr(dest, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_load");
  }

  append(access, wasm::TrapMachineInsnForLoad(byteSize),
         FaultingCodeOffset(load.getOffset()));
}

FaultingCodeOffset MacroAssemblerMIPSShared::ma_store(
    Register data, const BaseIndex& dest, LoadStoreSize size,
    LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  if (isLoongson() && Imm8::IsInSignedRange(dest.offset)) {
    FaultingCodeOffset fco;
    Register index = dest.index;

    if (dest.scale != TimesOne) {
      int32_t shift = Imm32::ShiftOf(dest.scale).value;

      MOZ_ASSERT(scratch2 != dest.base);
      index = scratch2;
      asMasm().ma_dsll(index, dest.index, Imm32(shift));
    }

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

  asMasm().computeScaledAddress(dest, scratch2);
  return asMasm().ma_store(data, Address(scratch2, dest.offset), size,
                           extension);
}

void MacroAssemblerMIPSShared::ma_store(Imm32 imm, const BaseIndex& dest,
                                        LoadStoreSize size,
                                        LoadStoreExtension extension) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  if (isLoongson() && Imm8::IsInSignedRange(dest.offset)) {
    Register data = zero;
    Register index = dest.index;

    if (imm.value) {
      Register scratch = temps.Acquire();
      MOZ_ASSERT(scratch != dest.base);
      MOZ_ASSERT(scratch != dest.index);
      data = scratch;
      ma_li(data, imm);
    }

    if (dest.scale != TimesOne) {
      int32_t shift = Imm32::ShiftOf(dest.scale).value;

      MOZ_ASSERT(scratch2 != dest.base);
      index = scratch2;
      asMasm().ma_dsll(index, dest.index, Imm32(shift));
    }

    switch (size) {
      case SizeByte:
        as_gssbx(data, dest.base, index, dest.offset);
        break;
      case SizeHalfWord:
        as_gsshx(data, dest.base, index, dest.offset);
        break;
      case SizeWord:
        as_gsswx(data, dest.base, index, dest.offset);
        break;
      case SizeDouble:
        as_gssdx(data, dest.base, index, dest.offset);
        break;
      default:
        MOZ_CRASH("Invalid argument for ma_store");
    }
    return;
  }

  // Make sure that scratch2 contains absolute address so that
  // offset is 0.
  asMasm().computeEffectiveAddress(dest, scratch2);

  Register scratch = temps.Acquire();
  // Scrach register is free now, use it for loading imm value
  ma_li(scratch, imm);

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

void MacroAssemblerMIPSShared::ma_store_unaligned(Register data,
                                                  const Address& address,
                                                  LoadStoreSize size) {
  int16_t lowOffset, hiOffset;
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  Register base;

  if (Imm16::IsInSignedRange(address.offset) &&
      Imm16::IsInSignedRange(address.offset + size / 8 - 1)) {
    base = address.base;
    lowOffset = Imm16(address.offset).encode();
    hiOffset = Imm16(address.offset + size / 8 - 1).encode();
  } else {
    ma_li(scratch, Imm32(address.offset));
    asMasm().addPtr(address.base, scratch);
    base = scratch;
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  switch (size) {
    case SizeHalfWord: {
      UseScratchRegisterScope temps2(*this);
      Register scratch2 = temps2.Acquire();
      MOZ_ASSERT(base != scratch2);
      as_sb(data, base, lowOffset);
      ma_ext(scratch2, data, 88);
      as_sb(scratch2, base, hiOffset);
      break;
    }
    case SizeWord:
      as_swl(data, base, hiOffset);
      as_swr(data, base, lowOffset);
      break;
    case SizeDouble:
      as_sdl(data, base, hiOffset);
      as_sdr(data, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_store_unaligned");
  }
}

void MacroAssemblerMIPSShared::ma_store_unaligned(Register data,
                                                  const BaseIndex& dest,
                                                  LoadStoreSize size) {
  int16_t lowOffset, hiOffset;
  UseScratchRegisterScope temps(*this);
  Register base = temps.Acquire();
  asMasm().computeScaledAddress(dest, base);
  Register scratch = temps.Acquire();

  if (Imm16::IsInSignedRange(dest.offset) &&
      Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
    lowOffset = Imm16(dest.offset).encode();
    hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
  } else {
    ma_li(scratch, Imm32(dest.offset));
    asMasm().addPtr(scratch, base);
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  switch (size) {
    case SizeHalfWord:
      MOZ_ASSERT(base != scratch);
      as_sb(data, base, lowOffset);
      ma_ext(scratch, data, 88);
      as_sb(scratch, base, hiOffset);
      break;
    case SizeWord:
      as_swl(data, base, hiOffset);
      as_swr(data, base, lowOffset);
      break;
    case SizeDouble:
      as_sdl(data, base, hiOffset);
      as_sdr(data, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_store_unaligned");
  }
}

void MacroAssemblerMIPSShared::ma_store_unaligned(
    const wasm::MemoryAccessDesc& access, Register data, const BaseIndex& dest,
    Register temp, LoadStoreSize size, LoadStoreExtension extension) {
  MOZ_ASSERT(std::endian::native == std::endian::little,
             "Wasm-only; wasm is disabled on big-endian.");
  int16_t lowOffset, hiOffset;
  Register base;

  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  asMasm().computeScaledAddress(dest, scratch2);

  if (Imm16::IsInSignedRange(dest.offset) &&
      Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
    base = scratch2;
    lowOffset = Imm16(dest.offset).encode();
    hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
  } else {
    Register scratch = temps.Acquire();
    ma_li(scratch, Imm32(dest.offset));
    asMasm().addPtr(scratch2, scratch);
    base = scratch;
    lowOffset = Imm16(0).encode();
    hiOffset = Imm16(size / 8 - 1).encode();
  }

  BufferOffset store;
  unsigned byteSize = access.byteSize();
  switch (size) {
    case SizeHalfWord:
      // begins with 1-byte store
      byteSize = 1;
      ma_ext(temp, data, 88);
      store = as_sb(temp, base, hiOffset);
      as_sb(data, base, lowOffset);
      break;
    case SizeWord:
      store = as_swl(data, base, hiOffset);
      as_swr(data, base, lowOffset);
      break;
    case SizeDouble:
      store = as_sdl(data, base, hiOffset);
      as_sdr(data, base, lowOffset);
      break;
    default:
      MOZ_CRASH("Invalid argument for ma_store");
  }
  append(access, wasm::TrapMachineInsnForStore(byteSize),
         FaultingCodeOffset(store.getOffset()));
}

// Branches when done from within mips-specific code.
void MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, Label* label,
                                    Condition c, JumpKind jumpKind) {
  switch (c) {
    case Equal:
    case NotEqual:
      asMasm().branchWithCode(getBranchCode(lhs, rhs, c), label, jumpKind);
      break;
    case Always:
      ma_b(label, jumpKind);
      break;
    case Zero:
    case NonZero:
    case Signed:
    case NotSigned:
      MOZ_ASSERT(lhs == rhs);
      asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
      break;
    default: {
      UseScratchRegisterScope temps(*this);
      Register scratch = temps.Acquire();
      Condition cond = ma_cmp(scratch, lhs, rhs, c);
      asMasm().branchWithCode(getBranchCode(scratch, cond), label, jumpKind,
                              scratch);
    } break;
  }
}

void MacroAssemblerMIPSShared::ma_b(Register lhs, Imm32 imm, Label* label,
                                    Condition c, JumpKind jumpKind) {
  MOZ_ASSERT(c != Overflow);
  if (imm.value == 0) {
    if (c == Always || c == AboveOrEqual) {
      ma_b(label, jumpKind);
    } else if (c == Below) {
      ;  // This condition is always false. No branch required.
    } else {
      asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
    }
  } else {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();
    switch (c) {
      case Equal:
      case NotEqual:
        ma_li(scratch, imm);
        ma_b(lhs, scratch, label, c, jumpKind);
        break;
      default:
        Condition cond = ma_cmp(scratch, lhs, imm, c);
        asMasm().branchWithCode(getBranchCode(scratch, cond), label, jumpKind,
                                scratch);
    }
  }
}

void MacroAssemblerMIPSShared::ma_b(Register lhs, ImmPtr imm, Label* l,
                                    Condition c, JumpKind jumpKind) {
  asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
}

void MacroAssemblerMIPSShared::ma_b(Label* label, JumpKind jumpKind) {
  asMasm().branchWithCode(getBranchCode(BranchIsJump), label, jumpKind);
}

Assembler::Condition MacroAssemblerMIPSShared::ma_cmp(Register dest,
                                                      Register lhs,
                                                      Register rhs,
                                                      Condition c) {
  switch (c) {
    case Above:
      // bgtu s,t,label =>
      //   sltu at,t,s
      //   bne at,$zero,offs
      as_sltu(dest, rhs, lhs);
      return NotEqual;
    case AboveOrEqual:
      // bgeu s,t,label =>
      //   sltu at,s,t
      //   beq at,$zero,offs
      as_sltu(dest, lhs, rhs);
      return Equal;
    case Below:
      // bltu s,t,label =>
      //   sltu at,s,t
      //   bne at,$zero,offs
      as_sltu(dest, lhs, rhs);
      return NotEqual;
    case BelowOrEqual:
      // bleu s,t,label =>
      //   sltu at,t,s
      //   beq at,$zero,offs
      as_sltu(dest, rhs, lhs);
      return Equal;
    case GreaterThan:
      // bgt s,t,label =>
      //   slt at,t,s
      //   bne at,$zero,offs
      as_slt(dest, rhs, lhs);
      return NotEqual;
    case GreaterThanOrEqual:
      // bge s,t,label =>
      //   slt at,s,t
      //   beq at,$zero,offs
      as_slt(dest, lhs, rhs);
      return Equal;
    case LessThan:
      // blt s,t,label =>
      //   slt at,s,t
      //   bne at,$zero,offs
      as_slt(dest, lhs, rhs);
      return NotEqual;
    case LessThanOrEqual:
      // ble s,t,label =>
      //   slt at,t,s
      //   beq at,$zero,offs
      as_slt(dest, rhs, lhs);
      return Equal;
    default:
      MOZ_CRASH("Invalid condition.");
  }
  return Always;
}

Assembler::Condition MacroAssemblerMIPSShared::ma_cmp(Register dest,
                                                      Register lhs, Imm32 imm,
                                                      Condition c) {
  UseScratchRegisterScope temps(*this);
  switch (c) {
    case Above:
    case BelowOrEqual:
      if (Imm16::IsInSignedRange(imm.value + 1) && imm.value != -1) {
        // lhs <= rhs via lhs < rhs + 1 if rhs + 1 does not overflow
        as_sltiu(dest, lhs, imm.value + 1);

        return (c == BelowOrEqual ? NotEqual : Equal);
      } else {
        Register scratch = dest == lhs ? temps.Acquire() : dest;
        ma_li(scratch, imm);
        as_sltu(dest, scratch, lhs);
        return (c == BelowOrEqual ? Equal : NotEqual);
      }
    case AboveOrEqual:
    case Below:
      if (Imm16::IsInSignedRange(imm.value)) {
        as_sltiu(dest, lhs, imm.value);
      } else {
        Register scratch = dest == lhs ? temps.Acquire() : dest;
        ma_li(scratch, imm);
        as_sltu(dest, lhs, scratch);
      }
      return (c == AboveOrEqual ? Equal : NotEqual);
    case GreaterThan:
    case LessThanOrEqual:
      if (Imm16::IsInSignedRange(imm.value + 1)) {
        // lhs <= rhs via lhs < rhs + 1.
        as_slti(dest, lhs, imm.value + 1);
        return (c == LessThanOrEqual ? NotEqual : Equal);
      } else {
        Register scratch = dest == lhs ? temps.Acquire() : dest;
        ma_li(scratch, imm);
        as_slt(dest, scratch, lhs);
        return (c == LessThanOrEqual ? Equal : NotEqual);
      }
    case GreaterThanOrEqual:
    case LessThan:
      if (Imm16::IsInSignedRange(imm.value)) {
        as_slti(dest, lhs, imm.value);
      } else {
        Register scratch = dest == lhs ? temps.Acquire() : dest;
        ma_li(scratch, imm);
        as_slt(dest, lhs, scratch);
      }
      return (c == GreaterThanOrEqual ? Equal : NotEqual);
    default:
      MOZ_CRASH("Invalid condition.");
  }
  return Always;
}

void MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Register rt,
                                          Condition c) {
  switch (c) {
    case Equal:
      // seq d,s,t =>
      //   xor d,s,t
      //   sltiu d,d,1
      as_xor(rd, rs, rt);
      as_sltiu(rd, rd, 1);
      break;
    case NotEqual:
      // sne d,s,t =>
      //   xor d,s,t
      //   sltu d,$zero,d
      as_xor(rd, rs, rt);
      as_sltu(rd, zero, rd);
      break;
    case Above:
      // sgtu d,s,t =>
      //   sltu d,t,s
      as_sltu(rd, rt, rs);
      break;
    case AboveOrEqual:
      // sgeu d,s,t =>
      //   sltu d,s,t
      //   xori d,d,1
      as_sltu(rd, rs, rt);
      as_xori(rd, rd, 1);
      break;
    case Below:
      // sltu d,s,t
      as_sltu(rd, rs, rt);
      break;
    case BelowOrEqual:
      // sleu d,s,t =>
      //   sltu d,t,s
      //   xori d,d,1
      as_sltu(rd, rt, rs);
      as_xori(rd, rd, 1);
      break;
    case GreaterThan:
      // sgt d,s,t =>
      //   slt d,t,s
      as_slt(rd, rt, rs);
      break;
    case GreaterThanOrEqual:
      // sge d,s,t =>
      //   slt d,s,t
      //   xori d,d,1
      as_slt(rd, rs, rt);
      as_xori(rd, rd, 1);
      break;
    case LessThan:
      // slt d,s,t
      as_slt(rd, rs, rt);
      break;
    case LessThanOrEqual:
      // sle d,s,t =>
      //   slt d,t,s
      //   xori d,d,1
      as_slt(rd, rt, rs);
      as_xori(rd, rd, 1);
      break;
    case Zero:
      MOZ_ASSERT(rs == rt);
      // seq d,s,$zero =>
      //   sltiu d,s,1
      as_sltiu(rd, rs, 1);
      break;
    case NonZero:
      MOZ_ASSERT(rs == rt);
      // sne d,s,$zero =>
      //   sltu d,$zero,s
      as_sltu(rd, zero, rs);
      break;
    case Signed:
      MOZ_ASSERT(rs == rt);
      as_slt(rd, rs, zero);
      break;
    case NotSigned:
      MOZ_ASSERT(rs == rt);
      // sge d,s,$zero =>
      //   slt d,s,$zero
      //   xori d,d,1
      as_slt(rd, rs, zero);
      as_xori(rd, rd, 1);
      break;
    default:
      MOZ_CRASH("Invalid condition.");
  }
}

void MacroAssemblerMIPSShared::compareFloatingPoint(
    FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, DoubleCondition c,
    FloatTestKind* testKind, FPConditionBit fcc) {
  switch (c) {
    case DoubleOrdered:
      as_cun(fmt, lhs, rhs, fcc);
      *testKind = TestForFalse;
      break;
    case DoubleEqual:
      as_ceq(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleNotEqual:
      as_cueq(fmt, lhs, rhs, fcc);
      *testKind = TestForFalse;
      break;
    case DoubleGreaterThan:
      as_colt(fmt, rhs, lhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleGreaterThanOrEqual:
      as_cole(fmt, rhs, lhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleLessThan:
      as_colt(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleLessThanOrEqual:
      as_cole(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleUnordered:
      as_cun(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleEqualOrUnordered:
      as_cueq(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleNotEqualOrUnordered:
      as_ceq(fmt, lhs, rhs, fcc);
      *testKind = TestForFalse;
      break;
    case DoubleGreaterThanOrUnordered:
      as_cult(fmt, rhs, lhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleGreaterThanOrEqualOrUnordered:
      as_cule(fmt, rhs, lhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleLessThanOrUnordered:
      as_cult(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    case DoubleLessThanOrEqualOrUnordered:
      as_cule(fmt, lhs, rhs, fcc);
      *testKind = TestForTrue;
      break;
    default:
      MOZ_CRASH("Invalid DoubleCondition.");
  }
}

void MacroAssemblerMIPSShared::ma_cmp_set_double(Register dest,
                                                 FloatRegister lhs,
                                                 FloatRegister rhs,
                                                 DoubleCondition c) {
  FloatTestKind moveCondition;
  compareFloatingPoint(DoubleFloat, lhs, rhs, c, &moveCondition);

#ifdef MIPSR6
  as_mfc1(dest, FloatRegisters::f24);
  if (moveCondition == TestForTrue) {
    as_andi(dest, dest, 0x1);
  } else {
    as_addiu(dest, dest, 0x1);
  }
#else
  ma_li(dest, Imm32(1));

  if (moveCondition == TestForTrue) {
    as_movf(dest, zero);
  } else {
    as_movt(dest, zero);
  }
#endif
}

void MacroAssemblerMIPSShared::ma_cmp_set_float32(Register dest,
                                                  FloatRegister lhs,
                                                  FloatRegister rhs,
                                                  DoubleCondition c) {
  FloatTestKind moveCondition;
  compareFloatingPoint(SingleFloat, lhs, rhs, c, &moveCondition);

#ifdef MIPSR6
  as_mfc1(dest, FloatRegisters::f24);
  if (moveCondition == TestForTrue) {
    as_andi(dest, dest, 0x1);
  } else {
    as_addiu(dest, dest, 0x1);
  }
#else
  ma_li(dest, Imm32(1));

  if (moveCondition == TestForTrue) {
    as_movf(dest, zero);
  } else {
    as_movt(dest, zero);
  }
#endif
}

void MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Imm32 imm,
                                          Condition c) {
  if (imm.value == 0) {
    switch (c) {
      case Equal:
      case BelowOrEqual:
        as_sltiu(rd, rs, 1);
        break;
      case NotEqual:
      case Above:
        as_sltu(rd, zero, rs);
        break;
      case AboveOrEqual:
      case Below:
        as_ori(rd, zero, c == AboveOrEqual ? 1 : 0);
        break;
      case GreaterThan:
      case LessThanOrEqual:
        as_slt(rd, zero, rs);
        if (c == LessThanOrEqual) {
          as_xori(rd, rd, 1);
        }
        break;
      case LessThan:
      case GreaterThanOrEqual:
        as_slt(rd, rs, zero);
        if (c == GreaterThanOrEqual) {
          as_xori(rd, rd, 1);
        }
        break;
      case Zero:
        as_sltiu(rd, rs, 1);
        break;
      case NonZero:
        as_sltu(rd, zero, rs);
        break;
      case Signed:
        as_slt(rd, rs, zero);
        break;
      case NotSigned:
        as_slt(rd, rs, zero);
        as_xori(rd, rd, 1);
        break;
      default:
        MOZ_CRASH("Invalid condition.");
    }
    return;
  }

  switch (c) {
    case Equal:
    case NotEqual:
      ma_xor(rd, rs, imm);
      if (c == Equal) {
        as_sltiu(rd, rd, 1);
      } else {
        as_sltu(rd, zero, rd);
      }
      break;
    case Zero:
    case NonZero:
    case Signed:
    case NotSigned:
      MOZ_CRASH("Invalid condition.");
    default:
      Condition cond = ma_cmp(rd, rs, imm, c);
      MOZ_ASSERT(cond == Equal || cond == NotEqual);

      if (cond == Equal) as_xori(rd, rd, 1);
  }
}

// fp instructions
void MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value) {
  Imm32 imm(mozilla::BitwiseCast<uint32_t>(value));

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

FaultingCodeOffset MacroAssemblerMIPSShared::ma_sd(FloatRegister ft,
                                                   BaseIndex address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  if (isLoongson() && Imm8::IsInSignedRange(address.offset)) {
    Register index = address.index;

    if (address.scale != TimesOne) {
      int32_t shift = Imm32::ShiftOf(address.scale).value;

      MOZ_ASSERT(scratch2 != address.base);
      index = scratch2;
      asMasm().ma_dsll(index, address.index, Imm32(shift));
    }

    FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
    as_gssdx(ft, address.base, index, address.offset);
    return fco;
  }

  asMasm().computeScaledAddress(address, scratch2);
  return asMasm().ma_sd(ft, Address(scratch2, address.offset));
}

FaultingCodeOffset MacroAssemblerMIPSShared::ma_ss(FloatRegister ft,
                                                   BaseIndex address) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  if (isLoongson() && Imm8::IsInSignedRange(address.offset)) {
    Register index = address.index;

    if (address.scale != TimesOne) {
      int32_t shift = Imm32::ShiftOf(address.scale).value;

      MOZ_ASSERT(scratch2 != address.base);
      index = scratch2;
      asMasm().ma_dsll(index, address.index, Imm32(shift));
    }

    FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
    as_gsssx(ft, address.base, index, address.offset);
    return fco;
  }

  asMasm().computeScaledAddress(address, scratch2);
  return asMasm().ma_ss(ft, Address(scratch2, address.offset));
}

FaultingCodeOffset MacroAssemblerMIPSShared::ma_ld(FloatRegister ft,
                                                   const BaseIndex& src) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  asMasm().computeScaledAddress(src, scratch2);
  return asMasm().ma_ld(ft, Address(scratch2, src.offset));
}

FaultingCodeOffset MacroAssemblerMIPSShared::ma_ls(FloatRegister ft,
                                                   const BaseIndex& src) {
  UseScratchRegisterScope temps(*this);
  Register scratch2 = temps.Acquire();
  asMasm().computeScaledAddress(src, scratch2);
  return asMasm().ma_ls(ft, Address(scratch2, src.offset));
}

void MacroAssemblerMIPSShared::ma_bc1s(FloatRegister lhs, FloatRegister rhs,
                                       Label* label, DoubleCondition c,
                                       JumpKind jumpKind, FPConditionBit fcc) {
  FloatTestKind testKind;
  compareFloatingPoint(SingleFloat, lhs, rhs, c, &testKind, fcc);
  asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
}

void MacroAssemblerMIPSShared::ma_bc1d(FloatRegister lhs, FloatRegister rhs,
                                       Label* label, DoubleCondition c,
                                       JumpKind jumpKind, FPConditionBit fcc) {
  FloatTestKind testKind;
  compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc);
  asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
}

void MacroAssemblerMIPSShared::minMax32(Register lhs, Register rhs,
                                        Register dest, bool isMax) {
  if (rhs == dest) {
    std::swap(lhs, rhs);
  }

  auto cond = isMax ? Assembler::GreaterThan : Assembler::LessThan;
  if (lhs != dest) {
    asMasm().move32(lhs, dest);
  }
  asMasm().cmp32Move32(cond, rhs, lhs, rhs, dest);
}

void MacroAssemblerMIPSShared::minMax32(Register lhs, Imm32 rhs, Register dest,
                                        bool isMax) {
  if (rhs.value == 0) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    if (isMax) {
      // dest = (~lhs >> 31) & lhs
      as_nor(scratch, lhs, zero);
      as_sra(scratch, scratch, 31);
      as_and(dest, lhs, scratch);
    } else {
      // dest = (lhs >> 31) & lhs
      as_sra(scratch, lhs, 31);
      as_and(dest, lhs, scratch);
    }
    return;
  }

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  asMasm().move32(rhs, scratch);

  minMax32(lhs, scratch, dest, isMax);
}

void MacroAssemblerMIPSShared::minMaxPtr(Register lhs, Register rhs,
                                         Register dest, bool isMax) {
  if (rhs == dest) {
    std::swap(lhs, rhs);
  }

  auto cond = isMax ? Assembler::GreaterThan : Assembler::LessThan;
  if (lhs != dest) {
    asMasm().movePtr(lhs, dest);
  }
  asMasm().cmpPtrMovePtr(cond, rhs, lhs, rhs, dest);
}

void MacroAssemblerMIPSShared::minMaxPtr(Register lhs, ImmWord rhs,
                                         Register dest, bool isMax) {
  if (rhs.value == 0) {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    if (isMax) {
      // dest = (~lhs >> 63) & lhs
      as_nor(scratch, lhs, zero);
      as_dsra32(scratch, scratch, 63);
      as_and(dest, lhs, scratch);
    } else {
      // dest = (lhs >> 63) & lhs
      as_dsra32(scratch, lhs, 63);
      as_and(dest, lhs, scratch);
    }
    return;
  }

  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  asMasm().movePtr(rhs, scratch);

  minMaxPtr(lhs, scratch, dest, isMax);
}

void MacroAssemblerMIPSShared::minMaxDouble(FloatRegister srcDest,
                                            FloatRegister second,
                                            bool handleNaN, bool isMax) {
  FloatRegister first = srcDest;

  Assembler::DoubleCondition cond = isMax ? Assembler::DoubleLessThanOrEqual
                                          : Assembler::DoubleGreaterThanOrEqual;
  Label nan, equal, done;
  FloatTestKind moveCondition;

  // First or second is NaN, result is NaN.
  ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump);
#ifdef MIPSR6
  if (isMax) {
    as_max(DoubleFloat, srcDest, first, second);
  } else {
    as_min(DoubleFloat, srcDest, first, second);
  }
#else
  // Make sure we handle -0 and 0 right.
  ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump);
  compareFloatingPoint(DoubleFloat, first, second, cond, &moveCondition);
  MOZ_ASSERT(TestForTrue == moveCondition);
  as_movt(DoubleFloat, first, second);
  ma_b(&done, ShortJump);

  // Check for zero.
  bind(&equal);
  asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
  compareFloatingPoint(DoubleFloat, first, ScratchDoubleReg,
                       Assembler::DoubleEqual, &moveCondition);

  // So now both operands are either -0 or 0.
  if (isMax) {
    // -0 + -0 = -0 and -0 + 0 = 0.
    as_addd(ScratchDoubleReg, first, second);
  } else {
    as_negd(ScratchDoubleReg, first);
    as_subd(ScratchDoubleReg, ScratchDoubleReg, second);
    as_negd(ScratchDoubleReg, ScratchDoubleReg);
  }
  MOZ_ASSERT(TestForTrue == moveCondition);
  // First is 0 or -0, move max/min to it, else just return it.
  as_movt(DoubleFloat, first, ScratchDoubleReg);
#endif
  ma_b(&done, ShortJump);

  bind(&nan);
  asMasm().loadConstantDouble(JS::GenericNaN(), srcDest);

  bind(&done);
}

void MacroAssemblerMIPSShared::minMaxFloat32(FloatRegister srcDest,
                                             FloatRegister second,
                                             bool handleNaN, bool isMax) {
  FloatRegister first = srcDest;

  Assembler::DoubleCondition cond = isMax ? Assembler::DoubleLessThanOrEqual
                                          : Assembler::DoubleGreaterThanOrEqual;
  Label nan, equal, done;
  FloatTestKind moveCondition;

  // First or second is NaN, result is NaN.
  ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump);
#ifdef MIPSR6
  if (isMax) {
    as_max(SingleFloat, srcDest, first, second);
  } else {
    as_min(SingleFloat, srcDest, first, second);
  }
#else
  // Make sure we handle -0 and 0 right.
  ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump);
  compareFloatingPoint(SingleFloat, first, second, cond, &moveCondition);
  MOZ_ASSERT(TestForTrue == moveCondition);
  as_movt(SingleFloat, first, second);
  ma_b(&done, ShortJump);

  // Check for zero.
  bind(&equal);
  asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
  compareFloatingPoint(SingleFloat, first, ScratchFloat32Reg,
                       Assembler::DoubleEqual, &moveCondition);

  // So now both operands are either -0 or 0.
  if (isMax) {
    // -0 + -0 = -0 and -0 + 0 = 0.
    as_adds(ScratchFloat32Reg, first, second);
  } else {
    as_negs(ScratchFloat32Reg, first);
    as_subs(ScratchFloat32Reg, ScratchFloat32Reg, second);
    as_negs(ScratchFloat32Reg, ScratchFloat32Reg);
  }
  MOZ_ASSERT(TestForTrue == moveCondition);
  // First is 0 or -0, move max/min to it, else just return it.
  as_movt(SingleFloat, first, ScratchFloat32Reg);
#endif
  ma_b(&done, ShortJump);

  bind(&nan);
  asMasm().loadConstantFloat32(JS::GenericNaN(), srcDest);

  bind(&done);
}

FaultingCodeOffset MacroAssemblerMIPSShared::loadDouble(const Address& address,
                                                        FloatRegister dest) {
  return asMasm().ma_ld(dest, address);
}

FaultingCodeOffset MacroAssemblerMIPSShared::loadDouble(const BaseIndex& src,
                                                        FloatRegister dest) {
  return asMasm().ma_ld(dest, src);
}

FaultingCodeOffset MacroAssemblerMIPSShared::loadFloat32(const Address& address,
                                                         FloatRegister dest) {
  return asMasm().ma_ls(dest, address);
}

FaultingCodeOffset MacroAssemblerMIPSShared::loadFloat32(const BaseIndex& src,
                                                         FloatRegister dest) {
  return asMasm().ma_ls(dest, src);
}

void MacroAssemblerMIPSShared::ma_call(ImmPtr dest) {
  asMasm().ma_liPatchable(CallReg, dest);
  as_jalr(CallReg);
  as_nop();
}

void MacroAssemblerMIPSShared::ma_jump(ImmPtr dest) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  asMasm().ma_liPatchable(scratch, dest);
  as_jr(scratch);
  as_nop();
}

MacroAssembler& MacroAssemblerMIPSShared::asMasm() {
  return *static_cast<MacroAssembler*>(this);
}

const MacroAssembler& MacroAssemblerMIPSShared::asMasm() const {
  return *static_cast<const MacroAssembler*>(this);
}

//{{{ check_macroassembler_style
// ===============================================================
// MacroAssembler high-level usage.

void MacroAssembler::flush() {}

// ===============================================================
// Stack manipulation functions.

void MacroAssembler::Push(Register reg) {
  push(reg);
  adjustFrame(int32_t(sizeof(intptr_t)));
}

void MacroAssembler::Push(const Imm32 imm) {
  push(imm);
  adjustFrame(int32_t(sizeof(intptr_t)));
}

void MacroAssembler::Push(const ImmWord imm) {
  push(imm);
  adjustFrame(int32_t(sizeof(intptr_t)));
}

void MacroAssembler::Push(const ImmPtr imm) {
  Push(ImmWord(uintptr_t(imm.value)));
}

void MacroAssembler::Push(const ImmGCPtr ptr) {
  push(ptr);
  adjustFrame(int32_t(sizeof(intptr_t)));
}

void MacroAssembler::Push(FloatRegister f) {
  push(f);
  adjustFrame(int32_t(f.pushSize()));
}

void MacroAssembler::Pop(Register reg) {
  pop(reg);
  adjustFrame(-int32_t(sizeof(intptr_t)));
}

void MacroAssembler::Pop(FloatRegister f) {
  pop(f);
  adjustFrame(-int32_t(f.pushSize()));
}

void MacroAssembler::Pop(const ValueOperand& val) {
  popValue(val);
  adjustFrame(-int32_t(sizeof(Value)));
}

void MacroAssembler::PopStackPtr() {
  loadPtr(Address(StackPointer, 0), StackPointer);
  adjustFrame(-int32_t(sizeof(intptr_t)));
}

// ===============================================================
// Simple call functions.

CodeOffset MacroAssembler::call(Register reg) {
  as_jalr(reg);
  as_nop();
  return CodeOffset(currentOffset());
}

CodeOffset MacroAssembler::call(Label* label) {
  ma_bal(label);
  return CodeOffset(currentOffset());
}

CodeOffset MacroAssembler::callWithPatch() {
  UseScratchRegisterScope temps(*this);
  as_bal(BOffImm16(3 * sizeof(uint32_t)));
  addPtr(Imm32(5 * sizeof(uint32_t)), ra);
  // Allocate space which will be patched by patchCall().
  spew(".space 32bit initValue 0xffff ffff");
  writeInst(UINT32_MAX);
  Register scratch = temps.Acquire();
  as_lw(scratch, ra, -(int32_t)(5 * sizeof(uint32_t)));
  addPtr(ra, scratch);
  as_jr(scratch);
  as_nop();
  return CodeOffset(currentOffset());
}

void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
  BufferOffset call(callerOffset - 7 * sizeof(uint32_t));

  BOffImm16 offset = BufferOffset(calleeOffset).diffB<BOffImm16>(call);
  if (!offset.isInvalid()) {
    InstImm* bal = (InstImm*)editSrc(call);
    bal->setBOffImm16(offset);
  } else {
    uint32_t u32Offset = callerOffset - 5 * sizeof(uint32_t);
    uint32_t* u32 =
        reinterpret_cast<uint32_t*>(editSrc(BufferOffset(u32Offset)));
    *u32 = calleeOffset - callerOffset;
  }
}

CodeOffset MacroAssembler::farJumpWithPatch() {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  ma_move(scratch, ra);
  as_bal(BOffImm16(3 * sizeof(uint32_t)));
  Register scratch2 = temps.Acquire();
  as_lw(scratch2, ra, 0);
  // Allocate space which will be patched by patchFarJump().
  CodeOffset farJump(currentOffset());
  spew(".space 32bit initValue 0xffff ffff");
  writeInst(UINT32_MAX);
  addPtr(ra, scratch2);
  as_jr(scratch2);
  ma_move(ra, scratch);
  return farJump;
}

void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) {
  uint32_t* u32 =
      reinterpret_cast<uint32_t*>(editSrc(BufferOffset(farJump.offset())));
  MOZ_ASSERT(*u32 == UINT32_MAX);
  *u32 = targetOffset - farJump.offset();
}

void MacroAssembler::patchFarJump(uint8_t* farJump, uint8_t* target) {
  uint32_t* u32 = reinterpret_cast<uint32_t*>(farJump);
  MOZ_ASSERT(*u32 == UINT32_MAX);

  *u32 = (intptr_t)target - (intptr_t)farJump;
}

CodeOffset MacroAssembler::call(wasm::SymbolicAddress target) {
  movePtr(target, CallReg);
  return call(CallReg);
}

CodeOffset MacroAssembler::call(const Address& addr) {
  loadPtr(addr, CallReg);
  return call(CallReg);
}

void MacroAssembler::call(ImmWord target) { call(ImmPtr((void*)target.value)); }

void MacroAssembler::call(ImmPtr target) {
  BufferOffset bo = m_buffer.nextOffset();
  addPendingJump(bo, target, RelocationKind::HARDCODED);
  ma_call(target);
}

void MacroAssembler::call(JitCode* c) {
  UseScratchRegisterScope temps(*this);
  BufferOffset bo = m_buffer.nextOffset();
  addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE);
  Register scratch = temps.Acquire();
  ma_liPatchable(scratch, ImmPtr(c->raw()));
  callJitNoProfiler(scratch);
}

CodeOffset MacroAssembler::nopPatchableToCall() {
  as_nop();  // lui
  as_nop();  // ori
  as_nop();  // dsll
  as_nop();  // ori
  as_nop();  // jalr
  as_nop();
  return CodeOffset(currentOffset());
}

void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) {
  Instruction* inst = (Instruction*)call - 6 /* six nops */;
  Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)target);
  inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
}

void MacroAssembler::patchCallToNop(uint8_t* call) {
  Instruction* inst = (Instruction*)call - 6 /* six nops */;

  inst[0].makeNop();
  inst[1].makeNop();
  inst[2].makeNop();
  inst[3].makeNop();
  inst[4].makeNop();
  inst[5].makeNop();
}

CodeOffset MacroAssembler::move32WithPatch(Register dest) {
  CodeOffset offs = CodeOffset(currentOffset());
  ma_liPatchable(dest, Imm32(0));
  return offs;
}

void MacroAssembler::patchMove32(CodeOffset offset, Imm32 n) {
  patchSub32FromStackPtr(offset, n);
}

void MacroAssembler::pushReturnAddress() { push(ra); }

void MacroAssembler::popReturnAddress() { pop(ra); }

// ===============================================================
// Jit Frames.

uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) {
  CodeLabel cl;

  ma_li(scratch, &cl);
  Push(scratch);
  bind(&cl);
  uint32_t retAddr = currentOffset();

  addCodeLabel(cl);
  return retAddr;
}

void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) {
  ma_and(buffer, ptr, Imm32(int32_t(~gc::ChunkMask)));
  loadPtr(Address(buffer, gc::ChunkStoreBufferOffset), buffer);
}

void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr,
                                             Register temp, Label* label) {
  MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
  MOZ_ASSERT(ptr != temp);

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

  ma_and(scratch2, ptr, Imm32(int32_t(~gc::ChunkMask)));
  branchPtr(InvertCondition(cond),
            Address(scratch2, gc::ChunkStoreBufferOffset), ImmWord(0), label);
}

void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); }

// ===============================================================
// WebAssembly

FaultingCodeOffset MacroAssembler::wasmTrapInstruction() {
  FaultingCodeOffset fco = FaultingCodeOffset(currentOffset());
  as_teq(zero, zero, WASM_TRAP);
  return fco;
}

void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input,
                                               Register output,
                                               bool isSaturating,
                                               Label* oolEntry) {
  UseScratchRegisterScope temps(*this);
  as_truncwd(ScratchFloat32Reg, input);
  Register scratch = temps.Acquire();
  as_cfc1(scratch, Assembler::FCSR);
  moveFromFloat32(ScratchFloat32Reg, output);
  ma_ext(scratch, scratch, Assembler::CauseV, 1);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);
}

void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input,
                                                Register output,
                                                bool isSaturating,
                                                Label* oolEntry) {
  UseScratchRegisterScope temps(*this);
  as_truncws(ScratchFloat32Reg, input);
  Register scratch = temps.Acquire();
  as_cfc1(scratch, Assembler::FCSR);
  moveFromFloat32(ScratchFloat32Reg, output);
  ma_ext(scratch, scratch, Assembler::CauseV, 1);
  ma_b(scratch, Imm32(0), oolEntry, Assembler::NotEqual);
}

void MacroAssembler::oolWasmTruncateCheckF32ToI32(
    FloatRegister input, Register output, TruncFlags flags,
    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
  outOfLineWasmTruncateToInt32Check(input, output, MIRType::Float32, flags,
                                    rejoin, trapSiteDesc);
}

void MacroAssembler::oolWasmTruncateCheckF64ToI32(
    FloatRegister input, Register output, TruncFlags flags,
    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
  outOfLineWasmTruncateToInt32Check(input, output, MIRType::Double, flags,
                                    rejoin, trapSiteDesc);
}

void MacroAssembler::oolWasmTruncateCheckF32ToI64(
    FloatRegister input, Register64 output, TruncFlags flags,
    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
  outOfLineWasmTruncateToInt64Check(input, output, MIRType::Float32, flags,
                                    rejoin, trapSiteDesc);
}

void MacroAssembler::oolWasmTruncateCheckF64ToI64(
    FloatRegister input, Register64 output, TruncFlags flags,
    const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) {
  outOfLineWasmTruncateToInt64Check(input, output, MIRType::Double, flags,
                                    rejoin, trapSiteDesc);
}

void MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt32Check(
    FloatRegister input, Register output, MIRType fromType, TruncFlags flags,
    Label* rejoin, const wasm::TrapSiteDesc& trapSiteDesc) {
  bool isUnsigned = flags & TRUNC_UNSIGNED;
  bool isSaturating = flags & TRUNC_SATURATING;

  if (isSaturating) {
    if (fromType == MIRType::Double) {
      asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
    } else {
      asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
    }

    if (isUnsigned) {
      ma_li(output, Imm32(UINT32_MAX));

      FloatTestKind moveCondition;
      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
          fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
          Assembler::DoubleLessThanOrUnordered, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      as_movt(output, zero);
    } else {
      // Positive overflow is already saturated to INT32_MAX, so we only have
      // to handle NaN and negative overflow here.

      FloatTestKind moveCondition;
      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input, input,
          Assembler::DoubleUnordered, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      as_movt(output, zero);

      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
          fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
          Assembler::DoubleLessThan, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      UseScratchRegisterScope temps(*this);
      Register scratch = temps.Acquire();
      ma_li(scratch, Imm32(INT32_MIN));
      as_movt(output, scratch);
    }

    MOZ_ASSERT(rejoin->bound());
    asMasm().jump(rejoin);
    return;
  }

  Label inputIsNaN;

  if (fromType == MIRType::Double) {
    asMasm().branchDouble(Assembler::DoubleUnordered, input, input,
                          &inputIsNaN);
  } else if (fromType == MIRType::Float32) {
    asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
  }

  asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc);
  asMasm().bind(&inputIsNaN);
  asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
}

void MacroAssemblerMIPSShared::outOfLineWasmTruncateToInt64Check(
    FloatRegister input, Register64 output_, MIRType fromType, TruncFlags flags,
    Label* rejoin, const wasm::TrapSiteDesc& trapSiteDesc) {
  bool isUnsigned = flags & TRUNC_UNSIGNED;
  bool isSaturating = flags & TRUNC_SATURATING;

  if (isSaturating) {
    Register output = output_.reg;

    if (fromType == MIRType::Double) {
      asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
    } else {
      asMasm().loadConstantFloat32(0.0f, ScratchFloat32Reg);
    }

    if (isUnsigned) {
      asMasm().ma_li(output, ImmWord(UINT64_MAX));

      FloatTestKind moveCondition;
      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
          fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
          Assembler::DoubleLessThanOrUnordered, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      as_movt(output, zero);

    } else {
      // Positive overflow is already saturated to INT64_MAX, so we only have
      // to handle NaN and negative overflow here.

      FloatTestKind moveCondition;
      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input, input,
          Assembler::DoubleUnordered, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      as_movt(output, zero);

      compareFloatingPoint(
          fromType == MIRType::Double ? DoubleFloat : SingleFloat, input,
          fromType == MIRType::Double ? ScratchDoubleReg : ScratchFloat32Reg,
          Assembler::DoubleLessThan, &moveCondition);
      MOZ_ASSERT(moveCondition == TestForTrue);

      UseScratchRegisterScope temps(*this);
      Register scratch = temps.Acquire();
      asMasm().ma_li(scratch, ImmWord(INT64_MIN));
      as_movt(output, scratch);
    }

    MOZ_ASSERT(rejoin->bound());
    asMasm().jump(rejoin);
    return;
  }

  Label inputIsNaN;

  if (fromType == MIRType::Double) {
    asMasm().branchDouble(Assembler::DoubleUnordered, input, input,
                          &inputIsNaN);
  } else if (fromType == MIRType::Float32) {
    asMasm().branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
  }

  asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc);
  asMasm().bind(&inputIsNaN);
  asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
}

void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access,
                              Register memoryBase, Register ptr,
                              Register ptrScratch, AnyRegister output) {
  wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
}

void MacroAssembler::wasmUnalignedLoad(const wasm::MemoryAccessDesc& access,
                                       Register memoryBase, Register ptr,
                                       Register ptrScratch, Register output,
                                       Register tmp) {
  wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp);
}

void MacroAssembler::wasmUnalignedLoadFP(const wasm::MemoryAccessDesc& access,
                                         Register memoryBase, Register ptr,
                                         Register ptrScratch,
                                         FloatRegister output, Register tmp1) {
  wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp1);
}

void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access,
                               AnyRegister value, Register memoryBase,
                               Register ptr, Register ptrScratch) {
  wasmStoreImpl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
}

void MacroAssembler::wasmUnalignedStore(const wasm::MemoryAccessDesc& access,
                                        Register value, Register memoryBase,
                                        Register ptr, Register ptrScratch,
                                        Register tmp) {
  wasmStoreImpl(access, AnyRegister(value), memoryBase, ptr, ptrScratch, tmp);
}

void MacroAssembler::wasmUnalignedStoreFP(const wasm::MemoryAccessDesc& access,
                                          FloatRegister floatValue,
                                          Register memoryBase, Register ptr,
                                          Register ptrScratch, Register tmp) {
  wasmStoreImpl(access, AnyRegister(floatValue), memoryBase, ptr, ptrScratch,
                tmp);
}

void MacroAssemblerMIPSShared::wasmLoadImpl(
    const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
    Register ptrScratch, AnyRegister output, Register tmp) {
  access.assertOffsetInGuardPages();
  uint32_t offset = access.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());
  bool isFloat = Scalar::isFloatingType(access.type());

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

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

  asMasm().memoryBarrierBefore(access.sync());
  FaultingCodeOffset fco;
  if (isFloat) {
    if (byteSize == 4) {
      fco = asMasm().ma_ls(output.fpu(), address);
    } else {
      fco = asMasm().ma_ld(output.fpu(), address);
    }
  } else {
    fco = asMasm().ma_load(output.gpr(), address,
                           static_cast<LoadStoreSize>(8 * byteSize),
                           isSigned ? SignExtend : ZeroExtend);
  }
  asMasm().append(access,
                  wasm::TrapMachineInsnForLoad(Scalar::byteSize(access.type())),
                  fco);

  asMasm().memoryBarrierAfter(access.sync());
}

void MacroAssemblerMIPSShared::wasmStoreImpl(
    const wasm::MemoryAccessDesc& access, AnyRegister value,
    Register memoryBase, Register ptr, Register ptrScratch, Register tmp) {
  access.assertOffsetInGuardPages();
  uint32_t offset = access.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());
  bool isFloat = Scalar::isFloatingType(access.type());

  BaseIndex address(memoryBase, ptr, TimesOne);
  if (IsUnaligned(access)) {
    MOZ_ASSERT(tmp != InvalidReg);
    if (isFloat) {
      if (byteSize == 4) {
        asMasm().storeUnalignedFloat32(access, value.fpu(), tmp, address);
      } else {
        asMasm().storeUnalignedDouble(access, value.fpu(), tmp, address);
      }
    } else {
      asMasm().ma_store_unaligned(access, value.gpr(), address, tmp,
                                  static_cast<LoadStoreSize>(8 * byteSize),
                                  isSigned ? SignExtend : ZeroExtend);
    }
    return;
  }

  asMasm().memoryBarrierBefore(access.sync());
  // Only the last emitted instruction is a memory access.
  FaultingCodeOffset fco;
  if (isFloat) {
    if (byteSize == 4) {
      fco = asMasm().ma_ss(value.fpu(), address);
    } else {
      fco = asMasm().ma_sd(value.fpu(), address);
    }
  } else {
    fco = asMasm().ma_store(value.gpr(), address,
                            static_cast<LoadStoreSize>(8 * byteSize),
                            isSigned ? SignExtend : ZeroExtend);
  }
  asMasm().append(
      access, wasm::TrapMachineInsnForStore(Scalar::byteSize(access.type())),
      fco);
  asMasm().memoryBarrierAfter(access.sync());
}

void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch,
                                               ExitFrameType type) {
  enterFakeExitFrame(cxreg, scratch, type);
}

CodeOffset MacroAssembler::sub32FromMemAndBranchIfNegativeWithPatch(
    Address address, Label* label) {
  UseScratchRegisterScope temps(*this);
  Register scratch = temps.Acquire();
  MOZ_ASSERT(scratch != address.base);
  ma_load(scratch, address);
  // mips doesn't have imm subtract insn, instead we use addiu rs, rt, -imm.
  // 128 is arbitrary, but makes `*address` count upwards, which may help
  // to identify cases where the subsequent ::patch..() call was forgotten.
  as_addiu(scratch, scratch, 128);
  // Points immediately after the insn to patch
  CodeOffset patchPoint = CodeOffset(currentOffset());
  ma_store(scratch, address);
  ma_b(scratch, scratch, label, Assembler::Signed);
  return patchPoint;
}

void MacroAssembler::patchSub32FromMemAndBranchIfNegative(CodeOffset offset,
                                                          Imm32 imm) {
  int32_t val = imm.value;
  // Patching it to zero would make the insn pointless
  MOZ_RELEASE_ASSERT(val >= 1 && val <= 127);
  InstImm* inst = (InstImm*)m_buffer.getInst(BufferOffset(offset.offset() - 4));
  // mips doesn't have imm subtract insn, instead we use addiu rs, rt, -imm.
  // 31     25 20 15
  // |      |  |  |
  // 001001 rs rt imm = addiu rs, rt, imm
  MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_addiu >> OpcodeShift));
  inst->setImm16(Imm16(-val & 0xffff));
}

// ========================================================================
// Primitive atomic operations.

template <typename T>
static void CompareExchange(MacroAssembler& masm,
                            const wasm::MemoryAccessDesc* access,
                            Scalar::Type type, Synchronization sync,
                            const T& mem, Register oldval, Register newval,
                            Register valueTemp, Register offsetTemp,
                            Register maskTemp, Register output) {
  UseScratchRegisterScope temps(masm);

  bool signExtend = Scalar::isSignedIntType(type);
  unsigned nbytes = Scalar::byteSize(type);

  switch (nbytes) {
    case 1:
    case 2:
      break;
    case 4:
      MOZ_ASSERT(valueTemp == InvalidReg);
      MOZ_ASSERT(offsetTemp == InvalidReg);
      MOZ_ASSERT(maskTemp == InvalidReg);
      break;
    default:
      MOZ_CRASH();
  }

  Label again, end;

  Register scratch2 = temps.Acquire();
  masm.computeEffectiveAddress(mem, scratch2);

  if (nbytes == 4) {
    Register scratch = temps.Acquire();

    masm.memoryBarrierBefore(sync);
    masm.bind(&again);

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

    masm.as_ll(output, scratch2, 0);
    masm.ma_b(output, oldval, &end, Assembler::NotEqual, ShortJump);
    masm.ma_move(scratch, newval);
    masm.as_sc(scratch, scratch2, 0);
    masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

    masm.memoryBarrierAfter(sync);
    masm.bind(&end);

    return;
  }

  masm.as_andi(offsetTemp, scratch2, 3);
  masm.subPtr(offsetTemp, scratch2);
  if constexpr (std::endian::native != std::endian::little) {
    masm.as_xori(offsetTemp, offsetTemp, 3);
  }
  masm.as_sll(offsetTemp, offsetTemp, 3);
  masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
  masm.as_sllv(maskTemp, maskTemp, offsetTemp);
  masm.as_nor(maskTemp, zero, maskTemp);

  masm.memoryBarrierBefore(sync);

  masm.bind(&again);

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

  Register scratch = temps.Acquire();
  masm.as_ll(scratch, scratch2, 0);

  masm.as_srlv(output, scratch, offsetTemp);

  switch (nbytes) {
    case 1:
      if (signExtend) {
        masm.ma_seb(valueTemp, oldval);
        masm.ma_seb(output, output);
      } else {
        masm.as_andi(valueTemp, oldval, 0xff);
        masm.as_andi(output, output, 0xff);
      }
      break;
    case 2:
      if (signExtend) {
        masm.ma_seh(valueTemp, oldval);
        masm.ma_seh(output, output);
      } else {
        masm.as_andi(valueTemp, oldval, 0xffff);
        masm.as_andi(output, output, 0xffff);
      }
      break;
  }

  masm.ma_b(output, valueTemp, &end, Assembler::NotEqual, ShortJump);

  // truncate newval for 8-bit and 16-bit cmpxchg
  switch (nbytes) {
    case 1:
      masm.as_andi(valueTemp, newval, 0xff);
      break;
    case 2:
      masm.as_andi(valueTemp, newval, 0xffff);
      break;
  }

  masm.as_sllv(valueTemp, valueTemp, offsetTemp);
  masm.as_and(scratch, scratch, maskTemp);
  masm.as_or(scratch, scratch, valueTemp);

  masm.as_sc(scratch, scratch2, 0);

  masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

  masm.memoryBarrierAfter(sync);

  masm.bind(&end);
}

void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
                                     const Address& mem, Register oldval,
                                     Register newval, Register valueTemp,
                                     Register offsetTemp, Register maskTemp,
                                     Register output) {
  CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp,
                  offsetTemp, maskTemp, output);
}

void MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync,
                                     const BaseIndex& mem, Register oldval,
                                     Register newval, Register valueTemp,
                                     Register offsetTemp, Register maskTemp,
                                     Register output) {
  CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp,
                  offsetTemp, maskTemp, output);
}

void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
                                         const Address& mem, Register oldval,
                                         Register newval, Register valueTemp,
                                         Register offsetTemp, Register maskTemp,
                                         Register output) {
  CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
                  newval, valueTemp, offsetTemp, maskTemp, output);
}

void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access,
                                         const BaseIndex& mem, Register oldval,
                                         Register newval, Register valueTemp,
                                         Register offsetTemp, Register maskTemp,
                                         Register output) {
  CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval,
                  newval, valueTemp, offsetTemp, maskTemp, output);
}

template <typename T>
static void AtomicExchange(MacroAssembler& masm,
                           const wasm::MemoryAccessDesc* access,
                           Scalar::Type type, Synchronization sync,
                           const T& mem, Register value, Register valueTemp,
                           Register offsetTemp, Register maskTemp,
                           Register output) {
  bool signExtend = Scalar::isSignedIntType(type);
  unsigned nbytes = Scalar::byteSize(type);

  switch (nbytes) {
    case 1:
    case 2:
      break;
    case 4:
      MOZ_ASSERT(valueTemp == InvalidReg);
      MOZ_ASSERT(offsetTemp == InvalidReg);
      MOZ_ASSERT(maskTemp == InvalidReg);
      break;
    default:
      MOZ_CRASH();
  }

  Label again;

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();
  masm.computeEffectiveAddress(mem, scratch2);

  if (nbytes == 4) {
    UseScratchRegisterScope temps(masm);
    Register scratch = temps.Acquire();
    masm.memoryBarrierBefore(sync);
    masm.bind(&again);

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

    masm.as_ll(output, scratch2, 0);
    masm.ma_move(scratch, value);
    masm.as_sc(scratch, scratch2, 0);
    masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

    masm.memoryBarrierAfter(sync);

    return;
  }

  masm.as_andi(offsetTemp, scratch2, 3);
  masm.subPtr(offsetTemp, scratch2);
  if constexpr (std::endian::native != std::endian::little) {
    masm.as_xori(offsetTemp, offsetTemp, 3);
  }
  masm.as_sll(offsetTemp, offsetTemp, 3);
  masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
  masm.as_sllv(maskTemp, maskTemp, offsetTemp);
  masm.as_nor(maskTemp, zero, maskTemp);
  switch (nbytes) {
    case 1:
      masm.as_andi(valueTemp, value, 0xff);
      break;
    case 2:
      masm.as_andi(valueTemp, value, 0xffff);
      break;
  }
  masm.as_sllv(valueTemp, valueTemp, offsetTemp);

  masm.memoryBarrierBefore(sync);

  masm.bind(&again);

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

  Register scratch = temps.Acquire();
  masm.as_ll(output, scratch2, 0);
  masm.as_and(scratch, output, maskTemp);
  masm.as_or(scratch, scratch, valueTemp);

  masm.as_sc(scratch, scratch2, 0);

  masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

  masm.as_srlv(output, output, offsetTemp);

  switch (nbytes) {
    case 1:
      if (signExtend) {
        masm.ma_seb(output, output);
      } else {
        masm.as_andi(output, output, 0xff);
      }
      break;
    case 2:
      if (signExtend) {
        masm.ma_seh(output, output);
      } else {
        masm.as_andi(output, output, 0xffff);
      }
      break;
  }

  masm.memoryBarrierAfter(sync);
}

void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
                                    const Address& mem, Register value,
                                    Register valueTemp, Register offsetTemp,
                                    Register maskTemp, Register output) {
  AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp,
                 maskTemp, output);
}

void MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync,
                                    const BaseIndex& mem, Register value,
                                    Register valueTemp, Register offsetTemp,
                                    Register maskTemp, Register output) {
  AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp,
                 maskTemp, output);
}

void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
                                        const Address& mem, Register value,
                                        Register valueTemp, Register offsetTemp,
                                        Register maskTemp, Register output) {
  AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
                 valueTemp, offsetTemp, maskTemp, output);
}

void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access,
                                        const BaseIndex& mem, Register value,
                                        Register valueTemp, Register offsetTemp,
                                        Register maskTemp, Register output) {
  AtomicExchange(*this, &access, access.type(), access.sync(), mem, value,
                 valueTemp, offsetTemp, maskTemp, output);
}

template <typename T>
static void AtomicFetchOp(MacroAssembler& masm,
                          const wasm::MemoryAccessDesc* access,
                          Scalar::Type type, Synchronization sync, AtomicOp op,
                          const T& mem, Register value, Register valueTemp,
                          Register offsetTemp, Register maskTemp,
                          Register output) {
  bool signExtend = Scalar::isSignedIntType(type);
  unsigned nbytes = Scalar::byteSize(type);

  switch (nbytes) {
    case 1:
    case 2:
      break;
    case 4:
      MOZ_ASSERT(valueTemp == InvalidReg);
      MOZ_ASSERT(offsetTemp == InvalidReg);
      MOZ_ASSERT(maskTemp == InvalidReg);
      break;
    default:
      MOZ_CRASH();
  }

  Label again;

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();
  masm.computeEffectiveAddress(mem, scratch2);

  if (nbytes == 4) {
    UseScratchRegisterScope temps(masm);
    Register scratch = temps.Acquire();
    masm.memoryBarrierBefore(sync);
    masm.bind(&again);

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

    masm.as_ll(output, scratch2, 0);

    switch (op) {
      case AtomicOp::Add:
        masm.as_addu(scratch, output, value);
        break;
      case AtomicOp::Sub:
        masm.as_subu(scratch, output, value);
        break;
      case AtomicOp::And:
        masm.as_and(scratch, output, value);
        break;
      case AtomicOp::Or:
        masm.as_or(scratch, output, value);
        break;
      case AtomicOp::Xor:
        masm.as_xor(scratch, output, value);
        break;
      default:
        MOZ_CRASH();
    }

    masm.as_sc(scratch, scratch2, 0);
    masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

    masm.memoryBarrierAfter(sync);

    return;
  }

  masm.as_andi(offsetTemp, scratch2, 3);
  masm.subPtr(offsetTemp, scratch2);
  if constexpr (std::endian::native != std::endian::little) {
    masm.as_xori(offsetTemp, offsetTemp, 3);
  }
  masm.as_sll(offsetTemp, offsetTemp, 3);
  masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
  masm.as_sllv(maskTemp, maskTemp, offsetTemp);
  masm.as_nor(maskTemp, zero, maskTemp);

  masm.memoryBarrierBefore(sync);

  masm.bind(&again);

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

  Register scratch = temps.Acquire();
  masm.as_ll(scratch, scratch2, 0);
  masm.as_srlv(output, scratch, offsetTemp);

  switch (op) {
    case AtomicOp::Add:
      masm.as_addu(valueTemp, output, value);
      break;
    case AtomicOp::Sub:
      masm.as_subu(valueTemp, output, value);
      break;
    case AtomicOp::And:
      masm.as_and(valueTemp, output, value);
      break;
    case AtomicOp::Or:
      masm.as_or(valueTemp, output, value);
      break;
    case AtomicOp::Xor:
      masm.as_xor(valueTemp, output, value);
      break;
    default:
      MOZ_CRASH();
  }

  switch (nbytes) {
    case 1:
      masm.as_andi(valueTemp, valueTemp, 0xff);
      break;
    case 2:
      masm.as_andi(valueTemp, valueTemp, 0xffff);
      break;
  }

  masm.as_sllv(valueTemp, valueTemp, offsetTemp);

  masm.as_and(scratch, scratch, maskTemp);
  masm.as_or(scratch, scratch, valueTemp);

  masm.as_sc(scratch, scratch2, 0);

  masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

  switch (nbytes) {
    case 1:
      if (signExtend) {
        masm.ma_seb(output, output);
      } else {
        masm.as_andi(output, output, 0xff);
      }
      break;
    case 2:
      if (signExtend) {
        masm.ma_seh(output, output);
      } else {
        masm.as_andi(output, output, 0xffff);
      }
      break;
  }

  masm.memoryBarrierAfter(sync);
}

void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
                                   AtomicOp op, Register value,
                                   const Address& mem, Register valueTemp,
                                   Register offsetTemp, Register maskTemp,
                                   Register output) {
  AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp,
                offsetTemp, maskTemp, output);
}

void MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync,
                                   AtomicOp op, Register value,
                                   const BaseIndex& mem, Register valueTemp,
                                   Register offsetTemp, Register maskTemp,
                                   Register output) {
  AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp,
                offsetTemp, maskTemp, output);
}

void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
                                       AtomicOp op, Register value,
                                       const Address& mem, Register valueTemp,
                                       Register offsetTemp, Register maskTemp,
                                       Register output) {
  AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value,
                valueTemp, offsetTemp, maskTemp, output);
}

void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access,
                                       AtomicOp op, Register value,
                                       const BaseIndex& mem, Register valueTemp,
                                       Register offsetTemp, Register maskTemp,
                                       Register output) {
  AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value,
                valueTemp, offsetTemp, maskTemp, output);
}

template <typename T>
static void AtomicEffectOp(MacroAssembler& masm,
                           const wasm::MemoryAccessDesc* access,
                           Scalar::Type type, Synchronization sync, AtomicOp op,
                           const T& mem, Register value, Register valueTemp,
                           Register offsetTemp, Register maskTemp) {
  unsigned nbytes = Scalar::byteSize(type);

  switch (nbytes) {
    case 1:
    case 2:
      break;
    case 4:
      MOZ_ASSERT(valueTemp == InvalidReg);
      MOZ_ASSERT(offsetTemp == InvalidReg);
      MOZ_ASSERT(maskTemp == InvalidReg);
      break;
    default:
      MOZ_CRASH();
  }

  Label again;

  UseScratchRegisterScope temps(masm);
  Register scratch2 = temps.Acquire();
  masm.computeEffectiveAddress(mem, scratch2);

  if (nbytes == 4) {
    UseScratchRegisterScope temps(masm);
    Register scratch = temps.Acquire();
    masm.memoryBarrierBefore(sync);
    masm.bind(&again);

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

    masm.as_ll(scratch, scratch2, 0);

    switch (op) {
      case AtomicOp::Add:
        masm.as_addu(scratch, scratch, value);
        break;
      case AtomicOp::Sub:
        masm.as_subu(scratch, scratch, value);
        break;
      case AtomicOp::And:
        masm.as_and(scratch, scratch, value);
        break;
      case AtomicOp::Or:
        masm.as_or(scratch, scratch, value);
        break;
      case AtomicOp::Xor:
        masm.as_xor(scratch, scratch, value);
        break;
      default:
        MOZ_CRASH();
    }

    masm.as_sc(scratch, scratch2, 0);
    masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

    masm.memoryBarrierAfter(sync);

    return;
  }

  masm.as_andi(offsetTemp, scratch2, 3);
  masm.subPtr(offsetTemp, scratch2);
  if constexpr (std::endian::native != std::endian::little) {
    masm.as_xori(offsetTemp, offsetTemp, 3);
  }
  masm.as_sll(offsetTemp, offsetTemp, 3);
  masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
  masm.as_sllv(maskTemp, maskTemp, offsetTemp);
  masm.as_nor(maskTemp, zero, maskTemp);

  masm.memoryBarrierBefore(sync);

  masm.bind(&again);

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

  Register scratch = temps.Acquire();
  masm.as_ll(scratch, scratch2, 0);
  masm.as_srlv(valueTemp, scratch, offsetTemp);

  switch (op) {
    case AtomicOp::Add:
      masm.as_addu(valueTemp, valueTemp, value);
      break;
    case AtomicOp::Sub:
      masm.as_subu(valueTemp, valueTemp, value);
      break;
    case AtomicOp::And:
      masm.as_and(valueTemp, valueTemp, value);
      break;
    case AtomicOp::Or:
      masm.as_or(valueTemp, valueTemp, value);
      break;
    case AtomicOp::Xor:
      masm.as_xor(valueTemp, valueTemp, value);
      break;
    default:
      MOZ_CRASH();
  }

  switch (nbytes) {
    case 1:
      masm.as_andi(valueTemp, valueTemp, 0xff);
      break;
    case 2:
      masm.as_andi(valueTemp, valueTemp, 0xffff);
      break;
  }

  masm.as_sllv(valueTemp, valueTemp, offsetTemp);

  masm.as_and(scratch, scratch, maskTemp);
  masm.as_or(scratch, scratch, valueTemp);

  masm.as_sc(scratch, scratch2, 0);

  masm.ma_b(scratch, scratch, &again, Assembler::Zero, ShortJump);

  masm.memoryBarrierAfter(sync);
}

void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
                                        AtomicOp op, Register value,
                                        const Address& mem, Register valueTemp,
                                        Register offsetTemp,
                                        Register maskTemp) {
  AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value,
                 valueTemp, offsetTemp, maskTemp);
}

void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access,
                                        AtomicOp op, Register value,
                                        const BaseIndex& mem,
                                        Register valueTemp, Register offsetTemp,
                                        Register maskTemp) {
  AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value,
                 valueTemp, offsetTemp, maskTemp);
}

// ========================================================================
// JS atomic operations.

template <typename T>
static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
                              Synchronization sync, const T& mem,
                              Register oldval, Register newval,
                              Register valueTemp, Register offsetTemp,
                              Register maskTemp, Register temp,
                              AnyRegister output) {
  if (arrayType == Scalar::Uint32) {
    masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp,
                         offsetTemp, maskTemp, temp);
    masm.convertUInt32ToDouble(temp, output.fpu());
  } else {
    masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp,
                         offsetTemp, maskTemp, output.gpr());
  }
}

void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
                                       Synchronization sync, const Address& mem,
                                       Register oldval, Register newval,
                                       Register valueTemp, Register offsetTemp,
                                       Register maskTemp, Register temp,
                                       AnyRegister output) {
  CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp,
                    offsetTemp, maskTemp, temp, output);
}

void MacroAssembler::compareExchangeJS(Scalar::Type arrayType,
                                       Synchronization sync,
                                       const BaseIndex& mem, Register oldval,
                                       Register newval, Register valueTemp,
                                       Register offsetTemp, Register maskTemp,
                                       Register temp, AnyRegister output) {
  CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp,
                    offsetTemp, maskTemp, temp, output);
}

template <typename T>
static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType,
                             Synchronization sync, const T& mem, Register value,
                             Register valueTemp, Register offsetTemp,
                             Register maskTemp, Register temp,
                             AnyRegister output) {
  if (arrayType == Scalar::Uint32) {
    masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp,
                        maskTemp, temp);
    masm.convertUInt32ToDouble(temp, output.fpu());
  } else {
    masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp,
                        maskTemp, output.gpr());
  }
}

void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
                                      Synchronization sync, const Address& mem,
                                      Register value, Register valueTemp,
                                      Register offsetTemp, Register maskTemp,
                                      Register temp, AnyRegister output) {
  AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp,
                   maskTemp, temp, output);
}

void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType,
                                      Synchronization sync,
                                      const BaseIndex& mem, Register value,
                                      Register valueTemp, Register offsetTemp,
                                      Register maskTemp, Register temp,
                                      AnyRegister output) {
  AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp,
                   maskTemp, temp, output);
}

template <typename T>
static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType,
                            Synchronization sync, AtomicOp op, Register value,
                            const T& mem, Register valueTemp,
                            Register offsetTemp, Register maskTemp,
                            Register temp, AnyRegister output) {
  if (arrayType == Scalar::Uint32) {
    masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp,
                       maskTemp, temp);
    masm.convertUInt32ToDouble(temp, output.fpu());
  } else {
    masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp,
                       maskTemp, output.gpr());
  }
}

void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
                                     Synchronization sync, AtomicOp op,
                                     Register value, const Address& mem,
                                     Register valueTemp, Register offsetTemp,
                                     Register maskTemp, Register temp,
                                     AnyRegister output) {
  AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp,
                  maskTemp, temp, output);
}

void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType,
                                     Synchronization sync, AtomicOp op,
                                     Register value, const BaseIndex& mem,
                                     Register valueTemp, Register offsetTemp,
                                     Register maskTemp, Register temp,
                                     AnyRegister output) {
  AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp,
                  maskTemp, temp, output);
}

void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
                                      Synchronization sync, AtomicOp op,
                                      Register value, const BaseIndex& mem,
                                      Register valueTemp, Register offsetTemp,
                                      Register maskTemp) {
  AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp,
                 offsetTemp, maskTemp);
}

void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType,
                                      Synchronization sync, AtomicOp op,
                                      Register value, const Address& mem,
                                      Register valueTemp, Register offsetTemp,
                                      Register maskTemp) {
  AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp,
                 offsetTemp, maskTemp);
}

void MacroAssembler::atomicPause() { as_sync(); }

void MacroAssembler::flexibleQuotient32(Register lhs, Register rhs,
                                        Register dest, bool isUnsigned,
                                        const LiveRegisterSet&) {
  quotient32(lhs, rhs, dest, isUnsigned);
}

void MacroAssembler::flexibleRemainder32(Register lhs, Register rhs,
                                         Register dest, bool isUnsigned,
                                         const LiveRegisterSet&) {
  remainder32(lhs, rhs, dest, isUnsigned);
}

void MacroAssembler::flexibleDivMod32(Register lhs, Register rhs,
                                      Register divOutput, Register remOutput,
                                      bool isUnsigned, const LiveRegisterSet&) {
  MOZ_ASSERT(lhs != divOutput && lhs != remOutput, "lhs is preserved");
  MOZ_ASSERT(rhs != divOutput && rhs != remOutput, "rhs is preserved");

#ifdef MIPSR6
  if (isUnsigned) {
    as_divu(divOutput, lhs, rhs);
    as_modu(remOutput, rhs, rhs);
  } else {
    as_div(divOutput, lhs, rhs);
    as_mod(remOutput, lhs, rhs);
  }
#else
  if (isUnsigned) {
    as_divu(lhs, rhs);
  } else {
    as_div(lhs, rhs);
  }
  as_mfhi(remOutput);
  as_mflo(divOutput);
#endif
}

CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) {
  return movWithPatch(ImmPtr(nullptr), dest);
}

void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc,
                                          CodeLocationLabel target) {
  PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr));
}

// ========================================================================
// Spectre Mitigations.

void MacroAssembler::speculationBarrier() { MOZ_CRASH(); }

void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest,
                                         Label* fail) {
  ScratchFloat32Scope fscratch(*this);

  // Round toward negative infinity.
  as_floorls(fscratch, src);
  moveFromDouble(fscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for -0 and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If any of the two most significant bits is set, |src| is -0 or NaN.
    moveFromFloat32(src, dest);
    ma_srl(dest, dest, Imm32(30));
    branch32(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest,
                                        Label* fail) {
  ScratchDoubleScope dscratch(*this);

  // Round toward negative infinity.
  as_floorld(dscratch, src);
  moveFromDouble(dscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for -0 and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If any of the two most significant bits is set, |src| is -0 or NaN.
    moveFromDouble(src, dest);
    ma_dsrl(dest, dest, Imm32(62));
    branchPtr(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest,
                                        Label* fail) {
  ScratchFloat32Scope fscratch(*this);

  // Round toward positive infinity.
  as_ceills(fscratch, src);
  moveFromDouble(fscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for (-1, -0] and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If binary value is not zero, the input was not 0, so we bail.
    moveFromFloat32(src, dest);
    branch32(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest,
                                       Label* fail) {
  ScratchDoubleScope dscratch(*this);

  // Round toward positive infinity.
  as_ceilld(dscratch, src);
  moveFromDouble(dscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for (-1, -0] and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If binary value is not zero, the input was not 0, so we bail.
    moveFromDouble(src, dest);
    branchPtr(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest,
                                         FloatRegister temp, Label* fail) {
  ScratchFloat32Scope fscratch(*this);

  Label negative, end, performRound;

  // Branch for negative inputs. Doesn't catch NaN or -0.
  loadConstantFloat32(0.0f, fscratch);
  ma_bc1s(src, fscratch, &negative, Assembler::DoubleLessThan, ShortJump);

  // If non-negative check for bailout.
  ma_bc1s(src, fscratch, &performRound, Assembler::DoubleNotEqual, ShortJump);
  {
    // If binary value is not zero, it is NaN or -0, so we bail.
    moveFromFloat32(src, dest);
    branch32(Assembler::NotEqual, dest, zero, fail);
    ma_b(&end, ShortJump);
  }

  // Input is negative, but isn't -0.
  bind(&negative);
  {
    // Inputs in [-0.5, 0) are rounded to -0. Fail.
    loadConstantFloat32(-0.5f, fscratch);
    branchFloat(Assembler::DoubleGreaterThanOrEqual, src, fscratch, fail);
  }

  bind(&performRound);
  {
    // Load biggest number less than 0.5 in the temp register.
    loadConstantFloat32(GetBiggestNumberLessThan(0.5f), temp);

    // Other inputs need the biggest float less than 0.5 added.
    as_adds(fscratch, src, temp);

    // Round toward negative infinity.
    as_floorls(fscratch, fscratch);
    moveFromDouble(fscratch, dest);

    // Sign extend lower 32 bits to test if the result isn't an Int32.
    {
      UseScratchRegisterScope temps(*this);
      Register scratch = temps.Acquire();

      move32SignExtendToPtr(dest, scratch);
      branchPtr(Assembler::NotEqual, dest, scratch, fail);
    }
  }
  bind(&end);
}

void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest,
                                        FloatRegister temp, Label* fail) {
  ScratchDoubleScope dscratch(*this);

  Label negative, end, performRound;

  // Branch for negative inputs. Doesn't catch NaN or -0.
  loadConstantDouble(0.0, dscratch);
  ma_bc1d(src, dscratch, &negative, Assembler::DoubleLessThan, ShortJump);

  // If non-negative check for bailout.
  ma_bc1d(src, dscratch, &performRound, Assembler::DoubleNotEqual, ShortJump);
  {
    // If binary value is not zero, it is NaN or -0, so we bail.
    moveFromDouble(src, dest);
    branchPtr(Assembler::NotEqual, dest, zero, fail);
    ma_b(&end, ShortJump);
  }

  // Input is negative, but isn't -0.
  bind(&negative);
  {
    // Inputs in [-0.5, 0) are rounded to -0. Fail.
    loadConstantDouble(-0.5, dscratch);
    branchDouble(Assembler::DoubleGreaterThanOrEqual, src, dscratch, fail);
  }

  bind(&performRound);
  {
    // Load biggest number less than 0.5 in the temp register.
    loadConstantDouble(GetBiggestNumberLessThan(0.5), temp);

    // Other inputs need the biggest double less than 0.5 added.
    as_addd(dscratch, src, temp);

    // Round toward negative infinity.
    as_floorld(dscratch, dscratch);
    moveFromDouble(dscratch, dest);

    // Sign extend lower 32 bits to test if the result isn't an Int32.
    {
      UseScratchRegisterScope temps(*this);
      Register scratch = temps.Acquire();

      move32SignExtendToPtr(dest, scratch);
      branchPtr(Assembler::NotEqual, dest, scratch, fail);
    }
  }
  bind(&end);
}

void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest,
                                         Label* fail) {
  ScratchFloat32Scope fscratch(*this);

  // Round toward zero.
  as_truncls(fscratch, src);
  moveFromDouble(fscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for (-1, -0] and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If any of the two most significant bits is set, |src| is negative or NaN.
    moveFromFloat32(src, dest);
    ma_srl(dest, dest, Imm32(30));
    branch32(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest,
                                        Label* fail) {
  ScratchDoubleScope dscratch(*this);

  // Round toward zero.
  as_truncld(dscratch, src);
  moveFromDouble(dscratch, dest);

  // Sign extend lower 32 bits to test if the result isn't an Int32.
  {
    UseScratchRegisterScope temps(*this);
    Register scratch = temps.Acquire();

    move32SignExtendToPtr(dest, scratch);
    branchPtr(Assembler::NotEqual, dest, scratch, fail);
  }

  // We have to check for (-1, -0] and NaN when the result is zero.
  Label notZero;
  ma_b(dest, zero, ¬Zero, Assembler::NotEqual, ShortJump);
  {
    // If any of the two most significant bits is set, |src| is negative or NaN.
    moveFromDouble(src, dest);
    ma_dsrl(dest, dest, Imm32(62));
    branchPtr(Assembler::NotEqual, dest, zero, fail);
  }
  bind(¬Zero);
}

void MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src,
                                     FloatRegister dest) {
  MOZ_CRASH("not supported on this platform");
}

void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src,
                                      FloatRegister dest) {
  MOZ_CRASH("not supported on this platform");
}

void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs,
                                    FloatRegister output) {
  UseScratchRegisterScope temps(*this);
  Register lhsi = temps.Acquire();
  Register rhsi = temps.Acquire();

  moveFromDouble(lhs, lhsi);
  moveFromDouble(rhs, rhsi);

  // Combine.
  if (hasR2()) {
    ma_dins(rhsi, lhsi, Imm32(0), Imm32(63));
  } else {
    ma_dext(lhsi, lhsi, Imm32(0), Imm32(63));
    ma_dsrl(rhsi, rhsi, Imm32(63));
    ma_dsll(rhsi, rhsi, Imm32(63));
    as_or(rhsi, rhsi, lhsi);
  }
  moveToDouble(rhsi, output);
}

void MacroAssembler::copySignFloat32(FloatRegister lhs, FloatRegister rhs,
                                     FloatRegister output) {
  UseScratchRegisterScope temps(*this);
  Register lhsi = temps.Acquire();
  Register rhsi = temps.Acquire();

  moveFromFloat32(lhs, lhsi);
  moveFromFloat32(rhs, rhsi);

  // Combine.
  if (hasR2()) {
    ma_ins(rhsi, lhsi, 031);
  } else {
    ma_ext(lhsi, lhsi, 031);
    ma_srl(rhsi, rhsi, Imm32(31));
    ma_sll(rhsi, rhsi, Imm32(31));
    as_or(rhsi, rhsi, lhsi);
  }
  moveToFloat32(rhsi, output);
}

void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift,
                                        Register pointer) {
  if (IsShiftInScaleRange(shift)) {
    computeEffectiveAddress(
        BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer);
    return;
  }
  lshift32(Imm32(shift), indexTemp32);
  addPtr(indexTemp32, pointer);
}

//}}} check_macroassembler_style

Messung V0.5 in Prozent
C=93 H=100 G=96

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.62Angebot  ¤

*Eine klare Vorstellung vom Zielzustand






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.