Source code
Revision control
Copy as Markdown
Other Tools
// 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 <stdint.h>
#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<const uint16_t*>(this);
return FirstHalfWord == 0;
}
inline bool IsShortInstruction() const {
uint8_t FirstByte = *reinterpret_cast<const uint8_t*>(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<const ShortInstr*>(this));
}
return *reinterpret_cast<const Instr*>(this);
}
// Set the raw instruction bits to value.
inline void SetInstructionBits(Instr value) {
*reinterpret_cast<Instr*>(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<enum BaseOpcode>(
Bits(kBaseOpcodeShift + kBaseOpcodeBits - 1, kBaseOpcodeShift));
}
// Return the fields at their original place in the instruction encoding.
inline BaseOpcode BaseOpcodeFieldRaw() const {
return static_cast<enum BaseOpcode>(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<Instruction*>(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