Title: [285532] trunk/Source
Revision
285532
Author
commit-qu...@webkit.org
Date
2021-11-09 13:53:02 -0800 (Tue, 09 Nov 2021)

Log Message

[RISCV64] Add assembly, disassembly infrastructure
https://bugs.webkit.org/show_bug.cgi?id=232870

Patch by Zan Dobersek <zdober...@igalia.com> on 2021-11-09
Reviewed by Yusuke Suzuki.

Source/_javascript_Core:

Provide the necessary facilities for assembling and disassembling
RISC-V instructions. This is just a preliminary patch that introduces
the necessary assembly and disassembly infrastructure while actual
enhancements to RISCV64Assembler and MacroAssemblerRISCV64 classes are
left for later.

In RISCV64Assembler.h header, necessary helper functions, enumerations
and structs are introduced that enable crafting instruction values in
accordance with the base RISC-V specification. All the necessary
immediate and instruction types are supported, and the relevant
instruction definitions are introduced and become usable in future work.

For debugging purposes, a custom RISC-V disassembler is also introduced
and is enabled when appropriate. The implementation utilizes
functionality introduced in RISCV64Assembler.h, and different formatters
are introduced to handle special cases even among the established
instruction types.

* Sources.txt:
* assembler/RISCV64Assembler.h:
(JSC::RISCV64Instructions::registerValue):
(JSC::RISCV64Instructions::InstructionValue::InstructionValue):
(JSC::RISCV64Instructions::InstructionValue::field):
(JSC::RISCV64Instructions::InstructionValue::opcode):
(JSC::RISCV64Instructions::ImmediateBase::isValid):
(JSC::RISCV64Instructions::ImmediateBase::v):
(JSC::RISCV64Instructions::ImmediateBase::ImmediateBase):
(JSC::RISCV64Instructions::ImmediateBase::field):
(JSC::RISCV64Instructions::IImmediate::IImmediate):
(JSC::RISCV64Instructions::IImmediate::value):
(JSC::RISCV64Instructions::SImmediate::SImmediate):
(JSC::RISCV64Instructions::SImmediate::value):
(JSC::RISCV64Instructions::BImmediate::BImmediate):
(JSC::RISCV64Instructions::BImmediate::value):
(JSC::RISCV64Instructions::UImmediate::UImmediate):
(JSC::RISCV64Instructions::UImmediate::value):
(JSC::RISCV64Instructions::JImmediate::JImmediate):
(JSC::RISCV64Instructions::JImmediate::value):
(JSC::RISCV64Instructions::RegistersBase::Size):
(JSC::RISCV64Instructions::RTypeBase::construct):
(JSC::RISCV64Instructions::RTypeBase::matches):
(JSC::RISCV64Instructions::RTypeBase::rd):
(JSC::RISCV64Instructions::RTypeBase::rs1):
(JSC::RISCV64Instructions::RTypeBase::rs2):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::construct):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::matches):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rd):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rs1):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rs2):
(JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rm):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::construct):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::matches):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::rd):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::rs1):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::rs2):
(JSC::RISCV64Instructions::RTypeBaseWithAqRl::aqrl):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::construct):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::matches):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rd):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs1):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs2):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs3):
(JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rm):
(JSC::RISCV64Instructions::ITypeBase::construct):
(JSC::RISCV64Instructions::ITypeBase::matches):
(JSC::RISCV64Instructions::ITypeBase::rd):
(JSC::RISCV64Instructions::ITypeBase::rs1):
(JSC::RISCV64Instructions::STypeBase::construct):
(JSC::RISCV64Instructions::STypeBase::matches):
(JSC::RISCV64Instructions::STypeBase::rs1):
(JSC::RISCV64Instructions::STypeBase::rs2):
(JSC::RISCV64Instructions::BTypeBase::construct):
(JSC::RISCV64Instructions::BTypeBase::matches):
(JSC::RISCV64Instructions::BTypeBase::rs1):
(JSC::RISCV64Instructions::BTypeBase::rs2):
(JSC::RISCV64Instructions::UTypeBase::construct):
(JSC::RISCV64Instructions::UTypeBase::matches):
(JSC::RISCV64Instructions::UTypeBase::rd):
(JSC::RISCV64Instructions::JTypeBase::construct):
(JSC::RISCV64Instructions::JTypeBase::matches):
(JSC::RISCV64Instructions::JTypeBase::rd):
(JSC::RISCV64Instructions::SLLI::construct):
(JSC::RISCV64Instructions::SRLI::construct):
(JSC::RISCV64Instructions::SRAI::construct):
(JSC::RISCV64Instructions::SLLIW::construct):
(JSC::RISCV64Instructions::SRLIW::construct):
(JSC::RISCV64Instructions::SRAIW::construct):
(JSC::RISCV64Instructions::FCVTImpl::construct):
(JSC::RISCV64Instructions::FMVImpl::construct):
* disassembler/RISCV64Disassembler.cpp: Added.
(JSC::RISCV64Disassembler::StringBufferBase::data):
(JSC::RISCV64Disassembler::StringBufferBase::size):
(JSC::RISCV64Disassembler::StringBufferBase::createString):
(JSC::RISCV64Disassembler::registerName<RISCV64Instructions::RegistersBase::GType>):
(JSC::RISCV64Disassembler::registerName<RISCV64Instructions::RegistersBase::FType>):
(JSC::RISCV64Disassembler::roundingMode):
(JSC::RISCV64Disassembler::memoryOperationFlags):
(JSC::RISCV64Disassembler::aqrlFlags):
(JSC::RISCV64Disassembler::InstructionList::contains):
(JSC::RISCV64Disassembler::RTypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::RTypeR2Formatting::disassemble):
(JSC::RISCV64Disassembler::RTypeWithRoundingModeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::RTypeWithRoundingModeFSQRTFormatting::disassemble):
(JSC::RISCV64Disassembler::RTypeWithRoundingModeFCVTFormatting::disassemble):
(JSC::RISCV64Disassembler::RTypeWithAqRlDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::RTypeWithAqRlLRFormatting::disassemble):
(JSC::RISCV64Disassembler::R4TypeWithRoundingModeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::ITypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::ITypeImmediateAsOffsetFormatting::disassemble):
(JSC::RISCV64Disassembler::STypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::BTypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::UTypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::JTypeDefaultFormatting::disassemble):
(JSC::RISCV64Disassembler::FenceInstructionFormatting::disassemble):
(JSC::RISCV64Disassembler::FenceIInstructionFormatting::disassemble):
(JSC::RISCV64Disassembler::EnvironmentInstructionFormatting::disassemble):
(JSC::RISCV64Disassembler::DisassemblyFormatting::disassemble):
(JSC::RISCV64Disassembler::Disassembler::disassemble):
(JSC::RISCV64Disassembler::Disassembler<InsnType>::disassemble):
(JSC::RISCV64Disassembler::disassembleOpcode):
(JSC::tryToDisassemble):

