Files
sdk/runtime/vm/compiler/assembler/assembler_riscv.h
T
Vyacheslav Egorov dcac60b672 [vm/aot] Fix BoxInt64 in deferred units
When generating code for deferred units compiler
can't emit PC relative call to the shared Mint allocation
stub. This causes compiler to emit an indirect call
through a Code object. Such calls clobber CODE_REG
which is actually an allocatable register.

Fix this by using non-allocatable register instead of
CODE_REG when generating indirect calls through
Code object in AOT mode. Callees don't expect
anything useful in CODE_REG anyway because AOT
calling convetion does not use it.

TEST=vm/cc/{BranchLinkPreservesRegisters,JumpAndLinkPreservesRegisters,CallCodePreservesRegisters}

Bug: b/242559057
Cq-Include-Trybots: luci.dart.try:vm-aot-linux-release-arm64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-release-x64-try,vm-aot-linux-product-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-linux-debug-simriscv64-try,vm-ffi-qemu-linux-release-riscv64-try
Change-Id: Ib1fdc1c104d0269d41bb1ab9cbe292ad28c7cd49
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/338127
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
2023-11-28 13:15:56 +00:00

1682 lines
61 KiB
C++

// 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.
#ifndef RUNTIME_VM_COMPILER_ASSEMBLER_ASSEMBLER_RISCV_H_
#define RUNTIME_VM_COMPILER_ASSEMBLER_ASSEMBLER_RISCV_H_
#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif // defined(DART_PRECOMPILED_RUNTIME)
#ifndef RUNTIME_VM_COMPILER_ASSEMBLER_ASSEMBLER_H_
#error Do not include assembler_riscv.h directly; use assembler.h instead.
#endif
#include <functional>
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/class_id.h"
#include "vm/compiler/assembler/assembler_base.h"
#include "vm/compiler/assembler/object_pool_builder.h"
#include "vm/constants.h"
#include "vm/hash_map.h"
#include "vm/simulator.h"
namespace dart {
// Forward declarations.
class FlowGraphCompiler;
class RuntimeEntry;
class RegisterSet;
namespace compiler {
class Address {
public:
Address(Register base, intptr_t offset) : base_(base), offset_(offset) {}
explicit Address(Register base) : base_(base), offset_(0) {}
// Prevent implicit conversion of Register to intptr_t.
Address(Register base, Register index) = delete;
Register base() const { return base_; }
intptr_t offset() const { return offset_; }
private:
Register base_;
intptr_t offset_;
};
class FieldAddress : public Address {
public:
FieldAddress(Register base, intptr_t offset)
: Address(base, offset - kHeapObjectTag) {}
// Prevent implicit conversion of Register to intptr_t.
FieldAddress(Register base, Register index) = delete;
};
// All functions produce exactly one instruction.
class MicroAssembler : public AssemblerBase {
public:
MicroAssembler(ObjectPoolBuilder* object_pool_builder,
intptr_t far_branch_level,
ExtensionSet extensions);
~MicroAssembler();
#if defined(TESTING)
void SetExtensions(ExtensionSet extensions) { extensions_ = extensions; }
#endif
bool Supports(Extension extension) const {
return extensions_.Includes(extension);
}
bool Supports(ExtensionSet extensions) const {
return extensions_.IncludesAll(extensions);
}
intptr_t far_branch_level() const { return far_branch_level_; }
void set_far_branch_level(intptr_t level) { far_branch_level_ = level; }
void Bind(Label* label);
// ==== RV32I ====
void lui(Register rd, intptr_t imm);
void lui_fixed(Register rd, intptr_t imm);
void auipc(Register rd, intptr_t imm);
void jal(Register rd, Label* label, JumpDistance d = kFarJump);
void jal(Label* label, JumpDistance d = kFarJump) { jal(RA, label, d); }
void j(Label* label, JumpDistance d = kFarJump) { jal(ZR, label, d); }
void jalr(Register rd, Register rs1, intptr_t offset = 0);
void jalr_fixed(Register rd, Register rs1, intptr_t offset);
void jalr(Register rs1, intptr_t offset = 0) { jalr(RA, rs1, offset); }
void jr(Register rs1, intptr_t offset = 0) { jalr(ZR, rs1, offset); }
void ret() { jalr(ZR, RA, 0); }
void beq(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void bne(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void blt(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void bge(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void bgt(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump) {
blt(rs2, rs1, l, d);
}
void ble(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump) {
bge(rs2, rs1, l, d);
}
void bltu(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void bgeu(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump);
void bgtu(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump) {
bltu(rs2, rs1, l, d);
}
void bleu(Register rs1, Register rs2, Label* l, JumpDistance d = kFarJump) {
bgeu(rs2, rs1, l, d);
}
void lb(Register rd, Address addr);
void lh(Register rd, Address addr);
void lw(Register rd, Address addr);
void lbu(Register rd, Address addr);
void lhu(Register rd, Address addr);
void sb(Register rs2, Address addr);
void sh(Register rs2, Address addr);
void sw(Register rs2, Address addr);
void addi(Register rd, Register rs1, intptr_t imm);
void subi(Register rd, Register rs1, intptr_t imm) { addi(rd, rs1, -imm); }
void slti(Register rd, Register rs1, intptr_t imm);
void sltiu(Register rd, Register rs1, intptr_t imm);
void xori(Register rd, Register rs1, intptr_t imm);
void ori(Register rd, Register rs1, intptr_t imm);
void andi(Register rd, Register rs1, intptr_t imm);
void slli(Register rd, Register rs1, intptr_t shamt);
void srli(Register rd, Register rs1, intptr_t shamt);
void srai(Register rd, Register rs1, intptr_t shamt);
void add(Register rd, Register rs1, Register rs2);
void sub(Register rd, Register rs1, Register rs2);
void sll(Register rd, Register rs1, Register rs2);
void slt(Register rd, Register rs1, Register rs2);
void sltu(Register rd, Register rs1, Register rs2);
void xor_(Register rd, Register rs1, Register rs2);
void srl(Register rd, Register rs1, Register rs2);
void sra(Register rd, Register rs1, Register rs2);
void or_(Register rd, Register rs1, Register rs2);
void and_(Register rd, Register rs1, Register rs2);
void fence(HartEffects predecessor, HartEffects successor);
void fence() { fence(kAll, kAll); }
void fencei();
void ecall();
void ebreak(); // Causes SIGTRAP(5).
void csrrw(Register rd, uint32_t csr, Register rs1);
void csrrs(Register rd, uint32_t csr, Register rs1);
void csrrc(Register rd, uint32_t csr, Register rs1);
void csrr(Register rd, uint32_t csr) { csrrs(rd, csr, ZR); }
void csrw(uint32_t csr, Register rs) { csrrw(ZR, csr, rs); }
void csrs(uint32_t csr, Register rs) { csrrs(ZR, csr, rs); }
void csrc(uint32_t csr, Register rs) { csrrc(ZR, csr, rs); }
void csrrwi(Register rd, uint32_t csr, uint32_t imm);
void csrrsi(Register rd, uint32_t csr, uint32_t imm);
void csrrci(Register rd, uint32_t csr, uint32_t imm);
void csrwi(uint32_t csr, uint32_t imm) { csrrwi(ZR, csr, imm); }
void csrsi(uint32_t csr, uint32_t imm) { csrrsi(ZR, csr, imm); }
void csrci(uint32_t csr, uint32_t imm) { csrrci(ZR, csr, imm); }
void trap(); // Permanently reserved illegal instruction; causes SIGILL(4).
void nop() { addi(ZR, ZR, 0); }
void li(Register rd, intptr_t imm) { addi(rd, ZR, imm); }
void mv(Register rd, Register rs) { addi(rd, rs, 0); }
void not_(Register rd, Register rs) { xori(rd, rs, -1); }
void neg(Register rd, Register rs) { sub(rd, ZR, rs); }
void snez(Register rd, Register rs) { sltu(rd, ZR, rs); }
void seqz(Register rd, Register rs) { sltiu(rd, rs, 1); }
void sltz(Register rd, Register rs) { slt(rd, rs, ZR); }
void sgtz(Register rd, Register rs) { slt(rd, ZR, rs); }
void beqz(Register rs, Label* label, JumpDistance distance = kFarJump) {
beq(rs, ZR, label, distance);
}
void bnez(Register rs, Label* label, JumpDistance distance = kFarJump) {
bne(rs, ZR, label, distance);
}
void blez(Register rs, Label* label, JumpDistance distance = kFarJump) {
bge(ZR, rs, label, distance);
}
void bgez(Register rs, Label* label, JumpDistance distance = kFarJump) {
bge(rs, ZR, label, distance);
}
void bltz(Register rs, Label* label, JumpDistance distance = kFarJump) {
blt(rs, ZR, label, distance);
}
void bgtz(Register rs, Label* label, JumpDistance distance = kFarJump) {
blt(ZR, rs, label, distance);
}
// ==== RV64I ====
#if XLEN >= 64
void lwu(Register rd, Address addr);
void ld(Register rd, Address addr);
void sd(Register rs2, Address addr);
void addiw(Register rd, Register rs1, intptr_t imm);
void subiw(Register rd, Register rs1, intptr_t imm) { addiw(rd, rs1, -imm); }
void slliw(Register rd, Register rs1, intptr_t shamt);
void srliw(Register rd, Register rs1, intptr_t shamt);
void sraiw(Register rd, Register rs1, intptr_t shamt);
void addw(Register rd, Register rs1, Register rs2);
void subw(Register rd, Register rs1, Register rs2);
void sllw(Register rd, Register rs1, Register rs2);
void srlw(Register rd, Register rs1, Register rs2);
void sraw(Register rd, Register rs1, Register rs2);
void negw(Register rd, Register rs) { subw(rd, ZR, rs); }
void sextw(Register rd, Register rs) { addiw(rd, rs, 0); }
#endif // XLEN >= 64
#if XLEN == 32
void lx(Register rd, Address addr) { lw(rd, addr); }
void sx(Register rs2, Address addr) { sw(rs2, addr); }
#elif XLEN == 64
void lx(Register rd, Address addr) { ld(rd, addr); }
void sx(Register rs2, Address addr) { sd(rs2, addr); }
#elif XLEN == 128
void lx(Register rd, Address addr) { lq(rd, addr); }
void sx(Register rs2, Address addr) { sq(rs2, addr); }
#endif
// ==== RV32M ====
void mul(Register rd, Register rs1, Register rs2);
void mulh(Register rd, Register rs1, Register rs2);
void mulhsu(Register rd, Register rs1, Register rs2);
void mulhu(Register rd, Register rs1, Register rs2);
void div(Register rd, Register rs1, Register rs2);
void divu(Register rd, Register rs1, Register rs2);
void rem(Register rd, Register rs1, Register rs2);
void remu(Register rd, Register rs1, Register rs2);
// ==== RV64M ====
#if XLEN >= 64
void mulw(Register rd, Register rs1, Register rs2);
void divw(Register rd, Register rs1, Register rs2);
void divuw(Register rd, Register rs1, Register rs2);
void remw(Register rd, Register rs1, Register rs2);
void remuw(Register rd, Register rs1, Register rs2);
#endif // XLEN >= 64
// ==== RV32A ====
void lrw(Register rd,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void scw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoswapw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoaddw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoxorw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoandw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoorw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amominw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amomaxw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amominuw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amomaxuw(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
// ==== RV64A ====
#if XLEN >= 64
void lrd(Register rd,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void scd(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoswapd(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoaddd(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoxord(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoandd(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amoord(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amomind(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amomaxd(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amominud(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
void amomaxud(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed);
#endif // XLEN >= 64
#if XLEN == 32
void lr(Register rd,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
lrw(rd, addr, order);
}
void sc(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
scw(rd, rs2, addr, order);
}
#elif XLEN == 64
void lr(Register rd,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
lrd(rd, addr, order);
}
void sc(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
scd(rd, rs2, addr, order);
}
#elif XLEN == 128
void lr(Register rd,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
lrq(rd, addr, order);
}
void sc(Register rd,
Register rs2,
Address addr,
std::memory_order order = std::memory_order_relaxed) {
scq(rd, rs2, addr, order);
}
#endif
// ==== RV32F ====
void flw(FRegister rd, Address addr);
void fsw(FRegister rs2, Address addr);
// rd := (rs1 * rs2) + rs3
void fmadds(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := (rs1 * rs2) - rs3
void fmsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := -(rs1 * rs2) + rs3
void fnmsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := -(rs1 * rs2) - rs3
void fnmadds(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
void fadds(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fsubs(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fmuls(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fdivs(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fsqrts(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
void fsgnjs(FRegister rd, FRegister rs1, FRegister rs2);
void fsgnjns(FRegister rd, FRegister rs1, FRegister rs2);
void fsgnjxs(FRegister rd, FRegister rs1, FRegister rs2);
void fmins(FRegister rd, FRegister rs1, FRegister rs2);
void fmaxs(FRegister rd, FRegister rs1, FRegister rs2);
void feqs(Register rd, FRegister rs1, FRegister rs2);
void flts(Register rd, FRegister rs1, FRegister rs2);
void fles(Register rd, FRegister rs1, FRegister rs2);
void fgts(Register rd, FRegister rs1, FRegister rs2) { flts(rd, rs2, rs1); }
void fges(Register rd, FRegister rs1, FRegister rs2) { fles(rd, rs2, rs1); }
void fclasss(Register rd, FRegister rs1);
// int32_t <- float
void fcvtws(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// uint32_t <- float
void fcvtwus(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// float <- int32_t
void fcvtsw(FRegister rd, Register rs1, RoundingMode rounding = RNE);
// float <- uint32_t
void fcvtswu(FRegister rd, Register rs1, RoundingMode rounding = RNE);
void fmvs(FRegister rd, FRegister rs) { fsgnjs(rd, rs, rs); }
void fabss(FRegister rd, FRegister rs) { fsgnjxs(rd, rs, rs); }
void fnegs(FRegister rd, FRegister rs) { fsgnjns(rd, rs, rs); }
// xlen <--bit_cast-- float
void fmvxw(Register rd, FRegister rs1);
// float <--bit_cast-- xlen
void fmvwx(FRegister rd, Register rs1);
// ==== RV64F ====
#if XLEN >= 64
// int64_t <- double
void fcvtls(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// uint64_t <- double
void fcvtlus(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// double <- int64_t
void fcvtsl(FRegister rd, Register rs1, RoundingMode rounding = RNE);
// double <- uint64_t
void fcvtslu(FRegister rd, Register rs1, RoundingMode rounding = RNE);
#endif // XLEN >= 64
// ==== RV32D ====
void fld(FRegister rd, Address addr);
void fsd(FRegister rs2, Address addr);
// rd := (rs1 * rs2) + rs3
void fmaddd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := (rs1 * rs2) - rs3
void fmsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := -(rs1 * rs2) - rs3
void fnmsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
// rd := -(rs1 * rs2) + rs3
void fnmaddd(FRegister rd,
FRegister rs1,
FRegister rs2,
FRegister rs3,
RoundingMode rounding = RNE);
void faddd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fsubd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fmuld(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fdivd(FRegister rd,
FRegister rs1,
FRegister rs2,
RoundingMode rounding = RNE);
void fsqrtd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
void fsgnjd(FRegister rd, FRegister rs1, FRegister rs2);
void fsgnjnd(FRegister rd, FRegister rs1, FRegister rs2);
void fsgnjxd(FRegister rd, FRegister rs1, FRegister rs2);
void fmind(FRegister rd, FRegister rs1, FRegister rs2);
void fmaxd(FRegister rd, FRegister rs1, FRegister rs2);
void fcvtsd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
void fcvtds(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
void feqd(Register rd, FRegister rs1, FRegister rs2);
void fltd(Register rd, FRegister rs1, FRegister rs2);
void fled(Register rd, FRegister rs1, FRegister rs2);
void fgtd(Register rd, FRegister rs1, FRegister rs2) { fltd(rd, rs2, rs1); }
void fged(Register rd, FRegister rs1, FRegister rs2) { fled(rd, rs2, rs1); }
void fclassd(Register rd, FRegister rs1);
// int32_t <- double
void fcvtwd(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// uint32_t <- double
void fcvtwud(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// double <- int32_t
void fcvtdw(FRegister rd, Register rs1, RoundingMode rounding = RNE);
// double <- uint32_t
void fcvtdwu(FRegister rd, Register rs1, RoundingMode rounding = RNE);
void fmvd(FRegister rd, FRegister rs) { fsgnjd(rd, rs, rs); }
void fabsd(FRegister rd, FRegister rs) { fsgnjxd(rd, rs, rs); }
void fnegd(FRegister rd, FRegister rs) { fsgnjnd(rd, rs, rs); }
// ==== RV64D ====
#if XLEN >= 64
// int64_t <- double
void fcvtld(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// uint64_t <- double
void fcvtlud(Register rd, FRegister rs1, RoundingMode rounding = RNE);
// xlen <--bit_cast-- double
void fmvxd(Register rd, FRegister rs1);
// double <- int64_t
void fcvtdl(FRegister rd, Register rs1, RoundingMode rounding = RNE);
// double <- uint64_t
void fcvtdlu(FRegister rd, Register rs1, RoundingMode rounding = RNE);
// double <--bit_cast-- xlen
void fmvdx(FRegister rd, Register rs1);
#endif // XLEN >= 64
// ==== Zba: Address generation ====
void adduw(Register rd, Register rs1, Register rs2);
void sh1add(Register rd, Register rs1, Register rs2);
void sh1adduw(Register rd, Register rs1, Register rs2);
void sh2add(Register rd, Register rs1, Register rs2);
void sh2adduw(Register rd, Register rs1, Register rs2);
void sh3add(Register rd, Register rs1, Register rs2);
void sh3adduw(Register rd, Register rs1, Register rs2);
void slliuw(Register rd, Register rs1, intx_t imm);
// ==== Zbb: Basic bit-manipulation ====
void andn(Register rd, Register rs1, Register rs2);
void orn(Register rd, Register rs1, Register rs2);
void xnor(Register rd, Register rs1, Register rs2);
void clz(Register rd, Register rs);
void clzw(Register rd, Register rs);
void ctz(Register rd, Register rs);
void ctzw(Register rd, Register rs);
void cpop(Register rd, Register rs);
void cpopw(Register rd, Register rs);
void max(Register rd, Register rs1, Register rs2); // NOLINT
void maxu(Register rd, Register rs1, Register rs2);
void min(Register rd, Register rs1, Register rs2); // NOLINT
void minu(Register rd, Register rs1, Register rs2);
void sextb(Register rd, Register rs);
void sexth(Register rd, Register rs);
void zexth(Register rd, Register rs);
void rol(Register rd, Register rs1, Register rs2);
void rolw(Register rd, Register rs1, Register rs2);
void ror(Register rd, Register rs1, Register rs2);
void rori(Register rd, Register rs1, intx_t imm);
void roriw(Register rd, Register rs1, intx_t imm);
void rorw(Register rd, Register rs1, Register rs2);
void orcb(Register rd, Register rs);
void rev8(Register rd, Register rs);
// ==== Zbc: Carry-less multiplication ====
void clmul(Register rd, Register rs1, Register rs2);
void clmulh(Register rd, Register rs1, Register rs2);
void clmulr(Register rd, Register rs1, Register rs2);
// ==== Zbs: Single-bit instructions ====
void bclr(Register rd, Register rs1, Register rs2);
void bclri(Register rd, Register rs1, intx_t shamt);
void bext(Register rd, Register rs1, Register rs2);
void bexti(Register rd, Register rs1, intx_t shamt);
void binv(Register rd, Register rs1, Register rs2);
void binvi(Register rd, Register rs1, intx_t shamt);
void bset(Register rd, Register rs1, Register rs2);
void bseti(Register rd, Register rs1, intx_t shamt);
// ==== Dart Simulator Debugging ====
void SimulatorPrintObject(Register rs1);
private:
// ==== RV32/64C ====
void c_lwsp(Register rd, Address addr);
#if XLEN == 32
void c_flwsp(FRegister rd, Address addr);
#else
void c_ldsp(Register rd, Address addr);
#endif
void c_fldsp(FRegister rd, Address addr);
void c_swsp(Register rs2, Address addr);
#if XLEN == 32
void c_fswsp(FRegister rs2, Address addr);
#else
void c_sdsp(Register rs2, Address addr);
#endif
void c_fsdsp(FRegister rs2, Address addr);
void c_lw(Register rd, Address addr);
void c_ld(Register rd, Address addr);
void c_flw(FRegister rd, Address addr);
void c_fld(FRegister rd, Address addr);
void c_sw(Register rs2, Address addr);
void c_sd(Register rs2, Address addr);
void c_fsw(FRegister rs2, Address addr);
void c_fsd(FRegister rs2, Address addr);
void c_j(Label* label);
#if XLEN == 32
void c_jal(Label* label);
#endif
void c_jr(Register rs1);
void c_jalr(Register rs1);
void c_beqz(Register rs1p, Label* label);
void c_bnez(Register rs1p, Label* label);
void c_li(Register rd, intptr_t imm);
void c_lui(Register rd, uintptr_t imm);
void c_addi(Register rd, Register rs1, intptr_t imm);
#if XLEN >= 64
void c_addiw(Register rd, Register rs1, intptr_t imm);
#endif
void c_addi16sp(Register rd, Register rs1, intptr_t imm);
void c_addi4spn(Register rdp, Register rs1, intptr_t imm);
void c_slli(Register rd, Register rs1, intptr_t imm);
void c_srli(Register rd, Register rs1, intptr_t imm);
void c_srai(Register rd, Register rs1, intptr_t imm);
void c_andi(Register rd, Register rs1, intptr_t imm);
void c_mv(Register rd, Register rs2);
void c_add(Register rd, Register rs1, Register rs2);
void c_and(Register rd, Register rs1, Register rs2);
void c_or(Register rd, Register rs1, Register rs2);
void c_xor(Register rd, Register rs1, Register rs2);
void c_sub(Register rd, Register rs1, Register rs2);
#if XLEN >= 64
void c_addw(Register rd, Register rs1, Register rs2);
void c_subw(Register rd, Register rs1, Register rs2);
#endif
void c_nop();
void c_ebreak();
protected:
intptr_t UpdateCBOffset(intptr_t branch_position, intptr_t new_offset);
intptr_t UpdateCJOffset(intptr_t branch_position, intptr_t new_offset);
intptr_t UpdateBOffset(intptr_t branch_position, intptr_t new_offset);
intptr_t UpdateJOffset(intptr_t branch_position, intptr_t new_offset);
intptr_t UpdateFarOffset(intptr_t branch_position, intptr_t new_offset);
intptr_t Position() { return buffer_.Size(); }
void EmitBranch(Register rs1,
Register rs2,
Label* label,
Funct3 func,
JumpDistance distance);
void EmitJump(Register rd, Label* label, Opcode op, JumpDistance distance);
void EmitCBranch(Register rs1p, Label* label, COpcode op);
void EmitCJump(Label* label, COpcode op);
void EmitRType(Funct5 funct5,
std::memory_order order,
Register rs2,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
Register rs2,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
RoundingMode round,
FRegister rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
Register rs1,
RoundingMode round,
FRegister rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
Register rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
Funct3 funct3,
Register rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
FRegister rs2,
FRegister rs1,
RoundingMode round,
Register rd,
Opcode opcode);
void EmitRType(Funct7 funct7,
intptr_t shamt,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode);
void EmitR4Type(FRegister rs3,
Funct2 funct2,
FRegister rs2,
FRegister rs1,
RoundingMode round,
FRegister rd,
Opcode opcode);
void EmitIType(intptr_t imm,
Register rs1,
Funct3 funct3,
Register rd,
Opcode opcode);
void EmitIType(intptr_t imm,
Register rs1,
Funct3 funct3,
FRegister rd,
Opcode opcode);
void EmitSType(intptr_t imm,
Register rs2,
Register rs1,
Funct3 funct3,
Opcode opcode);
void EmitSType(intptr_t imm,
FRegister rs2,
Register rs1,
Funct3 funct3,
Opcode opcode);
void EmitBType(intptr_t imm,
Register rs2,
Register rs1,
Funct3 funct3,
Opcode opcode);
void EmitUType(intptr_t imm, Register rd, Opcode opcode);
void EmitJType(intptr_t imm, Register rd, Opcode opcode);
uint16_t Read16(intptr_t position) {
return buffer_.Load<uint16_t>(position);
}
void Write16(intptr_t position, uint16_t instruction) {
return buffer_.Store<uint16_t>(position, instruction);
}
void Emit16(uint16_t instruction) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
buffer_.Emit<uint16_t>(instruction);
}
uint32_t Read32(intptr_t position) {
return buffer_.Load<uint32_t>(position);
}
void Write32(intptr_t position, uint32_t instruction) {
return buffer_.Store<uint32_t>(position, instruction);
}
public:
void Emit32(uint32_t instruction) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
buffer_.Emit<uint32_t>(instruction);
}
void Emit64(uint64_t instruction) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
buffer_.Emit<uint64_t>(instruction);
}
protected:
ExtensionSet extensions_;
intptr_t far_branch_level_;
};
class Assembler : public MicroAssembler {
public:
explicit Assembler(ObjectPoolBuilder* object_pool_builder,
intptr_t far_branch_level = 0);
~Assembler() {}
void PushRegister(Register r);
void PopRegister(Register r);
void PushRegisterPair(Register r0, Register r1);
void PopRegisterPair(Register r0, Register r1);
void PushRegisters(const RegisterSet& registers);
void PopRegisters(const RegisterSet& registers);
void PushRegistersInOrder(std::initializer_list<Register> regs);
void PushValueAtOffset(Register base, int32_t offset) { UNIMPLEMENTED(); }
// Push all registers which are callee-saved according to the ARM64 ABI.
void PushNativeCalleeSavedRegisters();
// Pop all registers which are callee-saved according to the ARM64 ABI.
void PopNativeCalleeSavedRegisters();
void ExtendValue(Register rd, Register rn, OperandSize sz) override;
void ExtendAndSmiTagValue(Register rd,
Register rn,
OperandSize sz = kWordBytes) override;
void Drop(intptr_t stack_elements) {
ASSERT(stack_elements >= 0);
if (stack_elements > 0) {
AddImmediate(SP, SP, stack_elements * target::kWordSize);
}
}
void Bind(Label* label) { MicroAssembler::Bind(label); }
// Unconditional jump to a given label.
void Jump(Label* label, JumpDistance distance = kFarJump) {
j(label, distance);
}
// Unconditional jump to a given address in register.
void Jump(Register target) { jr(target); }
// Unconditional jump to a given address in memory. Clobbers TMP.
void Jump(const Address& address);
void LoadField(Register dst, const FieldAddress& address) override;
void LoadCompressedField(Register dst, const FieldAddress& address) override {
LoadCompressed(dst, address);
}
void LoadMemoryValue(Register dst, Register base, int32_t offset) {
LoadFromOffset(dst, base, offset, kWordBytes);
}
void StoreMemoryValue(Register src, Register base, int32_t offset) {
StoreToOffset(src, base, offset, kWordBytes);
}
#if defined(TARGET_USES_THREAD_SANITIZER)
void TsanLoadAcquire(Register addr);
void TsanStoreRelease(Register addr);
#endif
void LoadAcquire(Register dst,
Register address,
int32_t offset = 0,
OperandSize size = kWordBytes) override;
void LoadAcquireCompressed(Register dst,
Register address,
int32_t offset = 0) override;
void StoreRelease(Register src,
Register address,
int32_t offset = 0) override;
void StoreReleaseCompressed(Register src,
Register address,
int32_t offset = 0);
void CompareWithCompressedFieldFromOffset(Register value,
Register base,
int32_t offset);
void CompareWithMemoryValue(Register value,
Address address,
OperandSize size = kWordBytes) override;
// Debugging and bringup support.
void Breakpoint() override { trap(); }
void SetPrologueOffset() {
if (prologue_offset_ == -1) {
prologue_offset_ = CodeSize();
}
}
void ReserveAlignedFrameSpace(intptr_t frame_space);
// In debug mode, this generates code to check that:
// FP + kExitLinkSlotFromEntryFp == SP
// or triggers breakpoint otherwise.
void EmitEntryFrameVerification();
// Instruction pattern from entrypoint is used in Dart frame prologs
// to set up the frame and save a PC which can be used to figure out the
// RawInstruction object corresponding to the code running in the frame.
static constexpr intptr_t kEntryPointToPcMarkerOffset = 0;
static intptr_t EntryPointToPcMarkerOffset() {
return kEntryPointToPcMarkerOffset;
}
// On some other platforms, we draw a distinction between safe and unsafe
// smis.
static bool IsSafe(const Object& object) { return true; }
static bool IsSafeSmi(const Object& object) { return target::IsSmi(object); }
void CompareRegisters(Register rn, Register rm);
void CompareObjectRegisters(Register rn, Register rm);
void TestRegisters(Register rn, Register rm);
// Branches to the given label if the condition holds.
void BranchIf(Condition condition,
Label* label,
JumpDistance distance = kFarJump);
void BranchIfZero(Register rn,
Label* label,
JumpDistance distance = kFarJump);
void BranchIfBit(Register rn,
intptr_t bit_number,
Condition condition,
Label* label,
JumpDistance distance = kFarJump);
void SetIf(Condition condition, Register rd);
void SmiUntag(Register reg) { SmiUntag(reg, reg); }
void SmiUntag(Register dst, Register src) { srai(dst, src, kSmiTagSize); }
void SmiTag(Register reg) override { SmiTag(reg, reg); }
void SmiTag(Register dst, Register src) { slli(dst, src, kSmiTagSize); }
// Truncates upper bits.
void LoadInt32FromBoxOrSmi(Register result, Register value) override {
if (result == value) {
ASSERT(TMP != value);
MoveRegister(TMP, value);
value = TMP;
}
ASSERT(value != result);
compiler::Label done;
SmiUntag(result, value);
BranchIfSmi(value, &done, compiler::Assembler::kNearJump);
LoadFieldFromOffset(result, value, target::Mint::value_offset(),
compiler::kFourBytes);
Bind(&done);
}
#if XLEN != 32
void LoadInt64FromBoxOrSmi(Register result, Register value) override {
if (result == value) {
ASSERT(TMP != value);
MoveRegister(TMP, value);
value = TMP;
}
ASSERT(value != result);
compiler::Label done;
SmiUntag(result, value);
BranchIfSmi(value, &done, compiler::Assembler::kNearJump);
LoadFieldFromOffset(result, value, target::Mint::value_offset());
Bind(&done);
}
#endif
void BranchIfNotSmi(Register reg,
Label* label,
JumpDistance distance = kFarJump);
void BranchIfSmi(Register reg,
Label* label,
JumpDistance distance = kFarJump) override;
void ArithmeticShiftRightImmediate(Register reg, intptr_t shift) override;
void CompareWords(Register reg1,
Register reg2,
intptr_t offset,
Register count,
Register temp,
Label* equals) override;
void JumpAndLink(const Code& code,
ObjectPoolBuilderEntry::Patchability patchable =
ObjectPoolBuilderEntry::kNotPatchable,
CodeEntryKind entry_kind = CodeEntryKind::kNormal,
ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior =
ObjectPoolBuilderEntry::kSnapshotable);
void JumpAndLinkPatchable(
const Code& code,
CodeEntryKind entry_kind = CodeEntryKind::kNormal,
ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior =
ObjectPoolBuilderEntry::kSnapshotable) {
JumpAndLink(code, ObjectPoolBuilderEntry::kPatchable, entry_kind,
snapshot_behavior);
}
// Emit a call that shares its object pool entries with other calls
// that have the same equivalence marker.
void JumpAndLinkWithEquivalence(
const Code& code,
const Object& equivalence,
CodeEntryKind entry_kind = CodeEntryKind::kNormal);
void Call(Address target);
void Call(Register target);
void Call(const Code& code) { JumpAndLink(code); }
void CallCFunction(Address target) { Call(target); }
void CallCFunction(Register target) {
Call(target);
}
void AddImmediate(Register dest, intx_t imm) {
AddImmediate(dest, dest, imm);
}
void MulImmediate(Register dest,
intx_t imm,
OperandSize width = kWordBytes) override {
MulImmediate(dest, dest, imm, width);
}
void AddRegisters(Register dest, Register src) { add(dest, dest, src); }
// [dest] = [src] << [scale] + [value].
void AddScaled(Register dest,
Register src,
ScaleFactor scale,
int32_t value) {
if (scale == 0) {
AddImmediate(dest, src, value);
} else {
slli(dest, src, scale);
AddImmediate(dest, dest, value);
}
}
void AddShifted(Register dest, Register base, Register index, intx_t shift);
void SubRegisters(Register dest, Register src) {
sub(dest, dest, src);
}
// Macros accepting a pp Register argument may attempt to load values from
// the object pool when possible. Unless you are sure that the untagged object
// pool pointer is in another register, or that it is not available at all,
// PP should be passed for pp. `dest` can be TMP2, `rn` cannot. `dest` can be
// TMP.
void AddImmediate(Register dest,
Register rn,
intx_t imm,
OperandSize sz = kWordBytes);
void MulImmediate(Register dest,
Register rn,
intx_t imm,
OperandSize width = kWordBytes);
void AndImmediate(Register rd,
Register rn,
intx_t imm,
OperandSize sz = kWordBytes);
void AndImmediate(Register rd, intx_t imm) override {
AndImmediate(rd, rd, imm);
}
void AndRegisters(Register dst,
Register src1,
Register src2 = kNoRegister) override {
ASSERT(src1 != src2); // Likely a mistake.
if (src2 == kNoRegister) {
src2 = dst;
}
and_(dst, src2, src1);
}
void OrImmediate(Register rd,
Register rn,
intx_t imm,
OperandSize sz = kWordBytes);
void OrImmediate(Register rd, intx_t imm) {
OrImmediate(rd, rd, imm);
}
void XorImmediate(Register rd,
Register rn,
intx_t imm,
OperandSize sz = kWordBytes);
void LslImmediate(Register rd, int32_t shift) {
slli(rd, rd, shift);
}
void LslRegister(Register dst, Register shift) override {
sll(dst, dst, shift);
}
void LsrImmediate(Register rd, int32_t shift) override {
srli(rd, rd, shift);
}
void TestImmediate(Register rn, intx_t imm, OperandSize sz = kWordBytes);
void CompareImmediate(Register rn,
intx_t imm,
OperandSize sz = kWordBytes) override;
Address PrepareLargeOffset(Register base, int32_t offset);
void LoadFromOffset(Register dest,
const Address& address,
OperandSize sz = kWordBytes) override;
void LoadFromOffset(Register dest,
Register base,
int32_t offset,
OperandSize sz = kWordBytes) {
LoadFromOffset(dest, Address(base, offset), sz);
}
void LoadFieldFromOffset(Register dest,
Register base,
int32_t offset,
OperandSize sz = kWordBytes) override {
LoadFromOffset(dest, base, offset - kHeapObjectTag, sz);
}
void LoadCompressedFieldFromOffset(Register dest,
Register base,
int32_t offset) override {
LoadCompressedFromOffset(dest, base, offset - kHeapObjectTag);
}
void LoadCompressedSmiFieldFromOffset(Register dest,
Register base,
int32_t offset) {
LoadCompressedSmiFromOffset(dest, base, offset - kHeapObjectTag);
}
// For loading indexed payloads out of tagged objects like Arrays. If the
// payload objects are word-sized, use TIMES_HALF_WORD_SIZE if the contents of
// [index] is a Smi, otherwise TIMES_WORD_SIZE if unboxed.
void LoadIndexedPayload(Register dest,
Register base,
int32_t payload_offset,
Register index,
ScaleFactor scale,
OperandSize sz = kWordBytes);
void LoadIndexedCompressed(Register dest,
Register base,
int32_t offset,
Register index);
void LoadSFromOffset(FRegister dest, Register base, int32_t offset);
void LoadDFromOffset(FRegister dest, Register base, int32_t offset);
void LoadSFieldFromOffset(FRegister dest, Register base, int32_t offset) {
LoadSFromOffset(dest, base, offset - kHeapObjectTag);
}
void LoadDFieldFromOffset(FRegister dest, Register base, int32_t offset) {
LoadDFromOffset(dest, base, offset - kHeapObjectTag);
}
void LoadFromStack(Register dst, intptr_t depth);
void StoreToStack(Register src, intptr_t depth);
void CompareToStack(Register src, intptr_t depth);
void StoreToOffset(Register src,
const Address& address,
OperandSize sz = kWordBytes) override;
void StoreToOffset(Register src,
Register base,
int32_t offset,
OperandSize sz = kWordBytes) {
StoreToOffset(src, Address(base, offset), sz);
}
void StoreFieldToOffset(Register src,
Register base,
int32_t offset,
OperandSize sz = kWordBytes) {
StoreToOffset(src, FieldAddress(base, offset), sz);
}
void StoreZero(const Address& address, Register temp = kNoRegister) {
StoreToOffset(ZR, address);
}
void StoreSToOffset(FRegister src, Register base, int32_t offset);
void StoreSFieldToOffset(FRegister src, Register base, int32_t offset) {
StoreSToOffset(src, base, offset - kHeapObjectTag);
}
void StoreDToOffset(FRegister src, Register base, int32_t offset);
void StoreDFieldToOffset(FRegister src, Register base, int32_t offset) {
StoreDToOffset(src, base, offset - kHeapObjectTag);
}
void LoadUnboxedDouble(FpuRegister dst, Register base, int32_t offset) {
LoadDFromOffset(dst, base, offset);
}
void StoreUnboxedDouble(FpuRegister src, Register base, int32_t offset) {
StoreDToOffset(src, base, offset);
}
void MoveUnboxedDouble(FpuRegister dst, FpuRegister src) {
fmvd(dst, src);
}
void LoadUnboxedSimd128(FpuRegister dst, Register base, int32_t offset) {
// No single register SIMD on RISC-V.
UNREACHABLE();
}
void StoreUnboxedSimd128(FpuRegister src, Register base, int32_t offset) {
// No single register SIMD on RISC-V.
UNREACHABLE();
}
void MoveUnboxedSimd128(FpuRegister dst, FpuRegister src) {
// No single register SIMD on RISC-V.
UNREACHABLE();
}
void LoadCompressed(Register dest, const Address& slot) {
LoadFromOffset(dest, slot);
}
void LoadCompressedFromOffset(Register dest, Register base, int32_t offset) {
LoadFromOffset(dest, base, offset);
}
void LoadCompressedSmi(Register dest, const Address& slot) override {
LoadFromOffset(dest, slot);
#if defined(DEBUG)
Label done;
BranchIfSmi(dest, &done, kNearJump);
Stop("Expected Smi");
Bind(&done);
#endif
}
void LoadCompressedSmiFromOffset(Register dest,
Register base,
int32_t offset) {
LoadFromOffset(dest, base, offset);
}
// Store into a heap object and apply the generational and incremental write
// barriers. All stores into heap objects must pass through this function or,
// if the value can be proven either Smi or old-and-premarked, its NoBarrier
// variants.
// Preserves object and value registers.
void StoreIntoObject(Register object,
const Address& dest,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi,
MemoryOrder memory_order = kRelaxedNonAtomic) override;
void StoreCompressedIntoObject(
Register object,
const Address& dest,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi,
MemoryOrder memory_order = kRelaxedNonAtomic) override;
void StoreBarrier(Register object, Register value, CanBeSmi can_value_be_smi);
void StoreIntoArray(Register object,
Register slot,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi);
void StoreCompressedIntoArray(Register object,
Register slot,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi);
void StoreIntoArrayBarrier(Register object,
Register slot,
Register value,
CanBeSmi can_value_be_smi);
void StoreIntoObjectOffset(Register object,
int32_t offset,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreCompressedIntoObjectOffset(
Register object,
int32_t offset,
Register value,
CanBeSmi can_value_be_smi = kValueCanBeSmi,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreIntoObjectNoBarrier(
Register object,
const Address& dest,
Register value,
MemoryOrder memory_order = kRelaxedNonAtomic) override;
void StoreCompressedIntoObjectNoBarrier(
Register object,
const Address& dest,
Register value,
MemoryOrder memory_order = kRelaxedNonAtomic) override;
void StoreIntoObjectOffsetNoBarrier(
Register object,
int32_t offset,
Register value,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreCompressedIntoObjectOffsetNoBarrier(
Register object,
int32_t offset,
Register value,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreIntoObjectNoBarrier(Register object,
const Address& dest,
const Object& value,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreCompressedIntoObjectNoBarrier(
Register object,
const Address& dest,
const Object& value,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreIntoObjectOffsetNoBarrier(
Register object,
int32_t offset,
const Object& value,
MemoryOrder memory_order = kRelaxedNonAtomic);
void StoreCompressedIntoObjectOffsetNoBarrier(
Register object,
int32_t offset,
const Object& value,
MemoryOrder memory_order = kRelaxedNonAtomic);
// Stores a non-tagged value into a heap object.
void StoreInternalPointer(Register object,
const Address& dest,
Register value);
// Object pool, loading from pool, etc.
void LoadPoolPointer(Register pp = PP);
bool constant_pool_allowed() const { return constant_pool_allowed_; }
void set_constant_pool_allowed(bool b) { constant_pool_allowed_ = b; }
bool CanLoadFromObjectPool(const Object& object) const;
void LoadNativeEntry(Register dst,
const ExternalLabel* label,
ObjectPoolBuilderEntry::Patchability patchable);
void LoadIsolate(Register dst);
void LoadIsolateGroup(Register dst);
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadObject(Register dst, const Object& obj) {
LoadObjectHelper(dst, obj, false);
}
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadUniqueObject(
Register dst,
const Object& obj,
ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior =
ObjectPoolBuilderEntry::kSnapshotable) {
LoadObjectHelper(dst, obj, true, snapshot_behavior);
}
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadImmediate(Register reg, intx_t imm);
void LoadSImmediate(FRegister reg, float imms);
void LoadDImmediate(FRegister reg, double immd);
void LoadQImmediate(FRegister reg, simd128_value_t immq);
// Load word from pool from the given offset using encoding that
// InstructionPattern::DecodeLoadWordFromPool can decode.
//
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadWordFromPoolIndex(Register dst, intptr_t index, Register pp = PP);
// Store word to pool at the given offset.
//
// Note: clobbers TMP, does not clobber TMP2.
void StoreWordToPoolIndex(Register src, intptr_t index, Register pp = PP);
void PushObject(const Object& object) {
if (IsSameObject(compiler::NullObject(), object)) {
PushRegister(NULL_REG);
} else if (target::IsSmi(object) && (target::ToRawSmi(object) == 0)) {
PushRegister(ZR);
} else {
LoadObject(TMP, object);
PushRegister(TMP);
}
}
void PushImmediate(int64_t immediate) {
if (immediate == 0) {
PushRegister(ZR);
} else {
LoadImmediate(TMP, immediate);
PushRegister(TMP);
}
}
void CompareObject(Register reg, const Object& object);
void ExtractClassIdFromTags(Register result, Register tags);
void ExtractInstanceSizeFromTags(Register result, Register tags);
void RangeCheck(Register value,
Register temp,
intptr_t low,
intptr_t high,
RangeCheckCondition condition,
Label* target) override;
void LoadClassId(Register result, Register object);
void LoadClassById(Register result, Register class_id);
void CompareClassId(Register object,
intptr_t class_id,
Register scratch = kNoRegister);
// Note: input and output registers must be different.
void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
void EnsureHasClassIdInDEBUG(intptr_t cid,
Register src,
Register scratch,
bool can_be_null = false) override;
void EnterFrame(intptr_t frame_size);
void LeaveFrame();
void Ret() { ret(); }
// Sets the return address to [value] as if there was a call.
// On RISC-V sets RA.
void SetReturnAddress(Register value) {
mv(RA, value);
}
// Emit code to transition between generated mode and native mode.
//
// These require and ensure that CSP and SP are equal and aligned and require
// a scratch register (in addition to TMP/TMP2).
void TransitionGeneratedToNative(Register destination_address,
Register new_exit_frame,
Register new_exit_through_ffi,
bool enter_safepoint);
void TransitionNativeToGenerated(Register scratch,
bool exit_safepoint,
bool ignore_unwind_in_progress = false);
void EnterFullSafepoint(Register scratch);
void ExitFullSafepoint(Register scratch, bool ignore_unwind_in_progress);
void CheckFpSpDist(intptr_t fp_sp_dist);
void CheckCodePointer();
void RestoreCodePointer();
void RestorePoolPointer();
// Restores the values of the registers that are blocked to cache some values
// e.g. WRITE_BARRIER_STATE and NULL_REG.
void RestorePinnedRegisters();
void SetupGlobalPoolAndDispatchTable();
void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
void EnterOsrFrame(intptr_t extra_size, Register new_pp = kNoRegister);
void LeaveDartFrame();
void LeaveDartFrame(intptr_t fp_sp_dist);
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
// Set up a stub frame so that the stack traversal code can easily identify
// a stub frame.
void EnterStubFrame() { EnterDartFrame(0); }
void LeaveStubFrame() { LeaveDartFrame(); }
// Set up a frame for calling a C function.
// Automatically save the pinned registers in Dart which are not callee-
// saved in the native calling convention.
// Use together with CallCFunction.
void EnterCFrame(intptr_t frame_space);
void LeaveCFrame();
void MonomorphicCheckedEntryJIT();
void MonomorphicCheckedEntryAOT();
void BranchOnMonomorphicCheckedEntryJIT(Label* label);
void CombineHashes(Register dst, Register other) override;
void FinalizeHashForSize(intptr_t bit_size,
Register dst,
Register scratch = TMP) override;
// If allocation tracing for |cid| is enabled, will jump to |trace| label,
// which will allocate in the runtime where tracing occurs.
void MaybeTraceAllocation(intptr_t cid,
Label* trace,
Register temp_reg,
JumpDistance distance = JumpDistance::kFarJump);
void TryAllocateObject(intptr_t cid,
intptr_t instance_size,
Label* failure,
JumpDistance distance,
Register instance_reg,
Register temp_reg) override;
void TryAllocateArray(intptr_t cid,
intptr_t instance_size,
Label* failure,
Register instance,
Register end_address,
Register temp1,
Register temp2);
void CheckAllocationCanary(Register top, Register tmp = TMP) {
#if defined(DEBUG)
Label okay;
lx(tmp, Address(top, 0));
subi(tmp, tmp, kAllocationCanary);
beqz(tmp, &okay, Assembler::kNearJump);
Stop("Allocation canary");
Bind(&okay);
#endif
}
void WriteAllocationCanary(Register top) {
#if defined(DEBUG)
ASSERT(top != TMP);
li(TMP, kAllocationCanary);
sx(TMP, Address(top, 0));
#endif
}
// Copy [size] bytes from [src] address to [dst] address.
// [size] should be a multiple of word size.
// Clobbers [src], [dst], [size] and [temp] registers.
void CopyMemoryWords(Register src,
Register dst,
Register size,
Register temp);
// This emits an PC-relative call of the form "bl <offset>". The offset
// is not yet known and needs therefore relocation to the right place before
// the code can be used.
//
// The necessary information for the "linker" (i.e. the relocation
// information) is stored in [UntaggedCode::static_calls_target_table_]: an
// entry of the form
//
// (Code::kPcRelativeCall & pc_offset, <target-code>, <target-function>)
//
// will be used during relocation to fix the offset.
//
// The provided [offset_into_target] will be added to calculate the final
// destination. It can be used e.g. for calling into the middle of a
// function.
void GenerateUnRelocatedPcRelativeCall(intptr_t offset_into_target = 0);
// This emits an PC-relative tail call of the form "b <offset>".
//
// See also above for the pc-relative call.
void GenerateUnRelocatedPcRelativeTailCall(intptr_t offset_into_target = 0);
Address ElementAddressForIntIndex(bool is_external,
intptr_t cid,
intptr_t index_scale,
Register array,
intptr_t index) const;
void ComputeElementAddressForIntIndex(Register address,
bool is_external,
intptr_t cid,
intptr_t index_scale,
Register array,
intptr_t index);
Address ElementAddressForRegIndex(bool is_external,
intptr_t cid,
intptr_t index_scale,
bool index_unboxed,
Register array,
Register index,
Register temp);
// Special version of ElementAddressForRegIndex for the case when cid and
// operand size for the target load don't match (e.g. when loading a few
// elements of the array with one load).
Address ElementAddressForRegIndexWithSize(bool is_external,
intptr_t cid,
OperandSize size,
intptr_t index_scale,
bool index_unboxed,
Register array,
Register index,
Register temp);
void ComputeElementAddressForRegIndex(Register address,
bool is_external,
intptr_t cid,
intptr_t index_scale,
bool index_unboxed,
Register array,
Register index);
void LoadStaticFieldAddress(Register address,
Register field,
Register scratch);
void LoadCompressedFieldAddressForRegOffset(Register address,
Register instance,
Register offset_in_words_as_smi);
void LoadFieldAddressForRegOffset(Register address,
Register instance,
Register offset_in_words_as_smi);
void LoadFieldAddressForOffset(Register address,
Register instance,
int32_t offset) override {
AddImmediate(address, instance, offset - kHeapObjectTag);
}
// Returns object data offset for address calculation; for heap objects also
// accounts for the tag.
static int32_t HeapDataOffset(bool is_external, intptr_t cid) {
return is_external
? 0
: (target::Instance::DataOffsetFor(cid) - kHeapObjectTag);
}
void AddImmediateBranchOverflow(Register rd,
Register rs1,
intx_t imm,
Label* overflow);
void SubtractImmediateBranchOverflow(Register rd,
Register rs1,
intx_t imm,
Label* overflow);
void MultiplyImmediateBranchOverflow(Register rd,
Register rs1,
intx_t imm,
Label* overflow);
void AddBranchOverflow(Register rd,
Register rs1,
Register rs2,
Label* overflow);
void SubtractBranchOverflow(Register rd,
Register rs1,
Register rs2,
Label* overflow);
void MultiplyBranchOverflow(Register rd,
Register rs1,
Register rs2,
Label* overflow);
// Clobbers [rs].
void CountLeadingZeroes(Register rd, Register rs);
private:
bool constant_pool_allowed_;
enum DeferredCompareType {
kNone,
kCompareReg,
kCompareImm,
kTestReg,
kTestImm,
};
DeferredCompareType deferred_compare_ = kNone;
Register deferred_left_ = kNoRegister;
Register deferred_reg_ = kNoRegister;
intptr_t deferred_imm_ = 0;
// Note: the function never clobbers TMP, TMP2 scratch registers.
void LoadObjectHelper(
Register dst,
const Object& obj,
bool is_unique,
ObjectPoolBuilderEntry::SnapshotBehavior snapshot_behavior =
ObjectPoolBuilderEntry::kSnapshotable);
void JumpAndLink(intptr_t target_code_pool_index, CodeEntryKind entry_kind);
friend class dart::FlowGraphCompiler;
std::function<void(Register reg)> generate_invoke_write_barrier_wrapper_;
std::function<void()> generate_invoke_array_write_barrier_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(Assembler);
};
} // namespace compiler
} // namespace dart
#endif // RUNTIME_VM_COMPILER_ASSEMBLER_ASSEMBLER_RISCV_H_