master
  1const assert = @import("std").debug.assert;
  2const std = @import("std");
  3const Type = @import("../../Type.zig");
  4const Zcu = @import("../../Zcu.zig");
  5
  6pub const Class = union(enum) {
  7    memory,
  8    byval,
  9    integer,
 10    double_integer,
 11    float_array: u8,
 12};
 13
 14/// For `float_array` the second element will be the amount of floats.
 15pub fn classifyType(ty: Type, zcu: *Zcu) Class {
 16    assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
 17
 18    var maybe_float_bits: ?u16 = null;
 19    switch (ty.zigTypeTag(zcu)) {
 20        .@"struct" => {
 21            if (ty.containerLayout(zcu) == .@"packed") return .byval;
 22            const float_count = countFloats(ty, zcu, &maybe_float_bits);
 23            if (float_count <= sret_float_count) return .{ .float_array = float_count };
 24
 25            const bit_size = ty.bitSize(zcu);
 26            if (bit_size > 128) return .memory;
 27            if (bit_size > 64) return .double_integer;
 28            return .integer;
 29        },
 30        .@"union" => {
 31            if (ty.containerLayout(zcu) == .@"packed") return .byval;
 32            const float_count = countFloats(ty, zcu, &maybe_float_bits);
 33            if (float_count <= sret_float_count) return .{ .float_array = float_count };
 34
 35            const bit_size = ty.bitSize(zcu);
 36            if (bit_size > 128) return .memory;
 37            if (bit_size > 64) return .double_integer;
 38            return .integer;
 39        },
 40        .int, .@"enum", .error_set, .float, .bool => return .byval,
 41        .vector => {
 42            const bit_size = ty.bitSize(zcu);
 43            // TODO is this controlled by a cpu feature?
 44            if (bit_size > 128) return .memory;
 45            return .byval;
 46        },
 47        .optional => {
 48            assert(ty.isPtrLikeOptional(zcu));
 49            return .byval;
 50        },
 51        .pointer => {
 52            assert(!ty.isSlice(zcu));
 53            return .byval;
 54        },
 55        .error_union,
 56        .frame,
 57        .@"anyframe",
 58        .noreturn,
 59        .void,
 60        .type,
 61        .comptime_float,
 62        .comptime_int,
 63        .undefined,
 64        .null,
 65        .@"fn",
 66        .@"opaque",
 67        .enum_literal,
 68        .array,
 69        => unreachable,
 70    }
 71}
 72
 73const sret_float_count = 4;
 74fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u8 {
 75    const ip = &zcu.intern_pool;
 76    const target = zcu.getTarget();
 77    const invalid = std.math.maxInt(u8);
 78    switch (ty.zigTypeTag(zcu)) {
 79        .@"union" => {
 80            const union_obj = zcu.typeToUnion(ty).?;
 81            var max_count: u8 = 0;
 82            for (union_obj.field_types.get(ip)) |field_ty| {
 83                const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
 84                if (field_count == invalid) return invalid;
 85                if (field_count > max_count) max_count = field_count;
 86                if (max_count > sret_float_count) return invalid;
 87            }
 88            return max_count;
 89        },
 90        .@"struct" => {
 91            const fields_len = ty.structFieldCount(zcu);
 92            var count: u8 = 0;
 93            var i: u32 = 0;
 94            while (i < fields_len) : (i += 1) {
 95                const field_ty = ty.fieldType(i, zcu);
 96                const field_count = countFloats(field_ty, zcu, maybe_float_bits);
 97                if (field_count == invalid) return invalid;
 98                count += field_count;
 99                if (count > sret_float_count) return invalid;
100            }
101            return count;
102        },
103        .float => {
104            const float_bits = maybe_float_bits.* orelse {
105                maybe_float_bits.* = ty.floatBits(target);
106                return 1;
107            };
108            if (ty.floatBits(target) == float_bits) return 1;
109            return invalid;
110        },
111        .void => return 0,
112        else => return invalid,
113    }
114}
115
116pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type {
117    const ip = &zcu.intern_pool;
118    switch (ty.zigTypeTag(zcu)) {
119        .@"union" => {
120            const union_obj = zcu.typeToUnion(ty).?;
121            for (union_obj.field_types.get(ip)) |field_ty| {
122                if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some;
123            }
124            return null;
125        },
126        .@"struct" => {
127            const fields_len = ty.structFieldCount(zcu);
128            var i: u32 = 0;
129            while (i < fields_len) : (i += 1) {
130                const field_ty = ty.fieldType(i, zcu);
131                if (getFloatArrayType(field_ty, zcu)) |some| return some;
132            }
133            return null;
134        },
135        .float => return ty,
136        else => return null,
137    }
138}