// 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_base_Instruction_h #define jit_riscv64_base_Instruction_h #include "mozilla/Assertions.h" #include #include "jit/riscv64/base/Integer.h" #include "jit/riscv64/constant/Constant-riscv64.h" namespace js::jit { // On RISCV all instructions are 32 bits, except for RVC. using Instr = int32_t; using ShortInstr = int16_t; // ----------------------------------------------------------------------------- // Specific instructions, constants, and masks. // These constants are declared in assembler-riscv64.cc, as they use named // registers and other constants. // An Illegal instruction const Instr kIllegalInstr = 0; // All other bits are 0s (i.e., ecall) // An ECALL instruction, used for redirected real time call const Instr rtCallRedirInstr = SYSTEM; // All other bits are 0s (i.e., ecall) // An EBreak instruction, used for debugging and semi-hosting const Instr kBreakInstr = SYSTEM | 1 << kImm12Shift; // ebreak constexpr uint8_t kInstrSize = 4; constexpr uint8_t kShortInstrSize = 2; class InstructionBase { // Sign-extend a |len|-bits integer. static constexpr int32_t sext(int32_t x, uint32_t len) { MOZ_ASSERT(0 < len && len <= 32); return ((x << (32 - len)) >> (32 - len)); } // Zero-extend a |len|-bits integer. static constexpr uint32_t zext(uint32_t x, uint32_t len) { MOZ_ASSERT(0 < len && len <= 32); return ((x << (32 - len)) >> (32 - len)); } public: enum { // On RISC-V, PC cannot actually be directly accessed. We behave as if PC // was always the value of the current instruction being executed. kPCReadOffset = 0 }; // Instruction type. enum Type { kRType, kR4Type, // Special R4 for Q extension kIType, kSType, kBType, kUType, kJType, // C extension kCRType, kCIType, kCSSType, kCIWType, kCLType, kCSType, kCAType, kCBType, kCJType, // V extension kVType, kVLType, kVSType, kVAMOType, kVIVVType, kVFVVType, kVMVVType, kVIVIType, kVIVXType, kVFVFType, kVMVXType, kVSETType, kUnsupported = -1 }; inline bool IsIllegalInstruction() const { uint16_t FirstHalfWord = *reinterpret_cast(this); return FirstHalfWord == 0; } inline bool IsShortInstruction() const { uint8_t FirstByte = *reinterpret_cast(this); return (FirstByte & 0x03) <= C2; } inline uint8_t InstructionSize() const { return IsShortInstruction() ? kShortInstrSize : kInstrSize; } // Get the raw instruction bits. inline Instr InstructionBits() const { if (IsShortInstruction()) { return 0x0000FFFF & (*reinterpret_cast(this)); } return *reinterpret_cast(this); } // Set the raw instruction bits to value. inline void SetInstructionBits(Instr value) { *reinterpret_cast(this) = value; } // Read one particular bit out of the instruction bits. inline int Bit(int nr) const { return (InstructionBits() >> nr) & 1; } // Read a bit field out of the instruction bits. inline int Bits(int hi, int lo) const { return (InstructionBits() >> lo) & ((2U << (hi - lo)) - 1); } // Accessors for the different named fields used in the RISC-V encoding. inline BaseOpcode BaseOpcodeValue() const { return static_cast( Bits(kBaseOpcodeShift + kBaseOpcodeBits - 1, kBaseOpcodeShift)); } // Return the fields at their original place in the instruction encoding. inline BaseOpcode BaseOpcodeFieldRaw() const { return static_cast(InstructionBits() & kBaseOpcodeMask); } // Safe to call within R-type instructions inline int Funct7FieldRaw() const { return InstructionBits() & kFunct7Mask; } // Safe to call within R-type instructions inline int Funct6FieldRaw() const { return InstructionBits() & kFunct6Mask; } // Safe to call within R-, I-, S-, or B-type instructions inline int Funct3FieldRaw() const { return InstructionBits() & kFunct3Mask; } // Safe to call within R-, I-, S-, or B-type instructions inline int Rs1FieldRawNoAssert() const { return InstructionBits() & kRs1FieldMask; } // Safe to call within R-, S-, or B-type instructions inline int Rs2FieldRawNoAssert() const { return InstructionBits() & kRs2FieldMask; } // Safe to call within R4-type instructions inline int Rs3FieldRawNoAssert() const { return InstructionBits() & kRs3FieldMask; } inline int32_t ITypeBits() const { return InstructionBits() & kITypeMask; } inline int32_t InstructionOpcodeType() const { if (IsShortInstruction()) { return InstructionBits() & kRvcOpcodeMask; } return InstructionBits() & kBaseOpcodeMask; } // Get the encoding type of the instruction. Type InstructionType() const; OffsetSize GetOffsetSize() const; inline ImmBranchRangeType GetImmBranchRangeType() const { return OffsetSizeToImmBranchRangeType(GetOffsetSize()); } /// Getters // Say if the instruction is a break or a trap. inline bool IsTrap() const { return InstructionBits() == kBreakInstr; } // Check if the instruction is a branch of some kind. inline bool IsBranch() const { return BaseOpcode() == BRANCH; } inline bool IsJal() const { return BaseOpcode() == JAL; } inline bool IsJalr() const { return BaseOpcode() == JALR; } inline bool IsLui() const { return BaseOpcode() == LUI; } inline bool IsAuipc() const { return BaseOpcode() == AUIPC; } inline bool IsAddi() const { return (InstructionBits() & kITypeMask) == RO_ADDI; } inline bool IsOri() const { return (InstructionBits() & kITypeMask) == RO_ORI; } inline bool IsSlli() const { return (InstructionBits() & kITypeMask) == RO_SLLI; } inline bool IsLw() const { return (InstructionBits() & kITypeMask) == RO_LW; } inline bool IsLd() const { return (InstructionBits() & kITypeMask) == RO_LD; } inline bool IsAddiw() const { return (InstructionBits() & kITypeMask) == RO_ADDIW; } inline int BaseOpcode() const { return InstructionBits() & kBaseOpcodeMask; } inline int RvcOpcode() const { MOZ_ASSERT(IsShortInstruction()); return InstructionBits() & kRvcOpcodeMask; } inline int Rs1Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kBType || InstructionType() == InstructionBase::kVType); return Bits(kRs1Shift + kRs1Bits - 1, kRs1Shift); } inline int Rs2Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kBType || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kVType); return Bits(kRs2Shift + kRs2Bits - 1, kRs2Shift); } inline int Rs3Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kR4Type); return Bits(kRs3Shift + kRs3Bits - 1, kRs3Shift); } inline int Vs1Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType); return Bits(kVs1Shift + kVs1Bits - 1, kVs1Shift); } inline int Vs2Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType); return Bits(kVs2Shift + kVs2Bits - 1, kVs2Shift); } inline int VdValue() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType); return Bits(kVdShift + kVdBits - 1, kVdShift); } inline int RdValue() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kUType || InstructionType() == InstructionBase::kJType || InstructionType() == InstructionBase::kVType); return Bits(kRdShift + kRdBits - 1, kRdShift); } inline int RvcRs1Value() const { return RvcRdValue(); } inline int RvcRdValue() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcRdShift + kRvcRdBits - 1, kRvcRdShift); } inline int RvcRs2Value() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcRs2Shift + kRvcRs2Bits - 1, kRvcRs2Shift); } inline int RvcRs1sValue() const { MOZ_ASSERT(IsShortInstruction()); return 0b1000 + Bits(kRvcRs1sShift + kRvcRs1sBits - 1, kRvcRs1sShift); } inline int RvcRs2sValue() const { MOZ_ASSERT(IsShortInstruction()); return 0b1000 + Bits(kRvcRs2sShift + kRvcRs2sBits - 1, kRvcRs2sShift); } inline int Funct7Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType); return Bits(kFunct7Shift + kFunct7Bits - 1, kFunct7Shift); } inline int Funct2Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kR4Type); return Bits(kFunct2Shift + kFunct2Bits - 1, kFunct2Shift); } inline int Funct3Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kBType); return Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); } inline int Funct5Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kRType && BaseOpcode() == OP_FP); return Bits(kFunct5Shift + kFunct5Bits - 1, kFunct5Shift); } inline int RvcFunct6Value() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcFunct6Shift + kRvcFunct6Bits - 1, kRvcFunct6Shift); } inline int RvcFunct4Value() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcFunct4Shift + kRvcFunct4Bits - 1, kRvcFunct4Shift); } inline int RvcFunct3Value() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcFunct3Shift + kRvcFunct3Bits - 1, kRvcFunct3Shift); } inline int RvcFunct2Value() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcFunct2Shift + kRvcFunct2Bits - 1, kRvcFunct2Shift); } inline int RvcFunct2BValue() const { MOZ_ASSERT(IsShortInstruction()); return Bits(kRvcFunct2BShift + kRvcFunct2Bits - 1, kRvcFunct2BShift); } inline int CsrValue() const { MOZ_ASSERT(InstructionType() == InstructionBase::kIType && BaseOpcode() == SYSTEM); return Bits(kCsrShift + kCsrBits - 1, kCsrShift); } inline int RoundMode() const { MOZ_ASSERT((InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type) && BaseOpcode() == OP_FP); return Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); } inline int MemoryOrder(bool is_pred) const { MOZ_ASSERT(InstructionType() == InstructionBase::kIType && BaseOpcode() == MISC_MEM); if (is_pred) { return Bits(kPredOrderShift + kMemOrderBits - 1, kPredOrderShift); } return Bits(kSuccOrderShift + kMemOrderBits - 1, kSuccOrderShift); } inline int Imm12Value() const { MOZ_ASSERT(InstructionType() == InstructionBase::kIType); int Value = Bits(kImm12Shift + kImm12Bits - 1, kImm12Shift); return sext(Value, kImm12Bits); } inline int BranchOffset() const { MOZ_ASSERT(InstructionType() == InstructionBase::kBType); // | imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | // 31 25 11 7 uint32_t Bits = InstructionBits(); int16_t imm13 = ((Bits & 0xf00) >> 7) | ((Bits & 0x7e000000) >> 20) | ((Bits & 0x80) << 4) | ((Bits & 0x80000000) >> 19); return sext(imm13, 13); } inline int StoreOffset() const { MOZ_ASSERT(InstructionType() == InstructionBase::kSType); // | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | // 31 25 11 7 uint32_t Bits = InstructionBits(); int16_t imm12 = ((Bits & 0xf80) >> 7) | ((Bits & 0xfe000000) >> 20); return sext(imm12, 12); } inline int Imm20UValue() const { MOZ_ASSERT(InstructionType() == InstructionBase::kUType); // | imm[31:12] | rd | opcode | // 31 12 int32_t Bits = InstructionBits(); return Bits >> 12; } inline int Imm20JValue() const { MOZ_ASSERT(InstructionType() == InstructionBase::kJType); // | imm[20|10:1|11|19:12] | rd | opcode | // 31 12 uint32_t Bits = InstructionBits(); int32_t imm20 = ((Bits & 0x7fe00000) >> 20) | ((Bits & 0x100000) >> 9) | (Bits & 0xff000) | ((Bits & 0x80000000) >> 11); return sext(imm20, 20 + 1); // +1 b/c J immediates are shifted by 1 bit. } inline bool IsArithShift() const { // Valid only for right shift operations MOZ_ASSERT((BaseOpcode() == OP || BaseOpcode() == OP_32 || BaseOpcode() == OP_IMM || BaseOpcode() == OP_IMM_32) && Funct3Value() == 0b101); return InstructionBits() & 0x40000000; } inline int Shamt() const { // Valid only for shift instructions (SLLI, SRLI, SRAI) MOZ_ASSERT(((InstructionBits() & kBaseOpcodeMask) == OP_IMM || (InstructionBits() & kBaseOpcodeMask) == OP_IMM_32) && (Funct3Value() == 0b001 || Funct3Value() == 0b101)); // | 0A0000 | shamt | rs1 | funct3 | rd | opcode | // 31 25 20 return Bits(kImm12Shift + 5, kImm12Shift); } inline int Shamt32() const { // Valid only for shift instructions (SLLIW, SRLIW, SRAIW) MOZ_ASSERT((InstructionBits() & kBaseOpcodeMask) == OP_IMM_32 && (Funct3Value() == 0b001 || Funct3Value() == 0b101)); // | 0A00000 | shamt | rs1 | funct3 | rd | opcode | // 31 24 20 return Bits(kImm12Shift + 4, kImm12Shift); } inline int RvcImm6Value() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | imm[5] | rs1/rd | imm[4:0] | opcode | // 15 12 6 2 uint32_t Bits = InstructionBits(); int32_t imm6 = ((Bits & 0x1000) >> 7) | ((Bits & 0x7c) >> 2); return sext(imm6, 6); } inline int RvcImm6Addi16spValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | nzimm[9] | 2 | nzimm[4|6|8:7|5] | opcode | // 15 12 6 2 uint32_t Bits = InstructionBits(); int32_t imm10 = ((Bits & 0x1000) >> 3) | ((Bits & 0x40) >> 2) | ((Bits & 0x20) << 1) | ((Bits & 0x18) << 4) | ((Bits & 0x4) << 3); MOZ_ASSERT(imm10 != 0); return sext(imm10, 10); } inline int RvcImm8Addi4spnValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | nzimm[11] | rd' | opcode | // 15 13 5 2 uint32_t Bits = InstructionBits(); int32_t uimm10 = ((Bits & 0x20) >> 2) | ((Bits & 0x40) >> 4) | ((Bits & 0x780) >> 1) | ((Bits & 0x1800) >> 7); MOZ_ASSERT(uimm10 != 0); return uimm10; } inline int RvcShamt6() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | nzuimm[5] | rs1/rd | nzuimm[4:0] | opcode | // 15 12 6 2 int32_t imm6 = RvcImm6Value(); return imm6 & 0x3f; } inline int RvcImm6LwspValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | uimm[5] | rs1 | uimm[4:2|7:6] | opcode | // 15 12 6 2 uint32_t Bits = InstructionBits(); int32_t imm8 = ((Bits & 0x1000) >> 7) | ((Bits & 0x70) >> 2) | ((Bits & 0xc) << 4); return imm8; } inline int RvcImm6LdspValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | uimm[5] | rs1 | uimm[4:3|8:6] | opcode | // 15 12 6 2 uint32_t Bits = InstructionBits(); int32_t imm9 = ((Bits & 0x1000) >> 7) | ((Bits & 0x60) >> 2) | ((Bits & 0x1c) << 4); return imm9; } inline int RvcImm6SwspValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | uimm[5:2|7:6] | rs2 | opcode | // 15 12 7 uint32_t Bits = InstructionBits(); int32_t imm8 = ((Bits & 0x1e00) >> 7) | ((Bits & 0x180) >> 1); return imm8; } inline int RvcImm6SdspValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | uimm[5:3|8:6] | rs2 | opcode | // 15 12 7 uint32_t Bits = InstructionBits(); int32_t imm9 = ((Bits & 0x1c00) >> 7) | ((Bits & 0x380) >> 1); return imm9; } inline int RvcImm5WValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | imm[5:3] | rs1 | imm[2|6] | rd | opcode | // 15 12 10 6 4 2 uint32_t Bits = InstructionBits(); int32_t imm7 = ((Bits & 0x1c00) >> 7) | ((Bits & 0x40) >> 4) | ((Bits & 0x20) << 1); return imm7; } inline int RvcImm5DValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | imm[5:3] | rs1 | imm[7:6] | rd | opcode | // 15 12 10 6 4 2 uint32_t Bits = InstructionBits(); int32_t imm8 = ((Bits & 0x1c00) >> 7) | ((Bits & 0x60) << 1); return imm8; } inline int RvcImm11CJValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | [11|4|9:8|10|6|7|3:1|5] | opcode | // 15 12 2 uint32_t Bits = InstructionBits(); int32_t imm12 = ((Bits & 0x4) << 3) | ((Bits & 0x38) >> 2) | ((Bits & 0x40) << 1) | ((Bits & 0x80) >> 1) | ((Bits & 0x100) << 2) | ((Bits & 0x600) >> 1) | ((Bits & 0x800) >> 7) | ((Bits & 0x1000) >> 1); return sext(imm12, 12); } inline int RvcImm8BValue() const { MOZ_ASSERT(IsShortInstruction()); // | funct3 | imm[8|4:3] | rs1` | imm[7:6|2:1|5] | opcode | // 15 12 10 7 2 uint32_t Bits = InstructionBits(); int32_t imm9 = ((Bits & 0x4) << 3) | ((Bits & 0x18) >> 2) | ((Bits & 0x60) << 1) | ((Bits & 0xc00) >> 7) | ((Bits & 0x1000) >> 4); return sext(imm9, 9); } inline int vl_vs_width() { int width = 0; if ((InstructionBits() & kBaseOpcodeMask) != LOAD_FP && (InstructionBits() & kBaseOpcodeMask) != STORE_FP) { return -1; } switch (InstructionBits() & (kRvvWidthMask | kRvvMewMask)) { case 0x0: width = 8; break; case 0x00005000: width = 16; break; case 0x00006000: width = 32; break; case 0x00007000: width = 64; break; case 0x10000000: width = 128; break; case 0x10005000: width = 256; break; case 0x10006000: width = 512; break; case 0x10007000: width = 1024; break; default: width = -1; break; } return width; } inline uint32_t Rvvzimm() const { if ((InstructionBits() & (kBaseOpcodeMask | kFunct3Mask | 0x80000000)) == RO_V_VSETVLI) { uint32_t Bits = InstructionBits(); uint32_t zimm = Bits & kRvvZimmMask; return zimm >> kRvvZimmShift; } else { MOZ_ASSERT((InstructionBits() & (kBaseOpcodeMask | kFunct3Mask | 0xC0000000)) == RO_V_VSETIVLI); uint32_t Bits = InstructionBits(); uint32_t zimm = Bits & kRvvZimmMask; return (zimm >> kRvvZimmShift) & 0x3FF; } } inline uint32_t Rvvuimm() const { MOZ_ASSERT((InstructionBits() & (kBaseOpcodeMask | kFunct3Mask | 0xC0000000)) == RO_V_VSETIVLI); uint32_t Bits = InstructionBits(); uint32_t uimm = Bits & kRvvUimmMask; return uimm >> kRvvUimmShift; } inline uint32_t RvvVsew() const { uint32_t zimm = Rvvzimm(); uint32_t vsew = (zimm >> 3) & 0x7; return vsew; } inline uint32_t RvvVlmul() const { uint32_t zimm = Rvvzimm(); uint32_t vlmul = zimm & 0x7; return vlmul; } inline uint8_t RvvVM() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType); return Bits(kRvvVmShift + kRvvVmBits - 1, kRvvVmShift); } inline const char* RvvSEW() const { uint32_t vsew = RvvVsew(); switch (vsew) { #define CAST_VSEW(name) \ case name: \ return #name; RVV_SEW(CAST_VSEW) default: return "unknown"; #undef CAST_VSEW } } inline const char* RvvLMUL() const { uint32_t vlmul = RvvVlmul(); switch (vlmul) { #define CAST_VLMUL(name) \ case name: \ return #name; RVV_LMUL(CAST_VLMUL) default: return "unknown"; #undef CAST_VLMUL } } inline int32_t RvvSimm5() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType); return sext(Bits(kRvvImm5Shift + kRvvImm5Bits - 1, kRvvImm5Shift), kRvvImm5Bits); } inline uint32_t RvvUimm5() const { MOZ_ASSERT(InstructionType() == InstructionBase::kVType); uint32_t imm = Bits(kRvvImm5Shift + kRvvImm5Bits - 1, kRvvImm5Shift); return zext(imm, kRvvImm5Bits); } inline bool AqValue() const { return Bits(kAqShift, kAqShift); } inline bool RlValue() const { return Bits(kRlShift, kRlShift); } /// Setters inline void SetRdValue(int rd) { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kUType || InstructionType() == InstructionBase::kJType || InstructionType() == InstructionBase::kVType); MOZ_ASSERT(is_uintn(rd, kRdBits)); Instr bits = InstructionBits() & ~kRdFieldMask; SetInstructionBits((rd << kRdShift) | bits); } inline void SetRs1Value(int rs1) { MOZ_ASSERT(InstructionType() == InstructionBase::kRType || InstructionType() == InstructionBase::kR4Type || InstructionType() == InstructionBase::kIType || InstructionType() == InstructionBase::kSType || InstructionType() == InstructionBase::kBType || InstructionType() == InstructionBase::kVType); MOZ_ASSERT(is_uintn(rs1, kRs1Bits)); Instr bits = InstructionBits() & ~kRs1FieldMask; SetInstructionBits((rs1 << kRs1Shift) | bits); } inline void SetImm12Value(int32_t imm12) { MOZ_ASSERT(InstructionType() == InstructionBase::kIType); MOZ_ASSERT(is_uint12(imm12) || is_int12(imm12)); // | imm[11:0] | rs1 | funct3 | rd | opcode | // 31 20 Instr bits = InstructionBits() & ~kImm12Mask; SetInstructionBits((imm12 << kImm12Shift) | bits); } inline void SetBranchOffset(int32_t imm13) { MOZ_ASSERT(InstructionType() == InstructionBase::kBType); MOZ_ASSERT((imm13 & 1) == 0); MOZ_ASSERT(is_intn(imm13, kBranchOffsetBits)); // | imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | // 31 25 11 7 Instr bits = InstructionBits() & ~kBImm12Mask; int32_t imm12 = ((imm13 & 0x800) >> 4) | // bit 11 ((imm13 & 0x1e) << 7) | // bits 4-1 ((imm13 & 0x7e0) << 20) | // bits 10-5 ((imm13 & 0x1000) << 19); // bit 12 SetInstructionBits((imm12 & kBImm12Mask) | bits); } inline void SetImm20UValue(int32_t imm20) { MOZ_ASSERT(InstructionType() == InstructionBase::kUType); MOZ_ASSERT(is_int20(imm20) || is_uint20(imm20)); // | imm[31:12] | rd | opcode | // 31 12 Instr bits = InstructionBits() & ~kImm20Mask; SetInstructionBits((imm20 << kImm20Shift) | bits); } inline void SetImm20JValue(int32_t imm21) { MOZ_ASSERT(InstructionType() == InstructionBase::kJType); MOZ_ASSERT((imm21 & 1) == 0); MOZ_ASSERT(is_intn(imm21, kJumpOffsetBits)); // | imm[20|10:1|11|19:12] | rd | opcode | // 31 12 Instr bits = InstructionBits() & ~kImm20Mask; int32_t imm20 = (imm21 & 0xff000) | // bits 19-12 ((imm21 & 0x800) << 9) | // bit 11 ((imm21 & 0x7fe) << 20) | // bits 10-1 ((imm21 & 0x100000) << 11); // bit 20 SetInstructionBits((imm20 & kImm20Mask) | bits); } inline void SetShamt(int32_t shamt) { // Valid only for shift instructions (SLLI, SRLI, SRAI) MOZ_ASSERT(((InstructionBits() & kBaseOpcodeMask) == OP_IMM || (InstructionBits() & kBaseOpcodeMask) == OP_IMM_32) && (Funct3Value() == 0b001 || Funct3Value() == 0b101)); MOZ_ASSERT_IF((InstructionBits() & kBaseOpcodeMask) == OP_IMM, 0 <= shamt && shamt <= 63); MOZ_ASSERT_IF((InstructionBits() & kBaseOpcodeMask) == OP_IMM_32, 0 <= shamt && shamt <= 31); // SLLI, SRLI, SRAI: // | 0A0000 | shamt | rs1 | funct3 | rd | opcode | // 31 25 20 // // SLLIW, SRLIW, SRAIW: // | 0A00000 | shamt | rs1 | funct3 | rd | opcode | // 31 24 20 int32_t imm12 = ((InstructionBits() & 0x40000000) >> kImm12Shift) | shamt; SetImm12Value(imm12); } /// Compound setters void SetIFormat(OpcodeRISCV32I opcode, int rd, int rs1, int32_t imm12) { SetInstructionBits(opcode); MOZ_ASSERT(InstructionType() == kIType); SetRdValue(rd); SetRs1Value(rs1); SetImm12Value(imm12); } void SetJFormat(OpcodeRISCV32I opcode, int rd, int32_t imm21) { SetInstructionBits(opcode); MOZ_ASSERT(InstructionType() == kJType); SetRdValue(rd); SetImm20JValue(imm21); } void SetUFormat(OpcodeRISCV32I opcode, int rd, int32_t imm20) { SetInstructionBits(opcode); MOZ_ASSERT(InstructionType() == kUType); SetRdValue(rd); SetImm20UValue(imm20); } void SetNop() { SetInstructionBits(kNopByte); } protected: InstructionBase() {} }; class Instruction : public InstructionBase { public: // Instructions are read of out a code stream. The only way to get a // reference to an instruction is to convert a pointer. There is no way // to allocate or create instances of class Instruction. // Use the At(pc) function to create references to Instruction. static Instruction* At(uint8_t* pc) { return reinterpret_cast(pc); } // We need to prevent the creation of instances of class Instruction. Instruction() = delete; Instruction(const Instruction&) = delete; Instruction& operator=(const Instruction&) = delete; }; } // namespace js::jit #endif // jit_riscv64_base_Instruction_h