Affix
view release on metacpan or search on metacpan
infix/src/arch/x64/abi_sysv_x64.c view on Meta::CPAN
continue;
if (classify_recursive(member->type, member_offset, classes, depth + 1, field_count, member->is_bitfield))
return true; // Propagate unaligned discovery
}
return false;
}
return false;
}
/**
* @internal
* @brief Classifies an aggregate type for argument passing according to the System V ABI.
* @details This function implements the complete classification algorithm. An aggregate
* is broken down into up to two "eightbytes". Each is classified as INTEGER,
* SSE, or MEMORY. If the size is > 16 bytes or classification fails, it's MEMORY.
*
* @param type The aggregate type to classify.
* @param[out] classes An array of two `arg_class_t` to be filled.
* @param[out] num_classes The number of valid classes (1 or 2).
*/
static void classify_aggregate_sysv(const infix_type * type, arg_class_t classes[2], size_t * num_classes) {
// Initialize to a clean state.
classes[0] = NO_CLASS;
classes[1] = NO_CLASS;
*num_classes = 0;
// If the size is greater than 16 bytes, it's passed in memory.
if (type->size > 16) {
classes[0] = MEMORY;
*num_classes = 1;
return;
}
// Run the recursive classification. If it returns true, an unaligned
// field was found, and the class is already set to MEMORY. We can stop.
size_t field_count = 0; // Initialize the counter for this aggregate.
if (classify_recursive(type, 0, classes, 0, &field_count, false)) { // Pass counter to initial call
*num_classes = 1;
return;
}
// Post-processing for alignment padding.
if (type->size > 0 && classes[0] == NO_CLASS)
classes[0] = INTEGER;
if (type->size > 8 && classes[1] == NO_CLASS)
classes[1] = INTEGER;
// Count the number of valid, classified eightbytes.
if (classes[0] != NO_CLASS)
(*num_classes)++;
if (classes[1] != NO_CLASS)
(*num_classes)++;
}
/**
* @internal
* @brief Stage 1 (Forward): Analyzes a signature and creates a call frame layout for System V.
* @details This function iterates through a function's arguments, classifying each one
* to determine its location (GPR, XMM, or stack) according to the SysV ABI rules.
* @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.
* @return `INFIX_SUCCESS` on success, or an error code on failure.
*/
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) {
if (out_layout == nullptr)
return INFIX_ERROR_INVALID_ARGUMENT;
// Allocate the layout struct that will hold our results.
infix_call_frame_layout * layout =
infix_arena_calloc(arena, 1, sizeof(infix_call_frame_layout), _Alignof(infix_call_frame_layout));
if (layout == nullptr) {
*out_layout = nullptr;
return INFIX_ERROR_ALLOCATION_FAILED;
}
layout->is_variadic = num_args > num_fixed_args;
layout->target_fn = target_fn;
layout->arg_locations =
infix_arena_calloc(arena, num_args, sizeof(infix_arg_location), _Alignof(infix_arg_location));
if (layout->arg_locations == nullptr && num_args > 0) {
*out_layout = nullptr;
return INFIX_ERROR_ALLOCATION_FAILED;
}
// gpr_count and xmm_count track the next available GPR and XMM argument registers.
// current_stack_offset tracks the next available stack slot for arguments.
size_t gpr_count = 0, xmm_count = 0, current_stack_offset = 0;
// Determine if the return value requires a hidden pointer argument passed in RDI.
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);
// Rule 1: Aggregates larger than 16 bytes are always returned via hidden pointer.
// Exception: 256/512-bit vectors are returned in YMM0/ZMM0.
layout->return_value_in_memory =
(ret_is_aggregate && ret_type->category != INFIX_TYPE_VECTOR && ret_type->size > 16);
// Rule 2: Small aggregates (<= 16 bytes) must also be returned via hidden pointer
// if their classification is MEMORY. This is critical for types like packed structs
// with unaligned members.
if (ret_is_aggregate && !layout->return_value_in_memory) {
arg_class_t ret_classes[2];
size_t num_ret_classes;
classify_aggregate_sysv(ret_type, ret_classes, &num_ret_classes);
if (num_ret_classes > 0 && ret_classes[0] == MEMORY)
layout->return_value_in_memory = true;
}
// Exception: `long double` is a special case and is always returned on the x87
// FPU stack, never via a hidden pointer.
if (is_long_double(ret_type))
layout->return_value_in_memory = false;
// If a hidden pointer is used, it consumes the first GPR (RDI).
if (layout->return_value_in_memory)
gpr_count++;
layout->num_stack_args = 0;
// 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;
( run in 0.628 second using v1.01-cache-2.11-cpan-39bf76dae61 )