Source/WTF:

* wtf/PlatformEnable.h: Enable RISCV64_DISASSEMBLER when necessary.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (285531 => 285532)


--- trunk/Source/_javascript_Core/ChangeLog	2021-11-09 21:36:48 UTC (rev 285531)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-11-09 21:53:02 UTC (rev 285532)
@@ -1,3 +1,132 @@
+2021-11-09  Zan Dobersek  <zdober...@igalia.com>
+
+        [RISCV64] Add assembly, disassembly infrastructure
+        https://bugs.webkit.org/show_bug.cgi?id=232870
+
+        Reviewed by Yusuke Suzuki.
+
+        Provide the necessary facilities for assembling and disassembling
+        RISC-V instructions. This is just a preliminary patch that introduces
+        the necessary assembly and disassembly infrastructure while actual
+        enhancements to RISCV64Assembler and MacroAssemblerRISCV64 classes are
+        left for later.
+
+        In RISCV64Assembler.h header, necessary helper functions, enumerations
+        and structs are introduced that enable crafting instruction values in
+        accordance with the base RISC-V specification. All the necessary
+        immediate and instruction types are supported, and the relevant
+        instruction definitions are introduced and become usable in future work.
+
+        For debugging purposes, a custom RISC-V disassembler is also introduced
+        and is enabled when appropriate. The implementation utilizes
+        functionality introduced in RISCV64Assembler.h, and different formatters
+        are introduced to handle special cases even among the established
+        instruction types.
+
+        * Sources.txt:
+        * assembler/RISCV64Assembler.h:
+        (JSC::RISCV64Instructions::registerValue):
+        (JSC::RISCV64Instructions::InstructionValue::InstructionValue):
+        (JSC::RISCV64Instructions::InstructionValue::field):
+        (JSC::RISCV64Instructions::InstructionValue::opcode):
+        (JSC::RISCV64Instructions::ImmediateBase::isValid):
+        (JSC::RISCV64Instructions::ImmediateBase::v):
+        (JSC::RISCV64Instructions::ImmediateBase::ImmediateBase):
+        (JSC::RISCV64Instructions::ImmediateBase::field):
+        (JSC::RISCV64Instructions::IImmediate::IImmediate):
+        (JSC::RISCV64Instructions::IImmediate::value):
+        (JSC::RISCV64Instructions::SImmediate::SImmediate):
+        (JSC::RISCV64Instructions::SImmediate::value):
+        (JSC::RISCV64Instructions::BImmediate::BImmediate):
+        (JSC::RISCV64Instructions::BImmediate::value):
+        (JSC::RISCV64Instructions::UImmediate::UImmediate):
+        (JSC::RISCV64Instructions::UImmediate::value):
+        (JSC::RISCV64Instructions::JImmediate::JImmediate):
+        (JSC::RISCV64Instructions::JImmediate::value):
+        (JSC::RISCV64Instructions::RegistersBase::Size):
+        (JSC::RISCV64Instructions::RTypeBase::construct):
+        (JSC::RISCV64Instructions::RTypeBase::matches):
+        (JSC::RISCV64Instructions::RTypeBase::rd):
+        (JSC::RISCV64Instructions::RTypeBase::rs1):
+        (JSC::RISCV64Instructions::RTypeBase::rs2):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::construct):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::matches):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rd):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rs1):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rs2):
+        (JSC::RISCV64Instructions::RTypeBaseWithRoundingMode::rm):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::construct):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::matches):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::rd):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::rs1):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::rs2):
+        (JSC::RISCV64Instructions::RTypeBaseWithAqRl::aqrl):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::construct):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::matches):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rd):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs1):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs2):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rs3):
+        (JSC::RISCV64Instructions::R4TypeBaseWithRoundingMode::rm):
+        (JSC::RISCV64Instructions::ITypeBase::construct):
+        (JSC::RISCV64Instructions::ITypeBase::matches):
+        (JSC::RISCV64Instructions::ITypeBase::rd):
+        (JSC::RISCV64Instructions::ITypeBase::rs1):
+        (JSC::RISCV64Instructions::STypeBase::construct):
+        (JSC::RISCV64Instructions::STypeBase::matches):
+        (JSC::RISCV64Instructions::STypeBase::rs1):
+        (JSC::RISCV64Instructions::STypeBase::rs2):
+        (JSC::RISCV64Instructions::BTypeBase::construct):
+        (JSC::RISCV64Instructions::BTypeBase::matches):
+        (JSC::RISCV64Instructions::BTypeBase::rs1):
+        (JSC::RISCV64Instructions::BTypeBase::rs2):
+        (JSC::RISCV64Instructions::UTypeBase::construct):
+        (JSC::RISCV64Instructions::UTypeBase::matches):
+        (JSC::RISCV64Instructions::UTypeBase::rd):
+        (JSC::RISCV64Instructions::JTypeBase::construct):
+        (JSC::RISCV64Instructions::JTypeBase::matches):
+        (JSC::RISCV64Instructions::JTypeBase::rd):
+        (JSC::RISCV64Instructions::SLLI::construct):
+        (JSC::RISCV64Instructions::SRLI::construct):
+        (JSC::RISCV64Instructions::SRAI::construct):
+        (JSC::RISCV64Instructions::SLLIW::construct):
+        (JSC::RISCV64Instructions::SRLIW::construct):
+        (JSC::RISCV64Instructions::SRAIW::construct):
+        (JSC::RISCV64Instructions::FCVTImpl::construct):
+        (JSC::RISCV64Instructions::FMVImpl::construct):
+        * disassembler/RISCV64Disassembler.cpp: Added.
+        (JSC::RISCV64Disassembler::StringBufferBase::data):
+        (JSC::RISCV64Disassembler::StringBufferBase::size):
+        (JSC::RISCV64Disassembler::StringBufferBase::createString):
+        (JSC::RISCV64Disassembler::registerName<RISCV64Instructions::RegistersBase::GType>):
+        (JSC::RISCV64Disassembler::registerName<RISCV64Instructions::RegistersBase::FType>):
+        (JSC::RISCV64Disassembler::roundingMode):
+        (JSC::RISCV64Disassembler::memoryOperationFlags):
+        (JSC::RISCV64Disassembler::aqrlFlags):
+        (JSC::RISCV64Disassembler::InstructionList::contains):
+        (JSC::RISCV64Disassembler::RTypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeR2Formatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeWithRoundingModeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeWithRoundingModeFSQRTFormatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeWithRoundingModeFCVTFormatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeWithAqRlDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::RTypeWithAqRlLRFormatting::disassemble):
+        (JSC::RISCV64Disassembler::R4TypeWithRoundingModeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::ITypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::ITypeImmediateAsOffsetFormatting::disassemble):
+        (JSC::RISCV64Disassembler::STypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::BTypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::UTypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::JTypeDefaultFormatting::disassemble):
+        (JSC::RISCV64Disassembler::FenceInstructionFormatting::disassemble):
+        (JSC::RISCV64Disassembler::FenceIInstructionFormatting::disassemble):
+        (JSC::RISCV64Disassembler::EnvironmentInstructionFormatting::disassemble):
+        (JSC::RISCV64Disassembler::DisassemblyFormatting::disassemble):
+        (JSC::RISCV64Disassembler::Disassembler::disassemble):
+        (JSC::RISCV64Disassembler::Disassembler<InsnType>::disassemble):
+        (JSC::RISCV64Disassembler::disassembleOpcode):
+        (JSC::tryToDisassemble):
+
 2021-11-09  Mikhail R. Gadelha  <mikh...@igalia.com>
 
         Refactoring and PutByVal cleanup

