engine: fix name mangling with c++ compilers

Added `extern "C"` to every header file to support using Ballistic
with C++ projects

Signed-off-by: Ronald Caesar <github43132@proton.me>
This commit is contained in:
Ronald Caesar
2026-04-16 16:56:45 -04:00
parent 43199c79f8
commit e3a0870b86
12 changed files with 689 additions and 574 deletions
+137 -125
View File
@@ -28,143 +28,155 @@
#include <stddef.h>
#include <stdint.h>
/// This struct manages a linear buffer of 32-bit integers where ARM64 machine code is written.
/// It tracks the current write position and handles boundary checking.
typedef struct
#ifdef __cplusplus
extern "C"
{
/// A pointer to the start of the code buffer.
uint32_t *buffer;
#endif /* __cplusplus */
/// The maximum number of instructions that can fit in the buffer.
size_t capacity;
/// This struct manages a linear buffer of 32-bit integers where ARM64 machine code is written.
/// It tracks the current write position and handles boundary checking.
typedef struct
{
/// A pointer to the start of the code buffer.
uint32_t *buffer;
/// The current write index within the buffer.
size_t offset;
/// The maximum number of instructions that can fit in the buffer.
size_t capacity;
/// The logging context used to report details and errors.
bal_logger_t logger;
/// The current write index within the buffer.
size_t offset;
/// The current error state of the assembler.
/// The logging context used to report details and errors.
bal_logger_t logger;
/// The current error state of the assembler.
///
/// Once this is set to anything other than [`BAL_SUCCESS`], all subsequent emit calls will
/// be ignored until the assembler is reset.
bal_error_t status;
} bal_assembler_t;
/// Initializes the assembler with a specific memory buffer and the size of the buffer in
/// `uint32_t` elements.
///
/// Once this is set to anything other than [`BAL_SUCCESS`], all subsequent emit calls will be
/// ignored until the assembler is reset.
bal_error_t status;
} bal_assembler_t;
/// # Returns
///
/// * [`BAL_SUCCESS`] on success.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if `assembler` or `buffer` is NULL.
/// * [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not 4-byte aligned.
bal_error_t bal_assembler_init(bal_assembler_t *assembler,
void *buffer,
size_t size,
bal_logger_t logger);
/// Initializes the assembler with a specific memory buffer and the size of the buffer in
/// `uint32_t` elements.
///
/// # Returns
///
/// * [`BAL_SUCCESS`] on success.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if `assembler` or `buffer` is NULL.
/// * [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not 4-byte aligned.
bal_error_t bal_assembler_init(bal_assembler_t *assembler,
void *buffer,
size_t size,
bal_logger_t logger);
/// Emit a `ADD` (Immediate) instruction.
///
/// Adds a register value `rn` and 12 bit immediate value `imm12` with the optional left shift
/// `sh` to apply to the immediate, and writes the result to the destination register `rd`.
///
/// # Safety
///
/// * `rn` will be truncated if it exceeds 5 bits.
/// * `imm12` will be truncated if it exceeds 12 bits.
/// * `sh` must have the value `0`, or `1`
/// * Function does not emit instructions if `assembler->status != BALL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_add_immediate(bal_assembler_t *assembler,
bal_register_index_t rd,
uint8_t rn,
uint16_t imm12,
uint8_t shift);
/// Emit a `ADD` (Immediate) instruction.
///
/// Adds a register value `rn` and 12 bit immediate value `imm12` with the optional left shift `sh`
/// to apply to the immediate, and writes the result to the destination register `rd`.
///
/// # Safety
///
/// * `rn` will be truncated if it exceeds 5 bits.
/// * `imm12` will be truncated if it exceeds 12 bits.
/// * `sh` must have the value `0`, or `1`
/// * Function does not emit instructions if `assembler->status != BALL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_add_immediate(
bal_assembler_t *assembler, bal_register_index_t rd, uint8_t rn, uint16_t imm12, uint8_t shift);
/// Emit a `MOVZ` (Move Wide with Zero) instruction.
///
/// Moves a 16-bit immediate into a register, shifting it left by 0, 16, 32, or 48 bits, and
/// the rest of the register to zero.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movz(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emit a `MOVZ` (Move Wide with Zero) instruction.
///
/// Moves a 16-bit immediate into a register, shifting it left by 0, 16, 32, or 48 bits, and
/// the rest of the register to zero.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movz(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVK` (Move Wide with Keep) instruction.
///
/// Moves a 16-bit immediate into a specific 16-bit field of a register, leaving the other bits
/// unchanged.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movk(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVK` (Move Wide with Keep) instruction.
///
/// Moves a 16-bit immediate into a specific 16-bit field of a register, leaving the other bits
/// unchanged.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movk(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVN` (Move Wide with Not) instruction.
///
/// Moves the bitwise inverse of a 16-bit immediate (shifted left) into a register, setting all
/// other bits to 1.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movn(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVN` (Move Wide with Not) instruction.
///
/// Moves the bitwise inverse of a 16-bit immediate (shifted left) into a register, setting all
/// other bits to 1.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movn(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `RET` instruction.
///
/// Returns from subroutine branches unconditionally to an address in a register with a hint
/// that this is a subroutine return.
///
/// # Safety
///
/// * `rn` must be between 0-31 inclusive.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_ret(bal_assembler_t *assembler, bal_register_index_t rn);
/// Emits a `RET` instruction.
///
/// Returns from subroutine branches unconditionally to an address in a register with a hint that
/// this is a subroutine return.
///
/// # Safety
///
/// * `rn` must be between 0-31 inclusive.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_ret(bal_assembler_t *assembler, bal_register_index_t rn);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_ASSEMBLER_H */
+15 -2
View File
@@ -3,8 +3,17 @@
#include <stddef.h>
void bal_internal_assert_fail(
const char *file, int line, const char *func, const char *expr_str, const char *user_msg, ...);
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
void bal_internal_assert_fail(const char *file,
int line,
const char *func,
const char *expr_str,
const char *user_msg,
...);
#define BAL_ASSERT(expression) \
do \
@@ -33,4 +42,8 @@ void bal_internal_assert_fail(
"Unreachable code executed", \
##__VA_ARGS__);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // BALLISTIC_COMMON_ASSERT_H
+9
View File
@@ -8,6 +8,11 @@
#include "bal_platform.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// BAL_HOT()/BAL_COLD()
/// Marks a function as hot or cold. Hot makes the compiler optimize it more
/// aggressively. Cold marks the function as rarely executed.
@@ -77,6 +82,10 @@
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_ATTRIBUTES_H */
/*** end of file ***/
+2 -2
View File
@@ -17,7 +17,7 @@
#ifdef __cplusplus
extern "C"
{
#endif
#endif /* __cplusplus */
#define BAL_OPERANDS_SIZE 5
#define BAL_OPERAND_BIT_WIDTH 5
@@ -91,5 +91,5 @@ extern "C"
#ifdef __cplusplus
}
#endif
#endif /* __cplusplus */
#endif // BAL_DECODER_H
+122 -112
View File
@@ -8,6 +8,11 @@
#include "bal_types.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// A byte pattern written to memory during initialization, poisoning allocated
/// regions. This is mainly used for detecting reads from uninitialized memory.
#define POISON_UNINITIALIZED_MEMORY 0xFF
@@ -40,136 +45,141 @@
/// The bit position for the is constant flag in a bal_instruction_t.
#define BAL_IS_CONSTANT_BIT_POSITION (1U << 16U)
/// Represents the mapping of a Guest Register to an SSA variable.
/// This is only used during Single Static Assignment construction
/// to track variable definitions across basic blocks.
typedef struct
{
/// The index of the most recent SSA definition for this register.
uint32_t current_ssa_index;
/// Represents the mapping of a Guest Register to an SSA variable.
/// This is only used during Single Static Assignment construction
/// to track variable definitions across basic blocks.
typedef struct
{
/// The index of the most recent SSA definition for this register.
uint32_t current_ssa_index;
/// The index of the SSA definition that existed at the start of the
/// current block.
uint32_t original_variable_index;
} bal_source_variable_t;
/// The index of the SSA definition that existed at the start of the
/// current block.
uint32_t original_variable_index;
} bal_source_variable_t;
/// Holds the Intermediate Representation buffers, SSA state, and other
/// important metadata. The structure is divided into hot and cold data aligned
/// to 64 bytes. Both hot and cold data lives on their own cache lines.
typedef struct
{
/* Hot Data */
/// Holds the Intermediate Representation buffers, SSA state, and other
/// important metadata. The structure is divided into hot and cold data aligned
/// to 64 bytes. Both hot and cold data lives on their own cache lines.
typedef struct
{
/* Hot Data */
/// Map of ARM registers to their current SSA definitions.
bal_source_variable_t *source_variables;
/// Map of ARM registers to their current SSA definitions.
bal_source_variable_t *source_variables;
/// The linear buffer of generated IR instructions for the current
/// compilation unit.
bal_instruction_t *instructions;
/// The linear buffer of generated IR instructions for the current
/// compilation unit.
bal_instruction_t *instructions;
/// Metadata tracking the bit-width (32 or 64 bit) for each variable.
bal_bit_width_t *ssa_bit_widths;
/// Metadata tracking the bit-width (32 or 64 bit) for each variable.
bal_bit_width_t *ssa_bit_widths;
/// Linear buffer of constants generated in the current compilation unit.
bal_constant_t *constants;
/// Linear buffer of constants generated in the current compilation unit.
bal_constant_t *constants;
/// The size of the `source_variables` array.
size_t source_variables_size;
/// The size of the `source_variables` array.
size_t source_variables_size;
/// The size of the `instructions` array.
size_t instructions_size;
/// The size of the `instructions` array.
size_t instructions_size;
/// The size of the `constants` array.
size_t constants_size;
/// The size of the `constants` array.
size_t constants_size;
/// The current number of instructions emitted.
/// The current number of instructions emitted.
///
/// This tracks the current position in `instructions` and `ssa_bit_widths`
/// arrays.
bal_instruction_count_t instruction_count;
/// The number of constants emitted.
///
/// This tracks the current position in the `constants` array.
bal_constant_count_t constant_count;
/// The current error state of the Engine.
///
/// If an operation fails, this field is set to a specific error code.
/// See [`bal_opcode_t`]. Once set to an error state, subsequent operation
/// on this engine will silently fail until [`bal_engine_reset`] is called.
bal_error_t status;
/* Cold Data */
/// The base pointer returned during the underlying heap allocation. This
/// is required to correctly free the engine's internal arrays.
void *arena_base;
/// The total size of the allocated arena.
size_t arena_size;
/// Handles logging for this engine.
bal_logger_t logger;
} bal_engine_t;
/// Initializes a Ballistic engine.
///
/// This tracks the current position in `instructions` and `ssa_bit_widths`
/// arrays.
bal_instruction_count_t instruction_count;
/// The number of constants emitted.
/// Populates `engine` with `logger` and empty buffers allocated with `allocator`. This is
/// a high-cost memory operation that reserves a lot of memory and should
/// be called sparingly.
///
/// This tracks the current position in the `constants` array.
bal_constant_count_t constant_count;
/// The current error state of the Engine.
/// Returns [`BAL_SUCCESS`] if the engine iz ready for use.
///
/// If an operation fails, this field is set to a specific error code.
/// See [`bal_opcode_t`]. Once set to an error state, subsequent operation
/// on this engine will silently fail until [`bal_engine_reset`] is called.
bal_error_t status;
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if the pointers are `NULL`.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if the allocator cannot fulfill the
/// request.
BAL_COLD bal_error_t bal_engine_init(const bal_allocator_t *allocator,
bal_engine_t *engine,
bal_logger_t logger);
/* Cold Data */
/// Translates machine code starting at `guest_address_start` into the engine's
/// internal IR. `interface` provides memory access handling (like instruction
/// fetching).
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Safety
///
/// If `guest_address_staart` is `NULL`, we assume the entry point is 0x0.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if functino arguments are invalid or
/// or `engine->status != BAL_SUCCESS`.
///
/// Returns [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if the array `engine->constants` overflows.
BAL_HOT bal_error_t bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
const bal_memory_interface_t *BAL_RESTRICT interface,
bal_guest_address_t *guest_address_start,
size_t max_instructions);
/// The base pointer returned during the underlying heap allocation. This
/// is required to correctly free the engine's internal arrays.
void *arena_base;
/// Resets `engine` for the next compilation unit. This is a low-cost memory
/// operation designed to be called between translation units.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if `engine` is `NULl`.
BAL_HOT bal_error_t bal_engine_reset(bal_engine_t *engine);
/// The total size of the allocated arena.
size_t arena_size;
/// Frees all `engine` heap-allocated resources using `allocator`.
///
/// # Warning
///
/// This function does not free the [`bal_engine_t`] struct itself, as the
/// caller may have allocated it on the stack.
BAL_COLD void bal_engine_destroy(const bal_allocator_t *allocator, bal_engine_t *engine);
/// Handles logging for this engine.
bal_logger_t logger;
#ifdef __cplusplus
}
#endif /* __cplusplus */
} bal_engine_t;
/// Initializes a Ballistic engine.
///
/// Populates `engine` with `logger` and empty buffers allocated with `allocator`. This is
/// a high-cost memory operation that reserves a lot of memory and should
/// be called sparingly.
///
/// Returns [`BAL_SUCCESS`] if the engine iz ready for use.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if the pointers are `NULL`.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if the allocator cannot fulfill the
/// request.
BAL_COLD bal_error_t bal_engine_init(const bal_allocator_t *allocator,
bal_engine_t *engine,
bal_logger_t logger);
/// Translates machine code starting at `guest_address_start` into the engine's
/// internal IR. `interface` provides memory access handling (like instruction
/// fetching).
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Safety
///
/// If `guest_address_staart` is `NULL`, we assume the entry point is 0x0.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if functino arguments are invalid or
/// or `engine->status != BAL_SUCCESS`.
///
/// Returns [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if the array `engine->constants` overflows.
BAL_HOT bal_error_t bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
const bal_memory_interface_t *BAL_RESTRICT interface,
bal_guest_address_t *guest_address_start,
size_t max_instructions);
/// Resets `engine` for the next compilation unit. This is a low-cost memory
/// operation designed to be called between translation units.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if `engine` is `NULl`.
BAL_HOT bal_error_t bal_engine_reset(bal_engine_t *engine);
/// Frees all `engine` heap-allocated resources using `allocator`.
///
/// # Warning
///
/// This function does not free the [`bal_engine_t`] struct itself, as the
/// caller may have allocated it on the stack.
BAL_COLD void bal_engine_destroy(const bal_allocator_t *allocator, bal_engine_t *engine);
#endif /* BALLISTIC_ENGINE_H */
/*** end of file ***/
+29 -20
View File
@@ -8,32 +8,41 @@
#include "bal_attributes.h"
typedef enum
#ifdef __cplusplus
extern "C"
{
// General Errors.
//
BAL_SUCCESS = 0,
BAL_ERROR_INVALID_ARGUMENT = -1,
BAL_ERROR_ALLOCATION_FAILED = -2,
#endif /* __cplusplus */
/// Guest code tried to execute an unaligned instruction.
BAL_ERROR_PC_ALIGNMENT = -3,
BAL_ERROR_MEMORY_ALIGNMENT = -4,
typedef enum
{
// General Errors.
//
BAL_SUCCESS = 0,
BAL_ERROR_INVALID_ARGUMENT = -1,
BAL_ERROR_ALLOCATION_FAILED = -2,
/// Ballistic tried to access memory it was not allowed to access.
BAL_ERROR_MEMORY_FAULT = -5,
BAL_ERROR_UNKNOWN_INSTRUCTION = -6,
/// Guest code tried to execute an unaligned instruction.
BAL_ERROR_PC_ALIGNMENT = -3,
BAL_ERROR_MEMORY_ALIGNMENT = -4,
// IR Errors.
//
BAL_ERROR_INSTRUCTION_OVERFLOW = -100,
/// Ballistic tried to access memory it was not allowed to access.
BAL_ERROR_MEMORY_FAULT = -5,
BAL_ERROR_UNKNOWN_INSTRUCTION = -6,
/// The decoded register type does not match the expected type.
BAL_ERROR_INCORRECT_REGISTER_TYPE = -101,
} bal_error_t;
// IR Errors.
//
BAL_ERROR_INSTRUCTION_OVERFLOW = -100,
/// Converts the enum into a readable string for error handling.
BAL_COLD const char *bal_error_to_string(bal_error_t error);
/// The decoded register type does not match the expected type.
BAL_ERROR_INCORRECT_REGISTER_TYPE = -101,
} bal_error_t;
/// Converts the enum into a readable string for error handling.
BAL_COLD const char *bal_error_to_string(bal_error_t error);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BAL_ERRORS_H */
+84 -74
View File
@@ -58,69 +58,74 @@
#include "bal_attributes.h"
#include <stdarg.h>
/// Defines the severity of a log message.
typedef enum
#ifdef __cplusplus
extern "C"
{
/// Logging is disabled.
BAL_LOG_LEVEL_NONE = -1,
#endif /* __cplusplus */
/// Critical errors that likely result in immediate termination or undefined behaviour.
BAL_LOG_LEVEL_ERROR = 0,
/// Defines the severity of a log message.
typedef enum
{
/// Logging is disabled.
BAL_LOG_LEVEL_NONE = -1,
/// Non-critical issues that may result in degraded performance or functionality loss.
BAL_LOG_LEVEL_WARN = 1,
/// Critical errors that likely result in immediate termination or undefined behaviour.
BAL_LOG_LEVEL_ERROR = 0,
/// General operational events.
BAL_LOG_LEVEL_INFO = 2,
/// Non-critical issues that may result in degraded performance or functionality loss.
BAL_LOG_LEVEL_WARN = 1,
/// Information useful for debugging logic errors.
BAL_LOG_LEVEL_DEBUG = 3,
/// General operational events.
BAL_LOG_LEVEL_INFO = 2,
/// Extreme verbose output.
BAL_LOG_LEVEL_TRACE = 4,
} bal_log_level_t;
/// Information useful for debugging logic errors.
BAL_LOG_LEVEL_DEBUG = 3,
/// Metadata associated with a specific log event.
typedef struct
{
/// Source file where the log occurred.
const char *filename;
/// Extreme verbose output.
BAL_LOG_LEVEL_TRACE = 4,
} bal_log_level_t;
/// The function name where the log occurred.
const char *function;
/// Metadata associated with a specific log event.
typedef struct
{
/// Source file where the log occurred.
const char *filename;
/// The log level.
bal_log_level_t level;
/// The function name where the log occurred.
const char *function;
/// The line number where the log occurred
int line;
} bal_log_data_t;
/// The log level.
bal_log_level_t level;
/// A function pointer defining a custom logging backend.
///
/// Implementations of this function are responsible for formatting and persisting the log
/// message. The `user_data` pointer is passed through from the `bal_logger_t` context. The
/// `bal_data` struct contains metadata about the event, while `format`, and `args` provide the
/// standard printf-style message content.
typedef void (*bal_log_function_t)(void *user_data,
bal_log_data_t *bal_data,
const char *format,
va_list args);
/// The line number where the log occurred
int line;
} bal_log_data_t;
/// The main logging context.
typedef struct
{
/// An opaque pointer passed to the log callback. This can be used to store file handles or
/// other context-specific data.
void *user_data;
/// A function pointer defining a custom logging backend.
///
/// Implementations of this function are responsible for formatting and persisting the log
/// message. The `user_data` pointer is passed through from the `bal_logger_t` context. The
/// `bal_data` struct contains metadata about the event, while `format`, and `args` provide the
/// standard printf-style message content.
typedef void (*bal_log_function_t)(void *user_data,
bal_log_data_t *bal_data,
const char *format,
va_list args);
/// The callback invoked when a message needs to be logged. If this is `NULL`, logging is
/// disabled for this context.
bal_log_function_t log;
/// The main logging context.
typedef struct
{
/// An opaque pointer passed to the log callback. This can be used to store file handles or
/// other context-specific data.
void *user_data;
/// The minimum severity level required for a message to be processed.
bal_log_level_t min_level;
} bal_logger_t;
/// The callback invoked when a message needs to be logged. If this is `NULL`, logging is
/// disabled for this context.
bal_log_function_t log;
/// The minimum severity level required for a message to be processed.
bal_log_level_t min_level;
} bal_logger_t;
// Remove all log code if log level not defined.
//
@@ -130,33 +135,34 @@ typedef struct
#endif
/// Dispatches a log message to the configured backend.
///
/// This is the entry point into the logging system. It invokes the callback defined in `logger`.
///
/// # Warning
///
/// Do not call this function directly. Use our logging macros like `BAL_LOG_INFO`.
///
/// # Safety
///
/// The `format` string must match the arguments provided in the variadic list, following standard
/// `printf`.
/// Dispatches a log message to the configured backend.
///
/// This is the entry point into the logging system. It invokes the callback defined in
/// `logger`.
///
/// # Warning
///
/// Do not call this function directly. Use our logging macros like `BAL_LOG_INFO`.
///
/// # Safety
///
/// The `format` string must match the arguments provided in the variadic list, following
/// standard `printf`.
BAL_COLD void bal_log_message(const bal_logger_t *logger,
bal_log_level_t log_level,
const char *filename,
const char *function,
int line,
const char *format,
...);
BAL_COLD void bal_log_message(const bal_logger_t *logger,
bal_log_level_t log_level,
const char *filename,
const char *function,
int line,
const char *format,
...);
/// Populates `logger` with Ballistic's default logging implementation.
///
/// # Safety
///
/// `logger` must NOT be `NULL`.
BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
/// Populates `logger` with Ballistic's default logging implementation.
///
/// # Safety
///
/// `logger` must NOT be `NULL`.
BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
#define BAL_LOG_ERROR(logger, format, ...) \
bal_log_message((logger), \
@@ -212,6 +218,10 @@ BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
} while (0)
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_LOGGING_H */
/*** end of file ***/
+126 -114
View File
@@ -8,132 +8,144 @@
#include <stddef.h>
#include <stdint.h>
struct bal_allocator_state_s;
typedef struct bal_allocator_state_s *bal_allocator_handle_t;
/// A function signature for allocating aligned memory.
///
/// Implementations must allocate a block of memory of at least `size` bytes
/// with an address that is a multiple of `alignment`. The `alignment`
/// parameter is guaranteed to be a power of two. The `allocator` parameter
/// provides access to the opaque state registered in [`bal_allocator_t`].
///
/// Returns a pointer to the allocated memory, or `NULL` if the request could
/// not be fulfilled.
typedef void *(*bal_allocate_function_t)(bal_allocator_handle_t allocator,
size_t alignment,
size_t size);
/// A function signature for releasing memory.
///
/// Implementations must deallocate the memory at `pointer`, which was
/// previously allocated by the corresponding to allocate function. The `size`
/// parameter indicates the size of the allocation being freed. Access to the
/// heap state is provided via `allocator`.
typedef void (*bal_free_function_t)(bal_allocator_handle_t allocator, void *pointer, size_t size);
/// Translates a Guest Virtual Address (GVA) to a Host Virtual Address (HVA).
///
/// Ballistic invokes this callback when it needs to fetch instructions or
/// access data.
///
/// Returns a pointer to the host memory containing the data at `guest_address`, or `NULL`
/// if the address is unmapped or invalid.
///
/// # Safety
///
/// The implementation must translate the provided `guest_address`
/// using the opaque `context` and return a pointer to the corresponding host
/// memory.
///
/// The implementation must write the number of contiguous, readable bytes
/// available at the returned pointer into `max_readable_size_bytes`. This prevents
/// Ballistic from reading beyond the end of a mapped page or buffer.
typedef const uint8_t *(*bal_translate_function_t)(void *context,
bal_guest_address_t guest_address,
size_t *max_readable_size_bytes);
/// The host application is responsible for providing an allocator capable of
/// handling aligned memory requests.
typedef struct
#ifdef __cplusplus
extern "C"
{
/// An opaque pointer defining the state or tracking information for the
/// heap.
bal_allocator_handle_t handle;
#endif /* __cplusplus */
/// The callback invoked to allocate aligned memory.
struct bal_allocator_state_s;
typedef struct bal_allocator_state_s *bal_allocator_handle_t;
/// A function signature for allocating aligned memory.
///
/// Implementations must allocate a block of memory of at least `size` bytes
/// with an address that is a multiple of `alignment`. The `alignment`
/// parameter is guaranteed to be a power of two. The `allocator` parameter
/// provides access to the opaque state registered in [`bal_allocator_t`].
///
/// Returns a pointer to the allocated memory, or `NULL` if the request could
/// not be fulfilled.
typedef void *(*bal_allocate_function_t)(bal_allocator_handle_t allocator,
size_t alignment,
size_t size);
/// A function signature for releasing memory.
///
/// Implementations must deallocate the memory at `pointer`, which was
/// previously allocated by the corresponding to allocate function. The `size`
/// parameter indicates the size of the allocation being freed. Access to the
/// heap state is provided via `allocator`.
typedef void (*bal_free_function_t)(bal_allocator_handle_t allocator,
void *pointer,
size_t size);
/// Translates a Guest Virtual Address (GVA) to a Host Virtual Address (HVA).
///
/// Ballistic invokes this callback when it needs to fetch instructions or
/// access data.
///
/// Returns a pointer to the host memory containing the data at `guest_address`, or `NULL`
/// if the address is unmapped or invalid.
///
/// # Safety
///
/// The implementation must return memory aligned to at least 64 bytes if
/// requested.
bal_allocate_function_t allocate;
/// The implementation must translate the provided `guest_address`
/// using the opaque `context` and return a pointer to the corresponding host
/// memory.
///
/// The implementation must write the number of contiguous, readable bytes
/// available at the returned pointer into `max_readable_size_bytes`. This prevents
/// Ballistic from reading beyond the end of a mapped page or buffer.
typedef const uint8_t *(*bal_translate_function_t)(void *context,
bal_guest_address_t guest_address,
size_t *max_readable_size_bytes);
/// The callback to release memory.
bal_free_function_t free;
/// The host application is responsible for providing an allocator capable of
/// handling aligned memory requests.
typedef struct
{
/// An opaque pointer defining the state or tracking information for the
/// heap.
bal_allocator_handle_t handle;
} bal_allocator_t;
/// The callback invoked to allocate aligned memory.
///
/// # Safety
///
/// The implementation must return memory aligned to at least 64 bytes if
/// requested.
bal_allocate_function_t allocate;
/// Defines the interface for translating guest addresses to host memory.
typedef struct
{
/// An opaque pointer to the context required for address translation
/// (e.g, a page walker or a buffer descriptor.).
void *context;
/// The callback to release memory.
bal_free_function_t free;
/// The callback invoked to perform address translation.
bal_translate_function_t translate;
} bal_memory_interface_t;
} bal_allocator_t;
/// Populates `out_allocator` with the default system implementation.
///
/// # Safety
///
/// `out_allocator` must not be `NULL`.
///
/// # Platform Support
///
/// This function only supports windows and POSIX-compliant systems.
BAL_COLD void bal_allocator_default_init(bal_allocator_t *out_allocator);
/// Defines the interface for translating guest addresses to host memory.
typedef struct
{
/// An opaque pointer to the context required for address translation
/// (e.g, a page walker or a buffer descriptor.).
void *context;
/// Initializes a flat, contiguous translation interface.
///
/// This convenience function sets up a [`bal_memory_interface_t`] where guest
/// addresses map directly to offsets within the provided host `buffer`.
///
/// The internal interface is allocated with `allocator`. `interface` is
/// populated with the resulting context and translation callbacks. `buffer`
/// must be a pre-allocated block of host memory of at least `size` bytes.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if any pointer are `NULL`.
///
/// Returns [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not aligned to a 16-byte
/// boundary.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if failed to allocate memory interface.
///
/// # Safety
///
/// The caller retains ownership of `buffer`. It must remain valid and
/// unmodified for the lifetime of the created interface.
BAL_COLD bal_error_t
bal_flat_translation_interface_init(bal_allocator_t *BAL_RESTRICT allocator,
bal_memory_interface_t *BAL_RESTRICT interface,
void *BAL_RESTRICT buffer,
size_t size,
bal_logger_t logger);
/// The callback invoked to perform address translation.
bal_translate_function_t translate;
} bal_memory_interface_t;
/// Populates `out_allocator` with the default system implementation.
///
/// # Safety
///
/// `out_allocator` must not be `NULL`.
///
/// # Platform Support
///
/// This function only supports windows and POSIX-compliant systems.
BAL_COLD void bal_allocator_default_init(bal_allocator_t *out_allocator);
/// Initializes a flat, contiguous translation interface.
///
/// This convenience function sets up a [`bal_memory_interface_t`] where guest
/// addresses map directly to offsets within the provided host `buffer`.
///
/// The internal interface is allocated with `allocator`. `interface` is
/// populated with the resulting context and translation callbacks. `buffer`
/// must be a pre-allocated block of host memory of at least `size` bytes.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if any pointer are `NULL`.
///
/// Returns [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not aligned to a 16-byte
/// boundary.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if failed to allocate memory interface.
///
/// # Safety
///
/// The caller retains ownership of `buffer`. It must remain valid and
/// unmodified for the lifetime of the created interface.
BAL_COLD bal_error_t
bal_flat_translation_interface_init(bal_allocator_t *BAL_RESTRICT allocator,
bal_memory_interface_t *BAL_RESTRICT interface,
void *BAL_RESTRICT buffer,
size_t size,
bal_logger_t logger);
/// Frees the internal state allocated within `interface` using the provided
/// `allocator` and resets `interface` to 0 with a memset.
///
/// This does **not** free the buffer passed to [`bal_flat_translation_interface_init`] as
/// Ballistic does not take ownership of the host memory region.
BAL_COLD bal_error_t bal_flat_translation_interface_destroy(bal_allocator_t *allocator,
bal_memory_interface_t *interface);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/// Frees the internal state allocated within `interface` using the provided
/// `allocator` and resets `interface` to 0 with a memset.
///
/// This does **not** free the buffer passed to [`bal_flat_translation_interface_init`] as
/// Ballistic does not take ownership of the host memory region.
BAL_COLD bal_error_t bal_flat_translation_interface_destroy(bal_allocator_t *allocator,
bal_memory_interface_t *interface);
#endif /* BALLISTIC_MEMORY_H */
/*** end of file ***/
+11
View File
@@ -1,6 +1,11 @@
#ifndef BALLISTIC_PLATFORM_H
#define BALLISTIC_PLATFORM_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#if defined(_WIN32) || defined(_WIN64)
#define BAL_PLATFORM_WINDOWS 1
@@ -49,4 +54,10 @@
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_PLATFORM_H */
/*** end of file ***/
+109 -100
View File
@@ -8,139 +8,148 @@
#include <stdint.h>
typedef uint64_t bal_guest_address_t;
typedef uint64_t bal_instruction_t;
typedef uint64_t bal_constant_t;
typedef uint16_t bal_instruction_count_t;
typedef uint16_t bal_constant_count_t;
typedef uint8_t bal_bit_width_t;
typedef enum
#ifdef __cplusplus
extern "C"
{
/// This opcode represents a volatile load of a guest register.
OPCODE_GET_REGISTER,
OPCODE_CONST,
OPCODE_MOV,
OPCODE_ADD,
OPCODE_SUB,
OPCODE_MUL,
OPCODE_DIV,
OPCODE_AND,
OPCODE_XOR,
OPCODE_OR_NOT,
OPCODE_SHIFT,
OPCODE_LOAD,
OPCODE_STORE,
OPCODE_JUMP,
OPCODE_CALL,
OPCODE_RETURN,
OPCODE_BRANCH_ZERO,
OPCODE_BRANCH_NOT_ZERO,
OPCODE_TEST_BIT_ZERO,
OPCODE_CMP,
OPCODE_CMP_COND,
OPCODE_TRAP,
OPCODE_ENUM_END = 0x7FF, // Force enum to 2 bytes.
} bal_opcode_t;
#endif /* __cplusplus */
typedef enum
{
/// The register index for X0.
BAL_REGISTER_X0 = 0,
typedef uint64_t bal_guest_address_t;
typedef uint64_t bal_instruction_t;
typedef uint64_t bal_constant_t;
typedef uint16_t bal_instruction_count_t;
typedef uint16_t bal_constant_count_t;
typedef uint8_t bal_bit_width_t;
/// The register index for X1.
BAL_REGISTER_X1 = 1,
typedef enum
{
/// This opcode represents a volatile load of a guest register.
OPCODE_GET_REGISTER,
OPCODE_CONST,
OPCODE_MOV,
OPCODE_ADD,
OPCODE_SUB,
OPCODE_MUL,
OPCODE_DIV,
OPCODE_AND,
OPCODE_XOR,
OPCODE_OR_NOT,
OPCODE_SHIFT,
OPCODE_LOAD,
OPCODE_STORE,
OPCODE_JUMP,
OPCODE_CALL,
OPCODE_RETURN,
OPCODE_BRANCH_ZERO,
OPCODE_BRANCH_NOT_ZERO,
OPCODE_TEST_BIT_ZERO,
OPCODE_CMP,
OPCODE_CMP_COND,
OPCODE_TRAP,
OPCODE_ENUM_END = 0x7FF, // Force enum to 2 bytes.
} bal_opcode_t;
/// The register index for X2.
BAL_REGISTER_X2 = 2,
typedef enum
{
/// The register index for X0.
BAL_REGISTER_X0 = 0,
/// The register index for X3.
BAL_REGISTER_X3 = 3,
/// The register index for X1.
BAL_REGISTER_X1 = 1,
/// The register index for X4.
BAL_REGISTER_X4 = 4,
/// The register index for X2.
BAL_REGISTER_X2 = 2,
/// The register index for X5.
BAL_REGISTER_X5 = 5,
/// The register index for X3.
BAL_REGISTER_X3 = 3,
/// The register index for X6.
BAL_REGISTER_X6 = 6,
/// The register index for X4.
BAL_REGISTER_X4 = 4,
/// The register index for X7.
BAL_REGISTER_X7 = 7,
/// The register index for X5.
BAL_REGISTER_X5 = 5,
/// The register index for X8.
BAL_REGISTER_X8 = 8,
/// The register index for X6.
BAL_REGISTER_X6 = 6,
/// The register index for X9.
BAL_REGISTER_X9 = 9,
/// The register index for X7.
BAL_REGISTER_X7 = 7,
/// The register index for 10.
BAL_REGISTER_X10 = 10,
/// The register index for X8.
BAL_REGISTER_X8 = 8,
/// The register index for 11.
BAL_REGISTER_X11 = 11,
/// The register index for X9.
BAL_REGISTER_X9 = 9,
/// The register index for 12.
BAL_REGISTER_X12 = 12,
/// The register index for 10.
BAL_REGISTER_X10 = 10,
/// The register index for 13.
BAL_REGISTER_X13 = 13,
/// The register index for 11.
BAL_REGISTER_X11 = 11,
/// The register index for 14.
BAL_REGISTER_X14 = 14,
/// The register index for 12.
BAL_REGISTER_X12 = 12,
/// The register index for 15.
BAL_REGISTER_X15 = 15,
/// The register index for 13.
BAL_REGISTER_X13 = 13,
/// The register index for 16.
BAL_REGISTER_X16 = 16,
/// The register index for 14.
BAL_REGISTER_X14 = 14,
/// The register index for 17.
BAL_REGISTER_X17 = 17,
/// The register index for 15.
BAL_REGISTER_X15 = 15,
/// The register index for 18.
BAL_REGISTER_X18 = 18,
/// The register index for 16.
BAL_REGISTER_X16 = 16,
/// The register index for 19.
BAL_REGISTER_X19 = 19,
/// The register index for 17.
BAL_REGISTER_X17 = 17,
/// The register index for 20.
BAL_REGISTER_X20 = 20,
/// The register index for 18.
BAL_REGISTER_X18 = 18,
/// The register index for 21.
BAL_REGISTER_X21 = 21,
/// The register index for 19.
BAL_REGISTER_X19 = 19,
/// The register index for 22.
BAL_REGISTER_X22 = 22,
/// The register index for 20.
BAL_REGISTER_X20 = 20,
/// The register index for 23.
BAL_REGISTER_X23 = 23,
/// The register index for 21.
BAL_REGISTER_X21 = 21,
/// The register index for 24.
BAL_REGISTER_X24 = 24,
/// The register index for 22.
BAL_REGISTER_X22 = 22,
/// The register index for 25.
BAL_REGISTER_X25 = 25,
/// The register index for 23.
BAL_REGISTER_X23 = 23,
/// The register index for 26.
BAL_REGISTER_X26 = 26,
/// The register index for 24.
BAL_REGISTER_X24 = 24,
/// The register index for 27.
BAL_REGISTER_X27 = 27,
/// The register index for 25.
BAL_REGISTER_X25 = 25,
/// The register index for 28.
BAL_REGISTER_X28 = 28,
/// The register index for 26.
BAL_REGISTER_X26 = 26,
/// The register index for 29 (Frame Pointer).
BAL_REGISTER_X29 = 29,
/// The register index for 27.
BAL_REGISTER_X27 = 27,
/// The register index for 30 (Link Register).
BAL_REGISTER_X30 = 30,
/// The register index for 28.
BAL_REGISTER_X28 = 28,
/// The register index for the Zero Register (XZR)/Stack Pointer (SP).
BAL_REGISTER_X31 = 31,
} bal_register_index_t;
/// The register index for 29 (Frame Pointer).
BAL_REGISTER_X29 = 29,
/// The register index for 30 (Link Register).
BAL_REGISTER_X30 = 30,
/// The register index for the Zero Register (XZR)/Stack Pointer (SP).
BAL_REGISTER_X31 = 31,
} bal_register_index_t;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_TYPES_H */
+21 -8
View File
@@ -3,19 +3,32 @@
* Generated with tools/generate_a64_table.py
*/
#ifndef BAL_DECODER_TABLE_GENERATED
#define BAL_DECODER_TABLE_GENERATED
#include "bal_decoder.h"
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#define BAL_DECODER_ARM64_INSTRUCTIONS_SIZE 3608
typedef struct {
uint16_t index;
uint8_t count;
} decoder_bucket_t;
typedef struct
{
uint16_t index;
uint8_t count;
} decoder_bucket_t;
extern const bal_decoder_instruction_metadata_t g_bal_decoder_arm64_instructions[BAL_DECODER_ARM64_INSTRUCTIONS_SIZE];
extern const decoder_bucket_t g_decoder_lookup_table[2048];
extern const bal_decoder_instruction_metadata_t
g_bal_decoder_arm64_instructions[BAL_DECODER_ARM64_INSTRUCTIONS_SIZE];
extern const decoder_bucket_t g_decoder_lookup_table[2048];
extern const bal_decoder_instruction_metadata_t *const g_decoder_hash_candidates[];
extern const bal_decoder_instruction_metadata_t *const g_decoder_hash_candidates[];
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BAL_DECODER_TABLE_GENERATED */
/*** end of file ***/
+24 -17
View File
@@ -10,17 +10,17 @@ Generated ARM decoder table header file -> ../src/decoder_table_gen.h
Generated ARM decoder table source file -> ../src/decoder_table_gen.c
"""
import argparse
import glob
import io
import os
import re
import sys
import glob
import argparse
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import ElementTree
from xml.etree.ElementTree import Element
from dataclasses import dataclass
from typing import List, Dict, Tuple, Optional
from xml.etree.ElementTree import Element
from xml.etree.ElementTree import ElementTree
DEFAULT_DECODER_GENERATED_HEADER_NAME = "bal_decoder_table_gen.h"
DEFAULT_DECODER_GENERATED_SOURCE_NAME = "bal_decoder_table_gen.c"
@@ -182,7 +182,7 @@ def derive_operand_type(text: str, hover: str) -> str:
h = hover.lower()
# Immediate checks
if any(
k in h for k in ["immediate", "amount", "offset", "index", "label", "shift", "option"]
k in h for k in ["immediate", "amount", "offset", "index", "label", "shift", "option"]
) or t.startswith("#"):
return "BAL_OPERAND_TYPE_IMMEDIATE"
@@ -227,9 +227,9 @@ def derive_operand_type(text: str, hover: str) -> str:
def parse_operands(
asmtemplate: ET.Element,
field_map: Dict[str, Tuple[int, int]],
explanation_map: Dict[str, str],
asmtemplate: ET.Element,
field_map: Dict[str, Tuple[int, int]],
explanation_map: Dict[str, str],
) -> List[Operand]:
"""
Parses `asmtemplate` to find operands and map them to bit fields.
@@ -266,8 +266,6 @@ def parse_operands(
if operand_type == "BAL_OPERAND_TYPE_NONE":
continue
operand: Operand = Operand(operand_type, bit_position, bit_width)
if operand not in operands:
@@ -454,7 +452,7 @@ def derive_opcode(mnemonic: str) -> str:
def generate_hash_table(
instructions: List[A64Instruction],
instructions: List[A64Instruction],
) -> Dict[int, List[A64Instruction]]:
buckets: Dict[int, List[A64Instruction]] = {
i: [] for i in range(DECODER_HASH_TABLE_SIZE)
@@ -569,9 +567,14 @@ if __name__ == "__main__":
# -------------------------------------------------------------------------
with open(output_header_path, "w", encoding="utf-8") as f:
f.write(f"{GENERATED_FILE_WARNING}\n\n")
f.write("#ifndef BAL_DECODER_TABLE_GENERATED\n")
f.write("#define BAL_DECODER_TABLE_GENERATED\n\n")
f.write(f'#include "{DECODER_HEADER_NAME}"\n')
f.write("#include <stdint.h>\n")
f.write("#include <stddef.h>\n\n")
f.write("#ifdef __cplusplus\n")
f.write('extern "C"\n')
f.write("{\n")
f.write("#endif /* __cplusplus */\n\n")
f.write(
f"#define {DECODER_ARM64_INSTRUCTIONS_SIZE_NAME} {len(all_instructions)}\n\n"
)
@@ -583,11 +586,16 @@ if __name__ == "__main__":
f"extern const {DECODER_METADATA_STRUCT_NAME} {DECODER_ARM64_INSTRUCTIONS_ARRAY_NAME}[{DECODER_ARM64_INSTRUCTIONS_SIZE_NAME}];\n"
)
f.write(
f"extern const {DECODER_HASH_TABLE_BUCKET_STRUCT_NAME} g_decoder_lookup_table[{DECODER_HASH_TABLE_SIZE}];\n\n"
f"extern const {DECODER_HASH_TABLE_BUCKET_STRUCT_NAME} g_decoder_lookup_table[{DECODER_HASH_TABLE_SIZE}];\n"
)
f.write(
f"extern const {DECODER_METADATA_STRUCT_NAME} *const {DECODER_ARM64_CANDIDATES_ARRAY_NAME}[];\n\n"
f"extern const {DECODER_METADATA_STRUCT_NAME} *const {DECODER_ARM64_CANDIDATES_ARRAY_NAME}[];\n\n"
)
f.write("#ifdef __cplusplus\n")
f.write("}\n")
f.write("#endif /* __cplusplus */\n")
f.write("#endif /* BAL_DECODER_TABLE_GENERATED */\n\n")
f.write("/*** end of file ***/\n\n")
print(f"Generated ARM decoder table header file -> {output_header_path}")
# -------------------------------------------------------------------------
@@ -605,7 +613,7 @@ if __name__ == "__main__":
# (start_index, count)
bucket_descriptors: List[Tuple[int, int]] = []
for i in range (DECODER_HASH_TABLE_SIZE):
for i in range(DECODER_HASH_TABLE_SIZE):
candidate: List[A64Instruction] = buckets[i]
count: int = len(candidate)
@@ -618,7 +626,7 @@ if __name__ == "__main__":
if candidate_tuple in sequence_map:
# Re-use existing sequence[candidate_tuple]
start_index: int = sequence_map[candidate_tuple]
start_index: int = sequence_map[candidate_tuple]
else:
# Create new sequence
start_index: int = len(flat_candidates)
@@ -631,7 +639,6 @@ if __name__ == "__main__":
bucket_descriptors.append((start_index, count))
if len(flat_candidates) >= 65535:
printf("Total candidates {len(flat_candidates)} exceeds uint16_t limit.")
sys.exit(1)