blob: 897c9d71b2a12bdd2276700d63c9826d22a81a8f [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/globals.h" // NOLINT
#if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
#define SHOULD_NOT_INCLUDE_RUNTIME
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/locations.h"
#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/simulator.h"
namespace dart {
DECLARE_FLAG(bool, check_code_pointer);
DECLARE_FLAG(bool, precompiled_mode);
DEFINE_FLAG(int, far_branch_level, 0, "Always use far branches");
namespace compiler {
MicroAssembler::MicroAssembler(ObjectPoolBuilder* object_pool_builder,
intptr_t far_branch_level,
ExtensionSet extensions)
: AssemblerBase(object_pool_builder),
extensions_(extensions),
far_branch_level_(far_branch_level) {
ASSERT(far_branch_level >= 0);
ASSERT(far_branch_level <= 2);
}
MicroAssembler::~MicroAssembler() {}
void MicroAssembler::Bind(Label* label) {
ASSERT(!label->IsBound());
intptr_t target_position = Position();
intptr_t branch_position;
#define BIND(head, update) \
branch_position = label->head; \
while (branch_position >= 0) { \
ASSERT(Utils::IsAligned(branch_position, Supports(RV_C) ? 2 : 4)); \
intptr_t new_offset = target_position - branch_position; \
ASSERT(Utils::IsAligned(new_offset, Supports(RV_C) ? 2 : 4)); \
intptr_t old_offset = update(branch_position, new_offset); \
if (old_offset == 0) break; \
branch_position -= old_offset; \
} \
label->head = -1
BIND(unresolved_cb_, UpdateCBOffset);
BIND(unresolved_cj_, UpdateCJOffset);
BIND(unresolved_b_, UpdateBOffset);
BIND(unresolved_j_, UpdateJOffset);
BIND(unresolved_far_, UpdateFarOffset);
label->BindTo(target_position);
}
intptr_t MicroAssembler::UpdateCBOffset(intptr_t branch_position,
intptr_t new_offset) {
CInstr instr(Read16(branch_position));
ASSERT((instr.opcode() == C_BEQZ) || (instr.opcode() == C_BNEZ));
intptr_t old_offset = instr.b_imm();
if (!IsCBImm(new_offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
Write16(branch_position,
instr.opcode() | EncodeCRs1p(instr.rs1p()) | EncodeCBImm(new_offset));
return old_offset;
}
intptr_t MicroAssembler::UpdateCJOffset(intptr_t branch_position,
intptr_t new_offset) {
CInstr instr(Read16(branch_position));
ASSERT((instr.opcode() == C_J) || (instr.opcode() == C_JAL));
intptr_t old_offset = instr.j_imm();
if (!IsCJImm(new_offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
Write16(branch_position, instr.opcode() | EncodeCJImm(new_offset));
return old_offset;
}
intptr_t MicroAssembler::UpdateBOffset(intptr_t branch_position,
intptr_t new_offset) {
Instr instr(Read32(branch_position));
ASSERT(instr.opcode() == BRANCH);
intptr_t old_offset = instr.btype_imm();
if (!IsBTypeImm(new_offset)) {
BailoutWithBranchOffsetError();
}
Write32(branch_position, EncodeRs2(instr.rs2()) | EncodeRs1(instr.rs1()) |
EncodeFunct3(instr.funct3()) |
EncodeOpcode(instr.opcode()) |
EncodeBTypeImm(new_offset));
return old_offset;
}
intptr_t MicroAssembler::UpdateJOffset(intptr_t branch_position,
intptr_t new_offset) {
Instr instr(Read32(branch_position));
ASSERT(instr.opcode() == JAL);
intptr_t old_offset = instr.jtype_imm();
if (!IsJTypeImm(new_offset)) {
BailoutWithBranchOffsetError();
}
Write32(branch_position, EncodeRd(instr.rd()) | EncodeOpcode(instr.opcode()) |
EncodeJTypeImm(new_offset));
return old_offset;
}
intptr_t MicroAssembler::UpdateFarOffset(intptr_t branch_position,
intptr_t new_offset) {
Instr auipc_instr(Read32(branch_position));
ASSERT(auipc_instr.opcode() == AUIPC);
ASSERT(auipc_instr.rd() == FAR_TMP);
Instr jr_instr(Read32(branch_position + 4));
ASSERT(jr_instr.opcode() == JALR);
ASSERT(jr_instr.rd() == ZR);
ASSERT(jr_instr.funct3() == F3_0);
ASSERT(jr_instr.rs1() == FAR_TMP);
intptr_t old_offset = auipc_instr.utype_imm() + jr_instr.itype_imm();
intx_t lo = new_offset << (XLEN - 12) >> (XLEN - 12);
intx_t hi = (new_offset - lo) << (XLEN - 32) >> (XLEN - 32);
if (!IsUTypeImm(hi)) {
FATAL("Jump/branch distance exceeds 2GB!");
}
Write32(branch_position,
EncodeUTypeImm(hi) | EncodeRd(FAR_TMP) | EncodeOpcode(AUIPC));
Write32(branch_position + 4, EncodeITypeImm(lo) | EncodeRs1(FAR_TMP) |
EncodeFunct3(F3_0) | EncodeRd(ZR) |
EncodeOpcode(JALR));
return old_offset;
}
void MicroAssembler::lui(Register rd, intptr_t imm) {
ASSERT(Supports(RV_I));
if (Supports(RV_C) && (rd != ZR) && (rd != SP) && IsCUImm(imm)) {
c_lui(rd, imm);
return;
}
EmitUType(imm, rd, LUI);
}
void MicroAssembler::lui_fixed(Register rd, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitUType(imm, rd, LUI);
}
void MicroAssembler::auipc(Register rd, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitUType(imm, rd, AUIPC);
}
void MicroAssembler::jal(Register rd, Label* label, JumpDistance distance) {
ASSERT(Supports(RV_I));
if (Supports(RV_C) &&
((distance == kNearJump) ||
(label->IsBound() && IsCJImm(label->Position() - Position())))) {
if (rd == ZR) {
c_j(label);
return;
}
#if XLEN == 32
if (rd == RA) {
c_jal(label);
return;
}
#endif // XLEN == 32
}
EmitJump(rd, label, JAL, distance);
}
void MicroAssembler::jalr(Register rd, Register rs1, intptr_t offset) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if (rs1 != ZR && offset == 0) {
if (rd == ZR) {
c_jr(rs1);
return;
} else if (rd == RA) {
c_jalr(rs1);
return;
}
}
}
EmitIType(offset, rs1, F3_0, rd, JALR);
}
void MicroAssembler::jalr_fixed(Register rd, Register rs1, intptr_t offset) {
ASSERT(Supports(RV_I));
EmitIType(offset, rs1, F3_0, rd, JALR);
}
void MicroAssembler::beq(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
ASSERT(Supports(RV_I));
if (Supports(RV_C) &&
((distance == kNearJump) ||
(label->IsBound() && IsCBImm(label->Position() - Position())))) {
if ((rs1 == ZR) && IsCRs1p(rs2)) {
c_beqz(rs2, label);
return;
} else if ((rs2 == ZR) && IsCRs1p(rs1)) {
c_beqz(rs1, label);
return;
}
}
EmitBranch(rs1, rs2, label, BEQ, distance);
}
void MicroAssembler::bne(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
ASSERT(Supports(RV_I));
if (Supports(RV_C) &&
((distance == kNearJump) ||
(label->IsBound() && IsCBImm(label->Position() - Position())))) {
if ((rs1 == ZR) && IsCRs1p(rs2)) {
c_bnez(rs2, label);
return;
} else if ((rs2 == ZR) && IsCRs1p(rs1)) {
c_bnez(rs1, label);
return;
}
}
EmitBranch(rs1, rs2, label, BNE, distance);
}
void MicroAssembler::blt(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
ASSERT(Supports(RV_I));
EmitBranch(rs1, rs2, label, BLT, distance);
}
void MicroAssembler::bge(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
ASSERT(Supports(RV_I));
EmitBranch(rs1, rs2, label, BGE, distance);
}
void MicroAssembler::bltu(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
ASSERT(Supports(RV_I));
EmitBranch(rs1, rs2, label, BLTU, distance);
}
void MicroAssembler::bgeu(Register rs1,
Register rs2,
Label* label,
JumpDistance distance) {
EmitBranch(rs1, rs2, label, BGEU, distance);
}
void MicroAssembler::lb(Register rd, Address addr) {
ASSERT(Supports(RV_I));
EmitIType(addr.offset(), addr.base(), LB, rd, LOAD);
}
void MicroAssembler::lh(Register rd, Address addr) {
ASSERT(Supports(RV_I));
EmitIType(addr.offset(), addr.base(), LH, rd, LOAD);
}
void MicroAssembler::lw(Register rd, Address addr) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd != ZR) && (addr.base() == SP) && IsCSPLoad4Imm(addr.offset())) {
c_lwsp(rd, addr);
return;
}
if (IsCRdp(rd) && IsCRs1p(addr.base()) && IsCMem4Imm(addr.offset())) {
c_lw(rd, addr);
return;
}
}
EmitIType(addr.offset(), addr.base(), LW, rd, LOAD);
}
void MicroAssembler::lbu(Register rd, Address addr) {
ASSERT(Supports(RV_I));
EmitIType(addr.offset(), addr.base(), LBU, rd, LOAD);
}
void MicroAssembler::lhu(Register rd, Address addr) {
ASSERT(Supports(RV_I));
EmitIType(addr.offset(), addr.base(), LHU, rd, LOAD);
}
void MicroAssembler::sb(Register rs2, Address addr) {
ASSERT(Supports(RV_I));
EmitSType(addr.offset(), rs2, addr.base(), SB, STORE);
}
void MicroAssembler::sh(Register rs2, Address addr) {
ASSERT(Supports(RV_I));
EmitSType(addr.offset(), rs2, addr.base(), SH, STORE);
}
void MicroAssembler::sw(Register rs2, Address addr) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPStore4Imm(addr.offset())) {
c_swsp(rs2, addr);
return;
}
if (IsCRs2p(rs2) && IsCRs1p(addr.base()) && IsCMem4Imm(addr.offset())) {
c_sw(rs2, addr);
return;
}
}
EmitSType(addr.offset(), rs2, addr.base(), SW, STORE);
}
void MicroAssembler::addi(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd != ZR) && (rs1 == ZR) && IsCIImm(imm)) {
c_li(rd, imm);
return;
}
if ((rd == rs1) && IsCIImm(imm) && (imm != 0)) {
c_addi(rd, rs1, imm);
return;
}
if ((rd == SP) && (rs1 == SP) && IsCI16Imm(imm) && (imm != 0)) {
c_addi16sp(rd, rs1, imm);
return;
}
if (IsCRdp(rd) && (rs1 == SP) && IsCI4SPNImm(imm) && (imm != 0)) {
c_addi4spn(rd, rs1, imm);
return;
}
if (imm == 0) {
if ((rd == ZR) && (rs1 == ZR)) {
c_nop();
return;
}
if ((rd != ZR) && (rs1 != ZR)) {
c_mv(rd, rs1);
return;
}
}
}
EmitIType(imm, rs1, ADDI, rd, OPIMM);
}
void MicroAssembler::slti(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitIType(imm, rs1, SLTI, rd, OPIMM);
}
void MicroAssembler::sltiu(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitIType(imm, rs1, SLTIU, rd, OPIMM);
}
void MicroAssembler::xori(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitIType(imm, rs1, XORI, rd, OPIMM);
}
void MicroAssembler::ori(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
EmitIType(imm, rs1, ORI, rd, OPIMM);
}
void MicroAssembler::andi(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCIImm(imm)) {
c_andi(rd, rs1, imm);
return;
}
}
EmitIType(imm, rs1, ANDI, rd, OPIMM);
}
void MicroAssembler::slli(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < XLEN));
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && (shamt != 0) && IsCIImm(shamt)) {
c_slli(rd, rs1, shamt);
return;
}
}
EmitRType(F7_0, shamt, rs1, SLLI, rd, OPIMM);
}
void MicroAssembler::srli(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < XLEN));
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && (shamt != 0) && IsCIImm(shamt)) {
c_srli(rd, rs1, shamt);
return;
}
}
EmitRType(F7_0, shamt, rs1, SRI, rd, OPIMM);
}
void MicroAssembler::srai(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < XLEN));
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && (shamt != 0) && IsCIImm(shamt)) {
c_srai(rd, rs1, shamt);
return;
}
}
EmitRType(SRA, shamt, rs1, SRI, rd, OPIMM);
}
void MicroAssembler::add(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if (rd == rs1) {
c_add(rd, rs1, rs2);
return;
}
if (rd == rs2) {
c_add(rd, rs2, rs1);
return;
}
}
EmitRType(F7_0, rs2, rs1, ADD, rd, OP);
}
void MicroAssembler::sub(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_sub(rd, rs1, rs2);
return;
}
}
EmitRType(SUB, rs2, rs1, ADD, rd, OP);
}
void MicroAssembler::sll(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SLL, rd, OP);
}
void MicroAssembler::slt(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SLT, rd, OP);
}
void MicroAssembler::sltu(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SLTU, rd, OP);
}
void MicroAssembler::xor_(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_xor(rd, rs1, rs2);
return;
}
if ((rd == rs2) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_xor(rd, rs2, rs1);
return;
}
}
EmitRType(F7_0, rs2, rs1, XOR, rd, OP);
}
void MicroAssembler::srl(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SR, rd, OP);
}
void MicroAssembler::sra(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(SRA, rs2, rs1, SR, rd, OP);
}
void MicroAssembler::or_(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_or(rd, rs1, rs2);
return;
}
if ((rd == rs2) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_or(rd, rs2, rs1);
return;
}
}
EmitRType(F7_0, rs2, rs1, OR, rd, OP);
}
void MicroAssembler::and_(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_and(rd, rs1, rs2);
return;
}
if ((rd == rs2) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_and(rd, rs2, rs1);
return;
}
}
EmitRType(F7_0, rs2, rs1, AND, rd, OP);
}
void MicroAssembler::fence(HartEffects predecessor, HartEffects successor) {
ASSERT((predecessor & kAll) == predecessor);
ASSERT((successor & kAll) == successor);
ASSERT(Supports(RV_I));
EmitIType((predecessor << 4) | successor, ZR, FENCE, ZR, MISCMEM);
}
void MicroAssembler::fencei() {
ASSERT(Supports(RV_I));
EmitIType(0, ZR, FENCEI, ZR, MISCMEM);
}
void MicroAssembler::ecall() {
ASSERT(Supports(RV_I));
EmitIType(ECALL, ZR, F3_0, ZR, SYSTEM);
}
void MicroAssembler::ebreak() {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
c_ebreak();
return;
}
EmitIType(EBREAK, ZR, F3_0, ZR, SYSTEM);
}
void MicroAssembler::SimulatorPrintObject(Register rs1) {
ASSERT(Supports(RV_I));
EmitIType(ECALL, rs1, F3_0, ZR, SYSTEM);
}
void MicroAssembler::csrrw(Register rd, uint32_t csr, Register rs1) {
ASSERT(Supports(RV_I));
EmitIType(csr, rs1, CSRRW, rd, SYSTEM);
}
void MicroAssembler::csrrs(Register rd, uint32_t csr, Register rs1) {
ASSERT(Supports(RV_I));
EmitIType(csr, rs1, CSRRS, rd, SYSTEM);
}
void MicroAssembler::csrrc(Register rd, uint32_t csr, Register rs1) {
ASSERT(Supports(RV_I));
EmitIType(csr, rs1, CSRRC, rd, SYSTEM);
}
void MicroAssembler::csrrwi(Register rd, uint32_t csr, uint32_t imm) {
ASSERT(Supports(RV_I));
EmitIType(csr, Register(imm), CSRRWI, rd, SYSTEM);
}
void MicroAssembler::csrrsi(Register rd, uint32_t csr, uint32_t imm) {
ASSERT(Supports(RV_I));
EmitIType(csr, Register(imm), CSRRSI, rd, SYSTEM);
}
void MicroAssembler::csrrci(Register rd, uint32_t csr, uint32_t imm) {
ASSERT(Supports(RV_I));
EmitIType(csr, Register(imm), CSRRCI, rd, SYSTEM);
}
void MicroAssembler::trap() {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
Emit16(0); // Permanently reserved illegal instruction.
} else {
Emit32(0); // Permanently reserved illegal instruction.
}
}
#if XLEN >= 64
void MicroAssembler::lwu(Register rd, Address addr) {
ASSERT(Supports(RV_I));
EmitIType(addr.offset(), addr.base(), LWU, rd, LOAD);
}
void MicroAssembler::ld(Register rd, Address addr) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd != ZR) && (addr.base() == SP) && IsCSPLoad8Imm(addr.offset())) {
c_ldsp(rd, addr);
return;
}
if (IsCRdp(rd) && IsCRs1p(addr.base()) && IsCMem8Imm(addr.offset())) {
c_ld(rd, addr);
return;
}
}
EmitIType(addr.offset(), addr.base(), LD, rd, LOAD);
}
void MicroAssembler::sd(Register rs2, Address addr) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPStore8Imm(addr.offset())) {
c_sdsp(rs2, addr);
return;
}
if (IsCRs2p(rs2) && IsCRs1p(addr.base()) && IsCMem8Imm(addr.offset())) {
c_sd(rs2, addr);
return;
}
}
EmitSType(addr.offset(), rs2, addr.base(), SD, STORE);
}
void MicroAssembler::addiw(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd != ZR) && (rs1 == ZR) && IsCIImm(imm)) {
c_li(rd, imm);
return;
}
if ((rd == rs1) && (rd != ZR) && IsCIImm(imm)) {
c_addiw(rd, rs1, imm);
return;
}
}
EmitIType(imm, rs1, ADDI, rd, OPIMM32);
}
void MicroAssembler::slliw(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < 32));
ASSERT(Supports(RV_I));
EmitRType(F7_0, shamt, rs1, SLLI, rd, OPIMM32);
}
void MicroAssembler::srliw(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < 32));
ASSERT(Supports(RV_I));
EmitRType(F7_0, shamt, rs1, SRI, rd, OPIMM32);
}
void MicroAssembler::sraiw(Register rd, Register rs1, intptr_t shamt) {
ASSERT((shamt > 0) && (shamt < XLEN));
ASSERT(Supports(RV_I));
EmitRType(SRA, shamt, rs1, SRI, rd, OPIMM32);
}
void MicroAssembler::addw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_addw(rd, rs1, rs2);
return;
}
if ((rd == rs2) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_addw(rd, rs2, rs1);
return;
}
}
EmitRType(F7_0, rs2, rs1, ADD, rd, OP32);
}
void MicroAssembler::subw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
if (Supports(RV_C)) {
if ((rd == rs1) && IsCRs1p(rs1) && IsCRs2p(rs2)) {
c_subw(rd, rs1, rs2);
return;
}
}
EmitRType(SUB, rs2, rs1, ADD, rd, OP32);
}
void MicroAssembler::sllw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SLL, rd, OP32);
}
void MicroAssembler::srlw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(F7_0, rs2, rs1, SR, rd, OP32);
}
void MicroAssembler::sraw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_I));
EmitRType(SRA, rs2, rs1, SR, rd, OP32);
}
#endif // XLEN >= 64
void MicroAssembler::mul(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, MUL, rd, OP);
}
void MicroAssembler::mulh(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, MULH, rd, OP);
}
void MicroAssembler::mulhsu(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, MULHSU, rd, OP);
}
void MicroAssembler::mulhu(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, MULHU, rd, OP);
}
void MicroAssembler::div(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, DIV, rd, OP);
}
void MicroAssembler::divu(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, DIVU, rd, OP);
}
void MicroAssembler::rem(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, REM, rd, OP);
}
void MicroAssembler::remu(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, REMU, rd, OP);
}
#if XLEN >= 64
void MicroAssembler::mulw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, MULW, rd, OP32);
}
void MicroAssembler::divw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, DIVW, rd, OP32);
}
void MicroAssembler::divuw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, DIVUW, rd, OP32);
}
void MicroAssembler::remw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, REMW, rd, OP32);
}
void MicroAssembler::remuw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_M));
EmitRType(MULDIV, rs2, rs1, REMUW, rd, OP32);
}
#endif // XLEN >= 64
void MicroAssembler::lrw(Register rd, Address addr, std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(LR, order, ZR, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::scw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(SC, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amoswapw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOSWAP, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amoaddw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOADD, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amoxorw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOXOR, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amoandw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOAND, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amoorw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOOR, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amominw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMIN, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amomaxw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMAX, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amominuw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMINU, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
void MicroAssembler::amomaxuw(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMAXU, order, rs2, addr.base(), WIDTH32, rd, AMO);
}
#if XLEN >= 64
void MicroAssembler::lrd(Register rd, Address addr, std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(LR, order, ZR, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::scd(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(SC, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amoswapd(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOSWAP, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amoaddd(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOADD, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amoxord(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOXOR, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amoandd(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOAND, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amoord(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOOR, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amomind(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMIN, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amomaxd(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMAX, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amominud(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMINU, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
void MicroAssembler::amomaxud(Register rd,
Register rs2,
Address addr,
std::memory_order order) {
ASSERT(addr.offset() == 0);
ASSERT(Supports(RV_A));
EmitRType(AMOMAXU, order, rs2, addr.base(), WIDTH64, rd, AMO);
}
#endif // XLEN >= 64
void MicroAssembler::flw(FRegister rd, Address addr) {
ASSERT(Supports(RV_F));
#if XLEN == 32
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPLoad4Imm(addr.offset())) {
c_flwsp(rd, addr);
return;
}
if (IsCFRdp(rd) && IsCRs1p(addr.base()) && IsCMem4Imm(addr.offset())) {
c_flw(rd, addr);
return;
}
}
#endif // XLEN == 32
EmitIType(addr.offset(), addr.base(), S, rd, LOADFP);
}
void MicroAssembler::fsw(FRegister rs2, Address addr) {
ASSERT(Supports(RV_F));
#if XLEN == 32
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPStore4Imm(addr.offset())) {
c_fswsp(rs2, addr);
return;
}
if (IsCFRs2p(rs2) && IsCRs1p(addr.base()) && IsCMem4Imm(addr.offset())) {
c_fsw(rs2, addr);
return;
}
}
#endif // XLEN == 32
EmitSType(addr.offset(), rs2, addr.base(), S, STOREFP);
}
void MicroAssembler::fmadds(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitR4Type(rs3, F2_S, rs2, rs1, rounding, rd, FMADD);
}
void MicroAssembler::fmsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitR4Type(rs3, F2_S, rs2, rs1, rounding, rd, FMSUB);
}
void MicroAssembler::fnmsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitR4Type(rs3, F2_S, rs2, rs1, rounding, rd, FNMSUB);
}
void MicroAssembler::fnmadds(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitR4Type(rs3, F2_S, rs2, rs1, rounding, rd, FNMADD);
}
void MicroAssembler::fadds(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FADDS, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FSUBS, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fmuls(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FMULS, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fdivs(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FDIVS, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsqrts(FRegister rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FSQRTS, FRegister(0), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsgnjs(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FSGNJS, rs2, rs1, J, rd, OPFP);
}
void MicroAssembler::fsgnjns(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FSGNJS, rs2, rs1, JN, rd, OPFP);
}
void MicroAssembler::fsgnjxs(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FSGNJS, rs2, rs1, JX, rd, OPFP);
}
void MicroAssembler::fmins(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FMINMAXS, rs2, rs1, MIN, rd, OPFP);
}
void MicroAssembler::fmaxs(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FMINMAXS, rs2, rs1, MAX, rd, OPFP);
}
void MicroAssembler::feqs(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FCMPS, rs2, rs1, FEQ, rd, OPFP);
}
void MicroAssembler::flts(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FCMPS, rs2, rs1, FLT, rd, OPFP);
}
void MicroAssembler::fles(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_F));
EmitRType(FCMPS, rs2, rs1, FLE, rd, OPFP);
}
void MicroAssembler::fclasss(Register rd, FRegister rs1) {
ASSERT(Supports(RV_F));
EmitRType(FCLASSS, FRegister(0), rs1, F3_1, rd, OPFP);
}
void MicroAssembler::fcvtws(Register rd, FRegister rs1, RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTintS, FRegister(W), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtwus(Register rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTintS, FRegister(WU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtsw(FRegister rd, Register rs1, RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTSint, FRegister(W), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtswu(FRegister rd,
Register rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTSint, FRegister(WU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fmvxw(Register rd, FRegister rs1) {
ASSERT(Supports(RV_F));
EmitRType(FMVXW, FRegister(0), rs1, F3_0, rd, OPFP);
}
void MicroAssembler::fmvwx(FRegister rd, Register rs1) {
ASSERT(Supports(RV_F));
EmitRType(FMVWX, FRegister(0), rs1, F3_0, rd, OPFP);
}
#if XLEN >= 64
void MicroAssembler::fcvtls(Register rd, FRegister rs1, RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTintS, FRegister(L), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtlus(Register rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTintS, FRegister(LU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtsl(FRegister rd, Register rs1, RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTSint, FRegister(L), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtslu(FRegister rd,
Register rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_F));
EmitRType(FCVTSint, FRegister(LU), rs1, rounding, rd, OPFP);
}
#endif // XLEN >= 64
void MicroAssembler::fld(FRegister rd, Address addr) {
ASSERT(Supports(RV_D));
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPLoad8Imm(addr.offset())) {
c_fldsp(rd, addr);
return;
}
if (IsCFRdp(rd) && IsCRs1p(addr.base()) && IsCMem8Imm(addr.offset())) {
c_fld(rd, addr);
return;
}
}
EmitIType(addr.offset(), addr.base(), D, rd, LOADFP);
}
void MicroAssembler::fsd(FRegister rs2, Address addr) {
ASSERT(Supports(RV_D));
if (Supports(RV_C)) {
if ((addr.base() == SP) && IsCSPStore8Imm(addr.offset())) {
c_fsdsp(rs2, addr);
return;
}
if (IsCFRs2p(rs2) && IsCRs1p(addr.base()) && IsCMem8Imm(addr.offset())) {
c_fsd(rs2, addr);
return;
}
}
EmitSType(addr.offset(), rs2, addr.base(), D, STOREFP);
}
void MicroAssembler::fmaddd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitR4Type(rs3, F2_D, rs2, rs1, rounding, rd, FMADD);
}
void MicroAssembler::fmsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitR4Type(rs3, F2_D, rs2, rs1, rounding, rd, FMSUB);
}
void MicroAssembler::fnmsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitR4Type(rs3, F2_D, rs2, rs1, rounding, rd, FNMSUB);
}
void MicroAssembler::fnmaddd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitR4Type(rs3, F2_D, rs2, rs1, rounding, rd, FNMADD);
}
void MicroAssembler::faddd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FADDD, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FSUBD, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fmuld(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FMULD, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fdivd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FDIVD, rs2, rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsqrtd(FRegister rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FSQRTD, FRegister(0), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fsgnjd(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FSGNJD, rs2, rs1, J, rd, OPFP);
}
void MicroAssembler::fsgnjnd(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FSGNJD, rs2, rs1, JN, rd, OPFP);
}
void MicroAssembler::fsgnjxd(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FSGNJD, rs2, rs1, JX, rd, OPFP);
}
void MicroAssembler::fmind(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FMINMAXD, rs2, rs1, MIN, rd, OPFP);
}
void MicroAssembler::fmaxd(FRegister rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FMINMAXD, rs2, rs1, MAX, rd, OPFP);
}
void MicroAssembler::fcvtsd(FRegister rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTS, FRegister(1), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtds(FRegister rd, FRegister rs1) {
ASSERT(Supports(RV_D));
EmitRType(FCVTD, FRegister(0), rs1, F3_0, rd, OPFP);
}
void MicroAssembler::feqd(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FCMPD, rs2, rs1, FEQ, rd, OPFP);
}
void MicroAssembler::fltd(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FCMPD, rs2, rs1, FLT, rd, OPFP);
}
void MicroAssembler::fled(Register rd, FRegister rs1, FRegister rs2) {
ASSERT(Supports(RV_D));
EmitRType(FCMPD, rs2, rs1, FLE, rd, OPFP);
}
void MicroAssembler::fclassd(Register rd, FRegister rs1) {
ASSERT(Supports(RV_D));
EmitRType(FCLASSD, FRegister(0), rs1, F3_1, rd, OPFP);
}
void MicroAssembler::fcvtwd(Register rd, FRegister rs1, RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTintD, FRegister(W), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtwud(Register rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTintD, FRegister(WU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtdw(FRegister rd, Register rs1, RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTDint, FRegister(W), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtdwu(FRegister rd,
Register rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTDint, FRegister(WU), rs1, rounding, rd, OPFP);
}
#if XLEN >= 64
void MicroAssembler::fcvtld(Register rd, FRegister rs1, RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTintD, FRegister(L), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtlud(Register rd,
FRegister rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTintD, FRegister(LU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fmvxd(Register rd, FRegister rs1) {
ASSERT(Supports(RV_D));
EmitRType(FMVXD, FRegister(0), rs1, F3_0, rd, OPFP);
}
void MicroAssembler::fcvtdl(FRegister rd, Register rs1, RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTDint, FRegister(L), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fcvtdlu(FRegister rd,
Register rs1,
RoundingMode rounding) {
ASSERT(Supports(RV_D));
EmitRType(FCVTDint, FRegister(LU), rs1, rounding, rd, OPFP);
}
void MicroAssembler::fmvdx(FRegister rd, Register rs1) {
ASSERT(Supports(RV_D));
EmitRType(FMVDX, FRegister(0), rs1, F3_0, rd, OPFP);
}
#endif // XLEN >= 64
void MicroAssembler::c_lwsp(Register rd, Address addr) {
ASSERT(rd != ZR);
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
Emit16(C_LWSP | EncodeCRd(rd) | EncodeCSPLoad4Imm(addr.offset()));
}
#if XLEN == 32
void MicroAssembler::c_flwsp(FRegister rd, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_F));
Emit16(C_FLWSP | EncodeCFRd(rd) | EncodeCSPLoad4Imm(addr.offset()));
}
#else
void MicroAssembler::c_ldsp(Register rd, Address addr) {
ASSERT(rd != ZR);
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
Emit16(C_LDSP | EncodeCRd(rd) | EncodeCSPLoad8Imm(addr.offset()));
}
#endif
void MicroAssembler::c_fldsp(FRegister rd, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_D));
Emit16(C_FLDSP | EncodeCFRd(rd) | EncodeCSPLoad8Imm(addr.offset()));
}
void MicroAssembler::c_swsp(Register rs2, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
Emit16(C_SWSP | EncodeCRs2(rs2) | EncodeCSPStore4Imm(addr.offset()));
}
#if XLEN == 32
void MicroAssembler::c_fswsp(FRegister rs2, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_F));
Emit16(C_FSWSP | EncodeCFRs2(rs2) | EncodeCSPStore4Imm(addr.offset()));
}
#else
void MicroAssembler::c_sdsp(Register rs2, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
Emit16(C_SDSP | EncodeCRs2(rs2) | EncodeCSPStore8Imm(addr.offset()));
}
#endif
void MicroAssembler::c_fsdsp(FRegister rs2, Address addr) {
ASSERT(addr.base() == SP);
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_D));
Emit16(C_FSDSP | EncodeCFRs2(rs2) | EncodeCSPStore8Imm(addr.offset()));
}
void MicroAssembler::c_lw(Register rd, Address addr) {
ASSERT(Supports(RV_C));
Emit16(C_LW | EncodeCRdp(rd) | EncodeCRs1p(addr.base()) |
EncodeCMem4Imm(addr.offset()));
}
void MicroAssembler::c_ld(Register rd, Address addr) {
ASSERT(Supports(RV_C));
Emit16(C_LD | EncodeCRdp(rd) | EncodeCRs1p(addr.base()) |
EncodeCMem8Imm(addr.offset()));
}
void MicroAssembler::c_flw(FRegister rd, Address addr) {
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_F));
Emit16(C_FLW | EncodeCFRdp(rd) | EncodeCRs1p(addr.base()) |
EncodeCMem4Imm(addr.offset()));
}
void MicroAssembler::c_fld(FRegister rd, Address addr) {
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_D));
Emit16(C_FLD | EncodeCFRdp(rd) | EncodeCRs1p(addr.base()) |
EncodeCMem8Imm(addr.offset()));
}
void MicroAssembler::c_sw(Register rs2, Address addr) {
ASSERT(Supports(RV_C));
Emit16(C_SW | EncodeCRs1p(addr.base()) | EncodeCRs2p(rs2) |
EncodeCMem4Imm(addr.offset()));
}
void MicroAssembler::c_sd(Register rs2, Address addr) {
ASSERT(Supports(RV_C));
Emit16(C_SD | EncodeCRs1p(addr.base()) | EncodeCRs2p(rs2) |
EncodeCMem8Imm(addr.offset()));
}
void MicroAssembler::c_fsw(FRegister rs2, Address addr) {
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_F));
Emit16(C_FSW | EncodeCRs1p(addr.base()) | EncodeCFRs2p(rs2) |
EncodeCMem4Imm(addr.offset()));
}
void MicroAssembler::c_fsd(FRegister rs2, Address addr) {
ASSERT(Supports(RV_C));
ASSERT(Supports(RV_D));
Emit16(C_FSD | EncodeCRs1p(addr.base()) | EncodeCFRs2p(rs2) |
EncodeCMem8Imm(addr.offset()));
}
void MicroAssembler::c_j(Label* label) {
ASSERT(Supports(RV_C));
EmitCJump(label, C_J);
}
#if XLEN == 32
void MicroAssembler::c_jal(Label* label) {
ASSERT(Supports(RV_C));
EmitCJump(label, C_JAL);
}
#endif // XLEN == 32
void MicroAssembler::c_jr(Register rs1) {
ASSERT(Supports(RV_C));
ASSERT(rs1 != ZR);
Emit16(C_JR | EncodeCRs1(rs1) | EncodeCRs2(ZR));
}
void MicroAssembler::c_jalr(Register rs1) {
ASSERT(Supports(RV_C));
Emit16(C_JALR | EncodeCRs1(rs1) | EncodeCRs2(ZR));
}
void MicroAssembler::c_beqz(Register rs1p, Label* label) {
ASSERT(Supports(RV_C));
EmitCBranch(rs1p, label, C_BEQZ);
}
void MicroAssembler::c_bnez(Register rs1p, Label* label) {
ASSERT(Supports(RV_C));
EmitCBranch(rs1p, label, C_BNEZ);
}
void MicroAssembler::c_li(Register rd, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd != ZR);
Emit16(C_LI | EncodeCRd(rd) | EncodeCIImm(imm));
}
void MicroAssembler::c_lui(Register rd, uintptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd != ZR);
ASSERT(rd != SP);
Emit16(C_LUI | EncodeCRd(rd) | EncodeCUImm(imm));
}
void MicroAssembler::c_addi(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(imm != 0);
ASSERT(rd == rs1);
Emit16(C_ADDI | EncodeCRd(rd) | EncodeCIImm(imm));
}
#if XLEN >= 64
void MicroAssembler::c_addiw(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
Emit16(C_ADDIW | EncodeCRd(rd) | EncodeCIImm(imm));
}
#endif
void MicroAssembler::c_addi16sp(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
Emit16(C_ADDI16SP | EncodeCRd(rd) | EncodeCI16Imm(imm));
}
void MicroAssembler::c_addi4spn(Register rdp, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rs1 == SP);
ASSERT(imm != 0);
Emit16(C_ADDI4SPN | EncodeCRdp(rdp) | EncodeCI4SPNImm(imm));
}
void MicroAssembler::c_slli(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
ASSERT(imm != 0);
Emit16(C_SLLI | EncodeCRd(rd) | EncodeCIImm(imm));
}
void MicroAssembler::c_srli(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
ASSERT(imm != 0);
Emit16(C_SRLI | EncodeCRs1p(rd) | EncodeCIImm(imm));
}
void MicroAssembler::c_srai(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
ASSERT(imm != 0);
Emit16(C_SRAI | EncodeCRs1p(rd) | EncodeCIImm(imm));
}
void MicroAssembler::c_andi(Register rd, Register rs1, intptr_t imm) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
Emit16(C_ANDI | EncodeCRs1p(rd) | EncodeCIImm(imm));
}
void MicroAssembler::c_mv(Register rd, Register rs2) {
ASSERT(Supports(RV_C));
ASSERT(rd != ZR);
ASSERT(rs2 != ZR);
Emit16(C_MV | EncodeCRd(rd) | EncodeCRs2(rs2));
}
void MicroAssembler::c_add(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
ASSERT(rd != ZR);
ASSERT(rd == rs1);
ASSERT(rs2 != ZR);
Emit16(C_ADD | EncodeCRd(rd) | EncodeCRs2(rs2));
}
void MicroAssembler::c_and(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
ASSERT(rd == rs1);
Emit16(C_AND | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
void MicroAssembler::c_or(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
Emit16(C_OR | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
void MicroAssembler::c_xor(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
Emit16(C_XOR | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
void MicroAssembler::c_sub(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
Emit16(C_SUB | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
#if XLEN >= 64
void MicroAssembler::c_addw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
Emit16(C_ADDW | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
void MicroAssembler::c_subw(Register rd, Register rs1, Register rs2) {
ASSERT(Supports(RV_C));
Emit16(C_SUBW | EncodeCRs1p(rs1) | EncodeCRs2p(rs2));
}
#endif // XLEN >= 64
void MicroAssembler::c_nop() {
ASSERT(Supports(RV_C));
Emit16(C_NOP);
}
void MicroAssembler::c_ebreak() {
ASSERT(Supports(RV_C));
Emit16(C_EBREAK);
}
static Funct3 InvertFunct3(Funct3 func) {
switch (func) {
case BEQ:
return BNE;
case BNE:
return BEQ;
case BGE:
return BLT;
case BGEU:
return BLTU;
case BLT:
return BGE;
case BLTU:
return BGEU;
default:
UNREACHABLE();
}
}
void MicroAssembler::EmitBranch(Register rs1,
Register rs2,
Label* label,
Funct3 func,
JumpDistance distance) {
intptr_t offset;
if (label->IsBound()) {
// Backward branch: use near or far branch based on actual distance.
offset = label->Position() - Position();
if (IsBTypeImm(offset)) {
EmitBType(offset, rs2, rs1, func, BRANCH);
return;
}
if (IsJTypeImm(offset + 4)) {
intptr_t start = Position();
const intptr_t kFarBranchLength = 8;
EmitBType(kFarBranchLength, rs2, rs1, InvertFunct3(func), BRANCH);
offset = label->Position() - Position();
EmitJType(offset, ZR, JAL);
intptr_t end = Position();
ASSERT_EQUAL(end - start, kFarBranchLength);
return;
}
intptr_t start = Position();
const intptr_t kFarBranchLength = 12;
EmitBType(kFarBranchLength, rs2, rs1, InvertFunct3(func), BRANCH);
offset = label->Position() - Position();
intx_t lo = offset << (XLEN - 12) >> (XLEN - 12);
intx_t hi = (offset - lo) << (XLEN - 32) >> (XLEN - 32);
if (!IsUTypeImm(hi)) {
FATAL("Branch distance exceeds 2GB!");
}
EmitUType(hi, FAR_TMP, AUIPC);
EmitIType(lo, FAR_TMP, F3_0, ZR, JALR);
intptr_t end = Position();
ASSERT_EQUAL(end - start, kFarBranchLength);
return;
} else {
// Forward branch: speculatively use near branches and re-assemble with far
// branches if any need greater length.
if (distance == kNearJump) {
offset = label->link_b(Position());
if (!IsBTypeImm(offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
EmitBType(offset, rs2, rs1, func, BRANCH);
} else if (far_branch_level() == 0) {
offset = label->link_b(Position());
if (!IsBTypeImm(offset)) {
// TODO(riscv): This isn't so much because the branch is out of range
// as some previous jump to the same target would be out of B-type
// range... A possible alternative is to have separate lists on Labels
// for pending B-type and J-type instructions.
BailoutWithBranchOffsetError();
}
EmitBType(offset, rs2, rs1, func, BRANCH);
} else if (far_branch_level() == 1) {
intptr_t start = Position();
const intptr_t kFarBranchLength = 8;
EmitBType(kFarBranchLength, rs2, rs1, InvertFunct3(func), BRANCH);
offset = label->link_j(Position());
EmitJType(offset, ZR, JAL);
intptr_t end = Position();
ASSERT_EQUAL(end - start, kFarBranchLength);
} else {
intptr_t start = Position();
const intptr_t kFarBranchLength = 12;
EmitBType(kFarBranchLength, rs2, rs1, InvertFunct3(func), BRANCH);
offset = label->link_far(Position());
intx_t lo = offset << (XLEN - 12) >> (XLEN - 12);
intx_t hi = (offset - lo) << (XLEN - 32) >> (XLEN - 32);
if (!IsUTypeImm(hi)) {
FATAL("Branch distance exceeds 2GB!");
}
EmitUType(hi, FAR_TMP, AUIPC);
EmitIType(lo, FAR_TMP, F3_0, ZR, JALR);
intptr_t end = Position();
ASSERT_EQUAL(end - start, kFarBranchLength);
}
}
}
void MicroAssembler::EmitJump(Register rd,
Label* label,
Opcode op,
JumpDistance distance) {
intptr_t offset;
if (label->IsBound()) {
// Backward jump: use near or far jump based on actual distance.
offset = label->Position() - Position();
if (IsJTypeImm(offset)) {
EmitJType(offset, rd, JAL);
return;
}
intx_t lo = offset << (XLEN - 12) >> (XLEN - 12);
intx_t hi = (offset - lo) << (XLEN - 32) >> (XLEN - 32);
if (!IsUTypeImm(hi)) {
FATAL("Jump distance exceeds 2GB!");
}
EmitUType(hi, FAR_TMP, AUIPC);
EmitIType(lo, FAR_TMP, F3_0, ZR, JALR);
return;
} else {
// Forward jump: speculatively use near jumps and re-assemble with far
// jumps if any need greater length.
if (distance == kNearJump) {
offset = label->link_j(Position());
if (!IsJTypeImm(offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
EmitJType(offset, rd, JAL);
} else if (far_branch_level() < 2) {
offset = label->link_j(Position());
if (!IsJTypeImm(offset)) {
BailoutWithBranchOffsetError();
}
EmitJType(offset, rd, JAL);
} else {
offset = label->link_far(Position());
intx_t lo = offset << (XLEN - 12) >> (XLEN - 12);
intx_t hi = (offset - lo) << (XLEN - 32) >> (XLEN - 32);
if (!IsUTypeImm(hi)) {
FATAL("Jump distance exceeds 2GB!");
}
EmitUType(hi, FAR_TMP, AUIPC);
EmitIType(lo, FAR_TMP, F3_0, ZR, JALR);
}
}
}
void MicroAssembler::EmitCBranch(Register rs1p, Label* label, COpcode op) {
intptr_t offset;
if (label->IsBound()) {
offset = label->Position() - Position();
} else {
offset = label->link_cb(Position());
}
if (!IsCBImm(offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
Emit16(op | EncodeCRs1p(rs1p) | EncodeCBImm(offset));
}
void MicroAssembler::EmitCJump(Label* label, COpcode op) {
intptr_t offset;
if (label->IsBound()) {
offset = label->Position() - Position();
} else {
offset = label->link_cj(Position());
}
if (!IsCJImm(offset)) {
FATAL("Incorrect Assembler::kNearJump");
}
Emit16(op | EncodeCJImm(offset));
}
void MicroAssembler::EmitRType(Funct5 funct5,
std::memory_order order,
Register rs2,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode) {
intptr_t funct7 = funct5 << 2;
switch (order) {
case std::memory_order_acq_rel:
funct7 |= 0b11;
break;
case std::memory_order_acquire:
funct7 |= 0b10;
break;
case std::memory_order_release:
funct7 |= 0b01;
break;
case std::memory_order_relaxed:
funct7 |= 0b00;
break;
default:
FATAL("Invalid memory order");
}
EmitRType((Funct7)funct7, rs2, rs1, funct3, rd, opcode);
}
void MicroAssembler::EmitRType(Funct7 funct7,
Register rs2,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeFRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
RoundingMode round,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeFRs1(rs1);
e |= EncodeRoundingMode(round);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
Register rs1,
RoundingMode round,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeRoundingMode(round);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
Register rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
Funct3 funct3,
Register rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeFRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
RoundingMode round,
Register rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeFRs2(rs2);
e |= EncodeFRs1(rs1);
e |= EncodeRoundingMode(round);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitRType(Funct7 funct7,
intptr_t shamt,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFunct7(funct7);
e |= EncodeShamt(shamt);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitR4Type(FRegister rs3,
Funct2 funct2,
FRegister rs2,
FRegister rs1,
RoundingMode round,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeFRs3(rs3);
e |= EncodeFunct2(funct2);
e |= EncodeFRs2(rs2);
e |= EncodeFRs1(rs1);
e |= EncodeRoundingMode(round);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitIType(intptr_t imm,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeITypeImm(imm);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitIType(intptr_t imm,
Register rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeITypeImm(imm);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeFRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitSType(intptr_t imm,
Register rs2,
Register rs1,
Funct3 funct3,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeSTypeImm(imm);
e |= EncodeRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitSType(intptr_t imm,
FRegister rs2,
Register rs1,
Funct3 funct3,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeSTypeImm(imm);
e |= EncodeFRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitBType(intptr_t imm,
Register rs2,
Register rs1,
Funct3 funct3,
Opcode opcode) {
uint32_t e = 0;
e |= EncodeBTypeImm(imm);
e |= EncodeRs2(rs2);
e |= EncodeRs1(rs1);
e |= EncodeFunct3(funct3);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitUType(intptr_t imm, Register rd, Opcode opcode) {
uint32_t e = 0;
e |= EncodeUTypeImm(imm);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
void MicroAssembler::EmitJType(intptr_t imm, Register rd, Opcode opcode) {
uint32_t e = 0;
e |= EncodeJTypeImm(imm);
e |= EncodeRd(rd);
e |= EncodeOpcode(opcode);
Emit32(e);
}
Assembler::Assembler(ObjectPoolBuilder* object_pool_builder,
intptr_t far_branch_level)
: MicroAssembler(object_pool_builder,
far_branch_level,
FLAG_use_compressed_instructions ? RV_GC : RV_G),
constant_pool_allowed_(false) {
generate_invoke_write_barrier_wrapper_ = [&](Register reg) {
// Note this does not destory RA.
lx(TMP,
Address(THR, target::Thread::write_barrier_wrappers_thread_offset(reg)));
jalr(TMP, TMP);
};
generate_invoke_array_write_barrier_ = [&]() {
Call(
Address(THR, target::Thread::array_write_barrier_entry_point_offset()));
};
}
void Assembler::PushRegister(Register r) {
ASSERT(r != SP);
subi(SP, SP, target::kWordSize);
sx(r, Address(SP, 0));
}
void Assembler::PopRegister(Register r) {
ASSERT(r != SP);
lx(r, Address(SP, 0));
addi(SP, SP, target::kWordSize);
}
void Assembler::PushRegisterPair(Register r0, Register r1) {
ASSERT(r0 != SP);
ASSERT(r1 != SP);
subi(SP, SP, 2 * target::kWordSize);
sx(r1, Address(SP, target::kWordSize));
sx(r0, Address(SP, 0));
}
void Assembler::PopRegisterPair(Register r0, Register r1) {
ASSERT(r0 != SP);
ASSERT(r1 != SP);
lx(r1, Address(SP, target::kWordSize));
lx(r0, Address(SP, 0));
addi(SP, SP, 2 * target::kWordSize);
}
void Assembler::PushRegisters(const RegisterSet& regs) {
// The order in which the registers are pushed must match the order
// in which the registers are encoded in the safepoint's stack map.
intptr_t size = (regs.CpuRegisterCount() * target::kWordSize) +
(regs.FpuRegisterCount() * kFpuRegisterSize);
if (size == 0) {
return; // Skip no-op SP update.
}
subi(SP, SP, size);
intptr_t offset = size;
for (intptr_t i = kNumberOfFpuRegisters - 1; i >= 0; i--) {
FRegister reg = static_cast<FRegister>(i);
if (regs.ContainsFpuRegister(reg)) {
offset -= kFpuRegisterSize;
fsd(reg, Address(SP, offset));
}
}
for (intptr_t i = kNumberOfCpuRegisters - 1; i >= 0; i--) {
Register reg = static_cast<Register>(i);
if (regs.ContainsRegister(reg)) {
offset -= target::kWordSize;
sx(reg, Address(SP, offset));
}
}
ASSERT(offset == 0);
}
void Assembler::PopRegisters(const RegisterSet& regs) {
// The order in which the registers are pushed must match the order
// in which the registers are encoded in the safepoint's stack map.
intptr_t size = (regs.CpuRegisterCount() * target::kWordSize) +
(regs.FpuRegisterCount() * kFpuRegisterSize);
if (size == 0) {
return; // Skip no-op SP update.
}
intptr_t offset = 0;
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
Register reg = static_cast<Register>(i);
if (regs.ContainsRegister(reg)) {
lx(reg, Address(SP, offset));
offset += target::kWordSize;
}
}
for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
FRegister reg = static_cast<FRegister>(i);
if (regs.ContainsFpuRegister(reg)) {
fld(reg, Address(SP, offset));
offset += kFpuRegisterSize;
}
}
ASSERT(offset == size);
addi(SP, SP, size);
}
void Assembler::PushNativeCalleeSavedRegisters() {
RegisterSet regs(kAbiPreservedCpuRegs, kAbiPreservedFpuRegs);
intptr_t size = (regs.CpuRegisterCount() * target::kWordSize) +
(regs.FpuRegisterCount() * sizeof(double));
subi(SP, SP, size);
intptr_t offset = 0;
for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
FRegister reg = static_cast<FRegister>(i);
if (regs.ContainsFpuRegister(reg)) {
fsd(reg, Address(SP, offset));
offset += sizeof(double);
}
}
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
Register reg = static_cast<Register>(i);
if (regs.ContainsRegister(reg)) {
sx(reg, Address(SP, offset));
offset += target::kWordSize;
}
}
ASSERT(offset == size);
}
void Assembler::PopNativeCalleeSavedRegisters() {
RegisterSet regs(kAbiPreservedCpuRegs, kAbiPreservedFpuRegs);
intptr_t size = (regs.CpuRegisterCount() * target::kWordSize) +
(regs.FpuRegisterCount() * sizeof(double));
intptr_t offset = 0;
for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
FRegister reg = static_cast<FRegister>(i);
if (regs.ContainsFpuRegister(reg)) {
fld(reg, Address(SP, offset));
offset += sizeof(double);
}
}
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
Register reg = static_cast<Register>(i);
if (regs.ContainsRegister(reg)) {
lx(reg, Address(SP, offset));
offset += target::kWordSize;
}
}
ASSERT(offset == size);
addi(SP, SP, size);
}
void Assembler::ExtendValue(Register rd, Register rn, OperandSize sz) {
switch (sz) {
#if XLEN == 64
case kEightBytes:
if (rd == rn) return; // No operation needed.
return mv(rd, rn);
case kUnsignedFourBytes:
return UNIMPLEMENTED();
case kFourBytes:
return sextw(rd, rn);
#elif XLEN == 32
case kUnsignedFourBytes:
case kFourBytes:
if (rd == rn) return; // No operation needed.
return mv(rd, rn);
#endif
case kUnsignedTwoBytes:
case kTwoBytes:
case kUnsignedByte:
case kByte:
default:
UNIMPLEMENTED();
break;
}
UNIMPLEMENTED();
}
void Assembler::ExtendAndSmiTagValue(Register rd, Register rn, OperandSize sz) {
if (sz == kWordBytes) {
SmiTag(rd, rn);
return;
}
switch (sz) {
#if XLEN == 64
case kUnsignedFourBytes:
slli(rd, rn, XLEN - kBitsPerInt32);
srli(rd, rd, XLEN - kBitsPerInt32 - kSmiTagShift);
return;
case kFourBytes:
slli(rd, rn, XLEN - kBitsPerInt32);
srai(rd, rd, XLEN - kBitsPerInt32 - kSmiTagShift);
return;
#endif
case kUnsignedTwoBytes:
slli(rd, rn, XLEN - kBitsPerInt16);
srli(rd, rd, XLEN - kBitsPerInt16 - kSmiTagShift);
return;
case kTwoBytes:
slli(rd, rn, XLEN - kBitsPerInt16);
srai(rd, rd, XLEN - kBitsPerInt16 - kSmiTagShift);
return;
case kUnsignedByte:
slli(rd, rn, XLEN - kBitsPerInt8);
srli(rd, rd, XLEN - kBitsPerInt8 - kSmiTagShift);
return;
case kByte:
slli(rd, rn, XLEN - kBitsPerInt8);
srai(rd, rd, XLEN - kBitsPerInt8 - kSmiTagShift);
return;
default:
UNIMPLEMENTED();
break;
}
}
// Unconditional jump to a given address in memory. Clobbers TMP.
void Assembler::Jump(const Address& address) {
lx(TMP2, address);
jr(TMP2);
}
void Assembler::LoadField(Register dst, const FieldAddress& address) {
lx(dst, address);
}
#if defined(USING_THREAD_SANITIZER)
void Assembler::TsanLoadAcquire(Register addr) {
UNIMPLEMENTED();
}
void Assembler::TsanStoreRelease(Register addr) {
UNIMPLEMENTED();
}
#endif
void Assembler::LoadAcquire(Register dst, Register address, int32_t offset) {
ASSERT(dst != address);
LoadFromOffset(dst, address, offset);
fence(HartEffects::kRead, HartEffects::kMemory);
#if defined(USING_THREAD_SANITIZER)
if (offset == 0) {
TsanLoadAcquire(address);
} else {
AddImmediate(TMP2, address, offset);
TsanLoadAcquire(TMP2);
}
#endif
}
void Assembler::LoadAcquireCompressed(Register dst,
Register address,
int32_t offset) {
LoadAcquire(dst, address, offset);
}
void Assembler::StoreRelease(Register src, Register address, int32_t offset) {
fence(HartEffects::kMemory, HartEffects::kRead);
StoreToOffset(src, address, offset);
}
void Assembler::StoreReleaseCompressed(Register src,
Register address,
int32_t offset) {
UNIMPLEMENTED();
}
void Assembler::CompareWithCompressedFieldFromOffset(Register value,
Register base,
int32_t offset) {
UNIMPLEMENTED();
}
void Assembler::CompareWithMemoryValue(Register value,
Address address,
OperandSize sz) {
UNIMPLEMENTED();
}
void Assembler::CompareFunctionTypeNullabilityWith(Register type,
int8_t value) {
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
lbu(TMP,
FieldAddress(type, compiler::target::FunctionType::nullability_offset()));
CompareImmediate(TMP, value);
}
void Assembler::CompareTypeNullabilityWith(Register type, int8_t value) {
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
lbu(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()));
CompareImmediate(TMP, value);
}
void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
if (frame_space != 0) {
addi(SP, SP, -frame_space);
}
const intptr_t kAbiStackAlignment = 16; // For both 32 and 64 bit.
andi(SP, SP, ~(kAbiStackAlignment - 1));
}
// In debug mode, this generates code to check that:
// FP + kExitLinkSlotFromEntryFp == SP
// or triggers breakpoint otherwise.
void Assembler::EmitEntryFrameVerification() {
#if defined(DEBUG)
Label done;
ASSERT(!constant_pool_allowed());
LoadImmediate(TMP, target::frame_layout.exit_link_slot_from_entry_fp *
target::kWordSize);
add(TMP, TMP, FPREG);
beq(TMP, SPREG, &done, kNearJump);
Breakpoint();
Bind(&done);
#endif
}
void Assembler::CompareRegisters(Register rn, Register rm) {
ASSERT(deferred_compare_ == kNone);
deferred_compare_ = kCompareReg;
deferred_left_ = rn;
deferred_reg_ = rm;
}
void Assembler::CompareObjectRegisters(Register rn, Register rm) {
CompareRegisters(rn, rm);
}
void Assembler::TestRegisters(Register rn, Register rm) {
ASSERT(deferred_compare_ == kNone);
deferred_compare_ = kTestReg;
deferred_left_ = rn;
deferred_reg_ = rm;
}
void Assembler::BranchIf(Condition condition,
Label* label,
JumpDistance distance) {
ASSERT(deferred_compare_ != kNone);
if (deferred_compare_ == kCompareImm || deferred_compare_ == kCompareReg) {
Register left = deferred_left_;
Register right;
if (deferred_compare_ == kCompareImm) {
if (deferred_imm_ == 0) {
right = ZR;
} else {
LoadImmediate(TMP2, deferred_imm_);
right = TMP2;
}
} else {
right = deferred_reg_;
}
switch (condition) {
case EQUAL:
beq(left, right, label, distance);
break;
case NOT_EQUAL:
bne(left, right, label, distance);
break;
case LESS:
blt(left, right, label, distance);
break;
case LESS_EQUAL:
ble(left, right, label, distance);
break;
case GREATER_EQUAL:
bge(left, right, label, distance);
break;
case GREATER:
bgt(left, right, label, distance);
break;
case UNSIGNED_LESS:
bltu(left, right, label, distance);
break;
case UNSIGNED_LESS_EQUAL:
bleu(left, right, label, distance);
break;
case UNSIGNED_GREATER_EQUAL:
bgeu(left, right, label, distance);
break;
case UNSIGNED_GREATER:
bgtu(left, right, label, distance);
break;
case OVERFLOW:
case NO_OVERFLOW:
FATAL("Use Add/Subtract/MultiplyBranchOverflow instead.");
default:
UNREACHABLE();
}
} else if (deferred_compare_ == kTestImm || deferred_compare_ == kTestReg) {
if (deferred_compare_ == kTestImm) {
AndImmediate(TMP2, deferred_left_, deferred_imm_);
} else {
and_(TMP2, deferred_left_, deferred_reg_);
}
switch (condition) {
case ZERO:
beqz(TMP2, label, distance);
break;
case NOT_ZERO:
bnez(TMP2, label, distance);
break;
default:
UNREACHABLE();
}
} else {
UNREACHABLE();
}
deferred_compare_ = kNone; // Consumed.
}
void Assembler::SetIf(Condition condition, Register rd) {
ASSERT(deferred_compare_ != kNone);
if (deferred_compare_ == kCompareImm) {
if (deferred_imm_ == 0) {
deferred_compare_ = kCompareReg;
deferred_reg_ = ZR;
SetIf(condition, rd);
return;
}
if (!IsITypeImm(deferred_imm_) || !IsITypeImm(deferred_imm_ + 1)) {
LoadImmediate(TMP2, deferred_imm_);
deferred_compare_ = kCompareReg;
deferred_reg_ = TMP2;
SetIf(condition, rd);
return;
}
Register left = deferred_left_;
intx_t right = deferred_imm_;
switch (condition) {
case EQUAL:
xori(rd, left, right);
seqz(rd, rd);
break;
case NOT_EQUAL:
xori(rd, left, right);
snez(rd, rd);
break;
case LESS:
slti(rd, left, right);
break;
case LESS_EQUAL:
slti(rd, left, right + 1);
break;
case GREATER_EQUAL:
slti(rd, left, right);
xori(rd, rd, 1);
break;
case GREATER:
slti(rd, left, right + 1);
xori(rd, rd, 1);
break;
case UNSIGNED_LESS:
sltiu(rd, left, right);
break;
case UNSIGNED_LESS_EQUAL:
sltiu(rd, left, right + 1);
break;
case UNSIGNED_GREATER_EQUAL:
sltiu(rd, left, right);
xori(rd, rd, 1);
break;
case UNSIGNED_GREATER:
sltiu(rd, left, right + 1);
xori(rd, rd, 1);
break;
default:
UNREACHABLE();
}
} else if (deferred_compare_ == kCompareReg) {
Register left = deferred_left_;
Register right = deferred_reg_;
switch (condition) {
case EQUAL:
if (right == ZR) {
seqz(rd, left);
} else {
xor_(rd, left, right);
seqz(rd, rd);
}
break;
case NOT_EQUAL:
if (right == ZR) {
snez(rd, left);
} else {
xor_(rd, left, right);
snez(rd, rd);
}
break;
case LESS:
slt(rd, left, right);
break;
case LESS_EQUAL:
slt(rd, right, left);
xori(rd, rd, 1);
break;
case GREATER_EQUAL:
slt(rd, left, right);
xori(rd, rd, 1);
break;
case GREATER:
slt(rd, right, left);
break;
case UNSIGNED_LESS:
sltu(rd, left, right);
break;
case UNSIGNED_LESS_EQUAL:
sltu(rd, right, left);
xori(rd, rd, 1);
break;
case UNSIGNED_GREATER_EQUAL:
sltu(rd, left, right);
xori(rd, rd, 1);
break;
case UNSIGNED_GREATER:
sltu(rd, right, left);
break;
default:
UNREACHABLE();
}
} else if (deferred_compare_ == kTestImm || deferred_compare_ == kTestReg) {
if (deferred_compare_ == kTestImm) {
AndImmediate(TMP2, deferred_left_, deferred_imm_);
} else {
and_(TMP2, deferred_left_, deferred_reg_);
}
switch (condition) {
case ZERO:
seqz(rd, TMP2);
break;
case NOT_ZERO:
snez(rd, TMP2);
break;
default:
UNREACHABLE();
}
} else {
UNREACHABLE();
}
deferred_compare_ = kNone; // Consumed.
}
void Assembler::BranchIfZero(Register rn, Label* label, JumpDistance distance) {
beqz(rn, label, distance);
}
void Assembler::BranchIfNotSmi(Register reg,
Label* label,
JumpDistance distance) {
ASSERT(reg != TMP2);
andi(TMP2, reg, kSmiTagMask);
bnez(TMP2, label, distance);
}
void Assembler::BranchIfSmi(Register reg, Label* label, JumpDistance distance) {
ASSERT(reg != TMP2);
andi(TMP2, reg, kSmiTagMask);
beqz(TMP2, label, distance);
}
void Assembler::Jump(const Code& target,
Register pp,
ObjectPoolBuilderEntry::Patchability patchable) {
const intptr_t index =
object_pool_builder().FindObject(ToObject(target), patchable);
LoadWordFromPoolIndex(CODE_REG, index, pp);
Jump(FieldAddress(CODE_REG, target::Code::entry_point_offset()));
}
void Assembler::JumpAndLink(const Code& target,
ObjectPoolBuilderEntry::Patchability patchable,
CodeEntryKind entry_kind) {
const intptr_t index =
object_pool_builder().FindObject(ToObject(target), patchable);
LoadWordFromPoolIndex(CODE_REG, index);
Call(FieldAddress(CODE_REG, target::Code::entry_point_offset(entry_kind)));
}
void Assembler::JumpAndLinkToRuntime() {
Call(Address(THR, target::Thread::call_to_runtime_entry_point_offset()));
}
void Assembler::JumpAndLinkWithEquivalence(const Code& target,
const Object& equivalence,