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