Affix

 view release on metacpan or  search on metacpan

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

/**
 * Copyright (c) 2025 Sanko Robinson
 *
 * This source code is dual-licensed under the Artistic License 2.0 or the MIT License.
 * You may choose to use this code under the terms of either license.
 *
 * 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 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"
#include "common/utility.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(INFIX_OS_MACOS)
#include <pthread.h>
#endif
#if defined(INFIX_OS_WINDOWS)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif
// Forward Declaration for Internal Creation Function
static infix_status _infix_reverse_create_internal(infix_reverse_t ** out_context,
                                                   infix_type * return_type,
                                                   infix_type ** arg_types,
                                                   size_t num_args,
                                                   size_t num_fixed_args,
                                                   void * user_callback_fn,
                                                   void * user_data,
                                                   bool is_callback);
// ABI Specification V-Table Declarations (extern to link to the specific implementations)
#if defined(INFIX_ABI_WINDOWS_X64)
extern const infix_forward_abi_spec g_win_x64_forward_spec;
extern const infix_reverse_abi_spec g_win_x64_reverse_spec;
extern const infix_direct_forward_abi_spec g_win_x64_direct_forward_spec;
#elif defined(INFIX_ABI_SYSV_X64)
extern const infix_forward_abi_spec g_sysv_x64_forward_spec;
extern const infix_reverse_abi_spec g_sysv_x64_reverse_spec;
extern const infix_direct_forward_abi_spec g_sysv_x64_direct_forward_spec;
#elif defined(INFIX_ABI_AAPCS64)
extern const infix_forward_abi_spec g_arm64_forward_spec;
extern const infix_reverse_abi_spec g_arm64_reverse_spec;
extern const infix_direct_forward_abi_spec g_arm64_direct_forward_spec;
#endif
/**
 * @internal
 * @brief Retrieves a pointer to the ABI specification v-table for forward calls.
 * @details This function is the entry point to the ABI abstraction layer. It uses
 * compile-time preprocessor macros (defined in `infix_config.h`) to select and
 * return the correct v-table for the target platform.
 * @return A pointer to the active `infix_forward_abi_spec`, or `nullptr` if the
 *         platform is unsupported.
 */
const infix_forward_abi_spec * get_current_forward_abi_spec() {
#if defined(INFIX_ABI_WINDOWS_X64)
    return &g_win_x64_forward_spec;
#elif defined(INFIX_ABI_SYSV_X64)
    return &g_sysv_x64_forward_spec;
#elif defined(INFIX_ABI_AAPCS64)
    return &g_arm64_forward_spec;
#else

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

        return true;  // Primitives, void, etc., are always resolved.
    }
}
/**
 * @internal
 * @brief Public-internal wrapper for the recursive resolution check.
 */
static bool _is_type_graph_resolved(const infix_type * type) { return _is_type_graph_resolved_recursive(type, NULL); }
/**
 * @internal
 * @brief Estimates the memory required to store the type metadata for a function signature.
 *
 * This function iterates through the return and argument types, using the internal
 * _infix_estimate_graph_size utility to sum the required bytes for a deep copy
 * of all type information.
 *
 * @param temp_arena A temporary arena for the estimator's bookkeeping.
 * @param return_type The function's return type.
 * @param arg_types The array of argument types.
 * @param num_args The number of arguments.
 * @return The estimated size in bytes.
 */
