1const std = @import("std");
2const assert = std.debug.assert;
3const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
4const Type = @import("../../Type.zig");
5const Zcu = @import("../../Zcu.zig");
6
7pub const Class = union(enum) {
8 memory,
9 byval,
10 i32_array: u8,
11 i64_array: u8,
12
13 fn arrSize(total_size: u64, arr_size: u64) Class {
14 const count = @as(u8, @intCast(std.mem.alignForward(u64, total_size, arr_size) / arr_size));
15 if (arr_size == 32) {
16 return .{ .i32_array = count };
17 } else {
18 return .{ .i64_array = count };
19 }
20 }
21};
22
23pub const Context = enum { ret, arg };
24
25pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class {
26 assert(ty.hasRuntimeBits(zcu));
27
28 var maybe_float_bits: ?u16 = null;
29 const max_byval_size = 512;
30 const ip = &zcu.intern_pool;
31 switch (ty.zigTypeTag(zcu)) {
32 .@"struct" => {
33 if (ty.containerLayout(zcu) == .@"packed") {
34 if (ty.bitSize(zcu) > 64) return .memory;
35 return .byval;
36 }
37 const bit_size = ty.abiSize(zcu) * 8;
38 if (bit_size > max_byval_size) return .memory;
39 const float_count = countFloats(ty, zcu, &maybe_float_bits);
40 if (float_count <= byval_float_count) return .byval;
41
42 if (ty.abiAlignment(zcu).compare(.gt, .@"4")) {
43 return Class.arrSize(bit_size, 64);
44 }
45
46 const fields = ty.structFieldCount(zcu);
47 var i: u32 = 0;
48 while (i < fields) : (i += 1) {
49 const field_ty = ty.fieldType(i, zcu);
50 if (field_ty.abiSize(zcu) > 4) return Class.arrSize(bit_size, 64);
51 }
52 return Class.arrSize(bit_size, 32);
53 },
54 .@"union" => {
55 const union_obj = zcu.typeToUnion(ty).?;
56 if (union_obj.layout == .@"packed") {
57 if (ty.bitSize(zcu) > 64) return .memory;
58 return .byval;
59 }
60 const bit_size = ty.abiSize(zcu) * 8;
61 if (bit_size > max_byval_size) return .memory;
62 const float_count = countFloats(ty, zcu, &maybe_float_bits);
63 if (float_count <= byval_float_count) return .byval;
64
65 if (union_obj.alignment.compareStrict(.gt, .@"4")) {
66 return Class.arrSize(bit_size, 64);
67 }
68
69 for (union_obj.field_types.get(ip)) |field_ty| {
70 if (Type.fromInterned(field_ty).abiSize(zcu) > 4) {
71 return Class.arrSize(bit_size, 64);
72 }
73 }
74 return Class.arrSize(bit_size, 32);
75 },
76 .bool => return .byval,
77 .int => {
78 if (ctx == .ret and ty.intInfo(zcu).bits > 64) return .memory;
79 return .byval;
80 },
81 .float => return switch (ty.floatBits(zcu.getTarget())) {
82 else => unreachable,
83 16, 32, 64 => .byval,
84 80, 128 => .{ .i64_array = 2 },
85 },
86 .@"enum", .error_set => {
87 const bit_size = ty.bitSize(zcu);
88 if (bit_size > 64) return .memory;
89 return .byval;
90 },
91 .vector => {
92 const bit_size = ty.bitSize(zcu);
93 // TODO is this controlled by a cpu feature?
94 if (ctx == .ret and bit_size > 128) return .memory;
95 if (bit_size > 512) return .memory;
96 return .byval;
97 },
98 .optional => {
99 assert(ty.isPtrLikeOptional(zcu));
100 return .byval;
101 },
102 .pointer => {
103 assert(!ty.isSlice(zcu));
104 return .byval;
105 },
106 .error_union,
107 .frame,
108 .@"anyframe",
109 .noreturn,
110 .void,
111 .type,
112 .comptime_float,
113 .comptime_int,
114 .undefined,
115 .null,
116 .@"fn",
117 .@"opaque",
118 .spirv,
119 .enum_literal,
120 .array,
121 => unreachable,
122 }
123}
124
125const byval_float_count = 4;
126fn countFloats(ty: Type, zcu: *Zcu, maybe_float_bits: *?u16) u32 {
127 const ip = &zcu.intern_pool;
128 const target = zcu.getTarget();
129 const invalid = std.math.maxInt(u32);
130 switch (ty.zigTypeTag(zcu)) {
131 .@"union" => {
132 const union_obj = zcu.typeToUnion(ty).?;
133 var max_count: u32 = 0;
134 for (union_obj.field_types.get(ip)) |field_ty| {
135 const field_count = countFloats(Type.fromInterned(field_ty), zcu, maybe_float_bits);
136 if (field_count == invalid) return invalid;
137 if (field_count > max_count) max_count = field_count;
138 if (max_count > byval_float_count) return invalid;
139 }
140 return max_count;
141 },
142 .@"struct" => {
143 const fields_len = ty.structFieldCount(zcu);
144 var count: u32 = 0;
145 var i: u32 = 0;
146 while (i < fields_len) : (i += 1) {
147 const field_ty = ty.fieldType(i, zcu);
148 const field_count = countFloats(field_ty, zcu, maybe_float_bits);
149 if (field_count == invalid) return invalid;
150 count += field_count;
151 if (count > byval_float_count) return invalid;
152 }
153 return count;
154 },
155 .float => {
156 const float_bits = maybe_float_bits.* orelse {
157 const float_bits = ty.floatBits(target);
158 if (float_bits != 32 and float_bits != 64) return invalid;
159 maybe_float_bits.* = float_bits;
160 return 1;
161 };
162 if (ty.floatBits(target) == float_bits) return 1;
163 return invalid;
164 },
165 .void => return 0,
166 else => return invalid,
167 }
168}