From 9ad13e1c223d6b19d408dcce1f660811908173cb Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 7 May 2026 19:26:29 +0200 Subject: [PATCH 01/18] Created RISC-V language module --- CMakeLists.txt | 2 ++ src/asmio/elf/object.cpp | 1 + src/asmio/riscv/module.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/asmio/riscv/module.hpp | 18 ++++++++++++++++++ src/asmio/riscv/writer.cpp | 13 +++++++++++++ src/asmio/riscv/writer.hpp | 15 +++++++++++++++ test/riscv.cpp | 18 ++++++++++++++++++ test/test.hpp | 6 +++++- 8 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/asmio/riscv/module.cpp create mode 100644 src/asmio/riscv/module.hpp create mode 100644 src/asmio/riscv/writer.cpp create mode 100644 src/asmio/riscv/writer.hpp create mode 100644 test/riscv.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d3a6b16..89291f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ FetchContent_MakeAvailable(vstl) set(ASMIO_WRITERS src/asmio/x86/writer.hpp src/asmio/aarch64/writer.hpp + src/asmio/riscv/writer.hpp ) set(ASMIO_BRIDGES "${ASMIO_WRITERS}") @@ -68,6 +69,7 @@ add_executable(test test/x86.cpp test/aarch64.cpp test/elf.cpp + test/riscv.cpp ) target_link_libraries(test PRIVATE asmiov) target_include_directories(test PRIVATE ${ASMIOV_INCLUDE_DIRS}) diff --git a/src/asmio/elf/object.cpp b/src/asmio/elf/object.cpp index 58c4c3d..c0bfa34 100644 --- a/src/asmio/elf/object.cpp +++ b/src/asmio/elf/object.cpp @@ -2,6 +2,7 @@ #include "object.hpp" #include +#include #include namespace asmio { diff --git a/src/asmio/riscv/module.cpp b/src/asmio/riscv/module.cpp new file mode 100644 index 0000000..f079fcc --- /dev/null +++ b/src/asmio/riscv/module.cpp @@ -0,0 +1,38 @@ +#include "module.hpp" +#include "writer.hpp" + +#include + +namespace asmio::riscv { + + using namespace tasml; + +# include "generated/riscv.hpp" + + /* + * class LanguageModule + */ + + const char* LanguageModule::name() const { + return "riscv"; + } + + FeatureSet LanguageModule::features() const { + return {}; + } + + void LanguageModule::parse(ErrorHandler& reporter, TokenStream stream, SegmentedBuffer& buffer) const { + BufferWriter writer {buffer}; + + if (try_parse_instruction(stream, writer)) { + return; + } + + Module::parse(reporter, stream, buffer); + } + + ElfMachine LanguageModule::machine() const { + return ElfMachine::RISCV; + } + +} diff --git a/src/asmio/riscv/module.hpp b/src/asmio/riscv/module.hpp new file mode 100644 index 0000000..8391e1f --- /dev/null +++ b/src/asmio/riscv/module.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +namespace asmio::riscv { + + struct LanguageModule : Module { + + const char* name() const override; + FeatureSet features() const override; + void parse(tasml::ErrorHandler& reporter, tasml::TokenStream stream, SegmentedBuffer& buffer) const override; + ElfMachine machine() const override; + + }; + + REGISTER_MODULE(LanguageModule); + +} diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp new file mode 100644 index 0000000..058a506 --- /dev/null +++ b/src/asmio/riscv/writer.cpp @@ -0,0 +1,13 @@ +#include "writer.hpp" + +namespace asmio::riscv { + + /* + * class BufferWriter + */ + + BufferWriter::BufferWriter(SegmentedBuffer& buffer) + : BasicBufferWriter(buffer) { + } + +} \ No newline at end of file diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp new file mode 100644 index 0000000..e4b5720 --- /dev/null +++ b/src/asmio/riscv/writer.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace asmio::riscv { + + class BufferWriter : public BasicBufferWriter { + + public: + + BufferWriter(SegmentedBuffer& buffer); + + }; + +} \ No newline at end of file diff --git a/test/riscv.cpp b/test/riscv.cpp new file mode 100644 index 0000000..e384fdb --- /dev/null +++ b/test/riscv.cpp @@ -0,0 +1,18 @@ +#include + +#include "test.hpp" +#include "vstl.hpp" + +namespace test { + + using namespace asmio; + using namespace asmio::riscv; + + TEST (riscv_module) { + + LanguageModule module; + CHECK(std::string(module.name()), "riscv"); + + }; + +} \ No newline at end of file diff --git a/test/test.hpp b/test/test.hpp index 962f676..35d2b10 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -2,6 +2,10 @@ #include #include +#include +#include +#include +#include namespace test { @@ -20,7 +24,7 @@ namespace test { asmio::ObjectFile baked = asmio::to_elf(buffer, asmio::Label::UNSET).bake(); asmio::util::TempFile temp {baked}; - std::string out = asmio::call_shell("objdump --visualize-jumps -wxd " + extra_flags + temp.path()); + std::string out = asmio::call_shell("riscv64-linux-gnu-objdump --visualize-jumps -wxd " + extra_flags + temp.path()); printf("%s\n", out.c_str()); printf("\nAuto-generated assertions:\n\n"); From 4fbecd9a6bb34dc815d7f62bf9682cf034eac520 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 7 May 2026 21:56:01 +0200 Subject: [PATCH 02/18] Added basic Risc-V encodings --- src/asmio/riscv/argument/registry.hpp | 57 +++++++++++++++++++++++++++ src/asmio/riscv/module.cpp | 26 ++++++++++++ src/asmio/riscv/writer.cpp | 43 +++++++++++++++++++- src/asmio/riscv/writer.hpp | 12 ++++++ test/riscv.cpp | 14 +++++-- 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 src/asmio/riscv/argument/registry.hpp diff --git a/src/asmio/riscv/argument/registry.hpp b/src/asmio/riscv/argument/registry.hpp new file mode 100644 index 0000000..f17d08d --- /dev/null +++ b/src/asmio/riscv/argument/registry.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +namespace asmio::riscv { + + struct PACKED Registry { + + public: + + const uint8_t reg; // registry ARM code + + constexpr Registry(uint8_t reg) + : reg(reg) {} + + }; + + /// Reference arbitrary register, 'number' MUST be in range [0, 31] + constexpr Registry X(uint8_t number) { + return {static_cast(number & 0b11111)}; + } + + // general purpose QWORD registers + constexpr Registry X0 = X(0); // always equal 0 + constexpr Registry X1 = X(1); + constexpr Registry X2 = X(2); + constexpr Registry X3 = X(3); + constexpr Registry X4 = X(4); + constexpr Registry X5 = X(5); + constexpr Registry X6 = X(6); + constexpr Registry X7 = X(7); + constexpr Registry X8 = X(8); + constexpr Registry X9 = X(9); + constexpr Registry X10 = X(10); + constexpr Registry X11 = X(11); + constexpr Registry X12 = X(12); + constexpr Registry X13 = X(13); + constexpr Registry X14 = X(14); + constexpr Registry X15 = X(15); + constexpr Registry X16 = X(16); + constexpr Registry X17 = X(17); + constexpr Registry X18 = X(18); + constexpr Registry X19 = X(19); + constexpr Registry X20 = X(20); + constexpr Registry X21 = X(21); + constexpr Registry X22 = X(22); + constexpr Registry X23 = X(23); + constexpr Registry X24 = X(24); + constexpr Registry X25 = X(25); + constexpr Registry X26 = X(26); + constexpr Registry X27 = X(27); + constexpr Registry X28 = X(28); + constexpr Registry X29 = X(29); + constexpr Registry X30 = X(30); + constexpr Registry X31 = X(31); + +} \ No newline at end of file diff --git a/src/asmio/riscv/module.cpp b/src/asmio/riscv/module.cpp index f079fcc..d49a080 100644 --- a/src/asmio/riscv/module.cpp +++ b/src/asmio/riscv/module.cpp @@ -7,6 +7,32 @@ namespace asmio::riscv { using namespace tasml; + template + T parse_argument(TokenStream stream); + + template + T parse_argument(TokenStream stream) { + return stream.expect(Token::INT).as_int(); + } + + template <> + Registry parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + if (raw[0] == 'x') { + int index = util::parse_decimal(raw.substr(1)); + + if (index >= 0 && index <= 31) { + return X(index); + } + + throw std::runtime_error {"Invalid register number, expected value in range [0, 31]"}; + } + + throw std::runtime_error {"Invalid argument format, expected register"}; + } + # include "generated/riscv.hpp" /* diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 058a506..81226af 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -1,7 +1,6 @@ #include "writer.hpp" namespace asmio::riscv { - /* * class BufferWriter */ @@ -10,4 +9,46 @@ namespace asmio::riscv { : BasicBufferWriter(buffer) { } + void BufferWriter::put_inst_r(uint8_t func7, Registry rs2, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7) { + put_dword((func7 & 0b1111111) << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | opc7 & 0b1111111); + } + + void BufferWriter::put_inst_i(uint16_t imm12, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7) { + put_dword((imm12 & 0xfff) << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | opc7 & 0b1111111); + } + + void BufferWriter::put_inst_s(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7) { + const uint32_t hi = (imm12 & 0b1111111'00000) >> 5; + const uint32_t lo = (imm12 & 0b0000000'11111) >> 0; + + put_dword(hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 7 | opc7 & 0b1111111); + } + + void BufferWriter::put_inst_b(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7) { + const uint32_t si = (imm12 & 0b1'0'00000'0000) >> 10; + const uint32_t b7 = (imm12 & 0b0'1'00000'0000) >> 9; + const uint32_t hi = (imm12 & 0b0'0'11111'0000) >> 4; + const uint32_t lo = (imm12 & 0b0'0'00000'1111) >> 0; + + put_dword(si << 31 | hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 8 | b7 << 7 | opc7 & 0b1111111); + + } + + void BufferWriter::put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7) { + put_dword(imm20 << 20 | rd.reg << 7 | opc7 & 0b1111111); + } + + void BufferWriter::put_inst_j(uint32_t imm20, Registry rd, uint8_t opc7) { + const uint32_t si = (imm20 & 0b1'00000000'0'0000000000) >> 19; + const uint32_t hi = (imm20 & 0b0'11111111'0'0000000000) >> 11; + const uint32_t b2 = (imm20 & 0b0'00000000'1'0000000000) >> 10; + const uint32_t lo = (imm20 & 0b0'00000000'0'1111111111) >> 0; + + put_dword(si << 31 | lo << 21 | b2 << 20 | hi << 12 | rd.reg << 7 | opc7 & 0b1111111); + } + + void BufferWriter::put_add(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0b000, rd, 0b0010011); + } + } \ No newline at end of file diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index e4b5720..9f29f13 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -1,15 +1,27 @@ #pragma once #include +#include namespace asmio::riscv { class BufferWriter : public BasicBufferWriter { + protected: + + void put_inst_r(uint8_t func7, Registry rs2, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7); + void put_inst_i(uint16_t imm12, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7); + void put_inst_s(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7); + void put_inst_b(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7); + void put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7); + void put_inst_j(uint32_t imm20, Registry rd, uint8_t opc7); + public: BufferWriter(SegmentedBuffer& buffer); + INST put_add(Registry rd, Registry rs, int16_t imm12); ///< Add signed 12 bit immediate to rs and save result to rd + }; } \ No newline at end of file diff --git a/test/riscv.cpp b/test/riscv.cpp index e384fdb..62ca983 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -1,4 +1,5 @@ #include +#include #include "test.hpp" #include "vstl.hpp" @@ -8,10 +9,17 @@ namespace test { using namespace asmio; using namespace asmio::riscv; - TEST (riscv_module) { + TEST (riscv_check_add) { - LanguageModule module; - CHECK(std::string(module.name()), "riscv"); + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_add(X1, X7, 13); + + segmented.link(0); + std::vector s0 = {0x93, 0x80, 0xd3, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx }; From 2b5b4730c5149cdf337412d37728a707a7ae4dc1 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 14 May 2026 16:30:35 +0200 Subject: [PATCH 03/18] Added some basic Risc-V instructions --- src/asmio/riscv/writer.cpp | 124 ++++++++++++++++++++++++++++++++++--- src/asmio/riscv/writer.hpp | 31 ++++++++++ test/riscv.cpp | 80 +++++++++++++++++++++++- 3 files changed, 227 insertions(+), 8 deletions(-) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 81226af..5f63aeb 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -10,18 +10,18 @@ namespace asmio::riscv { } void BufferWriter::put_inst_r(uint8_t func7, Registry rs2, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7) { - put_dword((func7 & 0b1111111) << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | opc7 & 0b1111111); + put_dword((func7 & 0b1111111) << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_i(uint16_t imm12, Registry rs1, uint8_t func3, Registry rd, uint8_t opc7) { - put_dword((imm12 & 0xfff) << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | opc7 & 0b1111111); + put_dword((imm12 & 0xfff) << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | rd.reg << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_s(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7) { const uint32_t hi = (imm12 & 0b1111111'00000) >> 5; const uint32_t lo = (imm12 & 0b0000000'11111) >> 0; - put_dword(hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 7 | opc7 & 0b1111111); + put_dword(hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_b(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7) { @@ -30,12 +30,12 @@ namespace asmio::riscv { const uint32_t hi = (imm12 & 0b0'0'11111'0000) >> 4; const uint32_t lo = (imm12 & 0b0'0'00000'1111) >> 0; - put_dword(si << 31 | hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 8 | b7 << 7 | opc7 & 0b1111111); + put_dword(si << 31 | hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 8 | b7 << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7) { - put_dword(imm20 << 20 | rd.reg << 7 | opc7 & 0b1111111); + put_dword(imm20 << 20 | rd.reg << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_j(uint32_t imm20, Registry rd, uint8_t opc7) { @@ -44,11 +44,121 @@ namespace asmio::riscv { const uint32_t b2 = (imm20 & 0b0'00000000'1'0000000000) >> 10; const uint32_t lo = (imm20 & 0b0'00000000'0'1111111111) >> 0; - put_dword(si << 31 | lo << 21 | b2 << 20 | hi << 12 | rd.reg << 7 | opc7 & 0b1111111); + put_dword(si << 31 | lo << 21 | b2 << 20 | hi << 12 | rd.reg << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_add(Registry rd, Registry rs, int16_t imm12) { - put_inst_i(imm12, rs, 0b000, rd, 0b0010011); + put_inst_i(imm12, rs, 0x0, rd, 0b0010011); + } + + void BufferWriter::put_xor(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x4, rd, 0b0010011); + } + + void BufferWriter::put_or(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x6, rd, 0b0010011); + } + + void BufferWriter::put_and(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x7, rd, 0b0010011); + } + + void BufferWriter::put_sll(Registry rd, Registry rs, int16_t imm12) { + imm12 &= 0b0000000'11111; + put_inst_i(imm12, rs, 0x1, rd, 0b0010011); + } + + void BufferWriter::put_srl(Registry rd, Registry rs, int16_t imm12) { + imm12 &= 0b0000000'11111; + put_inst_i(imm12, rs, 0x5, rd, 0b0010011); + + } + + void BufferWriter::put_sra(Registry rd, Registry rs, int16_t imm12) { + imm12 &= 0b0000000'11111; + imm12 |= 0b0100000'00000; + put_inst_i(imm12, rs, 0x5, rd, 0b0010011); + + } + + void BufferWriter::put_slt(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x2, rd, 0b0010011); + } + + void BufferWriter::put_sltu(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x3, rd, 0b0010011); + } + + void BufferWriter::put_add(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x0, rd, 0b0110011); + } + + void BufferWriter::put_sub(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x20, rs2, rs1, 0x0, rd, 0b0110011); + } + + void BufferWriter::put_xor(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x4, rd, 0b0110011); + } + + void BufferWriter::put_or(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x6, rd, 0b0110011); + } + + void BufferWriter::put_and(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x7, rd, 0b0110011); + } + + void BufferWriter::put_sll(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x1, rd, 0b0110011); + } + + void BufferWriter::put_srl(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x5, rd, 0b0110011); + } + + void BufferWriter::put_sra(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x20, rs2, rs1, 0x5, rd, 0b0110011); + } + + void BufferWriter::put_slt(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x2, rd, 0b0110011); + } + + void BufferWriter::put_sltu(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x00, rs2, rs1, 0x3, rd, 0b0110011); + } + + void BufferWriter::put_lb(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x0, rd, 0b0000011); + } + + void BufferWriter::put_lh(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x1, rd, 0b0000011); + } + + void BufferWriter::put_lw(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x2, rd, 0b0000011); + } + + void BufferWriter::put_lbu(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x4, rd, 0b0000011); + } + + void BufferWriter::put_lhu(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x5, rd, 0b0000011); + } + + void BufferWriter::put_sb(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs2, rs1, 0x0, 0b0000011); + } + + void BufferWriter::put_sh(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs2, rs1, 0x1, 0b0000011); + } + + void BufferWriter::put_sw(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs2, rs1, 0x2, 0b0000011); } } \ No newline at end of file diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 9f29f13..64be6d5 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -21,6 +21,37 @@ namespace asmio::riscv { BufferWriter(SegmentedBuffer& buffer); INST put_add(Registry rd, Registry rs, int16_t imm12); ///< Add signed 12 bit immediate to rs and save result to rd + INST put_xor(Registry rd, Registry rs, int16_t imm12); ///< Bitwise XOR 12 bit immediate rs and save result to rd + INST put_or(Registry rd, Registry rs, int16_t imm12); ///< Bitwise OR 12 bit immediate rs and save result to rd + INST put_and(Registry rd, Registry rs, int16_t imm12); ///< Bitwise AND 12 bit immediate rs and save result to rd + INST put_sll(Registry rd, Registry rs, int16_t imm12); ///< Shift register rs left by a 12 bit immediate and save result to rd + INST put_srl(Registry rd, Registry rs, int16_t imm12); ///< Shift register rs right logically by a 12 bit immediate and save result to rd + INST put_sra(Registry rd, Registry rs, int16_t imm12); ///< Shift register rs right arythetically by a 12 bit immediate and save result to rd + INST put_slt(Registry rd, Registry rs, int16_t imm12); ///< Set rd to 1 if rs is less than a 12 bit immediate, and to 0 otherwise (signed) + INST put_sltu(Registry rd, Registry rs, int16_t imm12); ///< Set rd to 1 if rs1 is less than a 12 bit immediate, and to 0 otherwise (unsigned) + + INST put_add(Registry rd, Registry rs1, Registry rs2); ///< Add register rs1 to rs2 and save result to rd + INST put_sub(Registry rd, Registry rs1, Registry rs2); ///< Subtract register rs1 from rs2 and save result to rd + INST put_xor(Registry rd, Registry rs1, Registry rs2); ///< Bitwise XOR register rs1 with rs2 and save result to rd + INST put_or(Registry rd, Registry rs1, Registry rs2); ///< Bitwise OR register rs1 with rs2 and save result to rd + INST put_and(Registry rd, Registry rs1, Registry rs2); ///< Bitwise AND register rs1 with rs2 and save result to rd + INST put_sll(Registry rd, Registry rs1, Registry rs2); ///< Shift register rs1 left by rs2 and save result to rd + INST put_srl(Registry rd, Registry rs1, Registry rs2); ///< Shift register rs1 right logically by rs2 and save result to rd + INST put_sra(Registry rd, Registry rs1, Registry rs2); ///< Shift register rs1 right arythetically by rs2 and save result to rd + INST put_slt(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (signed) + INST put_sltu(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (unsigned) + + // TODO: check sign extension behaviour of LW in RV64 + + INST put_lb(Registry rd, Registry rs, int16_t imm12); ///< Load byte at rs + imm12 into rd (with sign extension) + INST put_lh(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with sign extension) + INST put_lw(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd + INST put_lbu(Registry rd, Registry rs, int16_t imm12); ///< Load byte at rs + imm12 into rd (with zero extension) + INST put_lhu(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with zero extension) + + INST put_sb(Registry rs1, Registry rs2, int16_t imm12); ///< Store byte from rs2 at rs1 + imm + INST put_sh(Registry rs1, Registry rs2, int16_t imm12); ///< Store word from rs2 at rs1 + imm + INST put_sw(Registry rs1, Registry rs2, int16_t imm12); ///< Store dword from rs2 at rs1 + imm }; diff --git a/test/riscv.cpp b/test/riscv.cpp index 62ca983..1ac3978 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -9,7 +9,7 @@ namespace test { using namespace asmio; using namespace asmio::riscv; - TEST (riscv_check_add) { + TEST (rv32i_check_add) { SegmentedBuffer segmented; BufferWriter writer {segmented}; @@ -23,4 +23,82 @@ namespace test { }; + TEST (rv32i_check_basic_op_imm) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_add(X0, X4, 12); + writer.put_xor(X4, X11, 7); + writer.put_or(X11, X12, 77); + writer.put_and(X12, X11, 88); + writer.put_sll(X11, X3, 7); + writer.put_srl(X3, X7, 4); + writer.put_sra(X7, X1, 3); + writer.put_slt(X1, X12, 3); + writer.put_sltu(X12, X2, 1000); + + segmented.link(0); + std::vector s0 = {0x13, 0x00, 0xc2, 0x00, 0x13, 0xc2, 0x75, 0x00, 0x93, 0x65, 0xd6, 0x04, 0x13, 0xf6, 0x85, 0x05, 0x93, 0x95, 0x71, 0x00, 0x93, 0xd1, 0x43, 0x00, 0x93, 0xd3, 0x30, 0x40, 0x93, 0x20, 0x36, 0x00, 0x13, 0x36, 0x81, 0x3e}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (rv32i_check_basic_op_reg) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_add(X0, X4, X22); + writer.put_sub(X0, X4, X22); + writer.put_xor(X4, X11, X22); + writer.put_or(X11, X12, X22); + writer.put_and(X12, X11, X22); + writer.put_sll(X11, X3, X22); + writer.put_srl(X3, X7, X22); + writer.put_sra(X7, X1, X22); + writer.put_slt(X1, X12, X22); + writer.put_sltu(X12, X2, X22); + + segmented.link(0); + std::vector s0 = {0x33, 0x00, 0x62, 0x01, 0x33, 0x00, 0x62, 0x41, 0x33, 0xc2, 0x65, 0x01, 0xb3, 0x65, 0x66, 0x01, 0x33, 0xf6, 0x65, 0x01, 0xb3, 0x95, 0x61, 0x01, 0xb3, 0xd1, 0x63, 0x01, 0xb3, 0xd3, 0x60, 0x41, 0xb3, 0x20, 0x66, 0x01, 0x33, 0x36, 0x61, 0x01}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (rv32i_check_basic_loads) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_lb(X0, X5, 100); + writer.put_lh(X1, X6, 200); + writer.put_lw(X2, X7, 300); + writer.put_lbu(X3, X8, 400); + writer.put_lhu(X4, X9, 500); + + segmented.link(0); + std::vector s0 = {0x03, 0x80, 0x42, 0x06, 0x83, 0x10, 0x83, 0x0c, 0x03, 0xa1, 0xc3, 0x12, 0x83, 0x41, 0x04, 0x19, 0x03, 0xd2, 0x44, 0x1f}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (rv32i_check_basic_stores) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_sb(X1, X4, 0x100); + writer.put_sh(X2, X5, 0x200); + writer.put_sw(X3, X6, 0x300); + + // FIXME + SKIP("Invalid decode"); + + }; + } \ No newline at end of file From 81616df38296cf6e42a4d1e4af25c5d9d5a87ddb Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 15 May 2026 14:41:23 +0200 Subject: [PATCH 04/18] Finish R64 load/stores --- src/asmio/riscv/writer.cpp | 36 ++++++++++++++++++++++++++++-------- src/asmio/riscv/writer.hpp | 21 +++++++++++++-------- test/riscv.cpp | 35 +++++++++++++++++++++++++++-------- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 5f63aeb..6755569 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -133,11 +133,11 @@ namespace asmio::riscv { put_inst_i(imm12, rs, 0x0, rd, 0b0000011); } - void BufferWriter::put_lh(Registry rd, Registry rs, int16_t imm12) { + void BufferWriter::put_lw(Registry rd, Registry rs, int16_t imm12) { put_inst_i(imm12, rs, 0x1, rd, 0b0000011); } - void BufferWriter::put_lw(Registry rd, Registry rs, int16_t imm12) { + void BufferWriter::put_ld(Registry rd, Registry rs, int16_t imm12) { put_inst_i(imm12, rs, 0x2, rd, 0b0000011); } @@ -145,20 +145,40 @@ namespace asmio::riscv { put_inst_i(imm12, rs, 0x4, rd, 0b0000011); } - void BufferWriter::put_lhu(Registry rd, Registry rs, int16_t imm12) { + void BufferWriter::put_lwu(Registry rd, Registry rs, int16_t imm12) { put_inst_i(imm12, rs, 0x5, rd, 0b0000011); } - void BufferWriter::put_sb(Registry rs1, Registry rs2, int16_t imm12) { - put_inst_s(imm12, rs2, rs1, 0x0, 0b0000011); + void BufferWriter::put_ldu(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x6, rd, 0b0000011); } - void BufferWriter::put_sh(Registry rs1, Registry rs2, int16_t imm12) { - put_inst_s(imm12, rs2, rs1, 0x1, 0b0000011); + void BufferWriter::put_lq(Registry rd, Registry rs, int16_t imm12) { + put_inst_i(imm12, rs, 0x3, rd, 0b0000011); + } + + void BufferWriter::put_sb(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs1, rs2, 0x0, 0b0100011); } void BufferWriter::put_sw(Registry rs1, Registry rs2, int16_t imm12) { - put_inst_s(imm12, rs2, rs1, 0x2, 0b0000011); + put_inst_s(imm12, rs1, rs2, 0x1, 0b0100011); + } + + void BufferWriter::put_sd(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs1, rs2, 0x2, 0b0100011); + } + + void BufferWriter::put_sq(Registry rs1, Registry rs2, int16_t imm12) { + put_inst_s(imm12, rs1, rs2, 0x3, 0b0100011); + } + + void BufferWriter::put_ecall() { + put_inst_i(0, X0, 0x0, X0, 0b1110011); + } + + void BufferWriter::put_ebreak() { + put_inst_i(1, X0, 0x0, X0, 0b1110011); } } \ No newline at end of file diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 64be6d5..0176e5f 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -41,17 +41,22 @@ namespace asmio::riscv { INST put_slt(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (signed) INST put_sltu(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (unsigned) - // TODO: check sign extension behaviour of LW in RV64 - INST put_lb(Registry rd, Registry rs, int16_t imm12); ///< Load byte at rs + imm12 into rd (with sign extension) - INST put_lh(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with sign extension) - INST put_lw(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd + INST put_lw(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with sign extension) + INST put_ld(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd (with sign extension) INST put_lbu(Registry rd, Registry rs, int16_t imm12); ///< Load byte at rs + imm12 into rd (with zero extension) - INST put_lhu(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with zero extension) + INST put_lwu(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with zero extension) + INST put_ldu(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd (with zero extension) + INST put_lq(Registry rd, Registry rs, int16_t imm12); ///< Load qword at rs + imm12 into rd + + + INST put_sb(Registry rs1, Registry rs2, int16_t imm12); ///< Store byte from rs1 at rs2 + imm + INST put_sw(Registry rs1, Registry rs2, int16_t imm12); ///< Store word from rs1 at rs2 + imm + INST put_sd(Registry rs1, Registry rs2, int16_t imm12); ///< Store dword from rs1 at rs2 + imm + INST put_sq(Registry rs1, Registry rs2, int16_t imm12); ///< Store qword from rs1 at rs2 + imm - INST put_sb(Registry rs1, Registry rs2, int16_t imm12); ///< Store byte from rs2 at rs1 + imm - INST put_sh(Registry rs1, Registry rs2, int16_t imm12); ///< Store word from rs2 at rs1 + imm - INST put_sw(Registry rs1, Registry rs2, int16_t imm12); ///< Store dword from rs2 at rs1 + imm + INST put_ecall(); ///< Environment Call + INST put_ebreak(); ///< Environment Break }; diff --git a/test/riscv.cpp b/test/riscv.cpp index 1ac3978..dbe6e01 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -75,13 +75,15 @@ namespace test { segmented.elf_machine = ElfMachine::RISCV; writer.put_lb(X0, X5, 100); - writer.put_lh(X1, X6, 200); - writer.put_lw(X2, X7, 300); + writer.put_lw(X1, X6, 200); + writer.put_ld(X2, X7, 300); writer.put_lbu(X3, X8, 400); - writer.put_lhu(X4, X9, 500); + writer.put_lwu(X4, X9, 500); + writer.put_ldu(X4, X9, 600); + writer.put_lq(X4, X9, 700); segmented.link(0); - std::vector s0 = {0x03, 0x80, 0x42, 0x06, 0x83, 0x10, 0x83, 0x0c, 0x03, 0xa1, 0xc3, 0x12, 0x83, 0x41, 0x04, 0x19, 0x03, 0xd2, 0x44, 0x1f}; + std::vector s0 = {0x03, 0x80, 0x42, 0x06, 0x83, 0x10, 0x83, 0x0c, 0x03, 0xa1, 0xc3, 0x12, 0x83, 0x41, 0x04, 0x19, 0x03, 0xd2, 0x44, 0x1f, 0x03, 0xe2, 0x84, 0x25, 0x03, 0xb2, 0xc4, 0x2b}; CHECK(segmented.segments()[0].buffer, s0); // .rwx }; @@ -93,11 +95,28 @@ namespace test { segmented.elf_machine = ElfMachine::RISCV; writer.put_sb(X1, X4, 0x100); - writer.put_sh(X2, X5, 0x200); - writer.put_sw(X3, X6, 0x300); + writer.put_sw(X2, X5, 0x200); + writer.put_sd(X3, X6, 0x300); + writer.put_sq(X4, X7, 0x400); - // FIXME - SKIP("Invalid decode"); + segmented.link(0); + std::vector s0 = {0x23, 0x00, 0x12, 0x10, 0x23, 0x90, 0x22, 0x20, 0x23, 0x20, 0x33, 0x30, 0x23, 0xb0, 0x43, 0x40}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (rv32i_check_ecall_ebreak) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_ecall(); + writer.put_ebreak(); + + segmented.link(0); + std::vector s0 = {0x73, 0x00, 0x00, 0x00, 0x73, 0x00, 0x10, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx }; From 052ffc4e568f7973c00bb9a934d6451f88b57a49 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Mon, 18 May 2026 14:20:25 +0200 Subject: [PATCH 05/18] Add Risc-V branch instructions --- src/asmio/program/linkage.cpp | 27 +++++++++++- src/asmio/program/linkage.hpp | 3 ++ src/asmio/riscv/argument/condition.hpp | 27 ++++++++++++ src/asmio/riscv/module.cpp | 14 +++++++ src/asmio/riscv/writer.cpp | 58 +++++++++++++++++++++++--- src/asmio/riscv/writer.hpp | 15 ++++++- test/riscv.cpp | 19 +++++++++ 7 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 src/asmio/riscv/argument/condition.hpp diff --git a/src/asmio/program/linkage.cpp b/src/asmio/program/linkage.cpp index 31e6916..bdfbba5 100644 --- a/src/asmio/program/linkage.cpp +++ b/src/asmio/program/linkage.cpp @@ -26,7 +26,7 @@ namespace asmio { const int64_t distance = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend; if (distance & 0b11) { - throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(distance) + ") into target " + util::to_hex(dst.offset) + ", offset is not aligned!"}; + throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(distance) + ") into target " + util::to_hex(dst.offset) + ", offset is not 4-aligned!"}; } const int64_t offset = distance >> 2; @@ -66,10 +66,35 @@ namespace asmio { std::memcpy(buffer->get_pointer(dst), value_ptr, width); } + static void encode_riscv_b(SegmentedBuffer* buffer, const Linkage& linkage, BufferMarker src, size_t mount) { + BufferMarker dst = linkage.target; + const int64_t dist = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend; + + if (dist & 1) { + throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", offset is not 2-aligned!"}; + } + + const int64_t imm12 = dist >> 1; + + const uint32_t si = (imm12 & 0b1'0'000000'0000) >> 11; + const uint32_t b7 = (imm12 & 0b0'1'000000'0000) >> 10; + const uint32_t hi = (imm12 & 0b0'0'111111'0000) >> 4; + const uint32_t lo = (imm12 & 0b0'0'000000'1111) >> 0; + + *reinterpret_cast(buffer->get_pointer(dst)) |= (si << 31 | hi << 25 | lo << 8 | b7 << 7); + } + + static void encode_riscv_j(SegmentedBuffer* buffer, const Linkage& linkage, BufferMarker src, size_t mount) { + // TODO + } + /* * class LinkageType */ + Linkage::Type LinkageType::RISCV_BRANCH {ElfRelocationType::RISCV_BRANCH, encode_riscv_b}; + Linkage::Type LinkageType::RISCV_JUMP {ElfRelocationType::RISCV_JAL, encode_riscv_j}; + Linkage::Type LinkageType::AARCH64_21_5_LO_HI {ElfRelocationType::AARCH64_ADR_PREL_LO21, encode_21_5_lo_hi}; Linkage::Type LinkageType::AARCH64_14_5_ALIGNED {ElfRelocationType::AARCH64_TSTBR14, encode_shifted_aligned<14, 5>}; Linkage::Type LinkageType::AARCH64_19_5_ALIGNED {ElfRelocationType::AARCH64_CONDBR19, encode_shifted_aligned<19, 5>}; diff --git a/src/asmio/program/linkage.hpp b/src/asmio/program/linkage.hpp index 59c639f..7b3357d 100644 --- a/src/asmio/program/linkage.hpp +++ b/src/asmio/program/linkage.hpp @@ -6,6 +6,9 @@ namespace asmio { struct LinkageType { + static Linkage::Type RISCV_BRANCH; + static Linkage::Type RISCV_JUMP; + static Linkage::Type AARCH64_21_5_LO_HI; static Linkage::Type AARCH64_14_5_ALIGNED; static Linkage::Type AARCH64_19_5_ALIGNED; diff --git a/src/asmio/riscv/argument/condition.hpp b/src/asmio/riscv/argument/condition.hpp new file mode 100644 index 0000000..553b703 --- /dev/null +++ b/src/asmio/riscv/argument/condition.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace asmio::riscv { + + enum struct Condition : uint8_t { + EQ = 0x0, + NE = 0x1, + LT = 0x4, + GE = 0x5, + LTU = 0x6, + GEU = 0x7 + }; + + inline Condition parse_condition_enum(const std::string_view& view) { + if (view == "eq") return Condition::EQ; + if (view == "ne") return Condition::NE; + if (view == "lt") return Condition::LT; + if (view == "ge") return Condition::GE; + if (view == "ltu") return Condition::LTU; + if (view == "geu") return Condition::GEU; + + throw std::runtime_error {"Invalid condition enumeration"}; + } + +} \ No newline at end of file diff --git a/src/asmio/riscv/module.cpp b/src/asmio/riscv/module.cpp index d49a080..a290e42 100644 --- a/src/asmio/riscv/module.cpp +++ b/src/asmio/riscv/module.cpp @@ -15,6 +15,12 @@ namespace asmio::riscv { return stream.expect(Token::INT).as_int(); } + template <> + Label parse_argument(TokenStream stream) { + const Token& label = stream.expect(Token::REFERENCE); + return {label.raw.c_str() + 1}; + } + template <> Registry parse_argument(TokenStream stream) { const Token& token = stream.expect(Token::NAME); @@ -33,6 +39,14 @@ namespace asmio::riscv { throw std::runtime_error {"Invalid argument format, expected register"}; } + template <> + Condition parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + return parse_condition_enum(raw); + } + # include "generated/riscv.hpp" /* diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 6755569..b93807f 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -1,5 +1,7 @@ #include "writer.hpp" +#include + namespace asmio::riscv { /* * class BufferWriter @@ -25,13 +27,12 @@ namespace asmio::riscv { } void BufferWriter::put_inst_b(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7) { - const uint32_t si = (imm12 & 0b1'0'00000'0000) >> 10; - const uint32_t b7 = (imm12 & 0b0'1'00000'0000) >> 9; - const uint32_t hi = (imm12 & 0b0'0'11111'0000) >> 4; - const uint32_t lo = (imm12 & 0b0'0'00000'1111) >> 0; + const uint32_t si = (imm12 & 0b1'0'000000'0000) >> 11; + const uint32_t b7 = (imm12 & 0b0'1'000000'0000) >> 10; + const uint32_t hi = (imm12 & 0b0'0'111111'0000) >> 4; + const uint32_t lo = (imm12 & 0b0'0'000000'1111) >> 0; put_dword(si << 31 | hi << 25 | rs2.reg << 20 | rs1.reg << 15 | (func3 & 0b111) << 12 | lo << 8 | b7 << 7 | (opc7 & 0b1111111)); - } void BufferWriter::put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7) { @@ -173,6 +174,51 @@ namespace asmio::riscv { put_inst_s(imm12, rs1, rs2, 0x3, 0b0100011); } + void BufferWriter::put_b(Condition cond, Registry rs1, Registry rs2, const Label& label) { + buffer.add_linkage(label, LinkageType::RISCV_BRANCH); + put_inst_b(0, rs2, rs1, static_cast(cond), 0b1100011); + } + + void BufferWriter::put_beq(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::EQ, rs1, rs2, label); + } + + void BufferWriter::put_bne(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::NE, rs1, rs2, label); + } + + void BufferWriter::put_blt(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::LT, rs1, rs2, label); + } + + void BufferWriter::put_bge(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::GE, rs1, rs2, label); + } + + void BufferWriter::put_bltu(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::LTU, rs1, rs2, label); + } + + void BufferWriter::put_bgeu(Registry rs1, Registry rs2, const Label& label) { + put_b(Condition::GEU, rs1, rs2, label); + } + + void BufferWriter::put_bgt(Registry rs1, Registry rs2, const Label& label) { + put_blt(rs2, rs1, label); + } + + void BufferWriter::put_ble(Registry rs1, Registry rs2, const Label& label) { + put_bge(rs2, rs1, label); + } + + void BufferWriter::put_bgtu(Registry rs1, Registry rs2, const Label& label) { + put_bltu(rs2, rs1, label); + } + + void BufferWriter::put_bleu(Registry rs1, Registry rs2, const Label& label) { + put_bgeu(rs2, rs1, label); + } + void BufferWriter::put_ecall() { put_inst_i(0, X0, 0x0, X0, 0b1110011); } @@ -181,4 +227,4 @@ namespace asmio::riscv { put_inst_i(1, X0, 0x0, X0, 0b1110011); } -} \ No newline at end of file +} diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 0176e5f..13acafa 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace asmio::riscv { @@ -49,12 +50,24 @@ namespace asmio::riscv { INST put_ldu(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd (with zero extension) INST put_lq(Registry rd, Registry rs, int16_t imm12); ///< Load qword at rs + imm12 into rd - INST put_sb(Registry rs1, Registry rs2, int16_t imm12); ///< Store byte from rs1 at rs2 + imm INST put_sw(Registry rs1, Registry rs2, int16_t imm12); ///< Store word from rs1 at rs2 + imm INST put_sd(Registry rs1, Registry rs2, int16_t imm12); ///< Store dword from rs1 at rs2 + imm INST put_sq(Registry rs1, Registry rs2, int16_t imm12); ///< Store qword from rs1 at rs2 + imm + INST put_b(Condition cond, Registry rs1, Registry rs2, const Label& label); ///< Branch to label if condition is met between rs1 and rs2 + + INST put_beq(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 == rs2 + INST put_bne(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 != rs2 + INST put_blt(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 < rs2 + INST put_bge(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 >= rs2 + INST put_bltu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 < rs2 (zero-extended) + INST put_bgeu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 >= rs2 (zero-extended) + INST put_bgt(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 > rs2 + INST put_ble(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 + INST put_bgtu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 > rs2 (zero-extended) + INST put_bleu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 (zero-extended) + INST put_ecall(); ///< Environment Call INST put_ebreak(); ///< Environment Break diff --git a/test/riscv.cpp b/test/riscv.cpp index dbe6e01..d719d50 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -120,4 +120,23 @@ namespace test { }; + TEST (rv32i_check_branch) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_beq(X2, X2, "end"); + writer.put_and(X1, X0, X0); + writer.label("start"); + writer.put_add(X1, X1, 10); + writer.put_bgt(X2, X1, "start"); + writer.label("end"); + + segmented.link(0); + std::vector s0 = {0x63, 0x08, 0x21, 0x00, 0xb3, 0x70, 0x00, 0x00, 0x93, 0x80, 0xa0, 0x00, 0xe3, 0xce, 0x20, 0xfe}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From 19faf0d1ee89ab7d4b9efec5e125664bd563a126 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Mon, 18 May 2026 17:49:07 +0200 Subject: [PATCH 06/18] Add Risc-V jump instructions --- src/asmio/program/linkage.cpp | 24 +++++++++++++++++++++++- src/asmio/riscv/writer.cpp | 13 +++++++++++++ src/asmio/riscv/writer.hpp | 3 +++ test/riscv.cpp | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/asmio/program/linkage.cpp b/src/asmio/program/linkage.cpp index bdfbba5..4052424 100644 --- a/src/asmio/program/linkage.cpp +++ b/src/asmio/program/linkage.cpp @@ -76,6 +76,10 @@ namespace asmio { const int64_t imm12 = dist >> 1; + if (!util::is_signed_encodable(imm12, 12)) { + throw std::runtime_error {"Can't fit label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", some data would have been truncated!"}; + } + const uint32_t si = (imm12 & 0b1'0'000000'0000) >> 11; const uint32_t b7 = (imm12 & 0b0'1'000000'0000) >> 10; const uint32_t hi = (imm12 & 0b0'0'111111'0000) >> 4; @@ -85,7 +89,25 @@ namespace asmio { } static void encode_riscv_j(SegmentedBuffer* buffer, const Linkage& linkage, BufferMarker src, size_t mount) { - // TODO + BufferMarker dst = linkage.target; + const int64_t dist = buffer->get_offset(src) - buffer->get_offset(dst) + linkage.addend; + + if (dist & 1) { + throw std::runtime_error {"Can't reference label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", offset is not 2-aligned!"}; + } + + const int64_t imm20 = dist >> 1; + + if (!util::is_signed_encodable(imm20, 20)) { + throw std::runtime_error {"Can't fit label '" + linkage.label.string() + "' (offset " + util::to_hex(dist) + ") into target " + util::to_hex(dst.offset) + ", some data would have been truncated!"}; + } + + const uint32_t si = (imm20 & 0b1'00000000'0'0000000000) >> 19; + const uint32_t hi = (imm20 & 0b0'11111111'0'0000000000) >> 11; + const uint32_t b2 = (imm20 & 0b0'00000000'1'0000000000) >> 10; + const uint32_t lo = (imm20 & 0b0'00000000'0'1111111111) >> 0; + + *reinterpret_cast(buffer->get_pointer(dst)) |= (si << 31 | lo << 21 | b2 << 20 | hi << 12); } /* diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index b93807f..3152dfb 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -179,6 +179,19 @@ namespace asmio::riscv { put_inst_b(0, rs2, rs1, static_cast(cond), 0b1100011); } + void BufferWriter::put_jal(const Label& label) { + put_jal(X1, label); + } + + void BufferWriter::put_jal(Registry rd, const Label& label) { + buffer.add_linkage(label, LinkageType::RISCV_JUMP); + put_inst_j(0, rd, 0b1101111); + } + + void BufferWriter::put_jalr(Registry rd, Registry rs, int16_t offset) { + put_inst_i(offset, rs, 0x0, rd, 0b1100111); + } + void BufferWriter::put_beq(Registry rs1, Registry rs2, const Label& label) { put_b(Condition::EQ, rs1, rs2, label); } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 13acafa..bc2a4b2 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -56,6 +56,9 @@ namespace asmio::riscv { INST put_sq(Registry rs1, Registry rs2, int16_t imm12); ///< Store qword from rs1 at rs2 + imm INST put_b(Condition cond, Registry rs1, Registry rs2, const Label& label); ///< Branch to label if condition is met between rs1 and rs2 + INST put_jal(const Label& label); ///< Jump and link + INST put_jal(Registry rd, const Label& label); ///< Jump and link + INST put_jalr(Registry rd, Registry rs, int16_t offset = 0); ///< Jump and link to register INST put_beq(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 == rs2 INST put_bne(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 != rs2 diff --git a/test/riscv.cpp b/test/riscv.cpp index d719d50..0c3de43 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -139,4 +139,22 @@ namespace test { }; + TEST (rv32i_check_jump) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.label("start"); + writer.put_add(X1, X1, 10); + writer.put_jal("start"); + writer.put_jal(X8, "start"); + writer.put_jalr(X6, X7); + + segmented.link(0); + std::vector s0 = {0x93, 0x80, 0xa0, 0x00, 0xef, 0xf0, 0xdf, 0xff, 0x6f, 0xf4, 0x9f, 0xff, 0x67, 0x83, 0x03, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From 9e35e23c353670ba84f01e67c88f2af2aece42e3 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 19 May 2026 20:27:20 +0200 Subject: [PATCH 07/18] Add Risc-V LUI instruction --- src/asmio/riscv/writer.cpp | 6 +++++- src/asmio/riscv/writer.hpp | 2 ++ test/riscv.cpp | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 3152dfb..24f29be 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -36,7 +36,7 @@ namespace asmio::riscv { } void BufferWriter::put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7) { - put_dword(imm20 << 20 | rd.reg << 7 | (opc7 & 0b1111111)); + put_dword(imm20 << 12 | rd.reg << 7 | (opc7 & 0b1111111)); } void BufferWriter::put_inst_j(uint32_t imm20, Registry rd, uint8_t opc7) { @@ -232,6 +232,10 @@ namespace asmio::riscv { put_bgeu(rs2, rs1, label); } + void BufferWriter::put_lui(Registry rd, uint32_t imm) { + put_inst_u(imm, rd, 0b0110111); + } + void BufferWriter::put_ecall() { put_inst_i(0, X0, 0x0, X0, 0b1110011); } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index bc2a4b2..e2e25b7 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -71,6 +71,8 @@ namespace asmio::riscv { INST put_bgtu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 > rs2 (zero-extended) INST put_bleu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 (zero-extended) + INST put_lui(Registry rd, uint32_t imm); ///< Load an upper constant to register, rd = imm << 12 + INST put_ecall(); ///< Environment Call INST put_ebreak(); ///< Environment Break diff --git a/test/riscv.cpp b/test/riscv.cpp index 0c3de43..d0b66d7 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -157,4 +157,18 @@ namespace test { }; + TEST (rv32i_check_lui) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_lui(X1, 1); + + segmented.link(0); + std::vector s0 = {0xb7, 0x10, 0x00, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From 03dfeb548b962e012cf9ee386411c3ea438e268e Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 19 May 2026 20:51:12 +0200 Subject: [PATCH 08/18] Add RV32M extension instructions --- src/asmio/riscv/writer.cpp | 32 ++++++++++++++++++++++++++++++++ src/asmio/riscv/writer.hpp | 11 ++++++++++- test/riscv.cpp | 21 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 24f29be..9c6b08d 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -244,4 +244,36 @@ namespace asmio::riscv { put_inst_i(1, X0, 0x0, X0, 0b1110011); } + void BufferWriter::put_mul(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x0, rd, 0b0110011); + } + + void BufferWriter::put_mulh(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x1, rd, 0b0110011); + } + + void BufferWriter::put_mulhsu(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x2, rd, 0b0110011); + } + + void BufferWriter::put_mulhu(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x3, rd, 0b0110011); + } + + void BufferWriter::put_div(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x4, rd, 0b0110011); + } + + void BufferWriter::put_divu(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x5, rd, 0b0110011); + } + + void BufferWriter::put_rem(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x6, rd, 0b0110011); + } + + void BufferWriter::put_remu(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x7, rd, 0b0110011); + } + } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index e2e25b7..84b8695 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -72,10 +72,19 @@ namespace asmio::riscv { INST put_bleu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 (zero-extended) INST put_lui(Registry rd, uint32_t imm); ///< Load an upper constant to register, rd = imm << 12 - INST put_ecall(); ///< Environment Call INST put_ebreak(); ///< Environment Break + // M extension + INST put_mul(Registry rd, Registry rs1, Registry rs2); ///< Store lower 64 bits of rs1 * rs2 into rd + INST put_mulh(Registry rd, Registry rs1, Registry rs2); ///< Store upper 64 bits of rs1 * rs2 into rd + INST put_mulhsu(Registry rd, Registry rs1, Registry rs2); ///< Store upper 64 bits of rs1 * rs2 into rd + INST put_mulhu(Registry rd, Registry rs1, Registry rs2); ///< Store upper 64 bits of rs1 * rs2 into rd + INST put_div(Registry rd, Registry rs1, Registry rs2); ///< Store rs1 / rs2 into rd + INST put_divu(Registry rd, Registry rs1, Registry rs2); ///< Store rs1 / rs2 into rd + INST put_rem(Registry rd, Registry rs1, Registry rs2); ///< Store reminder of rs1 / rs2 into rd + INST put_remu(Registry rd, Registry rs1, Registry rs2); ///< Store reminder of rs1 / rs2 into rd + }; } \ No newline at end of file diff --git a/test/riscv.cpp b/test/riscv.cpp index d0b66d7..00d5c07 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -171,4 +171,25 @@ namespace test { }; + TEST (rv32m_check) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_mul(X1, X2, X3); + writer.put_mulh(X1, X2, X3); + writer.put_mulhsu(X1, X2, X3); + writer.put_mulhu(X1, X2, X3); + writer.put_div(X1, X2, X3); + writer.put_divu(X1, X2, X3); + writer.put_rem(X1, X2, X3); + writer.put_remu(X1, X2, X3); + + segmented.link(0); + std::vector s0 = {0xb3, 0x00, 0x31, 0x02, 0xb3, 0x10, 0x31, 0x02, 0xb3, 0x20, 0x31, 0x02, 0xb3, 0x30, 0x31, 0x02, 0xb3, 0x40, 0x31, 0x02, 0xb3, 0x50, 0x31, 0x02, 0xb3, 0x60, 0x31, 0x02, 0xb3, 0x70, 0x31, 0x02}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From aee1e296c62fb90c51b064373266780f8209d95d Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 20 May 2026 13:26:07 +0200 Subject: [PATCH 09/18] Finish RV64M extension --- src/asmio/riscv/writer.cpp | 20 ++++++++++++++++++++ src/asmio/riscv/writer.hpp | 6 ++++++ test/riscv.cpp | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 9c6b08d..fcedf33 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -276,4 +276,24 @@ namespace asmio::riscv { put_inst_r(0x01, rs2, rs1, 0x7, rd, 0b0110011); } + void BufferWriter::put_muld(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x0, rd, 0b0111011); + } + + void BufferWriter::put_divd(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x4, rd, 0b0111011); + } + + void BufferWriter::put_divud(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x5, rd, 0b0111011); + } + + void BufferWriter::put_remd(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x6, rd, 0b0111011); + } + + void BufferWriter::put_remud(Registry rd, Registry rs1, Registry rs2) { + put_inst_r(0x01, rs2, rs1, 0x7, rd, 0b0111011); + } + } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 84b8695..b7046b9 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -84,6 +84,12 @@ namespace asmio::riscv { INST put_divu(Registry rd, Registry rs1, Registry rs2); ///< Store rs1 / rs2 into rd INST put_rem(Registry rd, Registry rs1, Registry rs2); ///< Store reminder of rs1 / rs2 into rd INST put_remu(Registry rd, Registry rs1, Registry rs2); ///< Store reminder of rs1 / rs2 into rd + INST put_muld(Registry rd, Registry rs1, Registry rs2); ///< Multiply dword rs1 and rs2, store result in rd + INST put_divd(Registry rd, Registry rs1, Registry rs2); ///< Divide dword rs1 by rs2, store result in rd + INST put_divud(Registry rd, Registry rs1, Registry rs2); ///< Divide dword rs1 by rs2, store result in rd + INST put_remd(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd + INST put_remud(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd + }; diff --git a/test/riscv.cpp b/test/riscv.cpp index 00d5c07..539d2d1 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -192,4 +192,22 @@ namespace test { }; + TEST (rv64m_check) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_muld(X1, X2, X3); + writer.put_divd(X1, X2, X3); + writer.put_divud(X1, X2, X3); + writer.put_remd(X1, X2, X3); + writer.put_remud(X1, X2, X3); + + segmented.link(0); + std::vector s0 = {0xbb, 0x00, 0x31, 0x02, 0xbb, 0x40, 0x31, 0x02, 0xbb, 0x50, 0x31, 0x02, 0xbb, 0x60, 0x31, 0x02, 0xbb, 0x70, 0x31, 0x02}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From ed5dad872e818070afa2a950312140e1e4a584fd Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 20 May 2026 14:15:23 +0200 Subject: [PATCH 10/18] Flexible condition encoding in branaches --- src/asmio/riscv/argument/condition.hpp | 26 ++++++++++++++++++++------ src/asmio/riscv/argument/registry.hpp | 7 ++++--- src/asmio/riscv/writer.cpp | 18 +++++++++++++----- src/asmio/riscv/writer.hpp | 5 ++--- test/riscv.cpp | 17 +++++++++++++++++ 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/asmio/riscv/argument/condition.hpp b/src/asmio/riscv/argument/condition.hpp index 553b703..c2c447a 100644 --- a/src/asmio/riscv/argument/condition.hpp +++ b/src/asmio/riscv/argument/condition.hpp @@ -5,14 +5,28 @@ namespace asmio::riscv { enum struct Condition : uint8_t { - EQ = 0x0, - NE = 0x1, - LT = 0x4, - GE = 0x5, - LTU = 0x6, - GEU = 0x7 + EQ = 0x00, + NE = 0x01, + LT = 0x04, GT = 0x14, + GE = 0x05, LE = 0x15, + LTU = 0x06, GTU = 0x16, + GEU = 0x07, LEU = 0x17, }; + /** + * Extract the condition code used during instruction encoding + */ + constexpr uint8_t get_condition_code(Condition cond) { + return static_cast(cond) & 0xf; + } + + /** + * Check if during instruction encoding this condition requires inverting the arguments + */ + constexpr bool get_condition_swap(Condition cond) { + return static_cast(cond) & 0x10; + } + inline Condition parse_condition_enum(const std::string_view& view) { if (view == "eq") return Condition::EQ; if (view == "ne") return Condition::NE; diff --git a/src/asmio/riscv/argument/registry.hpp b/src/asmio/riscv/argument/registry.hpp index f17d08d..0980404 100644 --- a/src/asmio/riscv/argument/registry.hpp +++ b/src/asmio/riscv/argument/registry.hpp @@ -8,10 +8,11 @@ namespace asmio::riscv { public: - const uint8_t reg; // registry ARM code + const uint8_t reg; // registry Risc-V code - constexpr Registry(uint8_t reg) - : reg(reg) {} + public: + + constexpr Registry(uint8_t reg) noexcept : reg(reg) {} }; diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index fcedf33..bfdec18 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -176,7 +176,15 @@ namespace asmio::riscv { void BufferWriter::put_b(Condition cond, Registry rs1, Registry rs2, const Label& label) { buffer.add_linkage(label, LinkageType::RISCV_BRANCH); - put_inst_b(0, rs2, rs1, static_cast(cond), 0b1100011); + + Registry* rp1 = &rs1; + Registry* rp2 = &rs2; + + if (get_condition_swap(cond)) { + std::swap(rp1, rp2); + } + + put_inst_b(0,*rp2, *rp1, get_condition_code(cond), 0b1100011); } void BufferWriter::put_jal(const Label& label) { @@ -217,19 +225,19 @@ namespace asmio::riscv { } void BufferWriter::put_bgt(Registry rs1, Registry rs2, const Label& label) { - put_blt(rs2, rs1, label); + put_b(Condition::GT, rs1, rs2, label); } void BufferWriter::put_ble(Registry rs1, Registry rs2, const Label& label) { - put_bge(rs2, rs1, label); + put_b(Condition::LE, rs1, rs2, label); } void BufferWriter::put_bgtu(Registry rs1, Registry rs2, const Label& label) { - put_bltu(rs2, rs1, label); + put_b(Condition::GTU, rs1, rs2, label); } void BufferWriter::put_bleu(Registry rs1, Registry rs2, const Label& label) { - put_bgeu(rs2, rs1, label); + put_b(Condition::LEU, rs1, rs2, label); } void BufferWriter::put_lui(Registry rd, uint32_t imm) { diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index b7046b9..0ea431f 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -86,10 +86,9 @@ namespace asmio::riscv { INST put_remu(Registry rd, Registry rs1, Registry rs2); ///< Store reminder of rs1 / rs2 into rd INST put_muld(Registry rd, Registry rs1, Registry rs2); ///< Multiply dword rs1 and rs2, store result in rd INST put_divd(Registry rd, Registry rs1, Registry rs2); ///< Divide dword rs1 by rs2, store result in rd - INST put_divud(Registry rd, Registry rs1, Registry rs2); ///< Divide dword rs1 by rs2, store result in rd + INST put_divud(Registry rd, Registry rs1, Registry rs2); ///< Unsigned divide dword rs1 by rs2, store result in rd INST put_remd(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd - INST put_remud(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd - + INST put_remud(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 unsigned division, store result in rd }; diff --git a/test/riscv.cpp b/test/riscv.cpp index 539d2d1..2366c0f 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -139,6 +139,23 @@ namespace test { }; + TEST (rv32i_check_branch_inverted) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.label("test"); + writer.put_b(Condition::GE, X10, X11, "test"); + writer.put_b(Condition::LE, X10, X11, "test"); + writer.put_b(Condition::GTU, X10, X11, "test"); + + segmented.link(0); + std::vector s0 = {0x63, 0x50, 0xb5, 0x00, 0xe3, 0xde, 0xa5, 0xfe, 0xe3, 0xec, 0xa5, 0xfe}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + TEST (rv32i_check_jump) { SegmentedBuffer segmented; From a5d96aeb85ef3114bcb94b6bc456ce069d659cda Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 20 May 2026 14:36:50 +0200 Subject: [PATCH 11/18] Added RV32I aliases --- src/asmio/riscv/writer.cpp | 32 ++++++++++++++++++++++++++++++++ src/asmio/riscv/writer.hpp | 10 ++++++++++ test/riscv.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index bfdec18..2640623 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -304,4 +304,36 @@ namespace asmio::riscv { put_inst_r(0x01, rs2, rs1, 0x7, rd, 0b0111011); } + void BufferWriter::put_mov(Registry rd, Registry rs) { + put_add(rd, rs, 0); + } + + void BufferWriter::put_nop() { + put_add(X0, X0, 0); + } + + void BufferWriter::put_not(Registry rd, Registry rs) { + put_xor(rd, rs, -1); + } + + void BufferWriter::put_neg(Registry rd, Registry rs) { + put_sub(rd, X0, rs); + } + + void BufferWriter::put_j(const Label& label) { + put_jal(X0, label); + } + + void BufferWriter::put_jr(Registry rs) { + put_jalr(X0, rs); + } + + void BufferWriter::put_jlr(Registry rd, Registry rs) { + put_jalr(rd, rs); + } + + void BufferWriter::put_ret() { + put_jalr(X0, X1, 0); + } + } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index 0ea431f..c0adb5a 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -90,6 +90,16 @@ namespace asmio::riscv { INST put_remd(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd INST put_remud(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 unsigned division, store result in rd + // Aliasses + INST put_mov(Registry rd, Registry rs); ///< Copy value from rs to rd + INST put_nop(); ///< No Operation + INST put_not(Registry rd, Registry rs); ///< Invert bits + INST put_neg(Registry rd, Registry rs); ///< Negate two-complement number + INST put_j(const Label& label); ///< Jump + INST put_jr(Registry rs); ///< Jump register + INST put_jlr(Registry rd, Registry rs); ///< Jump register with link + INST put_ret(); ///< Return + }; } \ No newline at end of file diff --git a/test/riscv.cpp b/test/riscv.cpp index 2366c0f..656d7b9 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -9,6 +9,20 @@ namespace test { using namespace asmio; using namespace asmio::riscv; + TEST (rv32i_check_nop) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_nop(); + + segmented.link(0); + std::vector s0 = {0x13, 0x00, 0x00, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + } + TEST (rv32i_check_add) { SegmentedBuffer segmented; @@ -188,6 +202,23 @@ namespace test { }; + TEST (rv32i_check_aliases) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_mov(X11, X12); + writer.put_neg(X12, X13); + writer.put_not(X12, X13); + writer.put_ret(); + + segmented.link(0); + std::vector s0 = {0x93, 0x05, 0x06, 0x00, 0x33, 0x06, 0xd0, 0x40, 0x13, 0xc6, 0xf6, 0xff, 0x67, 0x80, 0x00, 0x00}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + } + TEST (rv32m_check) { SegmentedBuffer segmented; From 8134fd6b88a2ad0e834f22c13462668f08e68e8e Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 20 May 2026 14:51:08 +0200 Subject: [PATCH 12/18] Register name alises --- src/asmio/riscv/argument/registry.hpp | 33 +++++++++++++++++++++++++++ src/asmio/riscv/module.cpp | 32 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/asmio/riscv/argument/registry.hpp b/src/asmio/riscv/argument/registry.hpp index 0980404..0237abe 100644 --- a/src/asmio/riscv/argument/registry.hpp +++ b/src/asmio/riscv/argument/registry.hpp @@ -55,4 +55,37 @@ namespace asmio::riscv { constexpr Registry X30 = X(30); constexpr Registry X31 = X(31); + // register name aliases + constexpr Registry RA = X1; + constexpr Registry SP = X2; + constexpr Registry GP = X3; + constexpr Registry TP = X4; + constexpr Registry T0 = X5; + constexpr Registry T1 = X6; + constexpr Registry T2 = X7; + constexpr Registry S0 = X8; + constexpr Registry S1 = X9; + constexpr Registry A0 = X10; + constexpr Registry A1 = X11; + constexpr Registry A2 = X12; + constexpr Registry A3 = X13; + constexpr Registry A4 = X14; + constexpr Registry A5 = X15; + constexpr Registry A6 = X16; + constexpr Registry A7 = X17; + constexpr Registry S2 = X18; + constexpr Registry S3 = X19; + constexpr Registry S4 = X20; + constexpr Registry S5 = X21; + constexpr Registry S6 = X22; + constexpr Registry S7 = X23; + constexpr Registry S8 = X24; + constexpr Registry S9 = X25; + constexpr Registry S10 = X26; + constexpr Registry S11 = X27; + constexpr Registry T3 = X28; + constexpr Registry T4 = X29; + constexpr Registry T5 = X30; + constexpr Registry T6 = X31; + } \ No newline at end of file diff --git a/src/asmio/riscv/module.cpp b/src/asmio/riscv/module.cpp index a290e42..d43b97e 100644 --- a/src/asmio/riscv/module.cpp +++ b/src/asmio/riscv/module.cpp @@ -35,6 +35,38 @@ namespace asmio::riscv { throw std::runtime_error {"Invalid register number, expected value in range [0, 31]"}; } + + if (raw == "ra") return RA; + if (raw == "sp") return SP; + if (raw == "gp") return GP; + if (raw == "tp") return TP; + if (raw == "t0") return T0; + if (raw == "t1") return T1; + if (raw == "t2") return T2; + if (raw == "s0") return S0; + if (raw == "s1") return S1; + if (raw == "a0") return A0; + if (raw == "a1") return A1; + if (raw == "a2") return A2; + if (raw == "a3") return A3; + if (raw == "a4") return A4; + if (raw == "a5") return A5; + if (raw == "a6") return A6; + if (raw == "a7") return A7; + if (raw == "s2") return S2; + if (raw == "s3") return S3; + if (raw == "s4") return S4; + if (raw == "s5") return S5; + if (raw == "s6") return S6; + if (raw == "s7") return S7; + if (raw == "s8") return S8; + if (raw == "s9") return S9; + if (raw == "s10") return S10; + if (raw == "s11") return S11; + if (raw == "t3") return T3; + if (raw == "t4") return T4; + if (raw == "t5") return T5; + if (raw == "t6") return T6; throw std::runtime_error {"Invalid argument format, expected register"}; } From 39a6d6e167e507e2079a6625495a9bb8814a1d1b Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 23 May 2026 23:19:54 +0200 Subject: [PATCH 13/18] Add RV64A extension instructions --- src/asmio/riscv/argument/order.hpp | 25 +++++++++++++ src/asmio/riscv/module.cpp | 19 ++++++++++ src/asmio/riscv/writer.cpp | 58 ++++++++++++++++++++++++++++++ src/asmio/riscv/writer.hpp | 28 ++++++++++----- test/riscv.cpp | 34 ++++++++++++++++++ 5 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 src/asmio/riscv/argument/order.hpp diff --git a/src/asmio/riscv/argument/order.hpp b/src/asmio/riscv/argument/order.hpp new file mode 100644 index 0000000..7a8ceb7 --- /dev/null +++ b/src/asmio/riscv/argument/order.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace asmio::riscv { + + // Copied from the AArch64 + // TODO: unify? + enum struct Order : uint8_t { + NONE = 0b00, ///< Ensure no ordering of operations + RELEASE = 0b01, ///< Ensure that the preceding operations finish before this one starts + ACQUIRE = 0b10, ///< Ensure that the following operations wait for this one to finish + ACQUIRE_RELEASE = 0b11, ///< Ensure that both previous and preceding operations are executed in order + }; + + constexpr Order parse_order_enum(const std::string_view& view) { + if (view == "none") return Order::NONE; + if (view == "rl") return Order::RELEASE; + if (view == "aq") return Order::ACQUIRE; + if (view == "ra" || view == "ar" || view == "aqrl" || view == "rlaq") return Order::ACQUIRE_RELEASE; + + throw std::runtime_error {"Invalid order enumeration"}; + } + +} \ No newline at end of file diff --git a/src/asmio/riscv/module.cpp b/src/asmio/riscv/module.cpp index d43b97e..97ee697 100644 --- a/src/asmio/riscv/module.cpp +++ b/src/asmio/riscv/module.cpp @@ -79,6 +79,25 @@ namespace asmio::riscv { return parse_condition_enum(raw); } + template <> + Order parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + return parse_order_enum(raw); + } + + template <> + Size parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + if (raw == "dword") return DWORD; + if (raw == "qword") return QWORD; + + throw std::runtime_error {"Invalid size"}; + } + # include "generated/riscv.hpp" /* diff --git a/src/asmio/riscv/writer.cpp b/src/asmio/riscv/writer.cpp index 2640623..82873dd 100644 --- a/src/asmio/riscv/writer.cpp +++ b/src/asmio/riscv/writer.cpp @@ -48,6 +48,20 @@ namespace asmio::riscv { put_dword(si << 31 | lo << 21 | b2 << 20 | hi << 12 | rd.reg << 7 | (opc7 & 0b1111111)); } + void BufferWriter::put_inst_a(uint8_t func5, Registry rs2, Registry rs1, Size size, Registry rd, Order order) { + uint8_t func3; + + if (size == DWORD) { + func3 = 0b010; + } else if (size == QWORD) { + func3 = 0b011; + } else { + throw std::runtime_error {"Invalid operand, atomic operation size must be dword or qword"}; + } + + put_inst_r(func5 << 2 | static_cast(order), rs2, rs1, func3, rd, 0b0101111); + } + void BufferWriter::put_add(Registry rd, Registry rs, int16_t imm12) { put_inst_i(imm12, rs, 0x0, rd, 0b0010011); } @@ -304,6 +318,50 @@ namespace asmio::riscv { put_inst_r(0x01, rs2, rs1, 0x7, rd, 0b0111011); } + void BufferWriter::put_lr(Registry rd, Registry rs, Size s, Order order) { + put_inst_a(0x02, X0, rs, s, rd, order); + } + + void BufferWriter::put_sc(Registry rd, Registry rt, Registry rs, Size s, Order order) { + put_inst_a(0x03, rs, rt, s, rd, order); + } + + void BufferWriter::put_amoswap(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x01, val, ptr, s, old, order); + } + + void BufferWriter::put_amoadd(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x00, val, ptr, s, old, order); + } + + void BufferWriter::put_amoand(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x0C, val, ptr, s, old, order); + } + + void BufferWriter::put_amoor(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x08, val, ptr, s, old, order); + } + + void BufferWriter::put_amoxor(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x04, val, ptr, s, old, order); + } + + void BufferWriter::put_amomax(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x14, val, ptr, s, old, order); + } + + void BufferWriter::put_amomin(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x10, val, ptr, s, old, order); + } + + void BufferWriter::put_amomaxu(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x1C, val, ptr, s, old, order); + } + + void BufferWriter::put_amominu(Registry old, Registry ptr, Registry val, Size s, Order order) { + put_inst_a(0x18, val, ptr, s, old, order); + } + void BufferWriter::put_mov(Registry rd, Registry rs) { put_add(rd, rs, 0); } diff --git a/src/asmio/riscv/writer.hpp b/src/asmio/riscv/writer.hpp index c0adb5a..c22e4f0 100644 --- a/src/asmio/riscv/writer.hpp +++ b/src/asmio/riscv/writer.hpp @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include +#include namespace asmio::riscv { @@ -16,6 +18,7 @@ namespace asmio::riscv { void put_inst_b(uint16_t imm12, Registry rs2, Registry rs1, uint8_t func3, uint8_t opc7); void put_inst_u(uint32_t imm20, Registry rd, uint8_t opc7); void put_inst_j(uint32_t imm20, Registry rd, uint8_t opc7); + void put_inst_a(uint8_t func5, Registry rs2, Registry rs1, Size size, Registry rd, Order order); public: @@ -30,7 +33,6 @@ namespace asmio::riscv { INST put_sra(Registry rd, Registry rs, int16_t imm12); ///< Shift register rs right arythetically by a 12 bit immediate and save result to rd INST put_slt(Registry rd, Registry rs, int16_t imm12); ///< Set rd to 1 if rs is less than a 12 bit immediate, and to 0 otherwise (signed) INST put_sltu(Registry rd, Registry rs, int16_t imm12); ///< Set rd to 1 if rs1 is less than a 12 bit immediate, and to 0 otherwise (unsigned) - INST put_add(Registry rd, Registry rs1, Registry rs2); ///< Add register rs1 to rs2 and save result to rd INST put_sub(Registry rd, Registry rs1, Registry rs2); ///< Subtract register rs1 from rs2 and save result to rd INST put_xor(Registry rd, Registry rs1, Registry rs2); ///< Bitwise XOR register rs1 with rs2 and save result to rd @@ -41,7 +43,6 @@ namespace asmio::riscv { INST put_sra(Registry rd, Registry rs1, Registry rs2); ///< Shift register rs1 right arythetically by rs2 and save result to rd INST put_slt(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (signed) INST put_sltu(Registry rd, Registry rs1, Registry rs2); ///< Set rd to 1 if rs1 is less than rs2, and to 0 otherwise (unsigned) - INST put_lb(Registry rd, Registry rs, int16_t imm12); ///< Load byte at rs + imm12 into rd (with sign extension) INST put_lw(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with sign extension) INST put_ld(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd (with sign extension) @@ -49,17 +50,14 @@ namespace asmio::riscv { INST put_lwu(Registry rd, Registry rs, int16_t imm12); ///< Load word at rs + imm12 into rd (with zero extension) INST put_ldu(Registry rd, Registry rs, int16_t imm12); ///< Load dword at rs + imm12 into rd (with zero extension) INST put_lq(Registry rd, Registry rs, int16_t imm12); ///< Load qword at rs + imm12 into rd - INST put_sb(Registry rs1, Registry rs2, int16_t imm12); ///< Store byte from rs1 at rs2 + imm INST put_sw(Registry rs1, Registry rs2, int16_t imm12); ///< Store word from rs1 at rs2 + imm INST put_sd(Registry rs1, Registry rs2, int16_t imm12); ///< Store dword from rs1 at rs2 + imm INST put_sq(Registry rs1, Registry rs2, int16_t imm12); ///< Store qword from rs1 at rs2 + imm - INST put_b(Condition cond, Registry rs1, Registry rs2, const Label& label); ///< Branch to label if condition is met between rs1 and rs2 INST put_jal(const Label& label); ///< Jump and link INST put_jal(Registry rd, const Label& label); ///< Jump and link INST put_jalr(Registry rd, Registry rs, int16_t offset = 0); ///< Jump and link to register - INST put_beq(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 == rs2 INST put_bne(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 != rs2 INST put_blt(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 < rs2 @@ -70,12 +68,11 @@ namespace asmio::riscv { INST put_ble(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 INST put_bgtu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 > rs2 (zero-extended) INST put_bleu(Registry rs1, Registry rs2, const Label& label); ///< Branch if rs1 <= rs2 (zero-extended) - INST put_lui(Registry rd, uint32_t imm); ///< Load an upper constant to register, rd = imm << 12 INST put_ecall(); ///< Environment Call INST put_ebreak(); ///< Environment Break - // M extension + // "M" extension INST put_mul(Registry rd, Registry rs1, Registry rs2); ///< Store lower 64 bits of rs1 * rs2 into rd INST put_mulh(Registry rd, Registry rs1, Registry rs2); ///< Store upper 64 bits of rs1 * rs2 into rd INST put_mulhsu(Registry rd, Registry rs1, Registry rs2); ///< Store upper 64 bits of rs1 * rs2 into rd @@ -90,7 +87,20 @@ namespace asmio::riscv { INST put_remd(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 division, store result in rd INST put_remud(Registry rd, Registry rs1, Registry rs2); ///< Get reminder of dword rs1 and rs2 unsigned division, store result in rd - // Aliasses + // "A" extension + INST put_lr(Registry rd, Registry rs, Size s = DWORD, Order order = Order::NONE); ///< Load reserved + INST put_sc(Registry rd, Registry rt, Registry rs, Size s = DWORD, Order order = Order::NONE); ///< Store conditional, writes rs at address rt, status (0 on success, 1 otherwise) is written to rd + INST put_amoswap(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Swap + INST put_amoadd(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Add + INST put_amoand(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic bitwise AND + INST put_amoor(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic bitwise OR + INST put_amoxor(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic bitwise XOR + INST put_amomax(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Maximum + INST put_amomin(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Minimum + INST put_amomaxu(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Maximum (unsigned) + INST put_amominu(Registry old, Registry ptr, Registry val, Size s = DWORD, Order order = Order::NONE); ///< Atomic Minimum (unsigned) + + // Aliases INST put_mov(Registry rd, Registry rs); ///< Copy value from rs to rd INST put_nop(); ///< No Operation INST put_not(Registry rd, Registry rs); ///< Invert bits @@ -102,4 +112,4 @@ namespace asmio::riscv { }; -} \ No newline at end of file +} diff --git a/test/riscv.cpp b/test/riscv.cpp index 656d7b9..0ec2723 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -258,4 +258,38 @@ namespace test { }; + TEST (rv64a_check) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_lr(T0, T1, DWORD, Order::ACQUIRE); + writer.put_lr(T6, T5, QWORD, Order::RELEASE); + writer.put_sc(T0, T1, T2, DWORD, Order::NONE); + + writer.put_amoswap(T0, T1, T2, DWORD, Order::ACQUIRE); + writer.put_amoadd(T0, T1, T2, QWORD, Order::RELEASE); + writer.put_amoand(T0, T1, T2, DWORD, Order::ACQUIRE_RELEASE); + writer.put_amoor(T0, T1, T2, QWORD, Order::NONE); + writer.put_amoxor(T0, T1, T2, DWORD, Order::ACQUIRE); + writer.put_amomax(T0, T1, T2, QWORD, Order::RELEASE); + writer.put_amomin(T0, T1, T2, DWORD, Order::ACQUIRE_RELEASE); + writer.put_amomaxu(T0, T1, T2, QWORD, Order::NONE); + writer.put_amominu(T0, T1, T2, DWORD, Order::ACQUIRE); + + EXPECT_THROW(std::runtime_error) { + writer.put_amomin(T0, T1, T2, WORD, Order::NONE); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_amomin(T0, T1, T2, BYTE, Order::NONE); + }; + + segmented.link(0); + std::vector s0 = {0xaf, 0x22, 0x03, 0x14, 0xaf, 0x3f, 0x0f, 0x12, 0xaf, 0x22, 0x73, 0x18, 0xaf, 0x22, 0x73, 0x0c, 0xaf, 0x32, 0x73, 0x02, 0xaf, 0x22, 0x73, 0x66, 0xaf, 0x32, 0x73, 0x40, 0xaf, 0x22, 0x73, 0x24, 0xaf, 0x32, 0x73, 0xa2, 0xaf, 0x22, 0x73, 0x86, 0xaf, 0x32, 0x73, 0xe0, 0xaf, 0x22, 0x73, 0xc4}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + } \ No newline at end of file From d4e852019a4cffad982934e27298fbec33e1be9a Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 24 May 2026 11:18:09 +0200 Subject: [PATCH 14/18] Test on RiscV (QEMU on Linux) --- .github/workflows/ci.yml | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceb9b51..d4a6c76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,6 @@ name: ci.yml -on: - push: - branches: ['master', 'next'] - pull_request: - types: [ opened, synchronize, reopened ] - branches: ['master', 'next'] - +on: [ push, pull_request ] jobs: linux-x86: runs-on: ubuntu-latest @@ -27,6 +21,25 @@ jobs: - name: Test x86 run: cd build && ./test + linux-qemu-riscv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu binutils-riscv64-linux-gnu qemu-system-riscv64 qemu-user qemu-user-static + + - name: Configure + run: cmake -B build -DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++ + + - name: Build + run: cmake --build build + + - name: Test RiscV + run: cd build && qemu-riscv64 -L /usr/riscv64-linux-gnu ./test + linux-aarch64: runs-on: ubuntu-24.04-arm steps: From 3fcf12aff7293129997fb7da8ea99ba777ba333b Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 24 May 2026 11:45:37 +0200 Subject: [PATCH 15/18] Allow RiscV target architecture --- src/asmio/elf/header.hpp | 4 ++++ src/asmio/external.hpp | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/asmio/elf/header.hpp b/src/asmio/elf/header.hpp index 1000c81..4eec67a 100644 --- a/src/asmio/elf/header.hpp +++ b/src/asmio/elf/header.hpp @@ -33,6 +33,10 @@ namespace asmio { #if ARCH_X86 NATIVE = X86_64, #endif + +#if ARCH_RISCV64 + NATIVE = RISCV, +#endif }; enum struct ElfClass : uint8_t { diff --git a/src/asmio/external.hpp b/src/asmio/external.hpp index 17a895d..c7329e5 100644 --- a/src/asmio/external.hpp +++ b/src/asmio/external.hpp @@ -41,6 +41,16 @@ # define ARCH_AARCH64 false #endif -#if !(ARCH_X86 || ARCH_AARCH64) +#if defined(__riscv) +# if (__riscv_xlen == 64) +# define ARCH_RISCV64 true +# else +# define ARCH_RISCV64 false +# endif +#else +# define ARCH_RISCV64 false +#endif + +#if !(ARCH_X86 || ARCH_AARCH64 || ARCH_RISCV64) # error "Unsupported target architecture!" #endif \ No newline at end of file From 3970c37106e89b3259dc63d09ccbe16e9887cc0b Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 24 May 2026 13:26:13 +0200 Subject: [PATCH 16/18] Add USE_MOLD CMake option --- CMakeLists.txt | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89291f3..0e68d1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.31) +cmake_minimum_required(VERSION 2.28...3.31) project(ASMIOV C CXX) include(FetchContent) @@ -7,17 +7,21 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Python3 COMPONENTS Interpreter) +option(USE_MOLD "Use mold linker if installed" ON) + # Use the mold linker automatically for GCC if available, # as it is faster, more modern, and produces better errors. -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - find_program(MOLD_PROGRAM mold) - - if (MOLD_PROGRAM) - message(STATUS "The MOLD linker was found: ${MOLD_PROGRAM}") - set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=mold") - set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=mold") - else() - message(STATUS "The MOLD linker was NOT found!") +if (USE_MOLD) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + find_program(MOLD_PROGRAM mold) + + if (MOLD_PROGRAM) + message(STATUS "The MOLD linker was found: ${MOLD_PROGRAM}") + set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=mold") + set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=mold") + else() + message(STATUS "The MOLD linker was NOT found!") + endif() endif() endif() From f5829fbc5096331ab7fd941b912f4cd605d110b6 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 24 May 2026 14:14:16 +0200 Subject: [PATCH 17/18] Runtime RiscV test --- test/riscv.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/test/riscv.cpp b/test/riscv.cpp index 0ec2723..07cd0fd 100644 --- a/test/riscv.cpp +++ b/test/riscv.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "test.hpp" #include "vstl.hpp" @@ -292,4 +293,80 @@ namespace test { }; + /* + * region Executable + * Begin architecture depended tests for Risc-V + */ + +#if ARCH_RISCV64 + + TEST (riscv_exec_leaf_function) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_add(A0, X0, 42); + writer.put_ret(); + + auto exe = to_executable(segmented); + CHECK(exe.call_u64(), 42); + + }; + + TEST (riscv_exec_nop_neg) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_nop(); + writer.put_add(T0, X0, 7); + writer.put_neg(A0, T0); + writer.put_ret(); + + auto exe = to_executable(segmented); + CHECK(exe.call_i64(), -7); + + }; + + TEST (riscv_exec_jump) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_j("skip_30"); + writer.put_add(A0, X0, 30); + writer.put_ret(); + + writer.label("skip_30"); + writer.put_add(A0, X0, 40); + writer.put_ret(); + + auto exe = to_executable(segmented); + CHECK(exe.call_u64(), 40); + + }; + + TEST (riscv_exec_multiply) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::RISCV; + + writer.put_add(T0, X0, 11); + writer.put_add(T1, X0, 4); + writer.put_add(T2, X0, 3); + writer.put_or(T1, T1, T2); + writer.put_mul(A0, T0, T1); + writer.put_ret(); + + auto exe = to_executable(segmented); + CHECK(exe.call_u64(), 77); + + }; + +#endif + } \ No newline at end of file From 671b60218ba53d3fcff8e69dea988cd22ae7478c Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:34:27 +0200 Subject: [PATCH 18/18] Use uraimo/run-on-arch-action GHA --- .github/workflows/ci.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4a6c76..dfe726d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,23 +22,24 @@ jobs: run: cd build && ./test linux-qemu-riscv: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - - name: Dependencies - run: | - sudo apt-get update - sudo apt-get install -y gcc-riscv64-linux-gnu g++-riscv64-linux-gnu binutils-riscv64-linux-gnu qemu-system-riscv64 qemu-user qemu-user-static - - - name: Configure - run: cmake -B build -DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++ - - - name: Build - run: cmake --build build - - - name: Test RiscV - run: cd build && qemu-riscv64 -L /usr/riscv64-linux-gnu ./test + - uses: uraimo/run-on-arch-action@v3 + name: Run commands + with: + arch: riscv64 + distro: ubuntu24.04 + shell: /bin/sh + + install: | + apt-get update + apt-get install -y cmake g++ git python3 + + run: | + cmake -B build + cmake --build build + cd build && ./test linux-aarch64: runs-on: ubuntu-24.04-arm