static size_t _estimate_metadata_size(infix_arena_t * temp_arena,
                                      infix_type * return_type,
                                      infix_type ** arg_types,
                                      size_t num_args) {
    size_t total_size = 0;
    total_size += _infix_estimate_graph_size(temp_arena, return_type);
    if (arg_types != nullptr) {
        // Add space for the arg_types pointer array itself.
        total_size += sizeof(infix_type *) * num_args;
        for (size_t i = 0; i < num_args; ++i)
            total_size += _infix_estimate_graph_size(temp_arena, arg_types[i]);
    }
    return total_size;
}
// Forward Trampoline API Implementation
INFIX_API c23_nodiscard infix_unbound_cif_func infix_forward_get_unbound_code(infix_forward_t * trampoline) {
    if (trampoline == nullptr || trampoline->is_direct_trampoline || trampoline->target_fn != nullptr)
        return nullptr;
    return (infix_unbound_cif_func)trampoline->exec.rx_ptr;
}
INFIX_API c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t * trampoline) {
    if (trampoline == nullptr || trampoline->is_direct_trampoline || trampoline->target_fn == nullptr)
        return nullptr;
    return (infix_cif_func)trampoline->exec.rx_ptr;
}
INFIX_API c23_nodiscard infix_direct_cif_func infix_forward_get_direct_code(infix_forward_t * trampoline) {
    if (trampoline == nullptr || !trampoline->is_direct_trampoline)
        return nullptr;
    return (infix_direct_cif_func)trampoline->exec.rx_ptr;
}
/**
 * @internal
 * @brief The core implementation for creating a forward trampoline.
 *
 * This function orchestrates the JIT compilation pipeline:
 * 1. Validates input types to ensure they are fully resolved.
 * 2. Selects the appropriate ABI specification v-table for the target platform.
 * 3. Creates a temporary arena for all intermediate allocations (layout, code buffer).
 * 4. Invokes the ABI spec functions in sequence to generate the call frame layout and machine code.
 * 5. Allocates the final `infix_forward_t` handle.
 * 6. Creates a private arena for the handle and deep-copies all type info into it,
 *    making the handle completely self-contained and independent of its source types.
 * 7. Allocates executable memory, copies the generated code, and makes it executable.
 *
 * @param[out] out_trampoline Receives the created trampoline handle.
 * @param[in] target_arena The arena to eventually store the trampoline in.
 * @param[in] return_type The fully resolved return type.
 * @param[in] arg_types An array of fully resolved argument types.
 * @param[in] num_args Total number of arguments.
 * @param[in] num_fixed_args Number of fixed (non-variadic) arguments.
 * @param[in] target_fn The target function pointer, or `nullptr` for an unbound trampoline.
 * @return `INFIX_SUCCESS` on success.
 */
