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 )