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}