static infix_status _infix_forward_create_impl(infix_forward_t ** out_trampoline,
                                               infix_arena_t * target_arena,
                                               infix_type * return_type,
                                               infix_type ** arg_types,
                                               size_t num_args,
                                               size_t num_fixed_args,
                                               void * target_fn,
                                               bool is_safe) {
    if (out_trampoline == nullptr || return_type == nullptr || (arg_types == nullptr && num_args > 0)) {
        _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    // Pre-flight check: ensure all types are resolved before passing to ABI layer.
    if (!_is_type_graph_resolved(return_type)) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    for (size_t i = 0; i < num_args; ++i) {
        if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
            _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
            return INFIX_ERROR_INVALID_ARGUMENT;
        }
    }

    infix_status status = INFIX_SUCCESS;
    infix_call_frame_layout * layout = nullptr;
    infix_forward_t * handle = nullptr;

    // Use a temporary arena for all intermediate allocations during code generation.
    infix_arena_t * temp_arena = infix_arena_create(65536);
    if (!temp_arena) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
        return INFIX_ERROR_ALLOCATION_FAILED;
    }

    // --- Cache Lookup Stage ---
    // Generate a canonical signature for deduplication.
    char canonical_sig[8192];
    infix_status sig_status = INFIX_SUCCESS;
    {
        // Construct a temporary argument array for the printer.
        infix_function_argument * tmp_args = nullptr;
        if (num_args > 0) {
            tmp_args =
                infix_arena_alloc(temp_arena, sizeof(infix_function_argument) * num_args, _Alignof(infix_type *));
            if (!tmp_args) {
                status = INFIX_ERROR_ALLOCATION_FAILED;
                goto cleanup;
            }
            for (size_t i = 0; i < num_args; ++i) {
                tmp_args[i].type = arg_types[i];
                tmp_args[i].name = nullptr;
            }
        }
        sig_status = infix_function_print(canonical_sig,
                                          sizeof(canonical_sig),
                                          "func",
                                          return_type,
                                          tmp_args,
                                          num_args,
                                          num_fixed_args,
                                          INFIX_DIALECT_SIGNATURE);
    }

    if (sig_status == INFIX_SUCCESS) {
        infix_forward_t * cached = _infix_cache_lookup(canonical_sig, target_fn, is_safe);
        if (cached) {
            *out_trampoline = cached;
            status = INFIX_SUCCESS;
            goto cleanup;
        }
    }

    const infix_forward_abi_spec * spec = get_current_forward_abi_spec();
    if (spec == nullptr) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNSUPPORTED_ABI, 0);
        status = INFIX_ERROR_UNSUPPORTED_ABI;
        goto cleanup;
    }

    code_buffer buf;
    code_buffer_init(&buf, temp_arena);
    // JIT Compilation Pipeline
    // 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;
        goto cleanup;
    }
    // "Estimate" stage: Calculate the exact size needed for the handle's private arena.
    size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
    if (target_arena) {
        handle->arena = target_arena;
        handle->is_external_arena = true;
    }
    else {
        handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
        handle->is_external_arena = false;
    }
    if (handle->arena == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    // "Copy" stage: Deep copy all type info into the handle's private arena.
    handle->return_type = _copy_type_graph_to_arena(handle->arena, return_type);
    if (num_args > 0) {
        handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
        if (handle->arg_types == nullptr) {
            status = INFIX_ERROR_ALLOCATION_FAILED;
            goto cleanup;
        }
        for (size_t i = 0; i < num_args; ++i) {
            handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
            // Check for allocation failure during copy
            if (arg_types[i] != nullptr && handle->arg_types[i] == nullptr && !handle->arena->error) {
                status = INFIX_ERROR_ALLOCATION_FAILED;
                goto cleanup;
            }
        }
    }
    handle->num_args = num_args;
    handle->num_fixed_args = num_fixed_args;
    handle->target_fn = target_fn;
    handle->is_safe = is_safe;
    handle->ref_count = 1;

    // Save the canonical signature for the cache key.
    if (sig_status == INFIX_SUCCESS) {
        size_t sig_len = strlen(canonical_sig) + 1;
        handle->signature = infix_arena_alloc(handle->arena, sig_len, 1);
        if (handle->signature)
            infix_memcpy(handle->signature, canonical_sig, sig_len);
    }

    // Allocate and finalize executable memory.
    handle->exec = infix_executable_alloc(buf.size);
    if (handle->exec.rw_ptr == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
    if (!infix_executable_make_executable(&handle->exec,
                                          is_safe ? INFIX_EXECUTABLE_SAFE_FORWARD : INFIX_EXECUTABLE_FORWARD,
                                          layout->prologue_size,
                                          layout->epilogue_offset)) {
        status = INFIX_ERROR_PROTECTION_FAILED;
        goto cleanup;
    }
    infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Forward Trampoline Machine Code");
    *out_trampoline = handle;

    // Cache the newly created trampoline.
    // We ONLY cache if we own the arena. If the user provided an external arena,
    // they control the lifetime, and we can't guarantee the signature string
    // (which is in that arena) will remain valid as long as the cache entry exists.
    if (handle->signature && !handle->is_external_arena)
        _infix_cache_insert(handle);

cleanup:
    // If any step failed, ensure the partially created handle is fully destroyed.
    if (status != INFIX_SUCCESS && handle != nullptr)
        infix_forward_destroy(handle);
    // The temporary arena is always destroyed.
    infix_arena_destroy(temp_arena);
    return status;
}
/**
 * @internal
 * @brief The core implementation for creating a direct marshalling forward trampoline.
 *
 * This function orchestrates the JIT compilation pipeline for the direct marshalling
 * feature. It is the internal counterpart to the public `infix_forward_create_direct`
 * function and is called after the signature string has been parsed into a type graph.
 *
 * The pipeline is as follows:
 * 1.  Selects the appropriate `infix_direct_forward_abi_spec` v-table for the target platform.
 * 2.  Invokes `prepare_direct_forward_call_frame` to analyze the signature and handlers,
 *     producing a complete layout blueprint.
 * 3.  Calls the `generate_*` functions from the v-table in sequence. This emits machine code
 *     that includes direct calls to the user-provided marshaller and write-back functions.
 * 4.  Finalizes the `infix_forward_t` handle, marking it as a `is_direct_trampoline`.
 * 5.  Allocates executable memory, copies the generated code, and makes it executable.
 *
 * @param[out] out_trampoline Receives the created trampoline handle.
 * @param[in] return_type The fully resolved return type.
 * @param[in] arg_types An array of fully resolved argument types.
 * @param[in] num_args Total number of arguments.
 * @param[in] target_fn The target C function pointer.
 * @param[in] handlers An array of handler structs provided by the user.
 * @return `INFIX_SUCCESS` on success, or an error code on failure.
 */
static infix_status _infix_forward_create_direct_impl(infix_forward_t ** out_trampoline,
                                                      infix_type * return_type,
                                                      infix_type ** arg_types,
                                                      size_t num_args,
                                                      void * target_fn,
                                                      infix_direct_arg_handler_t * handlers) {
    // Validation and Setup
    if (!out_trampoline || !return_type || (!arg_types && num_args > 0) || !target_fn || !handlers) {
        _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }

    const infix_direct_forward_abi_spec * spec = get_current_direct_forward_abi_spec();
    if (spec == nullptr) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNSUPPORTED_ABI, 0);
        return INFIX_ERROR_UNSUPPORTED_ABI;
    }

    infix_status status = INFIX_SUCCESS;
    infix_direct_call_frame_layout * layout = nullptr;
    infix_forward_t * handle = nullptr;
    infix_arena_t * temp_arena = infix_arena_create(65536);
    if (!temp_arena) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
        return INFIX_ERROR_ALLOCATION_FAILED;
    }
    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));
    if (handle == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }

    handle->is_direct_trampoline = true;  // Mark this as a direct marshalling trampoline.

    size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
    handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
    handle->is_external_arena = false;
    if (handle->arena == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }

    handle->return_type = _copy_type_graph_to_arena(handle->arena, return_type);
    if (num_args > 0) {
        handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
        if (!handle->arg_types) {
            status = INFIX_ERROR_ALLOCATION_FAILED;
            goto cleanup;
        }
        for (size_t i = 0; i < num_args; ++i) {
            handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
            if (arg_types[i] && !handle->arg_types[i] && !handle->arena->error) {
                status = INFIX_ERROR_ALLOCATION_FAILED;
                goto cleanup;
            }
        }
    }
    handle->num_args = num_args;
    handle->num_fixed_args = num_args;  // Direct trampolines are always fixed-arity.
    handle->target_fn = target_fn;

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

    // sysconf is the standard POSIX way to get system configuration values.
    return sysconf(_SC_PAGESIZE);
