Affix

 view release on metacpan or  search on metacpan

CODE_OF_CONDUCT.md  view on Meta::CPAN

diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment

CODE_OF_CONDUCT.md  view on Meta::CPAN

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels

Changes.md  view on Meta::CPAN

# Changelog

All notable changes to Affix.pm will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.0.2] - 2025-12-14

### Changed

    - In an attempt to debug mystery failures in SDL3.pm, Affix.pm will warn and return `undef` instead of `croak`ing.
    - Improved error reporting: if the internal error message is empty, the numeric error code is now included in the warning.

### Fixed

LICENSE  view on Meta::CPAN

modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.

(11)  If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.

(12)  This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.

(13)  This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.

builder/Affix/Builder.pm  view on Meta::CPAN

        if    ( $cc_cmd =~ /cl(\.exe)?$/i ) { $cc_type = 'msvc'; }
        elsif ( $cc_cmd =~ /clang/i )       { $cc_type = 'clang'; }
        elsif ( $cc_cmd =~ /gcc/i )         { $cc_type = 'gcc'; }
        elsif ( $cc_cmd =~ /egcc/i )        { $cc_type = 'gcc'; }

        # 2. Setup Flags
        my ( $ar_cmd, @cflags, @arflags, $out_flag_cc, $out_flag_ar );
        my @includes = map { ( $cc_type eq 'msvc' ? '/I' : '-I' ) . $_ } @include_dirs;
        if ( $cc_type eq 'msvc' ) {
            $ar_cmd      = 'lib';
            @cflags      = ( '/nologo', '/c', '/std:c11', '/W3', '/GS', '/MD', '/O2', @includes );
            @cflags      = ( @cflags, '/DINFIX_DEBUG_ENABLED=1' ) if $verbose;
            @arflags     = ('/nologo');
            $out_flag_cc = '/Fo';
            $out_flag_ar = '/OUT:';
        }
        else {
            # GCC / Clang
            $ar_cmd      = 'ar';
            @cflags      = ( '-std=c11', '-Wall', '-Wextra', '-O2', '-fPIC', @includes );
            @cflags      = ( @cflags, '-DINFIX_DEBUG_ENABLED=1' ) if $verbose;
            @arflags     = ('rcs');
            $out_flag_cc = '-o';

builder/Affix/Builder.pm  view on Meta::CPAN

                $obj;
        }

        # Point to the Architecture-specific build lib
        my $infix_build_lib = cwd->absolute->child('infix')->child( 'build_lib', $Config{archname} )->stringify;

        # Check for -lrt requirement
        my $lrt_flag = $self->check_for_lrt();
        my $data     = {

            # Removed incorrect -lstdc++ logic. Added -lm for math.
            # -pthread is already in $ldflags via ADJUST
            extra_linker_flags => ( $ldflags . ' -L' . $infix_build_lib . ' -linfix ' . $lrt_flag . ' -lm' ),
            objects            => [@objs],
            lib_file           => $lib_file,
            module_name        => join '::',
            @parts
        };
        return $builder->link(%$data);
    }
    };

infix/LICENSE-A2  view on Meta::CPAN

modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.

(11)  If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.

(12)  This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.

(13)  This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.

infix/LICENSE-CC  view on Meta::CPAN

     accordance with the terms and conditions of this Public License.

  c. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  d. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  e. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  f. Licensed Material means the artistic or literary work, database,

infix/LICENSE-CC  view on Meta::CPAN

          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

infix/LICENSE-CC  view on Meta::CPAN


Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.