Modified: trunk/Source/_javascript_Core/Sources.txt (285531 => 285532)


--- trunk/Source/_javascript_Core/Sources.txt	2021-11-09 21:36:48 UTC (rev 285531)
+++ trunk/Source/_javascript_Core/Sources.txt	2021-11-09 21:53:02 UTC (rev 285532)
@@ -448,6 +448,7 @@
 disassembler/ARM64Disassembler.cpp
 disassembler/CapstoneDisassembler.cpp
 disassembler/Disassembler.cpp
+disassembler/RISCV64Disassembler.cpp
 disassembler/UDis86Disassembler.cpp
 disassembler/X86Disassembler.cpp
 

Modified: trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h (285531 => 285532)


--- trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h	2021-11-09 21:36:48 UTC (rev 285531)
+++ trunk/Source/_javascript_Core/assembler/RISCV64Assembler.h	2021-11-09 21:53:02 UTC (rev 285532)
@@ -29,6 +29,7 @@
 
 #include "AssemblerBuffer.h"
 #include "RISCV64Registers.h"
+#include <tuple>
 
 namespace JSC {
 
@@ -64,6 +65,1383 @@
 
 } // 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;

Added: trunk/Source/_javascript_Core/disassembler/RISCV64Disassembler.cpp (0 => 285532)


