1const std = @import("std");
2const InternPool = @import("../../InternPool.zig");
3const Type = @import("../../Type.zig");
4const Zcu = @import("../../Zcu.zig");
5
6pub const Class = union(enum) {
7 ignored,
8 gar,
9 far,
10 member: Type,
11 member_pair: [2]Type,
12 memory_gar,
13 memory_gar_pair,
14 address,
15
16 fn combineMember(container_class: Class, member_class: Class, member_ty: Type) Class {
17 const second_member_ty = switch (member_class) {
18 .ignored => return container_class,
19 .gar, .far => member_ty,
20 .member => |second_member_ty| second_member_ty,
21 .member_pair, .memory_gar, .memory_gar_pair, .address => return .address,
22 };
23 return switch (container_class) {
24 .ignored => .{ .member = second_member_ty },
25 .gar, .far, .memory_gar, .memory_gar_pair => unreachable,
26 .member => |first_member_ty| .{ .member_pair = .{ first_member_ty, second_member_ty } },
27 .member_pair, .address => .address,
28 };
29 }
30};
31
32pub fn classifyType(ty: Type, zcu: *Zcu) Class {
33 return Classifier.init(zcu).classifyType(ty);
34}
35
36const Classifier = struct {
37 zcu: *Zcu,
38 target: *const std.Target,
39 grlen: u8,
40 frlen: u8,
41
42 fn init(zcu: *Zcu) Classifier {
43 const target = zcu.getTarget();
44 return .{
45 .zcu = zcu,
46 .target = target,
47 .grlen = switch (target.cpu.arch) {
48 else => unreachable,
49 .loongarch32 => 32,
50 .loongarch64 => 64,
51 },
52 .frlen = if (target.cpu.has(.loongarch, .d))
53 64
54 else if (target.cpu.has(.loongarch, .f))
55 32
56 else
57 0,
58 };
59 }
60
61 fn classifyType(c: Classifier, ty: Type) Class {
62 switch (ty.zigTypeTag(c.zcu)) {
63 .type,
64 .comptime_float,
65 .comptime_int,
66 .undefined,
67 .null,
68 .error_union,
69 .error_set,
70 .@"fn",
71 .@"opaque",
72 .frame,
73 .@"anyframe",
74 .enum_literal,
75 .spirv,
76 => unreachable,
77 .void, .noreturn => return .ignored,
78 .bool => return .gar,
79 .int, .@"enum" => {
80 const bits = ty.intInfo(c.zcu).bits;
81 if (bits == 0) return .ignored;
82 if (bits <= c.grlen) return .gar;
83 if (bits <= 2 * c.grlen) return .memory_gar_pair;
84 return .address;
85 },
86 .float => {
87 const bits = ty.floatBits(c.target);
88 if (bits <= c.frlen) return .far;
89 if (bits <= c.grlen) return .gar;
90 if (bits <= 2 * c.grlen) return .memory_gar_pair;
91 return .address;
92 },
93 .pointer, .optional => return .gar,
94 .array => {
95 var class: Class = .ignored;
96 const elem_ty = ty.childType(c.zcu);
97 const elem_class = c.classifyType(elem_ty);
98 for (0..std.math.lossyCast(usize, ty.arrayLenIncludingSentinel(c.zcu))) |_| {
99 class = class.combineMember(elem_class, elem_ty);
100 if (class == .address) break;
101 }
102 if (class != .address) return class;
103 },
104 .@"struct" => switch (ty.containerLayout(c.zcu)) {
105 .auto => unreachable,
106 .@"extern" => {
107 var class: Class = .ignored;
108 var field_it: InternPool.LoadedStructType.RuntimeOrderIterator = if (c.zcu.typeToStruct(ty)) |loaded_struct|
109 loaded_struct.iterateRuntimeOrder(&c.zcu.intern_pool)
110 else
111 .{ .runtime_order = null, .fields_len = ty.structFieldCount(c.zcu), .next_index = 0 };
112 while (field_it.next()) |field_index| {
113 const field_ty = ty.fieldType(field_index, c.zcu);
114 class = class.combineMember(c.classifyType(field_ty), field_ty);
115 if (class == .address) break;
116 }
117 if (class != .address) return class;
118 },
119 .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)),
120 },
121 .@"union" => switch (ty.containerLayout(c.zcu)) {
122 .auto => unreachable,
123 .@"extern" => {},
124 .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)),
125 },
126 .vector => {},
127 }
128 const size = ty.abiSize(c.zcu);
129 if (size <= @divExact(c.grlen, 8)) return .memory_gar;
130 if (size <= @divExact(2 * c.grlen, 8)) return .memory_gar_pair;
131 return .address;
132 }
133};