From bd0bf1deba8be3e2dc23e09667b95b9a07c6862a Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Sat, 22 Nov 2025 22:12:22 -0400 Subject: [PATCH] jit/ir: Implement IR instruction management Introduces IR instruction management, including instruction_t and instruction_list_t definitions and their implementations. It also added const-correctness to the value_t API. Signed-off-by: Ronald Caesar --- CMakeLists.txt | 2 +- src/jit/CMakeLists.txt | 1 + src/jit/ir/instruction.cpp | 163 ++++++++++++++++++++++++++++++++++ src/jit/ir/instruction.h | 175 +++++++++++++++++++++++++++++++++++++ src/jit/ir/opcode.h | 3 + src/jit/ir/type.h | 4 +- src/jit/ir/value.cpp | 32 +++---- src/jit/ir/value.h | 23 ++--- 8 files changed, 375 insertions(+), 28 deletions(-) create mode 100644 src/jit/ir/instruction.cpp create mode 100644 src/jit/ir/instruction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 85907bc..a4d5d56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,7 @@ add_subdirectory(src/targets/switch1/hardware) include(TestBigEndian) TEST_BIG_ENDIAN(WORDS_BIGENDIAN) -list(APPEND POUND_PROJECT_TARGETS common host pvm) +list(APPEND POUND_PROJECT_TARGETS common host pvm jit) foreach(TARGET ${POUND_PROJECT_TARGETS}) # Apply Endianness definitions to all our targets. if(WORDS_BIGENDIAN) diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt index 3faa39a..d94138d 100644 --- a/src/jit/CMakeLists.txt +++ b/src/jit/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(jit PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ir/type.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ir/value.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ir/opcode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ir/instruction.cpp ) target_link_libraries(jit PRIVATE common host) diff --git a/src/jit/ir/instruction.cpp b/src/jit/ir/instruction.cpp new file mode 100644 index 0000000..a65bc79 --- /dev/null +++ b/src/jit/ir/instruction.cpp @@ -0,0 +1,163 @@ +#include "instruction.h" +#include "common/passert.h" +#include + +#define LOG_MODULE "jit" +#include "common/logging.h" + +namespace pound::jit::ir { + +const value_t* +instruction_get_arg (const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t *arg = &instruction->args[arg_index]; + PVM_ASSERT(nullptr != arg); + return arg; +} + +const uint64_t +instruction_get_arg_u64(const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t* arg = instruction_get_arg(instruction, arg_index); + PVM_ASSERT(nullptr != arg); + + PVM_ASSERT(IR_TYPE_U64 == arg->type); + const uint64_t value = value_get_u64(arg); + return value; +} + +const uint32_t +instruction_get_arg_u32(const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t* arg = instruction_get_arg(instruction, arg_index); + PVM_ASSERT(nullptr != arg); + + PVM_ASSERT(IR_TYPE_U32 == arg->type); + const uint32_t value = value_get_u32(arg); + return value; +} + +const uint8_t +instruction_get_arg_u8(const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t* arg = instruction_get_arg(instruction, arg_index); + PVM_ASSERT(nullptr != arg); + + PVM_ASSERT(IR_TYPE_U8 == arg->type); + const uint8_t value = value_get_u8(arg); + return value; +} + +const bool +instruction_get_arg_u1(const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t* arg = instruction_get_arg(instruction, arg_index); + PVM_ASSERT(nullptr != arg); + + PVM_ASSERT(IR_TYPE_U1 == arg->type); + const uint8_t value = value_get_u1(arg); + return value; +} + +const pound::jit::a32_register_t +instruction_get_arg_a32_register(const instruction_t *instruction, const size_t arg_index) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(arg_index < MAX_IR_ARGS); + + const value_t* arg = instruction_get_arg(instruction, arg_index); + PVM_ASSERT(nullptr != arg); + + PVM_ASSERT(IR_TYPE_A32_REGISTER == arg->type); + const pound::jit::a32_register_t value = value_get_a32_register(arg); + return value; +} + +const type_t +instruction_get_return_type (const instruction_t *instruction) +{ + PVM_ASSERT(nullptr != instruction); + PVM_ASSERT(instruction->opcode < NUM_OPCODE); + + const decoded_opcode_t *decoded_opcode = &g_opcodes[instruction->opcode]; + PVM_ASSERT(nullptr != decoded_opcode); + + return decoded_opcode->type; +} + +const char* +instruction_get_opcode_name(const instruction_t *instruction) +{ + PVM_ASSERT(nullptr != instruction); + + const decoded_opcode_t *decoded_opcode = &g_opcodes[instruction->opcode]; + PVM_ASSERT(nullptr != decoded_opcode); + + const char *name = decoded_opcode->name; + PVM_ASSERT(nullptr != name); + + return name; +} + +void +instruction_list_append (instruction_list_t *list, instruction_t *instruction) +{ + PVM_ASSERT(nullptr != list); + PVM_ASSERT(nullptr != instruction); + + instruction->next = nullptr; + instruction->previous = list->tail; + if (nullptr != list->tail) + { + list->tail->next = instruction; + } + else + { + list->head = instruction; + } + list->tail = instruction; +} + +void +instruction_list_remove (instruction_list_t *list, instruction_t *instruction) +{ + PVM_ASSERT(nullptr != list); + PVM_ASSERT(nullptr != instruction); + + if (nullptr != instruction->previous) + { + instruction->previous->next = instruction->next; + } + else + { + list->head = instruction->next; + } + + if (nullptr != instruction->next) + { + instruction->next->previous = instruction->previous; + } + else + { + list->tail = instruction->previous; + } + + instruction->next = nullptr; + instruction->previous = nullptr; +} +} diff --git a/src/jit/ir/instruction.h b/src/jit/ir/instruction.h new file mode 100644 index 0000000..8b43770 --- /dev/null +++ b/src/jit/ir/instruction.h @@ -0,0 +1,175 @@ +#ifndef POUND_JIT_IR_INSTRUCTION_H +#define POUND_JIT_IR_INSTRUCTION_H + +#include "opcode.h" +#include "value.h" +#include + +namespace pound::jit::ir { +// Maximum number of arguments an IR instruction can have. +#define MAX_IR_ARGS 4 + +/*! + * Represents a single instruction in the IR layer. + * + * Each instruction node encapsulates an opcode, its arguments, and pointers to + * form an intrusive double-linked list. + */ +typedef struct instruction_t +{ + // The opcode for this instruction. + opcode_t opcode; + + // An array of arguments for this instruction. + value_t args[MAX_IR_ARGS]; + + // Pointer to the next instruction in the intrusive list. + struct instruction_t *next; + + // Pointer to the previous instruction the intrusive list. + struct instruction_t *previous; +} instruction_t; + +/*! + * @brief Represents a double-linked list of IR instructions. + * + * This structure holds the head and tail pointers of an intrusive list + * composed of `instruction_t` nodes. It is used to store sequences + */ +typedef struct +{ + // Pointer to the first instruction in the list. + instruction_t *head; + + // Pointer to the last instruction in the list. + instruction_t *tail; +} instruction_list_t; + +/*! + * @brief Gets a pointer to the argument at a specific index. + * + * @param instruction Pointer to the IR instruction. + * @param arg_index The index of the argument to retrieve. + * + * @return A constant pointer to the argument at the specified index. + * @pre `instruction` must not be NULL + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + */ +const value_t* instruction_get_arg (const instruction_t *instruction, const size_t arg_index); + +/*! + * Retrieves a U64 argument from an instruction. + * + * @param instruction Pointer to the IR instruction. + * @apram arg_index The index of the argument to retrieve. + * + * @return The U64 value of the argument. + * @pre `instruction` must not be NULL. + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + * @pre The argument at `arg_index` must be of type `IR_TYPE_U64`. + */ +const uint64_t instruction_get_arg_u64(const instruction_t *instruction, const size_t arg_index); + +/*! + * @brief Retrieves a U32 argument from an instruction. + * + * @param instruction Pointer to the IR instruction. + * @param arg_index The index of the argument to retrieve. + * + * @return The U32 value of the argument. + * @pre `instruction` must not be NULL. + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + * @pre The argument at `arg_index` must be of type `IR_TYPE_U32`. + */ +const uint32_t instruction_get_arg_u32(const instruction_t *instruction, const size_t arg_index); + +/*! + * Retrives a U8 argument from an instruction. + * + * @param instruction Pointer to the IR instruction. + * @param arg_index The index of the argument to retrieve. + * + * @return The U8 value of the argument. + * @pre `instruction` must not be NULL. + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + * @pre The argument at `arg_index` must be of type `IR_TYPE_U8`. + */ +const uint8_t instruction_get_arg_u8(const instruction_t *instruction, const size_t arg_index); + +/*! + * @brief Retrieves a U1 (boolean) argument from an instruction. + * + * @param instruction Pointer to the IR instruction. + * @param arg_index The index of the argument to retrieve. + * + * @return The boolean value of the argument. + * @pre `instruction` must not be NULL. + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + * @pre The argument at `arg_index` must be of type `IR_TYPE_U1`. + */ +const bool instruction_get_arg_u1(const instruction_t *instruction, const size_t arg_index); + +/*! + * @brief Retrieves an A32 register identifier argument from an instruction. + * + * @param instruction Pointer to the IR instruction. + * @param arg_index The index of the argument to retrieve. + * + * @return The `a32_register_t` identifier. + * @pre `instruction` must not be NULL. + * @pre `arg_index` must be less than `MAX_IR_ARGS`. + * @pre The argument at `arg_index` must be of type `IR_TYPE_A32_REGISTER`. + */ +const pound::jit::a32_register_t instruction_get_arg_a32_register(const instruction_t *instruction, const size_t arg_index); + +/*! + * @brief Gets the return type of an instruction based on its opcode. + * + * @param instruction Pointer to the IR instruction. + * + * @return The `type_t` that this instruction's opcode returns. + * @pre `instruction` must not be NULL. + * @pre `instruction->opcode` must be a valid opcode index (less than `NUM_OPCODE`). + */ +const type_t instruction_get_return_type (const instruction_t *instruction); + + +/*! + * @brief Gets the name of an instruction's opcode as a C-string. + * + * @param instruction Pointer to the IR instruction. + * + * @return A constant C-string containing the opcode's name. + * @pre `instruction` must not be NULL. + * @pre `instruction->opcode` must be a valid opcode index (less than `NUM_OPCODE`). + * @pre The global `g_opcodes` array must be initialized and accessible. + */ +const char* instruction_get_opcode_name(const instruction_t *instruction); + +/*! + * @brief Appends an instruction to the tail of an instruction list. + * + * The instruction is added to the end of the list. If the list is empty, + * the instruction becomes both the head and the tail. + * + * @param list Pointer to the instruction list to modify. + * @param instruction Pointer to the `instruction_t` node to append. + * + * @pre `list` must not be NULL. + * @pre `instruction` must not be NULL. + */ +void instruction_list_append (instruction_list_t *list, instruction_t *instruction); + +/*! + * @brief Removes an instruction from an instruction list. + * + * @param list Pointer to the instruction list to modify. + * @param instruction Pointer to the `instruction_t` node to remove. + * + * @pre `list` must not be NULL. + * @pre `instruction` must not be NULL. + * @pre `instruction` must be a member of `list`. + */ +void instruction_list_remove (instruction_list_t *list, instruction_t *instruction); +} +#endif // POUND_JIT_IR_INSTRUCTION_H diff --git a/src/jit/ir/opcode.h b/src/jit/ir/opcode.h index 832ec40..e77d7cf 100644 --- a/src/jit/ir/opcode.h +++ b/src/jit/ir/opcode.h @@ -7,6 +7,8 @@ * by including "opcode.inc", which is processed using X-macros. */ +#ifndef POUMD_JIT_IR_OPCODE_H +#define POUMD_JIT_IR_OPCODE_H #include "type.h" namespace pound::jit::ir { @@ -56,3 +58,4 @@ extern decoded_opcode_t g_opcodes[NUM_OPCODE]; void opcode_init(void); } +#endif // POUMD_JIT_IR_OPCODE_H diff --git a/src/jit/ir/type.h b/src/jit/ir/type.h index 8d02944..d60f150 100644 --- a/src/jit/ir/type.h +++ b/src/jit/ir/type.h @@ -7,7 +7,8 @@ * This header declares the `type_t ` enumeration, which forms the basis of * type identification and checking within the JIT's IR. */ - +#ifndef POUND_JIT_IR_TYPE_H +#define POUND_JIT_IR_TYPE_H namespace pound::jit::ir { /*! @@ -60,3 +61,4 @@ typedef enum */ bool are_types_compatible(const type_t t1, const type_t t2); } // namespace pound::jit::ir +#endif // POUND_JIT_IR_TYPE_H diff --git a/src/jit/ir/value.cpp b/src/jit/ir/value.cpp index ce17ecb..f34e20a 100644 --- a/src/jit/ir/value.cpp +++ b/src/jit/ir/value.cpp @@ -17,7 +17,7 @@ value_init (value_t *p_value) } void -value_init_from_u64 (value_t *p_value, uint64_t u64) +value_init_from_u64 (value_t *p_value, const uint64_t u64) { PVM_ASSERT(nullptr != p_value); p_value->type = IR_TYPE_U64; @@ -25,7 +25,7 @@ value_init_from_u64 (value_t *p_value, uint64_t u64) } void -value_init_from_u32 (value_t *p_value, uint32_t u32) +value_init_from_u32 (value_t *p_value, const uint32_t u32) { PVM_ASSERT(nullptr != p_value); p_value->type = IR_TYPE_U32; @@ -33,7 +33,7 @@ value_init_from_u32 (value_t *p_value, uint32_t u32) } void -value_init_from_u8 (value_t *p_value, uint8_t u8) +value_init_from_u8 (value_t *p_value, const uint8_t u8) { PVM_ASSERT(nullptr != p_value); p_value->type = IR_TYPE_U8; @@ -41,7 +41,7 @@ value_init_from_u8 (value_t *p_value, uint8_t u8) } void -value_init_from_u1 (value_t *p_value, bool u1) +value_init_from_u1 (value_t *p_value, const bool u1) { PVM_ASSERT(nullptr != p_value); p_value->type = IR_TYPE_U1; @@ -49,7 +49,7 @@ value_init_from_u1 (value_t *p_value, bool u1) } void -value_init_from_a32_register (value_t *p_value, a32_register_t reg) +value_init_from_a32_register (value_t *p_value, const a32_register_t reg) { PVM_ASSERT(nullptr != p_value); p_value->type = IR_TYPE_A32_REGISTER; @@ -62,36 +62,36 @@ value_init_from_a32_register (value_t *p_value, a32_register_t reg) * ============================================================================ */ -uint64_t -value_get_u64 (value_t *p_value) -{ +const uint64_t +value_get_u64 (const value_t *p_value) +{ PVM_ASSERT(IR_TYPE_U64 == p_value->type); return p_value->inner.immediate_u64; } -uint32_t -value_get_u32 (value_t *p_value) +const uint32_t +value_get_u32 (const value_t *p_value) { PVM_ASSERT(IR_TYPE_U32 == p_value->type); return p_value->inner.immediate_u32; } -uint8_t -value_get_u8 (value_t *p_value) +const uint8_t +value_get_u8 (const value_t *p_value) { PVM_ASSERT(IR_TYPE_U8 == p_value->type); return p_value->inner.immediate_u8; } -bool -value_get_u1 (value_t *p_value) +const bool +value_get_u1 (const value_t *p_value) { PVM_ASSERT(IR_TYPE_U1 == p_value->type); return p_value->inner.immediate_u1; } -pound::jit::a32_register_t -value_get_a32_register (value_t *p_value) +const pound::jit::a32_register_t +value_get_a32_register (const value_t *p_value) { PVM_ASSERT(IR_TYPE_A32_REGISTER == p_value->type); return p_value->inner.immediate_a32_register; diff --git a/src/jit/ir/value.h b/src/jit/ir/value.h index 53b24e5..ab69bb8 100644 --- a/src/jit/ir/value.h +++ b/src/jit/ir/value.h @@ -10,6 +10,8 @@ * approach to store different types of data safely. */ +#ifndef POUND_JIT_IR_VALUE_H +#define POUND_JIT_IR_VALUE_H #include #include "type.h" #include "jit/a32_types.h" @@ -58,7 +60,7 @@ void value_init (value_t *p_value); * @post The `p_value`'s `type` will be set to `IR_TYPE_U64` and its * `inner.immediate_u64` member will contain `u64`. */ -void value_init_from_u64 (value_t *p_value, uint64_t u64); +void value_init_from_u64 (value_t *p_value, const uint64_t u64); /*! * @brief Initializes a `value_t` instance to hold an unsigned 32-bit immediate value. @@ -68,7 +70,7 @@ void value_init_from_u64 (value_t *p_value, uint64_t u64); * @post The `p_value`'s `type` will be set to `IR_TYPE_U32` and its * `inner.immediate_u32` member will contain `u32`. */ -void value_init_from_u32 (value_t *p_value, uint32_t u32); +void value_init_from_u32 (value_t *p_value, const uint32_t u32); /*! * @brief Initializes a `value_t` instance to hold an unsigned 8-bit immediate value. @@ -78,7 +80,7 @@ void value_init_from_u32 (value_t *p_value, uint32_t u32); * @post The `p_value`'s `type` will be set to `IR_TYPE_U8` and its * `inner.immediate_u8` member will contain `u8`. */ -void value_init_from_u8 (value_t *p_value, uint8_t u8); +void value_init_from_u8 (value_t *p_value, const uint8_t u8); /*! * @brief Initializes a `value_t` instance to hold a 1-bit boolean immediate value. @@ -88,7 +90,7 @@ void value_init_from_u8 (value_t *p_value, uint8_t u8); * @post The `p_value`'s `type` will be set to `IR_TYPE_U1` and its * `inner.immediate_u1` member will contain `u1`. */ -void value_init_from_u1 (value_t *p_value, bool u1); +void value_init_from_u1 (value_t *p_value, const bool u1); /*! * @brief Initializes a `value_t` instance to hold an A32 register identifier. @@ -101,7 +103,7 @@ void value_init_from_u1 (value_t *p_value, bool u1); * @post The `p_value`'s `type` will be set to `IR_TYPE_A32_REGISTER` and its * `inner.immediate_a32_register` member will contain `reg`. */ -void value_init_from_a32_register (value_t *p_value, a32_register_t reg); +void value_init_from_a32_register (value_t *p_value, const a32_register_t reg); /*! * @brief Retrieves an unsigned 64-bit immediate value from a `value_t`. @@ -112,7 +114,7 @@ void value_init_from_a32_register (value_t *p_value, a32_register_t reg); * @warning Calling this function on a `value_t` not of type `IR_TYPE_U64` * results in undefined behavior. */ -uint64_t value_get_u64 (value_t *p_value); +const uint64_t value_get_u64 (const value_t *p_value); /*! @@ -124,7 +126,7 @@ uint64_t value_get_u64 (value_t *p_value); * @warning Calling this function on a `value_t` not of type `IR_TYPE_U32` * results in undefined behavior. */ -uint32_t value_get_u32 (value_t *p_value); +const uint32_t value_get_u32 (const value_t *p_value); /*! * @brief Retrieves an unsigned 8-bit immediate value from a `value_t`. @@ -135,7 +137,7 @@ uint32_t value_get_u32 (value_t *p_value); * @warning Calling this function on a `value_t` not of type `IR_TYPE_U8` * results in undefined behavior. */ -uint8_t value_get_u8 (value_t *p_value); +const uint8_t value_get_u8 (const value_t *p_value); /** * @brief Retrieves an unsigned 1-bit immediate value from a `value_t`. @@ -146,7 +148,7 @@ uint8_t value_get_u8 (value_t *p_value); * @warning Calling this function on a `value_t` not of type `IR_TYPE_U1` * results in undefined behavior. */ -bool value_get_u1 (value_t *p_value); +const bool value_get_u1 (const value_t *p_value); /** * @brief Retrieves an A32 register identifier from a `value_t`. @@ -157,5 +159,6 @@ bool value_get_u1 (value_t *p_value); * @warning Calling this function on a `value_t` not of type `IR_TYPE_A32_REGISTER` * results in undefined behavior. */ -pound::jit::a32_register_t value_get_a32_register (value_t *p_value); +const pound::jit::a32_register_t value_get_a32_register (const value_t *p_value); } // namespace pound:::jit::ir +#endif // POUND_JIT_IR_TYPE_H