--- trunk/Source/_javascript_Core/disassembler/RISCV64Disassembler.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/disassembler/RISCV64Disassembler.cpp	2021-11-09 21:53:02 UTC (rev 285532)
@@ -0,0 +1,657 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#if ENABLE(DISASSEMBLER) && ENABLE(RISCV64_DISASSEMBLER)
+
+#include "MacroAssemblerCodeRef.h"
+#include "RISCV64Assembler.h"
+#include <array>
+#include <mutex>
+
+namespace JSC {
+
+namespace RISCV64Disassembler {
+
+template<size_t BufferSize>
+struct StringBufferBase {
+    char* data() { return buffer.data(); }
+    size_t size() { return sizeof(char) * buffer.size(); }
+
+    CString createString()
+    {
+        buffer[BufferSize - 1] = '\0';
+        return { buffer.data() };
+    }
+
+    std::array<char, BufferSize> buffer;
+};
+
+using StringBuffer = StringBufferBase<256>;
+using SmallStringBuffer = StringBufferBase<32>;
+
+template<typename RegisterType> const char* registerName(uint8_t) = delete;
+
+template<>
+const char* registerName<RISCV64Instructions::RegistersBase::GType>(uint8_t value)
+{
+    static const std::array<const char*, 32> s_gpRegNames {
+        "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+        "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+        "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+        "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31",
+    };
+
+    if (value < 32)
+        return s_gpRegNames[value];
+    return "<unknown>";
+}
+
+template<>
+const char* registerName<RISCV64Instructions::RegistersBase::FType>(uint8_t value)
+{
+    static const std::array<const char*, 32> s_fpRegNames {
+        "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+        "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+        "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+        "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+    };
+
+    if (value < 32)
+        return s_fpRegNames[value];
+    return "<unknown>";
+}
+
+const char* roundingMode(uint8_t value)
+{
+    using RISCV64Instructions::FPRoundingMode;
+
+    switch (value) {
+    case unsigned(FPRoundingMode::RNE):
+        return "rne";
+    case unsigned(FPRoundingMode::RTZ):
+        return "rtz";
+    case unsigned(FPRoundingMode::RDN):
+        return "rdn";
+    case unsigned(FPRoundingMode::RUP):
+        return "rup";
+    case unsigned(FPRoundingMode::RMM):
+        return "rmm";
+    case unsigned(FPRoundingMode::DYN):
+        return "dyn";
+    default:
+        break;
+    }
+
+    return "<unknown>";
+}
+
+SmallStringBuffer memoryOperationFlags(uint8_t value)
+{
+    using RISCV64Instructions::MemoryOperation;
+
+    SmallStringBuffer buffer;
+    if (!!value) {
+        snprintf(buffer.data(), buffer.size(), "%s%s%s%s",
+            (value & unsigned(MemoryOperation::I)) ? "i" : "",
+            (value & unsigned(MemoryOperation::O)) ? "o" : "",
+            (value & unsigned(MemoryOperation::R)) ? "r" : "",
+            (value & unsigned(MemoryOperation::W)) ? "w" : "");
+    } else
+        snprintf(buffer.data(), buffer.size(), "<none>");
+    return buffer;
+}
+
+const char* aqrlFlags(uint8_t value)
+{
+    using RISCV64Instructions::MemoryAccess;
+
+    switch (value) {
+    case 0:
+        return "";
+    case unsigned(MemoryAccess::Acquire):
+        return ".aq";
+    case unsigned(MemoryAccess::Release):
+        return ".rl";
+    case unsigned(MemoryAccess::AcquireRelease):
+        return ".aqrl";
+    default:
+        break;
+    }
+
+    return "<unknown>";
+}
+
+// A simple type that handles a parameter pack of instruction types, along with a contains<T>() helper that serves as a
+// compile-time check whether a given type is included in that parameter pack.
+
+template<typename... Args>
+struct InstructionList {
+    template<typename T>
+    static constexpr bool contains()
+    {
+        return (std::is_same_v<T, Args> || ...);
+    }
+};
+
+// To enable showing sensible disassembly, different instructions have to be formatted differently. Each such formatting
+// class specifies the list of instructions it can format, but generally instructions under a given formatter fall into
+// the same class of instructions. The disassemble() static function returns a CString holding the formatted data.
+
+struct RTypeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::ADD, RISCV64Instructions::SUB,
+        RISCV64Instructions::SLT, RISCV64Instructions::SLTU,
+        RISCV64Instructions::SLL, RISCV64Instructions::SRL, RISCV64Instructions::SRA,
+        RISCV64Instructions::XOR, RISCV64Instructions::OR, RISCV64Instructions::AND,
+        RISCV64Instructions::ADDW, RISCV64Instructions::SUBW,
+        RISCV64Instructions::SLLW, RISCV64Instructions::SRLW, RISCV64Instructions::SRAW,
+        RISCV64Instructions::FSGNJ_S, RISCV64Instructions::FSGNJ_D,
+        RISCV64Instructions::FSGNJN_S, RISCV64Instructions::FSGNJN_D,
+        RISCV64Instructions::FSGNJX_S, RISCV64Instructions::FSGNJX_D,
+        RISCV64Instructions::FMIN_S, RISCV64Instructions::FMIN_D,
+        RISCV64Instructions::FMAX_S, RISCV64Instructions::FMAX_D,
+        RISCV64Instructions::FEQ_S, RISCV64Instructions::FEQ_D,
+        RISCV64Instructions::FLT_S, RISCV64Instructions::FLT_D,
+        RISCV64Instructions::FLE_S, RISCV64Instructions::FLE_D,
+        RISCV64Instructions::MUL, RISCV64Instructions::MULH, RISCV64Instructions::MULHSU, RISCV64Instructions::MULHU,
+        RISCV64Instructions::DIV, RISCV64Instructions::DIVU, RISCV64Instructions::REM, RISCV64Instructions::REMU,
+        RISCV64Instructions::MULW, RISCV64Instructions::DIVW, RISCV64Instructions::DIVUW, RISCV64Instructions::REMW, RISCV64Instructions::REMUW>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s, %s",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)),
+            registerName<typename T::Registers::RS1>(T::rs1(insn)), registerName<typename T::Registers::RS2>(T::rs2(insn)));
+        return buffer.createString();
+    }
+};
+
+struct RTypeR2Formatting {
+    using List = InstructionList<
+        RISCV64Instructions::FMV_X_W, RISCV64Instructions::FMV_W_X,
+        RISCV64Instructions::FMV_X_D, RISCV64Instructions::FMV_D_X,
+        RISCV64Instructions::FCLASS_S, RISCV64Instructions::FCLASS_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s",
+            T::name,
+            registerName<typename T::Registers::RD>(T::rd(insn)), registerName<typename T::Registers::RS1>(T::rs1(insn)));
+        return buffer.createString();
+    }
+};
+
+struct RTypeWithRoundingModeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::FADD_S,
+        RISCV64Instructions::FADD_D,
+        RISCV64Instructions::FSUB_S,
+        RISCV64Instructions::FSUB_D,
+        RISCV64Instructions::FMUL_S,
+        RISCV64Instructions::FMUL_D,
+        RISCV64Instructions::FDIV_S,
+        RISCV64Instructions::FDIV_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+        uint8_t rm = T::rm(insn);
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s, %s%s%s",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)),
+            registerName<typename T::Registers::RS1>(T::rs1(insn)), registerName<typename T::Registers::RS2>(T::rs2(insn)),
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : ", rm:",
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : roundingMode(rm));
+        return buffer.createString();
+    }
+};
+
+struct RTypeWithRoundingModeFSQRTFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::FSQRT_S, RISCV64Instructions::FSQRT_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+        uint8_t rm = T::rm(insn);
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s%s%s",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)),
+            registerName<typename T::Registers::RS1>(T::rs1(insn)),
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : ", rm:",
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : roundingMode(rm));
+        return buffer.createString();
+    }
+};
+
+struct RTypeWithRoundingModeFCVTFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::FCVT_W_S, RISCV64Instructions::FCVT_WU_S,
+        RISCV64Instructions::FCVT_S_W, RISCV64Instructions::FCVT_S_WU,
+        RISCV64Instructions::FCVT_W_D, RISCV64Instructions::FCVT_WU_D,
+        RISCV64Instructions::FCVT_D_W, RISCV64Instructions::FCVT_D_WU,
+        RISCV64Instructions::FCVT_L_S, RISCV64Instructions::FCVT_LU_S,
+        RISCV64Instructions::FCVT_S_L, RISCV64Instructions::FCVT_S_LU,
+        RISCV64Instructions::FCVT_L_D, RISCV64Instructions::FCVT_LU_D,
+        RISCV64Instructions::FCVT_D_L, RISCV64Instructions::FCVT_D_LU,
+        RISCV64Instructions::FCVT_S_D, RISCV64Instructions::FCVT_D_S>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+        uint8_t rm = T::rm(insn);
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s%s%s",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)), registerName<typename T::Registers::RS1>(T::rs1(insn)),
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : ", rm:",
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : roundingMode(rm));
+        return buffer.createString();
+    }
+};
+
+struct RTypeWithAqRlDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::SC_W, RISCV64Instructions::SC_D,
+        RISCV64Instructions::AMOSWAP_W, RISCV64Instructions::AMOSWAP_D,
+        RISCV64Instructions::AMOADD_W, RISCV64Instructions::AMOADD_D,
+        RISCV64Instructions::AMOXOR_W, RISCV64Instructions::AMOXOR_D,
+        RISCV64Instructions::AMOAND_W, RISCV64Instructions::AMOAND_D,
+        RISCV64Instructions::AMOOR_W, RISCV64Instructions::AMOOR_D,
+        RISCV64Instructions::AMOMIN_W, RISCV64Instructions::AMOMIN_D,
+        RISCV64Instructions::AMOMAX_W, RISCV64Instructions::AMOMAX_D,
+        RISCV64Instructions::AMOMINU_W, RISCV64Instructions::AMOMINU_D,
+        RISCV64Instructions::AMOMAXU_W, RISCV64Instructions::AMOMAXU_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s%s %s, %s, %s",
+            T::name, aqrlFlags(T::aqrl(insn)), registerName<typename T::Registers::RD>(T::rd(insn)),
+            registerName<typename T::Registers::RS1>(T::rs1(insn)), registerName<typename T::Registers::RS2>(T::rs2(insn)));
+        return buffer.createString();
+    }
+};
+
+struct RTypeWithAqRlLRFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::LR_W, RISCV64Instructions::LR_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s%s %s, %s",
+            T::name, aqrlFlags(T::aqrl(insn)),
+            registerName<typename T::Registers::RD>(T::rd(insn)), registerName<typename T::Registers::RS1>(T::rs1(insn)));
+        return buffer.createString();
+    }
+};
+
+struct R4TypeWithRoundingModeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::FMADD_S, RISCV64Instructions::FMADD_D,
+        RISCV64Instructions::FMSUB_S, RISCV64Instructions::FMSUB_D,
+        RISCV64Instructions::FNMSUB_S, RISCV64Instructions::FNMSUB_D,
+        RISCV64Instructions::FNMADD_S, RISCV64Instructions::FNMADD_D>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+        uint8_t rm = T::rm(insn);
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s, %s, %s%s%s",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)), registerName<typename T::Registers::RS1>(T::rs1(insn)),
+            registerName<typename T::Registers::RS2>(T::rs2(insn)), registerName<typename T::Registers::RS3>(T::rs3(insn)),
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : ", rm:",
+            (rm == unsigned(RISCV64Instructions::FPRoundingMode::DYN)) ? "" : roundingMode(rm));
+        return buffer.createString();
+    }
+};
+
+struct ITypeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::ADDI, RISCV64Instructions::SLTI, RISCV64Instructions::SLTIU,
+        RISCV64Instructions::XORI, RISCV64Instructions::ORI, RISCV64Instructions::ANDI,
+        RISCV64Instructions::SLLI, RISCV64Instructions::SRLI, RISCV64Instructions::SRAI,
+        RISCV64Instructions::ADDIW, RISCV64Instructions::SLLIW, RISCV64Instructions::SRLIW, RISCV64Instructions::SRAIW>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s, %d",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)),
+            registerName<typename T::Registers::RS1>(T::rs1(insn)), RISCV64Instructions::IImmediate::value(insn));
+        return buffer.createString();
+    }
+};
+
+struct ITypeImmediateAsOffsetFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::JALR,
+        RISCV64Instructions::LB, RISCV64Instructions::LBU,
+        RISCV64Instructions::LH, RISCV64Instructions::LHU,
+        RISCV64Instructions::LW, RISCV64Instructions::LWU,
+        RISCV64Instructions::LD,
+        RISCV64Instructions::FLW, RISCV64Instructions::FLD>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %d(%s)",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)),
+            RISCV64Instructions::IImmediate::value(insn), registerName<typename T::Registers::RS1>(T::rs1(insn)));
+        return buffer.createString();
+    }
+};
+
+struct STypeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::SB, RISCV64Instructions::SH, RISCV64Instructions::SW, RISCV64Instructions::SD,
+        RISCV64Instructions::FSW, RISCV64Instructions::FSD>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %d(%s)",
+            T::name, registerName<typename T::Registers::RS2>(T::rs2(insn)),
+            RISCV64Instructions::SImmediate::value(insn), registerName<typename T::Registers::RS1>(T::rs1(insn)));
+        return buffer.createString();
+    }
+};
+
+struct BTypeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::BEQ,
+        RISCV64Instructions::BNE,
+        RISCV64Instructions::BLT,
+        RISCV64Instructions::BGE,
+        RISCV64Instructions::BLTU,
+        RISCV64Instructions::BGEU>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %s, %d",
+            T::name, registerName<typename T::Registers::RS1>(T::rs1(insn)), registerName<typename T::Registers::RS2>(T::rs2(insn)),
+            RISCV64Instructions::BImmediate::value(insn));
+        return buffer.createString();
+    }
+};
+
+struct UTypeDefaultFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::LUI, RISCV64Instructions::AUIPC>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s, %u",
+            T::name, registerName<typename T::Registers::RD>(T::rd(insn)), RISCV64Instructions::UImmediate::value(insn));
+        return buffer.createString();
+    }
+};
+
+struct JTypeDefaultFormatting {
+    using List = InstructionList<RISCV64Instructions::JAL>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %d(%s)",
+            T::name, RISCV64Instructions::JImmediate::value(insn), registerName<typename T::Registers::RD>(T::rd(insn)));
+        return buffer.createString();
+    }
+};
+
+struct FenceInstructionFormatting {
+    using List = InstructionList<RISCV64Instructions::FENCE>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(List::contains<T>());
+
+        auto immValue = RISCV64Instructions::IImmediate::value(insn);
+        auto predecessorFlags = memoryOperationFlags((immValue >> 4) & ((1 << 4) - 1));
+        auto successorFlags = memoryOperationFlags((immValue >> 0) & ((1 << 4) - 1));
+
+        StringBuffer buffer;
+        snprintf(buffer.data(), buffer.size(), "%s %s,%s",
+            T::name, predecessorFlags.data(), successorFlags.data());
+        return buffer.createString();
+    }
+};
+
+struct FenceIInstructionFormatting {
+    using List = InstructionList<RISCV64Instructions::FENCE_I>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue)
+    {
+        static_assert(List::contains<T>());
+
+        return { T::name };
+    }
+};
+
+struct EnvironmentInstructionFormatting {
+    using List = InstructionList<
+        RISCV64Instructions::ECALL, RISCV64Instructions::EBREAK>;
+
+    template<typename T>
+    static CString disassemble(RISCV64Instructions::InstructionValue)
+    {
+        static_assert(List::contains<T>());
+
+        return { T::name };
+    }
+};
+
+// The Disassembler struct below has a template parameter pack, containing a list of instructions through which it
+// should cascade and find a matching instruction type. When found, the DisassemblyFormatting class finds an
+// appropriate formatter and uses it to return the disassembly string for the given instruction value.
+
+template<typename T, typename FormattingType, typename... OtherFormattingTypes>
+struct DisassemblyFormattingImpl {
+    using Type = std::conditional_t<FormattingType::List::template contains<T>(),
+        FormattingType,
+        typename DisassemblyFormattingImpl<T, OtherFormattingTypes...>::Type>;
+};
+
+template<typename T, typename FormattingType>
+struct DisassemblyFormattingImpl<T, FormattingType> {
+    using Type = FormattingType;
+};
+
+template<typename T>
+struct DisassemblyFormatting {
+    using Type = typename DisassemblyFormattingImpl<T,
+        RTypeDefaultFormatting,
+        RTypeR2Formatting,
+        RTypeWithRoundingModeDefaultFormatting,
+        RTypeWithRoundingModeFSQRTFormatting,
+        RTypeWithRoundingModeFCVTFormatting,
+        RTypeWithAqRlDefaultFormatting,
+        RTypeWithAqRlLRFormatting,
+        R4TypeWithRoundingModeDefaultFormatting,
+        ITypeDefaultFormatting,
+        ITypeImmediateAsOffsetFormatting,
+        STypeDefaultFormatting,
+        BTypeDefaultFormatting,
+        UTypeDefaultFormatting,
+        JTypeDefaultFormatting,
+        FenceInstructionFormatting,
+        FenceIInstructionFormatting,
+        EnvironmentInstructionFormatting>::Type;
+
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        static_assert(Type::List::template contains<T>());
+        return Type::template disassemble<T>(insn);
+    }
+};
+
+template<typename InsnType, typename... OtherInsnTypes>
+struct Disassembler {
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        if (InsnType::matches(insn))
+            return DisassemblyFormatting<InsnType>::disassemble(insn);
+        return Disassembler<OtherInsnTypes...>::disassemble(insn);
+    }
+};
+
+template<typename InsnType>
+struct Disassembler<InsnType> {
+    static CString disassemble(RISCV64Instructions::InstructionValue insn)
+    {
+        if (InsnType::matches(insn))
+            return DisassemblyFormatting<InsnType>::disassemble(insn);
+        return { };
+    }
+};
+
+CString disassembleOpcode(uint32_t *pc)
+{
+    using namespace RISCV64Instructions;
+    using DisassemblerType = Disassembler<
+        // RV32I Base Instruction Set
+        LUI, AUIPC, JAL, JALR,
+        BEQ, BNE, BLT, BGE, BLTU, BGEU,
+        LB, LH, LW, LBU, LHU,
+        SB, SH, SW,
+        ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI,
+        ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND,
+        FENCE, ECALL, EBREAK,
+        // RV64I Base Instruction Set (in addition to RV32I)
+        LWU, LD, SD,
+        ADDIW, SLLIW, SRLIW, SRAIW,
+        ADDW, SUBW, SLLW, SRLW, SRAW,
+        // RV32/RV64 Zifencei Standard Extension
+        FENCE_I,
+        // RV32M Standard Extension
+        MUL, MULH, MULHSU, MULHU,
+        DIV, DIVU, REM, REMU,
+        // RV64M Standard Extension (in addition to RV32M)
+        MULW, DIVW, DIVUW, REMW, REMUW,
+        // RV32A Standard Extension
+        LR_W, SC_W,
+        AMOSWAP_W, AMOADD_W, AMOXOR_W, AMOAND_W, AMOOR_W, AMOMIN_W, AMOMAX_W, AMOMINU_W, AMOMAXU_W,
+        // RV64A Standard Extension (in addition to RV32A)
+        LR_D, SC_D,
+        AMOSWAP_D, AMOADD_D, AMOXOR_D, AMOAND_D, AMOOR_D, AMOMIN_D, AMOMAX_D, AMOMINU_D, AMOMAXU_D,
+        // RV32F Standard Extension
+        FLW, FSW,
+        FMADD_S, FMSUB_S, FNMSUB_S, FNMADD_S,
+        FADD_S, FSUB_S, FMUL_S, FDIV_S, FSQRT_S,
+        FSGNJ_S, FSGNJN_S, FSGNJX_S, FMIN_S, FMAX_S,
+        FCVT_W_S, FCVT_WU_S, FMV_X_W,
+        FEQ_S, FLT_S, FLE_S, FCLASS_S,
+        FCVT_S_W, FCVT_S_WU, FMV_W_X,
+        // RV64F Standard Extension (in addition to RV32F)
+        FCVT_L_S, FCVT_LU_S, FCVT_S_L, FCVT_S_LU,
+        // RV32D Standard Extension
+        FLD, FSD,
+        FMADD_D, FMSUB_D, FNMSUB_D, FNMADD_D,
+        FADD_D, FSUB_D, FMUL_D, FDIV_D, FSQRT_D,
+        FSGNJ_D, FSGNJN_D, FSGNJX_D, FMIN_D, FMAX_D,
+        FCVT_S_D, FCVT_D_S,
+        FEQ_D, FLT_D, FLE_D, FCLASS_D,
+        FCVT_W_D, FCVT_WU_D, FCVT_D_W, FCVT_D_WU,
+        // RV64D Standard Extension (in addition to RV32D)
+        FCVT_L_D, FCVT_LU_D, FMV_X_D,
+        FCVT_D_L, FCVT_D_LU, FMV_D_X>;
+
+    auto disassembly = DisassemblerType::disassemble(InstructionValue { *pc });
+    if (!disassembly.isNull())
+        return disassembly;
+    return CString { "<unrecognized opcode>" };
+}
+
+} // namespace RISCV64Disassembler
+
+bool tryToDisassemble(const MacroAssemblerCodePtr<DisassemblyPtrTag>& codePtr, size_t size, const char* prefix, PrintStream& out)
+{
+    uint32_t* currentPC = codePtr.untaggedExecutableAddress<uint32_t*>();
+    size_t byteCount = size;
+
+    while (byteCount) {
+        std::array<char, 20> pcString;
+        snprintf(pcString.data(), pcString.size(), "0x%lx", reinterpret_cast<uintptr_t>(currentPC));
+
+        out.printf("%s%16s: <%08x> %s\n", prefix, pcString.data(), *currentPC,
+            RISCV64Disassembler::disassembleOpcode(currentPC).data());
+
+        ++currentPC;
+        byteCount -= sizeof(uint32_t);
+    }
+
+    return true;
+}
+
+} // namespace JSC
+
+#endif // ENABLE(DISASSEMBLER) && ENABLE(RISCV64_DISASSEMBLER)

