From f53b87e697426da688b7967292711f0ba856d711 Mon Sep 17 00:00:00 2001 From: uslstenn Date: Sun, 12 Oct 2025 22:24:23 +0300 Subject: [PATCH 1/2] [tests] Decoder unit tests were added * Basic Unit tests hierarchy was introduced * Decoder functionality were fully covered with unit tests * Tests option was defined into root CMake and passed into CI --- .github/workflows/build.yml | 3 +- CMakeLists.txt | 1 + cmake/utils.cmake | 17 +- src/isa/CMakeLists.txt | 1 + src/isa/tests/CMakeLists.txt | 5 + src/isa/tests/decoder_test.cc | 381 ++++++++++++++++++++++++++++++++++ 6 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 src/isa/tests/CMakeLists.txt create mode 100644 src/isa/tests/decoder_test.cc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 50ba5cd..0fd7f6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,7 +67,8 @@ jobs: -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_EXPORT_COMPILE_COMMANDS=${{matrix.export_commands}} \ - -DPROT_ENABLE_WERROR=ON + -DPROT_ENABLE_WERROR=ON \ + -DPROT_BUILD_TESTS=ON - name: Build # Build your program with the given configuration diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cd83a8..fe66b6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) endif() option(PROT_ENABLE_WERROR "Enable -Werror option (CI)" OFF) +option(PROT_BUILD_TESTS "Enable tests building (CI)" OFF) enable_testing() diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 2464d58..eed3f43 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -6,7 +6,7 @@ macro(prot_add_utest src_file) set(TEST_NAME "${src_file}_test") add_executable(${TEST_NAME} ${src_file}) - target_link_libraries(${TEST_NAME} PRIVATE GMock::gmock_main PROT::defaults) + target_link_libraries(${TEST_NAME} PRIVATE GTest::gmock_main PROT::defaults) foreach(arg IN LISTS ARGN) target_link_libraries(${TEST_NAME} PRIVATE ${arg}) endforeach() @@ -15,3 +15,18 @@ macro(prot_add_utest src_file) EXTRA_ARGS --gtest_color=yes PROPERTIES LABELS unit) endmacro() + +function(prot_add_component_utest COMPONENT_NAME) + file(GLOB TESTLIST RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.cc") + + foreach(TEST_SRC ${TESTLIST}) + set(TEST_NAME "${src_file}_test") + prot_add_utest(${TEST_SRC} ARGN) + + set_target_properties(${TEST_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/test/${COMPONENT_NAME}" + ) + + endforeach() +endfunction() diff --git a/src/isa/CMakeLists.txt b/src/isa/CMakeLists.txt index 5be229c..fbe6a77 100644 --- a/src/isa/CMakeLists.txt +++ b/src/isa/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(prot_isa STATIC isa.cc) +add_subdirectory(tests) target_include_directories(prot_isa PUBLIC include) target_link_libraries(prot_isa PRIVATE PROT::defaults) diff --git a/src/isa/tests/CMakeLists.txt b/src/isa/tests/CMakeLists.txt new file mode 100644 index 0000000..c45dc75 --- /dev/null +++ b/src/isa/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +set(COMPONENT_NAME isa) + +if(PROT_BUILD_TESTS) + prot_add_component_utest(${COMPONENT_NAME} PROT::isa) +endif() diff --git a/src/isa/tests/decoder_test.cc b/src/isa/tests/decoder_test.cc new file mode 100644 index 0000000..87ea038 --- /dev/null +++ b/src/isa/tests/decoder_test.cc @@ -0,0 +1,381 @@ +#include + +#include "prot/isa.hh" + +namespace prot::test { + +TEST(decoder, LB) { + prot::isa::Word word = 0b11110000111101010'000'11100'0000011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLB); +} + +TEST(decoder, LH) { // xxxxxxxxxxxxxxxxx'001'xxxxx'0000011 + prot::isa::Word word = 0b11110000111101010'001'11100'0000011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLH); +} + +TEST(decoder, LW) { + prot::isa::Word word = 0b11110000111101010'010'11100'0000011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLW); +} + +TEST(decoder, LBU) { + prot::isa::Word word = 0b11110000111101010'100'11100'0000011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLBU); +} + +TEST(decoder, LHU) { + prot::isa::Word word = 0b11110000111101010'101'11100'0000011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLHU); +} + +TEST(decoder, FENCE) { + prot::isa::Word word = 0b11110000111101010'000'11100'0001111; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kFENCE); +} + +TEST(decoder, ADDI) { + prot::isa::Word word = 0b11110000111101010'000'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kADDI); +} + +TEST(decoder, SLTI) { + prot::isa::Word word = 0b11110000111101010'010'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSLTI); +} + +TEST(decoder, SLTIU) { + prot::isa::Word word = 0b11110000111101010'011'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSLTIU); +} + +TEST(decoder, XORI) { + prot::isa::Word word = 0b11110000111101010'100'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kXORI); +} + +TEST(decoder, ORI) { + prot::isa::Word word = 0b11110000111101010'110'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kORI); +} + +TEST(decoder, ANDI) { + prot::isa::Word word = 0b11110000111101010'111'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kANDI); +} + +TEST(decoder, AUIPC) { + prot::isa::Word word = 0b11110000111101010111110100010111; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kAUIPC); +} + +TEST(decoder, SB) { + prot::isa::Word word = 0b11110000111101010'000'11100'0100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSB); +} + +TEST(decoder, SH) { + prot::isa::Word word = 0b11110000111101010'001'11100'0100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSH); +} + +TEST(decoder, SW) { + prot::isa::Word word = 0b11110000111101010'010'11100'0100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSW); +} + +TEST(decoder, ADD) { + prot::isa::Word word = 0b0000000'1010101010'000'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kADD); +} + +TEST(decoder, SLL) { + prot::isa::Word word = 0b0000000'1010101010'001'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSLL); +} + +TEST(decoder, SLT) { + prot::isa::Word word = 0b0000000'1010101010'010'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSLT); +} + +TEST(decoder, SLTU) { + prot::isa::Word word = 0b0000000'1010101010'011'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSLTU); +} + +TEST(decoder, XOR) { + prot::isa::Word word = 0b0000000'1010101010'100'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kXOR); +} + +TEST(decoder, SRL) { + prot::isa::Word word = 0b0000000'1010101010'101'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSRL); +} + +TEST(decoder, AND) { + prot::isa::Word word = 0b0000000'1010101010'111'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kAND); +} + +TEST(decoder, SUB) { + prot::isa::Word word = 0b0100000'1010101010'000'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSUB); +} + +TEST(decoder, SRA) { + prot::isa::Word word = 0b0100000'1010101010'101'11100'0110011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSRA); +} + +TEST(decoder, LUI) { + prot::isa::Word word = 0b1111000011111110000001111'0110111; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLUI); +} + +TEST(decoder, BEQ) { + prot::isa::Word word = 0b11110101010111111'000'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBEQ); +} + +TEST(decoder, BNE) { + prot::isa::Word word = 0b11110101010111111'001'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBNE); +} + +TEST(decoder, BLT) { + prot::isa::Word word = 0b11110101010111111'100'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBLT); +} + +TEST(decoder, BGE) { + prot::isa::Word word = 0b11110101010111111'101'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBGE); +} + +TEST(decoder, BLTU) { + prot::isa::Word word = 0b11110101010111111'110'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBLTU); +} + +TEST(decoder, BGEU) { + prot::isa::Word word = 0b11110101010111111'111'11100'1100011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBGEU); +} + +TEST(decoder, JALR) { + prot::isa::Word word = 0b11110101010111111'000'11100'1100111; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kJALR); +} + +TEST(decoder, JAL) { + prot::isa::Word word = 0b1111000011110000111100001'1101111; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kJAL); +} + +// imm tests +//=--------- + +TEST(decoder, I_imm) { + prot::isa::Word word = 0b11110000111101010'111'11100'0010011; + // + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b111111111111111111111'11100001111); +} + +TEST(decoder, S_imm) { + // 7th bit + prot::isa::Word word = 0b0000000'0111101010'010'00001'0100011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b01); + // 11-8 + word = 0b0000000'0111101010'010'11110'0100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b1111'0); + // 30-25 + word = 0b0111111'0111101010'010'00000'0100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b011111100000); + // 31 + word = 0b1000000'0111101010'010'00000'0100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), + static_cast(0b111111111111111111111'00000000000)); +} + +TEST(decoder, B_imm) { + // 7 + prot::isa::Word word = 0b0000000'1010111111'000'00001'1100011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b0100000000000); + // 11-8 + word = 0b0000000'0111101010'000'11110'1100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b1111'0); + // 30-25 + word = 0b0111111'0111101010'000'00000'1100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b011111100000); + // 31 + word = 0b1000000'0111101010'000'00000'1100011; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), + static_cast(0b11111111111111111111'000000000000)); +} + +TEST(decoder, U_imm) { + // 31 - 12 + prot::isa::Word word = 0b11110000111101010111'11010'0010111; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b11110000111101010111'000000000000); +} + +TEST(decoder, J_imm) { + // 30-21 + prot::isa::Word word = 0b0'1110000111'0'00000000'00001'1101111; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b011100001110); + // 20 + word = 0b0'0000000000'1'00000000'00001'1101111; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b1'00000000000); + // 19-12 + word = 0b0'0000000000'0'10001111'00001'1101111; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), 0b10001111'000000000000); + // 31 + word = 0b1'0000000000'0'00000000'00001'1101111; + instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).imm(), + static_cast(0b111111111111'00000000000000000000)); +} + +TEST(decoder, ADDI_negimm) { + prot::isa::Word word = 0b111111111111'10101'000'11001'0010011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kADDI); + ASSERT_EQ((*instr).imm(), -1); +} + +TEST(decoder, LUI_large_imm) { + prot::isa::Word word = 0b11111111111111111111'11001'0110111; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kLUI); + ASSERT_EQ((*instr).imm(), 0xFFFFF000); +} + +TEST(decoder, R_type_fields) { + // ADD x5, x10, x21 + prot::isa::Word word = 0b0000000'10101'01010'000'00101'0110011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kADD); + ASSERT_EQ((*instr).rd(), 5); + ASSERT_EQ((*instr).rs1(), 10); + ASSERT_EQ((*instr).rs2(), 21); +} + +TEST(decoder, I_type_fields) { + // ADDI x7, x15, 123 + prot::isa::Word word = 0b000001111011'01111'000'00111'0010011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kADDI); + ASSERT_EQ((*instr).rd(), 7); + ASSERT_EQ((*instr).rs1(), 15); + ASSERT_EQ((*instr).imm(), 123); +} + +TEST(decoder, S_type_fields) { + // SW x12, offset(x20) + prot::isa::Word word = 0b0000000'01100'10100'010'00000'0100011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kSW); + ASSERT_EQ((*instr).rs1(), 20); + ASSERT_EQ((*instr).rs2(), 12); +} + +TEST(decoder, B_type_fields) { + // BEQ x8, x9, offset + prot::isa::Word word = 0b0000000'01001'01000'000'00000'1100011; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kBEQ); + ASSERT_EQ((*instr).rs1(), 8); + ASSERT_EQ((*instr).rs2(), 9); +} + +TEST(decoder, J_type_fields) { + // JAL x1, offset + prot::isa::Word word = 0b00000000000000000000'00001'1101111; + auto instr = prot::isa::Instruction::decode(word); + ASSERT_EQ((*instr).opcode(), prot::isa::Opcode::kJAL); + ASSERT_EQ((*instr).rd(), 1); +} +} // namespace prot::test From e0d0b7d47bf3f60d7a2b6e287809e1ac14f76d9b Mon Sep 17 00:00:00 2001 From: Andrey Derzhavin Date: Fri, 24 Oct 2025 00:42:24 +0300 Subject: [PATCH 2/2] Use imm field for SLLI, SRLI, SRAI - Fix interp & xbyak impls BREAKING CHANGE!!! --- src/interpreter/interp.cc | 16 ++++------------ src/isa/isa.cc | 6 +++--- src/jit/xbyak/xbyak.cc | 2 +- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/interpreter/interp.cc b/src/interpreter/interp.cc index f0f99bd..dedc7f4 100644 --- a/src/interpreter/interp.cc +++ b/src/interpreter/interp.cc @@ -14,7 +14,7 @@ void executeRegisterRegisterOp(const isa::Instruction &inst, CPUState &state, state.setReg(inst.rd(), op(rs1, rs2)); } -template Func> +template Func> void executeRegisterImmOp(const isa::Instruction &inst, CPUState &state, Func op) { auto rs1 = state.getReg(inst.rs1()); @@ -22,14 +22,6 @@ void executeRegisterImmOp(const isa::Instruction &inst, CPUState &state, state.setReg(inst.rd(), op(rs1, imm)); } -template Func> -void executeRegisterImmRegOp(const isa::Instruction &inst, CPUState &state, - Func op) { - auto rs1 = state.getReg(inst.rs1()); - auto imm = inst.rs2(); - state.setReg(inst.rd(), op(rs1, imm)); -} - void doADD(const isa::Instruction &inst, CPUState &state) { executeRegisterRegisterOp(inst, state, std::plus<>{}); } @@ -177,7 +169,7 @@ void doSLL(const isa::Instruction &inst, CPUState &state) { executeRegisterRegisterOp(inst, state, sllHelper); } void doSLLI(const isa::Instruction &inst, CPUState &state) { - executeRegisterImmRegOp(inst, state, sllHelper); + executeRegisterImmOp(inst, state, sllHelper); } void doSLT(const isa::Instruction &inst, CPUState &state) { @@ -203,7 +195,7 @@ void doSRA(const isa::Instruction &inst, CPUState &state) { executeRegisterRegisterOp(inst, state, sraHelper); } void doSRAI(const isa::Instruction &inst, CPUState &state) { - executeRegisterImmRegOp(inst, state, sraHelper); + executeRegisterImmOp(inst, state, sraHelper); } isa::Word srlHelper(isa::Word lhs, isa::Word rhs) { @@ -214,7 +206,7 @@ void doSRL(const isa::Instruction &inst, CPUState &state) { executeRegisterRegisterOp(inst, state, srlHelper); } void doSRLI(const isa::Instruction &inst, CPUState &state) { - executeRegisterImmRegOp(inst, state, srlHelper); + executeRegisterImmOp(inst, state, srlHelper); } void doSUB(const isa::Instruction &inst, CPUState &state) { diff --git a/src/isa/isa.cc b/src/isa/isa.cc index 3b49173..0559e02 100644 --- a/src/isa/isa.cc +++ b/src/isa/isa.cc @@ -168,7 +168,7 @@ std::optional Instruction::decode(Word word) { instr.m_opc = Opcode::kSLLI; instr.m_rd = std::bit_cast(slice<11, 7>(word) << 0) >> 0; instr.m_rs1 = std::bit_cast(slice<19, 15>(word) << 0) >> 0; - instr.m_rs2 = std::bit_cast(slice<24, 20>(word) << 0) >> 0; + instr.m_imm = std::bit_cast(slice<24, 20>(word) << 0) >> 0; return instr; } case 0b10000000010011: { @@ -334,7 +334,7 @@ std::optional Instruction::decode(Word word) { instr.m_opc = Opcode::kSRLI; instr.m_rd = std::bit_cast(slice<11, 7>(word) << 0) >> 0; instr.m_rs1 = std::bit_cast(slice<19, 15>(word) << 0) >> 0; - instr.m_rs2 = std::bit_cast(slice<24, 20>(word) << 0) >> 0; + instr.m_imm = std::bit_cast(slice<24, 20>(word) << 0) >> 0; return instr; } case 0b1000000000000000101000000010011: { @@ -343,7 +343,7 @@ std::optional Instruction::decode(Word word) { instr.m_opc = Opcode::kSRAI; instr.m_rd = std::bit_cast(slice<11, 7>(word) << 0) >> 0; instr.m_rs1 = std::bit_cast(slice<19, 15>(word) << 0) >> 0; - instr.m_rs2 = std::bit_cast(slice<24, 20>(word) << 0) >> 0; + instr.m_imm = std::bit_cast(slice<24, 20>(word) << 0) >> 0; return instr; } case 0b110011: { diff --git a/src/jit/xbyak/xbyak.cc b/src/jit/xbyak/xbyak.cc index 8c50b15..f19a66e 100644 --- a/src/jit/xbyak/xbyak.cc +++ b/src/jit/xbyak/xbyak.cc @@ -297,7 +297,7 @@ CodeHolder XByakJit::translate(const BBInfo &info) { } \ case kS##Op##I: { \ getRs1(temp1); \ - op(temp1, insn.rs2()); \ + op(temp1, insn.imm()); \ setRd(temp1); \ break; \ }