Affix

 view release on metacpan or  search on metacpan

infix/src/arch/riscv/abi_riscv64.c  view on Meta::CPAN

        rv64_mem_flw(buf, fa, base, off);
}

//
// Classification
//

/** @internal A single scalar leaf of an aggregate, in struct order. */
typedef struct {
    size_t offset;  /**< Byte offset of the leaf within the aggregate. */
    size_t size;    /**< Leaf size in bytes (1, 2, 4, 8 or 16 for a long double). */
    bool is_fp;     /**< `true` if the leaf is a `float`/`double` scalar. */
    bool is_signed; /**< `true` for signed primitive integer leaves narrower than XLEN. */
} rv64_all_leaf;

/** @internal A flattened, in-order list of every scalar leaf of an aggregate. */
typedef struct {
    size_t count;
    rv64_all_leaf leaves[RV_MAX_FLATTENED_FIELDS];
} rv64_all_leaf_list;

/**
 * @internal
 * @brief Recursively flatten every scalar leaf of an aggregate (structs and arrays).
 * @details Unions are not flattened; `_Complex` types expand into two leaves of the
 *          component type. Returns `false` if a leaf exceeds the leaf-table capacity,
 *          the type graph is pathologically deep, or a malformed (null) node is reached.
 */
static bool rv64_flatten_all_recursive(const infix_type * type,
                                       size_t base_offset,
                                       rv64_all_leaf_list * out,
                                       size_t depth) {
    // A recursive call can be made with a NULL type from a malformed aggregate.
    if (type == nullptr)
        return false;  // Terminate this recursion path.
    // Give up on pathologically deep type graphs instead of exhausting the stack.
    if (depth > RV_MAX_FLATTEN_DEPTH)
        return false;
    if (type->category == INFIX_TYPE_STRUCT) {
        if (type->meta.aggregate_info.members == nullptr)
            return false;
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
            const infix_struct_member * member = &type->meta.aggregate_info.members[i];
            if (member->type == nullptr)
                return false;
            // Check the leaf-table capacity before descending any further.
            if (out->count >= RV_MAX_FLATTENED_FIELDS)
                return false;
            if (!rv64_flatten_all_recursive(member->type, base_offset + member->offset, out, depth + 1))
                return false;
        }
        return true;
    }
    if (type->category == INFIX_TYPE_ARRAY) {
        if (type->meta.array_info.element_type == nullptr)
            return false;
        // A zero-sized element never advances the offset, so iterating every
        // element is pointless and, for chains like `a[127][127][...][0]`,
        // explodes exponentially without ever producing a leaf (which would
        // otherwise trip the leaf-table capacity guard). Flatten the element
        // type just once at the starting offset.
        if (type->meta.array_info.element_type->size == 0) {
            if (type->meta.array_info.num_elements > 0)
                return rv64_flatten_all_recursive(type->meta.array_info.element_type, base_offset, out, depth + 1);
            return true;  // An empty array has no effect on the leaf list.
        }
        for (size_t i = 0; i < type->meta.array_info.num_elements; ++i) {
            // Check the leaf-table capacity before each recursive call.
            if (out->count >= RV_MAX_FLATTENED_FIELDS)
                return false;
            if (!rv64_flatten_all_recursive(type->meta.array_info.element_type,
                                            base_offset + i * type->meta.array_info.element_type->size,
                                            out,
                                            depth + 1))
                return false;
        }
        return true;
    }
    if (type->category == INFIX_TYPE_COMPLEX) {
        // `_Complex double` is the two FP scalars {real, imag}.
        size_t comp_size = type->meta.complex_info.base_type ? type->meta.complex_info.base_type->size : type->size / 2;
        if (out->count + 2 > RV_MAX_FLATTENED_FIELDS)
            return false;
        out->leaves[out->count++] = (rv64_all_leaf){base_offset, comp_size, true, false};
        out->leaves[out->count++] = (rv64_all_leaf){base_offset + comp_size, comp_size, true, false};
        return true;
    }
    if (out->count >= RV_MAX_FLATTENED_FIELDS)
        return false;
    bool is_signed = type->category == INFIX_TYPE_PRIMITIVE && type->size < 8 &&
        (type->meta.primitive_id == INFIX_PRIMITIVE_SINT8 || type->meta.primitive_id == INFIX_PRIMITIVE_SINT16 ||
         type->meta.primitive_id == INFIX_PRIMITIVE_SINT32);
    out->leaves[out->count++] = (rv64_all_leaf){base_offset, type->size, is_float(type) || is_double(type), is_signed};
    return true;
}

/**
 * @internal
 * @brief Flatten an aggregate (or a scalar leaf list) into every scalar leaf.
 * @return `true` on success. A scalar type yields a single leaf.
 */
static bool rv64_flatten_all(const infix_type * type, rv64_all_leaf_list * out) {
    out->count = 0;
    return rv64_flatten_all_recursive(type, 0, out, 0);
}

/**
 * @internal
 * @brief Load an aggregate's floating-point leaves into consecutive FPRs.
 * @details A 16-byte FP leaf (long double) consumes two FPRs; smaller leaves one.
 */