Modified: trunk/Source/WTF/ChangeLog (285531 => 285532)


--- trunk/Source/WTF/ChangeLog	2021-11-09 21:36:48 UTC (rev 285531)
+++ trunk/Source/WTF/ChangeLog	2021-11-09 21:53:02 UTC (rev 285532)
@@ -1,3 +1,12 @@
+2021-11-09  Zan Dobersek  <zdober...@igalia.com>
+
+        [RISCV64] Add assembly, disassembly infrastructure
+        https://bugs.webkit.org/show_bug.cgi?id=232870
+
+        Reviewed by Yusuke Suzuki.
+
+        * wtf/PlatformEnable.h: Enable RISCV64_DISASSEMBLER when necessary.
+
 2021-11-09  Megan Gardner  <megan_gard...@apple.com>
 
         Scroll To Text Fragment directive parsing

Modified: trunk/Source/WTF/wtf/PlatformEnable.h (285531 => 285532)


--- trunk/Source/WTF/wtf/PlatformEnable.h	2021-11-09 21:36:48 UTC (rev 285531)
+++ trunk/Source/WTF/wtf/PlatformEnable.h	2021-11-09 21:53:02 UTC (rev 285532)
@@ -625,7 +625,11 @@
 #define ENABLE_ARM64_DISASSEMBLER 1
 #endif
 
-#if !defined(ENABLE_DISASSEMBLER) && (ENABLE(UDIS86) || ENABLE(ARM64_DISASSEMBLER) || (ENABLE(JIT) && USE(CAPSTONE)))
+#if !defined(ENABLE_RISCV64_DISASSEMBLER) && ENABLE(JIT) && CPU(RISCV64) && !USE(CAPSTONE)
+#define ENABLE_RISCV64_DISASSEMBLER 1
+#endif
+
+#if !defined(ENABLE_DISASSEMBLER) && (ENABLE(UDIS86) || ENABLE(ARM64_DISASSEMBLER) || ENABLE(RISCV64_DISASSEMBLER) || (ENABLE(JIT) && USE(CAPSTONE)))
 #define ENABLE_DISASSEMBLER 1
 #endif
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to