#endif
}
/**
 * @internal
 * @brief The core implementation for creating a reverse trampoline (callback or closure).
 *
 * @details This function orchestrates the JIT compilation pipeline for reverse calls.
 * It has a special `is_callback` flag that distinguishes between the two reverse
 * trampoline models:
 *
 * - **Type-safe Callback (`is_callback = true`):** In this model, the user provides a
 *   standard C function pointer with a matching signature. This function internally
 *   creates a *forward* trampoline (`cached_forward_trampoline`) that is used by the
 *   universal C dispatcher to call the user's handler in a type-safe way.
 *
 * - **Generic Closure (`is_callback = false`):** The user provides a generic handler of
 *   type `infix_closure_handler_fn`. The universal dispatcher calls this handler
 *   directly, without needing a cached forward trampoline.
 *
 * For security, the entire `infix_reverse_t` context struct is allocated in a
 * special page-aligned memory region that is made read-only after initialization.
 *
 * @param is_callback `true` to create a type-safe callback, `false` for a generic closure.
 * @return `INFIX_SUCCESS` on success.
 */
static infix_status _infix_reverse_create_internal(infix_reverse_t ** out_context,
                                                   infix_type * return_type,
                                                   infix_type ** arg_types,
                                                   size_t num_args,
                                                   size_t num_fixed_args,
                                                   void * user_callback_fn,
                                                   void * user_data,
                                                   bool is_callback) {
    if (out_context == nullptr || return_type == nullptr || num_fixed_args > num_args) {
        _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    // Pre-flight check: ensure all types are fully resolved.
    if (!_is_type_graph_resolved(return_type)) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    if (arg_types == nullptr && num_args > 0) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    for (size_t i = 0; i < num_args; ++i) {
        if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
            _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNRESOLVED_NAMED_TYPE, 0);
            return INFIX_ERROR_INVALID_ARGUMENT;
        }
    }
    const infix_reverse_abi_spec * spec = get_current_reverse_abi_spec();
    if (spec == nullptr) {
        _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_UNSUPPORTED_ABI, 0);
        return INFIX_ERROR_UNSUPPORTED_ABI;
    }
    infix_status status = INFIX_SUCCESS;
    infix_reverse_call_frame_layout * layout = nullptr;
    infix_reverse_t * context = nullptr;
    infix_arena_t * temp_arena = nullptr;
    infix_protected_t prot = {.rw_ptr = nullptr, .size = 0};
    code_buffer buf;
    temp_arena = infix_arena_create(65536);
    if (!temp_arena) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
        return INFIX_ERROR_ALLOCATION_FAILED;
    }
    code_buffer_init(&buf, temp_arena);
    // Security Hardening: Allocate the context struct itself in special, page-aligned
    // memory that can be made read-only after initialization.
    size_t page_size = get_page_size();
    size_t context_alloc_size = (sizeof(infix_reverse_t) + page_size - 1) & ~(page_size - 1);
    prot = infix_protected_alloc(context_alloc_size);
    if (prot.rw_ptr == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    context = (infix_reverse_t *)prot.rw_ptr;
    infix_memset(context, 0, context_alloc_size);
    // "Estimate" stage: Calculate the exact size needed for the context's private arena.
    size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
    // Create the context's private arena with the calculated size plus some headroom for safety.
    context->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
    if (context->arena == nullptr) {
        status = INFIX_ERROR_ALLOCATION_FAILED;
        goto cleanup;
    }
    // Populate the context fields.
    context->protected_ctx = prot;
    context->num_args = num_args;
    context->num_fixed_args = num_fixed_args;
    context->is_variadic = (num_fixed_args < num_args);
    context->user_callback_fn = user_callback_fn;
    context->user_data = user_data;
    context->internal_dispatcher = infix_internal_dispatch_callback_fn_impl;
    context->cached_forward_trampoline = nullptr;
    // "Copy" stage: deep copy all types into the context's private arena.
    context->return_type = _copy_type_graph_to_arena(context->arena, return_type);
    if (num_args > 0) {
        context->arg_types = infix_arena_alloc(context->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
        if (context->arg_types == nullptr) {
            status = INFIX_ERROR_ALLOCATION_FAILED;
            goto cleanup;
        }
        for (size_t i = 0; i < num_args; ++i) {
            context->arg_types[i] = _copy_type_graph_to_arena(context->arena, arg_types[i]);
            if (arg_types[i] != nullptr && context->arg_types[i] == nullptr) {
                status = INFIX_ERROR_ALLOCATION_FAILED;
                goto cleanup;
            }
        }
    }
    // Special step for type-safe callbacks: generate and cache a forward trampoline
    // that will be used to call the user's type-safe C handler.
    if (is_callback) {
        status = infix_forward_create_manual(&context->cached_forward_trampoline,
                                             context->return_type,
                                             context->arg_types,
                                             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;
        goto cleanup;
    }
    infix_memcpy(context->exec.rw_ptr, buf.code, buf.size);
    if (!infix_executable_make_executable(&context->exec, INFIX_EXECUTABLE_REVERSE, layout->prologue_size, 0)) {
        status = INFIX_ERROR_PROTECTION_FAILED;
        goto cleanup;
    }
    // Security Hardening: Make the context memory read-only to prevent runtime corruption.
    if (!infix_protected_make_readonly(context->protected_ctx)) {
        status = INFIX_ERROR_PROTECTION_FAILED;
        goto cleanup;
    }
    infix_dump_hex(context->exec.rx_ptr, buf.size, "Reverse Trampoline Machine Code");
    *out_context = context;
cleanup:
    if (status != INFIX_SUCCESS) {
        // If allocation of the context itself failed, prot.rw_ptr will be null.
        if (prot.rw_ptr != nullptr)
            infix_reverse_destroy(context);
    }
    infix_arena_destroy(temp_arena);
    return status;
}
/**
 * @brief Creates a type-safe reverse trampoline (callback) from `infix_type` objects (Manual API).
 * @param[out] out_context Receives the created context handle.
 * @param[in] return_type The function's return type.
 * @param[in] arg_types An array of argument types.
 * @param[in] num_args The number of arguments.
 * @param[in] num_fixed_args The number of non-variadic arguments.
 * @param[in] user_callback_fn A pointer to the type-safe C handler function.
 * @return `INFIX_SUCCESS` on success.
 */
INFIX_API c23_nodiscard infix_status infix_reverse_create_callback_manual(infix_reverse_t ** out_context,
                                                                          infix_type * return_type,
                                                                          infix_type ** arg_types,
                                                                          size_t num_args,
                                                                          size_t num_fixed_args,



( run in 0.738 second using v1.01-cache-2.11-cpan-df04353d9ac )