8085a97a63
Instead of handling FFI related execution state and safepoint in assembly handle it in runtime code. The transition needs to be done before JumpToFrame unwinds stack because unwinding destroys exit frame and this can't be done at safepoint as GC might be traversing the stack. An incorrect order of operation was manifesting as crashes in GC when one isolate in a group was encountering a lot of exceptions thrown from an FFI call and another isolate is triggering GCs. To catch this in the future added a bit of validation to ExitSafepoint runtime call which triggers when --use-slow-path is enabled. Though after refactoring this code does not trigger this code path anymore because it was completely removed - but it is better than nothing. This CL also removes a lot of unnecessary complexity which was associated with handling this transition in the stub itself. TEST=ffi/vmspecific_handle_test Bug: b/408377905 Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-mac-debug-simarm64_arm64-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try Change-Id: Ia073cb6bb9e1b5a0ea8514c7e048cee6019b84d6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420324 Commit-Queue: Slava Egorov <vegorov@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com>
1697 lines
62 KiB
C++
1697 lines
62 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
|
|
ExtensionSet extensions() const { return extensions_; }
|
|
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 zextb(Register rd, Register rs) { andi(rd, rs, 0xFF); }
|
|
|
|
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);
|
|
|
|
void zextw(Register rd, Register rs) { adduw(rd, rs, ZR); }
|
|
|
|
// ==== 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);
|
|
|
|
// ==== Zicond: Integer conditional operations ====
|
|
// rd := rs2 == 0 ? 0 : rs1
|
|
void czeroeqz(Register rd, Register rs1, Register rs2);
|
|
// rd := rs2 != 0 ? 0 : rs1
|
|
void czeronez(Register rd, Register rs1, Register rs2);
|
|
|
|
// ==== Zabha: Byte and halfword AMOs ====
|
|
void amoswapb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoaddb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoxorb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoandb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoorb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amominb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amomaxb(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amominub(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amomaxub(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoswaph(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoaddh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoxorh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoandh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amoorh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amominh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amomaxh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amominuh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
void amomaxuh(Register rd,
|
|
Register rs2,
|
|
Address addr,
|
|
std::memory_order order = std::memory_order_relaxed);
|
|
|
|
// ==== Zfa: Additional floating-point instructions ====
|
|
void flis(FRegister rd, intptr_t index);
|
|
void flid(FRegister rd, intptr_t index);
|
|
void fminms(FRegister rd, FRegister rs1, FRegister rs2);
|
|
void fmaxms(FRegister rd, FRegister rs1, FRegister rs2);
|
|
void fminmd(FRegister rd, FRegister rs1, FRegister rs2);
|
|
void fmaxmd(FRegister rd, FRegister rs1, FRegister rs2);
|
|
void frounds(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
|
|
void froundd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
|
|
void froundnxs(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
|
|
void froundnxd(FRegister rd, FRegister rs1, RoundingMode rounding = RNE);
|
|
void fcvtmodwd(Register rd, FRegister rs1);
|
|
#if XLEN == 32
|
|
void fmvhxd(Register rd, FRegister rs1);
|
|
void fmvpdx(FRegister rd, Register rs1, Register rs2);
|
|
#endif
|
|
void fltqs(Register rd, FRegister rs1, FRegister rs2);
|
|
void fleqs(Register rd, FRegister rs1, FRegister rs2);
|
|
void fltqd(Register rd, FRegister rs1, FRegister rs2);
|
|
void fleqd(Register rd, FRegister rs1, FRegister rs2);
|
|
|
|
// ==== Zalasr: Load-acquire, store-release ====
|
|
void lb(Register rd, Address addr, std::memory_order order);
|
|
void lh(Register rd, Address addr, std::memory_order order);
|
|
void lw(Register rd, Address addr, std::memory_order order);
|
|
void sb(Register rs2, Address addr, std::memory_order order);
|
|
void sh(Register rs2, Address addr, std::memory_order order);
|
|
void sw(Register rs2, Address addr, std::memory_order order);
|
|
|
|
#if XLEN >= 64
|
|
void ld(Register rd, Address addr, std::memory_order order);
|
|
void sd(Register rs2, Address addr, std::memory_order order);
|
|
#endif
|
|
|
|
// ==== 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();
|
|
|
|
// ==== Zcb: Additional code-size saving instructions ====
|
|
void c_lbu(Register rd, Address addr);
|
|
void c_lh(Register rd, Address addr);
|
|
void c_lhu(Register rd, Address addr);
|
|
void c_sb(Register rs2, Address addr);
|
|
void c_sh(Register rs2, Address addr);
|
|
void c_zextb(Register rd, Register rs1);
|
|
void c_sextb(Register rd, Register rs1);
|
|
void c_zexth(Register rd, Register rs1);
|
|
void c_sexth(Register rd, Register rs1);
|
|
#if XLEN >= 64
|
|
void c_zextw(Register rd, Register rs1);
|
|
#endif
|
|
void c_mul(Register rd, Register rs1, Register rs2);
|
|
void c_not(Register rd, Register rs1);
|
|
|
|
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,
|
|
Register rs2,
|
|
Register rs1,
|
|
RoundingMode round,
|
|
FRegister rd,
|
|
Opcode opcode);
|
|
void EmitRType(Funct7 funct7,
|
|
Register 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) override { 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 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);
|
|
}
|
|
|
|
void TsanLoadAcquire(Register addr);
|
|
void TsanStoreRelease(Register addr);
|
|
|
|
void LoadAcquire(Register dst,
|
|
const Address& address,
|
|
OperandSize size = kWordBytes) override;
|
|
|
|
void StoreRelease(Register src,
|
|
const Address& address,
|
|
OperandSize size = kWordBytes) override;
|
|
|
|
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 dst,
|
|
Register src,
|
|
int32_t shift,
|
|
OperandSize sz = kWordBytes) override;
|
|
void ArithmeticShiftRightImmediate(Register reg,
|
|
int32_t shift,
|
|
OperandSize sz = kWordBytes) override {
|
|
ArithmeticShiftRightImmediate(reg, reg, shift);
|
|
}
|
|
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); }
|
|
void AddScaled(Register dest,
|
|
Register base,
|
|
Register index,
|
|
ScaleFactor scale,
|
|
int32_t disp) override {
|
|
if (base == kNoRegister || base == ZR) {
|
|
if (scale == TIMES_1) {
|
|
AddImmediate(dest, index, disp);
|
|
} else {
|
|
slli(dest, index, scale);
|
|
AddImmediate(dest, disp);
|
|
}
|
|
} else {
|
|
AddShifted(dest, base, index, scale);
|
|
AddImmediate(dest, disp);
|
|
}
|
|
}
|
|
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) override;
|
|
void AndImmediate(Register rd,
|
|
intx_t imm,
|
|
OperandSize sz = kWordBytes) override {
|
|
AndImmediate(rd, rd, imm, sz);
|
|
}
|
|
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,
|
|
Register rn,
|
|
int32_t shift,
|
|
OperandSize sz = kWordBytes) override;
|
|
void LslImmediate(Register rd,
|
|
int32_t shift,
|
|
OperandSize sz = kWordBytes) override {
|
|
LslImmediate(rd, rd, shift, sz);
|
|
}
|
|
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);
|
|
Address PrepareAtomicOffset(Register base, int32_t offset);
|
|
void Load(Register dest,
|
|
const Address& address,
|
|
OperandSize sz = kWordBytes) override;
|
|
// 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) override;
|
|
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 Store(Register src,
|
|
const Address& address,
|
|
OperandSize sz = kWordBytes) override;
|
|
void StoreZero(const Address& address, Register temp = kNoRegister) {
|
|
Store(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 InitializeHeader(Register tags, Register object) {
|
|
sx(tags, FieldAddress(object, target::Object::tags_offset()));
|
|
#if defined(TARGET_HAS_FAST_WRITE_WRITE_FENCE)
|
|
fence(kWrite, kWrite);
|
|
#endif
|
|
}
|
|
void InitializeHeaderUntagged(Register tags, Register object) {
|
|
sx(tags, Address(object, target::Object::tags_offset()));
|
|
#if defined(TARGET_HAS_FAST_WRITE_WRITE_FENCE)
|
|
fence(kWrite, kWrite);
|
|
#endif
|
|
}
|
|
|
|
void StoreBarrier(Register object,
|
|
Register value,
|
|
CanBeSmi can_value_be_smi,
|
|
Register scratch) override;
|
|
void ArrayStoreBarrier(Register object,
|
|
Register slot,
|
|
Register value,
|
|
CanBeSmi can_value_be_smi,
|
|
Register scratch) override;
|
|
void VerifyStoreNeedsNoWriteBarrier(Register object, Register value) override;
|
|
|
|
void StoreObjectIntoObjectNoBarrier(
|
|
Register object,
|
|
const Address& dest,
|
|
const Object& value,
|
|
MemoryOrder memory_order = kRelaxedNonAtomic,
|
|
OperandSize size = kWordBytes) override;
|
|
|
|
// 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) override;
|
|
|
|
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 set_tag = true);
|
|
void VerifyInGenerated(Register scratch);
|
|
void VerifyNotInGenerated(Register scratch);
|
|
void EnterFullSafepoint(Register scratch);
|
|
void ExitFullSafepoint(Register scratch);
|
|
|
|
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 MaybeTraceAllocation(Register 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);
|
|
|
|
static bool AddressCanHoldConstantIndex(const Object& constant,
|
|
bool is_external,
|
|
intptr_t cid,
|
|
intptr_t index_scale);
|
|
|
|
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);
|
|
|
|
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,
|
|
bool is_shared);
|
|
|
|
void LoadFieldAddressForRegOffset(Register address,
|
|
Register instance,
|
|
Register offset_in_words_as_smi) override;
|
|
|
|
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_
|