| // 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; | |
| }; | |
| #if XLEN == 32 | |
| #define XSUFFIX(inst) inst##w | |
| #elif XLEN == 64 | |
| #define XSUFFIX(inst) inst##d | |
| #elif XLEN == 128 | |
| #define XSUFFIX(inst) inst##q | |
| #endif | |
| // 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 lpad(intptr_t label = 0) { auipc(ZR, label); } | |
| 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 == 32 | |
| void lwu(Register rd, Address addr) { lw(rd, addr); } | |
| void addiw(Register rd, Register rs1, intptr_t imm) { addi(rd, rs1, imm); } | |
| void subiw(Register rd, Register rs1, intptr_t imm) { subi(rd, rs1, imm); } | |
| void slliw(Register rd, Register rs1, intptr_t shamt) { | |
| slli(rd, rs1, shamt); | |
| } | |
| void srliw(Register rd, Register rs1, intptr_t shamt) { | |
| srli(rd, rs1, shamt); | |
| } | |
| void sraiw(Register rd, Register rs1, intptr_t shamt) { | |
| srai(rd, rs1, shamt); | |
| } | |
| void addw(Register rd, Register rs1, Register rs2) { add(rd, rs1, rs2); } | |
| void subw(Register rd, Register rs1, Register rs2) { sub(rd, rs1, rs2); } | |
| void sllw(Register rd, Register rs1, Register rs2) { sll(rd, rs1, rs2); } | |
| void srlw(Register rd, Register rs1, Register rs2) { srl(rd, rs1, rs2); } | |
| void sraw(Register rd, Register rs1, Register rs2) { sra(rd, rs1, rs2); } | |
| void negw(Register rd, Register rs) { neg(rd, rs); } | |
| #else | |
| 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 | |
| void lx(Register rd, Address addr) { XSUFFIX(l)(rd, addr); } | |
| void sx(Register rs2, Address addr) { XSUFFIX(s)(rs2, addr); } | |
| // ==== 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 == 32 | |
| void mulw(Register rd, Register rs1, Register rs2) { mul(rd, rs1, rs2); } | |
| void divw(Register rd, Register rs1, Register rs2) { div(rd, rs1, rs2); } | |
| void divuw(Register rd, Register rs1, Register rs2) { divu(rd, rs1, rs2); } | |
| void remw(Register rd, Register rs1, Register rs2) { rem(rd, rs1, rs2); } | |
| void remuw(Register rd, Register rs1, Register rs2) { remu(rd, rs1, rs2); } | |
| #else | |
| 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 | |
| void lrx(Register rd, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed) { | |
| XSUFFIX(lr)(rd, addr, order); | |
| } | |
| #define AMOX(amo) \ | |
| void amo##x(Register rd, Register rs2, Address addr, \ | |
| std::memory_order order = std::memory_order_relaxed) { \ | |
| XSUFFIX(amo)(rd, rs2, addr, order); \ | |
| } | |
| AMOX(sc) | |
| AMOX(amoswap) | |
| AMOX(amoadd) | |
| AMOX(amoxor) | |
| AMOX(amoand) | |
| AMOX(amoor) | |
| AMOX(amomin) | |
| AMOX(amomax) | |
| AMOX(amominu) | |
| AMOX(amomaxu) | |
| AMOX(amocas) | |
| #undef AMOX | |
| // ==== 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); | |
| // ==== Zimop: May-be-operations ==== | |
| void mopr(intptr_t n, Register rd, Register rs1); | |
| void moprr(intptr_t n, Register rd, Register rs1, Register rs2); | |
| // ==== Zcmop: Compressed may-be-operations ==== | |
| void cmop(intptr_t n); | |
| // ==== Zicfiss: Shadow stack ==== | |
| void sspush(Register rs2); | |
| void sspopchk(Register rs1); | |
| void ssrdp(Register rd); | |
| void ssamoswapw(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| #if XLEN >= 64 | |
| void ssamoswapd(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| #endif // XLEN >= 64 | |
| // ==== RV32V: Vectors ==== | |
| void vsetvli(Register rd, | |
| Register rs1, | |
| ElementWidth sew, | |
| LengthMultiplier lmul, | |
| TailMode vta, | |
| MaskMode vma); | |
| void vle8v(VRegister vd, Address rs1, VectorMask vm = unmasked); | |
| void vle16v(VRegister vd, Address rs1, VectorMask vm = unmasked); | |
| void vle32v(VRegister vd, Address rs1, VectorMask vm = unmasked); | |
| void vle64v(VRegister vd, Address rs1, VectorMask vm = unmasked); | |
| void vse8v(VRegister vs3, Address rs1, VectorMask vm = unmasked); | |
| void vse16v(VRegister vs3, Address rs1, VectorMask vm = unmasked); | |
| void vse32v(VRegister vs3, Address rs1, VectorMask vm = unmasked); | |
| void vse64v(VRegister vs3, Address rs1, VectorMask vm = unmasked); | |
| void vmvvx(VRegister vd, Register rs1, VectorMask vm = unmasked); | |
| // ==== 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 | |
| void lx(Register rd, Address addr, std::memory_order o) { | |
| XSUFFIX(l)(rd, addr, o); | |
| } | |
| void sx(Register rs2, Address addr, std::memory_order o) { | |
| XSUFFIX(s)(rs2, addr, o); | |
| } | |
| // ==== Zacas: Compare-and-swap ==== | |
| void amocasb(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| void amocash(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| void amocasw(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| void amocasd(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| #if XLEN >= 64 | |
| void amocasq(Register rd, | |
| Register rs2, | |
| Address addr, | |
| std::memory_order order = std::memory_order_relaxed); | |
| #endif | |
| // ==== Zawrs: Wait on reservation set ==== | |
| void wrsnto(); | |
| void wrssto(); | |
| // ==== 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(Funct12 funct12, | |
| Register rs1, | |
| Funct3 funct3, | |
| Register 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 PushRegistersAligned(const RegisterSet& registers, intptr_t space); | |
| void PopRegistersAligned(const RegisterSet& registers, intptr_t space); | |
| 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 dst, const Address& address, OperandSize size); | |
| void TsanStoreRelease(Register src, const Address& address, OperandSize size); | |
| void TsanFuncEntry(bool preserve_registers = true); | |
| void TsanFuncExit(bool preserve_registers = true); | |
| 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 StoreStoreFence() override { fence(kWrite, kWrite); } | |
| 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); | |
| // rd <- cond ? 1 : 0 | |
| void SetIf(Condition condition, Register rd); | |
| // rd <- cond ? 0 : rs | |
| void ZeroIf(Condition condition, Register rd, Register rs); | |
| 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 LoadS(FRegister dest, const Address& address) { | |
| LoadSFromOffset(dest, address.base(), address.offset()); | |
| } | |
| void LoadD(FRegister dest, const Address& address) { | |
| LoadDFromOffset(dest, address.base(), address.offset()); | |
| } | |
| 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 StoreS(FRegister src, const Address& address) { | |
| StoreSToOffset(src, address.base(), address.offset()); | |
| } | |
| void StoreD(FRegister src, const Address& address) { | |
| StoreDToOffset(src, address.base(), address.offset()); | |
| } | |
| 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 ExtractBitField(Register dst, | |
| Register src, | |
| intptr_t low_bit, | |
| intptr_t width) override; | |
| 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, | |
| bool tsan_enter_exit = true); | |
| // 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); | |
| // 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_ |