diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..551c865 --- /dev/null +++ b/.clang-format @@ -0,0 +1,36 @@ +--- +Language: Cpp +Standard: c++20 +BasedOnStyle: LLVM + +IndentWidth: 4 +TabWidth: 4 +NamespaceIndentation: All +AccessModifierOffset: -4 + +ColumnLimit: 120 + +PenaltyReturnTypeOnItsOwnLine: 1000 + +BinPackParameters: false +BinPackArguments: false +AlignAfterOpenBracket: BlockIndent + +AllowShortFunctionsOnASingleLine: None +InsertBraces: true +AllowShortLambdasOnASingleLine: Empty + +BreakConstructorInitializers: AfterColon +PackConstructorInitializers: CurrentLine + +PointerAlignment: Left +ReferenceAlignment: Left + +SpaceAfterTemplateKeyword: false +AlwaysBreakTemplateDeclarations: No + +SortIncludes: Never + +AlignTrailingComments: + Kind: Always + OverEmptyLines: 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba4cdf1..96b8fb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,10 +107,21 @@ jobs: alert-threshold: '150%' fail-on-alert: true + clang-format: + name: Clang Format + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v4 + - name: Check formatting + run: | + find include tests -name "*.h" -o -name "*.cpp" | \ + xargs clang-format --dry-run --Werror + all-checks: name: All Checks runs-on: ubuntu-latest if: github.event_name == 'pull_request' - needs: unittests + needs: [unittests, clang-format] steps: - run: echo "All checks passed" diff --git a/include/pvm/bytecode/bytecode.h b/include/pvm/bytecode/bytecode.h index ff264f9..05bee0b 100644 --- a/include/pvm/bytecode/bytecode.h +++ b/include/pvm/bytecode/bytecode.h @@ -27,17 +27,18 @@ namespace ngu::pvm { // - integral type → 1 byte // - bytecode → N bytes (via ::capacity()) // - raw array T[N] → N bytes - template consteval std::size_t contribution() { + template consteval std::size_t contribution() { using U = std::remove_cvref_t; - if constexpr (std::is_integral_v) + if constexpr (std::is_integral_v) { return 1; - else if constexpr (requires { U::capacity(); }) + } else if constexpr (requires { U::capacity(); }) { return U::capacity(); - else if constexpr (std::is_array_v) + } else if constexpr (std::is_array_v) { return std::extent_v; + } return 0; } - } + } // namespace detail /** * @brief Fixed-capacity compile-time byte buffer holding @p N bytes of bytecode. @@ -49,15 +50,15 @@ namespace ngu::pvm { * - @c std::uint8_t - single byte; @n * - @c bytecode - another bytecode buffer (contributes M bytes); @n * - raw array @c T[M] - contributes M bytes. @n - * The deduction guide computes @p N as the sum of all argument contributions at compile time. @n - * Array constructor - copies the first @c len bytes from a @c uint8_t[N] array. + * The deduction guide computes @p N as the sum of all argument contributions at compile time. + * @n Array constructor - copies the first @c len bytes from a @c uint8_t[N] array. * * @par Fields * @c bytes - raw byte storage. @n * @c length - number of bytes written; never exceeds @p N. */ template struct bytecode { - template consteval explicit bytecode(Args&&... args) { + template consteval explicit bytecode(Args&&... args) { (append(std::forward(args)), ...); } @@ -87,7 +88,7 @@ namespace ngu::pvm { } } - template consteval void append(std::uint8_t const (&arr)[M]) { + template consteval void append(std::uint8_t const (&arr)[M]) { for (std::size_t i = 0; i < M && length < N; ++i) { bytes[length++] = arr[i]; } @@ -100,7 +101,7 @@ namespace ngu::pvm { } }; - template bytecode(Args&&... args) -> bytecode<(detail::contribution() + ...)>; -} + template bytecode(Args&&... args) -> bytecode<(detail::contribution() + ...)>; +} // namespace ngu::pvm -#endif //NGU_PVM_BYTECODE_BYTECODE_H \ No newline at end of file +#endif // NGU_PVM_BYTECODE_BYTECODE_H \ No newline at end of file diff --git a/include/pvm/bytecode/bytecode_decoder.h b/include/pvm/bytecode/bytecode_decoder.h index ac74c35..af7e9c0 100644 --- a/include/pvm/bytecode/bytecode_decoder.h +++ b/include/pvm/bytecode/bytecode_decoder.h @@ -28,7 +28,8 @@ namespace ngu::pvm { /// @brief Bytecode decoding mode. /// @c RUNTIME - stores only instruction offsets; used by the interpreter at runtime. @n - /// @c COMPILE_TIME - additionally stores opcodes, immediates and metadata; builds a label table. + /// @c COMPILE_TIME - additionally stores opcodes, immediates and metadata; builds a label + /// table. enum decode_time { RUNTIME, COMPILE_TIME }; /** @@ -64,8 +65,7 @@ namespace ngu::pvm { std::conditional_t data_ct{}; }; - template - using insn_entry = instruction_entry; + template using insn_entry = instruction_entry; using insn_entry_rt = insn_entry; using insn_entry_ct = insn_entry; @@ -74,7 +74,8 @@ namespace ngu::pvm { * @brief A single entry in the label table. * * @c label_id - label identifier (immediate value from @c OP_LABEL). @n - * @c target_index - index of the next real instruction after the label (meta-instructions are not counted). @n + * @c target_index - index of the next real instruction after the label (meta-instructions are + * not counted). @n * @c target - pointer to the next instruction entry in the stream. */ struct label_entry { @@ -84,14 +85,12 @@ namespace ngu::pvm { }; /// @brief View over the decoder instruction stream. @tparam DecodeTime Decoding mode. - template class instruction_stream : - public indexed_view> { + template class instruction_stream : public indexed_view> { public: using value_type = instruction_entry; - constexpr instruction_stream(const value_type* data, - const std::size_t count) - : data_(data), count_(count) {} + constexpr instruction_stream(const value_type* data, const std::size_t count) : data_(data), count_(count) { + } constexpr const value_type& at_impl(std::size_t i) const { return data_[i]; @@ -106,8 +105,7 @@ namespace ngu::pvm { std::size_t count_; }; - template - using insn_stream = instruction_stream; + template using insn_stream = instruction_stream; using insn_stream_rt = insn_stream; using insn_stream_ct = insn_stream; @@ -116,9 +114,8 @@ namespace ngu::pvm { struct label_table : public indexed_view { using value_type = label_entry; - constexpr label_table(const value_type* data, - const std::size_t count) - : data_(data), count_(count) {} + constexpr label_table(const value_type* data, const std::size_t count) : data_(data), count_(count) { + } constexpr const value_type& at_impl(std::size_t i) const { return data_[i]; @@ -146,9 +143,10 @@ namespace ngu::pvm { template class bytecode_decoder { static constexpr std::size_t MAX_INSN = (N + 3) / 4; //(N + divisor - 1) / divisor public: - constexpr explicit bytecode_decoder(const bytecode &code); + constexpr explicit bytecode_decoder(const bytecode& code); - /// @brief Returns the instruction stream. @return @ref insn_stream for the given @c DecodeTime. + /// @brief Returns the instruction stream. @return @ref insn_stream for the given @c + /// DecodeTime. constexpr insn_stream get_instruction_stream() const { return insn_stream{insns_, insn_count_}; } @@ -169,10 +167,11 @@ namespace ngu::pvm { } private: - // Iterates over bytecode bytes and fills insns_[]. In COMPILE_TIME mode also extracts opcodes, immediates and meta flags. + // Iterates over bytecode bytes and fills insns_[]. In COMPILE_TIME mode also extracts + // opcodes, immediates and meta flags. constexpr void build_instruction_stream() { - const std::uint8_t *pc{ code_.bytes }; - const std::uint8_t *end{ code_.bytes + code_.size() }; + const std::uint8_t* pc{code_.bytes}; + const std::uint8_t* end{code_.bytes + code_.size()}; std::size_t index{}; while (pc < end && index < MAX_INSN) { @@ -198,7 +197,8 @@ namespace ngu::pvm { insn_count_ = index; } - // Scans insns_[] for OP_LABEL entries and fills labels_[]. new_idx counts only real (non-meta) instructions. + // Scans insns_[] for OP_LABEL entries and fills labels_[]. new_idx counts only real + // (non-meta) instructions. constexpr void build_label_table() { std::uint64_t new_idx{}; for (std::size_t i{}; i < insn_count_; ++i) { @@ -208,8 +208,7 @@ namespace ngu::pvm { labels_[label_count_].target = &insns_[i + 1]; labels_[label_count_].target_index = new_idx; ++label_count_; - } - else { + } else { ++new_idx; } } @@ -229,12 +228,13 @@ namespace ngu::pvm { return bytecode_decoder(code); } - template constexpr bytecode_decoder::bytecode_decoder(const bytecode &code) : code_(code) { + template + constexpr bytecode_decoder::bytecode_decoder(const bytecode& code) : code_(code) { build_instruction_stream(); if constexpr (DecodeTime == COMPILE_TIME) { build_label_table(); } } -} +} // namespace ngu::pvm -#endif //NGU_PVM_BYTECODE_BYTECODE_DECODER_H \ No newline at end of file +#endif // NGU_PVM_BYTECODE_BYTECODE_DECODER_H \ No newline at end of file diff --git a/include/pvm/bytecode/instruction_decoder.h b/include/pvm/bytecode/instruction_decoder.h index 10043fe..66a643e 100644 --- a/include/pvm/bytecode/instruction_decoder.h +++ b/include/pvm/bytecode/instruction_decoder.h @@ -25,7 +25,8 @@ namespace ngu::pvm { * @brief Static utility set for extracting fields from a 32-bit instruction header. * * All methods accept a raw @c header and return the corresponding field - * according to the format: @c OPCODE(0) | INSN_SIZE(8) | MODE(11) | DESTINATION(12) | SOURCE(16). + * according to the format: @c OPCODE(0) | INSN_SIZE(8) | MODE(11) | DESTINATION(12) | + * SOURCE(16). */ struct instruction_decoder { using bits = arch::insn_bits; @@ -62,12 +63,11 @@ namespace ngu::pvm { /// @brief Returns the total instruction size in bytes (header + immediate). static constexpr std::size_t total_size(const std::uint32_t header) { - return detail::get_total_size( - static_cast(insn_size(header)) - ); + return detail::get_total_size(static_cast(insn_size(header))); } - /// @brief Reads the immediate value from @p data (little-endian) after the header. Returns 0 if no immediate is present. + /// @brief Reads the immediate value from @p data (little-endian) after the header. Returns + /// 0 if no immediate is present. static constexpr std::uint64_t immediate(const std::uint32_t header, const std::uint8_t* data) { if (!has_immediate(header)) { return 0; @@ -82,12 +82,11 @@ namespace ngu::pvm { return result; } - /// @brief Reads 4 bytes from @p data and assembles them into a 32-bit header (little-endian). + /// @brief Reads 4 bytes from @p data and assembles them into a 32-bit header + /// (little-endian). static constexpr std::uint32_t read_header(const std::uint8_t* data) { - return static_cast(data[0]) | - (static_cast(data[1]) << 8) | - (static_cast(data[2]) << 16) | - (static_cast(data[3]) << 24); + return static_cast(data[0]) | (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | (static_cast(data[3]) << 24); } }; @@ -102,10 +101,12 @@ namespace ngu::pvm { */ class instruction_view { public: - explicit instruction_view(const std::uint32_t* ptr = nullptr) : ip_(ptr) {} + explicit instruction_view(const std::uint32_t* ptr = nullptr) : ip_(ptr) { + } - bool valid() const { return ip_ != nullptr; } - std::uint32_t data() const { return valid() ? *ip_ : 0; } + std::uint32_t data() const { + return valid() ? *ip_ : 0; + } std::uint8_t opcode() const { return insn_decoder::opcode(data()); @@ -131,13 +132,16 @@ namespace ngu::pvm { return insn_decoder::has_immediate(data()); } + std::uint64_t immediate() const { + return valid() ? insn_decoder::immediate(data(), reinterpret_cast(ip_ + 1)) : 0; + } + std::size_t size() const { return insn_decoder::total_size(data()); } - std::uint64_t immediate() const { - return valid() ? insn_decoder::immediate(data(), - reinterpret_cast(ip_ + 1)) : 0; + bool valid() const { + return ip_ != nullptr; } private: @@ -145,6 +149,6 @@ namespace ngu::pvm { }; using insn_view = instruction_view; -} +} // namespace ngu::pvm -#endif //NGU_PVM_BYTECODE_INSTRUCTION_DECODER_H \ No newline at end of file +#endif // NGU_PVM_BYTECODE_INSTRUCTION_DECODER_H \ No newline at end of file diff --git a/include/pvm/bytecode/instruction_encoder.h b/include/pvm/bytecode/instruction_encoder.h index bfcb96c..0057fb2 100644 --- a/include/pvm/bytecode/instruction_encoder.h +++ b/include/pvm/bytecode/instruction_encoder.h @@ -22,7 +22,7 @@ namespace ngu::pvm { /** - * @brief Static utility set for encoding fields into a 32-bit instruction header. + * @brief Static utility set for encoding fields into a 32-bit instruction header.пше * * Symmetric to @ref instruction_decoder - performs the inverse operation: * packs fields into a header and writes it into a byte buffer. @@ -32,14 +32,9 @@ namespace ngu::pvm { /// @brief Assembles a 32-bit header from instruction fields. static constexpr std::uint32_t make_header( - std::uint8_t opcode, - arch::insn_size insn_sz, - arch::insn_mode mode, - std::uint8_t dst, - std::uint8_t src + std::uint8_t opcode, arch::insn_size insn_sz, arch::insn_mode mode, std::uint8_t dst, std::uint8_t src ) { - return static_cast(opcode) | - (static_cast(insn_sz) << bits::INSN_SIZE) | + return static_cast(opcode) | (static_cast(insn_sz) << bits::INSN_SIZE) | (static_cast(mode) << bits::MODE) | (static_cast(dst) << bits::DESTINATION) | (static_cast(src) << bits::SOURCE); @@ -53,13 +48,17 @@ namespace ngu::pvm { data[3] = (header >> 24) & 0xFF; } - /// @brief Replaces the opcode in an existing header with @p opcode, leaving all other fields intact. + /// @brief Replaces the opcode in an existing header with @p opcode, leaving all other + /// fields intact. static constexpr std::uint32_t patch_opcode(std::uint32_t header, std::uint8_t opcode) { return (header & ~0xFFu) | opcode; } - /// @brief Writes @p value into @p data (little-endian) according to @p insn_sz. Returns the number of bytes written. - static constexpr std::size_t encode_immediate(std::uint64_t value, arch::insn_size insn_sz, std::uint8_t* data) { + /// @brief Writes @p value into @p data (little-endian) according to @p insn_sz. Returns the + /// number of bytes written. + static constexpr std::size_t encode_immediate( + std::uint64_t value, arch::insn_size insn_sz, std::uint8_t* data + ) { auto const cnt = detail::get_imm_size(insn_sz); for (std::size_t i{}; i < cnt; ++i) { data[i] = static_cast(value >> (i * 8)); @@ -69,6 +68,6 @@ namespace ngu::pvm { }; using insn_encoder = instruction_encoder; -} +} // namespace ngu::pvm -#endif //NGU_PVM_BYTECODE_INSTRUCTION_ENCODER_H \ No newline at end of file +#endif // NGU_PVM_BYTECODE_INSTRUCTION_ENCODER_H \ No newline at end of file diff --git a/include/pvm/codegen/assembler.h b/include/pvm/codegen/assembler.h index 5d00191..9873e16 100644 --- a/include/pvm/codegen/assembler.h +++ b/include/pvm/codegen/assembler.h @@ -34,11 +34,11 @@ namespace ngu::pvm { * Each method returns @c detail::insn_data - a single encoded instruction. * * @par Instruction groups - * Standard (@c dst + @c src): @c MOV, @c ADD, @c SUB, @c XOR, @c AND, @c OR, @c SHL, @c SHR, @c ROL, @c ROR, @c CMP. @n - * Unary (@c dst): @c NOT. @n - * Branches (@c offset): @c JMP, @c JMPA, @c JMPI, @c JNE, @c JE, @c JNEI, @c JEI. @n - * Meta (@c label_id): @c LABEL, @c JMPL, @c JEL, @c JNEL - resolved into real instructions on the final pass. @n - * No operands: @c NOP, @c HALT. + * Standard (@c dst + @c src): @c MOV, @c ADD, @c SUB, @c XOR, @c AND, @c OR, @c SHL, @c SHR, + * @c ROL, @c ROR, @c CMP. @n Unary (@c dst): @c NOT. @n Branches (@c offset): @c + * JMP, @c JMPA, @c JMPI, @c JNE, @c JE, @c JNEI, @c JEI. @n Meta (@c label_id): @c + * LABEL, @c JMPL, @c JEL, @c JNEL - resolved into real instructions on the final pass. @n No + * operands: @c NOP, @c HALT. * * @par Operand * The @c operand type accepts both a register (@c arch::reg) and an immediate value - @@ -46,183 +46,482 @@ namespace ngu::pvm { */ class assembler { public: - consteval explicit assembler(const architecture &arch) : arch_(arch) {} + consteval explicit assembler(const architecture& arch) : arch_(arch) { + } consteval detail::insn_data NOT(const arch::reg dst) const { - return {insn_encoder::make_header(arch_.ops.NOT, arch::insn_size::WORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), 0), 0, arch::insn_size::WORD}; + return { + insn_encoder::make_header( + arch_.ops.NOT, arch::insn_size::WORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), 0 + ), + 0, + arch::insn_size::WORD + }; } consteval detail::insn_data MOV(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.MOV, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.MOV, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.MOV, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.MOV, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data XOR(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.XOR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.XOR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.XOR, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.XOR, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data ADD(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.ADD, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.ADD, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.ADD, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.ADD, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data SUB(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.SUB, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.SUB, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.SUB, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.SUB, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data SHL(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.SHL, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.SHL, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.SHL, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.SHL, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data SHR(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.SHR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.SHR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.SHR, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.SHR, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data AND(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.AND, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.AND, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.AND, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.AND, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data OR(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.OR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.OR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.OR, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.OR, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data ROL(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.ROL, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.ROL, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.ROL, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.ROL, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data ROR(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.ROR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.ROR, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.ROR, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.ROR, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data CMP(const arch::reg dst, const operand src) const { if (src.is_imm) { auto const sz = detail::calc_insn_size(src.value, src.is_signed); - return {insn_encoder::make_header(arch_.ops.CMP, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0), src.value, sz}; + return { + insn_encoder::make_header( + arch_.ops.CMP, sz, arch::insn_mode::IMMEDIATE, detail::phys_reg(arch_, dst), 0 + ), + src.value, + sz + }; } - return {insn_encoder::make_header(arch_.ops.CMP, arch::insn_size::DWORD, arch::insn_mode::REGISTER, detail::phys_reg(arch_, dst), detail::phys_reg(arch_, static_cast(src.value))), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.CMP, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + detail::phys_reg(arch_, dst), + detail::phys_reg(arch_, static_cast(src.value)) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JMP(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JMP, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JMP, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JMP, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JMP, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JMPA(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JMPA, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JMPA, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JMPA, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JMPA, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JMPI(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JMPI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JMPI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JMPI, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JMPI, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JNE(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JNE, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JNE, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JNE, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JNE, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JE(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JE, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JE, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JE, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JE, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JNEI(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JNEI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JNEI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JNEI, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JNEI, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data JEI(const operand offset) const { if (offset.is_imm) { auto const sz = detail::calc_insn_size(offset.value, offset.is_signed); - return {insn_encoder::make_header(arch_.ops.JEI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz}; + return { + insn_encoder::make_header(arch_.ops.JEI, sz, arch::insn_mode::IMMEDIATE, 0, 0), offset.value, sz + }; } - return {insn_encoder::make_header(arch_.ops.JEI, arch::insn_size::DWORD, arch::insn_mode::REGISTER, 0, static_cast(offset.value)), 0, arch::insn_size::DWORD}; + return { + insn_encoder::make_header( + arch_.ops.JEI, + arch::insn_size::DWORD, + arch::insn_mode::REGISTER, + 0, + static_cast(offset.value) + ), + 0, + arch::insn_size::DWORD + }; } consteval detail::insn_data LABEL(const std::uint32_t label_id) const { - return {insn_encoder::make_header(arch::meta_op::OP_LABEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0), label_id, arch::insn_size::DWORD_D}; + return { + insn_encoder::make_header( + arch::meta_op::OP_LABEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0 + ), + label_id, + arch::insn_size::DWORD_D + }; } consteval detail::insn_data JMPL(const std::uint32_t label_id) const { - return {insn_encoder::make_header(arch::meta_op::OP_JMPL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0), label_id, arch::insn_size::DWORD_D}; + return { + insn_encoder::make_header( + arch::meta_op::OP_JMPL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0 + ), + label_id, + arch::insn_size::DWORD_D + }; } consteval detail::insn_data JEL(const std::uint32_t label_id) const { - return {insn_encoder::make_header(arch::meta_op::OP_JEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0), label_id, arch::insn_size::DWORD_D}; + return { + insn_encoder::make_header( + arch::meta_op::OP_JEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0 + ), + label_id, + arch::insn_size::DWORD_D + }; } consteval detail::insn_data JNEL(const std::uint32_t label_id) const { - return {insn_encoder::make_header(arch::meta_op::OP_JNEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0), label_id, arch::insn_size::DWORD_D}; + return { + insn_encoder::make_header( + arch::meta_op::OP_JNEL, arch::insn_size::DWORD_D, arch::insn_mode::IMMEDIATE, 0, 0 + ), + label_id, + arch::insn_size::DWORD_D + }; } consteval detail::insn_data NOP() const { - return {insn_encoder::make_header(arch_.ops.NOP, arch::insn_size::BYTE, arch::insn_mode::REGISTER, 0, 0), 0, arch::insn_size::BYTE}; + return { + insn_encoder::make_header(arch_.ops.NOP, arch::insn_size::BYTE, arch::insn_mode::REGISTER, 0, 0), + 0, + arch::insn_size::BYTE + }; } consteval detail::insn_data HALT() const { - return {insn_encoder::make_header(arch_.ops.HALT, arch::insn_size::BYTE, arch::insn_mode::REGISTER, 0, 0), 0, arch::insn_size::BYTE}; + return { + insn_encoder::make_header(arch_.ops.HALT, arch::insn_size::BYTE, arch::insn_mode::REGISTER, 0, 0), + 0, + arch::insn_size::BYTE + }; } private: architecture arch_; }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_CODEGEN_ASSEMBLER_H \ No newline at end of file +#endif // NGU_PVM_CODEGEN_ASSEMBLER_H \ No newline at end of file diff --git a/include/pvm/codegen/codegen.h b/include/pvm/codegen/codegen.h index 9f2e58e..d07de34 100644 --- a/include/pvm/codegen/codegen.h +++ b/include/pvm/codegen/codegen.h @@ -25,13 +25,15 @@ namespace ngu::pvm { namespace detail { - /// @brief Returns the number of instructions in type T (1 for a single instruction, T::count for macro-instructions). - template consteval std::size_t instruction_count() { + /// @brief Returns the number of instructions in type T (1 for a single instruction, + /// T::count for macro-instructions). + template consteval std::size_t instruction_count() { using U = std::remove_cvref_t; - if constexpr (requires { U::count; }) + if constexpr (requires { U::count; }) { return U::count; - else + } else { return 1; + } } /// @brief Shrinks the bytecode to its actual size, removing the unused tail of the buffer. @@ -40,13 +42,15 @@ namespace ngu::pvm { constexpr std::size_t exact = big.length > 0 ? big.length : 1; std::uint8_t arr[exact]{}; - for (std::size_t i{}; i < big.length; ++i) + for (std::size_t i{}; i < big.length; ++i) { arr[i] = big.bytes[i]; - return bytecode{ arr, big.length }; + } + return bytecode{arr, big.length}; } - /// @brief First assembly pass: encodes instructions into bytecode with meta-opcodes and labels left unresolved. - template consteval auto intermediate_assemble(Instructions&&... insns) { + /// @brief First assembly pass: encodes instructions into bytecode with meta-opcodes and + /// labels left unresolved. + template consteval auto intermediate_assemble(Instructions&&... insns) { constexpr std::size_t count = (instruction_count() + ...); constexpr std::size_t max_size = count * 12; @@ -56,8 +60,9 @@ namespace ngu::pvm { auto add = [&](auto&& arg) { using U = std::remove_cvref_t; if constexpr (requires { U::count; }) { - for (std::size_t i{}; i < U::count; ++i) + for (std::size_t i{}; i < U::count; ++i) { arr[idx++] = arg.data[i]; + } } else { arr[idx++] = static_cast(arg); } @@ -74,28 +79,30 @@ namespace ngu::pvm { auto imm_size = get_imm_size(insn.insn_sz); auto hdr_size = total_size - imm_size; - for (std::size_t j = 0; j < hdr_size; ++j) + for (std::size_t j = 0; j < hdr_size; ++j) { bytes[pos++] = (insn.header >> (j * 8)) & 0xFF; - for (std::size_t j = 0; j < imm_size; ++j) + } + for (std::size_t j = 0; j < imm_size; ++j) { bytes[pos++] = (insn.immediate >> (j * 8)) & 0xFF; + } } - return bytecode{ bytes, pos }; + return bytecode{bytes, pos}; } /// @brief Looks up the instruction index for a given label ID in the label table. consteval std::uint64_t find_label_target(const label_table& labels, std::uint64_t label_id) { - for (std::size_t i{}; i < labels.size(); ++i) - if (labels[i].label_id == label_id) + for (std::size_t i{}; i < labels.size(); ++i) { + if (labels[i].label_id == label_id) { return labels[i].target_index; + } + } return 0; } - /// @brief Second assembly pass: resolves labels and meta-opcodes, replacing them with real opcodes and jump targets. - template consteval auto compile_bytecode( - const bytecode& code, - const architecture& arch - ) { + /// @brief Second assembly pass: resolves labels and meta-opcodes, replacing them with real + /// opcodes and jump targets. + template consteval auto compile_bytecode(const bytecode& code, const architecture& arch) { std::uint8_t result[N]{}; std::size_t result_pos{}; @@ -112,9 +119,7 @@ namespace ngu::pvm { } if (entry.data_ct.is_meta) { - auto const real_opcode = resolve_meta( - arch.ops, static_cast(entry.data_ct.opcode) - ); + auto const real_opcode = resolve_meta(arch.ops, static_cast(entry.data_ct.opcode)); auto const target = find_label_target(labels, entry.data_ct.immediate); auto header = insn_decoder::read_header(&code.bytes[entry.offset]); @@ -136,9 +141,9 @@ namespace ngu::pvm { } } - return bytecode{ result, result_pos }; + return bytecode{result, result_pos}; } - } + } // namespace detail /** * @brief Assembles bytecode from a set of instructions in two passes. @@ -150,15 +155,18 @@ namespace ngu::pvm { * @param insns Instructions to assemble. * @return Bytecode ready for execution. */ - template consteval auto assemble(const architecture& arch, Instructions&&... insns) { + template consteval auto assemble(const architecture& arch, Instructions&&... insns) { auto intermediate = detail::intermediate_assemble(std::forward(insns)...); return detail::compile_bytecode(intermediate, arch); } -} +} // namespace ngu::pvm // Assembles and shrinks bytecode to its exact size at compile time. -// Uses a captureless consteval lambda as a non-type template parameter to satisfy consteval context requirements. -#define PVM_ASSEMBLE(arch, ...) \ - ::ngu::pvm::detail::shrink_to_fit<[]() consteval { return ::ngu::pvm::assemble(arch, __VA_ARGS__); }>() - -#endif //NGU_PVM_CODEGEN_CODEGEN_H \ No newline at end of file +// Uses a captureless consteval lambda as a non-type template parameter to satisfy consteval context +// requirements. +#define PVM_ASSEMBLE(arch, ...) \ + ::ngu::pvm::detail::shrink_to_fit<[]() consteval { \ + return ::ngu::pvm::assemble(arch, __VA_ARGS__); \ + }>() + +#endif // NGU_PVM_CODEGEN_CODEGEN_H \ No newline at end of file diff --git a/include/pvm/codegen/codegen_types.h b/include/pvm/codegen/codegen_types.h index 6cdfaaa..9be1221 100644 --- a/include/pvm/codegen/codegen_types.h +++ b/include/pvm/codegen/codegen_types.h @@ -29,7 +29,8 @@ namespace ngu::pvm { * * @c header - encoded 32-bit instruction word. @n * @c immediate - immediate value (0 if absent). @n - * @c insn_sz - instruction size variant; used by @c size() to compute the total byte length. + * @c insn_sz - instruction size variant; used by @c size() to compute the total byte + * length. */ struct instruction_data { constexpr std::size_t size() const { @@ -44,25 +45,25 @@ namespace ngu::pvm { using insn_data = instruction_data; /** - * @brief A fixed-size compile-time array of @ref instruction_data produced by @ref macro_assembler methods. + * @brief A fixed-size compile-time array of @ref instruction_data produced by @ref + * macro_assembler methods. * * @c count - number of instructions, deduced from the @c Types pack. @n * @c data - array of encoded instructions. * * @tparam Types Pack of @ref instruction_data instances; its size determines @c count. */ - template struct instruction_sequence { + template struct instruction_sequence { static constexpr std::size_t count = sizeof...(Types); - consteval explicit instruction_sequence(Types&&... args) - : data{ static_cast(args)... } {} + consteval explicit instruction_sequence(Types&&... args) : data{static_cast(args)...} { + } instruction_data data[sizeof...(Types)]; }; - template - using insn_seq = instruction_sequence; - } + template using insn_seq = instruction_sequence; + } // namespace detail /** * @brief Unified operand type accepted by @ref assembler and @ref macro_assembler methods. @@ -70,19 +71,21 @@ namespace ngu::pvm { * Constructed from an integral immediate value or an @c arch::reg register. @n * @c value - register index or immediate value. @n * @c is_imm - true if constructed from an integral value; false if from @c arch::reg. @n - * @c is_signed - true if the integral type is signed; used to select the minimal instruction size. + * @c is_signed - true if the integral type is signed; used to select the minimal instruction + * size. */ struct operand { - template - explicit constexpr operand(const Type v) requires std::is_unsigned_v || std::is_integral_v - : value(v), is_imm(true), is_signed(std::is_signed_v) {} - constexpr explicit operand(const arch::reg r) - : value(r) {} + template explicit constexpr operand(const Type v) + requires std::is_unsigned_v || std::is_integral_v + : value(v), is_signed(std::is_signed_v), is_imm(true) { + } + constexpr explicit operand(const arch::reg r) : value(r) { + } std::uint64_t value{}; bool is_signed{}; bool is_imm{}; }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_CODEGEN_CODEGEN_TYPES_H \ No newline at end of file +#endif // NGU_PVM_CODEGEN_CODEGEN_TYPES_H \ No newline at end of file diff --git a/include/pvm/codegen/macro_assembler.h b/include/pvm/codegen/macro_assembler.h index 7dd399a..4365c0e 100644 --- a/include/pvm/codegen/macro_assembler.h +++ b/include/pvm/codegen/macro_assembler.h @@ -33,64 +33,48 @@ namespace ngu::pvm { * @par Macro groups * Arithmetic: @c ZERO, @c NEG, @c MOV64, @c MUL_POW2, @c DIV_POW2, @c MUL3, @c MUL5. @n * Comparison/flow: @c ABS, @c MIN, @c MAX, @c LOOP. @n - * Bit manipulation: @c MASK_LO, @c CLEAR_LO, @c EXTRACT_BYTE, @c INSERT_BYTE, @c ROT16, @c BSWAP32, @c TEST_BIT. @n - * 64-bit ops: @c INC64, @c ADD64. @n - * Crypto primitives: @c ARX, @c XR, @c AX, @c XS (ChaCha/Salsa-style operations). @n - * Utilities: @c MOVC, @c SWAP, @c ALIGN_UP. + * Bit manipulation: @c MASK_LO, @c CLEAR_LO, @c EXTRACT_BYTE, @c INSERT_BYTE, @c ROT16, @c + * BSWAP32, @c TEST_BIT. + * @n 64-bit ops: @c INC64, @c ADD64. @n Crypto primitives: @c ARX, @c XR, @c AX, @c XS + * (ChaCha/Salsa-style operations). @n Utilities: @c MOVC, @c SWAP, @c ALIGN_UP. */ class macro_assembler : public assembler { public: - consteval explicit macro_assembler(const architecture& arch) - : assembler(arch) {} + consteval explicit macro_assembler(const architecture& arch) : assembler(arch) { + } // dst = 0 consteval auto ZERO(const arch::reg dst) const { - return detail::insn_seq{ - XOR(dst, operand(dst)) - }; + return detail::insn_seq{XOR(dst, operand(dst))}; } // dst = -dst (two's complement) consteval auto NEG(const arch::reg dst) const { - return detail::insn_seq{ - NOT(dst), - ADD(dst, operand(1u)) - }; + return detail::insn_seq{NOT(dst), ADD(dst, operand(1u))}; } // dst = src (via xor+or) consteval auto MOVC(const arch::reg dst, const arch::reg src) const { - return detail::insn_seq{ - XOR(dst, operand(dst)), - OR(dst, operand(src)) - }; + return detail::insn_seq{XOR(dst, operand(dst)), OR(dst, operand(src))}; } // dst = (hi << 32) | lo consteval auto MOV64(const arch::reg dst, std::uint32_t hi, std::uint32_t lo) const { - return detail::insn_seq{ - MOV(dst, operand(hi)), - SHL(dst, operand(32u)), - OR(dst, operand(lo)) - }; + return detail::insn_seq{MOV(dst, operand(hi)), SHL(dst, operand(32u)), OR(dst, operand(lo))}; } // swap a, b without temp consteval auto SWAP(const arch::reg a, const arch::reg b) const { - return detail::insn_seq{ - XOR(a, operand(b)), - XOR(b, operand(a)), - XOR(a, operand(b)) - }; + return detail::insn_seq{XOR(a, operand(b)), XOR(b, operand(a)), XOR(a, operand(b))}; } // dst = |dst| (signed). scratch is clobbered. consteval auto ABS(const arch::reg dst, const arch::reg scratch, std::uint32_t label_id) const { return detail::insn_seq{ MOV(scratch, operand(dst)), - SHR(scratch, operand(63u)), // scratch = 1 if negative, 0 if positive/zero + SHR(scratch, operand(63u)), // scratch = 1 if negative, 0 if positive/zero CMP(scratch, operand(0u)), - JEL(label_id), // if positive/zero, skip negation + JEL(label_id), // if positive/zero, skip negation NOT(dst), ADD(dst, operand(1u)), LABEL(label_id) @@ -98,89 +82,76 @@ namespace ngu::pvm { } // a = min(a, b) unsigned. scratch is clobbered. - consteval auto MIN(const arch::reg a, const arch::reg b, const arch::reg scratch, std::uint32_t label_id) const { + consteval auto MIN(const arch::reg a, const arch::reg b, const arch::reg scratch, std::uint32_t label_id) + const { return detail::insn_seq{ - CMP(a, operand(b)), // bit0=EQ, bit1=LT(a b, so a = b + JNEL(label_id), // if LT was set (a < b), keep a + MOV(a, operand(b)), // a > b, so a = b LABEL(label_id) }; } // a = max(a, b) unsigned. scratch is clobbered. - consteval auto MAX(const arch::reg a, const arch::reg b, const arch::reg scratch, std::uint32_t label_id) const { + consteval auto MAX(const arch::reg a, const arch::reg b, const arch::reg scratch, std::uint32_t label_id) + const { return detail::insn_seq{ - CMP(a, operand(b)), // bit0=EQ, bit1=LT(a b), keep a - MOV(a, operand(b)), // a < b, so a = b + JEL(label_id), // if LT was not set (a > b), keep a + MOV(a, operand(b)), // a < b, so a = b LABEL(label_id) }; } // dst *= 2^n consteval auto MUL_POW2(const arch::reg dst, std::uint8_t n) const { - return detail::insn_seq{ - SHL(dst, operand(n)) - }; + return detail::insn_seq{SHL(dst, operand(n))}; } // dst /= 2^n (unsigned) consteval auto DIV_POW2(const arch::reg dst, std::uint8_t n) const { - return detail::insn_seq{ - SHR(dst, operand(n)) - }; + return detail::insn_seq{SHR(dst, operand(n))}; } // dst *= 3 consteval auto MUL3(const arch::reg dst, const arch::reg scratch) const { - return detail::insn_seq{ - MOV(scratch, operand(dst)), - SHL(scratch, operand(1u)), - ADD(dst, operand(scratch)) - }; + return detail::insn_seq{MOV(scratch, operand(dst)), SHL(scratch, operand(1u)), ADD(dst, operand(scratch))}; } // dst *= 5 consteval auto MUL5(const arch::reg dst, const arch::reg scratch) const { - return detail::insn_seq{ - MOV(scratch, operand(dst)), - SHL(scratch, operand(2u)), - ADD(dst, operand(scratch)) - }; + return detail::insn_seq{MOV(scratch, operand(dst)), SHL(scratch, operand(2u)), ADD(dst, operand(scratch))}; } // dst = (dst + align-1) & ~(align-1), align must be power of 2 consteval auto ALIGN_UP(const arch::reg dst, std::uint32_t align) const { - return detail::insn_seq{ - ADD(dst, operand(align - 1)), - AND(dst, operand(~(align - 1))) - }; + return detail::insn_seq{ADD(dst, operand(align - 1)), AND(dst, operand(~(align - 1)))}; } // (lo, hi) += 1 with carry - consteval auto INC64(const arch::reg lo, const arch::reg hi, - std::uint32_t label_id) const { + consteval auto INC64(const arch::reg lo, const arch::reg hi, std::uint32_t label_id) const { return detail::insn_seq{ - ADD(lo, operand(1u)), - CMP(lo, operand(0u)), - JNEL(label_id), - ADD(hi, operand(1u)), - LABEL(label_id) + ADD(lo, operand(1u)), CMP(lo, operand(0u)), JNEL(label_id), ADD(hi, operand(1u)), LABEL(label_id) }; } // (dst_lo, dst_hi) += (src_lo, src_hi) with carry - consteval auto ADD64(const arch::reg dst_lo, const arch::reg dst_hi, - const arch::reg src_lo, const arch::reg src_hi, - const arch::reg tmp, std::uint32_t label_id) const { + consteval auto ADD64( + const arch::reg dst_lo, + const arch::reg dst_hi, + const arch::reg src_lo, + const arch::reg src_hi, + const arch::reg tmp, + std::uint32_t label_id + ) const { return detail::insn_seq{ MOV(tmp, operand(dst_lo)), ADD(dst_lo, operand(src_lo)), @@ -204,16 +175,12 @@ namespace ngu::pvm { // dst &= (1 << n) - 1 consteval auto MASK_LO(const arch::reg dst, std::uint8_t n) const { - return detail::insn_seq{ - AND(dst, operand((1ull << n) - 1)) - }; + return detail::insn_seq{AND(dst, operand((1ull << n) - 1))}; } // dst &= ~((1 << n) - 1) consteval auto CLEAR_LO(const arch::reg dst, std::uint8_t n) const { - return detail::insn_seq{ - AND(dst, operand(~((1ull << n) - 1))) - }; + return detail::insn_seq{AND(dst, operand(~((1ull << n) - 1)))}; } // scratch = (src >> idx*8) & 0xFF @@ -236,9 +203,7 @@ namespace ngu::pvm { // dst = swap upper/lower 16 bits consteval auto ROT16(const arch::reg dst) const { - return detail::insn_seq{ - ROL(dst, operand(16u)) - }; + return detail::insn_seq{ROL(dst, operand(16u))}; } // dst = byte-reverse dst @@ -264,34 +229,24 @@ namespace ngu::pvm { } // a += b; c ^= a; c <<<= n - consteval auto ARX(const arch::reg a, const arch::reg b, - const arch::reg c, std::uint8_t rot) const { - return detail::insn_seq{ - ADD(a, operand(b)), - XOR(c, operand(a)), - ROL(c, operand(rot)) - }; + consteval auto ARX(const arch::reg a, const arch::reg b, const arch::reg c, std::uint8_t rot) const { + return detail::insn_seq{ADD(a, operand(b)), XOR(c, operand(a)), ROL(c, operand(rot))}; } // a ^= b; a <<<= n consteval auto XR(const arch::reg a, const arch::reg b, std::uint8_t rot) const { - return detail::insn_seq{ - XOR(a, operand(b)), - ROL(a, operand(rot)) - }; + return detail::insn_seq{XOR(a, operand(b)), ROL(a, operand(rot))}; } // a += b; c ^= a consteval auto AX(const arch::reg a, const arch::reg b, const arch::reg c) const { - return detail::insn_seq{ - ADD(a, operand(b)), - XOR(c, operand(a)) - }; + return detail::insn_seq{ADD(a, operand(b)), XOR(c, operand(a))}; } // scratch = (v << left) ^ (v >> right) - consteval auto XS(const arch::reg v, const arch::reg scratch, - const arch::reg tmp, std::uint8_t left, std::uint8_t right) const { + consteval auto XS( + const arch::reg v, const arch::reg scratch, const arch::reg tmp, std::uint8_t left, std::uint8_t right + ) const { return detail::insn_seq{ MOV(scratch, operand(v)), SHL(scratch, operand(left)), @@ -303,13 +258,9 @@ namespace ngu::pvm { // counter -= 1; jump to body_label if counter != 0 consteval auto LOOP(const arch::reg counter, std::uint32_t body_label) const { - return detail::insn_seq{ - SUB(counter, operand(1u)), - CMP(counter, operand(0u)), - JNEL(body_label) - }; + return detail::insn_seq{SUB(counter, operand(1u)), CMP(counter, operand(0u)), JNEL(body_label)}; } }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_CODEGEN_MACRO_ASSEMBLER_H \ No newline at end of file +#endif // NGU_PVM_CODEGEN_MACRO_ASSEMBLER_H \ No newline at end of file diff --git a/include/pvm/engine/architecture.h b/include/pvm/engine/architecture.h index af58b6a..c966a7b 100644 --- a/include/pvm/engine/architecture.h +++ b/include/pvm/engine/architecture.h @@ -31,40 +31,37 @@ namespace ngu::pvm { return state * 6364136223846793005ULL + 1442695040888963407ULL; } - consteval void shuffle_indices( - std::uint8_t* arr, - const std::size_t count, - const std::uint64_t entropy - ) { + consteval void shuffle_indices(std::uint8_t* arr, const std::size_t count, const std::uint64_t entropy) { for (std::size_t i{}; i < count; i++) { arr[i] = static_cast(i); } - std::uint64_t rng_state{ entropy }; - for (std::size_t i{ count - 1 }; i > 0; i--) { + std::uint64_t rng_state{entropy}; + for (std::size_t i{count - 1}; i > 0; i--) { rng_state = lcg_next(rng_state); - std::size_t j{ rng_state % (i + 1) }; - std::uint8_t tmp{ arr[i] }; + std::size_t j{rng_state % (i + 1)}; + std::uint8_t tmp{arr[i]}; arr[i] = arr[j]; arr[j] = tmp; } } - } + } // namespace detail /** - * @brief Describes the ISA (Instruction Set Architecture) and ABI (Application Binary Interface). + * @brief Describes the ISA (Instruction Set Architecture) and ABI (Application Binary + * Interface). * * The entire architecture is evaluated at compile time (consteval). * * @par Instruction Set (ISA) - * Instruction format: @c OPCODE(0) | INSN_SIZE(8) | MODE(11) | DESTINATION(12) | SOURCE(16) @n - * Without immediate: @c BYTE(2B), @c WORD(2B), @c DWORD(4B) @n - * With immediate: @c DWORD_B(5/1B), @c DWORD_W(6/2B), @c DWORD_D(8/4B), @c DWORD_Q(12/8B) @n - * Addressing modes: @c REGISTER, @c IMMEDIATE @n - * Registers: @c R0–R11 - 12 general-purpose registers; @c FLAGS and @c PC - special-purpose. @n - * Meta-opcodes: @c OP_LABEL, @c OP_JMPL, @c OP_JEL, @c OP_JNEL - markers resolved into real - * branch instructions on the final pass. @n - * Standard opcodes: 21 opcodes describing the VM execution logic (@c MOV, @c ADD, @c SUB, ...). + * Instruction format: @c OPCODE(0) | INSN_SIZE(8) | MODE(11) | DESTINATION(12) | + * SOURCE(16) @n Without immediate: @c BYTE(2B), @c WORD(2B), @c DWORD(4B) @n With + * immediate: @c DWORD_B(5/1B), @c DWORD_W(6/2B), @c DWORD_D(8/4B), @c DWORD_Q(12/8B) + * @n Addressing modes: @c REGISTER, @c IMMEDIATE @n Registers: @c R0–R11 + * - 12 general-purpose registers; @c FLAGS and @c PC - special-purpose. @n Meta-opcodes: @c + * OP_LABEL, @c OP_JMPL, @c OP_JEL, @c OP_JNEL - markers resolved into real branch instructions + * on the final pass. @n Standard opcodes: 21 opcodes describing the VM execution logic + * (@c MOV, @c ADD, @c SUB, ...). * * @par Binary Interface (ABI) * Physical indices of registers and opcodes are randomized independently and stored @@ -75,12 +72,16 @@ namespace ngu::pvm { public: template struct instruction { - enum class mode : std::uint8_t { - REGISTER, IMMEDIATE - }; + enum class mode : std::uint8_t { REGISTER, IMMEDIATE }; enum class insn_size : std::uint8_t { - BYTE, WORD, DWORD, DWORD_B, DWORD_W, DWORD_D, DWORD_Q, + BYTE, + WORD, + DWORD, + DWORD_B, + DWORD_W, + DWORD_D, + DWORD_Q, }; struct bits { @@ -113,8 +114,20 @@ namespace ngu::pvm { struct registers { enum logical_reg : std::uint8_t { - REG_R0, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6, REG_R7, REG_R8, REG_R9, REG_R10, REG_R11, - REG_FLAGS, REG_PC, + REG_R0, + REG_R1, + REG_R2, + REG_R3, + REG_R4, + REG_R5, + REG_R6, + REG_R7, + REG_R8, + REG_R9, + REG_R10, + REG_R11, + REG_FLAGS, + REG_PC, REG_MAX }; @@ -132,7 +145,8 @@ namespace ngu::pvm { }; mapping_entry physical_map[REG_MAX]; - }; registers regs{}; + }; + registers regs{}; using reg = registers::logical_reg; @@ -147,17 +161,32 @@ namespace ngu::pvm { enum logical_op : std::uint8_t { OP_MOV, - OP_ADD, OP_SUB, - OP_AND, OP_OR, OP_XOR, OP_NOT, - OP_SHL, OP_SHR, OP_ROL, OP_ROR, + OP_ADD, + OP_SUB, + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + OP_SHL, + OP_SHR, + OP_ROL, + OP_ROR, OP_CMP, - OP_JMP, OP_JMPA, OP_JMPI, - OP_JNE, OP_JE, OP_JNEI, OP_JEI, - OP_NOP, OP_HALT, + OP_JMP, + OP_JMPA, + OP_JMPI, + OP_JNE, + OP_JE, + OP_JNEI, + OP_JEI, + OP_NOP, + OP_HALT, OP_MAX, }; - static_assert(static_cast(OP_MAX) < static_cast(OP_META_START), "Opcodes out of bounds"); + static_assert( + static_cast(OP_MAX) < static_cast(OP_META_START), "Opcodes out of bounds" + ); union { struct { @@ -176,12 +205,12 @@ namespace ngu::pvm { mapping_entry physical_map[OP_MAX]; static constexpr mapping_entry meta_map[] = { - { OP_JMPL, OP_JMPI }, - { OP_JEL, OP_JEI }, - { OP_JNEL, OP_JNEI }, + {OP_JMPL, OP_JMPI}, + {OP_JEL, OP_JEI}, + {OP_JNEL, OP_JNEI}, }; - - }; opcodes ops{}; + }; + opcodes ops{}; using op = opcodes::logical_op; using meta_op = opcodes::meta_op; @@ -192,7 +221,8 @@ namespace ngu::pvm { * Determines the physical representation of registers and opcodes, * and builds their logical-to-physical mapping tables. * Indices are randomized via Fisher-Yates shuffle seeded with @p entropy (LCG-based). - * Registers and opcodes are shuffled independently: opcodes use @c lcg_next(entropy) as seed. + * Registers and opcodes are shuffled independently: opcodes use @c lcg_next(entropy) as + * seed. * * @param entropy Seed value. The same seed always produces the same ABI. * @return Initialized @ref architecture instance. @@ -211,10 +241,18 @@ namespace ngu::pvm { #define INIT_REG(path, idx) arch.regs.path = reg_perm[idx] - INIT_REG(gpr.R0, reg::REG_R0); INIT_REG(gpr.R1, reg::REG_R1); INIT_REG(gpr.R2, reg::REG_R2); - INIT_REG(gpr.R3, reg::REG_R3); INIT_REG(gpr.R4, reg::REG_R4); INIT_REG(gpr.R5, reg::REG_R5); - INIT_REG(gpr.R6, reg::REG_R6); INIT_REG(gpr.R7, reg::REG_R7); INIT_REG(gpr.R8, reg::REG_R8); - INIT_REG(gpr.R9, reg::REG_R9); INIT_REG(gpr.R10, reg::REG_R10); INIT_REG(gpr.R11, reg::REG_R11); + INIT_REG(gpr.R0, reg::REG_R0); + INIT_REG(gpr.R1, reg::REG_R1); + INIT_REG(gpr.R2, reg::REG_R2); + INIT_REG(gpr.R3, reg::REG_R3); + INIT_REG(gpr.R4, reg::REG_R4); + INIT_REG(gpr.R5, reg::REG_R5); + INIT_REG(gpr.R6, reg::REG_R6); + INIT_REG(gpr.R7, reg::REG_R7); + INIT_REG(gpr.R8, reg::REG_R8); + INIT_REG(gpr.R9, reg::REG_R9); + INIT_REG(gpr.R10, reg::REG_R10); + INIT_REG(gpr.R11, reg::REG_R11); INIT_REG(spr.FLAGS, reg::REG_FLAGS); INIT_REG(spr.PC, reg::REG_PC); @@ -222,30 +260,37 @@ namespace ngu::pvm { #undef INIT_REG for (std::size_t i{}; i < reg::REG_MAX; ++i) { - arch.regs.physical_map[i] = mapping_entry( - static_cast(i), - reg_perm[i] - ); + arch.regs.physical_map[i] = mapping_entry(static_cast(i), reg_perm[i]); } #define INIT_OP(name, idx) arch.ops.name = op_perm[idx] INIT_OP(MOV, op::OP_MOV); - INIT_OP(ADD, op::OP_ADD); INIT_OP(SUB, op::OP_SUB); - INIT_OP(AND, op::OP_AND); INIT_OP(OR, op::OP_OR); INIT_OP(XOR, op::OP_XOR); INIT_OP(NOT, op::OP_NOT); - INIT_OP(SHL, op::OP_SHL); INIT_OP(SHR, op::OP_SHR); INIT_OP(ROL, op::OP_ROL); INIT_OP(ROR, op::OP_ROR); + INIT_OP(ADD, op::OP_ADD); + INIT_OP(SUB, op::OP_SUB); + INIT_OP(AND, op::OP_AND); + INIT_OP(OR, op::OP_OR); + INIT_OP(XOR, op::OP_XOR); + INIT_OP(NOT, op::OP_NOT); + INIT_OP(SHL, op::OP_SHL); + INIT_OP(SHR, op::OP_SHR); + INIT_OP(ROL, op::OP_ROL); + INIT_OP(ROR, op::OP_ROR); INIT_OP(CMP, op::OP_CMP); - INIT_OP(JMP, op::OP_JMP); INIT_OP(JMPA, op::OP_JMPA); INIT_OP(JMPI, op::OP_JMPI); - INIT_OP(JNE, op::OP_JNE); INIT_OP(JE, op::OP_JE); INIT_OP(JNEI, op::OP_JNEI); INIT_OP(JEI, op::OP_JEI); - INIT_OP(NOP, op::OP_NOP); INIT_OP(HALT, op::OP_HALT); + INIT_OP(JMP, op::OP_JMP); + INIT_OP(JMPA, op::OP_JMPA); + INIT_OP(JMPI, op::OP_JMPI); + INIT_OP(JNE, op::OP_JNE); + INIT_OP(JE, op::OP_JE); + INIT_OP(JNEI, op::OP_JNEI); + INIT_OP(JEI, op::OP_JEI); + INIT_OP(NOP, op::OP_NOP); + INIT_OP(HALT, op::OP_HALT); #undef INIT_OP for (std::size_t i{}; i < op::OP_MAX; ++i) { - arch.ops.physical_map[i] = mapping_entry( - static_cast(i), - op_perm[i] - ); + arch.ops.physical_map[i] = mapping_entry(static_cast(i), op_perm[i]); } return arch; @@ -259,12 +304,17 @@ namespace ngu::pvm { } PVM_FORCEINLINE constexpr std::uint8_t resolve_meta(const arch::opcodes& ops, arch::meta_op m) { - for (const auto& [from, to] : arch::opcodes::meta_map) - if (from == m) return ops.physical_map[to].to; + for (const auto& [from, to] : arch::opcodes::meta_map) { + if (from == m) { + return ops.physical_map[to].to; + } + } return 0; } - PVM_FORCEINLINE constexpr arch::insn_size calc_insn_size(const std::uint64_t val, const bool is_signed = false) { + PVM_FORCEINLINE constexpr arch::insn_size calc_insn_size( + const std::uint64_t val, const bool is_signed = false + ) { if (is_signed) { auto const signed_val = static_cast(val); @@ -296,15 +346,15 @@ namespace ngu::pvm { } PVM_FORCEINLINE constexpr std::size_t get_total_size(const arch::insn_size sz) { - constexpr std::size_t sizes[]{ 2, 2, 4, 5, 6, 8, 12 }; + constexpr std::size_t sizes[]{2, 2, 4, 5, 6, 8, 12}; return sizes[static_cast(sz)]; } PVM_FORCEINLINE constexpr std::size_t get_imm_size(const arch::insn_size sz) { - constexpr std::size_t imm_sizes[]{ 0, 0, 0, 1, 2, 4, 8 }; + constexpr std::size_t imm_sizes[]{0, 0, 0, 1, 2, 4, 8}; return imm_sizes[static_cast(sz)]; } - } -} + } // namespace detail +} // namespace ngu::pvm -#endif //NGU_PVM_ENGINE_ARCHITECTURE_H \ No newline at end of file +#endif // NGU_PVM_ENGINE_ARCHITECTURE_H \ No newline at end of file diff --git a/include/pvm/engine/context.h b/include/pvm/engine/context.h index ea7ce15..873b75f 100644 --- a/include/pvm/engine/context.h +++ b/include/pvm/engine/context.h @@ -32,19 +32,21 @@ namespace ngu::pvm { * allowing the context to be created at compile time. * * @par Fields - * @c arch_ - copy of the @ref architecture object; defines physical indices of registers and opcodes. @n + * @c arch_ - copy of the @ref architecture object; defines physical indices of registers and + * opcodes. @n * @c regs_ - register value array, indexed by physical indices. @n * @c halted_ - execution stop flag; set via @ref halt(). * * @par Methods * Each register operation is available in two variants: @n - * - runtime (@c PVM_FORCEINLINE): @c set_reg, @c get_reg, @c set_flags, @c set_pc, @c jump, @c halt. @n + * - runtime (@c PVM_FORCEINLINE): @c set_reg, @c get_reg, @c set_flags, @c set_pc, @c jump, @c + * halt. @n * - compile-time (@c consteval): @c set_reg_ct, @c set_flags_ct, @c set_pc_ct. */ class context { public: - explicit consteval context(const architecture& arch) : - arch_(arch) {}; + explicit consteval context(const architecture& arch) : arch_(arch) { + } /// @brief Writes @p val to register at physical index @p idx. PVM_FORCEINLINE void set_reg(const std::uint8_t idx, const std::uint64_t val) { @@ -126,6 +128,6 @@ namespace ngu::pvm { std::uint64_t regs_[arch::reg::REG_MAX]{}; bool halted_{}; }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_ENGINE_CONTEXT_H \ No newline at end of file +#endif // NGU_PVM_ENGINE_CONTEXT_H \ No newline at end of file diff --git a/include/pvm/engine/instructions.h b/include/pvm/engine/instructions.h index 5c655da..c55da7e 100644 --- a/include/pvm/engine/instructions.h +++ b/include/pvm/engine/instructions.h @@ -27,9 +27,9 @@ namespace ngu::pvm { /** * @brief Instruction dispatcher based on the Strategy pattern. * - * @c execute() constructs a temporary @c Strategy object and calls @c impl() with the provided arguments. - * The pattern allows swapping primitive implementations - for example, - * obfuscated variants such as @c xored_mov, @c xored_add, etc. could be introduced in the future. + * @c execute() constructs a temporary @c Strategy object and calls @c impl() with the provided + * arguments. The pattern allows swapping primitive implementations - for example, obfuscated + * variants such as @c xored_mov, @c xored_add, etc. could be introduced in the future. * * @tparam Strategy Type implementing the @c impl() method with the required signature. */ @@ -39,73 +39,72 @@ namespace ngu::pvm { } }; - template - using insn_dispatch = instruction_dispatch; + template using insn_dispatch = instruction_dispatch; struct mov_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), val); } }; struct xor_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) ^ val); } }; struct add_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) + val); } }; struct sub_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) - val); } }; struct shl_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const shift = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) << shift); } }; struct shr_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const shift = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) >> shift); } }; struct and_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) & val); } }; struct or_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->set_reg(insn.destination(), ctx->get_reg(insn.destination()) | val); } }; struct not_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { ctx->set_reg(insn.destination(), ~ctx->get_reg(insn.destination())); } }; struct rol_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = ctx->get_reg(insn.destination()); auto shift = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); shift &= 63; @@ -114,7 +113,7 @@ namespace ngu::pvm { }; struct ror_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const val = ctx->get_reg(insn.destination()); auto shift = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); shift &= 63; @@ -123,34 +122,38 @@ namespace ngu::pvm { }; struct cmp_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const a = ctx->get_reg(insn.destination()); auto const b = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); std::uint64_t flags{}; - if (a == b) flags |= (1ULL << 0); - if (a < b) flags |= (1ULL << 1); + if (a == b) { + flags |= (1ULL << 0); + } + if (a < b) { + flags |= (1ULL << 1); + } ctx->set_flags(flags); } }; struct jmp_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const offset = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->jump(ctx->get_pc() + insn.size() + offset); } }; struct jmpa_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { auto const offset = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->jump(offset); } }; struct jmpi_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { auto const pos = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); if (pos >= insn_entries.size()) { @@ -162,7 +165,7 @@ namespace ngu::pvm { }; struct jne_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { if (!(ctx->get_flags() & 1)) { auto const offset = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->jump(ctx->get_pc() + insn.size() + offset); @@ -171,7 +174,7 @@ namespace ngu::pvm { }; struct je_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn) { if (ctx->get_flags() & 1) { auto const offset = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); ctx->jump(ctx->get_pc() + insn.size() + offset); @@ -180,7 +183,7 @@ namespace ngu::pvm { }; struct jnei_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { if (!(ctx->get_flags() & 1)) { auto const pos = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); @@ -194,7 +197,7 @@ namespace ngu::pvm { }; struct jei_base { - PVM_FORCEINLINE static void impl(context *ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { + PVM_FORCEINLINE static void impl(context* ctx, const insn_view& insn, const insn_stream_rt& insn_entries) { if (ctx->get_flags() & 1) { auto const pos = insn.has_immediate() ? insn.immediate() : ctx->get_reg(insn.source()); @@ -208,16 +211,16 @@ namespace ngu::pvm { }; struct halt_base { - PVM_FORCEINLINE static void impl(context *ctx) { + PVM_FORCEINLINE static void impl(context* ctx) { ctx->halt(); } }; struct nop_base { PVM_FORCEINLINE static void impl() { - //NOP + // NOP } }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_ENGINE_INSTRUCTIONS_H +#endif // NGU_PVM_ENGINE_INSTRUCTIONS_H diff --git a/include/pvm/engine/interpreter.h b/include/pvm/engine/interpreter.h index c9d0ea6..c3d8951 100644 --- a/include/pvm/engine/interpreter.h +++ b/include/pvm/engine/interpreter.h @@ -28,171 +28,170 @@ #include "../bytecode/bytecode_decoder.h" namespace ngu::pvm { - /** - * @brief Executes bytecode against a given @ref architecture. - * - * The constructor is marked @c consteval and takes the architecture by value, - * initializing the internal @ref context with the same copy. - * - * @par Fields - * @c arch_ - copy of the @ref architecture object; used to resolve physical opcodes in @ref dispatch(). @n - * @c ctx_ - execution state; marked @c mutable since @ref run() is const but modifies the context. - * - * @par Execution - * Main execution loop - @ref run(); instruction dispatching - @ref dispatch(). - */ - class interpreter { - public: - /// @brief Execution completion code returned by @ref run() and @ref dispatch(). - enum class status : std::uint8_t { - VM_SUCCESS, ///< Bytecode executed successfully or halted via @c HALT. - VM_ERROR_UNKNOWN_OPCODE, ///< Physical opcode does not match any known instruction. - VM_ERROR_INVALID_REGISTER, ///< Register index is out of bounds. - VM_ERROR_PC_OUT_OF_BOUNDS, ///< PC jumped outside the bytecode buffer. - VM_ERROR_INVALID_INSTRUCTION,///< Instruction header failed validation. - VM_ERROR_HALTED, ///< Execution attempted on an already halted context. - }; - - explicit consteval interpreter(const architecture &arch) : - arch_(arch), ctx_(arch_) {} - - /** - * @brief Executes bytecode @p cb until completion or an error. - * - * On each instruction, a context snapshot (@c ctx_ct) is created and written back - * only on successful execution - ensuring atomicity of each step. @n - * PC advances either via an explicit jump or by the size of the current instruction. - * - * @param cb Bytecode to execute. - * @return @ref status completion code. - */ - template PVM_FORCEINLINE status run(const bytecode& cb) const; - - /// @brief Returns a copy of the architecture. @return @ref architecture by value. - architecture get_arch() const { - return arch_; - } - - /// @brief Returns a pointer to the mutable execution context. @return Pointer to @ref context. - context* get_ctx() const { - return &ctx_; - } - - private: - PVM_FORCEINLINE status dispatch( - context* ctx, - const insn_view& insn, - const insn_stream_rt& insn_entries - ) const; - - architecture arch_; - mutable context ctx_; - }; - - template interpreter::status interpreter::run(const bytecode &cb) const { - const std::size_t size = cb.size(); - const std::uint8_t* pc = cb.bytes; - const std::uint8_t* end = cb.bytes + size; - - auto decoder = make_bytecode_decoder(cb); - - while (pc < end && !ctx_.is_halted()) { - if (pc >= end) { - return status::VM_ERROR_PC_OUT_OF_BOUNDS; - } - - auto ctx_ct = ctx_; - ctx_ct.set_pc(static_cast(pc - cb.bytes)); - - auto const insn = insn_view(reinterpret_cast(pc)); - - auto const prev_pc = ctx_ct.get_pc(); - - if (const auto status = dispatch(&ctx_ct, insn, decoder.get_instruction_stream()); - status != status::VM_SUCCESS) { - return status; - } - - if (ctx_ct.is_halted()) { - return status::VM_SUCCESS; - } + /** + * @brief Executes bytecode against a given @ref architecture. + * + * The constructor is marked @c consteval and takes the architecture by value, + * initializing the internal @ref context with the same copy. + * + * @par Fields + * @c arch_ - copy of the @ref architecture object; used to resolve physical opcodes in @ref + * dispatch(). @n + * @c ctx_ - execution state; marked @c mutable since @ref run() is const but modifies the + * context. + * + * @par Execution + * Main execution loop - @ref run(); instruction dispatching - @ref dispatch(). + */ + class interpreter { + public: + /// @brief Execution completion code returned by @ref run() and @ref dispatch(). + enum class status : std::uint8_t { + VM_SUCCESS, ///< Bytecode executed successfully or halted via @c HALT. + VM_ERROR_UNKNOWN_OPCODE, ///< Physical opcode does not match any known instruction. + VM_ERROR_INVALID_REGISTER, ///< Register index is out of bounds. + VM_ERROR_PC_OUT_OF_BOUNDS, ///< PC jumped outside the bytecode buffer. + VM_ERROR_INVALID_INSTRUCTION, ///< Instruction header failed validation. + VM_ERROR_HALTED, ///< Execution attempted on an already halted context. + }; + + explicit consteval interpreter(const architecture& arch) : arch_(arch), ctx_(arch_) { + } + + /** + * @brief Executes bytecode @p cb until completion or an error. + * + * On each instruction, a context snapshot (@c ctx_ct) is created and written back + * only on successful execution - ensuring atomicity of each step. @n + * PC advances either via an explicit jump or by the size of the current instruction. + * + * @param cb Bytecode to execute. + * @return @ref status completion code. + */ + template PVM_FORCEINLINE status run(const bytecode& cb) const; + + /// @brief Returns a copy of the architecture. @return @ref architecture by value. + architecture get_arch() const { + return arch_; + } + + /// @brief Returns a pointer to the mutable execution context. @return Pointer to @ref + /// context. + context* get_ctx() const { + return &ctx_; + } + + private: + PVM_FORCEINLINE status dispatch(context* ctx, const insn_view& insn, const insn_stream_rt& insn_entries) const; + + architecture arch_; + mutable context ctx_; + }; + + template interpreter::status interpreter::run(const bytecode& cb) const { + const std::size_t size = cb.size(); + const std::uint8_t* pc = cb.bytes; + const std::uint8_t* end = cb.bytes + size; + + auto decoder = make_bytecode_decoder(cb); + + while (pc < end && !ctx_.is_halted()) { + if (pc >= end) { + return status::VM_ERROR_PC_OUT_OF_BOUNDS; + } + + auto ctx_ct = ctx_; + ctx_ct.set_pc(static_cast(pc - cb.bytes)); + + auto const insn = insn_view(reinterpret_cast(pc)); + + auto const prev_pc = ctx_ct.get_pc(); - ctx_ = ctx_ct; + if (const auto status = dispatch(&ctx_ct, insn, decoder.get_instruction_stream()); + status != status::VM_SUCCESS) { + return status; + } - if (auto const curr_pc = ctx_ct.get_pc(); curr_pc != prev_pc) { - if (curr_pc >= size) { - return status::VM_ERROR_PC_OUT_OF_BOUNDS; + if (ctx_ct.is_halted()) { + return status::VM_SUCCESS; } - pc = cb.bytes + curr_pc; - } else { - pc += insn.size(); - } - } - return status::VM_SUCCESS; - } - - inline interpreter::status interpreter::dispatch( - context *ctx, - const insn_view& insn, - const insn_stream_rt& insn_entries - ) const { - if (!insn.valid()) { - return status::VM_ERROR_INVALID_INSTRUCTION; - } - - // We use if/else for one simple reason: switch requires an integral constant expression, - // but since arch_ is a class field, it is accessed through a runtime object (this), which by definition cannot be a constant expression. - // Building a { opcode - dispatcher } table would likely introduce overhead for 21 opcodes and negatively affect performance. - // The decision regarding a table-based approach may be revisited in the future after further benchmarking. - - if (auto const opc = insn.opcode(); opc == arch_.ops.MOV) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.XOR) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.ADD) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.SUB) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.SHL) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.SHR) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.AND) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.OR) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.NOT) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.ROL) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.ROR) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.CMP) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.JMP) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.JMPA) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.JMPI) { - insn_dispatch::execute(ctx, insn, insn_entries); - } else if (opc == arch_.ops.JNE) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.JNEI) { - insn_dispatch::execute(ctx, insn, insn_entries); - } else if (opc == arch_.ops.JE) { - insn_dispatch::execute(ctx, insn); - } else if (opc == arch_.ops.JEI) { - insn_dispatch::execute(ctx, insn, insn_entries); - } else if (opc == arch_.ops.NOP) { - insn_dispatch::execute(); - } else if (opc == arch_.ops.HALT) { - insn_dispatch::execute(ctx); - return status::VM_SUCCESS; - } else { - return status::VM_ERROR_UNKNOWN_OPCODE; - } - return status::VM_SUCCESS; - } -} - -#endif //NGU_PVM_ENGINE_INTERPRETER_H + + ctx_ = ctx_ct; + + if (auto const curr_pc = ctx_ct.get_pc(); curr_pc != prev_pc) { + if (curr_pc >= size) { + return status::VM_ERROR_PC_OUT_OF_BOUNDS; + } + pc = cb.bytes + curr_pc; + } else { + pc += insn.size(); + } + } + return status::VM_SUCCESS; + } + + inline interpreter::status interpreter::dispatch( + context* ctx, const insn_view& insn, const insn_stream_rt& insn_entries + ) const { + if (!insn.valid()) { + return status::VM_ERROR_INVALID_INSTRUCTION; + } + + // We use if/else for one simple reason: switch requires an integral constant expression, + // but since arch_ is a class field, it is accessed through a runtime object (this), which + // by definition cannot be a constant expression. Building a { opcode - dispatcher } table + // would likely introduce overhead for 21 opcodes and negatively affect performance. The + // decision regarding a table-based approach may be revisited in the future after further + // benchmarking. + + if (auto const opc = insn.opcode(); opc == arch_.ops.MOV) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.XOR) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.ADD) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.SUB) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.SHL) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.SHR) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.AND) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.OR) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.NOT) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.ROL) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.ROR) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.CMP) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.JMP) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.JMPA) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.JMPI) { + insn_dispatch::execute(ctx, insn, insn_entries); + } else if (opc == arch_.ops.JNE) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.JNEI) { + insn_dispatch::execute(ctx, insn, insn_entries); + } else if (opc == arch_.ops.JE) { + insn_dispatch::execute(ctx, insn); + } else if (opc == arch_.ops.JEI) { + insn_dispatch::execute(ctx, insn, insn_entries); + } else if (opc == arch_.ops.NOP) { + insn_dispatch::execute(); + } else if (opc == arch_.ops.HALT) { + insn_dispatch::execute(ctx); + return status::VM_SUCCESS; + } else { + return status::VM_ERROR_UNKNOWN_OPCODE; + } + return status::VM_SUCCESS; + } +} // namespace ngu::pvm + +#endif // NGU_PVM_ENGINE_INTERPRETER_H diff --git a/include/pvm/pvm.h b/include/pvm/pvm.h index c59843e..e574b47 100644 --- a/include/pvm/pvm.h +++ b/include/pvm/pvm.h @@ -23,4 +23,4 @@ #include "codegen/macro_assembler.h" #include "codegen/codegen.h" -#endif //NGU_PVM_H +#endif // NGU_PVM_H diff --git a/include/pvm/utilities/platform.h b/include/pvm/utilities/platform.h index 08d0084..9ed0f63 100644 --- a/include/pvm/utilities/platform.h +++ b/include/pvm/utilities/platform.h @@ -24,4 +24,4 @@ #define PVM_FORCEINLINE __forceinline #endif -#endif //NGU_PVM_UTILITIES_PLATFORM_H \ No newline at end of file +#endif // NGU_PVM_UTILITIES_PLATFORM_H \ No newline at end of file diff --git a/include/pvm/utilities/utilities.h b/include/pvm/utilities/utilities.h index 6b295c3..f955f74 100644 --- a/include/pvm/utilities/utilities.h +++ b/include/pvm/utilities/utilities.h @@ -44,8 +44,9 @@ namespace ngu::pvm { } template constexpr void for_each(Fn&& fn) const { - for (std::size_t i{}; i < size(); ++i) + for (std::size_t i{}; i < size(); ++i) { fn((*this)[i]); + } } protected: @@ -65,12 +66,12 @@ namespace ngu::pvm { */ template struct mapping_entry { constexpr mapping_entry() = default; - consteval mapping_entry(const From from, const To to) : - from(from), to(to) {} + consteval mapping_entry(const From from, const To to) : from(from), to(to) { + } From from{}; To to{}; }; -} +} // namespace ngu::pvm -#endif //NGU_PVM_UTILITIES_UTILITIES_H +#endif // NGU_PVM_UTILITIES_UTILITIES_H diff --git a/tests/assets/crypto.h b/tests/assets/crypto.h index 44eaf27..e71312a 100644 --- a/tests/assets/crypto.h +++ b/tests/assets/crypto.h @@ -24,9 +24,9 @@ #include namespace ngu::pvm::crypto { - constexpr std::uint32_t XTEA_DELTA{ 0x9E3779B9 }; - constexpr std::uint32_t XTEA_ROUNDS{ 32 }; - constexpr std::uint32_t SUM_INIT{ XTEA_DELTA * XTEA_ROUNDS }; + constexpr std::uint32_t XTEA_DELTA{0x9E3779B9}; + constexpr std::uint32_t XTEA_ROUNDS{32}; + constexpr std::uint32_t SUM_INIT{XTEA_DELTA * XTEA_ROUNDS}; namespace detail { struct rc4_state { @@ -53,7 +53,7 @@ namespace ngu::pvm::crypto { int j{}; for (int i{}; i < 256; i++) { j = (j + state.S[i] + key[i % keylen]) & 0xFF; - std::uint8_t tmp{ state.S[i] }; + std::uint8_t tmp{state.S[i]}; state.S[i] = state.S[j]; state.S[j] = tmp; } @@ -67,15 +67,17 @@ namespace ngu::pvm::crypto { state.i = (state.i + 1) & 0xFF; state.j = (state.j + state.S[state.i]) & 0xFF; - std::uint8_t tmp{ state.S[state.i] }; + std::uint8_t tmp{state.S[state.i]}; state.S[state.i] = state.S[state.j]; state.S[state.j] = tmp; return state.S[(state.S[state.i] + state.S[state.j]) & 0xFF]; } - } + } // namespace detail - constexpr std::pair xtea_encrypt(std::uint32_t v0, std::uint32_t v1, const std::uint32_t key[4]) { + constexpr std::pair xtea_encrypt( + std::uint32_t v0, std::uint32_t v1, const std::uint32_t key[4] + ) { std::uint32_t sum{}; for (int i{}; i < static_cast(XTEA_ROUNDS); i++) { v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); @@ -85,8 +87,10 @@ namespace ngu::pvm::crypto { return {v0, v1}; } - constexpr std::pair xtea_decrypt(std::uint32_t v0, std::uint32_t v1, const std::uint32_t key[4]) { - std::uint32_t sum{ XTEA_DELTA * XTEA_ROUNDS }; + constexpr std::pair xtea_decrypt( + std::uint32_t v0, std::uint32_t v1, const std::uint32_t key[4] + ) { + std::uint32_t sum{XTEA_DELTA * XTEA_ROUNDS}; for (int i{}; i < static_cast(XTEA_ROUNDS); i++) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum -= XTEA_DELTA; @@ -105,10 +109,18 @@ namespace ngu::pvm::crypto { } constexpr void chacha20_quarter_round(std::uint32_t& a, std::uint32_t& b, std::uint32_t& c, std::uint32_t& d) { - a += b; d ^= a; d = detail::rotl32(d, 16); - c += d; b ^= c; b = detail::rotl32(b, 12); - a += b; d ^= a; d = detail::rotl32(d, 8); - c += d; b ^= c; b = detail::rotl32(b, 7); + a += b; + d ^= a; + d = detail::rotl32(d, 16); + c += d; + b ^= c; + b = detail::rotl32(b, 12); + a += b; + d ^= a; + d = detail::rotl32(d, 8); + c += d; + b ^= c; + b = detail::rotl32(b, 7); } constexpr std::tuple chacha20_qr( @@ -117,6 +129,6 @@ namespace ngu::pvm::crypto { chacha20_quarter_round(a, b, c, d); return {a, b, c, d}; } -} +} // namespace ngu::pvm::crypto -#endif //NGU_PVM_TESTS_ASSETS_CRYPTO_H +#endif // NGU_PVM_TESTS_ASSETS_CRYPTO_H diff --git a/tests/benchmark/benchmark.h b/tests/benchmark/benchmark.h index 51b09f0..140c7c6 100644 --- a/tests/benchmark/benchmark.h +++ b/tests/benchmark/benchmark.h @@ -30,14 +30,14 @@ using namespace ngu::pvm; class VirtualMachineBenchmark : public benchmark::Fixture { protected: static constexpr auto arch = architecture::make(1); - static constexpr auto cr = assembler(arch); + static constexpr auto cr = assembler(arch); }; BENCHMARK_F(VirtualMachineBenchmark, XteaDecrypt_Native)(benchmark::State& state) { - static constexpr std::uint32_t KEY[4] = { 0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF }; + static constexpr std::uint32_t KEY[4] = {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}; static constexpr std::uint32_t PLAIN_V0 = 0x01234567; static constexpr std::uint32_t PLAIN_V1 = 0x89ABCDEF; - static constexpr auto cipher = crypto::xtea_encrypt(PLAIN_V0, PLAIN_V1, KEY); + static constexpr auto cipher = crypto::xtea_encrypt(PLAIN_V0, PLAIN_V1, KEY); for (auto _ : state) { auto [v0, v1] = crypto::xtea_decrypt(cipher.first, cipher.second, KEY); @@ -47,24 +47,25 @@ BENCHMARK_F(VirtualMachineBenchmark, XteaDecrypt_Native)(benchmark::State& state } BENCHMARK_F(VirtualMachineBenchmark, XteaDecrypt_VM)(benchmark::State& state) { - static constexpr std::uint32_t KEY[4] = { 0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF }; + static constexpr std::uint32_t KEY[4] = {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}; static constexpr std::uint32_t PLAIN_V0 = 0x01234567; static constexpr std::uint32_t PLAIN_V1 = 0x89ABCDEF; - static constexpr auto cipher = crypto::xtea_encrypt(PLAIN_V0, PLAIN_V1, KEY); + static constexpr auto cipher = crypto::xtea_encrypt(PLAIN_V0, PLAIN_V1, KEY); - static constexpr std::uint64_t LOOP_START = 1; + static constexpr std::uint64_t LOOP_START = 1; static constexpr std::uint64_t SWITCH1_KEY1 = 2; static constexpr std::uint64_t SWITCH1_KEY2 = 3; static constexpr std::uint64_t SWITCH1_KEY3 = 4; - static constexpr std::uint64_t SWITCH1_END = 5; + static constexpr std::uint64_t SWITCH1_END = 5; static constexpr std::uint64_t SWITCH2_KEY1 = 6; static constexpr std::uint64_t SWITCH2_KEY2 = 7; static constexpr std::uint64_t SWITCH2_KEY3 = 8; - static constexpr std::uint64_t SWITCH2_END = 9; + static constexpr std::uint64_t SWITCH2_END = 9; // R1=v0(cipher), R2=v1(cipher), R3=sum, R4=delta, R5=rounds_counter // R6/R7/R8 = scratch - static constexpr auto code = PVM_ASSEMBLE(arch, + static constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(cipher.first)), cr.MOV(arch::reg::REG_R2, operand(cipher.second)), cr.MOV(arch::reg::REG_R3, operand(crypto::SUM_INIT)), @@ -166,14 +167,14 @@ BENCHMARK_F(VirtualMachineBenchmark, XteaDecrypt_VM)(benchmark::State& state) { } BENCHMARK_F(VirtualMachineBenchmark, Rc4KeystreamXor_Native)(benchmark::State& state) { - static constexpr std::uint8_t KEY[] = { 'K', 'e', 'y' }; - static constexpr std::size_t KETLEN = sizeof KEY; + static constexpr std::uint8_t KEY[] = {'K', 'e', 'y'}; + static constexpr std::size_t KETLEN = sizeof KEY; static constexpr std::uint8_t PLAINTEXT_BYTE = 'P'; for (auto _ : state) { - constexpr auto keystream = crypto::rc4_keystream_byte(KEY, KETLEN); - std::uint8_t cipher = PLAINTEXT_BYTE ^ keystream; - std::uint8_t decrypted = cipher ^ keystream; + constexpr auto keystream = crypto::rc4_keystream_byte(KEY, KETLEN); + std::uint8_t cipher = PLAINTEXT_BYTE ^ keystream; + std::uint8_t decrypted = cipher ^ keystream; benchmark::DoNotOptimize(cipher); benchmark::DoNotOptimize(decrypted); benchmark::ClobberMemory(); @@ -181,12 +182,13 @@ BENCHMARK_F(VirtualMachineBenchmark, Rc4KeystreamXor_Native)(benchmark::State& s } BENCHMARK_F(VirtualMachineBenchmark, Rc4KeystreamXor_VM)(benchmark::State& state) { - static constexpr std::uint8_t KEY[] = { 'K', 'e', 'y' }; - static constexpr std::size_t KETLEN = sizeof KEY; - static constexpr std::uint8_t PLAINTEXT_BYTE = 'P'; + static constexpr std::uint8_t KEY[] = {'K', 'e', 'y'}; + static constexpr std::size_t KETLEN = sizeof KEY; + static constexpr std::uint8_t PLAINTEXT_BYTE = 'P'; static constexpr std::uint8_t EXPECTED_KEYSTREAM = crypto::rc4_keystream_byte(KEY, KETLEN); - static constexpr auto code = PVM_ASSEMBLE(arch, + static constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(static_cast(PLAINTEXT_BYTE))), cr.MOV(arch::reg::REG_R2, operand(static_cast(EXPECTED_KEYSTREAM))), @@ -229,7 +231,10 @@ BENCHMARK_F(VirtualMachineBenchmark, ChaCha20QuarterRound_Native)(benchmark::Sta benchmark::DoNotOptimize(b); benchmark::DoNotOptimize(c); benchmark::DoNotOptimize(d); - a = INPUT_A; b = INPUT_B; c = INPUT_C; d = INPUT_D; + a = INPUT_A; + b = INPUT_B; + c = INPUT_C; + d = INPUT_D; } } @@ -240,7 +245,8 @@ BENCHMARK_F(VirtualMachineBenchmark, ChaCha20QuarterRound_VM)(benchmark::State& static constexpr std::uint32_t INPUT_D = 0x01234567u; // R1=a, R2=b, R3=c, R4=d, R5/R6=scratch for rotl32 - static constexpr auto code = PVM_ASSEMBLE(arch, + static constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(INPUT_A)), cr.MOV(arch::reg::REG_R2, operand(INPUT_B)), cr.MOV(arch::reg::REG_R3, operand(INPUT_C)), @@ -311,4 +317,4 @@ BENCHMARK_F(VirtualMachineBenchmark, ChaCha20QuarterRound_VM)(benchmark::State& } } -#endif //NGU_PVM_TESTS_BENCHMARK_H +#endif // NGU_PVM_TESTS_BENCHMARK_H diff --git a/tests/unittest/unittest_crypto.h b/tests/unittest/unittest_crypto.h index 8a4f341..f1e4aea 100644 --- a/tests/unittest/unittest_crypto.h +++ b/tests/unittest/unittest_crypto.h @@ -32,19 +32,20 @@ TEST_F(VirtualMachineTest, XteaDecryptRoundTrip) { static constexpr auto cipher = crypto::xtea_encrypt(PLAIN_V0, PLAIN_V1, KEY); - constexpr std::uint64_t LOOP_START = 1; + constexpr std::uint64_t LOOP_START = 1; constexpr std::uint64_t SWITCH1_KEY1 = 2; constexpr std::uint64_t SWITCH1_KEY2 = 3; constexpr std::uint64_t SWITCH1_KEY3 = 4; - constexpr std::uint64_t SWITCH1_END = 5; + constexpr std::uint64_t SWITCH1_END = 5; constexpr std::uint64_t SWITCH2_KEY1 = 6; constexpr std::uint64_t SWITCH2_KEY2 = 7; constexpr std::uint64_t SWITCH2_KEY3 = 8; - constexpr std::uint64_t SWITCH2_END = 9; + constexpr std::uint64_t SWITCH2_END = 9; // R1=v0(cipher), R2=v1(cipher), R3=sum, R4=delta, R5=rounds_counter // R6/R7/R8 = scratch - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(cipher.first)), cr.MOV(arch::reg::REG_R2, operand(cipher.second)), cr.MOV(arch::reg::REG_R3, operand(crypto::SUM_INIT)), @@ -160,15 +161,16 @@ TEST_F(VirtualMachineTest, XteaDecryptRoundTrip) { // The keystream byte is pre-computed at compile time by the reference // implementation so the test stays self-contained and easy to read. TEST_F(VirtualMachineTest, Rc4KeystreamXor) { - constexpr std::uint8_t KEY[] = {'K', 'e', 'y'}; - constexpr std::size_t KETLEN = sizeof KEY; - constexpr std::uint8_t PLAIN = 'P'; - constexpr std::uint8_t KEYSTREAM = crypto::rc4_keystream_byte(KEY, KETLEN); + constexpr std::uint8_t KEY[] = {'K', 'e', 'y'}; + constexpr std::size_t KETLEN = sizeof KEY; + constexpr std::uint8_t PLAIN = 'P'; + constexpr std::uint8_t KEYSTREAM = crypto::rc4_keystream_byte(KEY, KETLEN); constexpr std::uint8_t EXPECTED_CIPHER = crypto::rc4_process(KEY, KETLEN, PLAIN); static_assert(EXPECTED_CIPHER == (PLAIN ^ KEYSTREAM)); - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(static_cast(PLAIN))), cr.MOV(arch::reg::REG_R2, operand(static_cast(KEYSTREAM))), @@ -208,7 +210,8 @@ TEST_F(VirtualMachineTest, ChaCha20QuarterRound) { constexpr std::uint32_t EXPECTED_D = std::get<3>(expected); // R1=a, R2=b, R3=c, R4=d, R5/R6=scratch for rotl32 - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(INPUT_A)), cr.MOV(arch::reg::REG_R2, operand(INPUT_B)), cr.MOV(arch::reg::REG_R3, operand(INPUT_C)), @@ -279,4 +282,4 @@ TEST_F(VirtualMachineTest, ChaCha20QuarterRound) { EXPECT_EQ(static_cast(test_vm.get_ctx()->get_reg(arch::reg::REG_R4)), EXPECTED_D); } -#endif //NGU_PVM_TESTS_UNITTEST_CRYPTO_H +#endif // NGU_PVM_TESTS_UNITTEST_CRYPTO_H diff --git a/tests/unittest/unittest_fixture.h b/tests/unittest/unittest_fixture.h index 9a0baae..65e801b 100644 --- a/tests/unittest/unittest_fixture.h +++ b/tests/unittest/unittest_fixture.h @@ -29,7 +29,7 @@ using namespace ngu::pvm; class VirtualMachineTest : public ::testing::Test { protected: static constexpr auto arch = architecture::make(1); - static constexpr auto cr = macro_assembler(arch); + static constexpr auto cr = macro_assembler(arch); }; -#endif //NGU_PVM_TESTS_UNITTEST_FIXTURE_H +#endif // NGU_PVM_TESTS_UNITTEST_FIXTURE_H diff --git a/tests/unittest/unittest_macro.h b/tests/unittest/unittest_macro.h index 45902e0..4329528 100644 --- a/tests/unittest/unittest_macro.h +++ b/tests/unittest/unittest_macro.h @@ -21,11 +21,8 @@ #include "unittest_fixture.h" TEST_F(VirtualMachineTest, MacroZero) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0xDEADBEEFu)), - cr.ZERO(arch::reg::REG_R1), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(0xDEADBEEFu)), cr.ZERO(arch::reg::REG_R1), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -34,11 +31,8 @@ TEST_F(VirtualMachineTest, MacroZero) { } TEST_F(VirtualMachineTest, MacroNeg) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(5u)), - cr.NEG(arch::reg::REG_R1), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.NEG(arch::reg::REG_R1), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -47,11 +41,8 @@ TEST_F(VirtualMachineTest, MacroNeg) { } TEST_F(VirtualMachineTest, MacroNegZero) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0u)), - cr.NEG(arch::reg::REG_R1), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(0u)), cr.NEG(arch::reg::REG_R1), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -60,7 +51,8 @@ TEST_F(VirtualMachineTest, MacroNegZero) { } TEST_F(VirtualMachineTest, MacroMovc) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xABCDu)), cr.MOV(arch::reg::REG_R2, operand(0xFFFFu)), cr.MOVC(arch::reg::REG_R2, arch::reg::REG_R1), @@ -80,7 +72,8 @@ TEST_F(VirtualMachineTest, MacroMov64) { constexpr std::uint64_t EXPECTED = (static_cast(HI) << 32) | LO; // MOV64(dst, hi, lo) expands to: MOV(dst, hi); SHL(dst, 32); OR(dst, lo) - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(HI)), cr.SHL(arch::reg::REG_R1, operand(32u)), cr.OR(arch::reg::REG_R1, operand(LO)), @@ -94,7 +87,8 @@ TEST_F(VirtualMachineTest, MacroMov64) { } TEST_F(VirtualMachineTest, MacroSwap) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xAAAAu)), cr.MOV(arch::reg::REG_R2, operand(0x5555u)), cr.SWAP(arch::reg::REG_R1, arch::reg::REG_R2), @@ -110,9 +104,10 @@ TEST_F(VirtualMachineTest, MacroSwap) { TEST_F(VirtualMachineTest, MacroLoop) { // Counts from 5 down to 0, adding 1 to an accumulator each iteration. - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0u)), // accumulator - cr.MOV(arch::reg::REG_R2, operand(5u)), // loop counter + constexpr auto code = PVM_ASSEMBLE( + arch, + cr.MOV(arch::reg::REG_R1, operand(0u)), // accumulator + cr.MOV(arch::reg::REG_R2, operand(5u)), // loop counter cr.LABEL(1), cr.ADD(arch::reg::REG_R1, operand(1u)), cr.LOOP(arch::reg::REG_R2, 1), @@ -127,9 +122,10 @@ TEST_F(VirtualMachineTest, MacroLoop) { } TEST_F(VirtualMachineTest, MacroMulPow2) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(7u)), - cr.MUL_POW2(arch::reg::REG_R1, 3), // 7 * 2^3 = 56 + cr.MUL_POW2(arch::reg::REG_R1, 3), // 7 * 2^3 = 56 cr.HALT() ); @@ -140,9 +136,10 @@ TEST_F(VirtualMachineTest, MacroMulPow2) { } TEST_F(VirtualMachineTest, MacroDivPow2) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(64u)), - cr.DIV_POW2(arch::reg::REG_R1, 3), // 64 / 2^3 = 8 + cr.DIV_POW2(arch::reg::REG_R1, 3), // 64 / 2^3 = 8 cr.HALT() ); @@ -153,9 +150,10 @@ TEST_F(VirtualMachineTest, MacroDivPow2) { } TEST_F(VirtualMachineTest, MacroMaskLo) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xABCDEF01u)), - cr.MASK_LO(arch::reg::REG_R1, 8), // keep low 8 bits → 0x01 + cr.MASK_LO(arch::reg::REG_R1, 8), // keep low 8 bits → 0x01 cr.HALT() ); @@ -166,10 +164,8 @@ TEST_F(VirtualMachineTest, MacroMaskLo) { } TEST_F(VirtualMachineTest, MacroAbsPositive) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(42u)), - cr.ABS(arch::reg::REG_R1, arch::reg::REG_R2, 1), - cr.HALT() + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(42u)), cr.ABS(arch::reg::REG_R1, arch::reg::REG_R2, 1), cr.HALT() ); auto test_vm = interpreter(arch); @@ -180,7 +176,8 @@ TEST_F(VirtualMachineTest, MacroAbsPositive) { TEST_F(VirtualMachineTest, MacroAbsNegative) { // Pass as uint64 to force DWORD_Q encoding: immediate is zero-extended on decode, // so signed-compressed encoding (-7 → 0xF9) would read back as 249, not -7. - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(static_cast(-7LL))), cr.ABS(arch::reg::REG_R1, arch::reg::REG_R2, 1), cr.HALT() @@ -192,10 +189,8 @@ TEST_F(VirtualMachineTest, MacroAbsNegative) { } TEST_F(VirtualMachineTest, MacroAbsZero) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0u)), - cr.ABS(arch::reg::REG_R1, arch::reg::REG_R2, 1), - cr.HALT() + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0u)), cr.ABS(arch::reg::REG_R1, arch::reg::REG_R2, 1), cr.HALT() ); auto test_vm = interpreter(arch); @@ -204,7 +199,8 @@ TEST_F(VirtualMachineTest, MacroAbsZero) { } TEST_F(VirtualMachineTest, MacroMinALessB) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(3u)), cr.MOV(arch::reg::REG_R2, operand(7u)), cr.MIN(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -217,7 +213,8 @@ TEST_F(VirtualMachineTest, MacroMinALessB) { } TEST_F(VirtualMachineTest, MacroMinAGreaterB) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(9u)), cr.MOV(arch::reg::REG_R2, operand(4u)), cr.MIN(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -230,7 +227,8 @@ TEST_F(VirtualMachineTest, MacroMinAGreaterB) { } TEST_F(VirtualMachineTest, MacroMinEqual) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.MOV(arch::reg::REG_R2, operand(5u)), cr.MIN(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -243,7 +241,8 @@ TEST_F(VirtualMachineTest, MacroMinEqual) { } TEST_F(VirtualMachineTest, MacroMaxAGreaterB) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(9u)), cr.MOV(arch::reg::REG_R2, operand(4u)), cr.MAX(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -256,7 +255,8 @@ TEST_F(VirtualMachineTest, MacroMaxAGreaterB) { } TEST_F(VirtualMachineTest, MacroMaxALessB) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(3u)), cr.MOV(arch::reg::REG_R2, operand(7u)), cr.MAX(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -269,7 +269,8 @@ TEST_F(VirtualMachineTest, MacroMaxALessB) { } TEST_F(VirtualMachineTest, MacroMaxEqual) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.MOV(arch::reg::REG_R2, operand(5u)), cr.MAX(arch::reg::REG_R1, arch::reg::REG_R2, arch::reg::REG_R3, 1), @@ -281,4 +282,4 @@ TEST_F(VirtualMachineTest, MacroMaxEqual) { EXPECT_EQ(test_vm.get_ctx()->get_reg(arch::reg::REG_R1), 5u); } -#endif //NGU_PVM_TESTS_UNITTEST_MACRO_H +#endif // NGU_PVM_TESTS_UNITTEST_MACRO_H diff --git a/tests/unittest/unittest_primitive.h b/tests/unittest/unittest_primitive.h index 42ab12f..38143d8 100644 --- a/tests/unittest/unittest_primitive.h +++ b/tests/unittest/unittest_primitive.h @@ -33,10 +33,7 @@ TEST_F(VirtualMachineTest, Initialization) { } TEST_F(VirtualMachineTest, MovImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(42u)), - cr.HALT() - ); + constexpr auto code = PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(42u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -45,7 +42,8 @@ TEST_F(VirtualMachineTest, MovImmediate) { } TEST_F(VirtualMachineTest, MovRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xBEEFu)), cr.MOV(arch::reg::REG_R2, operand(arch::reg::REG_R1)), cr.HALT() @@ -59,10 +57,8 @@ TEST_F(VirtualMachineTest, MovRegister) { } TEST_F(VirtualMachineTest, BoundaryValues) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0u)), - cr.MOV(arch::reg::REG_R2, operand(UINT64_MAX)), - cr.HALT() + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0u)), cr.MOV(arch::reg::REG_R2, operand(UINT64_MAX)), cr.HALT() ); auto test_vm = interpreter(arch); @@ -73,7 +69,8 @@ TEST_F(VirtualMachineTest, BoundaryValues) { } TEST_F(VirtualMachineTest, AllRegistersIndependent) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(1u)), cr.MOV(arch::reg::REG_R2, operand(2u)), cr.MOV(arch::reg::REG_R3, operand(3u)), @@ -99,11 +96,8 @@ TEST_F(VirtualMachineTest, AllRegistersIndependent) { } TEST_F(VirtualMachineTest, AddImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(10u)), - cr.ADD(arch::reg::REG_R1, operand(5u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.ADD(arch::reg::REG_R1, operand(5u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -112,7 +106,8 @@ TEST_F(VirtualMachineTest, AddImmediate) { } TEST_F(VirtualMachineTest, AddRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.MOV(arch::reg::REG_R2, operand(5u)), cr.ADD(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -127,10 +122,8 @@ TEST_F(VirtualMachineTest, AddRegister) { } TEST_F(VirtualMachineTest, AddOverflow) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(UINT64_MAX - 5)), - cr.ADD(arch::reg::REG_R1, operand(10u)), - cr.HALT() + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(UINT64_MAX - 5)), cr.ADD(arch::reg::REG_R1, operand(10u)), cr.HALT() ); auto test_vm = interpreter(arch); @@ -140,11 +133,8 @@ TEST_F(VirtualMachineTest, AddOverflow) { } TEST_F(VirtualMachineTest, SubImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(20u)), - cr.SUB(arch::reg::REG_R1, operand(7u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(20u)), cr.SUB(arch::reg::REG_R1, operand(7u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -153,7 +143,8 @@ TEST_F(VirtualMachineTest, SubImmediate) { } TEST_F(VirtualMachineTest, SubRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(20u)), cr.MOV(arch::reg::REG_R2, operand(7u)), cr.SUB(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -168,11 +159,8 @@ TEST_F(VirtualMachineTest, SubRegister) { } TEST_F(VirtualMachineTest, SubUnderflow) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(5u)), - cr.SUB(arch::reg::REG_R1, operand(10u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.SUB(arch::reg::REG_R1, operand(10u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -181,7 +169,8 @@ TEST_F(VirtualMachineTest, SubUnderflow) { } TEST_F(VirtualMachineTest, AndImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11110000u)), cr.AND(arch::reg::REG_R1, operand(0b10101010u)), cr.HALT() @@ -194,7 +183,8 @@ TEST_F(VirtualMachineTest, AndImmediate) { } TEST_F(VirtualMachineTest, AndRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xFFu)), cr.MOV(arch::reg::REG_R2, operand(0x0Fu)), cr.AND(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -209,10 +199,8 @@ TEST_F(VirtualMachineTest, AndRegister) { } TEST_F(VirtualMachineTest, OrImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0b11110000u)), - cr.OR(arch::reg::REG_R1, operand(0b10101010u)), - cr.HALT() + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11110000u)), cr.OR(arch::reg::REG_R1, operand(0b10101010u)), cr.HALT() ); auto test_vm = interpreter(arch); @@ -222,7 +210,8 @@ TEST_F(VirtualMachineTest, OrImmediate) { } TEST_F(VirtualMachineTest, OrRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11110000u)), cr.MOV(arch::reg::REG_R2, operand(0b00001111u)), cr.OR(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -237,7 +226,8 @@ TEST_F(VirtualMachineTest, OrRegister) { } TEST_F(VirtualMachineTest, XorImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11110000u)), cr.XOR(arch::reg::REG_R1, operand(0b10101010u)), cr.HALT() @@ -250,7 +240,8 @@ TEST_F(VirtualMachineTest, XorImmediate) { } TEST_F(VirtualMachineTest, XorRegister) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11001100u)), cr.MOV(arch::reg::REG_R2, operand(0b10101010u)), cr.XOR(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -265,7 +256,8 @@ TEST_F(VirtualMachineTest, XorRegister) { } TEST_F(VirtualMachineTest, XorSelf) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xFFu)), cr.XOR(arch::reg::REG_R1, operand(arch::reg::REG_R1)), cr.HALT() @@ -278,11 +270,8 @@ TEST_F(VirtualMachineTest, XorSelf) { } TEST_F(VirtualMachineTest, NotImmediate) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0b10101010u)), - cr.NOT(arch::reg::REG_R1), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(0b10101010u)), cr.NOT(arch::reg::REG_R1), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -291,11 +280,8 @@ TEST_F(VirtualMachineTest, NotImmediate) { } TEST_F(VirtualMachineTest, NotZero) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(0u)), - cr.NOT(arch::reg::REG_R1), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(0u)), cr.NOT(arch::reg::REG_R1), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -304,11 +290,8 @@ TEST_F(VirtualMachineTest, NotZero) { } TEST_F(VirtualMachineTest, ShiftLeft) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(5u)), - cr.SHL(arch::reg::REG_R1, operand(2u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.SHL(arch::reg::REG_R1, operand(2u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -317,11 +300,8 @@ TEST_F(VirtualMachineTest, ShiftLeft) { } TEST_F(VirtualMachineTest, ShiftLeftOverflow) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(1u)), - cr.SHL(arch::reg::REG_R1, operand(63u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(1u)), cr.SHL(arch::reg::REG_R1, operand(63u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -330,11 +310,8 @@ TEST_F(VirtualMachineTest, ShiftLeftOverflow) { } TEST_F(VirtualMachineTest, ShiftRight) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(20u)), - cr.SHR(arch::reg::REG_R1, operand(2u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(20u)), cr.SHR(arch::reg::REG_R1, operand(2u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -343,7 +320,8 @@ TEST_F(VirtualMachineTest, ShiftRight) { } TEST_F(VirtualMachineTest, ShiftRightBoundary) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0x8000000000000000ULL)), cr.SHR(arch::reg::REG_R1, operand(63u)), cr.HALT() @@ -356,7 +334,8 @@ TEST_F(VirtualMachineTest, ShiftRightBoundary) { } TEST_F(VirtualMachineTest, RotateLeft) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0x8000000000000001ULL)), cr.ROL(arch::reg::REG_R1, operand(1u)), cr.HALT() @@ -369,7 +348,8 @@ TEST_F(VirtualMachineTest, RotateLeft) { } TEST_F(VirtualMachineTest, RotateRight) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0x8000000000000001ULL)), cr.ROR(arch::reg::REG_R1, operand(1u)), cr.HALT() @@ -382,11 +362,8 @@ TEST_F(VirtualMachineTest, RotateRight) { } TEST_F(VirtualMachineTest, CompareEqual) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(42u)), - cr.CMP(arch::reg::REG_R1, operand(42u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(42u)), cr.CMP(arch::reg::REG_R1, operand(42u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -397,11 +374,8 @@ TEST_F(VirtualMachineTest, CompareEqual) { } TEST_F(VirtualMachineTest, CompareNotEqual) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(42u)), - cr.CMP(arch::reg::REG_R1, operand(24u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(42u)), cr.CMP(arch::reg::REG_R1, operand(24u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -410,11 +384,8 @@ TEST_F(VirtualMachineTest, CompareNotEqual) { } TEST_F(VirtualMachineTest, CompareLess) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(10u)), - cr.CMP(arch::reg::REG_R1, operand(20u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.CMP(arch::reg::REG_R1, operand(20u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -426,11 +397,8 @@ TEST_F(VirtualMachineTest, CompareLess) { } TEST_F(VirtualMachineTest, CompareGreater) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(20u)), - cr.CMP(arch::reg::REG_R1, operand(10u)), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(20u)), cr.CMP(arch::reg::REG_R1, operand(10u)), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -441,7 +409,8 @@ TEST_F(VirtualMachineTest, CompareGreater) { } TEST_F(VirtualMachineTest, JumpUnconditional) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.JMPL(1), cr.MOV(arch::reg::REG_R2, operand(99u)), @@ -457,7 +426,8 @@ TEST_F(VirtualMachineTest, JumpUnconditional) { } TEST_F(VirtualMachineTest, JumpIndexedUnconditional) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.MOV(arch::reg::REG_R2, operand(10u)), cr.JMPI(operand(18u)), @@ -487,7 +457,8 @@ TEST_F(VirtualMachineTest, JumpIndexedUnconditional) { } TEST_F(VirtualMachineTest, JumpLabel) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R2, operand(100u)), cr.JMPL(1), cr.MOV(arch::reg::REG_R2, operand(101u)), @@ -509,7 +480,8 @@ TEST_F(VirtualMachineTest, JumpLabel) { } TEST_F(VirtualMachineTest, JumpIfEqualTaken) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.CMP(arch::reg::REG_R1, operand(5u)), cr.JEL(1), @@ -525,7 +497,8 @@ TEST_F(VirtualMachineTest, JumpIfEqualTaken) { } TEST_F(VirtualMachineTest, JumpIfEqualNotTaken) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.CMP(arch::reg::REG_R1, operand(10u)), cr.JEL(1), @@ -541,7 +514,8 @@ TEST_F(VirtualMachineTest, JumpIfEqualNotTaken) { } TEST_F(VirtualMachineTest, JumpIfNotEqualTaken) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.CMP(arch::reg::REG_R1, operand(10u)), cr.JNEL(1), @@ -557,7 +531,8 @@ TEST_F(VirtualMachineTest, JumpIfNotEqualTaken) { } TEST_F(VirtualMachineTest, JumpIfNotEqualNotTaken) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(5u)), cr.CMP(arch::reg::REG_R1, operand(5u)), cr.JNEL(1), @@ -573,13 +548,8 @@ TEST_F(VirtualMachineTest, JumpIfNotEqualNotTaken) { } TEST_F(VirtualMachineTest, NopInstruction) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(42u)), - cr.NOP(), - cr.NOP(), - cr.NOP(), - cr.HALT() - ); + constexpr auto code = + PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(42u)), cr.NOP(), cr.NOP(), cr.NOP(), cr.HALT()); auto test_vm = interpreter(arch); ASSERT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); @@ -588,17 +558,15 @@ TEST_F(VirtualMachineTest, NopInstruction) { } TEST_F(VirtualMachineTest, HaltStatus) { - constexpr auto code = PVM_ASSEMBLE(arch, - cr.MOV(arch::reg::REG_R1, operand(1u)), - cr.HALT() - ); + constexpr auto code = PVM_ASSEMBLE(arch, cr.MOV(arch::reg::REG_R1, operand(1u)), cr.HALT()); auto test_vm = interpreter(arch); EXPECT_EQ(test_vm.run(code), interpreter::status::VM_SUCCESS); } TEST_F(VirtualMachineTest, ArithmeticChain) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(100u)), cr.ADD(arch::reg::REG_R1, operand(50u)), cr.SUB(arch::reg::REG_R1, operand(25u)), @@ -614,7 +582,8 @@ TEST_F(VirtualMachineTest, ArithmeticChain) { TEST_F(VirtualMachineTest, LogicalChain) { // 0xFF & 0xF0 = 0xF0, | 0x0A = 0xFA, ^ 0x55 = 0xAF - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0xFFu)), cr.AND(arch::reg::REG_R1, operand(0xF0u)), cr.OR(arch::reg::REG_R1, operand(0x0Au)), @@ -629,7 +598,8 @@ TEST_F(VirtualMachineTest, LogicalChain) { } TEST_F(VirtualMachineTest, RegisterToRegisterOperations) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(10u)), cr.MOV(arch::reg::REG_R2, operand(20u)), cr.MOV(arch::reg::REG_R3, operand(arch::reg::REG_R1)), @@ -646,7 +616,8 @@ TEST_F(VirtualMachineTest, RegisterToRegisterOperations) { } TEST_F(VirtualMachineTest, MultiRegisterOperations) { - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(1u)), cr.MOV(arch::reg::REG_R2, operand(2u)), cr.MOV(arch::reg::REG_R3, operand(3u)), @@ -665,7 +636,8 @@ TEST_F(VirtualMachineTest, MultiRegisterOperations) { TEST_F(VirtualMachineTest, BitwiseCombo) { // (0b11001100 & 0b10101010) = 0b10001000; | 0b01010101 = 0b11011101 - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(0b11001100u)), cr.MOV(arch::reg::REG_R2, operand(0b10101010u)), cr.AND(arch::reg::REG_R1, operand(arch::reg::REG_R2)), @@ -682,7 +654,8 @@ TEST_F(VirtualMachineTest, BitwiseCombo) { TEST_F(VirtualMachineTest, ShiftChain) { // 1 << 4 = 16, << 2 = 64, >> 3 = 8 - constexpr auto code = PVM_ASSEMBLE(arch, + constexpr auto code = PVM_ASSEMBLE( + arch, cr.MOV(arch::reg::REG_R1, operand(1u)), cr.SHL(arch::reg::REG_R1, operand(4u)), cr.SHL(arch::reg::REG_R1, operand(2u)), @@ -696,4 +669,4 @@ TEST_F(VirtualMachineTest, ShiftChain) { EXPECT_EQ(test_vm.get_ctx()->get_reg(arch::reg::REG_R1), 8u); } -#endif //NGU_PVM_TESTS_UNITTEST_PRIMITIVE_H +#endif // NGU_PVM_TESTS_UNITTEST_PRIMITIVE_H