Affix
view release on metacpan or search on metacpan
infix/src/common/infix_internals.h view on Meta::CPAN
* `arg_types`) is a deep copy stored in the trampoline's private `arena`,
* ensuring its lifetime is independent of the types used to create it.
*/
struct infix_forward_t {
infix_arena_t * arena; /**< Private or shared arena holding all type metadata for this trampoline. */
bool is_external_arena; /**< True if the arena is user-provided and should not be freed by `infix_forward_destroy`.
*/
infix_executable_t exec; /**< The executable memory containing the JIT-compiled code. */
infix_type * return_type; /**< A deep copy of the function's return type. */
infix_type ** arg_types; /**< A deep copy of the function's argument types. */
size_t num_args; /**< The total number of arguments. */
size_t num_fixed_args; /**< The number of non-variadic arguments. */
void * target_fn; /**< The target C function pointer (for bound trampolines), or `nullptr` for unbound. */
bool is_direct_trampoline; /**< If true, this is a high-performance direct marshalling trampoline. */
bool is_safe; /**< If true, the trampoline wraps the call in an exception handler. */
size_t ref_count; /**< Reference count for deduplication and shared ownership. */
char * signature; /**< The normalized signature string used to create this trampoline. */
};
/**
* @brief A function pointer to the universal C dispatcher for reverse calls.
* @details This is the C function that the JIT-compiled reverse trampoline stub calls
* after marshalling all arguments into a standard C format.
*/
typedef void (*infix_internal_dispatch_callback_fn)(infix_reverse_t *, void *, void **);
/**
* @struct infix_reverse_t
* @brief Internal definition of a reverse trampoline (callback/closure) handle.
* @details This is the concrete implementation of the opaque `infix_reverse_t` pointer.
* The entire struct is allocated in a page-aligned memory region that is made read-only
* after initialization to prevent memory corruption vulnerabilities. Like the forward
* trampoline, it is self-contained and owns deep copies of all its type metadata.
*/
struct infix_reverse_t {
infix_arena_t * arena; /**< Private arena for type metadata. */
infix_executable_t exec; /**< Executable memory for the JIT stub. */
infix_protected_t protected_ctx; /**< The read-only memory region holding this struct. */
infix_type * return_type; /**< Deep copy of the function's return type. */
infix_type ** arg_types; /**< Deep copy of the function's argument types. */
size_t num_args; /**< Total number of arguments. */
size_t num_fixed_args; /**< Number of non-variadic arguments. */
bool is_variadic; /**< `true` if the signature contains variadic arguments. */
void * user_callback_fn; /**< The user-provided handler function pointer (type-safe or generic). */
void * user_data; /**< The user-provided context pointer for closures. */
infix_internal_dispatch_callback_fn
internal_dispatcher; /**< Pointer to the universal C dispatcher implementation. */
infix_forward_t *
cached_forward_trampoline; /**< For type-safe callbacks, a pre-generated trampoline to call the C handler. */
};
/**
* @struct infix_arena_t
* @brief Internal definition of a memory arena.
* @details An arena is a fast, region-based allocator. It pre-allocates a single
* block of memory and serves subsequent small allocation requests by simply
* "bumping" a pointer. All memory allocated from an arena is freed at once by
* destroying the arena itself, eliminating the need to track individual allocations.
*/
struct infix_arena_t {
char * buffer; /**< The backing memory buffer for the arena. */
size_t capacity; /**< The total size of the buffer. */
size_t current_offset; /**< The current high-water mark of allocation. */
bool error; /**< A flag set if any allocation fails, preventing subsequent allocations. */
struct infix_arena_t * next_block; /**< A pointer to the next block in the chain, if this one is full. */
size_t block_size; /**< The size of this specific block's buffer, for chained arenas. */
};
// Mutex Abstraction for Internal Synchronization
#if defined(INFIX_OS_WINDOWS)
#include <windows.h>
typedef SRWLOCK infix_mutex_t;
#define INFIX_MUTEX_INITIALIZER SRWLOCK_INIT
#define INFIX_MUTEX_LOCK(m) AcquireSRWLockExclusive(m)
#define INFIX_MUTEX_UNLOCK(m) ReleaseSRWLockExclusive(m)
#define INFIX_MUTEX_DESTROY(m) ((void)0)
#else
#include <pthread.h>
typedef pthread_mutex_t infix_mutex_t;
#define INFIX_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
#define INFIX_MUTEX_LOCK(m) pthread_mutex_lock(m)
#define INFIX_MUTEX_UNLOCK(m) pthread_mutex_unlock(m)
#define INFIX_MUTEX_DESTROY(m) pthread_mutex_destroy(m)
#endif
/**
* @struct _infix_registry_entry_t
* @brief A single entry in the registry's hash table.
* @details This is a node in a singly-linked list used for chaining in the
* event of a hash collision.
*/
typedef struct _infix_registry_entry_t {
const char * name; /**< The registered name of the type. */
uint64_t hash; /**< The pre-calculated djb2 hash of the name. */
infix_type * type; /**< A pointer to the canonical `infix_type` object. */
bool is_forward_declaration; /**< `true` if this is just a forward declaration (`@Name;`). */
struct _infix_registry_entry_t * next; /**< The next entry in the hash bucket chain. */
} _infix_registry_entry_t;
/**
* @struct infix_registry_t
* @brief Internal definition of a named type registry.
* @details Implemented as a hash table with separate chaining for collision resolution.
* The canonical `infix_type` objects and entry metadata are owned by a single arena,
* while the internal bucket array is heap-allocated to allow for efficient resizing.
*/
struct infix_registry_t {
infix_arena_t * arena; /**< The arena that owns all type metadata and entry structs. */
bool is_external_arena; /**< True if the arena is user-provided and should not be freed. */
size_t num_buckets; /**< The number of buckets in the hash table. */
size_t num_items; /**< The total number of items in the registry. */
_infix_registry_entry_t ** buckets; /**< The array of hash table buckets (heap-allocated). */
};
/**
* @struct parser_state
* @brief Holds the complete state of the recursive descent parser during a single parse operation.
*/
typedef struct {
const char * p; /**< The current read position (cursor) in the signature string. */
const char * start; /**< The beginning of the signature string, used for calculating error positions. */
infix_arena_t * arena; /**< The temporary arena for allocating the raw, unresolved type graph. */
int depth; /**< The current recursion depth, to prevent stack overflows. */
} parser_state;
/**
* @struct code_buffer
* @brief A dynamic buffer for staged machine code generation.
*/
typedef struct {
uint8_t * code; /**< A pointer to the code buffer, allocated from the arena. */
size_t capacity; /**< The current capacity of the buffer. */
size_t size; /**< The number of bytes currently written to the buffer. */
bool error; /**< A flag set on allocation failure. */
infix_arena_t * arena; /**< The temporary arena used for code generation. */
} code_buffer;
/**
* @struct infix_library_t
* @brief Internal definition of a dynamic library handle.
* @details This is a simple wrapper around the platform's native library handle to provide a consistent API.
*
* On Windows, GetModuleHandle(NULL) returns a special handle to the main executable that must NOT be freed with
* FreeLibrary. This flag tracks that state to ensure infix_library_close behaves correctly.
*/
struct infix_library_t {
void * handle; /**< The platform-native library handle (`HMODULE` on Windows, `void*` on POSIX). */
#if defined(INFIX_OS_WINDOWS)
bool is_pseudo_handle; /**< True if the handle is a "pseudo-handle" from GetModuleHandle. */
#endif
};
// ABI Abstraction Layer
/**
* @def INFIX_MAX_STACK_ALLOC
* @brief A safety limit (4MB) for the total stack space a trampoline can allocate.
* This prevents stack exhaustion from malformed or malicious type layouts.
*/
#define INFIX_MAX_STACK_ALLOC (1024 * 1024 * 4)
/**
* @def INFIX_MAX_ARG_SIZE
* @brief A safety limit (64KB) for the size of a single argument.
*/
#define INFIX_MAX_ARG_SIZE (1024 * 64)
/**
* @enum infix_arg_location_type
* @brief Describes the physical location where a function argument is passed according to the ABI.
*
* This enumeration abstracts away the differences in how various ABIs use
* registers and the stack to pass data. It is the primary output of the ABI
* classification process.
*/
typedef enum {
/** @brief Argument is passed in a general-purpose integer register (e.g., `RCX`, `RDI`, `X0`). */
ARG_LOCATION_GPR,
#if defined(INFIX_ABI_AAPCS64)
/** @brief (AArch64) Argument is passed in a vector/floating-point register (e.g., `V0`). */
ARG_LOCATION_VPR,
/** @brief (AArch64) A struct <= 16 bytes passed in a pair of GPRs (e.g., `X0`, `X1`). */
ARG_LOCATION_GPR_PAIR,
/** @brief (AArch64) A large struct (> 16 bytes) passed by reference; the pointer is in a GPR. */
ARG_LOCATION_GPR_REFERENCE,
/** @brief (AArch64) A Homogeneous Floating-point Aggregate passed in consecutive VPRs. */
ARG_LOCATION_VPR_HFA,
#else // x64 ABIs
/** @brief (x64) Argument is passed in an SSE/XMM register (e.g., `XMM0`). */
ARG_LOCATION_XMM,
/** @brief (SysV x64) A struct passed in two GPRs (e.g., `RDI`, `RSI`). */
ARG_LOCATION_GPR_PAIR,
/** @brief (SysV x64) A struct passed in two SSE registers (e.g., `XMM0`, `XMM1`). */
ARG_LOCATION_SSE_SSE_PAIR,
/** @brief (SysV x64) A struct split between a GPR and an SSE register. */
ARG_LOCATION_INTEGER_SSE_PAIR,
/** @brief (SysV x64) A struct split between an SSE and a GPR register. */
ARG_LOCATION_SSE_INTEGER_PAIR,
#endif
infix/src/common/infix_internals.h view on Meta::CPAN
* @struct infix_direct_arg_layout
* @brief Internal layout information for a single argument in a direct marshalling trampoline.
*
* This struct combines the ABI location information with pointers to the type and
* handler information needed by the JIT emitters.
*/
typedef struct {
infix_arg_location location; ///< The physical location (register/stack) of the argument.
const infix_type * type; ///< The `infix_type` of this argument.
const infix_direct_arg_handler_t * handler; ///< Pointer to the user-provided handler struct for this argument.
} infix_direct_arg_layout;
/**
* @struct infix_direct_call_frame_layout
* @brief A complete layout blueprint for a direct marshalling forward call frame.
*
* This structure serves as the plan for the JIT engine, detailing every register,
* stack slot, and marshaller/write-back call needed to execute a direct FFI call.
*/
typedef struct {
size_t total_stack_alloc; ///< Total bytes to allocate on the stack for arguments and ABI-required space.
size_t num_args; ///< The total number of arguments.
void * target_fn; ///< The target C function address.
bool return_value_in_memory; ///< `true` if the return value uses a hidden pointer argument.
infix_direct_arg_layout * args; ///< An array of layout info for each argument.
uint32_t prologue_size; ///< Size of the generated prologue in bytes.
uint32_t epilogue_offset; ///< Offset from the start of the JIT block to the epilogue.
} infix_direct_call_frame_layout;
/**
* @brief Defines the ABI-specific implementation interface for direct marshalling forward trampolines.
*
* This v-table defines the contract for generating a high-performance, direct-marshalling
* trampoline. It is parallel to `infix_forward_abi_spec`.
*/
typedef struct {
/** @brief Analyzes a function signature to create a complete direct call frame layout. */
infix_status (*prepare_direct_forward_call_frame)(infix_arena_t * arena,
infix_direct_call_frame_layout ** out_layout,
infix_type * ret_type,
infix_type ** arg_types,
size_t num_args,
infix_direct_arg_handler_t * handlers,
void * target_fn);
/** @brief Generates the function prologue (stack setup, saving registers). */
infix_status (*generate_direct_forward_prologue)(code_buffer * buf, infix_direct_call_frame_layout * layout);
/** @brief Generates code to call marshallers and move arguments into their native locations. */
infix_status (*generate_direct_forward_argument_moves)(code_buffer * buf, infix_direct_call_frame_layout * layout);
/** @brief Generates the `call` instruction to the target function. */
infix_status (*generate_direct_forward_call_instruction)(code_buffer * buf,
infix_direct_call_frame_layout * layout);
/** @brief Generates the function epilogue (handling return value, calling write-back handlers, returning). */
infix_status (*generate_direct_forward_epilogue)(code_buffer * buf,
infix_direct_call_frame_layout * layout,
infix_type * ret_type);
} infix_direct_forward_abi_spec;
// Internal Function Prototypes (Shared across modules)
/**
* @brief Sets the thread-local error state with detailed information.
* @details Located in `src/core/error.c`, this function is the primary mechanism
* for reporting errors from within the library. It populates the thread-local
* `g_infix_last_error` struct. For parser errors, it generates a rich diagnostic
* message with a code snippet.
* @param category The general category of the error.
* @param code The specific error code.
* @param position For parser errors, the byte offset into the signature string where the error occurred.
*/
INFIX_INTERNAL void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position);
/**
* @brief Sets the thread-local error state for a system-level error.
* @details Located in `src/core/error.c`, this is used for errors originating from
* the operating system, such as `dlopen` or `mmap` failures.
* @param category The general category of the error.
* @param code The `infix` error code that corresponds to the failure.
* @param system_code The OS-specific error code (e.g., from `errno` or `GetLastError`).
* @param msg An optional custom message from the OS (e.g., from `dlerror`).
*/
INFIX_INTERNAL void _infix_set_system_error(infix_error_category_t category,
infix_error_code_t code,
long system_code,
const char * msg);
/**
* @brief Clears the thread-local error state.
* @details Located in `src/core/error.c`. This is called at the beginning of every public
* API function to ensure that a prior error from an unrelated call is not accidentally returned.
*/
INFIX_INTERNAL void _infix_clear_error(void);
INFIX_INTERNAL void skip_whitespace(parser_state * state);
INFIX_INTERNAL void _infix_set_parser_error(parser_state * state, infix_error_code_t code);
INFIX_INTERNAL infix_type * parse_type(parser_state * state);
INFIX_INTERNAL infix_type * parse_primitive(parser_state * state);
/**
* @brief Recalculates the layout of a fully resolved type graph.
* @details Located in `src/core/types.c`. This is the "Layout" stage of the data pipeline.
* It recursively walks a type graph and computes the final `size`, `alignment`, and
* member `offset` fields for all aggregate types. It must only be called on a fully
* resolved graph.
* @param[in,out] type The root of the type graph to recalculate. The graph is modified in-place.
*/
INFIX_INTERNAL void _infix_type_recalculate_layout(infix_type * type);
/**
* @brief Resolves all named type references in a type graph in-place.
* @details Located in `src/core/type_registry.c`. This is the "Resolve" stage of the
* data pipeline. It traverses a type graph and replaces all `INFIX_TYPE_NAMED_REFERENCE`
* nodes (`@Name`) with direct pointers to the canonical `infix_type` objects from the registry.
* @param[in,out] type_ptr A pointer to the root of the type graph to resolve. The pointer may be changed.
* @param[in] registry The registry to use for lookups.
* @return `INFIX_SUCCESS` on success, or an error if a name cannot be resolved.
*/
INFIX_INTERNAL c23_nodiscard infix_status _infix_resolve_type_graph_inplace(infix_type ** type_ptr,
infix_registry_t * registry);
/**
* @brief The internal core of the signature parser.
* @details Located in `src/core/signature.c`. This is the "Parse" stage of the data pipeline.
* It takes a signature string and produces a raw, unresolved `infix_type` graph in a new,
* temporary arena. It does not perform any copying, resolution, or layout calculation.
* @param[out] out_type On success, receives the parsed type graph.
* @param[out] out_arena On success, receives the temporary arena holding the graph.
* @param[in] signature The signature string to parse.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_INTERNAL c23_nodiscard infix_status _infix_parse_type_internal(infix_type **, infix_arena_t **, const char *);
/**
* @brief An internal-only function to serialize a type's body without its registered name.
* @details Located in `src/core/signature.c`. Unlike `infix_type_print`, which will
* print `@Name` for a named struct, this function will always print the full `{...}`
* body. This is essential for `infix_registry_print` to function correctly.
* @param[out] buffer The output buffer.
* @param[in] buffer_size The size of the buffer.
* @param[in] type The type to print.
* @param[in] dialect The output format dialect.
* @return `INFIX_SUCCESS` on success.
*/
INFIX_INTERNAL c23_nodiscard infix_status _infix_type_print_body_only(char *,
size_t,
const infix_type *,
infix_print_dialect_t);
/**
* @brief Performs a deep copy of a type graph into a destination arena.
* @details Located in `src/core/types.c`. This is the "Copy" stage of the data pipeline,
* crucial for creating self-contained trampoline objects and ensuring memory safety. It uses
* memoization to correctly handle cycles and shared type objects.
* @param[in] dest_arena The destination arena for the new type graph.
* @param[in] src_type The source type graph to copy.
* @return A pointer to the newly created copy in `dest_arena`, or `nullptr` on failure.
*/
INFIX_INTERNAL infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *);
/**
* @brief Estimates the total memory required to deep-copy a complete type graph.
* @details Located in `src/core/types.c`. This function recursively walks the entire
* type graph, including all nested aggregates and function arguments, to calculate
* the exact size needed for an arena that will hold a deep copy.
* @param[in] temp_arena A temporary arena used for the estimator's own bookkeeping (e.g., cycle detection).
* @param[in] type The root of the type graph to estimate.
* @return The estimated size in bytes required for a deep copy.
*/
INFIX_INTERNAL size_t _infix_estimate_graph_size(infix_arena_t * temp_arena, const infix_type * type);
/**
* @brief Gets the ABI v-table for forward calls for the current platform.
* @details See `src/jit/trampoline.c`. This function is the entry point to the ABI
* abstraction layer, returning the correct set of function pointers based on the
* compile-time ABI detection.
* @return A pointer to the active `infix_forward_abi_spec`.
*/
INFIX_INTERNAL const infix_forward_abi_spec * get_current_forward_abi_spec(void);
/**
* @brief Gets the ABI v-table for reverse calls for the current platform.
* @details See `src/jit/trampoline.c`. This function mirrors `get_current_forward_abi_spec`
( run in 0.592 second using v1.01-cache-2.11-cpan-39bf76dae61 )