Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -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
13 changes: 12 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
25 changes: 13 additions & 12 deletions include/pvm/bytecode/bytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ namespace ngu::pvm {
// - integral type → 1 byte
// - bytecode<N> → N bytes (via ::capacity())
// - raw array T[N] → N bytes
template <typename T> consteval std::size_t contribution() {
template<typename T> consteval std::size_t contribution() {
using U = std::remove_cvref_t<T>;
if constexpr (std::is_integral_v<U>)
if constexpr (std::is_integral_v<U>) {
return 1;
else if constexpr (requires { U::capacity(); })
} else if constexpr (requires { U::capacity(); }) {
return U::capacity();
else if constexpr (std::is_array_v<U>)
} else if constexpr (std::is_array_v<U>) {
return std::extent_v<U>;
}
return 0;
}
}
} // namespace detail

/**
* @brief Fixed-capacity compile-time byte buffer holding @p N bytes of bytecode.
Expand All @@ -49,15 +50,15 @@ namespace ngu::pvm {
* - @c std::uint8_t - single byte; @n
* - @c bytecode<M> - 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<std::size_t N> struct bytecode {
template <typename... Args> consteval explicit bytecode(Args&&... args) {
template<typename... Args> consteval explicit bytecode(Args&&... args) {
(append(std::forward<Args>(args)), ...);
}

Expand Down Expand Up @@ -87,7 +88,7 @@ namespace ngu::pvm {
}
}

template <std::size_t M> consteval void append(std::uint8_t const (&arr)[M]) {
template<std::size_t M> consteval void append(std::uint8_t const (&arr)[M]) {
for (std::size_t i = 0; i < M && length < N; ++i) {
bytes[length++] = arr[i];
}
Expand All @@ -100,7 +101,7 @@ namespace ngu::pvm {
}
};

template <typename... Args> bytecode(Args&&... args) -> bytecode<(detail::contribution<Args>() + ...)>;
}
template<typename... Args> bytecode(Args&&... args) -> bytecode<(detail::contribution<Args>() + ...)>;
} // namespace ngu::pvm

#endif //NGU_PVM_BYTECODE_BYTECODE_H
#endif // NGU_PVM_BYTECODE_BYTECODE_H
50 changes: 25 additions & 25 deletions include/pvm/bytecode/bytecode_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

/**
Expand Down Expand Up @@ -64,8 +65,7 @@ namespace ngu::pvm {
std::conditional_t<DecodeTime == COMPILE_TIME, extension_data_ct, empty> data_ct{};
};

template<decode_time DecodeTime>
using insn_entry = instruction_entry<DecodeTime>;
template<decode_time DecodeTime> using insn_entry = instruction_entry<DecodeTime>;

using insn_entry_rt = insn_entry<RUNTIME>;
using insn_entry_ct = insn_entry<COMPILE_TIME>;
Expand All @@ -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 {
Expand All @@ -84,14 +85,12 @@ namespace ngu::pvm {
};

/// @brief View over the decoder instruction stream. @tparam DecodeTime Decoding mode.
template<decode_time DecodeTime> class instruction_stream :
public indexed_view<instruction_stream<DecodeTime>> {
template<decode_time DecodeTime> class instruction_stream : public indexed_view<instruction_stream<DecodeTime>> {
public:
using value_type = instruction_entry<DecodeTime>;

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];
Expand All @@ -106,8 +105,7 @@ namespace ngu::pvm {
std::size_t count_;
};

template<decode_time DecodeTime>
using insn_stream = instruction_stream<DecodeTime>;
template<decode_time DecodeTime> using insn_stream = instruction_stream<DecodeTime>;

using insn_stream_rt = insn_stream<RUNTIME>;
using insn_stream_ct = insn_stream<COMPILE_TIME>;
Expand All @@ -116,9 +114,8 @@ namespace ngu::pvm {
struct label_table : public indexed_view<label_table> {
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];
Expand Down Expand Up @@ -146,9 +143,10 @@ namespace ngu::pvm {
template<decode_time DecodeTime, std::size_t N> class bytecode_decoder {
static constexpr std::size_t MAX_INSN = (N + 3) / 4; //(N + divisor - 1) / divisor
public:
constexpr explicit bytecode_decoder(const bytecode<N> &code);
constexpr explicit bytecode_decoder(const bytecode<N>& 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<DecodeTime> get_instruction_stream() const {
return insn_stream{insns_, insn_count_};
}
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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;
}
}
Expand All @@ -229,12 +228,13 @@ namespace ngu::pvm {
return bytecode_decoder<DecodeTime, N>(code);
}

template<decode_time DecodeTime, std::size_t N> constexpr bytecode_decoder<DecodeTime, N>::bytecode_decoder(const bytecode<N> &code) : code_(code) {
template<decode_time DecodeTime, std::size_t N>
constexpr bytecode_decoder<DecodeTime, N>::bytecode_decoder(const bytecode<N>& code) : code_(code) {
build_instruction_stream();
if constexpr (DecodeTime == COMPILE_TIME) {
build_label_table();
}
}
}
} // namespace ngu::pvm

#endif //NGU_PVM_BYTECODE_BYTECODE_DECODER_H
#endif // NGU_PVM_BYTECODE_BYTECODE_DECODER_H
40 changes: 22 additions & 18 deletions include/pvm/bytecode/instruction_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<arch::insn_size>(insn_size(header))
);
return detail::get_total_size(static_cast<arch::insn_size>(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;
Expand All @@ -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<std::uint32_t>(data[0]) |
(static_cast<std::uint32_t>(data[1]) << 8) |
(static_cast<std::uint32_t>(data[2]) << 16) |
(static_cast<std::uint32_t>(data[3]) << 24);
return static_cast<std::uint32_t>(data[0]) | (static_cast<std::uint32_t>(data[1]) << 8) |
(static_cast<std::uint32_t>(data[2]) << 16) | (static_cast<std::uint32_t>(data[3]) << 24);
}
};

Expand All @@ -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());
Expand All @@ -131,20 +132,23 @@ namespace ngu::pvm {
return insn_decoder::has_immediate(data());
}

std::uint64_t immediate() const {
return valid() ? insn_decoder::immediate(data(), reinterpret_cast<const std::uint8_t*>(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<const std::uint8_t*>(ip_ + 1)) : 0;
bool valid() const {
return ip_ != nullptr;
}

private:
const std::uint32_t* ip_{};
};

using insn_view = instruction_view;
}
} // namespace ngu::pvm

#endif //NGU_PVM_BYTECODE_INSTRUCTION_DECODER_H
#endif // NGU_PVM_BYTECODE_INSTRUCTION_DECODER_H
Loading
Loading