infix/include/infix/infix.h  view on Meta::CPAN

 * @brief APIs for inspecting and serializing the contents of a named type registry.
 * @ingroup high_level_api
 * @{
 */
/** @brief An opaque handle to a registry iterator. Created by `infix_registry_iterator_begin`. */
typedef struct infix_registry_iterator_t infix_registry_iterator_t;
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small.
 */
c23_nodiscard infix_status infix_registry_print(char *, size_t, const infix_registry_t *);
/**
 * @brief Initializes an iterator for traversing the types in a registry.

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file abi_arm64.c
 * @brief Implements the FFI logic for the AArch64 (ARM64) architecture.
 * @ingroup internal_abi_aarch64
 *
 * @internal
 * This file provides the concrete implementation of the `infix_forward_abi_spec`
 * and `infix_reverse_abi_spec` for the ARM64 architecture. It primarily follows
 * the standard "Procedure Call Standard for the ARM 64-bit Architecture" (AAPCS64),
 * but also contains critical conditional logic to handle deviations for specific
 * platforms like Apple macOS and Windows on ARM.
 *
 * @section aapcs64_rules Key AAPCS64 Rules Implemented
 *
 * - **Register Usage:**
 *   - The first 8 integer/pointer arguments are passed in GPRs (X0-X7).
 *   - The first 8 floating-point/vector arguments are passed in VPRs (V0-V7).
 *
 * - **Homogeneous Floating-point Aggregates (HFAs):** Structs or arrays composed
 *   entirely of 1 to 4 identical floating-point types (`float` or `double`) are

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

static bool is_hfa(const infix_type * type, const infix_type ** base_type);

/** @internal The v-table of AArch64 functions for generating forward trampolines. */
static infix_status prepare_forward_call_frame_arm64(infix_arena_t * arena,
                                                     infix_call_frame_layout ** out_layout,
                                                     infix_type * ret_type,
                                                     infix_type ** arg_types,
                                                     size_t num_args,
                                                     size_t num_fixed_args,
                                                     void * target_fn);
static infix_status generate_forward_prologue_arm64(code_buffer * buf, infix_call_frame_layout * layout);
static infix_status generate_forward_argument_moves_arm64(code_buffer * buf,
                                                          infix_call_frame_layout * layout,
                                                          infix_type ** arg_types,
                                                          size_t num_args,
                                                          c23_maybe_unused size_t num_fixed_args);
static infix_status generate_forward_call_instruction_arm64(code_buffer *, infix_call_frame_layout *);
static infix_status generate_forward_epilogue_arm64(code_buffer * buf,
                                                    infix_call_frame_layout * layout,
                                                    infix_type * ret_type);
const infix_forward_abi_spec g_arm64_forward_spec = {
    .prepare_forward_call_frame = prepare_forward_call_frame_arm64,
    .generate_forward_prologue = generate_forward_prologue_arm64,
    .generate_forward_argument_moves = generate_forward_argument_moves_arm64,
    .generate_forward_call_instruction = generate_forward_call_instruction_arm64,
    .generate_forward_epilogue = generate_forward_epilogue_arm64};

/** @internal The v-table of AArch64 functions for generating reverse trampolines. */
static infix_status prepare_reverse_call_frame_arm64(infix_arena_t * arena,
                                                     infix_reverse_call_frame_layout ** out_layout,
                                                     infix_reverse_t * context);
static infix_status generate_reverse_prologue_arm64(code_buffer * buf, infix_reverse_call_frame_layout * layout);
static infix_status generate_reverse_argument_marshalling_arm64(code_buffer * buf,
                                                                infix_reverse_call_frame_layout * layout,
                                                                infix_reverse_t * context);
static infix_status generate_reverse_dispatcher_call_arm64(code_buffer * buf,
                                                           infix_reverse_call_frame_layout * layout,
                                                           infix_reverse_t * context);
static infix_status generate_reverse_epilogue_arm64(code_buffer * buf,
                                                    infix_reverse_call_frame_layout * layout,
                                                    infix_reverse_t * context);
const infix_reverse_abi_spec g_arm64_reverse_spec = {
    .prepare_reverse_call_frame = prepare_reverse_call_frame_arm64,
    .generate_reverse_prologue = generate_reverse_prologue_arm64,
    .generate_reverse_argument_marshalling = generate_reverse_argument_marshalling_arm64,
    .generate_reverse_dispatcher_call = generate_reverse_dispatcher_call_arm64,
    .generate_reverse_epilogue = generate_reverse_epilogue_arm64};

/** @internal The v-table for the new Direct Marshalling ABI. */
static infix_status prepare_direct_forward_call_frame_arm64(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);
static infix_status generate_direct_forward_prologue_arm64(code_buffer * buf, infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_argument_moves_arm64(code_buffer * buf,
                                                                 infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_call_instruction_arm64(code_buffer * buf,
                                                                   infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_epilogue_arm64(code_buffer * buf,
                                                           infix_direct_call_frame_layout * layout,
                                                           infix_type * ret_type);
const infix_direct_forward_abi_spec g_arm64_direct_forward_spec = {
    .prepare_direct_forward_call_frame = prepare_direct_forward_call_frame_arm64,
    .generate_direct_forward_prologue = generate_direct_forward_prologue_arm64,
    .generate_direct_forward_argument_moves = generate_direct_forward_argument_moves_arm64,
    .generate_direct_forward_call_instruction = generate_direct_forward_call_instruction_arm64,
    .generate_direct_forward_epilogue = generate_direct_forward_epilogue_arm64};

/**
 * @internal
 * @brief Recursively finds the first primitive floating-point type in a potential HFA.
 * @details This function performs a depth-first search to find the very first `float`
 *          or `double` primitive within an aggregate. This becomes the candidate
 *          "base type" that all other members of the aggregate will be compared against.
 * @param type The type to search within.
 * @return A pointer to the `infix_type` of the base element, or `nullptr` if not found.
 */

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

    if (!is_hfa_recursive_check(type, base, &field_count))
        return false;
    if (out_base_type)
        *out_base_type = base;
    return true;
}
/**
 * @internal
 * @brief Stage 1 (Forward): Analyzes a signature and creates a call frame layout for AAPCS64.
 * @details This function assigns each argument to a location (GPR, VPR, or Stack) according
 *          to the AAPCS64 rules. It contains extensive conditional logic to handle ABI
 *          deviations on Apple and Windows platforms, especially for variadic arguments
 *          and 16-byte aggregate alignment.
 *
 * @param arena The temporary arena for allocations.
 * @param out_layout Receives the created layout blueprint.
 * @param ret_type The function's return type.
 * @param arg_types Array of argument types.
 * @param num_args Total number of arguments.
 * @param num_fixed_args Number of non-variadic arguments.
 * @param target_fn The target function address.

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

    // Security: Prevent excessive stack allocation.
    if (layout->total_stack_alloc > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Forward): Generates the function prologue for the AArch64 trampoline.
 * @details Sets up the stack frame by saving the frame pointer (X29) and link register (X30),
 *          saves callee-saved registers (X19-X22) that will be used to hold the trampoline's
 *          context, moves the trampoline's arguments into those preserved registers, and
 *          allocates the necessary stack space for stack-passed arguments.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_prologue_arm64(code_buffer * buf, infix_call_frame_layout * layout) {
    // `stp x29, x30, [sp, #-16]!` : Push Frame Pointer and Link Register to the stack, pre-decrementing SP.
    emit_arm64_stp_pre_index(buf, true, X29_FP_REG, X30_LR_REG, SP_REG, -16);
    // `mov x29, sp` : Establish the new Frame Pointer.
    emit_arm64_mov_reg(buf, true, X29_FP_REG, SP_REG);
    // `stp x19, x20, [sp, #-16]!` : Save callee-saved registers that we will use for our context.
    emit_arm64_stp_pre_index(buf, true, X19_REG, X20_REG, SP_REG, -16);
    // `stp x21, x22, [sp, #-16]!`
    emit_arm64_stp_pre_index(buf, true, X21_REG, X22_REG, SP_REG, -16);
    // Move the trampoline's own arguments into these now-safe callee-saved registers.
    if (layout->target_fn == nullptr) {  // Unbound trampoline args: (target_fn, ret_ptr, args_ptr) in X0, X1, X2.

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

 *          is null, a `BRK` instruction is executed to crash safely.
 * @param buf The code buffer.
 * @param layout The call frame layout.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_call_instruction_arm64(code_buffer * buf,
                                                            c23_maybe_unused infix_call_frame_layout * layout) {
    if (layout->target_fn)
        // For a bound trampoline, the target is hardcoded. Load it into X19.
        emit_arm64_load_u64_immediate(buf, X19_REG, (uint64_t)layout->target_fn);
    // For an unbound trampoline, X19 was already loaded from the first argument in the prologue.
    // `cbnz x19, #8` : If the target function pointer in x19 is not zero, branch 8 bytes forward.
    emit_arm64_cbnz(buf, true, X19_REG, 8);
    // `brk #0` : If the pointer was null, execute a breakpoint instruction to cause a deliberate crash.
    emit_arm64_brk(buf, 0);
    // `blr x19` : Branch with link to the target function address in x19.
    emit_arm64_blr_reg(buf, X19_REG);
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 4 (Forward): Generates the function epilogue.
 * @details Emits code to handle the return value (from X0/X1 or V0-V3), deallocates
 *          the stack frame, restores callee-saved registers, and returns to the caller.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @param ret_type The function's return type.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_epilogue_arm64(code_buffer * buf,
                                                    infix_call_frame_layout * layout,
                                                    infix_type * ret_type) {
    // If the function returns a value and it wasn't returned via hidden pointer...
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        // ...copy the result from the appropriate return register(s) into the user's return buffer (pointer in X20).
        const infix_type * hfa_base = nullptr;
        // The order of these checks is critical. Handle the most specific cases first.
        if (is_long_double(ret_type) || (ret_type->category == INFIX_TYPE_VECTOR && ret_type->size == 16))
            // On non-Apple AArch64, long double is 16 bytes and returned in V0.
            // On Apple, this case is never hit because types.c aliases it to a standard double.

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

        return INFIX_ERROR_LAYOUT_FAILED;
    }
    size_t total_local_space = return_size + args_array_size + saved_args_data_size;
    // The total stack allocation for the frame must be 16-byte aligned.
    if (total_local_space > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    layout->total_stack_alloc = (total_local_space + 15) & ~15;
    // Local variables are accessed via positive offsets from the stack pointer (SP)
    // after the initial `sub sp, sp, #alloc` in the prologue.
    // The layout on our local stack will be: [ return_buffer | args_array | saved_args_data ]
    layout->return_buffer_offset = 0;
    layout->args_array_offset = layout->return_buffer_offset + (int32_t)return_size;
    layout->saved_args_offset = layout->args_array_offset + (int32_t)args_array_size;
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Reverse): Generates the prologue for the reverse trampoline stub.
 * @details This function emits the standard AArch64 function entry code. It saves the
 *          caller's frame pointer (X29) and the link register (X30, the return address)
 *          to the stack, establishes a new frame by pointing X29 to the current stack
 *          pointer, and allocates the pre-calculated stack space for local variables.
 *
 * @param buf The code buffer to write to.
 * @param layout The blueprint containing the total stack space to allocate.
 * @return `INFIX_SUCCESS` on success.
 */
static infix_status generate_reverse_prologue_arm64(code_buffer * buf, infix_reverse_call_frame_layout * layout) {
    // `stp x29, x30, [sp, #-16]!` : Save Frame Pointer and Link Register, pre-decrementing SP.
    emit_arm64_stp_pre_index(buf, true, X29_FP_REG, X30_LR_REG, SP_REG, -16);
    // `mov x29, sp` : Establish the new frame pointer.
    emit_arm64_mov_reg(buf, true, X29_FP_REG, SP_REG);
    // `sub sp, sp, #total_stack_alloc` : Allocate space for our local variables.
    if (layout->total_stack_alloc > 0)
        emit_arm64_sub_imm(buf, true, false, SP_REG, SP_REG, (uint32_t)layout->total_stack_alloc);
    return INFIX_SUCCESS;
}
/**

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

    size_t gpr_idx = 0, vpr_idx = 0, current_saved_data_offset = 0;
    // Arguments passed on the caller's stack start at an offset from our new frame pointer.
    // [fp] points to old fp, [fp, #8] points to old lr. Args start at [fp, #16].
    size_t caller_stack_offset = 16;
    for (size_t i = 0; i < context->num_args; ++i) {
        infix_type * type = context->arg_types[i];
        bool is_variadic_arg = i >= context->num_fixed_args;
        int32_t arg_save_loc = (int32_t)(layout->saved_args_offset + current_saved_data_offset);
#if defined(INFIX_OS_MACOS)
        // On macOS, all variadic arguments are passed on the stack.
        // This is a special case that bypasses all register logic.
        if (is_variadic_arg) {
            // On macOS, variadic args smaller than 8 bytes are promoted to an 8-byte stack slot.
            // We must copy the entire slot, not just the type's smaller size.
            size_t size_on_stack = (type->size < 8) ? 8 : type->size;
            size_on_stack = (size_on_stack + 7) & ~7;  // Ensure it's a multiple of 8
            for (size_t offset = 0; offset < size_on_stack; offset += 8) {
                emit_arm64_ldr_imm(buf, true, X9_REG, X29_FP_REG, (int32_t)(caller_stack_offset + offset));
                int32_t dest_offset = arg_save_loc + (int32_t)offset;
                if (dest_offset >= 0 && ((unsigned)dest_offset / 8) <= 0xFFF && (dest_offset % 8 == 0))
                    emit_arm64_str_imm(buf, true, X9_REG, SP_REG, dest_offset);

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

            if (dest_offset >= 0 && ((unsigned)dest_offset / 8) <= 0xFFF && (dest_offset % 8 == 0))
                emit_arm64_str_imm(buf, true, X9_REG, SP_REG, dest_offset);
            else {
                emit_arm64_add_imm(buf, true, false, X10_REG, SP_REG, dest_offset);
                emit_arm64_str_imm(buf, true, X9_REG, X10_REG, 0);
            }
            current_saved_data_offset += (type->size + 15) & ~15;
            continue;  // Skip to the next argument
        }
#endif
        // Standard logic for non-macOS or non-variadic arguments
        bool is_pass_by_ref = (type->size > 16) && !is_variadic_arg;
        bool is_from_stack = false;
        // Determine if the argument is expected in a VPR based on type and platform.
        bool expect_in_vpr = is_float(type) || is_double(type) || is_long_double(type);
#if defined(INFIX_OS_WINDOWS)
        if (context->is_variadic)
            expect_in_vpr = false;
#endif
        if (is_pass_by_ref) {
            int32_t dest_offset = layout->args_array_offset + (int32_t)(i * sizeof(void *));

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

        emit_arm64_add_imm(buf, true, false, X1_REG, SP_REG, (uint32_t)layout->return_buffer_offset);
    // Arg 3: Load pointer to args_array into X2.
    emit_arm64_add_imm(buf, true, false, X2_REG, SP_REG, (uint32_t)layout->args_array_offset);
    // Load the C dispatcher's address into a scratch register (X9) and call it.
    emit_arm64_load_u64_immediate(buf, X9_REG, (uint64_t)context->internal_dispatcher);
    emit_arm64_blr_reg(buf, X9_REG);  // blr x9
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 5 (Reverse): Generates the epilogue for the reverse trampoline stub.
 * @details After the C dispatcher returns, this code retrieves the return value from the
 *          return buffer on the stub's local stack and places it into the correct native return
 *          registers (X0, X1, V0, etc.) as required by the AAPCS64. It then tears down the
 *          stack frame and returns control to the native caller.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @param context The reverse context.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_reverse_epilogue_arm64(code_buffer * buf,
                                                    infix_reverse_call_frame_layout * layout,
                                                    infix_reverse_t * context) {
    bool ret_is_aggregate =
        (context->return_type->category == INFIX_TYPE_STRUCT || context->return_type->category == INFIX_TYPE_UNION ||
         context->return_type->category == INFIX_TYPE_ARRAY || context->return_type->category == INFIX_TYPE_COMPLEX);
    bool return_in_memory = ret_is_aggregate && context->return_type->size > 16;
    if (context->return_type->category != INFIX_TYPE_VOID && !return_in_memory) {
        const infix_type * base = nullptr;
        if (is_hfa(context->return_type, &base)) {
            size_t num_elements = context->return_type->size / base->size;

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

 * @internal
 * @brief Stage 1 (Direct): Analyzes a signature and creates a call frame layout for AAPCS64.
 */
static infix_status prepare_direct_forward_call_frame_arm64(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) {
    // 1. Reuse the standard classification logic.
    infix_call_frame_layout * standard_layout = nullptr;
    infix_status status =
        prepare_forward_call_frame_arm64(arena, &standard_layout, ret_type, arg_types, num_args, num_args, target_fn);
    if (status != INFIX_SUCCESS)
        return status;

    // 2. Create the new direct layout and copy basic info.
    infix_direct_call_frame_layout * layout =
        infix_arena_calloc(arena, 1, sizeof(infix_direct_call_frame_layout), _Alignof(infix_direct_call_frame_layout));
    if (!layout)

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN


    size_t total_needed = standard_layout->total_stack_alloc + scratch_space_needed;
    layout->total_stack_alloc = (total_needed + 15) & ~15;

    *out_layout = layout;
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 2 (Direct): Generates the function prologue.
 */
static infix_status generate_direct_forward_prologue_arm64(code_buffer * buf, infix_direct_call_frame_layout * layout) {
    // Standard prologue: save FP/LR, set up new FP.
    emit_arm64_stp_pre_index(buf, true, X29_FP_REG, X30_LR_REG, SP_REG, -16);
    emit_arm64_mov_reg(buf, true, X29_FP_REG, SP_REG);

    // Save callee-saved registers for our context.
    // X19: target_fn, X20: ret_ptr, X21: lang_args array
    emit_arm64_stp_pre_index(buf, true, X19_REG, X20_REG, SP_REG, -16);
    emit_arm64_stp_pre_index(buf, true, X21_REG, X22_REG, SP_REG, -16);  // X22 as scratch

    // The direct CIF is called with (ret_ptr, lang_args) in X0, X1.
    emit_arm64_mov_reg(buf, true, X20_REG, X0_REG);  // x20 = ret_ptr

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

                                                                   infix_direct_call_frame_layout * layout) {
    emit_arm64_load_u64_immediate(buf, X19_REG, (uint64_t)layout->target_fn);
    emit_arm64_cbnz(buf, true, X19_REG, 8);
    emit_arm64_brk(buf, 0);
    emit_arm64_blr_reg(buf, X19_REG);
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 4 (Direct): Generates the epilogue, including write-back calls.
 */
static infix_status generate_direct_forward_epilogue_arm64(code_buffer * buf,
                                                           infix_direct_call_frame_layout * layout,
                                                           infix_type * ret_type) {
    // 1. Handle C function's return value.
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        const infix_type * hfa_base = nullptr;
        if (is_long_double(ret_type) || (ret_type->category == INFIX_TYPE_VECTOR && ret_type->size == 16))
            emit_arm64_str_q_imm(buf, V0_REG, X20_REG, 0);
        else if (is_hfa(ret_type, &hfa_base)) {
            size_t num_elements = ret_type->size / hfa_base->size;
            for (size_t i = 0; i < num_elements; ++i)

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

                size_t s = layout->args[i].type->size;
                size_t end = layout->args[i].location.stack_offset + ((s + 7) & ~7);
                if (end > stack_offset)
                    stack_offset = end;
            }
        }
        standard_alloc_size = (stack_offset + 15) & ~15;
    }

    // 2. Call Write-Back Handlers
    size_t epilogue_scratch_offset = 0;  // Track offset locally to ensure consistency

    for (size_t i = 0; i < layout->num_args; ++i) {
        const infix_direct_arg_layout * arg_layout = &layout->args[i];

        // Re-calculate offset for this arg (Must match Phase 1 & 2 logic exactly)
        int32_t my_scratch_offset = -1;
        bool needs_scratch = false;
        size_t size = 0;
        size_t align = 0;

        if (arg_layout->handler->aggregate_marshaller) {
            size = arg_layout->type->size;
            align = arg_layout->type->alignment;
            needs_scratch = true;
        }

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

        else if (arg_layout->handler->writeback_handler) {
            const infix_type * pointee = (arg_layout->type->category == INFIX_TYPE_POINTER)
                ? arg_layout->type->meta.pointer_info.pointee_type
                : arg_layout->type;
            size = pointee->size;
            align = pointee->alignment;
            needs_scratch = true;
        }

        if (needs_scratch) {
            epilogue_scratch_offset = _infix_align_up(epilogue_scratch_offset, align);
            my_scratch_offset = (int32_t)(standard_alloc_size + epilogue_scratch_offset);
            epilogue_scratch_offset += size;
        }

        if (arg_layout->handler->writeback_handler) {
            // Save C return value (in X0/V0) before calling out.
            // Note: Technically should save more registers for HFA returns, but this matches basic needs.
            emit_arm64_sub_imm(buf, true, false, SP_REG, SP_REG, 32);
            emit_arm64_str_imm(buf, true, X0_REG, SP_REG, 0);
            emit_arm64_str_imm(buf, true, X1_REG, SP_REG, 8);
            emit_arm64_str_q_imm(buf, V0_REG, SP_REG, 16);  // Save V0 (covers float/double/vector)

infix/src/arch/aarch64/abi_arm64.c  view on Meta::CPAN

            emit_arm64_blr_reg(buf, X10_REG);

            // Restore C return value.
            emit_arm64_ldr_q_imm(buf, V0_REG, SP_REG, 16);
            emit_arm64_ldr_imm(buf, true, X1_REG, SP_REG, 8);
            emit_arm64_ldr_imm(buf, true, X0_REG, SP_REG, 0);
            emit_arm64_add_imm(buf, true, false, SP_REG, SP_REG, 32);
        }
    }

    // 3. Standard Epilogue
    if (layout->total_stack_alloc > 0)
        emit_arm64_add_imm(buf, true, false, SP_REG, SP_REG, (uint32_t)layout->total_stack_alloc);
    emit_arm64_ldp_post_index(buf, true, X21_REG, X22_REG, SP_REG, 16);
    emit_arm64_ldp_post_index(buf, true, X19_REG, X20_REG, SP_REG, 16);
    emit_arm64_ldp_post_index(buf, true, X29_FP_REG, X30_LR_REG, SP_REG, 16);
    emit_arm64_ret(buf, X30_LR_REG);

    return INFIX_SUCCESS;
}

infix/src/arch/aarch64/abi_arm64_common.h  view on Meta::CPAN

 * @file abi_arm64_common.h
 * @brief Common register definitions and instruction encodings for the AArch64 (ARM64) architecture.
 * @ingroup internal_abi_aarch64
 *
 * @internal
 * This header serves two primary purposes for the AArch64 backend:
 *
 * 1.  **Register Enumerations:** It defines enums for the general-purpose registers (GPRs) and
 *     the floating-point/SIMD registers (VPRs). These enums provide a clear, type-safe,
 *     and self-documenting way to refer to specific registers when emitting machine
 *     code or implementing the ABI logic. The comments on each register describe its
 *     role according to the standard AAPCS64 calling convention.
 *
 * 2.  **Instruction Encoding Constants:** It contains preprocessor definitions for the
 *     fixed bitfields of various AArch64 instructions. This abstracts away the
 *     "magic numbers" of machine code generation, making the emitter code in
 *     `abi_arm64_emitters.c` more readable and easier to verify against the official
 *     ARM Architecture Reference Manual.
 *
 * By centralizing these definitions, this header provides a single source of truth for
 * the low-level architectural details, separating them from the higher-level ABI logic.
 * @endinternal
 */
#include <stdint.h>
/**
 * @internal
 * @enum arm64_gpr
 * @brief Enumerates the ARM64 General-Purpose Registers (GPRs), X0-X30 and SP.
 *
 * @details The enum values (0-31) correspond directly to the 5-bit register numbers
 * used in the encoding of machine code instructions. The comments on each register

infix/src/arch/aarch64/abi_arm64_emitters.c  view on Meta::CPAN

 * @file abi_arm64_emitters.c
 * @brief Implements internal helper functions for emitting AArch64 machine code.
 * @ingroup internal_abi_aarch64
 *
 * @internal
 * This file provides the concrete implementations for the low-level AArch64
 * instruction emitters. Each function constructs a single, valid 32-bit AArch64
 * instruction word from its component parts (registers, immediates, etc.) and
 * appends it to a `code_buffer`.
 *
 * This module encapsulates the bitwise logic for encoding ARM64 instructions,
 * keeping the main `abi_arm64.c` file focused on the higher-level logic of
 * applying the AAPCS64 ABI rules.
 * @endinternal
 */
#include "arch/aarch64/abi_arm64_emitters.h"
#include "common/utility.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
// GPR <-> Immediate Value Emitters
/*

infix/src/arch/aarch64/abi_arm64_emitters.h  view on Meta::CPAN

 * @file abi_arm64_emitters.h
 * @brief Declares internal helper functions for emitting AArch64 machine code.
 * @ingroup internal_abi_aarch64
 *
 * @internal
 * This header provides the function prototypes for all low-level AArch64 instruction
 * emitters. These functions are the fundamental building blocks used by `abi_arm64.c`
 * to generate the machine code for both forward and reverse trampolines.
 *
 * This module was created to cleanly separate the low-level, bit-twiddling details
 * of AArch64 instruction set encoding from the higher-level logic of applying the
 * AAPCS64 ABI rules (like argument classification and stack layout).
 * @endinternal
 */
#include "arch/aarch64/abi_arm64_common.h"
#include "common/infix_internals.h"
// GPR <-> Immediate Value Emitters
/** @internal @brief Emits a MOVZ/MOVK sequence to load an arbitrary 64-bit immediate into a GPR. */
void emit_arm64_load_u64_immediate(code_buffer * buf, arm64_gpr dest, uint64_t value);
// GPR <-> GPR Move Emitters
/** @internal @brief Emits `MOV <Xd|Wd>, <Xn|Wn>` for a register-to-register move. */

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file abi_sysv_x64.c
 * @brief Implements the FFI logic for the System V AMD64 ABI.
 * @ingroup internal_abi_x64
 *
 * @internal
 * This file provides the concrete implementation of the ABI spec for the System V
 * x86-64 ABI, the standard calling convention for Linux, macOS, BSD, and other
 * UNIX-like operating systems on this architecture.
 *
 * Key features of the System V ABI implemented here:
 *
 * - **Register Usage:**

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

} arg_class_t;

/** The v-table of System V x64 functions for generating forward trampolines. */
static infix_status prepare_forward_call_frame_sysv_x64(infix_arena_t * arena,
                                                        infix_call_frame_layout ** out_layout,
                                                        infix_type * ret_type,
                                                        infix_type ** arg_types,
                                                        size_t num_args,
                                                        size_t num_fixed_args,
                                                        void * target_fn);
static infix_status generate_forward_prologue_sysv_x64(code_buffer * buf, infix_call_frame_layout * layout);
static infix_status generate_forward_argument_moves_sysv_x64(code_buffer * buf,
                                                             infix_call_frame_layout * layout,
                                                             infix_type ** arg_types,
                                                             size_t num_args,
                                                             size_t num_fixed_args);
static infix_status generate_forward_call_instruction_sysv_x64(code_buffer *, infix_call_frame_layout *);
static infix_status generate_forward_epilogue_sysv_x64(code_buffer * buf,
                                                       infix_call_frame_layout * layout,
                                                       infix_type * ret_type);
const infix_forward_abi_spec g_sysv_x64_forward_spec = {
    .prepare_forward_call_frame = prepare_forward_call_frame_sysv_x64,
    .generate_forward_prologue = generate_forward_prologue_sysv_x64,
    .generate_forward_argument_moves = generate_forward_argument_moves_sysv_x64,
    .generate_forward_call_instruction = generate_forward_call_instruction_sysv_x64,
    .generate_forward_epilogue = generate_forward_epilogue_sysv_x64};

/** The v-table of System V x64 functions for generating reverse trampolines. */
static infix_status prepare_reverse_call_frame_sysv_x64(infix_arena_t * arena,
                                                        infix_reverse_call_frame_layout ** out_layout,
                                                        infix_reverse_t * context);
static infix_status generate_reverse_prologue_sysv_x64(code_buffer * buf, infix_reverse_call_frame_layout * layout);
static infix_status generate_reverse_argument_marshalling_sysv_x64(code_buffer * buf,
                                                                   infix_reverse_call_frame_layout * layout,
                                                                   infix_reverse_t * context);
static infix_status generate_reverse_dispatcher_call_sysv_x64(code_buffer * buf,
                                                              infix_reverse_call_frame_layout * layout,
                                                              infix_reverse_t * context);
static infix_status generate_reverse_epilogue_sysv_x64(code_buffer * buf,
                                                       infix_reverse_call_frame_layout * layout,
                                                       infix_reverse_t * context);
const infix_reverse_abi_spec g_sysv_x64_reverse_spec = {
    .prepare_reverse_call_frame = prepare_reverse_call_frame_sysv_x64,
    .generate_reverse_prologue = generate_reverse_prologue_sysv_x64,
    .generate_reverse_argument_marshalling = generate_reverse_argument_marshalling_sysv_x64,
    .generate_reverse_dispatcher_call = generate_reverse_dispatcher_call_sysv_x64,
    .generate_reverse_epilogue = generate_reverse_epilogue_sysv_x64};

/** The v-table for the new Direct Marshalling ABI. */
static infix_status prepare_direct_forward_call_frame_sysv_x64(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);
static infix_status generate_direct_forward_prologue_sysv_x64(code_buffer * buf,
                                                              infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_argument_moves_sysv_x64(code_buffer * buf,
                                                                    infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_call_instruction_sysv_x64(code_buffer * buf,
                                                                      infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_epilogue_sysv_x64(code_buffer * buf,
                                                              infix_direct_call_frame_layout * layout,
                                                              infix_type * ret_type);
const infix_direct_forward_abi_spec g_sysv_x64_direct_forward_spec = {
    .prepare_direct_forward_call_frame = prepare_direct_forward_call_frame_sysv_x64,
    .generate_direct_forward_prologue = generate_direct_forward_prologue_sysv_x64,
    .generate_direct_forward_argument_moves = generate_direct_forward_argument_moves_sysv_x64,
    .generate_direct_forward_call_instruction = generate_direct_forward_call_instruction_sysv_x64,
    .generate_direct_forward_epilogue = generate_direct_forward_epilogue_sysv_x64};

/**
 * @internal
 * @brief Recursively classifies the eightbytes of an aggregate type.
 * @details This is the core of the complex System V classification algorithm. It traverses
 * the fields of a struct/array, examining each 8-byte chunk ("eightbyte") and assigning it a
 * class (INTEGER, SSE, MEMORY). The classification is "merged" according to ABI rules
 * (e.g., if an eightbyte contains both INTEGER and SSE parts, it becomes INTEGER).
 *
 * @param type The type of the current member/element being examined.

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

    // Main Argument Classification Loop
    for (size_t i = 0; i < num_args; ++i) {
        infix_type * type = arg_types[i];
        // Security: Reject excessively large types before they reach the code generator.
        if (type->size > INFIX_MAX_ARG_SIZE) {
            *out_layout = nullptr;
            return INFIX_ERROR_LAYOUT_FAILED;
        }
        // An array passed as a function parameter decays to a pointer.
        // We must treat it as a pointer (INTEGER class) for classification,
        // bypassing the aggregate classification logic which would incorrectly
        // treat it as a by-value struct.
        if (type->category == INFIX_TYPE_ARRAY) {
            if (gpr_count < NUM_GPR_ARGS) {
                layout->arg_locations[i].type = ARG_LOCATION_GPR;
                layout->arg_locations[i].reg_index = gpr_count++;
            }
            else {
                layout->arg_locations[i].type = ARG_LOCATION_STACK;
                layout->arg_locations[i].stack_offset = current_stack_offset;
                current_stack_offset += 8;  // Pointers are 8 bytes on the stack

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

    // Safety check against excessive stack allocation.
    if (layout->total_stack_alloc > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Forward): Generates the function prologue for the System V trampoline.
 * @details Sets up a standard stack frame, saves registers for the trampoline's context,
 *          and allocates stack space for arguments.
 * @param buf The code buffer.
 * @param layout The call frame layout.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_prologue_sysv_x64(code_buffer * buf, infix_call_frame_layout * layout) {
    // Standard Function Prologue
    emit_push_reg(buf, RBP_REG);              // push rbp
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);  // mov rbp, rsp
    // Save Callee-Saved Registers
    // We will use these registers to store our context (target_fn, ret_ptr, args_ptr)
    // across the native function call, so we must save their original values first.
    emit_push_reg(buf, R12_REG);  // push r12
    emit_push_reg(buf, R13_REG);  // push r13
    emit_push_reg(buf, R14_REG);  // push r14
    emit_push_reg(buf, R15_REG);  // push r15
    // Move Trampoline Arguments to Persistent Registers

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

/**
 * @internal
 * @brief Stage 3.5 (Forward): Generates the null-check and call instruction.
 * @param buf The code buffer.
 * @param layout The call frame layout.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_call_instruction_sysv_x64(code_buffer * buf,
                                                               c23_maybe_unused infix_call_frame_layout * layout) {
    // For a bound trampoline, load the hardcoded address into R12.
    // For an unbound trampoline, R12 was already loaded from RDI in the prologue.
    if (layout->target_fn)
        emit_mov_reg_imm64(buf, R12_REG, (uint64_t)layout->target_fn);
    // On SysV x64, the target function pointer is stored in R12.
    emit_test_reg_reg(buf, R12_REG, R12_REG);  // test r12, r12 ; check if function pointer is null
    emit_jnz_short(buf, 2);                    // jnz +2       ; if not null, skip the crash instruction
    emit_ud2(buf);                             // ud2          ; crash safely if null
    emit_call_reg(buf, R12_REG);               // call r12     ; call the function
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 4 (Forward): Generates the function epilogue for the System V trampoline.
 * @details Emits code to handle the function's return value (from RAX/RDX, XMM0/XMM1, or
 *          the x87 FPU stack for `long double`) and properly tear down the stack frame.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @param ret_type The function's return type.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_epilogue_sysv_x64(code_buffer * buf,
                                                       infix_call_frame_layout * layout,
                                                       infix_type * ret_type) {
    // Handle Return Value
    // If the function returns something and it wasn't via a hidden pointer...
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        if (is_long_double(ret_type))
            // `long double` is returned on the x87 FPU stack (st0).
            // We store it into the user's return buffer (pointer held in r13).
            // fstpt [r13] (Store Floating Point value and Pop)
            emit_fstpt_mem(buf, R13_REG, 0);

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

    // Local variables are accessed via negative offsets from the frame pointer (RBP).
    // The layout is [ return_buffer | args_array | saved_args_data ]
    layout->return_buffer_offset = -(int32_t)layout->total_stack_alloc;
    layout->args_array_offset = layout->return_buffer_offset + return_size;
    layout->saved_args_offset = layout->args_array_offset + args_array_size;
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Reverse): Generates the prologue for the reverse trampoline stub.
 * @details Emits standard System V function entry code, creates a stack frame,
 *          and allocates all necessary local stack space.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_reverse_prologue_sysv_x64(code_buffer * buf, infix_reverse_call_frame_layout * layout) {
    emit_push_reg(buf, RBP_REG);                                  // push rbp
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);                      // mov rbp, rsp
    emit_sub_reg_imm32(buf, RSP_REG, layout->total_stack_alloc);  // Allocate our calculated space.
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 3 (Reverse): Generates code to marshal arguments from their native
 *          locations into the generic `void**` array for the C dispatcher.
 * @param buf The code buffer.

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

        emit_lea_reg_mem(buf, RSI_REG, RBP_REG, layout->return_buffer_offset);  // lea rsi, [rbp + return_buffer_offset]
    // Arg 3 (RDX): Pointer to the args_array we just built.
    emit_lea_reg_mem(buf, RDX_REG, RBP_REG, layout->args_array_offset);  // lea rdx, [rbp + args_array_offset]
    // Load the dispatcher's address into a scratch register and call it.
    emit_mov_reg_imm64(buf, RAX_REG, (uint64_t)context->internal_dispatcher);  // mov rax, #dispatcher_addr
    emit_call_reg(buf, RAX_REG);
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 5 (Reverse): Generates the epilogue for the reverse trampoline stub.
 * @details Retrieves the return value from the local buffer and places it into the
 *          correct return registers (RAX/RDX, XMM0/XMM1) or the x87 FPU stack. Then,
 *          it tears down the stack frame and returns to the native caller.
 * @param buf The code buffer.
 * @param layout The layout blueprint.
 * @param context The reverse context.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_reverse_epilogue_sysv_x64(code_buffer * buf,
                                                       infix_reverse_call_frame_layout * layout,
                                                       infix_reverse_t * context) {
    if (context->return_type->category != INFIX_TYPE_VOID) {
        // Correctly determine if the return value uses a hidden pointer by performing a full ABI classification.
        bool return_in_memory = false;
        infix_type * ret_type = context->return_type;
        bool ret_is_aggregate = (ret_type->category == INFIX_TYPE_STRUCT || ret_type->category == INFIX_TYPE_UNION ||
                                 ret_type->category == INFIX_TYPE_ARRAY || ret_type->category == INFIX_TYPE_COMPLEX);
        if (ret_is_aggregate) {
            if (ret_type->size > 16)

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

                if (classes[1] == SSE)
                    if (context->return_type->category == INFIX_TYPE_VECTOR && context->return_type->size == 32)
                        emit_vmovupd_ymm_mem(buf, XMM1_REG, RBP_REG, layout->return_buffer_offset + 32);
                    else
                        emit_movsd_xmm_mem(buf, XMM1_REG, RBP_REG, layout->return_buffer_offset + 8);
                else  // INTEGER
                    emit_mov_reg_mem(buf, RDX_REG, RBP_REG, layout->return_buffer_offset + 8);
            }
        }
    }
    // Standard function epilogue: tear down stack frame and return.
    emit_mov_reg_reg(buf, RSP_REG, RBP_REG);
    emit_pop_reg(buf, RBP_REG);
    emit_ret(buf);
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 1 (Direct): Analyzes a signature and creates a call frame layout for System V.
 * @details This is the direct-marshalling equivalent of the standard `prepare` function.

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

    if (layout->total_stack_alloc > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    *out_layout = layout;
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 2 (Direct): Generates the direct marshalling prologue for System V.
 * @details Establishes a stack frame, saves callee-saved registers for context,
 * moves the direct CIF arguments (`ret_ptr`, `lang_args`) into them, and allocates all
 * stack space required for outgoing arguments and local marshalling buffers.
 */
static infix_status generate_direct_forward_prologue_sysv_x64(code_buffer * buf,
                                                              infix_direct_call_frame_layout * layout) {
    emit_push_reg(buf, RBP_REG);
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);

    // Save callee-saved registers we will use for our context.
    // We push 4 registers (32 bytes) to maintain 16-byte stack alignment
    // (Previous stack state: [RetAddr]+[OldRBP] = 16 bytes. +32 bytes = 48 bytes. Aligned.)
    emit_push_reg(buf, R12_REG);  // Will hold scratch data / target function
    emit_push_reg(buf, R13_REG);  // Will hold return value pointer
    emit_push_reg(buf, R14_REG);  // Will hold language objects array pointer

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

    emit_mov_reg_imm64(buf, R12_REG, (uint64_t)layout->target_fn);
    emit_test_reg_reg(buf, R12_REG, R12_REG);
    emit_jnz_short(buf, 2);
    emit_ud2(buf);
    emit_call_reg(buf, R12_REG);
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 4 (Direct): Generates the function epilogue for System V.
 */
static infix_status generate_direct_forward_epilogue_sysv_x64(code_buffer * buf,
                                                              infix_direct_call_frame_layout * layout,
                                                              infix_type * ret_type) {
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        // Use full ABI classification for return values
        if (is_long_double(ret_type))
            emit_fstpt_mem(buf, R13_REG, 0);
        else {
            arg_class_t classes[2];
            size_t num_classes = 0;
            bool is_aggregate = ret_type->category == INFIX_TYPE_STRUCT || ret_type->category == INFIX_TYPE_UNION ||

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

            emit_call_reg(buf, R10_REG);

            // Restore return registers
            emit_movsd_xmm_mem(buf, XMM0_REG, RSP_REG, 0);
            emit_add_reg_imm32(buf, RSP_REG, 32);
            emit_pop_reg(buf, RDX_REG);
            emit_pop_reg(buf, RAX_REG);
        }
    }

    // Standard Epilogue
    if (layout->total_stack_alloc > 0)
        emit_add_reg_imm32(buf, RSP_REG, (int32_t)layout->total_stack_alloc);

    emit_pop_reg(buf, R15_REG);
    emit_pop_reg(buf, R14_REG);
    emit_pop_reg(buf, R13_REG);
    emit_pop_reg(buf, R12_REG);

    emit_pop_reg(buf, RBP_REG);
    emit_ret(buf);

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file abi_win_x64.c
 * @brief Implements the FFI logic for the Windows x64 calling convention.
 * @ingroup internal_abi_x64
 *
 * @internal
 * This file provides the concrete implementation of the ABI spec for the Microsoft
 * x64 calling convention, used on all 64-bit versions of Windows.
 *
 * Key features and differences from the System V ABI implemented here:
 *
 * - **Register "Slots":** The first four arguments are passed in registers, but the
 *   slots are shared. RCX/XMM0 is the first slot, RDX/XMM1 is the second, etc.

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

/** The size in bytes of the mandatory stack space reserved by the caller for the callee. */
#define SHADOW_SPACE 32
/** @brief The v-table of Windows x64 functions for generating forward trampolines. */
static infix_status prepare_forward_call_frame_win_x64(infix_arena_t * arena,
                                                       infix_call_frame_layout ** out_layout,
                                                       infix_type * ret_type,
                                                       infix_type ** arg_types,
                                                       size_t num_args,
                                                       size_t num_fixed_args,
                                                       void * target_fn);
static infix_status generate_forward_prologue_win_x64(code_buffer * buf, infix_call_frame_layout * layout);
static infix_status generate_forward_argument_moves_win_x64(code_buffer * buf,
                                                            infix_call_frame_layout * layout,
                                                            infix_type ** arg_types,
                                                            size_t num_args,
                                                            size_t num_fixed_args);
static infix_status generate_forward_call_instruction_win_x64(code_buffer *, infix_call_frame_layout *);
static infix_status generate_forward_epilogue_win_x64(code_buffer * buf,
                                                      infix_call_frame_layout * layout,
                                                      infix_type * ret_type);
const infix_forward_abi_spec g_win_x64_forward_spec = {
    .prepare_forward_call_frame = prepare_forward_call_frame_win_x64,
    .generate_forward_prologue = generate_forward_prologue_win_x64,
    .generate_forward_argument_moves = generate_forward_argument_moves_win_x64,
    .generate_forward_call_instruction = generate_forward_call_instruction_win_x64,
    .generate_forward_epilogue = generate_forward_epilogue_win_x64};
/** @brief The v-table of Windows x64 functions for generating reverse trampolines. */
static infix_status prepare_reverse_call_frame_win_x64(infix_arena_t * arena,
                                                       infix_reverse_call_frame_layout ** out_layout,
                                                       infix_reverse_t * context);
static infix_status generate_reverse_prologue_win_x64(code_buffer * buf, infix_reverse_call_frame_layout * layout);
static infix_status generate_reverse_argument_marshalling_win_x64(code_buffer * buf,
                                                                  infix_reverse_call_frame_layout * layout,
                                                                  infix_reverse_t * context);
static infix_status generate_reverse_dispatcher_call_win_x64(code_buffer * buf,
                                                             infix_reverse_call_frame_layout * layout,
                                                             infix_reverse_t * context);
static infix_status generate_reverse_epilogue_win_x64(code_buffer * buf,
                                                      infix_reverse_call_frame_layout * layout,
                                                      infix_reverse_t * context);
const infix_reverse_abi_spec g_win_x64_reverse_spec = {
    .prepare_reverse_call_frame = prepare_reverse_call_frame_win_x64,
    .generate_reverse_prologue = generate_reverse_prologue_win_x64,
    .generate_reverse_argument_marshalling = generate_reverse_argument_marshalling_win_x64,
    .generate_reverse_dispatcher_call = generate_reverse_dispatcher_call_win_x64,
    .generate_reverse_epilogue = generate_reverse_epilogue_win_x64};

/** @brief The v-table for the new Direct Marshalling ABI. */
static infix_status prepare_direct_forward_call_frame_win_x64(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);
static infix_status generate_direct_forward_prologue_win_x64(code_buffer * buf,
                                                             infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_argument_moves_win_x64(code_buffer * buf,
                                                                   infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_call_instruction_win_x64(code_buffer * buf,
                                                                     infix_direct_call_frame_layout * layout);
static infix_status generate_direct_forward_epilogue_win_x64(code_buffer * buf,
                                                             infix_direct_call_frame_layout * layout,
                                                             infix_type * ret_type);
const infix_direct_forward_abi_spec g_win_x64_direct_forward_spec = {
    .prepare_direct_forward_call_frame = prepare_direct_forward_call_frame_win_x64,
    .generate_direct_forward_prologue = generate_direct_forward_prologue_win_x64,
    .generate_direct_forward_argument_moves = generate_direct_forward_argument_moves_win_x64,
    .generate_direct_forward_call_instruction = generate_direct_forward_call_instruction_win_x64,
    .generate_direct_forward_epilogue = generate_direct_forward_epilogue_win_x64};

/**
 * @internal
 * @brief Determines if a type is returned by value in RAX or via a hidden pointer.
 * @details On Windows x64, aggregates are returned by value in RAX only if their size is
 * 1, 2, 4, or 8 bytes. All other aggregates are returned by reference.
 * @param type The type to check.
 * @return `true` if the type is returned via a hidden pointer, `false` otherwise.
 */
static bool return_value_is_by_reference(const infix_type * type) {

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

    if (layout->total_stack_alloc > INFIX_MAX_STACK_ALLOC) {
        fprintf(stderr, "Error: Calculated stack allocation exceeds safe limit of %d bytes.\n", INFIX_MAX_STACK_ALLOC);
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Forward): Generates the function prologue for the Windows x64 trampoline.
 * @details This function emits the standard machine code required at the beginning of a function.
 *          The generated assembly performs these steps:
 *          1.  `push rbp` / `mov rbp, rsp`: Creates a standard stack frame.
 *          2.  `push r12-r15`: Saves all callee-saved registers that the trampoline will
 *              use to hold its context (target function pointer, return buffer, args array).
 *          3.  `mov r12, rcx`, etc.: Moves the trampoline's own arguments (which arrive in
 *              RCX, RDX, R8) into the preserved registers (R12, R13, R14).
 *          4.  `sub rsp, imm32`: Allocates the required space on the stack. This allocation
 *              **must** include the 32-byte "shadow space" for the callee, in addition
 *              to space for any arguments passed on the stack.
 *
 * @param buf The code buffer to write the assembly into.
 * @param layout The call frame layout containing total stack allocation information.
 * @return `INFIX_SUCCESS` on successful code generation.
 */
static infix_status generate_forward_prologue_win_x64(code_buffer * buf, infix_call_frame_layout * layout) {
    emit_push_reg(buf, RBP_REG);              // push rbp
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);  // mov rbp, rsp
    // Save callee-saved registers we will use to hold our context.
    emit_push_reg(buf, R12_REG);  // push r12 (will hold target function address)
    emit_push_reg(buf, R13_REG);  // push r13 (will hold return value pointer)
    emit_push_reg(buf, R14_REG);  // push r14 (will hold argument pointers array)
    emit_push_reg(buf, R15_REG);  // push r15 (will be a scratch register for data moves)
    // Move incoming trampoline arguments to non-volatile registers.
    if (layout->target_fn == nullptr) {           // Unbound: (target_fn, ret_ptr, args_ptr) in RCX, RDX, R8
        emit_mov_reg_reg(buf, R12_REG, RCX_REG);  // R12 = target function

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

 * @param buf The code buffer.
 * @param layout The call frame layout.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_forward_call_instruction_win_x64(code_buffer * buf,
                                                              c23_maybe_unused infix_call_frame_layout * layout) {
    if (layout->target_fn) {
        // For a bound trampoline, the target is hardcoded. Load it into R12.
        emit_mov_reg_imm64(buf, R12_REG, (uint64_t)layout->target_fn);
    }
    // For an unbound trampoline, R12 was already loaded from the first argument in the prologue.
    // On Windows x64, the target function pointer is stored in R12.
    emit_test_reg_reg(buf, R12_REG, R12_REG);  // test r12, r12
    emit_jnz_short(buf, 2);                    // jnz +2
    emit_ud2(buf);                             // ud2
    emit_call_reg(buf, R12_REG);               // call r12
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 4 (Forward): Generates the function epilogue for the Windows x64 trampoline.
 * @details This function emits the code to handle the function's return value and
 *          properly tear down the stack frame.
 *
 *          Key behaviors implemented:
 *          - **Return Value Handling:** After the native `call` returns, it copies the
 *            result from the return register (`RAX` for integers/pointers, `XMM0` for
 *            floats/doubles/vectors) into the user-provided return buffer.
 *          - **Stack Cleanup:** Deallocates the stack space reserved in the prologue.
 *          - **Register Restoration:** Restores the saved callee-saved registers (r12-r15)
 *            and the caller's base pointer (`rbp`).
 *          - **Return:** Executes a `ret` instruction.
 *
 * @param buf The code buffer.
 * @param layout The call frame layout.
 * @param ret_type The `infix_type` of the function's return value.
 * @return `INFIX_SUCCESS` on successful code generation.
 */
static infix_status generate_forward_epilogue_win_x64(code_buffer * buf,
                                                      infix_call_frame_layout * layout,
                                                      infix_type * ret_type) {
    // R13 holds the pointer to the FFI return buffer.
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        if (is_float(ret_type))
            emit_movss_mem_xmm(buf, R13_REG, 0, XMM0_REG);
        else if (is_double(ret_type))
            emit_movsd_mem_xmm(buf, R13_REG, 0, XMM0_REG);
        else if (ret_type->size == 16 &&
                 (ret_type->category == INFIX_TYPE_PRIMITIVE || ret_type->category == INFIX_TYPE_VECTOR))

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

    layout->return_buffer_offset = SHADOW_SPACE;
    layout->gpr_save_area_offset = layout->return_buffer_offset + return_size;
    layout->xmm_save_area_offset = layout->gpr_save_area_offset + gpr_reg_save_area_size;
    layout->args_array_offset = layout->xmm_save_area_offset + xmm_reg_save_area_size;
    layout->saved_args_offset = layout->args_array_offset + args_array_size;
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Reverse): Generates the prologue for the reverse trampoline stub.
 * @details Emits the standard Windows x64 function entry code. This involves:
 *          1. Creating a standard stack frame (`push rbp; mov rbp, rsp`).
 *          2. Saving any non-volatile registers that the stub will use as scratch space
 *             (RSI and RDI in this implementation).
 *          3. Allocating all necessary local stack space for the stub's internal
 *             data structures, as calculated in the `prepare` stage.
 *
 * @param buf The code buffer to write the assembly into.
 * @param layout The blueprint containing the total stack space to allocate.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_reverse_prologue_win_x64(code_buffer * buf, infix_reverse_call_frame_layout * layout) {
    // Standard function prologue to establish a stack frame.
    emit_push_reg(buf, RBP_REG);
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);
    // Save callee-saved registers that we might use as scratch registers.
    emit_push_reg(buf, RSI_REG);
    emit_push_reg(buf, RDI_REG);
    // Allocate all local stack space calculated in the prepare stage. This includes
    // space for register save areas, the return buffer, args_array, and shadow space.
    if (layout->total_stack_alloc > 0)
        emit_sub_reg_imm32(buf, RSP_REG, layout->total_stack_alloc);
    return INFIX_SUCCESS;

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

                // The value in the GPR save area IS the pointer we need. Load it directly.
                emit_mov_reg_mem(buf, RAX_REG, RSP_REG, source_offset);
            else
                // The value is the data itself. Get a pointer TO the saved data.
                emit_lea_reg_mem(buf, RAX_REG, RSP_REG, source_offset);
            // Store the final pointer into the args_array.
            emit_mov_mem_reg(buf, RSP_REG, layout->args_array_offset + i * sizeof(void *), RAX_REG);
        }
        else {
            // Argument was passed on the caller's stack.
            // After our prologue, caller stack args start at [rbp + 16 (ret addr + old rbp) + 32 (shadow space)].
            int32_t caller_stack_offset = 16 + SHADOW_SPACE + (stack_slot_offset * 8);
            if (passed_by_ref)
                // The value on the stack IS the pointer we need. Load it.
                emit_mov_reg_mem(buf, RAX_REG, RBP_REG, caller_stack_offset);
            else
                // The value on the stack is the data. Get a pointer TO it.
                emit_lea_reg_mem(buf, RAX_REG, RBP_REG, caller_stack_offset);
            // Store the final pointer into the args_array.
            emit_mov_mem_reg(buf, RSP_REG, layout->args_array_offset + i * sizeof(void *), RAX_REG);
            // Advance our offset into the caller's stack frame for the next argument.

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

        emit_lea_reg_mem(buf, RDX_REG, RSP_REG, layout->return_buffer_offset);
    // Arg 3 (R8): Load the address of the `args_array` on our local stack.
    emit_lea_reg_mem(buf, R8_REG, RSP_REG, layout->args_array_offset);
    // Load the C dispatcher's address into a scratch register (R9) and call it.
    emit_mov_reg_imm64(buf, R9_REG, (uint64_t)context->internal_dispatcher);
    emit_call_reg(buf, R9_REG);
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 5 (Reverse): Generates the epilogue for the reverse trampoline stub.
 * @details After the C dispatcher returns, this code is responsible for the final steps
 *          of the reverse trampoline. It retrieves the return value from the buffer on
 *          the stub's local stack and places it into the correct native return register
 *          (`RAX` or `XMM0`) as required by the Windows x64 ABI.
 *
 *          This function correctly handles the rules for returning values:
 *          - **By-Reference Returns:** For large aggregates, the original caller passes a
 *            hidden pointer in `RCX`. The ABI requires the callback to return this same
 *            pointer in `RAX`. This function emits code to load the saved pointer
 *            (from the GPR save area) into `RAX`.
 *          - **By-Value Returns:** For values returned directly in registers, this
 *            function emits the correct `mov` instructions to load the data from
 *            the stack buffer into either `RAX` (for integers/pointers/small structs)
 *            or `XMM0` (for floats/doubles/vectors), respecting compiler-specific
 *            rules for types like `__int128_t`.
 *
 *          Finally, it emits the standard function epilogue to deallocate the stack frame,
 *          restore the caller's saved registers, and return control to the native caller.
 * @param buf The code buffer.
 * @param layout The blueprint containing stack offsets.
 * @param context The context containing the return type information.
 * @return `INFIX_SUCCESS`.
 */
static infix_status generate_reverse_epilogue_win_x64(code_buffer * buf,
                                                      infix_reverse_call_frame_layout * layout,
                                                      infix_reverse_t * context) {
    // Handle the return value after the dispatcher returns.
    if (context->return_type->category != INFIX_TYPE_VOID) {
        if (return_value_is_by_reference(context->return_type))
            // The return value was written directly via the hidden pointer.
            // The ABI requires this original pointer (which was in RCX) to be returned in RAX.
            emit_mov_reg_mem(buf, RAX_REG, RSP_REG, layout->gpr_save_area_offset + 0 * 8);
        else {
            // The return value is in our local buffer. Load it into the correct return register.

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

                emit_vmovupd_ymm_mem(buf, XMM0_REG, RSP_REG, layout->return_buffer_offset);
            else if (is_float(context->return_type))
                emit_movss_xmm_mem(buf, XMM0_REG, RSP_REG, layout->return_buffer_offset);
            else if (is_double(context->return_type))
                emit_movsd_xmm_mem(buf, XMM0_REG, RSP_REG, layout->return_buffer_offset);
            else
                // All other by-value types (integers, pointers, small structs) are returned in RAX.
                emit_mov_reg_mem(buf, RAX_REG, RSP_REG, layout->return_buffer_offset);
        }
    }
    // Epilogue: deallocate stack and restore non-volatile registers.
    emit_add_reg_imm32(buf, RSP_REG, layout->total_stack_alloc);
    emit_pop_reg(buf, RDI_REG);
    emit_pop_reg(buf, RSI_REG);
    emit_pop_reg(buf, RBP_REG);
    emit_ret(buf);
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 1 (Direct): Analyzes a signature and creates a call frame layout for Windows x64.

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

            const infix_type * type = arg_types[i];
            bool by_ref = is_passed_by_reference(type) ||
                (type->category == INFIX_TYPE_POINTER && handlers[i].aggregate_marshaller);
            // Overwrite the temp offset with the final outgoing offset.
            layout->args[i].location.stack_offset = (uint32_t)outgoing_stack_offset;
            size_t size_on_stack = by_ref ? 8 : ((type->size + 7) & ~7);
            outgoing_stack_offset += size_on_stack;
        }
    }

    // Ensure the base of the scratch area is 16-byte aligned, matching Generate phase logic.
    size_t scratch_base_offset = (outgoing_stack_offset + 15) & ~15;

    size_t total_needed = scratch_base_offset + temp_space_offset;
    layout->total_stack_alloc = (total_needed + 15) & ~15;

    // Final pass: Adjust temp/scratch offsets to be relative to RSP after allocation.
    size_t temp_base_offset = scratch_base_offset;
    for (size_t i = 0; i < num_args; ++i) {
        if (layout->args[i].handler->aggregate_marshaller || layout->args[i].handler->scalar_marshaller ||
            layout->args[i].handler->writeback_handler) {

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN


    if (layout->total_stack_alloc > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }
    *out_layout = layout;
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 2 (Direct): Generates the function prologue.
 * @details Establishes a stack frame, saves callee-saved registers (R12-R15) for context,
 * moves the direct CIF arguments (`ret_ptr`, `lang_args`) into them, and allocates all
 * stack space required for outgoing arguments, shadow space, and local marshalling buffers.
 */
static infix_status generate_direct_forward_prologue_win_x64(code_buffer * buf,
                                                             infix_direct_call_frame_layout * layout) {
    emit_push_reg(buf, RBP_REG);
    emit_mov_reg_reg(buf, RBP_REG, RSP_REG);
    // Save callee-saved registers we will use for our context.
    emit_push_reg(buf, R12_REG);  // Will hold scratch data
    emit_push_reg(buf, R13_REG);  // Will hold return value pointer
    emit_push_reg(buf, R14_REG);  // Will hold language objects array pointer
    emit_push_reg(buf, R15_REG);  // Will hold target function address

    // The direct CIF is called with (ret_ptr, lang_args) in RCX, RDX.

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN

    emit_mov_reg_imm64(buf, R15_REG, (uint64_t)layout->target_fn);  // Use R15 for target function
    emit_test_reg_reg(buf, R15_REG, R15_REG);
    emit_jnz_short(buf, 2);
    emit_ud2(buf);
    emit_call_reg(buf, R15_REG);
    return INFIX_SUCCESS;
}

/**
 * @internal
 * @brief Stage 4 (Direct): Generates the epilogue, including write-back calls.
 */
static infix_status generate_direct_forward_epilogue_win_x64(code_buffer * buf,
                                                             infix_direct_call_frame_layout * layout,
                                                             infix_type * ret_type) {
    // 1. Handle C function's return value.
    if (ret_type->category != INFIX_TYPE_VOID && !layout->return_value_in_memory) {
        if (is_float(ret_type))
            emit_movss_mem_xmm(buf, R13_REG, 0, XMM0_REG);
        else if (is_double(ret_type))
            emit_movsd_mem_xmm(buf, R13_REG, 0, XMM0_REG);
        else if (ret_type->size == 16 &&
                 (ret_type->category == INFIX_TYPE_PRIMITIVE || ret_type->category == INFIX_TYPE_VECTOR))

infix/src/arch/x64/abi_win_x64.c  view on Meta::CPAN


            emit_mov_reg_imm64(buf, R10_REG, (uint64_t)arg_layout->handler->writeback_handler);
            emit_call_reg(buf, R10_REG);

            emit_mov_reg_mem(buf, RAX_REG, RSP_REG, 32);
            emit_movsd_xmm_mem(buf, XMM0_REG, RSP_REG, 40);
            emit_add_reg_imm32(buf, RSP_REG, 48);
        }
    }

    // 3. Standard Epilogue
    if (layout->total_stack_alloc > 0)
        emit_add_reg_imm32(buf, RSP_REG, layout->total_stack_alloc);

    emit_pop_reg(buf, R15_REG);
    emit_pop_reg(buf, R14_REG);
    emit_pop_reg(buf, R13_REG);
    emit_pop_reg(buf, R12_REG);
    emit_pop_reg(buf, RBP_REG);
    emit_ret(buf);

infix/src/arch/x64/abi_x64_emitters.c  view on Meta::CPAN

    if (dest_base % 8 == RSP_REG)
        emit_byte(buf, 0x24);
    if (mod == 0x40)
        emit_byte(buf, (uint8_t)offset);
    else if (mod == 0x80)
        emit_int32(buf, offset);
}
/**
 * @internal
 * @brief Emits a VEX prefix for an AVX instruction.
 * @details This helper centralizes the logic of choosing between 2-byte (C5) and 3-byte (C4) VEX encodings.
 */
static void emit_vex_prefix(
    code_buffer * buf, bool r, bool x, bool b, uint8_t m, bool w, uint8_t v, bool l, uint8_t p) {
    // VEX encoding inverts R, X, B bits.
    // The 2-byte VEX prefix cannot encode the L bit for 256-bit operations.
    // The condition must ensure we only use it for 128-bit operations (l=0).
    if (!b && !x && m == 1 && w == 0 && !l) {
        // Use 2-byte VEX prefix (C5) when possible.
        emit_byte(buf, 0xC5);
        uint8_t byte2 = ((!r) << 7) | ((~v & 0xF) << 3) | ((l & 1) << 2) | (p & 3);

infix/src/common/double_tap.h  view on Meta::CPAN

 * @file double_tap.h
 * @brief A lightweight, single-header TAP (Test Anything Protocol) library.
 * @ingroup internal_test_harness
 *
 * @details This file provides a simple, self-contained testing harness that produces
 * TAP-compliant output, which is ideal for integration with CI/CD systems and other
 * testing tools. It is used for all unit and regression tests within the `infix` project.
 *
 * The library is designed to be trivial to use:
 * 1.  Define `DBLTAP_ENABLE` and `DBLTAP_IMPLEMENTATION` in a single test file.
 * 2.  Write all test logic within a function named `test_body(void)` using the `TEST` macro.
 * 3.  Use the provided macros (`plan`, `ok`, `subtest`, etc.) to structure tests.
 *
 * The library provides its own `main` function that initializes the harness, calls
 * the user-defined `test_body`, and reports the final results.
 *
 * @section thread_safety Thread Safety
 *
 * The design uses thread-local storage (`_Thread_local`, `__thread`) to manage the
 * test state (test counts, subtest nesting, etc.). This allows multiple threads to
 * run tests concurrently without interfering with each other's output or results,

infix/src/common/infix_config.h  view on Meta::CPAN

#if !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200809L
#endif
#if (defined(__linux__) || defined(__gnu_linux__)) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
// Operating System Detection
/**
 * @details This section defines `INFIX_OS_*` macros based on compiler-provided
 * preprocessor definitions. It also defines the broader `INFIX_ENV_POSIX` for systems
 * that follow POSIX conventions, which simplifies later `#ifdef` logic.
 */
#if defined(_WIN32)
#define INFIX_OS_WINDOWS
#include <windows.h>  // Included early for common types like SYSTEM_INFO, HANDLE, etc.
// Compatibility shim for POSIX types not present in Clang/MSVC headers.
#if !defined(__CYGWIN__)  // Cygwin provides its own full POSIX environment
#include <stddef.h>       // For ptrdiff_t
#ifndef ssize_t
// Define ssize_t as ptrdiff_t, the standard signed counterpart to size_t.
typedef ptrdiff_t ssize_t;

infix/src/common/infix_internals.h  view on Meta::CPAN

    int32_t args_array_offset;    /**< Stack offset for the `void**` array passed to the C dispatcher. */
    int32_t saved_args_offset;    /**< Stack offset for the area where argument data is stored/marshalled. */
    int32_t gpr_save_area_offset; /**< (Win x64) Stack offset for saving non-volatile GPRs. */
    int32_t xmm_save_area_offset; /**< (Win x64) Stack offset for saving non-volatile XMMs. */
} infix_reverse_call_frame_layout;
/**
 * @brief Defines the ABI-specific implementation interface for forward trampolines.
 *
 * @details This structure is a virtual function table (v-table) that decouples the
 * platform-agnostic JIT engine (`trampoline.c`) from the platform-specific
 * code generation logic (`arch/...`). Each supported ABI (e.g., SysV x64,
 * Win x64, AArch64) provides a concrete implementation of this interface.
 *
 * The JIT pipeline for a forward call proceeds in a well-defined order:
 * 1. `prepare_forward_call_frame` is called first to analyze the function
 *    signature and produce a complete `infix_call_frame_layout` blueprint.
 * 2. The `generate_*` functions are then called in sequence, consuming the layout
 *    blueprint to emit the corresponding machine code into a `code_buffer`.
 */
typedef struct {
    /**

infix/src/common/infix_internals.h  view on Meta::CPAN

     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*prepare_forward_call_frame)(infix_arena_t * arena,
                                               infix_call_frame_layout ** out_layout,
                                               infix_type * ret_type,
                                               infix_type ** arg_types,
                                               size_t num_args,
                                               size_t num_fixed_args,
                                               void * target_fn);
    /**
     * @brief Generates the function prologue (stack setup, saving registers).
     * @param[in,out] buf The code buffer to append machine code to.
     * @param[in] layout The layout blueprint from the previous step.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_forward_prologue)(code_buffer * buf, infix_call_frame_layout * layout);
    /**
     * @brief Generates code to move arguments from the `void**` array into registers and/or the stack.
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @param[in] arg_types The array of argument types.
     * @param[in] num_args Total number of arguments.
     * @param[in] num_fixed_args Number of fixed arguments.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_forward_argument_moves)(code_buffer * buf,

infix/src/common/infix_internals.h  view on Meta::CPAN

                                                    size_t num_args,
                                                    size_t num_fixed_args);
    /**
     * @brief Generates the `call` instruction to the target function.
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_forward_call_instruction)(code_buffer * buf, infix_call_frame_layout * layout);
    /**
     * @brief Generates the function epilogue (handling return value, restoring stack, returning).
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @param[in] ret_type The function's return type.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_forward_epilogue)(code_buffer * buf,
                                              infix_call_frame_layout * layout,
                                              infix_type * ret_type);
} infix_forward_abi_spec;
/**
 * @brief Defines the ABI-specific implementation interface for reverse trampolines.
 * @details This v-table defines the contract for generating the JIT stub for a
 * reverse call (callback). The stub's primary job is to receive arguments in
 * native ABI format, marshal them into a generic `void**` array, and call the
 * universal C dispatcher.
 */

infix/src/common/infix_internals.h  view on Meta::CPAN

     * @brief Analyzes a function signature to create a layout for the reverse call stub's stack frame.
     * @param[in] arena The temporary arena for allocations.
     * @param[out] out_layout Receives the newly created layout blueprint.
     * @param[in] context The reverse trampoline context, containing all type info.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*prepare_reverse_call_frame)(infix_arena_t * arena,
                                               infix_reverse_call_frame_layout ** out_layout,
                                               infix_reverse_t * context);
    /**
     * @brief Generates the reverse stub's prologue (stack setup).
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_reverse_prologue)(code_buffer * buf, infix_reverse_call_frame_layout * layout);
    /**
     * @brief Generates code to marshal arguments from their native locations (registers/stack) into a `void**` array.
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @param[in] context The reverse context.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_reverse_argument_marshalling)(code_buffer * buf,
                                                          infix_reverse_call_frame_layout * layout,
                                                          infix_reverse_t * context);

infix/src/common/infix_internals.h  view on Meta::CPAN

     * @brief Generates the call to the universal C dispatcher (`infix_internal_dispatch_callback_fn_impl`).
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @param[in] context The reverse context.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_reverse_dispatcher_call)(code_buffer * buf,
                                                     infix_reverse_call_frame_layout * layout,
                                                     infix_reverse_t * context);
    /**
     * @brief Generates the reverse stub's epilogue (handling return value, restoring stack, returning).
     * @param[in,out] buf The code buffer.
     * @param[in] layout The layout blueprint.
     * @param[in] context The reverse context.
     * @return `INFIX_SUCCESS` on success.
     */
    infix_status (*generate_reverse_epilogue)(code_buffer * buf,
                                              infix_reverse_call_frame_layout * layout,
                                              infix_reverse_t * context);
} infix_reverse_abi_spec;

/**
 * @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.

infix/src/common/infix_internals.h  view on Meta::CPAN

 */
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

infix/src/common/utility.h  view on Meta::CPAN

 * @internal
 * This header is the central point for the library's internal debugging infrastructure.
 * Its primary feature is that it behaves differently based on the `INFIX_DEBUG_ENABLED`
 * preprocessor macro. This allows debugging code to be seamlessly integrated
 * during development without affecting the performance or size of the final
 * production binary.
 *
 * - **When `INFIX_DEBUG_ENABLED` is defined and non-zero (Debug Mode):**
 *   - It declares the `infix_dump_hex` function for detailed memory inspection.
 *   - It defines the `INFIX_DEBUG_PRINTF` macro, which integrates with the `double_tap`
 *     test harness's logging system (`note()`) if available, or falls back to a
 *     standard `printf`. This allows debug messages from the core library to appear
 *     cleanly within the test output.
 *
 * - **When `INFIX_DEBUG_ENABLED` is not defined or is zero (Release Mode):**
 *   - All debugging macros are defined as no-ops (`((void)0)`).
 *   - The `infix_dump_hex` function is defined as an empty `static inline` function.
 *   - This design ensures that all debugging code and calls are completely compiled
 *     out by the optimizer, resulting in zero overhead in release builds.
 * @endinternal
 */

infix/src/common/utility.h  view on Meta::CPAN

// The double_tap framework is only included if both debug mode AND the main
// test harness toggle are enabled. This allows for debug builds of non-test executables.
#if defined(DBLTAP_ENABLE)
#include "common/double_tap.h"
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A macro for printing formatted debug messages during a debug build with the test harness.
 * @details In debug builds where `double_tap.h` is active, this macro wraps the `note()`
 *          macro, integrating debug output from the library's internals cleanly into the
 *          TAP-formatted test logs.
 * @example
 * ```c
 * INFIX_DEBUG_PRINTF("Processing type %d with size %zu", type->id, type->size);
 * ```
 */
#define INFIX_DEBUG_PRINTF(...) note("INFIX_DEBUG: " __VA_ARGS__)
#else
#include <stdio.h>
/**
 * @internal

infix/src/core/arena.c  view on Meta::CPAN

            if (next_size < size + alignment)
                next_size = size + alignment;
            current_block->next_block = infix_arena_create(next_size);
            if (current_block->next_block == nullptr) {
                current_block->error = true;  // Mark the last valid block as errored.
                return nullptr;               // Growth failed.
            }
            current_block = current_block->next_block;
        }
    }
    // This line should be unreachable if the logic is correct.
    return nullptr;
}
/**
 * @internal
 * @brief Allocates and zero-initializes a block of memory from an arena.
 *
 * This function is a convenience wrapper around `infix_arena_alloc` that also
 * ensures the allocated memory is set to zero, mimicking the behavior of `calloc`.
 * It includes a check for integer overflow on the `num * size` calculation and
 * will set a detailed error on failure.

infix/src/core/loader.c  view on Meta::CPAN

    if (status != INFIX_SUCCESS)
        return status;
    // Safely copy the data using the parsed size.
    infix_memcpy(buffer, symbol_addr, type->size);
    infix_arena_destroy(arena);
    return INFIX_SUCCESS;
}
/**
 * @brief Writes data from a buffer into an exported global variable in a library.
 *
 * @details This function is analogous to `infix_read_global`. It finds the symbol's
 * address and uses the signature string to determine the correct number of bytes
 * to copy from the source buffer to the library's memory.
 *
 * @note This operation assumes that the memory page containing the global variable
 *       is writable. This is typical for `.data` or `.bss` segments but may fail
 *       if the variable is in a read-only segment (e.g., a `const` global). The
 *       function does not attempt to change memory permissions.
 *
 * @param[in] lib The library handle.
 * @param[in] symbol_name The name of the global variable.

infix/src/core/signature.c  view on Meta::CPAN

    *out_args = args;
    *out_num_args = num_args;
    return INFIX_SUCCESS;
}
// High-Level API Implementation
/**
 * @internal
 * @brief The internal entry point for the signature parser (the "Parse" stage).
 *
 * This function takes a signature string and produces a raw, unresolved type
 * graph in a new, temporary arena. It is the core parsing logic, separated from the
 * higher-level functions that manage the full data pipeline. It is careful not to
 * modify the global error context string (`g_infix_last_signature_context`), which
 * is the responsibility of its public API callers.
 *
 * @param[out] out_type On success, receives the parsed type graph.
 * @param[out] out_arena On success, receives the temporary arena holding the graph. The caller is responsible for
 * destroying it.
 * @param[in] signature The signature string to parse.
 * @return `INFIX_SUCCESS` on success.
 */

infix/src/core/signature.c  view on Meta::CPAN

                _print(state, ",");
            const infix_struct_member * member = &type->meta.aggregate_info.members[i];
            if (member->name)
                _print(state, "%s:", member->name);
            _infix_type_print_signature_recursive(state, member->type);
            if (member->bit_width > 0)
                _print(state, ":%u", member->bit_width);
        }
        _print(state, ">");
        break;
    // For all other types, we replicate the printing logic from the main printer
    // to ensure we print the structure, not a potential top-level alias name.
    case INFIX_TYPE_VOID:
        _print(state, "void");
        break;
    case INFIX_TYPE_POINTER:
        _print(state, "*");
        if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
            type->meta.pointer_info.pointee_type->category == INFIX_TYPE_VOID)
            _print(state, "void");
        else

infix/src/core/signature.c  view on Meta::CPAN

        }
    }
    else if (buffer_size > 0)
        buffer[buffer_size - 1] = '\0';
    return state.status;
}
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * @details The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined. The order of
 * definitions in the output string is not guaranteed.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small
 *         or another error occurs.
 */
c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {

infix/src/core/type_registry.c  view on Meta::CPAN

            _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
            final_status = INFIX_ERROR_ALLOCATION_FAILED;
            goto cleanup;
        }

        // Update the entry. If a placeholder already exists (from forward decl), we MUST update it in-place
        // to preserve pointers from other types that have already resolved to it.
        if (entry->type) {
            // Struct-copy the new definition into the existing placeholder memory.
            *entry->type = *new_def;
            // Restore the self-reference if the copy logic pointed the arena elsewhere.
            entry->type->arena = registry->arena;
            // The new definition is complete, so ensure the flag is cleared.
            entry->type->is_incomplete = false;
        }
        else {
            entry->type = new_def;
            entry->type->is_incomplete = false;
        }

        // Ensure the name is attached and the flag is cleared.

infix/src/core/types.c  view on Meta::CPAN

 */
/**
 * @file types.c
 * @brief Implements the public API for creating and managing type descriptions.
 * @ingroup internal_core
 *
 * @details This module serves two primary functions:
 * 1.  It provides the public functions for programmatically constructing `infix_type`
 *     objects (the "Manual API"). These functions are the building blocks for
 *     users who need to create type information dynamically without parsing strings.
 * 2.  It contains the crucial internal logic for two core stages of the data pipeline:
 *     - **Copying (`_copy_type_graph_to_arena`):** Deep-copies a type graph to a
 *       new memory arena, which is fundamental to creating self-contained trampoline
 *       objects and ensuring memory safety.
 *     - **Layout (`_infix_type_recalculate_layout`):** Traverses a fully resolved
 *       type graph to compute the final memory layout (size, alignment, and offsets)
 *       of all structures and unions.
 *
 * The creation functions (`infix_type_create_*`) perform an initial, preliminary layout
 * calculation. This layout is considered unresolved until a final pass with
 * `_infix_type_recalculate_layout` is performed after all named types have been

infix/src/core/types.c  view on Meta::CPAN

 */
infix_struct_member infix_type_create_bitfield_member(const char * name,
                                                      infix_type * type,
                                                      size_t offset,
                                                      uint8_t bit_width) {
    return (infix_struct_member){name, type, offset, bit_width, 0, true};
}

/**
 * @internal
 * @brief Shared logic to calculate struct layout, including bitfields and FAMs.
 * @return `true` on success, `false` if an integer overflow occurred.
 */
static bool _layout_struct(infix_type * type) {
    size_t current_byte_offset = 0;
    uint8_t current_bit_offset = 0;  // 0-7 bits used in the current byte
    size_t max_alignment = 1;

    for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
        infix_struct_member * member = &type->meta.aggregate_info.members[i];

infix/src/core/types.c  view on Meta::CPAN

            size_t aligned = _infix_align_up(current_byte_offset, member_align);
            if (aligned < current_byte_offset) {
                _infix_set_error(INFIX_CATEGORY_PARSER, INFIX_CODE_INTEGER_OVERFLOW, 0);
                return false;
            }
            current_byte_offset = aligned;
            member->offset = current_byte_offset;

            if (member_align > max_alignment)
                max_alignment = member_align;
            continue;  // FAM logic done
        }

        // 2. Handle Bitfields
        if (member->is_bitfield) {
            // Zero-width bitfield: force alignment to the next boundary of the declared type.
            if (member->bit_width == 0) {
                if (current_bit_offset > 0) {
                    if (current_byte_offset == SIZE_MAX) {
                        _infix_set_error(INFIX_CATEGORY_PARSER, INFIX_CODE_INTEGER_OVERFLOW, 0);
                        return false;

infix/src/core/types.c  view on Meta::CPAN

    // the natural alignment of members.
    if (type->meta.aggregate_info.is_packed)
        max_alignment = type->alignment;

    type->alignment = max_alignment;
    type->size = _infix_align_up(current_byte_offset, max_alignment);
    return true;
}
/**
 * @internal
 * @brief Common setup logic for creating aggregate types (structs and unions).
 *
 * @details This helper function reduces code duplication by handling the common tasks of:
 * 1. Validating that member types are not null.
 * 2. Allocating the main `infix_type` object from the arena.
 * 3. Allocating a new array for the members within the arena and copying the
 *    user-provided member data into it.
 *
 * @param[in] arena The arena to allocate from.
 * @param[out] out_type The output pointer for the new `infix_type`.
 * @param[out] out_arena_members The output pointer for the newly copied members array.

infix/src/core/types.c  view on Meta::CPAN

    type->category = INFIX_TYPE_STRUCT;
    type->meta.aggregate_info.members = arena_members;
    type->meta.aggregate_info.num_members = num_members;
    type->meta.aggregate_info.is_packed = false;

    // This performs a preliminary layout calculation.
    // Note: This layout may be incomplete if it contains unresolved named references or flexible arrays.
    // The final, correct layout will be computed by `_infix_type_recalculate_layout`.
    // However, we must set a preliminary size/alignment here.

    // We use the recalculate logic to do the heavy lifting, assuming a temporary arena can be made.
    // But we can't create an arena here easily if we are in a strict context.
    // So we do a simplified pass just like the old logic, ignoring complex bitfield rules for now.
    // The proper bitfield layout happens in `_infix_type_recalculate_layout`.

    for (size_t i = 0; i < num_members; ++i) {
        infix_struct_member * member = &arena_members[i];
        if (member->type->alignment == 0 && member->type->category != INFIX_TYPE_NAMED_REFERENCE &&
            !(member->type->category == INFIX_TYPE_ARRAY && member->type->meta.array_info.is_flexible)) {
            if (member->type->category != INFIX_TYPE_ARRAY) {
                *out_type = nullptr;
                _infix_set_error(INFIX_CATEGORY_PARSER, INFIX_CODE_INVALID_MEMBER_TYPE, 0);
                return INFIX_ERROR_INVALID_ARGUMENT;

infix/src/core/utility.c  view on Meta::CPAN

 * is defined and set to a non-zero value.
 *
 * In release builds, this entire translation unit is effectively empty, ensuring
 * that debugging code has no impact on the final binary's size or performance.
 * The corresponding function declarations in `utility.h` become empty inline
 * stubs, which are optimized away by the compiler.
 */
// This file is only compiled if debugging is enabled.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
// Use the double-tap test harness's `note` macro for debug printing if available.
// This integrates the debug output seamlessly into the TAP test logs.
#if defined(DBLTAP_ENABLE) && defined(DBLTAP_IMPLEMENTATION)
#include "common/double_tap.h"
#else
// If not building as part of a test, fall back to a standard printf implementation.
#include <stdio.h>
#ifndef note
#define note(...)                 \
    do {                          \
        printf("# " __VA_ARGS__); \
        printf("\n");             \

infix/src/infix.c  view on Meta::CPAN

#include "core/type_registry.c"
// 5. Signature Parser: Implements the high-level string-based API.
//    (Depends on types, arena, and registry).
#include "core/signature.c"
// 6. Dynamic Library Loader: Implements cross-platform `dlopen`/`dlsym`.
//    (Depends on error handling, types, and arena).
#include "core/loader.c"
// 7. Type System: Defines and manages `infix_type` objects and graph algorithms.
//    (Depends on the arena and error handling).
#include "core/types.c"
// 8. Debugging Utilities: Low-level helpers for logging and inspection.
//    (No dependencies).
#include "core/utility.c"
// 9. Platform and processor feature detection.
//    (No dependencies).
#include "core/platform.c"
// 10. Trampoline Engine: The central JIT compiler.
//    This must be last, as it depends on all other components and includes the
//    final ABI- and architecture-specific C files itself.
#include "jit/trampoline.c"

infix/src/jit/executor.c  view on Meta::CPAN

#if defined(INFIX_OS_MACOS)
/**
 * @internal
 * @brief macOS-specific function pointers and types for checking JIT entitlements.
 *
 * @details To support hardened runtimes on Apple platforms (especially Apple Silicon),
 * `infix` must use special APIs like `MAP_JIT` and `pthread_jit_write_protect_np`.
 * However, these are only effective if the host application has been granted the
 * `com.apple.security.cs.allow-jit` entitlement.
 *
 * This logic performs a runtime check for these APIs and the entitlement, gracefully
 * falling back to the legacy (but less secure) `mprotect` method if they are not
 * available. This provides maximum security for production apps while maintaining
 * maximum convenience for developers who may not have codesigned their test executables.
 */
typedef const struct __CFString * CFStringRef;
typedef const void * CFTypeRef;
typedef struct __SecTask * SecTaskRef;
typedef struct __CFError * CFErrorRef;
#define kCFStringEncodingUTF8 0x08000100
// A struct to hold dynamically loaded function pointers from macOS frameworks.

infix/src/jit/executor.c  view on Meta::CPAN

    static bool g_use_secure_jit_path = false;
    if (g_use_secure_jit_path) {
        pthread_jit_write_protect_np(false);  // Make writable region executable.
        result = true;
    }
    else
#endif
        // On macOS with the JIT entitlement, we don't use mprotect. Instead, we toggle
        // a thread-local "write permission" state for all JIT memory. The memory is
        // RX by default, and we temporarily make it RW for writing.
        // However, the current logic does this change via `pthread_jit_write_protect_np`
        // within the allocator itself. For now, this is a placeholder for that logic.
        result = (mprotect(exec.rw_ptr, exec.size, PROT_READ | PROT_EXEC) == 0);
    if (!result)
        _infix_set_system_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_PROTECTION_FAILURE, errno, nullptr);
#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
    // Other single-mapping POSIX platforms use mprotect.
    result = (mprotect(exec.rw_ptr, exec.size, PROT_READ | PROT_EXEC) == 0);
    if (!result)
        _infix_set_system_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_PROTECTION_FAILURE, errno, nullptr);
#else
    // On dual-mapping platforms, the RX mapping is already executable. This is a no-op.

infix/src/jit/trampoline.c  view on Meta::CPAN

 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file trampoline.c
 * @brief The core JIT engine for generating forward and reverse trampolines.
 * @ingroup internal_jit
 *
 * @details This module is the central orchestrator of the `infix` library. It brings
 * together the type system, memory management, and ABI-specific logic to generate
 * executable machine code at runtime.
 *
 * It implements both the high-level Signature API (e.g., `infix_forward_create`)
 * and the low-level Manual API (e.g., `infix_forward_create_manual`). The high-level
 * functions are convenient wrappers that use the signature parser to create the
 * necessary `infix_type` objects before calling the core internal implementation.
 *
 * The core logic is encapsulated in `_infix_forward_create_internal` and
 * `_infix_reverse_create_internal`. These functions follow a clear pipeline:
 * 1.  **Prepare:** Analyze the function signature with the appropriate ABI-specific
 *     `prepare_*_call_frame` function to create a layout blueprint.
 * 2.  **Generate:** Use the layout blueprint to call a sequence of ABI-specific
 *     `generate_*` functions, which emit machine code into a temporary `code_buffer`.
 * 3.  **Finalize:** Allocate executable memory, copy the generated code into it,
 *     create the final self-contained trampoline handle (deep-copying all type
 *     metadata), and make the code executable.
 */
#include "common/infix_internals.h"

infix/src/jit/trampoline.c  view on Meta::CPAN

    }
    code_buffer buf;
    code_buffer_init(&buf, temp_arena);
    // JIT Compilation Pipeline
    // 1. Prepare: Classify arguments and create the layout blueprint.
    status = spec->prepare_forward_call_frame(
        temp_arena, &layout, return_type, arg_types, num_args, num_fixed_args, target_fn);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    // 2. Generate: Emit machine code based on the layout.
    status = spec->generate_forward_prologue(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_forward_argument_moves(&buf, layout, arg_types, num_args, num_fixed_args);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_forward_call_instruction(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_forward_epilogue(&buf, layout, return_type);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    if (buf.error || temp_arena->error) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    // Finalize Handle
    handle = infix_calloc(1, sizeof(infix_forward_t));
    if (handle == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;

infix/src/jit/trampoline.c  view on Meta::CPAN

    }
    code_buffer buf;
    code_buffer_init(&buf, temp_arena);

    // 2. JIT Compilation Pipeline
    status = spec->prepare_direct_forward_call_frame(
        temp_arena, &layout, return_type, arg_types, num_args, handlers, target_fn);
    if (status != INFIX_SUCCESS)
        goto cleanup;

    status = spec->generate_direct_forward_prologue(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;

    status = spec->generate_direct_forward_argument_moves(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;

    status = spec->generate_direct_forward_call_instruction(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;

    status = spec->generate_direct_forward_epilogue(&buf, layout, return_type);
    if (status != INFIX_SUCCESS)
        goto cleanup;

    if (buf.error || temp_arena->error) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }

    // 3. Finalize Handle
    handle = infix_calloc(1, sizeof(infix_forward_t));

infix/src/jit/trampoline.c  view on Meta::CPAN

                                             context->num_args,
                                             context->num_fixed_args,
                                             user_callback_fn);
        if (status != INFIX_SUCCESS)
            goto cleanup;
    }
    // JIT Compilation Pipeline for Reverse Stub
    status = spec->prepare_reverse_call_frame(temp_arena, &layout, context);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_reverse_prologue(&buf, layout);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_reverse_argument_marshalling(&buf, layout, context);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_reverse_dispatcher_call(&buf, layout, context);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    status = spec->generate_reverse_epilogue(&buf, layout, context);
    if (status != INFIX_SUCCESS)
        goto cleanup;
    // End of Pipeline
    if (buf.error || temp_arena->error) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    context->exec = infix_executable_alloc(buf.size);
    if (context->exec.rw_ptr == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;

infix/src/jit/trampoline.c  view on Meta::CPAN

    status = infix_reverse_create_closure_manual(
        out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn, user_data);
    infix_arena_destroy(arena);
    return status;
}
// ============================================================================
//                       UNITY BUILD INCLUDES
// This section includes the actual ABI implementations at the end of the file.
// Because `trampoline.c` is the central translation unit, including the
// correct ABI-specific .c file here makes its functions (`g_win_x64_spec`, etc.)
// available without needing to add platform-specific logic to the build system.
// The `infix_config.h` header ensures only one of these #if blocks is active.
// ============================================================================
#if defined(INFIX_ABI_WINDOWS_X64)
#include "../arch/x64/abi_win_x64.c"
#include "../arch/x64/abi_x64_emitters.c"
#elif defined(INFIX_ABI_SYSV_X64)
#include "../arch/x64/abi_sysv_x64.c"
#include "../arch/x64/abi_x64_emitters.c"
#elif defined(INFIX_ABI_AAPCS64)
#include "../arch/aarch64/abi_arm64.c"

lib/Affix.c  view on Meta::CPAN

// Close switch and break loop
#define DISPATCH_END() \
    }                  \
    break;             \
    }

// No table needed
#define DEFINE_DISPATCH_TABLE()
#endif

// We use a macro to generate two variants (Stack vs Arena) to ensure logic sync.
#define GENERATE_TRIGGER_XSUB(NAME, USE_STACK_ALLOC)                                                         \
    void NAME(pTHX_ CV * cv) {                                                                               \
        dSP;                                                                                                 \
        dAXMARK;                                                                                             \
        dXSTARG;                                                                                             \
        Affix * affix = (Affix *)CvXSUBANY(cv).any_ptr;                                                      \
                                                                                                             \
        if (UNLIKELY((SP - MARK) != affix->num_args))                                                        \
            croak("Wrong number of arguments. Expected %d, got %d", (int)affix->num_args, (int)(SP - MARK)); \
                                                                                                             \

lib/Affix.c  view on Meta::CPAN

    void * new_addr = (char *)ptr_val + offset;

    Affix_Pin * new_pin;
    Newxz(new_pin, 1, Affix_Pin);
    new_pin->pointer = new_addr;
    new_pin->managed = false;  // Aliases are never managed
    new_pin->type_arena = infix_arena_create(256);

    if (type) {
        if (type->category == INFIX_TYPE_ARRAY) {
            // Decay Array[T] -> Pointer[T] logic.
            // When adding an offset to an array, the result is a pointer to the element type,
            // not a new array. This allows dereferencing assignment ($$ptr = val) to work correctly.
            const infix_type * elem_src = type->meta.array_info.element_type;
            const infix_type * elem_copy = _copy_type_graph_to_arena(new_pin->type_arena, elem_src);

            // Cast to (infix_type*) to satisfy signature; safe because we just allocated it in our arena.
            if (infix_type_create_pointer_to(
                    new_pin->type_arena, (infix_type **)&new_pin->type, (infix_type *)elem_copy) != INFIX_SUCCESS) {
                infix_arena_destroy(new_pin->type_arena);
                safefree(new_pin);

lib/Affix.c  view on Meta::CPAN

        XSUB_EXPORT(strdup, "$", "memory");
        XSUB_EXPORT(strnlen, "$$", "memory");
        XSUB_EXPORT(is_null, "$", "memory");
    }

    newXS("Affix::get_system_error", Affix_get_system_error, __FILE__);
    (void)newXSproto_portable("Affix::set_destruct_level", Affix_set_destruct_level, __FILE__, "$");

#undef XSUB_EXPORT

    Perl_xs_boot_epilog(aTHX_ ax);
}

lib/Affix.h  view on Meta::CPAN

                                    SV ** perl_stack_frame,
                                    void * args_buffer,
                                    void ** c_args,
                                    void * ret_buffer);
/// Function pointer type for a "pull" operation: marshalling from C (void*) to Perl (SV).
typedef void (*Affix_Pull)(pTHX_ Affix * affix, SV *, const infix_type *, void *);
/// Function pointer type for a "push" operation: marshalling from Perl (SV) to C (void*).
typedef void (*Affix_Push_Handler)(pTHX_ Affix * affix, SV *, void *);
/**
 * Function pointer for a specialized out-parameter write-back handler.
 * By pre-resolving this function, we avoid conditional logic in the hot path.
 * @param pTHX_ The Perl interpreter context.
 * @param affix The main Affix context object.
 * @param info A pointer to the OutParamInfo struct for this parameter.
 * @param perl_sv The referenced SV* to be modified (e.g., the scalar backing `$$foo`).
 * @param c_arg_ptr The pointer from the c_args array (e.g., `T**` for a `T*` out-param).
 */
typedef void (*Affix_Out_Param_Writer)(pTHX_ Affix * affix, const OutParamInfo * info, SV * perl_sv, void * c_arg_ptr);
/// Stores the pre-calculated information needed to write back an "out" parameter.
struct OutParamInfo {
    size_t perl_stack_index;          // Index of the SV* in the perl_stack_frame

lib/Affix.pm  view on Meta::CPAN

                    $values_map->{$final_val} //= $name;
                    $counter = $final_val + 1;
                }
                return ( $const_map, $values_map );
            }

            # A pure-perl expression solver (Shunting-yard algorithm)
            # Handles: + - * / | & ^ ( )
            method _calculate_expr( $expr, $lookup ) {

                # 1. Tokenize: Split on operators, keep parens and logic ops
                # Include '%' in the regex
                my @raw_tokens = split /([+\-*\/%|&^()])/, $expr;
                my @tokens;
                foreach my $t (@raw_tokens) {

                    # Skip if undefined or purely whitespace
                    next unless defined $t && $t =~ /\S/;

                    # Trim leading/trailing whitespace from identifiers
                    $t =~ s/^\s+|\s+$//g;

lib/Affix.pm  view on Meta::CPAN

            #~ https://svn.eiffel.com/eiffelstudio-public/branches/Eiffel_54/Delivery/docs/papers/dll.html
            field $eiffel : reader : param //= _can_run qw[se];

            #~ https://dlang.org/articles/dll-linux.html#dso9
            #~ dmd -c dll.d -fPIC
            #~ dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/library/is
            field $d       : reader : param //= _can_run qw[dmd];
            field $fortran : reader : param //= _can_run qw[gfortran ifx ifort];

            #~ https://github.com/secana/Native-FSharp-Library
            #~ https://secanablog.wordpress.com/2020/02/01/writing-a-native-library-in-f-which-can-be-called-from-c/
            field $fsharp : reader : param //= _can_run qw[dotnet];

            #~ https://futhark.readthedocs.io/en/stable/usage.html
            field $futhark : reader : param //= _can_run qw[futhark];    # .fut => .c

            #~ https://medium.com/@walkert/fun-building-shared-libraries-in-go-639500a6a669
            #~ https://github.com/vladimirvivien/go-cshared-examples
            field $go : reader : param //= _can_run qw[go];

            #~ https://github.com/bennoleslie/haskell-shared-example

lib/Affix.pm  view on Meta::CPAN

            #~ https://peterme.net/dynamic-libraries-in-nim.html
            field $nim : reader : param //= _can_run qw[nim];    # .nim => .c

            #~ https://odin-lang.org/news/calling-odin-from-python/
            field $odin : reader : param //= _can_run qw[odin];

            #~ https://p-org.github.io/P/getstarted/install/#step-4-recommended-ide-optional
            #~ https://p-org.github.io/P/getstarted/usingP/#compiling-a-p-program
            field $p : reader : param //= _can_run qw[p];    # .p => C#

            #~ https://blog.asleson.org/2021/02/23/how-to-writing-a-c-shared-library-in-rust/
            field $rust : reader : param //= _can_run qw[cargo];

            #~ swiftc point.swift -emit-module -emit-library
            #~ https://forums.swift.org/t/creating-a-c-accessible-shared-library-in-swift/45329/5
            #~ https://theswiftdev.com/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/#should-i-choose-dynamic-or-static-linking
            field $swift : reader : param //= _can_run qw[swiftc];

            #~ https://www.rangakrish.com/index.php/2023/04/02/building-v-language-dll/
            #~ https://dev.to/piterweb/how-to-create-and-use-dlls-on-vlang-1p13
            field $v : reader : param //= _can_run qw[v];



( run in 2.067 seconds using v1.01-cache-2.11-cpan-0371d4a6215 )