/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jit/loong64/Assembler-loong64.h" #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "gc/Marking.h" #include "jit/AutoWritableJitCode.h" #include "jit/ExecutableAllocator.h" #include "vm/Realm.h" using mozilla::DebugOnly; using namespace js; using namespace js::jit; // Note this is used for inter-wasm calls and may pass arguments and results // in floating point registers even if the system ABI does not. // TODO(loong64): Inconsistent with LoongArch's calling convention. // LoongArch floating-point parameters calling convention: // The first eight floating-point parameters should be passed in f0-f7, and // the other floating point parameters will be passed like integer parameters. // But we just pass the other floating-point parameters on stack here. ABIArg ABIArgGenerator::next(MIRType type) { switch (type) { case MIRType::Int32: case MIRType::Int64: case MIRType::Pointer: case MIRType::RefOrNull: case MIRType::StackResults: { if (intRegIndex_ == NumIntArgRegs) { current_ = ABIArg(stackOffset_); stackOffset_ += sizeof(uintptr_t); break; } current_ = ABIArg(Register::FromCode(intRegIndex_ + a0.encoding())); intRegIndex_++; break; } case MIRType::Float32: case MIRType::Double: { if (floatRegIndex_ == NumFloatArgRegs) { current_ = ABIArg(stackOffset_); stackOffset_ += sizeof(double); break; } current_ = ABIArg(FloatRegister( FloatRegisters::Encoding(floatRegIndex_ + f0.encoding()), type == MIRType::Double ? FloatRegisters::Double : FloatRegisters::Single)); floatRegIndex_++; break; } case MIRType::Simd128: { MOZ_CRASH("LoongArch does not support simd yet."); break; } default: MOZ_CRASH("Unexpected argument type"); } return current_; } // Encode a standard register when it is being used as rd, the rj, and // an extra register(rk). These should never be called with an InvalidReg. uint32_t js::jit::RJ(Register r) { MOZ_ASSERT(r != InvalidReg); return r.encoding() << RJShift; } uint32_t js::jit::RK(Register r) { MOZ_ASSERT(r != InvalidReg); return r.encoding() << RKShift; } uint32_t js::jit::RD(Register r) { MOZ_ASSERT(r != InvalidReg); return r.encoding() << RDShift; } uint32_t js::jit::FJ(FloatRegister r) { return r.encoding() << RJShift; } uint32_t js::jit::FK(FloatRegister r) { return r.encoding() << RKShift; } uint32_t js::jit::FD(FloatRegister r) { return r.encoding() << RDShift; } uint32_t js::jit::FA(FloatRegister r) { return r.encoding() << FAShift; } uint32_t js::jit::SA2(uint32_t value) { MOZ_ASSERT(value < 4); return (value & SA2Mask) << SAShift; } uint32_t js::jit::SA3(uint32_t value) { MOZ_ASSERT(value < 8); return (value & SA3Mask) << SAShift; } Register js::jit::toRK(Instruction& i) { return Register::FromCode((i.encode() & RKMask) >> RKShift); } Register js::jit::toRJ(Instruction& i) { return Register::FromCode((i.encode() & RJMask) >> RJShift); } Register js::jit::toRD(Instruction& i) { return Register::FromCode((i.encode() & RDMask) >> RDShift); } Register js::jit::toR(Instruction& i) { return Register::FromCode(i.encode() & RegMask); } void InstImm::extractImm16(BOffImm16* dest) { *dest = BOffImm16(*this); } void AssemblerLOONG64::finish() { MOZ_ASSERT(!isFinished); isFinished = true; } bool AssemblerLOONG64::appendRawCode(const uint8_t* code, size_t numBytes) { return m_buffer.appendRawCode(code, numBytes); } bool AssemblerLOONG64::reserve(size_t size) { // This buffer uses fixed-size chunks so there's no point in reserving // now vs. on-demand. return !oom(); } bool AssemblerLOONG64::swapBuffer(wasm::Bytes& bytes) { // For now, specialize to the one use case. As long as wasm::Bytes is a // Vector, not a linked-list of chunks, there's not much we can do other // than copy. MOZ_ASSERT(bytes.empty()); if (!bytes.resize(bytesNeeded())) { return false; } m_buffer.executableCopy(bytes.begin()); return true; } void AssemblerLOONG64::copyJumpRelocationTable(uint8_t* dest) { if (jumpRelocations_.length()) { memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); } } void AssemblerLOONG64::copyDataRelocationTable(uint8_t* dest) { if (dataRelocations_.length()) { memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); } } AssemblerLOONG64::Condition AssemblerLOONG64::InvertCondition(Condition cond) { switch (cond) { case Equal: return NotEqual; case NotEqual: return Equal; case Zero: return NonZero; case NonZero: return Zero; case LessThan: return GreaterThanOrEqual; case LessThanOrEqual: return GreaterThan; case GreaterThan: return LessThanOrEqual; case GreaterThanOrEqual: return LessThan; case Above: return BelowOrEqual; case AboveOrEqual: return Below; case Below: return AboveOrEqual; case BelowOrEqual: return Above; case Signed: return NotSigned; case NotSigned: return Signed; default: MOZ_CRASH("unexpected condition"); } } AssemblerLOONG64::DoubleCondition AssemblerLOONG64::InvertCondition( DoubleCondition cond) { switch (cond) { case DoubleOrdered: return DoubleUnordered; case DoubleEqual: return DoubleNotEqualOrUnordered; case DoubleNotEqual: return DoubleEqualOrUnordered; case DoubleGreaterThan: return DoubleLessThanOrEqualOrUnordered; case DoubleGreaterThanOrEqual: return DoubleLessThanOrUnordered; case DoubleLessThan: return DoubleGreaterThanOrEqualOrUnordered; case DoubleLessThanOrEqual: return DoubleGreaterThanOrUnordered; case DoubleUnordered: return DoubleOrdered; case DoubleEqualOrUnordered: return DoubleNotEqual; case DoubleNotEqualOrUnordered: return DoubleEqual; case DoubleGreaterThanOrUnordered: return DoubleLessThanOrEqual; case DoubleGreaterThanOrEqualOrUnordered: return DoubleLessThan; case DoubleLessThanOrUnordered: return DoubleGreaterThanOrEqual; case DoubleLessThanOrEqualOrUnordered: return DoubleGreaterThan; default: MOZ_CRASH("unexpected condition"); } } AssemblerLOONG64::Condition AssemblerLOONG64::InvertCmpCondition( Condition cond) { switch (cond) { case Equal: case NotEqual: return cond; case LessThan: return GreaterThan; case LessThanOrEqual: return GreaterThanOrEqual; case GreaterThan: return LessThanOrEqual; case GreaterThanOrEqual: return LessThan; case Above: return Below; case AboveOrEqual: return BelowOrEqual; case Below: return Above; case BelowOrEqual: return AboveOrEqual; default: MOZ_CRASH("no meaningful swapped-operand condition"); } } BOffImm16::BOffImm16(InstImm inst) : data((inst.encode() >> Imm16Shift) & Imm16Mask) {} Instruction* BOffImm16::getDest(Instruction* src) const { return &src[(((int32_t)data << 16) >> 16) + 1]; } bool AssemblerLOONG64::oom() const { return AssemblerShared::oom() || m_buffer.oom() || jumpRelocations_.oom() || dataRelocations_.oom(); } // Size of the instruction stream, in bytes. size_t AssemblerLOONG64::size() const { return m_buffer.size(); } // Size of the relocation table, in bytes. size_t AssemblerLOONG64::jumpRelocationTableBytes() const { return jumpRelocations_.length(); } size_t AssemblerLOONG64::dataRelocationTableBytes() const { return dataRelocations_.length(); } // Size of the data table, in bytes. size_t AssemblerLOONG64::bytesNeeded() const { return size() + jumpRelocationTableBytes() + dataRelocationTableBytes(); } // write a blob of binary into the instruction stream BufferOffset AssemblerLOONG64::writeInst(uint32_t x, uint32_t* dest) { MOZ_ASSERT(hasCreator()); if (dest == nullptr) { return m_buffer.putInt(x); } WriteInstStatic(x, dest); return BufferOffset(); } void AssemblerLOONG64::WriteInstStatic(uint32_t x, uint32_t* dest) { MOZ_ASSERT(dest != nullptr); *dest = x; } BufferOffset AssemblerLOONG64::haltingAlign(int alignment) { // TODO(loong64): Implement a proper halting align. return nopAlign(alignment); } BufferOffset AssemblerLOONG64::nopAlign(int alignment) { BufferOffset ret; MOZ_ASSERT(m_buffer.isAligned(4)); if (alignment == 8) { if (!m_buffer.isAligned(alignment)) { BufferOffset tmp = as_nop(); if (!ret.assigned()) { ret = tmp; } } } else { MOZ_ASSERT((alignment & (alignment - 1)) == 0); while (size() & (alignment - 1)) { BufferOffset tmp = as_nop(); if (!ret.assigned()) { ret = tmp; } } } return ret; } // Logical operations. BufferOffset AssemblerLOONG64::as_and(Register rd, Register rj, Register rk) { spew("and %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_and, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_or(Register rd, Register rj, Register rk) { spew("or %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_or, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_xor(Register rd, Register rj, Register rk) { spew("xor %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_xor, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_nor(Register rd, Register rj, Register rk) { spew("nor %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_nor, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_andn(Register rd, Register rj, Register rk) { spew("andn %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_andn, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_orn(Register rd, Register rj, Register rk) { spew("orn %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_orn, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_andi(Register rd, Register rj, int32_t ui12) { MOZ_ASSERT(is_uintN(ui12, 12)); spew("andi %3s,%3s,0x%x", rd.name(), rj.name(), ui12); return writeInst(InstImm(op_andi, ui12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ori(Register rd, Register rj, int32_t ui12) { MOZ_ASSERT(is_uintN(ui12, 12)); spew("ori %3s,%3s,0x%x", rd.name(), rj.name(), ui12); return writeInst(InstImm(op_ori, ui12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_xori(Register rd, Register rj, int32_t ui12) { MOZ_ASSERT(is_uintN(ui12, 12)); spew("xori %3s,%3s,0x%x", rd.name(), rj.name(), ui12); return writeInst(InstImm(op_xori, ui12, rj, rd, 12).encode()); } // Branch and jump instructions BufferOffset AssemblerLOONG64::as_b(JOffImm26 off) { spew("b %d", off.decode()); return writeInst(InstJump(op_b, off).encode()); } BufferOffset AssemblerLOONG64::as_bl(JOffImm26 off) { spew("bl %d", off.decode()); return writeInst(InstJump(op_bl, off).encode()); } BufferOffset AssemblerLOONG64::as_jirl(Register rd, Register rj, BOffImm16 off) { spew("jirl %3s, %3s, %d", rd.name(), rj.name(), off.decode()); return writeInst(InstImm(op_jirl, off, rj, rd).encode()); } InstImm AssemblerLOONG64::getBranchCode(JumpOrCall jumpOrCall) { // jirl or beq if (jumpOrCall == BranchIsCall) { return InstImm(op_jirl, BOffImm16(0), zero, ra); } return InstImm(op_beq, BOffImm16(0), zero, zero); } InstImm AssemblerLOONG64::getBranchCode(Register rj, Register rd, Condition c) { // beq, bne MOZ_ASSERT(c == AssemblerLOONG64::Equal || c == AssemblerLOONG64::NotEqual); return InstImm(c == AssemblerLOONG64::Equal ? op_beq : op_bne, BOffImm16(0), rj, rd); } InstImm AssemblerLOONG64::getBranchCode(Register rj, Condition c) { // beq, bne, blt, bge switch (c) { case AssemblerLOONG64::Equal: case AssemblerLOONG64::Zero: case AssemblerLOONG64::BelowOrEqual: return InstImm(op_beq, BOffImm16(0), rj, zero); case AssemblerLOONG64::NotEqual: case AssemblerLOONG64::NonZero: case AssemblerLOONG64::Above: return InstImm(op_bne, BOffImm16(0), rj, zero); case AssemblerLOONG64::GreaterThan: return InstImm(op_blt, BOffImm16(0), zero, rj); case AssemblerLOONG64::GreaterThanOrEqual: case AssemblerLOONG64::NotSigned: return InstImm(op_bge, BOffImm16(0), rj, zero); case AssemblerLOONG64::LessThan: case AssemblerLOONG64::Signed: return InstImm(op_blt, BOffImm16(0), rj, zero); case AssemblerLOONG64::LessThanOrEqual: return InstImm(op_bge, BOffImm16(0), zero, rj); default: MOZ_CRASH("Condition not supported."); } } // Code semantics must conform to compareFloatingpoint InstImm AssemblerLOONG64::getBranchCode(FPConditionBit cj) { return InstImm(op_bcz, 0, cj, true); // bcnez } // Arithmetic instructions BufferOffset AssemblerLOONG64::as_add_w(Register rd, Register rj, Register rk) { spew("add_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_add_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_add_d(Register rd, Register rj, Register rk) { spew("add_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_add_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_sub_w(Register rd, Register rj, Register rk) { spew("sub_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sub_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_sub_d(Register rd, Register rj, Register rk) { spew("sub_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sub_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_addi_w(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("addi_w %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_addi_w, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_addi_d(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("addi_d %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_addi_d, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_addu16i_d(Register rd, Register rj, int32_t si16) { MOZ_ASSERT(Imm16::IsInSignedRange(si16)); spew("addu16i_d %3s,%3s,0x%x", rd.name(), rj.name(), si16); return writeInst(InstImm(op_addu16i_d, Imm16(si16), rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_alsl_w(Register rd, Register rj, Register rk, uint32_t sa2) { MOZ_ASSERT(sa2 < 4); spew("alsl_w %3s,%3s,0x%x", rd.name(), rj.name(), sa2); return writeInst(InstReg(op_alsl_w, sa2, rk, rj, rd, 2).encode()); } BufferOffset AssemblerLOONG64::as_alsl_wu(Register rd, Register rj, Register rk, uint32_t sa2) { MOZ_ASSERT(sa2 < 4); spew("alsl_wu %3s,%3s,0x%x", rd.name(), rj.name(), sa2); return writeInst(InstReg(op_alsl_wu, sa2, rk, rj, rd, 2).encode()); } BufferOffset AssemblerLOONG64::as_alsl_d(Register rd, Register rj, Register rk, uint32_t sa2) { MOZ_ASSERT(sa2 < 4); spew("alsl_d %3s,%3s,%3s,0x%x", rd.name(), rj.name(), rk.name(), sa2); return writeInst(InstReg(op_alsl_d, sa2, rk, rj, rd, 2).encode()); } BufferOffset AssemblerLOONG64::as_lu12i_w(Register rd, int32_t si20) { spew("lu12i_w %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_lu12i_w, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_lu32i_d(Register rd, int32_t si20) { spew("lu32i_d %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_lu32i_d, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_lu52i_d(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_uintN(si12, 12)); spew("lu52i_d %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_lu52i_d, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_slt(Register rd, Register rj, Register rk) { spew("slt %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_slt, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_sltu(Register rd, Register rj, Register rk) { spew("sltu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sltu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_slti(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("slti %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_slti, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_sltui(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("sltui %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_sltui, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_pcaddi(Register rd, int32_t si20) { spew("pcaddi %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_pcaddi, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_pcaddu12i(Register rd, int32_t si20) { spew("pcaddu12i %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_pcaddu12i, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_pcaddu18i(Register rd, int32_t si20) { spew("pcaddu18i %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_pcaddu18i, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_pcalau12i(Register rd, int32_t si20) { spew("pcalau12i %3s,0x%x", rd.name(), si20); return writeInst(InstImm(op_pcalau12i, si20, rd, false).encode()); } BufferOffset AssemblerLOONG64::as_mul_w(Register rd, Register rj, Register rk) { spew("mul_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mul_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulh_w(Register rd, Register rj, Register rk) { spew("mulh_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulh_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulh_wu(Register rd, Register rj, Register rk) { spew("mulh_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulh_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mul_d(Register rd, Register rj, Register rk) { spew("mul_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mul_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulh_d(Register rd, Register rj, Register rk) { spew("mulh_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulh_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulh_du(Register rd, Register rj, Register rk) { spew("mulh_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulh_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulw_d_w(Register rd, Register rj, Register rk) { spew("mulw_d_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulw_d_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mulw_d_wu(Register rd, Register rj, Register rk) { spew("mulw_d_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mulw_d_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_div_w(Register rd, Register rj, Register rk) { spew("div_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_div_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mod_w(Register rd, Register rj, Register rk) { spew("mod_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mod_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_div_wu(Register rd, Register rj, Register rk) { spew("div_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_div_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mod_wu(Register rd, Register rj, Register rk) { spew("mod_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mod_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_div_d(Register rd, Register rj, Register rk) { spew("div_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_div_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mod_d(Register rd, Register rj, Register rk) { spew("mod_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mod_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_div_du(Register rd, Register rj, Register rk) { spew("div_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_div_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_mod_du(Register rd, Register rj, Register rk) { spew("mod_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_mod_du, rk, rj, rd).encode()); } // Shift instructions BufferOffset AssemblerLOONG64::as_sll_w(Register rd, Register rj, Register rk) { spew("sll_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sll_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_srl_w(Register rd, Register rj, Register rk) { spew("srl_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_srl_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_sra_w(Register rd, Register rj, Register rk) { spew("sra_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sra_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_rotr_w(Register rd, Register rj, Register rk) { spew("rotr_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_rotr_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_slli_w(Register rd, Register rj, int32_t ui5) { MOZ_ASSERT(is_uintN(ui5, 5)); spew("slli_w %3s,%3s,0x%x", rd.name(), rj.name(), ui5); return writeInst(InstImm(op_slli_w, ui5, rj, rd, 5).encode()); } BufferOffset AssemblerLOONG64::as_srli_w(Register rd, Register rj, int32_t ui5) { MOZ_ASSERT(is_uintN(ui5, 5)); spew("srli_w %3s,%3s,0x%x", rd.name(), rj.name(), ui5); return writeInst(InstImm(op_srli_w, ui5, rj, rd, 5).encode()); } BufferOffset AssemblerLOONG64::as_srai_w(Register rd, Register rj, int32_t ui5) { MOZ_ASSERT(is_uintN(ui5, 5)); spew("srai_w %3s,%3s,0x%x", rd.name(), rj.name(), ui5); return writeInst(InstImm(op_srai_w, ui5, rj, rd, 5).encode()); } BufferOffset AssemblerLOONG64::as_rotri_w(Register rd, Register rj, int32_t ui5) { MOZ_ASSERT(is_uintN(ui5, 5)); spew("rotri_w %3s,%3s,0x%x", rd.name(), rj.name(), ui5); return writeInst(InstImm(op_rotri_w, ui5, rj, rd, 5).encode()); } BufferOffset AssemblerLOONG64::as_sll_d(Register rd, Register rj, Register rk) { spew("sll_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sll_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_srl_d(Register rd, Register rj, Register rk) { spew("srl_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_srl_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_sra_d(Register rd, Register rj, Register rk) { spew("sra_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_sra_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_rotr_d(Register rd, Register rj, Register rk) { spew("rotr_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_rotr_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_slli_d(Register rd, Register rj, int32_t ui6) { MOZ_ASSERT(is_uintN(ui6, 6)); spew("slli_d %3s,%3s,0x%x", rd.name(), rj.name(), ui6); return writeInst(InstImm(op_slli_d, ui6, rj, rd, 6).encode()); } BufferOffset AssemblerLOONG64::as_srli_d(Register rd, Register rj, int32_t ui6) { MOZ_ASSERT(is_uintN(ui6, 6)); spew("srli_d %3s,%3s,0x%x", rd.name(), rj.name(), ui6); return writeInst(InstImm(op_srli_d, ui6, rj, rd, 6).encode()); } BufferOffset AssemblerLOONG64::as_srai_d(Register rd, Register rj, int32_t ui6) { MOZ_ASSERT(is_uintN(ui6, 6)); spew("srai_d %3s,%3s,0x%x", rd.name(), rj.name(), ui6); return writeInst(InstImm(op_srai_d, ui6, rj, rd, 6).encode()); } BufferOffset AssemblerLOONG64::as_rotri_d(Register rd, Register rj, int32_t ui6) { MOZ_ASSERT(is_uintN(ui6, 6)); spew("rotri_d %3s,%3s,0x%x", rd.name(), rj.name(), ui6); return writeInst(InstImm(op_rotri_d, ui6, rj, rd, 6).encode()); } // Bit operation instrucitons BufferOffset AssemblerLOONG64::as_ext_w_b(Register rd, Register rj) { spew("ext_w_b %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_ext_w_b, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ext_w_h(Register rd, Register rj) { spew("ext_w_h %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_ext_w_h, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_clo_w(Register rd, Register rj) { spew("clo_w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_clo_w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_clz_w(Register rd, Register rj) { spew("clz_w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_clz_w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_cto_w(Register rd, Register rj) { spew("cto_w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_cto_w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ctz_w(Register rd, Register rj) { spew("ctz_w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_ctz_w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_clo_d(Register rd, Register rj) { spew("clo_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_clo_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_clz_d(Register rd, Register rj) { spew("clz_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_clz_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_cto_d(Register rd, Register rj) { spew("cto_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_cto_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ctz_d(Register rd, Register rj) { spew("ctz_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_ctz_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bytepick_w(Register rd, Register rj, Register rk, int32_t sa2) { MOZ_ASSERT(sa2 < 4); spew("bytepick_w %3s,%3s,%3s, 0x%x", rd.name(), rj.name(), rk.name(), sa2); return writeInst(InstReg(op_bytepick_w, sa2, rk, rj, rd, 2).encode()); } BufferOffset AssemblerLOONG64::as_bytepick_d(Register rd, Register rj, Register rk, int32_t sa3) { MOZ_ASSERT(sa3 < 8); spew("bytepick_d %3s,%3s,%3s, 0x%x", rd.name(), rj.name(), rk.name(), sa3); return writeInst(InstReg(op_bytepick_d, sa3, rk, rj, rd, 3).encode()); } BufferOffset AssemblerLOONG64::as_revb_2h(Register rd, Register rj) { spew("revb_2h %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revb_2h, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_revb_4h(Register rd, Register rj) { spew("revb_4h %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revb_4h, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_revb_2w(Register rd, Register rj) { spew("revb_2w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revb_2w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_revb_d(Register rd, Register rj) { spew("revb_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revb_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_revh_2w(Register rd, Register rj) { spew("revh_2w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revh_2w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_revh_d(Register rd, Register rj) { spew("revh_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_revh_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bitrev_4b(Register rd, Register rj) { spew("bitrev_4b %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_bitrev_4b, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bitrev_8b(Register rd, Register rj) { spew("bitrev_8b %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_bitrev_8b, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bitrev_w(Register rd, Register rj) { spew("bitrev_w %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_bitrev_w, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bitrev_d(Register rd, Register rj) { spew("bitrev_d %3s,%3s", rd.name(), rj.name()); return writeInst(InstReg(op_bitrev_d, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bstrins_w(Register rd, Register rj, int32_t msbw, int32_t lsbw) { MOZ_ASSERT(lsbw <= msbw); spew("bstrins_w %3s,%3s,0x%x,0x%x", rd.name(), rj.name(), msbw, lsbw); return writeInst(InstImm(op_bstr_w, msbw, lsbw, rj, rd, 5).encode()); } BufferOffset AssemblerLOONG64::as_bstrins_d(Register rd, Register rj, int32_t msbd, int32_t lsbd) { MOZ_ASSERT(lsbd <= msbd); spew("bstrins_d %3s,%3s,0x%x,0x%x", rd.name(), rj.name(), msbd, lsbd); return writeInst(InstImm(op_bstrins_d, msbd, lsbd, rj, rd, 6).encode()); } BufferOffset AssemblerLOONG64::as_bstrpick_w(Register rd, Register rj, int32_t msbw, int32_t lsbw) { MOZ_ASSERT(lsbw <= msbw); spew("bstrpick_w %3s,%3s,0x%x,0x%x", rd.name(), rj.name(), msbw, lsbw); return writeInst(InstImm(op_bstr_w, msbw, lsbw, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_bstrpick_d(Register rd, Register rj, int32_t msbd, int32_t lsbd) { MOZ_ASSERT(lsbd <= msbd); spew("bstrpick_d %3s,%3s,0x%x,0x%x", rd.name(), rj.name(), msbd, lsbd); return writeInst(InstImm(op_bstrpick_d, msbd, lsbd, rj, rd, 6).encode()); } BufferOffset AssemblerLOONG64::as_maskeqz(Register rd, Register rj, Register rk) { spew("maskeqz %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_maskeqz, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_masknez(Register rd, Register rj, Register rk) { spew("masknez %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_masknez, rk, rj, rd).encode()); } // Load and store instructions BufferOffset AssemblerLOONG64::as_ld_b(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_b %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_b, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_h(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_h %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_h, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_w(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_w %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_w, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_d(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_d %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_d, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_bu(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_bu %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_bu, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_hu(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_hu %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_hu, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ld_wu(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("ld_wu %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_ld_wu, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_st_b(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("st_b %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_st_b, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_st_h(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("st_h %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_st_h, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_st_w(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("st_w %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_st_w, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_st_d(Register rd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("st_d %3s,%3s,0x%x", rd.name(), rj.name(), si12); return writeInst(InstImm(op_st_d, si12, rj, rd, 12).encode()); } BufferOffset AssemblerLOONG64::as_ldx_b(Register rd, Register rj, Register rk) { spew("ldx_b %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_b, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_h(Register rd, Register rj, Register rk) { spew("ldx_h %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_h, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_w(Register rd, Register rj, Register rk) { spew("ldx_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_d(Register rd, Register rj, Register rk) { spew("ldx_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_bu(Register rd, Register rj, Register rk) { spew("ldx_bu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_bu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_hu(Register rd, Register rj, Register rk) { spew("ldx_hu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_hu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldx_wu(Register rd, Register rj, Register rk) { spew("ldx_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ldx_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_stx_b(Register rd, Register rj, Register rk) { spew("stx_b %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_stx_b, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_stx_h(Register rd, Register rj, Register rk) { spew("stx_h %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_stx_h, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_stx_w(Register rd, Register rj, Register rk) { spew("stx_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_stx_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_stx_d(Register rd, Register rj, Register rk) { spew("stx_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_stx_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ldptr_w(Register rd, Register rj, int32_t si14) { MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); spew("ldptr_w %3s,%3s,0x%x", rd.name(), rj.name(), si14); return writeInst(InstImm(op_ldptr_w, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_ldptr_d(Register rd, Register rj, int32_t si14) { MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); spew("ldptr_d %3s,%3s,0x%x", rd.name(), rj.name(), si14); return writeInst(InstImm(op_ldptr_d, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_stptr_w(Register rd, Register rj, int32_t si14) { MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); spew("stptr_w %3s,%3s,0x%x", rd.name(), rj.name(), si14); return writeInst(InstImm(op_stptr_w, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_stptr_d(Register rd, Register rj, int32_t si14) { MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); spew("stptr_d %3s,%3s,0x%x", rd.name(), rj.name(), si14); return writeInst(InstImm(op_stptr_d, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_preld(int32_t hint, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("preld 0x%x,%3s,0x%x", hint, rj.name(), si12); return writeInst(InstImm(op_preld, si12, rj, hint).encode()); } // Atomic instructions BufferOffset AssemblerLOONG64::as_amswap_w(Register rd, Register rj, Register rk) { spew("amswap_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amswap_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amswap_d(Register rd, Register rj, Register rk) { spew("amswap_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amswap_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amadd_w(Register rd, Register rj, Register rk) { spew("amadd_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amadd_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amadd_d(Register rd, Register rj, Register rk) { spew("amadd_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amadd_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amand_w(Register rd, Register rj, Register rk) { spew("amand_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amand_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amand_d(Register rd, Register rj, Register rk) { spew("amand_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amand_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amor_w(Register rd, Register rj, Register rk) { spew("amor_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amor_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amor_d(Register rd, Register rj, Register rk) { spew("amor_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amor_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amxor_w(Register rd, Register rj, Register rk) { spew("amxor_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amxor_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amxor_d(Register rd, Register rj, Register rk) { spew("amxor_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amxor_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_w(Register rd, Register rj, Register rk) { spew("ammax_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_d(Register rd, Register rj, Register rk) { spew("ammax_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_w(Register rd, Register rj, Register rk) { spew("ammin_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_d(Register rd, Register rj, Register rk) { spew("ammin_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_wu(Register rd, Register rj, Register rk) { spew("ammax_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_du(Register rd, Register rj, Register rk) { spew("ammax_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_wu(Register rd, Register rj, Register rk) { spew("ammin_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_du(Register rd, Register rj, Register rk) { spew("ammin_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amswap_db_w(Register rd, Register rj, Register rk) { spew("amswap_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amswap_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amswap_db_d(Register rd, Register rj, Register rk) { spew("amswap_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amswap_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amadd_db_w(Register rd, Register rj, Register rk) { spew("amadd_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amadd_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amadd_db_d(Register rd, Register rj, Register rk) { spew("amadd_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amadd_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amand_db_w(Register rd, Register rj, Register rk) { spew("amand_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amand_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amand_db_d(Register rd, Register rj, Register rk) { spew("amand_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amand_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amor_db_w(Register rd, Register rj, Register rk) { spew("amor_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amor_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amor_db_d(Register rd, Register rj, Register rk) { spew("amor_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amor_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amxor_db_w(Register rd, Register rj, Register rk) { spew("amxor_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amxor_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_amxor_db_d(Register rd, Register rj, Register rk) { spew("amxor_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_amxor_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_db_w(Register rd, Register rj, Register rk) { spew("ammax_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_db_d(Register rd, Register rj, Register rk) { spew("ammax_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_db_w(Register rd, Register rj, Register rk) { spew("ammin_db_w %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_db_w, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_db_d(Register rd, Register rj, Register rk) { spew("ammin_db_d %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_db_d, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_db_wu(Register rd, Register rj, Register rk) { spew("ammax_db_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_db_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammax_db_du(Register rd, Register rj, Register rk) { spew("ammax_db_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammax_db_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_db_wu(Register rd, Register rj, Register rk) { spew("ammin_db_wu %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_db_wu, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ammin_db_du(Register rd, Register rj, Register rk) { spew("ammin_db_du %3s,%3s,%3s", rd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_ammin_db_du, rk, rj, rd).encode()); } BufferOffset AssemblerLOONG64::as_ll_w(Register rd, Register rj, int32_t si14) { spew("ll_w %3s,%3s,0x%x", rd.name(), rj.name(), si14); MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); return writeInst(InstImm(op_ll_w, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_ll_d(Register rd, Register rj, int32_t si14) { spew("ll_d %3s,%3s,0x%x", rd.name(), rj.name(), si14); MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); return writeInst(InstImm(op_ll_d, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_sc_w(Register rd, Register rj, int32_t si14) { spew("sc_w %3s,%3s,0x%x", rd.name(), rj.name(), si14); MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); return writeInst(InstImm(op_sc_w, si14 >> 2, rj, rd, 14).encode()); } BufferOffset AssemblerLOONG64::as_sc_d(Register rd, Register rj, int32_t si14) { spew("sc_d %3s,%3s,0x%x", rd.name(), rj.name(), si14); MOZ_ASSERT(is_intN(si14, 16) && ((si14 & 0x3) == 0)); return writeInst(InstImm(op_sc_d, si14 >> 2, rj, rd, 14).encode()); } // Barrier instructions BufferOffset AssemblerLOONG64::as_dbar(int32_t hint) { MOZ_ASSERT(is_uintN(hint, 15)); spew("dbar 0x%x", hint); return writeInst(InstImm(op_dbar, hint).encode()); } BufferOffset AssemblerLOONG64::as_ibar(int32_t hint) { MOZ_ASSERT(is_uintN(hint, 15)); spew("ibar 0x%x", hint); return writeInst(InstImm(op_ibar, hint).encode()); } /* =============================================================== */ // FP Arithmetic instructions BufferOffset AssemblerLOONG64::as_fadd_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fadd_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fadd_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fadd_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fadd_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fadd_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fsub_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fsub_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fsub_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fsub_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fsub_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fsub_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmul_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmul_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmul_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmul_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmul_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmul_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fdiv_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fdiv_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fdiv_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fdiv_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fdiv_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fdiv_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmadd_s(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fmadd_s %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fmadd_s, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmadd_d(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fmadd_d %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fmadd_d, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmsub_s(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fmsub_s %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fmsub_s, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmsub_d(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fmsub_d %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fmsub_d, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fnmadd_s(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fnmadd_s %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fnmadd_s, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fnmadd_d(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fnmadd_d %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fnmadd_d, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fnmsub_s(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fnmsub_s %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fnmsub_s, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fnmsub_d(FloatRegister fd, FloatRegister fj, FloatRegister fk, FloatRegister fa) { spew("fnmsub_d %3s,%3s,%3s,%3s", fd.name(), fj.name(), fk.name(), fa.name()); return writeInst(InstReg(op_fnmsub_d, fa, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmax_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmax_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmax_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmax_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmax_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmax_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmin_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmin_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmin_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmin_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmin_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmin_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmaxa_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmaxa_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmaxa_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmaxa_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmaxa_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmaxa_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmina_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmina_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmina_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmina_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fmina_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fmina_d, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fabs_s(FloatRegister fd, FloatRegister fj) { spew("fabs_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fabs_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fabs_d(FloatRegister fd, FloatRegister fj) { spew("fabs_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fabs_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fneg_s(FloatRegister fd, FloatRegister fj) { spew("fneg_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fneg_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fneg_d(FloatRegister fd, FloatRegister fj) { spew("fneg_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fneg_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fsqrt_s(FloatRegister fd, FloatRegister fj) { spew("fsqrt_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fsqrt_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fsqrt_d(FloatRegister fd, FloatRegister fj) { spew("fsqrt_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fsqrt_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fcopysign_s(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fcopysign_s %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fcopysign_s, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fcopysign_d(FloatRegister fd, FloatRegister fj, FloatRegister fk) { spew("fcopysign_d %3s,%3s,%3s", fd.name(), fj.name(), fk.name()); return writeInst(InstReg(op_fcopysign_d, fk, fj, fd).encode()); } // FP compare instructions // fcmp.cond.s and fcmp.cond.d instructions BufferOffset AssemblerLOONG64::as_fcmp_cor(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cor_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, COR, fk, fj, cd).encode()); } else { spew("fcmp_cor_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, COR, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_ceq(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_ceq_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CEQ, fk, fj, cd).encode()); } else { spew("fcmp_ceq_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CEQ, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cne(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cne_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CNE, fk, fj, cd).encode()); } else { spew("fcmp_cne_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CNE, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cle(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cle_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CLE, fk, fj, cd).encode()); } else { spew("fcmp_cle_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CLE, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_clt(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_clt_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CLT, fk, fj, cd).encode()); } else { spew("fcmp_clt_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CLT, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cun(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cun_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CUN, fk, fj, cd).encode()); } else { spew("fcmp_cun_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CUN, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cueq(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cueq_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CUEQ, fk, fj, cd).encode()); } else { spew("fcmp_cueq_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CUEQ, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cune(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cune_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CUNE, fk, fj, cd).encode()); } else { spew("fcmp_cune_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CUNE, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cule(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cule_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CULE, fk, fj, cd).encode()); } else { spew("fcmp_cule_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CULE, fk, fj, cd).encode()); } } BufferOffset AssemblerLOONG64::as_fcmp_cult(FloatFormat fmt, FloatRegister fj, FloatRegister fk, FPConditionBit cd) { if (fmt == DoubleFloat) { spew("fcmp_cult_d FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_d, CULT, fk, fj, cd).encode()); } else { spew("fcmp_cult_s FCC%d,%3s,%3s", cd, fj.name(), fk.name()); return writeInst(InstReg(op_fcmp_cond_s, CULT, fk, fj, cd).encode()); } } // FP conversion instructions BufferOffset AssemblerLOONG64::as_fcvt_s_d(FloatRegister fd, FloatRegister fj) { spew("fcvt_s_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fcvt_s_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fcvt_d_s(FloatRegister fd, FloatRegister fj) { spew("fcvt_d_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fcvt_d_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ffint_s_w(FloatRegister fd, FloatRegister fj) { spew("ffint_s_w %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ffint_s_w, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ffint_s_l(FloatRegister fd, FloatRegister fj) { spew("ffint_s_l %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ffint_s_l, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ffint_d_w(FloatRegister fd, FloatRegister fj) { spew("ffint_d_w %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ffint_d_w, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ffint_d_l(FloatRegister fd, FloatRegister fj) { spew("ffint_d_l %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ffint_d_l, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftint_w_s(FloatRegister fd, FloatRegister fj) { spew("ftint_w_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftint_w_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftint_w_d(FloatRegister fd, FloatRegister fj) { spew("ftint_w_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftint_w_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftint_l_s(FloatRegister fd, FloatRegister fj) { spew("ftint_l_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftint_l_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftint_l_d(FloatRegister fd, FloatRegister fj) { spew("ftint_l_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftint_l_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrm_w_s(FloatRegister fd, FloatRegister fj) { spew("ftintrm_w_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrm_w_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrm_w_d(FloatRegister fd, FloatRegister fj) { spew("ftintrm_w_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrm_w_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrm_l_s(FloatRegister fd, FloatRegister fj) { spew("ftintrm_l_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrm_l_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrm_l_d(FloatRegister fd, FloatRegister fj) { spew("ftintrm_l_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrm_l_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrp_w_s(FloatRegister fd, FloatRegister fj) { spew("ftintrp_w_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrp_w_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrp_w_d(FloatRegister fd, FloatRegister fj) { spew("ftintrp_w_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrp_w_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrp_l_s(FloatRegister fd, FloatRegister fj) { spew("ftintrp_l_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrp_l_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrp_l_d(FloatRegister fd, FloatRegister fj) { spew("ftintrp_l_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrp_l_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrz_w_s(FloatRegister fd, FloatRegister fj) { spew("ftintrz_w_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrz_w_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrz_w_d(FloatRegister fd, FloatRegister fj) { spew("ftintrz_w_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrz_w_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrz_l_s(FloatRegister fd, FloatRegister fj) { spew("ftintrz_l_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrz_l_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrz_l_d(FloatRegister fd, FloatRegister fj) { spew("ftintrz_l_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrz_l_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrne_w_s(FloatRegister fd, FloatRegister fj) { spew("ftintrne_w_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrne_w_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrne_w_d(FloatRegister fd, FloatRegister fj) { spew("ftintrne_w_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrne_w_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrne_l_s(FloatRegister fd, FloatRegister fj) { spew("ftintrne_l_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrne_l_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_ftintrne_l_d(FloatRegister fd, FloatRegister fj) { spew("ftintrne_l_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_ftintrne_l_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_frint_s(FloatRegister fd, FloatRegister fj) { spew("frint_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_frint_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_frint_d(FloatRegister fd, FloatRegister fj) { spew("frint_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_frint_d, fj, fd).encode()); } // FP mov instructions BufferOffset AssemblerLOONG64::as_fmov_s(FloatRegister fd, FloatRegister fj) { spew("fmov_s %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fmov_s, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fmov_d(FloatRegister fd, FloatRegister fj) { spew("fmov_d %3s,%3s", fd.name(), fj.name()); return writeInst(InstReg(op_fmov_d, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fsel(FloatRegister fd, FloatRegister fj, FloatRegister fk, FPConditionBit ca) { spew("fsel %3s,%3s,%3s,%d", fd.name(), fj.name(), fk.name(), ca); return writeInst(InstReg(op_fsel, ca, fk, fj, fd).encode()); } BufferOffset AssemblerLOONG64::as_movgr2fr_w(FloatRegister fd, Register rj) { spew("movgr2fr_w %3s,%3s", fd.name(), rj.name()); return writeInst(InstReg(op_movgr2fr_w, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_movgr2fr_d(FloatRegister fd, Register rj) { spew("movgr2fr_d %3s,%3s", fd.name(), rj.name()); return writeInst(InstReg(op_movgr2fr_d, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_movgr2frh_w(FloatRegister fd, Register rj) { spew("movgr2frh_w %3s,%3s", fd.name(), rj.name()); return writeInst(InstReg(op_movgr2frh_w, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_movfr2gr_s(Register rd, FloatRegister fj) { spew("movfr2gr_s %3s,%3s", rd.name(), fj.name()); return writeInst(InstReg(op_movfr2gr_s, fj, rd).encode()); } BufferOffset AssemblerLOONG64::as_movfr2gr_d(Register rd, FloatRegister fj) { spew("movfr2gr_d %3s,%3s", rd.name(), fj.name()); return writeInst(InstReg(op_movfr2gr_d, fj, rd).encode()); } BufferOffset AssemblerLOONG64::as_movfrh2gr_s(Register rd, FloatRegister fj) { spew("movfrh2gr_s %3s,%3s", rd.name(), fj.name()); return writeInst(InstReg(op_movfrh2gr_s, fj, rd).encode()); } BufferOffset AssemblerLOONG64::as_movgr2fcsr(Register rj) { spew("movgr2fcsr %3s", rj.name()); return writeInst(InstReg(op_movgr2fcsr, rj, FCSR).encode()); } BufferOffset AssemblerLOONG64::as_movfcsr2gr(Register rd) { spew("movfcsr2gr %3s", rd.name()); return writeInst(InstReg(op_movfcsr2gr, FCSR, rd).encode()); } BufferOffset AssemblerLOONG64::as_movfr2cf(FPConditionBit cd, FloatRegister fj) { spew("movfr2cf %d,%3s", cd, fj.name()); return writeInst(InstReg(op_movfr2cf, fj, cd).encode()); } BufferOffset AssemblerLOONG64::as_movcf2fr(FloatRegister fd, FPConditionBit cj) { spew("movcf2fr %3s,%d", fd.name(), cj); return writeInst(InstReg(op_movcf2fr, cj, fd).encode()); } BufferOffset AssemblerLOONG64::as_movgr2cf(FPConditionBit cd, Register rj) { spew("movgr2cf %d,%3s", cd, rj.name()); return writeInst(InstReg(op_movgr2cf, rj, cd).encode()); } BufferOffset AssemblerLOONG64::as_movcf2gr(Register rd, FPConditionBit cj) { spew("movcf2gr %3s,%d", rd.name(), cj); return writeInst(InstReg(op_movcf2gr, cj, rd).encode()); } // FP load/store instructions BufferOffset AssemblerLOONG64::as_fld_s(FloatRegister fd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("fld_s %3s,%3s,0x%x", fd.name(), rj.name(), si12); return writeInst(InstImm(op_fld_s, si12, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fld_d(FloatRegister fd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("fld_d %3s,%3s,0x%x", fd.name(), rj.name(), si12); return writeInst(InstImm(op_fld_d, si12, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fst_s(FloatRegister fd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("fst_s %3s,%3s,0x%x", fd.name(), rj.name(), si12); return writeInst(InstImm(op_fst_s, si12, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fst_d(FloatRegister fd, Register rj, int32_t si12) { MOZ_ASSERT(is_intN(si12, 12)); spew("fst_d %3s,%3s,0x%x", fd.name(), rj.name(), si12); return writeInst(InstImm(op_fst_d, si12, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fldx_s(FloatRegister fd, Register rj, Register rk) { spew("fldx_s %3s,%3s,%3s", fd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_fldx_s, rk, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fldx_d(FloatRegister fd, Register rj, Register rk) { spew("fldx_d %3s,%3s,%3s", fd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_fldx_d, rk, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fstx_s(FloatRegister fd, Register rj, Register rk) { spew("fstx_s %3s,%3s,%3s", fd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_fstx_s, rk, rj, fd).encode()); } BufferOffset AssemblerLOONG64::as_fstx_d(FloatRegister fd, Register rj, Register rk) { spew("fstx_d %3s,%3s,%3s", fd.name(), rj.name(), rk.name()); return writeInst(InstReg(op_fstx_d, rk, rj, fd).encode()); } /* ========================================================================= */ void AssemblerLOONG64::bind(Label* label, BufferOffset boff) { spew(".set Llabel %p", label); // If our caller didn't give us an explicit target to bind to // then we want to bind to the location of the next instruction BufferOffset dest = boff.assigned() ? boff : nextOffset(); if (label->used()) { int32_t next; // A used label holds a link to branch that uses it. BufferOffset b(label); do { // Even a 0 offset may be invalid if we're out of memory. if (oom()) { return; } Instruction* inst = editSrc(b); // Second word holds a pointer to the next branch in label's chain. next = inst[1].encode(); bind(reinterpret_cast(inst), b.getOffset(), dest.getOffset()); b = BufferOffset(next); } while (next != LabelBase::INVALID_OFFSET); } label->bind(dest.getOffset()); } void AssemblerLOONG64::retarget(Label* label, Label* target) { spew("retarget %p -> %p", label, target); if (label->used() && !oom()) { if (target->bound()) { bind(label, BufferOffset(target)); } else if (target->used()) { // The target is not bound but used. Prepend label's branch list // onto target's. int32_t next; BufferOffset labelBranchOffset(label); // Find the head of the use chain for label. do { Instruction* inst = editSrc(labelBranchOffset); // Second word holds a pointer to the next branch in chain. next = inst[1].encode(); labelBranchOffset = BufferOffset(next); } while (next != LabelBase::INVALID_OFFSET); // Then patch the head of label's use chain to the tail of // target's use chain, prepending the entire use chain of target. Instruction* inst = editSrc(labelBranchOffset); int32_t prev = target->offset(); target->use(label->offset()); inst[1].setData(prev); } else { // The target is unbound and unused. We can just take the head of // the list hanging off of label, and dump that into target. target->use(label->offset()); } } label->reset(); } void dbg_break() {} void AssemblerLOONG64::as_break(uint32_t code) { MOZ_ASSERT(code <= MAX_BREAK_CODE); spew("break %d", code); writeInst(InstImm(op_break, code).encode()); } // This just stomps over memory with 32 bits of raw data. Its purpose is to // overwrite the call of JITed code with 32 bits worth of an offset. This will // is only meant to function on code that has been invalidated, so it should // be totally safe. Since that instruction will never be executed again, a // ICache flush should not be necessary void AssemblerLOONG64::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) { // Raw is going to be the return address. uint32_t* raw = (uint32_t*)label.raw(); // Overwrite the 4 bytes before the return address, which will // end up being the call instruction. *(raw - 1) = imm.value; } uint8_t* AssemblerLOONG64::NextInstruction(uint8_t* inst_, uint32_t* count) { Instruction* inst = reinterpret_cast(inst_); if (count != nullptr) { *count += sizeof(Instruction); } return reinterpret_cast(inst->next()); } void AssemblerLOONG64::ToggleToJmp(CodeLocationLabel inst_) { InstImm* inst = (InstImm*)inst_.raw(); MOZ_ASSERT(inst->extractBitField(31, 26) == (uint32_t)op_addu16i_d >> 26); // We converted beq to addu16i_d, so now we restore it. inst->setOpcode(op_beq, 6); } void AssemblerLOONG64::ToggleToCmp(CodeLocationLabel inst_) { InstImm* inst = (InstImm*)inst_.raw(); // toggledJump is allways used for short jumps. MOZ_ASSERT(inst->extractBitField(31, 26) == (uint32_t)op_beq >> 26); // Replace "beq $zero, $zero, offset" with "addu16i_d $zero, $zero, offset" inst->setOpcode(op_addu16i_d, 6); } // Since there are no pools in LoongArch64 implementation, this should be // simple. Instruction* Instruction::next() { return this + 1; } InstImm AssemblerLOONG64::invertBranch(InstImm branch, BOffImm16 skipOffset) { uint32_t rj = 0; OpcodeField opcode = (OpcodeField)((branch.extractBitField(31, 26)) << 26); switch (opcode) { case op_beq: branch.setBOffImm16(skipOffset); branch.setOpcode(op_bne, 6); return branch; case op_bne: branch.setBOffImm16(skipOffset); branch.setOpcode(op_beq, 6); return branch; case op_bge: branch.setBOffImm16(skipOffset); branch.setOpcode(op_blt, 6); return branch; case op_bgeu: branch.setBOffImm16(skipOffset); branch.setOpcode(op_bltu, 6); return branch; case op_blt: branch.setBOffImm16(skipOffset); branch.setOpcode(op_bge, 6); return branch; case op_bltu: branch.setBOffImm16(skipOffset); branch.setOpcode(op_bgeu, 6); return branch; case op_beqz: branch.setBOffImm16(skipOffset); branch.setOpcode(op_bnez, 6); return branch; case op_bnez: branch.setBOffImm16(skipOffset); branch.setOpcode(op_beqz, 6); return branch; case op_bcz: branch.setBOffImm16(skipOffset); rj = branch.extractRJ(); if (rj & 0x8) { branch.setRJ(rj & 0x17); } else { branch.setRJ(rj | 0x8); } return branch; default: MOZ_CRASH("Error creating long branch."); } } #ifdef JS_JITSPEW void AssemblerLOONG64::decodeBranchInstAndSpew(InstImm branch) { OpcodeField opcode = (OpcodeField)((branch.extractBitField(31, 26)) << 26); uint32_t rd_id; uint32_t rj_id; uint32_t cj_id; uint32_t immi = branch.extractImm16Value(); switch (opcode) { case op_beq: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("beq 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_bne: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("bne 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_bge: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("bge 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_bgeu: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("bgeu 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_blt: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("blt 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_bltu: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("bltu 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; case op_beqz: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("beqz 0x%x,%3s,0x%x", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), rd_id); break; case op_bnez: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("bnez 0x%x,%3s,0x%x", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), rd_id); break; case op_bcz: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); cj_id = branch.extractBitField(CJShift + CJBits - 1, CJShift); if (rj_id & 0x8) { spew("bcnez 0x%x,FCC%d,0x%x", (int32_t(immi << 18) >> 16) + 4, cj_id, rd_id); } else { spew("bceqz 0x%x,FCC%d,0x%x", (int32_t(immi << 18) >> 16) + 4, cj_id, rd_id); } break; case op_jirl: rd_id = branch.extractRD(); rj_id = branch.extractRJ(); spew("beqz 0x%x,%3s,%3s", (int32_t(immi << 18) >> 16) + 4, Registers::GetName(rj_id), Registers::GetName(rd_id)); break; default: MOZ_CRASH("Error disassemble branch."); } } #endif void Assembler::executableCopy(uint8_t* buffer) { MOZ_ASSERT(isFinished); m_buffer.executableCopy(buffer); } uintptr_t Assembler::GetPointer(uint8_t* instPtr) { Instruction* inst = (Instruction*)instPtr; return Assembler::ExtractLoad64Value(inst); } static JitCode* CodeFromJump(Instruction* jump) { uint8_t* target = (uint8_t*)Assembler::ExtractLoad64Value(jump); return JitCode::FromExecutable(target); } void Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { while (reader.more()) { JitCode* child = CodeFromJump((Instruction*)(code->raw() + reader.readUnsigned())); TraceManuallyBarrieredEdge(trc, &child, "rel32"); } } static void TraceOneDataRelocation(JSTracer* trc, mozilla::Maybe& awjc, JitCode* code, Instruction* inst) { void* ptr = (void*)Assembler::ExtractLoad64Value(inst); void* prior = ptr; // Data relocations can be for Values or for raw pointers. If a Value is // zero-tagged, we can trace it as if it were a raw pointer. If a Value // is not zero-tagged, we have to interpret it as a Value to ensure that the // tag bits are masked off to recover the actual pointer. uintptr_t word = reinterpret_cast(ptr); if (word >> JSVAL_TAG_SHIFT) { // This relocation is a Value with a non-zero tag. Value v = Value::fromRawBits(word); TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value"); ptr = (void*)v.bitsAsPunboxPointer(); } else { // This relocation is a raw pointer or a Value with a zero tag. // No barrier needed since these are constants. TraceManuallyBarrieredGenericPointerEdge( trc, reinterpret_cast(&ptr), "jit-masm-ptr"); } if (ptr != prior) { if (awjc.isNothing()) { awjc.emplace(code); } Assembler::UpdateLoad64Value(inst, uint64_t(ptr)); } } /* static */ void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { mozilla::Maybe awjc; while (reader.more()) { size_t offset = reader.readUnsigned(); Instruction* inst = (Instruction*)(code->raw() + offset); TraceOneDataRelocation(trc, awjc, code, inst); } } void Assembler::Bind(uint8_t* rawCode, const CodeLabel& label) { if (label.patchAt().bound()) { auto mode = label.linkMode(); intptr_t offset = label.patchAt().offset(); intptr_t target = label.target().offset(); if (mode == CodeLabel::RawPointer) { *reinterpret_cast(rawCode + offset) = rawCode + target; } else { MOZ_ASSERT(mode == CodeLabel::MoveImmediate || mode == CodeLabel::JumpImmediate); Instruction* inst = (Instruction*)(rawCode + offset); Assembler::UpdateLoad64Value(inst, (uint64_t)(rawCode + target)); } } } void Assembler::bind(InstImm* inst, uintptr_t branch, uintptr_t target) { int64_t offset = target - branch; InstImm inst_jirl = InstImm(op_jirl, BOffImm16(0), zero, ra); InstImm inst_beq = InstImm(op_beq, BOffImm16(0), zero, zero); // If encoded offset is 4, then the jump must be short if (BOffImm16(inst[0]).decode() == 4) { MOZ_ASSERT(BOffImm16::IsInRange(offset)); inst[0].setBOffImm16(BOffImm16(offset)); inst[1].makeNop(); // because before set INVALID_OFFSET return; } // Generate the long jump for calls because return address has to be the // address after the reserved block. if (inst[0].encode() == inst_jirl.encode()) { addLongJump(BufferOffset(branch), BufferOffset(target)); Assembler::WriteLoad64Instructions(inst, ScratchRegister, LabelBase::INVALID_OFFSET); inst[3].makeNop(); // There are 1 nop. inst[4] = InstImm(op_jirl, BOffImm16(0), ScratchRegister, ra); return; } if (BOffImm16::IsInRange(offset)) { // Skip trailing nops . bool skipNops = (inst[0].encode() != inst_jirl.encode() && inst[0].encode() != inst_beq.encode()); inst[0].setBOffImm16(BOffImm16(offset)); inst[1].makeNop(); if (skipNops) { inst[2] = InstImm(op_bge, BOffImm16(3 * sizeof(uint32_t)), zero, zero); // There are 2 nops after this } return; } if (inst[0].encode() == inst_beq.encode()) { // Handle long unconditional jump. Only four 4 instruction. addLongJump(BufferOffset(branch), BufferOffset(target)); Assembler::WriteLoad64Instructions(inst, ScratchRegister, LabelBase::INVALID_OFFSET); inst[3] = InstImm(op_jirl, BOffImm16(0), ScratchRegister, zero); } else { // Handle long conditional jump. inst[0] = invertBranch(inst[0], BOffImm16(5 * sizeof(uint32_t))); // No need for a "nop" here because we can clobber scratch. addLongJump(BufferOffset(branch + sizeof(uint32_t)), BufferOffset(target)); Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, LabelBase::INVALID_OFFSET); inst[4] = InstImm(op_jirl, BOffImm16(0), ScratchRegister, zero); } } void Assembler::processCodeLabels(uint8_t* rawCode) { for (const CodeLabel& label : codeLabels_) { Bind(rawCode, label); } } uint32_t Assembler::PatchWrite_NearCallSize() { // Load an address needs 3 instructions, and a jump. return (3 + 1) * sizeof(uint32_t); } void Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) { Instruction* inst = (Instruction*)start.raw(); uint8_t* dest = toCall.raw(); // Overwrite whatever instruction used to be here with a call. // Always use long jump for two reasons: // - Jump has to be the same size because of PatchWrite_NearCallSize. // - Return address has to be at the end of replaced block. // Short jump wouldn't be more efficient. Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)dest); inst[3] = InstImm(op_jirl, BOffImm16(0), ScratchRegister, ra); } uint64_t Assembler::ExtractLoad64Value(Instruction* inst0) { InstImm* i0 = (InstImm*)inst0; InstImm* i1 = (InstImm*)i0->next(); InstImm* i2 = (InstImm*)i1->next(); InstImm* i3 = (InstImm*)i2->next(); MOZ_ASSERT((i0->extractBitField(31, 25)) == ((uint32_t)op_lu12i_w >> 25)); MOZ_ASSERT((i1->extractBitField(31, 22)) == ((uint32_t)op_ori >> 22)); MOZ_ASSERT((i2->extractBitField(31, 25)) == ((uint32_t)op_lu32i_d >> 25)); if ((i3->extractBitField(31, 22)) == ((uint32_t)op_lu52i_d >> 22)) { // Li64 uint64_t value = (uint64_t(i0->extractBitField(Imm20Bits + Imm20Shift - 1, Imm20Shift)) << 12) | (uint64_t( i1->extractBitField(Imm12Bits + Imm12Shift - 1, Imm12Shift))) | (uint64_t(i2->extractBitField(Imm20Bits + Imm20Shift - 1, Imm20Shift)) << 32) | (uint64_t(i3->extractBitField(Imm12Bits + Imm12Shift - 1, Imm12Shift)) << 52); return value; } else { // Li48 uint64_t value = (uint64_t(i0->extractBitField(Imm20Bits + Imm20Shift - 1, Imm20Shift)) << 12) | (uint64_t( i1->extractBitField(Imm12Bits + Imm12Shift - 1, Imm12Shift))) | (uint64_t(i2->extractBitField(Imm20Bits + Imm20Shift - 1, Imm20Shift)) << 32); return uint64_t((int64_t(value) << 16) >> 16); } } void Assembler::UpdateLoad64Value(Instruction* inst0, uint64_t value) { // Todo: with ma_liPatchable InstImm* i0 = (InstImm*)inst0; InstImm* i1 = (InstImm*)i0->next(); InstImm* i2 = (InstImm*)i1->next(); InstImm* i3 = (InstImm*)i2->next(); MOZ_ASSERT((i0->extractBitField(31, 25)) == ((uint32_t)op_lu12i_w >> 25)); MOZ_ASSERT((i1->extractBitField(31, 22)) == ((uint32_t)op_ori >> 22)); MOZ_ASSERT((i2->extractBitField(31, 25)) == ((uint32_t)op_lu32i_d >> 25)); if ((i3->extractBitField(31, 22)) == ((uint32_t)op_lu52i_d >> 22)) { // Li64 *i0 = InstImm(op_lu12i_w, (int32_t)((value >> 12) & 0xfffff), Register::FromCode(i0->extractRD()), false); *i1 = InstImm(op_ori, (int32_t)(value & 0xfff), Register::FromCode(i1->extractRJ()), Register::FromCode(i1->extractRD()), 12); *i2 = InstImm(op_lu32i_d, (int32_t)((value >> 32) & 0xfffff), Register::FromCode(i2->extractRD()), false); *i3 = InstImm(op_lu52i_d, (int32_t)((value >> 52) & 0xfff), Register::FromCode(i3->extractRJ()), Register::FromCode(i3->extractRD()), 12); } else { // Li48 *i0 = InstImm(op_lu12i_w, (int32_t)((value >> 12) & 0xfffff), Register::FromCode(i0->extractRD()), false); *i1 = InstImm(op_ori, (int32_t)(value & 0xfff), Register::FromCode(i1->extractRJ()), Register::FromCode(i1->extractRD()), 12); *i2 = InstImm(op_lu32i_d, (int32_t)((value >> 32) & 0xfffff), Register::FromCode(i2->extractRD()), false); } } void Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value) { Instruction* inst1 = inst0->next(); Instruction* inst2 = inst1->next(); *inst0 = InstImm(op_lu12i_w, (int32_t)((value >> 12) & 0xfffff), reg, false); *inst1 = InstImm(op_ori, (int32_t)(value & 0xfff), reg, reg, 12); *inst2 = InstImm(op_lu32i_d, (int32_t)((value >> 32) & 0xfffff), reg, false); } void Assembler::PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) { PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value), PatchedImmPtr(expectedValue.value)); } void Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, PatchedImmPtr expectedValue) { Instruction* inst = (Instruction*)label.raw(); // Extract old Value DebugOnly value = Assembler::ExtractLoad64Value(inst); MOZ_ASSERT(value == uint64_t(expectedValue.value)); // Replace with new value Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value)); } uint64_t Assembler::ExtractInstructionImmediate(uint8_t* code) { InstImm* inst = (InstImm*)code; return Assembler::ExtractLoad64Value(inst); } void Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) { Instruction* inst = (Instruction*)inst_.raw(); InstImm* i0 = (InstImm*)inst; InstImm* i1 = (InstImm*)i0->next(); InstImm* i2 = (InstImm*)i1->next(); Instruction* i3 = (Instruction*)i2->next(); MOZ_ASSERT((i0->extractBitField(31, 25)) == ((uint32_t)op_lu12i_w >> 25)); MOZ_ASSERT((i1->extractBitField(31, 22)) == ((uint32_t)op_ori >> 22)); MOZ_ASSERT((i2->extractBitField(31, 25)) == ((uint32_t)op_lu32i_d >> 25)); if (enabled) { MOZ_ASSERT((i3->extractBitField(31, 25)) != ((uint32_t)op_lu12i_w >> 25)); InstImm jirl = InstImm(op_jirl, BOffImm16(0), ScratchRegister, ra); *i3 = jirl; } else { InstNOP nop; *i3 = nop; } }