// Copyright 2022 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef jit_riscv64_extension_Base_riscv_i_h_ #define jit_riscv64_extension_Base_riscv_i_h_ #include "mozilla/Assertions.h" #include #include "jit/riscv64/constant/Constant-riscv64.h" #include "jit/riscv64/extension/base-assembler-riscv.h" namespace js { namespace jit { class AssemblerRISCVI : public AssemblerRiscvBase { public: void lui(Register rd, int32_t imm20); void auipc(Register rd, int32_t imm20); // Jumps void jal(Register rd, int32_t imm20); void jalr(Register rd, Register rs1, int16_t imm12); // Branches void beq(Register rs1, Register rs2, int16_t imm12); void bne(Register rs1, Register rs2, int16_t imm12); void blt(Register rs1, Register rs2, int16_t imm12); void bge(Register rs1, Register rs2, int16_t imm12); void bltu(Register rs1, Register rs2, int16_t imm12); void bgeu(Register rs1, Register rs2, int16_t imm12); // Loads void lb(Register rd, Register rs1, int16_t imm12); void lh(Register rd, Register rs1, int16_t imm12); void lw(Register rd, Register rs1, int16_t imm12); void lbu(Register rd, Register rs1, int16_t imm12); void lhu(Register rd, Register rs1, int16_t imm12); // Stores void sb(Register source, Register base, int16_t imm12); void sh(Register source, Register base, int16_t imm12); void sw(Register source, Register base, int16_t imm12); // Arithmetic with immediate void addi(Register rd, Register rs1, int16_t imm12); void slti(Register rd, Register rs1, int16_t imm12); void sltiu(Register rd, Register rs1, int16_t imm12); void xori(Register rd, Register rs1, int16_t imm12); void ori(Register rd, Register rs1, int16_t imm12); void andi(Register rd, Register rs1, int16_t imm12); void slli(Register rd, Register rs1, uint8_t shamt); void srli(Register rd, Register rs1, uint8_t shamt); void srai(Register rd, Register rs1, uint8_t shamt); // Arithmetic 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); // Other pseudo instructions that are not part of RISCV pseudo assemly void nor(Register rd, Register rs, Register rt) { or_(rd, rs, rt); not_(rd, rd); } // Memory fences void fence(uint8_t pred, uint8_t succ); void fence_tso(); // Environment call / break void ecall(); void ebreak(); void sync() { fence(0b1111, 0b1111); } // This is a de facto standard (as set by GNU binutils) 32-bit unimplemented // instruction (i.e., it should always trap, if your implementation has // invalid instruction traps). void unimp(); static int JumpOffset(Instr instr); static int AuipcOffset(Instr instr); static int JalrOffset(Instr instr); static int LoadOffset(Instr instr); static int BranchOffset(Instr instr); static int BrachlongOffset(Instr auipc, Instr instr_I); static inline Instr SetBranchOffset(int32_t pos, int32_t target_pos, Instr instr) { int32_t imm = target_pos - pos; MOZ_ASSERT((imm & 1) == 0); MOZ_ASSERT(is_intn(imm, kBranchOffsetBits)); instr &= ~kBImm12Mask; int32_t imm12 = ((imm & 0x800) >> 4) | // bit 11 ((imm & 0x1e) << 7) | // bits 4-1 ((imm & 0x7e0) << 20) | // bits 10-5 ((imm & 0x1000) << 19); // bit 12 return instr | (imm12 & kBImm12Mask); } static inline Instr SetJalOffset(int32_t pos, int32_t target_pos, Instr instr) { MOZ_ASSERT(IsJal(instr)); int32_t imm = target_pos - pos; MOZ_ASSERT((imm & 1) == 0); MOZ_ASSERT(is_intn(imm, kJumpOffsetBits)); instr &= ~kImm20Mask; int32_t imm20 = (imm & 0xff000) | // bits 19-12 ((imm & 0x800) << 9) | // bit 11 ((imm & 0x7fe) << 20) | // bits 10-1 ((imm & 0x100000) << 11); // bit 20 return instr | (imm20 & kImm20Mask); } static inline Instr SetJalrOffset(int32_t offset, Instr instr) { MOZ_ASSERT(IsJalr(instr)); MOZ_ASSERT(is_int12(offset)); instr &= ~kImm12Mask; int32_t imm12 = offset << kImm12Shift; MOZ_ASSERT(IsJalr(instr | (imm12 & kImm12Mask))); MOZ_ASSERT(JalrOffset(instr | (imm12 & kImm12Mask)) == offset); return instr | (imm12 & kImm12Mask); } static inline Instr SetLoadOffset(int32_t offset, Instr instr) { #if JS_CODEGEN_RISCV64 MOZ_ASSERT(IsLd(instr)); #elif JS_CODEGEN_RISCV32 MOZ_ASSERT(IsLw(instr)); #endif MOZ_ASSERT(is_int12(offset)); instr &= ~kImm12Mask; int32_t imm12 = offset << kImm12Shift; return instr | (imm12 & kImm12Mask); } static inline Instr SetAuipcOffset(int32_t offset, Instr instr) { MOZ_ASSERT(IsAuipc(instr)); MOZ_ASSERT(is_int20(offset)); instr = (instr & ~kImm31_12Mask) | ((offset & kImm19_0Mask) << 12); return instr; } // Check if an instruction is a branch of some kind. static bool IsBranch(Instr instr); static bool IsNop(Instr instr); static bool IsJump(Instr instr); static bool IsJal(Instr instr); static bool IsJalr(Instr instr); static bool IsLui(Instr instr); static bool IsAuipc(Instr instr); static bool IsAddi(Instr instr); static bool IsOri(Instr instr); static bool IsSlli(Instr instr); static bool IsLw(Instr instr); inline int32_t branch_offset(Label* L) { return branch_offset_helper(L, OffsetSize::kOffset13); } inline int32_t jump_offset(Label* L) { return branch_offset_helper(L, OffsetSize::kOffset21); } // Branches void beq(Register rs1, Register rs2, Label* L) { beq(rs1, rs2, branch_offset(L)); } void bne(Register rs1, Register rs2, Label* L) { bne(rs1, rs2, branch_offset(L)); } void blt(Register rs1, Register rs2, Label* L) { blt(rs1, rs2, branch_offset(L)); } void bge(Register rs1, Register rs2, Label* L) { bge(rs1, rs2, branch_offset(L)); } void bltu(Register rs1, Register rs2, Label* L) { bltu(rs1, rs2, branch_offset(L)); } void bgeu(Register rs1, Register rs2, Label* L) { bgeu(rs1, rs2, branch_offset(L)); } void beqz(Register rs, int16_t imm13) { beq(rs, zero_reg, imm13); } void beqz(Register rs1, Label* L) { beqz(rs1, branch_offset(L)); } void bnez(Register rs, int16_t imm13) { bne(rs, zero_reg, imm13); } void bnez(Register rs1, Label* L) { bnez(rs1, branch_offset(L)); } void blez(Register rs, int16_t imm13) { bge(zero_reg, rs, imm13); } void blez(Register rs1, Label* L) { blez(rs1, branch_offset(L)); } void bgez(Register rs, int16_t imm13) { bge(rs, zero_reg, imm13); } void bgez(Register rs1, Label* L) { bgez(rs1, branch_offset(L)); } void bltz(Register rs, int16_t imm13) { blt(rs, zero_reg, imm13); } void bltz(Register rs1, Label* L) { bltz(rs1, branch_offset(L)); } void bgtz(Register rs, int16_t imm13) { blt(zero_reg, rs, imm13); } void bgtz(Register rs1, Label* L) { bgtz(rs1, branch_offset(L)); } void bgt(Register rs1, Register rs2, int16_t imm13) { blt(rs2, rs1, imm13); } void bgt(Register rs1, Register rs2, Label* L) { bgt(rs1, rs2, branch_offset(L)); } void ble(Register rs1, Register rs2, int16_t imm13) { bge(rs2, rs1, imm13); } void ble(Register rs1, Register rs2, Label* L) { ble(rs1, rs2, branch_offset(L)); } void bgtu(Register rs1, Register rs2, int16_t imm13) { bltu(rs2, rs1, imm13); } void bgtu(Register rs1, Register rs2, Label* L) { bgtu(rs1, rs2, branch_offset(L)); } void bleu(Register rs1, Register rs2, int16_t imm13) { bgeu(rs2, rs1, imm13); } void bleu(Register rs1, Register rs2, Label* L) { bleu(rs1, rs2, branch_offset(L)); } void j(int32_t imm21) { jal(zero_reg, imm21); } void j(Label* L) { j(jump_offset(L)); } void b(Label* L) { j(L); } void jal(int32_t imm21) { jal(ra, imm21); } void jal(Label* L) { jal(jump_offset(L)); } void jr(Register rs) { jalr(zero_reg, rs, 0); } void jr(Register rs, int32_t imm12) { jalr(zero_reg, rs, imm12); } void jalr(Register rs, int32_t imm12) { jalr(ra, rs, imm12); } void jalr(Register rs) { jalr(ra, rs, 0); } void call(int32_t offset) { auipc(ra, (offset >> 12) + ((offset & 0x800) >> 11)); jalr(ra, ra, offset << 20 >> 20); } 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, zero_reg, rs); } void seqz(Register rd, Register rs) { sltiu(rd, rs, 1); } void snez(Register rd, Register rs) { sltu(rd, zero_reg, rs); } void sltz(Register rd, Register rs) { slt(rd, rs, zero_reg); } void sgtz(Register rd, Register rs) { slt(rd, zero_reg, rs); } #if JS_CODEGEN_RISCV64 void lwu(Register rd, Register rs1, int16_t imm12); void ld(Register rd, Register rs1, int16_t imm12); void sd(Register source, Register base, int16_t imm12); void addiw(Register rd, Register rs1, int16_t imm12); void slliw(Register rd, Register rs1, uint8_t shamt); void srliw(Register rd, Register rs1, uint8_t shamt); void sraiw(Register rd, Register rs1, uint8_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, zero_reg, rs); } void sext_w(Register rd, Register rs) { addiw(rd, rs, 0); } static bool IsAddiw(Instr instr); static bool IsLd(Instr instr); #endif }; } // namespace jit } // namespace js #endif // jit_riscv64_extension_Base_riscv_I_h_