| /* |
| * Copyright (C) 2021 Igalia S.L. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(ASSEMBLER) && CPU(RISCV64) |
| |
| #include "AssemblerBuffer.h" |
| #include "RISCV64Registers.h" |
| #include <tuple> |
| |
| namespace JSC { |
| |
| namespace RISCV64Registers { |
| |
| typedef enum : int8_t { |
| #define REGISTER_ID(id, name, r, cs) id, |
| FOR_EACH_GP_REGISTER(REGISTER_ID) |
| #undef REGISTER_ID |
| |
| #define REGISTER_ALIAS(id, name, alias) id = alias, |
| FOR_EACH_REGISTER_ALIAS(REGISTER_ALIAS) |
| #undef REGISTER_ALIAS |
| |
| InvalidGPRReg = -1, |
| } RegisterID; |
| |
| typedef enum : int8_t { |
| #define REGISTER_ID(id, name) id, |
| FOR_EACH_SP_REGISTER(REGISTER_ID) |
| #undef REGISTER_ID |
| |
| InvalidSPReg = -1, |
| } SPRegisterID; |
| |
| typedef enum : int8_t { |
| #define REGISTER_ID(id, name, r, cs) id, |
| FOR_EACH_FP_REGISTER(REGISTER_ID) |
| #undef REGISTER_ID |
| |
| InvalidFPRReg = -1, |
| } FPRegisterID; |
| |
| } // namespace RISCV64Registers |
| |
| namespace RISCV64Instructions { |
| |
| enum class Opcode : unsigned { |
| LOAD = 0b0000011, |
| LOAD_FP = 0b0000111, |
| MISC_MEM = 0b0001111, |
| OP_IMM = 0b0010011, |
| AUIPC = 0b0010111, |
| OP_IMM_32 = 0b0011011, |
| STORE = 0b0100011, |
| STORE_FP = 0b0100111, |
| AMO = 0b0101111, |
| OP = 0b0110011, |
| LUI = 0b0110111, |
| OP_32 = 0b0111011, |
| MADD = 0b1000011, |
| MSUB = 0b1000111, |
| NMSUB = 0b1001011, |
| NMADD = 0b1001111, |
| OP_FP = 0b1010011, |
| BRANCH = 0b1100011, |
| JALR = 0b1100111, |
| JAL = 0b1101111, |
| SYSTEM = 0b1110011, |
| }; |
| |
| enum class FCVTType { |
| W, WU, |
| L, LU, |
| S, D, |
| }; |
| |
| enum class FMVType { |
| X, W, D, |
| }; |
| |
| enum class FPRoundingMode : unsigned { |
| RNE = 0b000, |
| RTZ = 0b001, |
| RDN = 0b010, |
| RUP = 0b011, |
| RMM = 0b100, |
| DYN = 0b111, |
| }; |
| |
| enum class MemoryOperation : uint8_t { |
| I = 1 << 3, |
| O = 1 << 2, |
| R = 1 << 1, |
| W = 1 << 0, |
| RW = R | W, |
| IORW = I | O | R | W, |
| }; |
| |
| enum class MemoryAccess : uint8_t { |
| Acquire = 1 << 1, |
| Release = 1 << 0, |
| AcquireRelease = Acquire | Release, |
| }; |
| |
| // Register helpers |
| |
| using RegisterID = RISCV64Registers::RegisterID; |
| using FPRegisterID = RISCV64Registers::FPRegisterID; |
| |
| template<typename T> |
| auto registerValue(T registerID) |
| -> std::enable_if_t<(std::is_same_v<T, RegisterID> || std::is_same_v<T, FPRegisterID>), unsigned> |
| { |
| return unsigned(registerID) & ((1 << 5) - 1); |
| } |
| |
| // InstructionValue contains the 32-bit instruction value and also provides access into the desired field. |
| |
| struct InstructionValue { |
| explicit InstructionValue(uint32_t value) |
| : value(value) |
| { } |
| |
| template<unsigned fieldStart, unsigned fieldSize> |
| uint32_t field() |
| { |
| static_assert(fieldStart + fieldSize <= (sizeof(uint32_t) * 8)); |
| return (value >> fieldStart) & ((1 << fieldSize) - 1); |
| } |
| |
| uint32_t opcode() { return field<0, 7>(); } |
| |
| uint32_t value; |
| }; |
| |
| // Immediate types |
| |
| // ImmediateBase acts as the base struct for the different types. The bit-size of the immediate is determined as the |
| // template parameter on the ImmediateBase struct. Internally, every immediate value is represented through a uint32_t |
| // from which the appropriate bit-sets are then copied into the target instruction. |
| |
| // ImmediateBase provides three ways to construct the target immediate (the type of which is specified as a template |
| // parameter to these construction methods): |
| // ImmediateBase<N>::v<ImmediateType, int32_t>() -- for constant immediates |
| // ImmediateBase<N>::v<ImmediateType>(int32_t/int64_t) -- for variable immediates whose values were validated beforehand |
| // ImmediateBase<N>::ImmediateBase(uint32_t) -- for immediate values already packed in the uint32_t format |
| |
| // There's also ImmediateType::value(InstructionValue) helpers that for a given instruction value retrieve the |
| // appropriate signed immediate value that was encoded in that instruction (except for the U-type immediate which is |
| // a 32-bit unsigned value). |
| |
| template<unsigned immediateSize> |
| struct ImmediateBase { |
| static_assert(immediateSize <= sizeof(uint32_t) * 8); |
| |
| template<typename T> |
| static auto isValid(T immValue) |
| -> std::enable_if_t<(std::is_same_v<T, int32_t> || std::is_same_v<T, int64_t>), bool> |
| { |
| constexpr unsigned shift = sizeof(T) * 8 - immediateSize; |
| return immValue == ((immValue << shift) >> shift); |
| } |
| |
| template<typename ImmediateType, int32_t immValue> |
| static ImmediateType v() |
| { |
| static_assert((-(1 << (immediateSize - 1)) <= immValue) && (immValue <= ((1 << (immediateSize - 1)) - 1))); |
| int32_t value = immValue; |
| return ImmediateType((*reinterpret_cast<uint32_t*>(&value)) & ((1 << immediateSize) - 1)); |
| } |
| |
| template<typename ImmediateType> |
| static ImmediateType v(int32_t immValue) |
| { |
| ASSERT(isValid(immValue)); |
| uint32_t value = *reinterpret_cast<uint32_t*>(&immValue); |
| return ImmediateType(value & ((1 << immediateSize) - 1)); |
| } |
| |
| template<typename ImmediateType> |
| static ImmediateType v(int64_t immValue) |
| { |
| ASSERT(isValid(immValue)); |
| uint64_t value = *reinterpret_cast<uint64_t*>(&immValue); |
| return ImmediateType(uint32_t(value & ((uint64_t(1) << immediateSize) - 1))); |
| } |
| |
| explicit ImmediateBase(uint32_t immValue) |
| : imm(immValue) |
| { |
| if constexpr (immediateSize < sizeof(uint32_t) * 8) |
| ASSERT(imm < (1 << immediateSize)); |
| } |
| |
| template<unsigned fieldStart, unsigned fieldSize> |
| uint32_t field() |
| { |
| static_assert(fieldStart + fieldSize <= immediateSize); |
| return (imm >> fieldStart) & ((1 << fieldSize) - 1); |
| } |
| |
| uint32_t imm; |
| }; |
| |
| struct IImmediate : ImmediateBase<12> { |
| explicit IImmediate(uint32_t immValue) |
| : ImmediateBase<12>(immValue) |
| { } |
| |
| static int32_t value(InstructionValue insn) |
| { |
| uint32_t base = insn.field<20, 12>(); |
| int32_t imm = *reinterpret_cast<int32_t*>(&base); |
| return ((imm << 20) >> 20); |
| } |
| }; |
| |
| struct SImmediate : ImmediateBase<12> { |
| explicit SImmediate(uint32_t immValue) |
| : ImmediateBase<12>(immValue) |
| { } |
| |
| static int32_t value(InstructionValue insn) |
| { |
| uint32_t base = 0 |
| | (insn.field<31, 1>() << 11) |
| | (insn.field<25, 6>() << 5) |
| | (insn.field< 8, 4>() << 1) |
| | (insn.field< 7, 1>() << 0); |
| int32_t imm = *reinterpret_cast<int32_t*>(&base); |
| return ((imm << 20) >> 20); |
| } |
| }; |
| |
| struct BImmediate : ImmediateBase<13> { |
| explicit BImmediate(uint32_t immValue) |
| : ImmediateBase<13>(immValue) |
| { } |
| |
| static int32_t value(InstructionValue insn) |
| { |
| uint32_t base = 0 |
| | (insn.field<31, 1>() << 12) |
| | (insn.field< 7, 1>() << 11) |
| | (insn.field<25, 6>() << 5) |
| | (insn.field< 8, 4>() << 1); |
| int32_t imm = *reinterpret_cast<int32_t*>(&base); |
| return ((imm << 19) >> 19); |
| } |
| }; |
| |
| struct UImmediate : ImmediateBase<32> { |
| explicit UImmediate(uint32_t immValue) |
| : ImmediateBase((immValue >> 12) << 12) |
| { } |
| |
| static uint32_t value(InstructionValue insn) |
| { |
| return insn.field<12, 20>() << 12; |
| } |
| }; |
| |
| struct JImmediate : ImmediateBase<21> { |
| explicit JImmediate(uint32_t immValue) |
| : ImmediateBase<21>(immValue) |
| { } |
| |
| static int32_t value(InstructionValue insn) |
| { |
| uint32_t base = 0 |
| | (insn.field<31, 1>() << 20) |
| | (insn.field<12, 8>() << 12) |
| | (insn.field<20, 1>() << 11) |
| | (insn.field<25, 6>() << 5) |
| | (insn.field<21, 4>() << 1); |
| int32_t imm = *reinterpret_cast<int32_t*>(&base); |
| return ((imm << 11) >> 11); |
| } |
| }; |
| |
| // Instruction types |
| |
| // Helper struct that provides different groupings of register types as required for different instructions. |
| // The tuple size and contained types are used for compile-time checks of matching register types being passed |
| // to those instructions. |
| |
| struct RegistersBase { |
| struct GType { }; // General-purpose register |
| struct FType { }; // Floating-point register |
| struct ZType { }; // Zero-value unused register |
| |
| template<typename... RTypes> |
| using Tuple = std::tuple<RTypes...>; |
| template<size_t I, typename TupleType> |
| using Type = std::tuple_element_t<I, TupleType>; |
| |
| template<typename TupleType> |
| static constexpr size_t Size() |
| { |
| return std::tuple_size_v<TupleType>; |
| } |
| |
| using G = Tuple<GType>; |
| using GG = Tuple<GType, GType>; |
| using GF = Tuple<GType, FType>; |
| using GGG = Tuple<GType, GType, GType>; |
| using GGZ = Tuple<GType, GType, ZType>; |
| using GFF = Tuple<GType, FType, FType>; |
| using GFZ = Tuple<GType, FType, ZType>; |
| using FG = Tuple<FType, GType>; |
| using FF = Tuple<FType, FType>; |
| using FGZ = Tuple<FType, GType, ZType>; |
| using FFF = Tuple<FType, FType, FType>; |
| using FFZ = Tuple<FType, FType, ZType>; |
| using FFFF = Tuple<FType, FType, FType, FType>; |
| using ZZ = Tuple<ZType, ZType>; |
| }; |
| |
| // These are the base instruction structs. For R-type instructions, additional variations are provided. |
| |
| // Opcode, different spec-defined constant instruction fields and the required register types are specified through the |
| // template parameters. The construct() static methods compose and return the instruction value in the 32-bit unsigned |
| // format. |
| |
| // The matches() methods are usable to match a given InstructionValue against the target instruction type. Baseline |
| // implementations test the opcode and constant fields, but different instruction specializations can provide a better |
| // matching technique if necessary. |
| |
| // For each base instruction type there's also static getters for dynamic bit-fields like register values, rounding mode |
| // or different flag types. These should be used on an InstructionValue after a matching instruction type was already |
| // confirmed. These are mostly used for disassembly, leaving it to that implementation to handle the returned raw |
| // bit-field values. |
| |
| template<typename RegisterTypes> |
| struct RTypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 3); |
| using RD = RegistersBase::Type<0, RegisterTypes>; |
| using RS1 = RegistersBase::Type<1, RegisterTypes>; |
| using RS2 = RegistersBase::Type<2, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, unsigned funct3, unsigned funct7, typename RegisterTypes> |
| struct RTypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct3 < (1 << 3)); |
| static_assert(funct7 < (1 << 7)); |
| |
| using Base = RTypeBase<opcode, funct3, funct7, RegisterTypes>; |
| using Registers = RTypeRegisters<RegisterTypes>; |
| |
| template<typename RDType, typename RS1Type, typename RS2Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, RS2Type rs2) |
| { |
| uint32_t instruction = 0 |
| | (funct7 << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (funct3 << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct3 == insn.field<12, 3>() && funct7 == insn.field<25, 7>(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| }; |
| |
| template<Opcode opcode, unsigned funct7, typename RegisterTypes> |
| struct RTypeBaseWithRoundingMode { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct7 < (1 << 7)); |
| |
| using Base = RTypeBaseWithRoundingMode<opcode, funct7, RegisterTypes>; |
| using Registers = RTypeRegisters<RegisterTypes>; |
| |
| template<typename RDType, typename RS1Type, typename RS2Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, RS2Type rs2, FPRoundingMode rm) |
| { |
| ASSERT(unsigned(rm) < (1 << 3)); |
| |
| uint32_t instruction = 0 |
| | (funct7 << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (unsigned(rm) << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct7 == insn.field<25, 7>(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| static uint8_t rm(InstructionValue insn) { return insn.field<12, 3>(); } |
| }; |
| |
| template<Opcode opcode, unsigned funct3, unsigned funct7, typename RegisterTypes> |
| struct RTypeBaseWithAqRl { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct3 < (1 << 3)); |
| static_assert(funct7 < (1 << 7)); |
| |
| using Base = RTypeBaseWithAqRl<opcode, funct3, funct7, RegisterTypes>; |
| using Registers = RTypeRegisters<RegisterTypes>; |
| |
| template<typename RDType, typename RS1Type, typename RS2Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, RS2Type rs2, const std::initializer_list<MemoryAccess>& access) |
| { |
| unsigned aqrl = 0; |
| for (auto& value : access) |
| aqrl |= unsigned(value); |
| ASSERT(aqrl < (1 << 2)); |
| |
| uint32_t instruction = 0 |
| | ((funct7 | aqrl) << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (funct3 << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct3 == insn.field<12, 3>() && (funct7 >> 2) == insn.field<27, 5>(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| static uint8_t aqrl(InstructionValue insn) { return insn.field<25, 2>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct R4TypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 4); |
| using RD = RegistersBase::Type<0, RegisterTypes>; |
| using RS1 = RegistersBase::Type<1, RegisterTypes>; |
| using RS2 = RegistersBase::Type<2, RegisterTypes>; |
| using RS3 = RegistersBase::Type<3, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, unsigned funct2, typename RegisterTypes> |
| struct R4TypeBaseWithRoundingMode { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct2 < (1 << 2)); |
| |
| using Base = R4TypeBaseWithRoundingMode<opcode, funct2, RegisterTypes>; |
| using Registers = R4TypeRegisters<RegisterTypes>; |
| |
| template<typename RDType, typename RS1Type, typename RS2Type, typename RS3Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, RS2Type rs2, RS3Type rs3, FPRoundingMode rm) |
| { |
| ASSERT(unsigned(rm) < (1 << 3)); |
| |
| uint32_t instruction = 0 |
| | (registerValue(rs3) << 27) |
| | (funct2 << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (unsigned(rm) << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct2 == insn.field<25, 2>(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| static uint8_t rs3(InstructionValue insn) { return insn.field<27, 5>(); } |
| static uint8_t rm(InstructionValue insn) { return insn.field<12, 3>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct ITypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 2); |
| using RD = RegistersBase::Type<0, RegisterTypes>; |
| using RS1 = RegistersBase::Type<1, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, unsigned funct3, typename RegisterTypes> |
| struct ITypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct3 < (1 << 3)); |
| |
| using Base = ITypeBase<opcode, funct3, RegisterTypes>; |
| using Registers = ITypeRegisters<RegisterTypes>; |
| |
| template<typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, IImmediate imm) |
| { |
| uint32_t instruction = 0 |
| | (imm.field<0, 12>() << 20) |
| | (registerValue(rs1) << 15) |
| | (funct3 << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct3 == insn.field<12, 3>(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct STypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 2); |
| using RS1 = RegistersBase::Type<0, RegisterTypes>; |
| using RS2 = RegistersBase::Type<1, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, unsigned funct3, typename RegisterTypes> |
| struct STypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct3 < (1 << 3)); |
| |
| using Base = STypeBase<opcode, funct3, RegisterTypes>; |
| using Registers = STypeRegisters<RegisterTypes>; |
| |
| template<typename RS1Type, typename RS2Type> |
| static uint32_t construct(RS1Type rs1, RS2Type rs2, SImmediate imm) |
| { |
| uint32_t instruction = 0 |
| | (imm.field<5, 7>() << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (funct3 << 12) |
| | (imm.field<0, 5>() << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct3 == insn.field<12, 3>(); |
| } |
| |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct BTypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 2); |
| using RS1 = RegistersBase::Type<0, RegisterTypes>; |
| using RS2 = RegistersBase::Type<1, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, unsigned funct3, typename RegisterTypes> |
| struct BTypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| static_assert(funct3 < (1 << 3)); |
| |
| using Base = STypeBase<opcode, funct3, RegisterTypes>; |
| using Registers = STypeRegisters<RegisterTypes>; |
| |
| template<typename RS1Type, typename RS2Type> |
| static uint32_t construct(RS1Type rs1, RS2Type rs2, BImmediate imm) |
| { |
| uint32_t instruction = 0 |
| | (imm.field<12, 1>() << 31) |
| | (imm.field< 5, 6>() << 25) |
| | (registerValue(rs2) << 20) |
| | (registerValue(rs1) << 15) |
| | (funct3 << 12) |
| | (imm.field< 1, 4>() << 8) |
| | (imm.field<11, 1>() << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode() && funct3 == insn.field<12, 3>(); |
| } |
| |
| static uint8_t rs1(InstructionValue insn) { return insn.field<15, 5>(); } |
| static uint8_t rs2(InstructionValue insn) { return insn.field<20, 5>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct UTypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 1); |
| using RD = RegistersBase::Type<0, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, typename RegisterTypes> |
| struct UTypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| |
| using Base = UTypeBase<opcode, RegisterTypes>; |
| using Registers = UTypeRegisters<RegisterTypes>; |
| |
| template<typename RDType> |
| static uint32_t construct(RDType rd, UImmediate imm) |
| { |
| uint32_t instruction = imm.imm | (registerValue(rd) << 7) | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| }; |
| |
| template<typename RegisterTypes> |
| struct JTypeRegisters { |
| static_assert(RegistersBase::Size<RegisterTypes>() == 1); |
| using RD = RegistersBase::Type<0, RegisterTypes>; |
| }; |
| |
| template<Opcode opcode, typename RegisterTypes> |
| struct JTypeBase { |
| static_assert(unsigned(opcode) < (1 << 7)); |
| |
| using Base = JTypeBase<opcode, RegisterTypes>; |
| using Registers = UTypeRegisters<RegisterTypes>; |
| |
| template<typename RDType> |
| static uint32_t construct(RDType rd, JImmediate imm) |
| { |
| uint32_t instruction = 0 |
| | (imm.field<20, 1>() << 31) |
| | (imm.field< 1, 10>() << 21) |
| | (imm.field<11, 1>() << 20) |
| | (imm.field<12, 8>() << 12) |
| | (registerValue(rd) << 7) |
| | unsigned(opcode); |
| return instruction; |
| } |
| |
| static bool matches(InstructionValue insn) |
| { |
| return unsigned(opcode) == insn.opcode(); |
| } |
| |
| static uint8_t rd(InstructionValue insn) { return insn.field<7, 5>(); } |
| }; |
| |
| // The following instruction definitions utilize the base instruction structs, in most cases specifying everything |
| // necessary in the template parameters of the base instruction struct they are inheriting from. For each instruction |
| // there's also a pretty-print name constant included in the definition, for use by the disassembler. |
| |
| // RV32I Base Instruction Set |
| |
| struct LUI : UTypeBase<Opcode::LUI, RegistersBase::G> { |
| static constexpr const char* name = "lui"; |
| }; |
| |
| struct AUIPC : UTypeBase<Opcode::AUIPC, RegistersBase::G> { |
| static constexpr const char* name = "auipc"; |
| }; |
| |
| struct JAL : JTypeBase<Opcode::JAL, RegistersBase::G> { |
| static constexpr const char* name = "jal"; |
| }; |
| |
| struct JALR : ITypeBase<Opcode::JALR, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "jalr"; |
| }; |
| |
| struct BEQ : BTypeBase<Opcode::BRANCH, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "beq"; |
| }; |
| |
| struct BNE : BTypeBase<Opcode::BRANCH, 0b001, RegistersBase::GG> { |
| static constexpr const char* name = "bne"; |
| }; |
| |
| struct BLT : BTypeBase<Opcode::BRANCH, 0b100, RegistersBase::GG> { |
| static constexpr const char* name = "blt"; |
| }; |
| |
| struct BGE : BTypeBase<Opcode::BRANCH, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "bge"; |
| }; |
| |
| struct BLTU : BTypeBase<Opcode::BRANCH, 0b110, RegistersBase::GG> { |
| static constexpr const char* name = "bltu"; |
| }; |
| |
| struct BGEU : BTypeBase<Opcode::BRANCH, 0b111, RegistersBase::GG> { |
| static constexpr const char* name = "bgeu"; |
| }; |
| |
| struct LB : ITypeBase<Opcode::LOAD, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "lb"; |
| }; |
| |
| struct LH : ITypeBase<Opcode::LOAD, 0b001, RegistersBase::GG> { |
| static constexpr const char* name = "lh"; |
| }; |
| |
| struct LW : ITypeBase<Opcode::LOAD, 0b010, RegistersBase::GG> { |
| static constexpr const char* name = "lw"; |
| }; |
| |
| struct LBU : ITypeBase<Opcode::LOAD, 0b100, RegistersBase::GG> { |
| static constexpr const char* name = "lbu"; |
| }; |
| |
| struct LHU : ITypeBase<Opcode::LOAD, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "lhu"; |
| }; |
| |
| struct SB : STypeBase<Opcode::STORE, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "sb"; |
| }; |
| |
| struct SH : STypeBase<Opcode::STORE, 0b001, RegistersBase::GG> { |
| static constexpr const char* name = "sh"; |
| }; |
| |
| struct SW : STypeBase<Opcode::STORE, 0b010, RegistersBase::GG> { |
| static constexpr const char* name = "sw"; |
| }; |
| |
| struct ADDI : ITypeBase<Opcode::OP_IMM, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "addi"; |
| }; |
| |
| struct SLTI : ITypeBase<Opcode::OP_IMM, 0b010, RegistersBase::GG> { |
| static constexpr const char* name = "slti"; |
| }; |
| |
| struct SLTIU : ITypeBase<Opcode::OP_IMM, 0b011, RegistersBase::GG> { |
| static constexpr const char* name = "sltiu"; |
| }; |
| |
| struct XORI : ITypeBase<Opcode::OP_IMM, 0b100, RegistersBase::GG> { |
| static constexpr const char* name = "xori"; |
| }; |
| |
| struct ORI : ITypeBase<Opcode::OP_IMM, 0b110, RegistersBase::GG> { |
| static constexpr const char* name = "ori"; |
| }; |
| |
| struct ANDI : ITypeBase<Opcode::OP_IMM, 0b111, RegistersBase::GG> { |
| static constexpr const char* name = "andi"; |
| }; |
| |
| struct SLLI : ITypeBase<Opcode::OP_IMM, 0b001, RegistersBase::GG> { |
| static constexpr const char* name = "slli"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 6)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b000000 << 6) | shiftAmount>()); |
| } |
| }; |
| |
| struct SRLI : ITypeBase<Opcode::OP_IMM, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "srli"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 6)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b000000 << 6) | shiftAmount>()); |
| } |
| }; |
| |
| struct SRAI : ITypeBase<Opcode::OP_IMM, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "srai"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 6)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b010000 << 6) | shiftAmount>()); |
| } |
| }; |
| |
| struct ADD : RTypeBase<Opcode::OP, 0b000, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "add"; |
| }; |
| |
| struct SUB : RTypeBase<Opcode::OP, 0b000, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "sub"; |
| }; |
| |
| struct SLL : RTypeBase<Opcode::OP, 0b001, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "sll"; |
| }; |
| |
| struct SLT : RTypeBase<Opcode::OP, 0b010, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "slt"; |
| }; |
| |
| struct SLTU : RTypeBase<Opcode::OP, 0b011, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "sltu"; |
| }; |
| |
| struct XOR : RTypeBase<Opcode::OP, 0b100, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "xor"; |
| }; |
| |
| struct SRL : RTypeBase<Opcode::OP, 0b101, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "srl"; |
| }; |
| |
| struct SRA : RTypeBase<Opcode::OP, 0b101, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "sra"; |
| }; |
| |
| struct OR : RTypeBase<Opcode::OP, 0b110, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "or"; |
| }; |
| |
| struct AND : RTypeBase<Opcode::OP, 0b111, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "and"; |
| }; |
| |
| struct FENCE : ITypeBase<Opcode::MISC_MEM, 0b000, RegistersBase::ZZ> { |
| static constexpr const char* name = "fence"; |
| }; |
| |
| struct ECALL : ITypeBase<Opcode::SYSTEM, 0b000, RegistersBase::ZZ> { |
| static constexpr const char* name = "ecall"; |
| }; |
| |
| struct EBREAK : ITypeBase<Opcode::SYSTEM, 0b000, RegistersBase::ZZ> { |
| static constexpr const char* name = "ebreak"; |
| }; |
| |
| // RV64I Base Instruction Set (in addition to RV32I) |
| |
| struct LWU : ITypeBase<Opcode::LOAD, 0b110, RegistersBase::GG> { |
| static constexpr const char* name = "lwu"; |
| }; |
| |
| struct LD : ITypeBase<Opcode::LOAD, 0b011, RegistersBase::GG> { |
| static constexpr const char* name = "ld"; |
| }; |
| |
| struct SD : STypeBase<Opcode::STORE, 0b011, RegistersBase::GG> { |
| static constexpr const char* name = "sd"; |
| }; |
| |
| struct ADDIW : ITypeBase<Opcode::OP_IMM_32, 0b000, RegistersBase::GG> { |
| static constexpr const char* name = "addiw"; |
| }; |
| |
| struct SLLIW : ITypeBase<Opcode::OP_IMM_32, 0b001, RegistersBase::GG> { |
| static constexpr const char* name = "slliw"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 5)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b0000000 << 5) | shiftAmount>()); |
| } |
| }; |
| |
| struct SRLIW : ITypeBase<Opcode::OP_IMM_32, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "srliw"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 5)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b0000000 << 5) | shiftAmount>()); |
| } |
| }; |
| |
| struct SRAIW : ITypeBase<Opcode::OP_IMM_32, 0b101, RegistersBase::GG> { |
| static constexpr const char* name = "sraiw"; |
| |
| using Base::construct; |
| template<unsigned shiftAmount, typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| static_assert(shiftAmount < (1 << 5)); |
| return Base::construct(rd, rs1, IImmediate::v<IImmediate, (0b0100000 << 5) | shiftAmount>()); |
| } |
| }; |
| |
| struct ADDW : RTypeBase<Opcode::OP_32, 0b000, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "addw"; |
| }; |
| |
| struct SUBW : RTypeBase<Opcode::OP_32, 0b000, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "subw"; |
| }; |
| |
| struct SLLW : RTypeBase<Opcode::OP_32, 0b001, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "sllw"; |
| }; |
| |
| struct SRLW : RTypeBase<Opcode::OP_32, 0b101, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "srlw"; |
| }; |
| |
| struct SRAW : RTypeBase<Opcode::OP_32, 0b101, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "sraw"; |
| }; |
| |
| // RV32/RV64 Zifencei Standard Extension |
| |
| struct FENCE_I : ITypeBase<Opcode::MISC_MEM, 0b001, RegistersBase::ZZ> { |
| static constexpr const char* name = "fence.i"; |
| }; |
| |
| // RV32M Standard Extension |
| |
| struct MUL : RTypeBase<Opcode::OP, 0b000, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "mul"; |
| }; |
| |
| struct MULH : RTypeBase<Opcode::OP, 0b001, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "mulh"; |
| }; |
| |
| struct MULHSU : RTypeBase<Opcode::OP, 0b010, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "mulhsu"; |
| }; |
| |
| struct MULHU : RTypeBase<Opcode::OP, 0b011, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "mulhu"; |
| }; |
| |
| struct DIV : RTypeBase<Opcode::OP, 0b100, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "div"; |
| }; |
| |
| struct DIVU : RTypeBase<Opcode::OP, 0b101, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "divu"; |
| }; |
| |
| struct REM : RTypeBase<Opcode::OP, 0b110, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "rem"; |
| }; |
| |
| struct REMU : RTypeBase<Opcode::OP, 0b111, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "remu"; |
| }; |
| |
| // RV64M Standard Extension (in addition to RV32M) |
| |
| struct MULW : RTypeBase<Opcode::OP_32, 0b000, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "mulw"; |
| }; |
| |
| struct DIVW : RTypeBase<Opcode::OP_32, 0b100, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "divw"; |
| }; |
| |
| struct DIVUW : RTypeBase<Opcode::OP_32, 0b101, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "divuw"; |
| }; |
| |
| struct REMW : RTypeBase<Opcode::OP_32, 0b110, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "remw"; |
| }; |
| |
| struct REMUW : RTypeBase<Opcode::OP_32, 0b111, 0b0000001, RegistersBase::GGG> { |
| static constexpr const char* name = "remuw"; |
| }; |
| |
| // RV32A Standard Extension |
| |
| struct LR_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0001000, RegistersBase::GGZ> { |
| static constexpr const char* name = "lr.w"; |
| }; |
| |
| struct SC_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0001100, RegistersBase::GGG> { |
| static constexpr const char* name = "sc.w"; |
| }; |
| |
| struct AMOSWAP_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0000100, RegistersBase::GGG> { |
| static constexpr const char* name = "amoswap.w"; |
| }; |
| |
| struct AMOADD_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoadd.w"; |
| }; |
| |
| struct AMOXOR_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0010000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoxor.w"; |
| }; |
| |
| struct AMOAND_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0110000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoand.w"; |
| }; |
| |
| struct AMOOR_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoor.w"; |
| }; |
| |
| struct AMOMIN_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b1000000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomin.w"; |
| }; |
| |
| struct AMOMAX_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b1010000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomax.w"; |
| }; |
| |
| struct AMOMINU_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b1100000, RegistersBase::GGG> { |
| static constexpr const char* name = "amominu.w"; |
| }; |
| |
| struct AMOMAXU_W : RTypeBaseWithAqRl<Opcode::AMO, 0b010, 0b1110000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomaxu.w"; |
| }; |
| |
| // RV64A Standard Extension (in addition to RV32A) |
| |
| struct LR_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0001000, RegistersBase::GGZ> { |
| static constexpr const char* name = "lr.d"; |
| }; |
| |
| struct SC_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0001100, RegistersBase::GGG> { |
| static constexpr const char* name = "sc.d"; |
| }; |
| |
| struct AMOSWAP_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0000100, RegistersBase::GGG> { |
| static constexpr const char* name = "amoswap.d"; |
| }; |
| |
| struct AMOADD_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0000000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoadd.d"; |
| }; |
| |
| struct AMOXOR_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0010000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoxor.d"; |
| }; |
| |
| struct AMOAND_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0110000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoand.d"; |
| }; |
| |
| struct AMOOR_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b0100000, RegistersBase::GGG> { |
| static constexpr const char* name = "amoor.d"; |
| }; |
| |
| struct AMOMIN_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b1000000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomin.d"; |
| }; |
| |
| struct AMOMAX_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b1010000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomax.d"; |
| }; |
| |
| struct AMOMINU_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b1100000, RegistersBase::GGG> { |
| static constexpr const char* name = "amominu.d"; |
| }; |
| |
| struct AMOMAXU_D : RTypeBaseWithAqRl<Opcode::AMO, 0b011, 0b1110000, RegistersBase::GGG> { |
| static constexpr const char* name = "amomaxu.d"; |
| }; |
| |
| // RV32F Standard Extension |
| |
| template<FCVTType ToType, FCVTType FromType> |
| struct FCVTBase { |
| static constexpr bool valid = false; |
| }; |
| |
| template<typename RDRegisterType, typename RS1RegisterType, unsigned rs2, unsigned funct7, typename RegisterTypes> |
| struct FCVTImpl : RTypeBaseWithRoundingMode<Opcode::OP_FP, funct7, RegisterTypes> { |
| static constexpr bool valid = true; |
| using RDType = RDRegisterType; |
| using RS1Type = RS1RegisterType; |
| |
| template<typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1, FPRoundingMode rm) |
| { |
| static_assert(rs2 < (1 << 5)); |
| return RTypeBaseWithRoundingMode<Opcode::OP_FP, funct7, RegisterTypes>::construct(rd, rs1, RegisterID(rs2), rm); |
| } |
| }; |
| |
| template<FMVType ToType, FMVType FromType> |
| struct FMVBase { |
| static constexpr bool valid = false; |
| }; |
| |
| template<typename RDRegisterType, typename RS1RegisterType, unsigned funct7, typename RegisterTypes> |
| struct FMVImpl : RTypeBase<Opcode::OP_FP, 0b000, funct7, RegisterTypes> { |
| static constexpr bool valid = true; |
| using RDType = RDRegisterType; |
| using RS1Type = RS1RegisterType; |
| |
| template<typename RDType, typename RS1Type> |
| static uint32_t construct(RDType rd, RS1Type rs1) |
| { |
| return RTypeBase<Opcode::OP_FP, 0b000, funct7, RegisterTypes>::construct(rd, rs1, RegisterID(0)); |
| } |
| }; |
| |
| struct FLW : ITypeBase<Opcode::LOAD_FP, 0b010, RegistersBase::FG> { |
| static constexpr const char* name = "flw"; |
| }; |
| |
| struct FSW : STypeBase<Opcode::STORE_FP, 0b010, RegistersBase::GF> { |
| static constexpr const char* name = "fsw"; |
| }; |
| |
| struct FMADD_S : R4TypeBaseWithRoundingMode<Opcode::MADD, 0b00, RegistersBase::FFFF> { |
| static constexpr const char* name = "fmadd.s"; |
| }; |
| |
| struct FMSUB_S : R4TypeBaseWithRoundingMode<Opcode::MSUB, 0b00, RegistersBase::FFFF> { |
| static constexpr const char* name = "fmsub.s"; |
| }; |
| |
| struct FNMSUB_S : R4TypeBaseWithRoundingMode<Opcode::NMSUB, 0b00, RegistersBase::FFFF> { |
| static constexpr const char* name = "fnmsub.s"; |
| }; |
| |
| struct FNMADD_S : R4TypeBaseWithRoundingMode<Opcode::NMADD, 0b00, RegistersBase::FFFF> { |
| static constexpr const char* name = "fnmadd.s"; |
| }; |
| |
| struct FADD_S : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0000000, RegistersBase::FFF> { |
| static constexpr const char* name = "fadd.s"; |
| }; |
| |
| struct FSUB_S : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0000100, RegistersBase::FFF> { |
| static constexpr const char* name = "fsub.s"; |
| }; |
| |
| struct FMUL_S : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0001000, RegistersBase::FFF> { |
| static constexpr const char* name = "fmul.s"; |
| }; |
| |
| struct FDIV_S : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0001100, RegistersBase::FFF> { |
| static constexpr const char* name = "fdiv.s"; |
| }; |
| |
| struct FSQRT_S : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0101100, RegistersBase::FFZ> { |
| static constexpr const char* name = "fsqrt.s"; |
| }; |
| |
| struct FSGNJ_S : RTypeBase<Opcode::OP_FP, 0b000, 0b0010000, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnj.s"; |
| }; |
| |
| struct FSGNJN_S : RTypeBase<Opcode::OP_FP, 0b001, 0b0010000, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnjn.s"; |
| }; |
| |
| struct FSGNJX_S : RTypeBase<Opcode::OP_FP, 0b010, 0b0010000, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnjx.s"; |
| }; |
| |
| struct FMIN_S : RTypeBase<Opcode::OP_FP, 0b000, 0b0010100, RegistersBase::FFF> { |
| static constexpr const char* name = "fmin.s"; |
| }; |
| |
| struct FMAX_S : RTypeBase<Opcode::OP_FP, 0b001, 0b0010100, RegistersBase::FFF> { |
| static constexpr const char* name = "fmax.s"; |
| }; |
| |
| template<> |
| struct FCVTBase<FCVTType::W, FCVTType::S> : FCVTImpl<RegisterID, FPRegisterID, 0b00000, 0b1100000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.w.s"; |
| }; |
| using FCVT_W_S = FCVTBase<FCVTType::W, FCVTType::S>; |
| |
| template<> |
| struct FCVTBase<FCVTType::WU, FCVTType::S> : FCVTImpl<RegisterID, FPRegisterID, 0b00001, 0b1100000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.wu.s"; |
| }; |
| using FCVT_WU_S = FCVTBase<FCVTType::WU, FCVTType::S>; |
| |
| template<> |
| struct FMVBase<FMVType::X, FMVType::W> : FMVImpl<RegisterID, FPRegisterID, 0b1110000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fmv.x.w"; |
| }; |
| using FMV_X_W = FMVBase<FMVType::X, FMVType::W>; |
| |
| struct FEQ_S : RTypeBase<Opcode::OP_FP, 0b010, 0b1010000, RegistersBase::GFF> { |
| static constexpr const char* name = "feq.s"; |
| }; |
| |
| struct FLT_S : RTypeBase<Opcode::OP_FP, 0b001, 0b1010000, RegistersBase::GFF> { |
| static constexpr const char* name = "flt.s"; |
| }; |
| |
| struct FLE_S : RTypeBase<Opcode::OP_FP, 0b000, 0b1010000, RegistersBase::GFF> { |
| static constexpr const char* name = "fle.s"; |
| }; |
| |
| struct FCLASS_S : RTypeBase<Opcode::OP_FP, 0b001, 0b1110000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fclass.s"; |
| }; |
| |
| template<> |
| struct FCVTBase<FCVTType::S, FCVTType::W> : FCVTImpl<FPRegisterID, RegisterID, 0b00000, 0b1101000, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.s.w"; |
| }; |
| using FCVT_S_W = FCVTBase<FCVTType::S, FCVTType::W>; |
| |
| template<> |
| struct FCVTBase<FCVTType::S, FCVTType::WU> : FCVTImpl<FPRegisterID, RegisterID, 0b00001, 0b1101000, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.s.wu"; |
| }; |
| using FCVT_S_WU = FCVTBase<FCVTType::S, FCVTType::WU>; |
| |
| template<> |
| struct FMVBase<FMVType::W, FMVType::X> : FMVImpl<FPRegisterID, RegisterID, 0b1111000, RegistersBase::FGZ> { |
| static constexpr const char* name = "fmv.w.x"; |
| }; |
| using FMV_W_X = FMVBase<FMVType::W, FMVType::X>; |
| |
| // RV64F Standard Extension (in addition to RV32F) |
| |
| template<> |
| struct FCVTBase<FCVTType::L, FCVTType::S> : FCVTImpl<RegisterID, FPRegisterID, 0b00010, 0b1100000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.l.s"; |
| }; |
| using FCVT_L_S = FCVTBase<FCVTType::L, FCVTType::S>; |
| |
| template<> |
| struct FCVTBase<FCVTType::LU, FCVTType::S> : FCVTImpl<RegisterID, FPRegisterID, 0b00011, 0b1100000, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.lu.s"; |
| }; |
| using FCVT_LU_S = FCVTBase<FCVTType::LU, FCVTType::S>; |
| |
| template<> |
| struct FCVTBase<FCVTType::S, FCVTType::L> : FCVTImpl<FPRegisterID, RegisterID, 0b00010, 0b1101000, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.s.l"; |
| }; |
| using FCVT_S_L = FCVTBase<FCVTType::S, FCVTType::L>; |
| |
| template<> |
| struct FCVTBase<FCVTType::S, FCVTType::LU> : FCVTImpl<FPRegisterID, RegisterID, 0b00011, 0b1101000, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.s.lu"; |
| }; |
| using FCVT_S_LU = FCVTBase<FCVTType::S, FCVTType::LU>; |
| |
| // RV32D Standard Extension |
| |
| struct FLD : ITypeBase<Opcode::LOAD_FP, 0b011, RegistersBase::FG> { |
| static constexpr const char* name = "fld"; |
| }; |
| |
| struct FSD : STypeBase<Opcode::STORE_FP, 0b011, RegistersBase::GF> { |
| static constexpr const char* name = "fsd"; |
| }; |
| |
| struct FMADD_D : R4TypeBaseWithRoundingMode<Opcode::MADD, 0b01, RegistersBase::FFFF> { |
| static constexpr const char* name = "fmadd.d"; |
| }; |
| |
| struct FMSUB_D : R4TypeBaseWithRoundingMode<Opcode::MSUB, 0b01, RegistersBase::FFFF> { |
| static constexpr const char* name = "fmsub.d"; |
| }; |
| |
| struct FNMSUB_D : R4TypeBaseWithRoundingMode<Opcode::NMSUB, 0b01, RegistersBase::FFFF> { |
| static constexpr const char* name = "fnmsub.d"; |
| }; |
| |
| struct FNMADD_D : R4TypeBaseWithRoundingMode<Opcode::NMADD, 0b01, RegistersBase::FFFF> { |
| static constexpr const char* name = "fnmadd.d"; |
| }; |
| |
| struct FADD_D : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0000001, RegistersBase::FFF> { |
| static constexpr const char* name = "fadd.d"; |
| }; |
| |
| struct FSUB_D : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0000101, RegistersBase::FFF> { |
| static constexpr const char* name = "fsub.d"; |
| }; |
| |
| struct FMUL_D : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0001001, RegistersBase::FFF> { |
| static constexpr const char* name = "fmul.d"; |
| }; |
| |
| struct FDIV_D : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0001101, RegistersBase::FFF> { |
| static constexpr const char* name = "fdiv.d"; |
| }; |
| |
| struct FSQRT_D : RTypeBaseWithRoundingMode<Opcode::OP_FP, 0b0101101, RegistersBase::FFZ> { |
| static constexpr const char* name = "fsqrt.d"; |
| }; |
| |
| struct FSGNJ_D : RTypeBase<Opcode::OP_FP, 0b000, 0b0010001, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnj.d"; |
| }; |
| |
| struct FSGNJN_D : RTypeBase<Opcode::OP_FP, 0b001, 0b0010001, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnjn.d"; |
| }; |
| |
| struct FSGNJX_D : RTypeBase<Opcode::OP_FP, 0b010, 0b0010001, RegistersBase::FFF> { |
| static constexpr const char* name = "fsgnjx.d"; |
| }; |
| |
| struct FMIN_D : RTypeBase<Opcode::OP_FP, 0b000, 0b0010101, RegistersBase::FFF> { |
| static constexpr const char* name = "fmin.d"; |
| }; |
| |
| struct FMAX_D : RTypeBase<Opcode::OP_FP, 0b001, 0b0010101, RegistersBase::FFF> { |
| static constexpr const char* name = "fmax.d"; |
| }; |
| |
| template<> |
| struct FCVTBase<FCVTType::S, FCVTType::D> : FCVTImpl<FPRegisterID, FPRegisterID, 0b00001, 0b0100000, RegistersBase::FFZ> { |
| static constexpr const char* name = "fcvt.s.d"; |
| }; |
| using FCVT_S_D = FCVTBase<FCVTType::S, FCVTType::D>; |
| |
| template<> |
| struct FCVTBase<FCVTType::D, FCVTType::S> : FCVTImpl<FPRegisterID, FPRegisterID, 0b00000, 0b0100001, RegistersBase::FFZ> { |
| static constexpr const char* name = "fcvt.d.s"; |
| }; |
| using FCVT_D_S = FCVTBase<FCVTType::D, FCVTType::S>; |
| |
| struct FEQ_D : RTypeBase<Opcode::OP_FP, 0b010, 0b1010001, RegistersBase::GFF> { |
| static constexpr const char* name = "feq.d"; |
| }; |
| |
| struct FLT_D : RTypeBase<Opcode::OP_FP, 0b001, 0b1010001, RegistersBase::GFF> { |
| static constexpr const char* name = "flt.d"; |
| }; |
| |
| struct FLE_D : RTypeBase<Opcode::OP_FP, 0b000, 0b1010001, RegistersBase::GFF> { |
| static constexpr const char* name = "fle.d"; |
| }; |
| |
| struct FCLASS_D : RTypeBase<Opcode::OP_FP, 0b001, 0b1110001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fclass.d"; |
| }; |
| |
| template<> |
| struct FCVTBase<FCVTType::W, FCVTType::D> : FCVTImpl<RegisterID, FPRegisterID, 0b00000, 0b1100001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.w.d"; |
| }; |
| using FCVT_W_D = FCVTBase<FCVTType::W, FCVTType::D>; |
| |
| template<> |
| struct FCVTBase<FCVTType::WU, FCVTType::D> : FCVTImpl<RegisterID, FPRegisterID, 0b00001, 0b1100001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.wu.d"; |
| }; |
| using FCVT_WU_D = FCVTBase<FCVTType::WU, FCVTType::D>; |
| |
| template<> |
| struct FCVTBase<FCVTType::D, FCVTType::W> : FCVTImpl<FPRegisterID, RegisterID, 0b00000, 0b1101001, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.d.w"; |
| }; |
| using FCVT_D_W = FCVTBase<FCVTType::D, FCVTType::W>; |
| |
| template<> |
| struct FCVTBase<FCVTType::D, FCVTType::WU> : FCVTImpl<FPRegisterID, RegisterID, 0b00001, 0b1101001, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.d.wu"; |
| }; |
| using FCVT_D_WU = FCVTBase<FCVTType::D, FCVTType::WU>; |
| |
| // RV64D Standard Extension (in addition to RV32D) |
| |
| template<> |
| struct FCVTBase<FCVTType::L, FCVTType::D> : FCVTImpl<RegisterID, FPRegisterID, 0b00010, 0b1100001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.l.d"; |
| }; |
| using FCVT_L_D = FCVTBase<FCVTType::L, FCVTType::D>; |
| |
| template<> |
| struct FCVTBase<FCVTType::LU, FCVTType::D> : FCVTImpl<RegisterID, FPRegisterID, 0b00011, 0b1100001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fcvt.lu.d"; |
| }; |
| using FCVT_LU_D = FCVTBase<FCVTType::LU, FCVTType::D>; |
| |
| template<> |
| struct FMVBase<FMVType::X, FMVType::D> : FMVImpl<RegisterID, FPRegisterID, 0b1110001, RegistersBase::GFZ> { |
| static constexpr const char* name = "fmv.x.d"; |
| }; |
| using FMV_X_D = FMVBase<FMVType::X, FMVType::D>; |
| |
| template<> |
| struct FCVTBase<FCVTType::D, FCVTType::L> : FCVTImpl<FPRegisterID, RegisterID, 0b00010, 0b1101001, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.d.l"; |
| }; |
| using FCVT_D_L = FCVTBase<FCVTType::D, FCVTType::L>; |
| |
| template<> |
| struct FCVTBase<FCVTType::D, FCVTType::LU> : FCVTImpl<FPRegisterID, RegisterID, 0b00011, 0b1101001, RegistersBase::FGZ> { |
| static constexpr const char* name = "fcvt.d.lu"; |
| }; |
| using FCVT_D_LU = FCVTBase<FCVTType::D, FCVTType::LU>; |
| |
| template<> |
| struct FMVBase<FMVType::D, FMVType::X> : FMVImpl<FPRegisterID, RegisterID, 0b1111001, RegistersBase::FGZ> { |
| static constexpr const char* name = "fmv.d.x"; |
| }; |
| using FMV_D_X = FMVBase<FMVType::D, FMVType::X>; |
| |
| } // namespace RISCV64Instructions |
| |
| class RISCV64Assembler { |
| public: |
| using RegisterID = RISCV64Registers::RegisterID; |
| using SPRegisterID = RISCV64Registers::SPRegisterID; |
| using FPRegisterID = RISCV64Registers::FPRegisterID; |
| |
| static constexpr RegisterID firstRegister() { return RISCV64Registers::x0; } |
| static constexpr RegisterID lastRegister() { return RISCV64Registers::x31; } |
| static constexpr unsigned numberOfRegisters() { return lastRegister() - firstRegister() + 1; } |
| |
| static constexpr SPRegisterID firstSPRegister() { return RISCV64Registers::pc; } |
| static constexpr SPRegisterID lastSPRegister() { return RISCV64Registers::pc; } |
| static constexpr unsigned numberOfSPRegisters() { return lastSPRegister() - firstSPRegister() + 1; } |
| |
| static constexpr FPRegisterID firstFPRegister() { return RISCV64Registers::f0; } |
| static constexpr FPRegisterID lastFPRegister() { return RISCV64Registers::f31; } |
| static constexpr unsigned numberOfFPRegisters() { return lastFPRegister() - firstFPRegister() + 1; } |
| |
| static const char* gprName(RegisterID id) |
| { |
| ASSERT(id >= firstRegister() && id <= lastRegister()); |
| static const char* const nameForRegister[numberOfRegisters()] = { |
| #define REGISTER_NAME(id, name, r, cs) name, |
| FOR_EACH_GP_REGISTER(REGISTER_NAME) |
| #undef REGISTER_NAME |
| }; |
| return nameForRegister[id]; |
| } |
| |
| static const char* sprName(SPRegisterID id) |
| { |
| ASSERT(id >= firstSPRegister() && id <= lastSPRegister()); |
| static const char* const nameForRegister[numberOfSPRegisters()] = { |
| #define REGISTER_NAME(id, name) name, |
| FOR_EACH_SP_REGISTER(REGISTER_NAME) |
| #undef REGISTER_NAME |
| }; |
| return nameForRegister[id]; |
| } |
| |
| static const char* fprName(FPRegisterID id) |
| { |
| ASSERT(id >= firstFPRegister() && id <= lastFPRegister()); |
| static const char* const nameForRegister[numberOfFPRegisters()] = { |
| #define REGISTER_NAME(id, name, r, cs) name, |
| FOR_EACH_FP_REGISTER(REGISTER_NAME) |
| #undef REGISTER_NAME |
| }; |
| return nameForRegister[id]; |
| } |
| |
| RISCV64Assembler() { } |
| |
| AssemblerBuffer& buffer() { return m_buffer; } |
| |
| typedef enum { |
| ConditionEQ, |
| ConditionNE, |
| ConditionHS, ConditionCS = ConditionHS, |
| ConditionLO, ConditionCC = ConditionLO, |
| ConditionMI, |
| ConditionPL, |
| ConditionVS, |
| ConditionVC, |
| ConditionHI, |
| ConditionLS, |
| ConditionGE, |
| ConditionLT, |
| ConditionGT, |
| ConditionLE, |
| ConditionAL, |
| ConditionInvalid |
| } Condition; |
| |
| static Condition invert(Condition cond) |
| { |
| return static_cast<Condition>(cond ^ 1); |
| } |
| |
| static void* getRelocatedAddress(void* code, AssemblerLabel label) |
| { |
| ASSERT(label.isSet()); |
| return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + label.offset()); |
| } |
| |
| size_t codeSize() const { return m_buffer.codeSize(); } |
| |
| static unsigned getCallReturnOffset(AssemblerLabel) { return 0; } |
| |
| AssemblerLabel labelIgnoringWatchpoints() { return { }; } |
| AssemblerLabel labelForWatchpoint() { return { }; } |
| AssemblerLabel label() { return { }; } |
| |
| static void linkJump(void*, AssemblerLabel, void*) { } |
| |
| static void linkJump(AssemblerLabel, AssemblerLabel) { } |
| |
| static void linkCall(void*, AssemblerLabel, void*) { } |
| |
| static void linkPointer(void*, AssemblerLabel, void*) { } |
| |
| static ptrdiff_t maxJumpReplacementSize() |
| { |
| return 4; |
| } |
| |
| static constexpr ptrdiff_t patchableJumpSize() |
| { |
| return 4; |
| } |
| |
| static void repatchPointer(void*, void*) { } |
| |
| static void relinkJump(void*, void*) { } |
| static void relinkJumpToNop(void*) { } |
| static void relinkCall(void*, void*) { } |
| |
| unsigned debugOffset() { return m_buffer.debugOffset(); } |
| |
| static void cacheFlush(void*, size_t) { } |
| |
| using CopyFunction = void*(&)(void*, const void*, size_t); |
| template <CopyFunction copy> |
| static void fillNops(void*, size_t) { } |
| |
| AssemblerBuffer m_buffer; |
| }; |
| |
| } // namespace JSC |
| |
| #endif // ENABLE(ASSEMBLER) && CPU(RISCV64) |