Affix

 view release on metacpan or  search on metacpan

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

            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))
            // `__int128_t` (on GCC/Clang) and 16-byte vectors are returned in XMM0.
            emit_movups_mem_xmm(buf, R13_REG, 0, XMM0_REG);
        else if (ret_type->size == 32 && ret_type->category == INFIX_TYPE_VECTOR)
            emit_vmovupd_mem_ymm(buf, R13_REG, 0, XMM0_REG);
        else if (ret_type->size == 64 && ret_type->category == INFIX_TYPE_VECTOR)
            emit_vmovupd_mem_zmm(buf, R13_REG, 0, XMM0_REG);
        else {
            // All other by-value types are returned in RAX. Use a size-appropriate store.
            switch (ret_type->size) {
            case 1:
                emit_mov_mem_reg8(buf, R13_REG, 0, RAX_REG);
                break;
            case 2:
                // This handles int16 and float16
                emit_mov_mem_reg16(buf, R13_REG, 0, RAX_REG);
                break;
            case 4:
                emit_mov_mem_reg32(buf, R13_REG, 0, RAX_REG);
                break;
            case 8:
                emit_mov_mem_reg(buf, R13_REG, 0, RAX_REG);
                break;
            default:
                break;  // Should be unreachable
            }
        }
    }
    if (layout->max_align >= 32)
        emit_vzeroupper(buf);

    // Restore stack pointer to the saved registers area.
    // RBP was set to RSP after all pushes.
    // mov rsp, rbp
    emit_mov_reg_reg(buf, RSP_REG, RBP_REG);

    // Restore callee-saved registers and return.
    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);
    return INFIX_SUCCESS;
}
/**
 * @internal
 * @brief Stage 1 (Reverse): Calculates the stack layout for a reverse trampoline stub.
 * @details This function determines the total stack space needed by the JIT-compiled stub.
 * This space includes areas to save all incoming argument registers, a buffer for the
 * return value, the `args_array`, a data area for by-value arguments, and the
 * shadow space the stub must provide for the C dispatcher it calls.
 *
 * @param arena The temporary arena for allocations.
 * @param[out] out_layout The resulting reverse call frame layout blueprint, populated with offsets.
 * @param context The reverse trampoline context with full signature information.
 * @return `INFIX_SUCCESS` on success, or an error code on failure.
 */
static infix_status prepare_reverse_call_frame_win_x64(infix_arena_t * arena,
                                                       infix_reverse_call_frame_layout ** out_layout,
                                                       infix_reverse_t * context) {
    infix_reverse_call_frame_layout * layout = infix_arena_calloc(
        arena, 1, sizeof(infix_reverse_call_frame_layout), _Alignof(infix_reverse_call_frame_layout));
    if (!layout)
        return INFIX_ERROR_ALLOCATION_FAILED;
    if (!context || !context->return_type)
        return INFIX_ERROR_INVALID_ARGUMENT;
    // Calculate space needed for each component, ensuring 16-byte alignment for safety.
    size_t return_size = (context->return_type->size + 15) & ~15;
    size_t args_array_size = context->num_args * sizeof(void *);

    size_t gpr_reg_save_area_size = NUM_GPR_ARGS * 8;
    size_t xmm_reg_save_area_size = NUM_XMM_ARGS * 64;  // Reserve 64 bytes for each XMM/YMM/ZMM

    size_t saved_args_data_size = 0;
    size_t max_align = 16;
    for (size_t i = 0; i < context->num_args; ++i) {
        if (context->arg_types[i] == nullptr) {
            *out_layout = nullptr;
            _infix_set_error(INFIX_CATEGORY_ABI, INFIX_CODE_INVALID_MEMBER_TYPE, 0);
            return INFIX_ERROR_INVALID_ARGUMENT;
        }
        size_t align = context->arg_types[i]->alignment;
        if (align < 8)
            align = 8;
        if (align > max_align)
            max_align = align;

        if (!is_passed_by_reference(context->arg_types[i])) {
            saved_args_data_size = _infix_align_up(saved_args_data_size, align);
            saved_args_data_size += context->arg_types[i]->size;
        }
    }
    // Security: Check against excessively large argument data size.
    if (saved_args_data_size > INFIX_MAX_ARG_SIZE) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }

    // The total space needed includes all local data plus the shadow space for the call to the C dispatcher.
    size_t total_local_space = return_size + args_array_size + saved_args_data_size + gpr_reg_save_area_size +
        xmm_reg_save_area_size + SHADOW_SPACE;

    // Add max_align to account for potential internal padding
    total_local_space += max_align;

    // Prevent integer overflow from fuzzer-provided types that are impractically large by ensuring the total required
    // stack space is within a safe limit.
    if (total_local_space > INFIX_MAX_STACK_ALLOC) {
        *out_layout = nullptr;
        return INFIX_ERROR_LAYOUT_FAILED;
    }

    // The total allocation for the stack frame must be aligned to the maximum required alignment.
    layout->total_stack_alloc = (uint32_t)_infix_align_up(total_local_space, max_align);

    // Define the layout of our local stack variables relative to RSP after allocation.
    // [ shadow space (32) | return_buffer | gpr_save | xmm_save | args_array | (padding) | saved_args_data ]
    layout->return_buffer_offset = (int32_t)_infix_align_up(SHADOW_SPACE, max_align);
    layout->gpr_save_area_offset = layout->return_buffer_offset + (int32_t)_infix_align_up(return_size, max_align);
    layout->xmm_save_area_offset =
        layout->gpr_save_area_offset + (int32_t)_infix_align_up(gpr_reg_save_area_size, max_align);
    layout->args_array_offset =
        layout->xmm_save_area_offset + (int32_t)_infix_align_up(xmm_reg_save_area_size, max_align);

    // Ensure proper alignment for the saved arguments area.
    layout->saved_args_offset =
        (int32_t)_infix_align_up((size_t)(layout->args_array_offset + args_array_size), max_align);

    layout->max_align = (uint32_t)max_align;

    *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



( run in 0.920 second using v1.01-cache-2.11-cpan-99c4e6809bf )