static void rv64_emit_load_fp_aggregate(code_buffer * buf,
                                        uint8_t addr_reg,
                                        uint8_t base_fpr,
                                        const infix_type * type) {
    rv64_all_leaf_list leaves;
    if (!rv64_flatten_all(type, &leaves))
        return;
    uint8_t fpr = base_fpr;
    for (size_t j = 0; j < leaves.count; ++j) {
        const rv64_all_leaf * leaf = &leaves.leaves[j];

infix/src/arch/riscv/abi_riscv64.c  view on Meta::CPAN

            uint32_t off = (uint32_t)((*stack_offset + 7) & ~7);
            cls.stack_offset = off;
            *stack_offset = off + 8;  // The pointer occupies a single 8-byte slot.
        }
        return cls;
    }
    if (type->size > 8) {
        if (is_variadic_arg) {
            // Variadic 2xXLEN: an aligned (even) register pair when the type's
            // alignment is 2xXLEN bits, a plain pair otherwise; never split.
            bool needs_even = (type->alignment >= 16);
            if ((*gpr_count + 1 >= RV_NUM_GPR_ARGS) || (needs_even && (*gpr_count % 2 != 0))) {
                *variadic_stack_mode = true;
                cls.type = ARG_LOCATION_STACK;
                cls.stack_offset = rv64_stack_place(stack_offset, type);
                return cls;
            }
            cls.type = ARG_LOCATION_GPR_PAIR;
            cls.reg_index = (uint8_t)*gpr_count;
            *gpr_count += 2;
            return cls;
        }
        // Named: exactly one GPR left -> low half in that register, high half on the stack.
        if (*gpr_count == RV_NUM_GPR_ARGS - 1) {
            cls.type = ARG_LOCATION_GPR_STACK_SPLIT;
            cls.reg_index = (uint8_t)(*gpr_count)++;
            uint32_t off = (uint32_t)((*stack_offset + 7) & ~7);
            cls.stack_offset = off;
            *stack_offset = off + 8;
            return cls;
        }
        if (*gpr_count + 1 < RV_NUM_GPR_ARGS) {
            cls.type = ARG_LOCATION_GPR_PAIR;
            cls.reg_index = (uint8_t)*gpr_count;
            *gpr_count += 2;
        }
        else {
            cls.type = ARG_LOCATION_STACK;
            cls.stack_offset = rv64_stack_place(stack_offset, type);
        }
        return cls;
    }
    if (*gpr_count < RV_NUM_GPR_ARGS) {
        cls.type = ARG_LOCATION_GPR;
        cls.reg_index = (uint8_t)(*gpr_count)++;
    }
    else {
        if (is_variadic_arg)
            *variadic_stack_mode = true;
        cls.type = ARG_LOCATION_STACK;
        cls.stack_offset = rv64_stack_place(stack_offset, type);
    }
    return cls;
}

/**
 * @internal
 * @brief Classify a single argument into its physical ABI location.
 * @param type The argument's type.
 * @param is_variadic_arg `true` for arguments beyond `num_fixed_args`.
 * @param gpr_count The running GPR index (starts at 1 if a0 holds the sret pointer).
 * @param vpr_count The running FPR index.
 * @param stack_offset The running outgoing/caller stack offset.
 * @param variadic_stack_mode Sticky flag: once set, all variadic args go on the stack.
 */
static rv64_arg_class rv64_classify_arg(infix_type * type,
                                        bool is_variadic_arg,
                                        size_t * gpr_count,
                                        size_t * vpr_count,
                                        uint32_t * stack_offset,
                                        bool * variadic_stack_mode) {
    rv64_arg_class cls = {.type = ARG_LOCATION_STACK, .num_regs = 1};

    // Arrays decay to pointers and are always passed as 8-byte values.
    if (type->category == INFIX_TYPE_ARRAY) {
        if (!*variadic_stack_mode && *gpr_count < RV_NUM_GPR_ARGS) {
            cls.type = ARG_LOCATION_GPR;
            cls.reg_index = (uint8_t)(*gpr_count)++;
        }
        else {
            if (is_variadic_arg)
                *variadic_stack_mode = true;
            cls.type = ARG_LOCATION_STACK;
            *stack_offset = (*stack_offset + 7) & ~7;
            cls.stack_offset = *stack_offset;
            *stack_offset += 8;
        }
        return cls;
    }

    // Once any variadic argument has been placed on the stack, all the rest are too.
    if (is_variadic_arg && *variadic_stack_mode) {
        cls.type = ARG_LOCATION_STACK;
        cls.stack_offset = rv64_stack_place(stack_offset, type);
        return cls;
    }

    // Variadic arguments use the integer calling convention (GPRs).
    if (is_variadic_arg)
        return rv64_classify_integer(type, true, gpr_count, stack_offset, variadic_stack_mode);

    // Named single-precision / double-precision scalars.
    if (is_float(type) || is_double(type)) {
        if (*vpr_count < RV_NUM_FPR_ARGS) {
            cls.type = ARG_LOCATION_VPR;
            cls.reg_index = (uint8_t)(*vpr_count)++;
            cls.num_regs = 1;
        }
        else {
            // FPRs are exhausted: the psABI spills FP reals into the integer
            // argument registers (a0-a7) before they go on the stack.
            return rv64_classify_integer(type, false, gpr_count, stack_offset, variadic_stack_mode);
        }
        return cls;
    }

    // Aggregates: > 16 bytes pass by reference; the psABI hardware FP rules apply
    // to aggregates of at most 2xXLEN bytes with at most two FP members.
    bool is_aggregate = (type->category == INFIX_TYPE_STRUCT || type->category == INFIX_TYPE_UNION ||
                         type->category == INFIX_TYPE_COMPLEX);
    if (is_aggregate) {



( run in 1.119 second using v1.01-cache-2.11-cpan-b301d465b3d )