1pub const Class = enum {
2 /// INTEGER: This class consists of integral types that fit into one of the general
3 /// purpose registers.
4 integer,
5 /// SSE: The class consists of types that fit into a vector register.
6 sse,
7 /// SSEUP: The class consists of types that fit into a vector register and can be passed
8 /// and returned in the upper bytes of it.
9 sseup,
10 /// X87, X87UP: These classes consist of types that will be returned via the
11 /// x87 FPU.
12 x87,
13 /// The 15-bit exponent, 1-bit sign, and 6 bytes of padding of an `f80`.
14 x87up,
15 /// NO_CLASS: This class is used as initializer in the algorithms. It will be used for
16 /// padding and empty structures and unions.
17 none,
18 /// MEMORY: This class consists of types that will be passed and returned in mem-
19 /// ory via the stack.
20 memory,
21 /// Win64 passes 128-bit integers as `Class.memory` but returns them as `Class.sse`.
22 win_i128,
23 /// A `Class.sse` containing one `f32`.
24 float,
25 /// A `Class.sse` containing two `f32`s.
26 float_combine,
27 /// Clang uses different element sizes depending on the vector length.
28 bool_vector_mask,
29 /// Clang passes each vector element in a separate `Class.integer`.
30 integer_per_element,
31 /// Clang passes each vector element in a separate `Class.sse`.
32 sse_per_element,
33 /// Just complete insanity, idk what to say.
34 sse_sse_x87_per_qword,
35 /// Clang passes each 16 bytes in a separate `Class.sse`.
36 sse_per_xword,
37 /// Clang passes each 32 bytes in a separate `Class.sse`.
38 sse_per_yword,
39 /// Clang passes each 64 bytes in a separate `Class.sse`.
40 sse_per_zword,
41
42 pub const zero_bit: [8]Class = .{ .none, .none, .none, .none, .none, .none, .none, .none };
43
44 pub const one_integer: [8]Class = .{ .integer, .none, .none, .none, .none, .none, .none, .none };
45 pub const two_integers: [8]Class = .{ .integer, .integer, .none, .none, .none, .none, .none, .none };
46 pub const three_integers: [8]Class = .{ .integer, .integer, .integer, .none, .none, .none, .none, .none };
47 pub const four_integers: [8]Class = .{ .integer, .integer, .integer, .integer, .none, .none, .none, .none };
48 pub const len_integers: [8]Class = .{ .integer_per_element, .none, .none, .none, .none, .none, .none, .none };
49
50 pub const @"f16" = @"f64";
51 pub const @"f32": [8]Class = .{ .float, .none, .none, .none, .none, .none, .none, .none };
52 pub const @"f64": [8]Class = .{ .sse, .none, .none, .none, .none, .none, .none, .none };
53 pub const @"f80": [8]Class = .{ .x87, .x87up, .none, .none, .none, .none, .none, .none };
54 pub const @"f128": [8]Class = .{ .sse, .sseup, .none, .none, .none, .none, .none, .none };
55
56 /// COMPLEX_X87: This class consists of types that will be returned via the x87
57 /// FPU.
58 pub const complex_x87: [8]Class = .{ .x87, .x87up, .x87, .x87up, .none, .none, .none, .none };
59
60 pub const stack: [8]Class = .{ .memory, .none, .none, .none, .none, .none, .none, .none };
61
62 pub fn isX87(class: Class) bool {
63 return switch (class) {
64 .x87, .x87up => true,
65 else => false,
66 };
67 }
68
69 /// Combine a field class with the prev one.
70 fn combineSystemV(prev_class: Class, next_class: Class) Class {
71 // "If both classes are equal, this is the resulting class."
72 if (prev_class == next_class)
73 return if (prev_class == .float) .float_combine else prev_class;
74
75 // "If one of the classes is NO_CLASS, the resulting class
76 // is the other class."
77 if (prev_class == .none) return next_class;
78
79 // "If one of the classes is MEMORY, the result is the MEMORY class."
80 if (prev_class == .memory or next_class == .memory) return .memory;
81
82 // "If one of the classes is INTEGER, the result is the INTEGER."
83 if (prev_class == .integer or next_class == .integer) return .integer;
84
85 // "If one of the classes is X87, X87UP, COMPLEX_X87 class,
86 // MEMORY is used as class."
87 if (prev_class.isX87() or next_class.isX87()) return .memory;
88
89 // "Otherwise class SSE is used."
90 return .sse;
91 }
92};
93
94pub const Context = enum { ret, arg, other };
95
96pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Context) Class {
97 // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
98 // "There's a strict one-to-one correspondence between a function call's arguments
99 // and the registers used for those arguments. Any argument that doesn't fit in 8
100 // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument
101 // is never spread across multiple registers."
102 // "All floating point operations are done using the 16 XMM registers."
103 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed
104 // as if they were integers of the same size."
105 var ty = init_ty;
106 while (true) return switch (ty.zigTypeTag(zcu)) {
107 .void => return .none,
108 .bool,
109 .pointer,
110 .int,
111 .@"enum",
112 .error_set,
113 .@"struct",
114 .@"union",
115 .optional,
116 .array,
117 .error_union,
118 .@"anyframe",
119 .frame,
120 => switch (ty.abiSize(zcu)) {
121 0 => .none,
122 1, 2, 4, 8 => .integer,
123 else => switch (ty.zigTypeTag(zcu)) {
124 .int => .win_i128,
125 .@"struct", .@"union" => if (ty.containerLayout(zcu) != .@"packed" or
126 target.cpu.has(.x86, .soft_float)) .memory else .win_i128,
127 else => .memory,
128 },
129 },
130 .noreturn => unreachable,
131 .float => switch (ty.floatBits(target)) {
132 else => unreachable,
133 16, 32, 64 => if (target.cpu.has(.x86, .soft_float)) .integer else .sse,
134 80 => .memory,
135 128 => if (target.cpu.has(.x86, .soft_float)) .memory else .win_i128,
136 },
137 .vector => {
138 const len = ty.vectorLen(zcu);
139 if (len == 0) return .none;
140 const elem_ty = ty.childType(zcu);
141 if (len == 1) {
142 ty = elem_ty;
143 continue;
144 }
145 const reg_size: u64, const split_class: Class = if (target.cpu.has(.x86, .avx512f))
146 .{ 64, .sse_per_zword }
147 else if (target.cpu.has(.x86, .avx))
148 .{ 32, .sse_per_yword }
149 else
150 .{ 16, .sse_per_xword };
151 if (elem_ty.toIntern() == .bool_type) {
152 if (len > reg_size) return if (ctx == .arg) .integer_per_element else .memory;
153 return .bool_vector_mask;
154 }
155 const elem_size = elem_ty.abiSize(zcu);
156 const unaligned_size = elem_size * len;
157 if ((unaligned_size <= 8 or unaligned_size > reg_size) and !std.math.isPowerOfTwo(len)) {
158 if (ctx == .ret and len > Win64.c_abi_int_return_regs.len) return .memory;
159 if (!elem_ty.isRuntimeFloat()) return .integer_per_element;
160 if (ctx == .ret and len > 2 and elem_size == 8) return .sse_sse_x87_per_qword;
161 return .sse_per_element;
162 }
163 if (unaligned_size <= reg_size) return if (ctx == .arg) .memory else .sse;
164 if (ctx == .ret and unaligned_size > reg_size * Win64.c_abi_sse_return_regs.len) return .memory;
165 return split_class;
166 },
167 .type,
168 .comptime_float,
169 .comptime_int,
170 .undefined,
171 .null,
172 .@"fn",
173 .@"opaque",
174 .spirv,
175 .enum_literal,
176 => unreachable,
177 };
178}
179
180/// There are a maximum of 8 possible return slots. Returned values are in
181/// the beginning of the array; unused slots are filled with .none.
182pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Context) [8]Class {
183 switch (ty.zigTypeTag(zcu)) {
184 else => unreachable,
185 .void => return Class.zero_bit,
186 .bool => return Class.one_integer,
187 .noreturn => unreachable,
188 .int, .@"enum", .error_set => {
189 const bits = ty.intInfo(zcu).bits;
190 if (bits == 0) return Class.zero_bit;
191 if (bits <= 64 * 1) return Class.one_integer;
192 if (bits <= 64 * 2) return Class.two_integers;
193 if (bits <= 64 * 3) return Class.three_integers;
194 if (bits <= 64 * 4) return Class.four_integers;
195 return Class.stack;
196 },
197 .float => if (target.cpu.has(.x86, .soft_float)) switch (ty.floatBits(target)) {
198 else => unreachable,
199 16, 32, 64 => return Class.one_integer,
200 80, 128 => return Class.two_integers,
201 } else switch (ty.floatBits(target)) {
202 else => unreachable,
203 16 => {
204 if (ctx == .other) return Class.stack;
205 // TODO clang doesn't allow __fp16 as .ret or .arg
206 return Class.f16;
207 },
208 32 => return Class.f32,
209 64 => return Class.f64,
210 // "The 64-bit mantissa of arguments of type long double
211 // belongs to class X87, the 16-bit exponent plus 6 bytes
212 // of padding belongs to class X87UP."
213 80 => return Class.f80,
214 // "Arguments of types __float128, _Decimal128 and __m128 are
215 // split into two halves. The least significant ones belong
216 // to class SSE, the most significant one to class SSEUP."
217 128 => return Class.f128,
218 },
219 .pointer => switch (ty.ptrSize(zcu)) {
220 .slice => return Class.two_integers,
221 else => return Class.one_integer,
222 },
223 .vector => {
224 const len = ty.vectorLen(zcu);
225 if (len == 0) return Class.zero_bit;
226 const elem_ty = ty.childType(zcu);
227 if (elem_ty.toIntern() == .bool_type) {
228 if (len <= 32) return Class.one_integer;
229 if (len <= 64) return Class.f64;
230 if (ctx != .arg) return Class.stack;
231 if (len <= 128) return Class.len_integers;
232 if (len <= 256 and target.cpu.has(.x86, .avx)) return Class.len_integers;
233 if (len <= 512 and target.cpu.has(.x86, .avx512f)) return Class.len_integers;
234 return Class.stack;
235 }
236 if (elem_ty.isRuntimeFloat() and elem_ty.floatBits(target) == 80) switch (len) {
237 0 => unreachable,
238 1 => return Class.f80,
239 2 => return Class.complex_x87,
240 else => return Class.stack,
241 };
242 const unaligned_size = elem_ty.abiSize(zcu) * len;
243 if (unaligned_size <= 4) return Class.one_integer;
244 if (unaligned_size == 8 * 1 * 1 and len == 1) {
245 if (ctx == .arg and elem_ty.isRuntimeFloat()) return Class.stack; // what?
246 if (ctx != .other and !elem_ty.isRuntimeFloat() and target.os.tag == .freebsd) return Class.one_integer; // who?
247 }
248 if (unaligned_size <= 8 * 1) return .{ .sse, .none, .none, .none, .none, .none, .none, .none };
249 if (unaligned_size <= 8 * 2) return .{ .sse, .sseup, .none, .none, .none, .none, .none, .none };
250 if (!target.cpu.has(.x86, .avx)) {
251 if (ctx == .ret) switch (unaligned_size) {
252 else => {},
253 8 * 3 => if (len == 3) return if (elem_ty.isRuntimeFloat()) .{
254 .sse_sse_x87_per_qword, .none, .none, .none, .none, .none, .none, .none, // how?
255 } else Class.len_integers, // why?
256 8 * 2 * 2, 8 * 2 * 4 => return .{ .sse_per_xword, .none, .none, .none, .none, .none, .none, .none },
257 };
258 return Class.stack;
259 }
260 if (unaligned_size <= 8 * 3) return .{ .sse, .sseup, .sseup, .none, .none, .none, .none, .none };
261 if (unaligned_size <= 8 * 4) return .{ .sse, .sseup, .sseup, .sseup, .none, .none, .none, .none };
262 if (!target.cpu.has(.x86, .avx512f)) {
263 if (ctx == .ret) switch (unaligned_size) {
264 else => {},
265 8 * 4 * 2, 8 * 4 * 4 => return .{ .sse_per_yword, .none, .none, .none, .none, .none, .none, .none },
266 };
267 return Class.stack;
268 }
269 if (unaligned_size <= 8 * 5) return .{ .sse, .sseup, .sseup, .sseup, .sseup, .none, .none, .none };
270 if (unaligned_size <= 8 * 6) return .{ .sse, .sseup, .sseup, .sseup, .sseup, .sseup, .none, .none };
271 if (unaligned_size <= 8 * 7) return .{ .sse, .sseup, .sseup, .sseup, .sseup, .sseup, .sseup, .none };
272 if (unaligned_size <= 8 * 8) return .{ .sse, .sseup, .sseup, .sseup, .sseup, .sseup, .sseup, .sseup };
273 if (ctx == .ret) switch (unaligned_size) {
274 else => {},
275 8 * 8 * 2, 8 * 8 * 4 => return .{ .sse_per_zword, .none, .none, .none, .none, .none, .none, .none },
276 };
277 return Class.stack;
278 },
279 .optional => {
280 if (ty.optionalReprIsPayload(zcu)) {
281 return classifySystemV(ty.optionalChild(zcu), zcu, target, ctx);
282 }
283 return Class.stack;
284 },
285 .@"struct", .@"union" => {
286 // "If the size of an object is larger than eight eightbytes, or
287 // it contains unaligned fields, it has class MEMORY"
288 // "If the size of the aggregate exceeds a single eightbyte, each is classified
289 // separately.".
290 const ty_size = ty.abiSize(zcu);
291 if (ty_size == 0) return Class.zero_bit;
292 switch (ty.containerLayout(zcu)) {
293 .auto => unreachable,
294 .@"extern" => {},
295 .@"packed" => {
296 if (ty_size <= 8) return Class.one_integer;
297 if (ty_size <= 16) return Class.two_integers;
298 unreachable; // frontend should not have allowed this type as extern
299 },
300 }
301 if (ty_size > 64) return Class.stack;
302
303 var result: [8]Class = @splat(.none);
304 _ = if (zcu.typeToStruct(ty)) |loaded_struct|
305 classifySystemVStruct(&result, 0, loaded_struct, zcu, target)
306 else if (zcu.typeToUnion(ty)) |loaded_union|
307 classifySystemVUnion(&result, 0, loaded_union, zcu, target)
308 else
309 unreachable;
310
311 // Post-merger cleanup
312
313 // "If one of the classes is MEMORY, the whole argument is passed in memory"
314 // "If X87UP is not preceded by X87, the whole argument is passed in memory."
315 for (result, 0..) |class, i| switch (class) {
316 .memory => return Class.stack,
317 .x87up => if (i == 0 or result[i - 1] != .x87) return Class.stack,
318 else => {},
319 };
320 // "If the size of the aggregate exceeds two eightbytes and the first eight-
321 // byte isn't SSE or any other eightbyte isn't SSEUP, the whole argument
322 // is passed in memory."
323 if (ty_size > 16 and (result[0] != .sse or
324 std.mem.findNone(Class, result[1..], &.{ .sseup, .none }) != null)) return Class.stack;
325
326 // "If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE."
327 for (&result, 0..) |*class, i| switch (class.*) {
328 .sseup => switch (result[i - 1]) {
329 .sse, .sseup => {},
330 else => class.* = .sse,
331 },
332 .float => if (i + 1 < result.len) switch (result[i + 1]) {
333 .none => {},
334 else => class.* = .float_combine,
335 },
336 else => {},
337 };
338 return result;
339 },
340 .array => {
341 const ty_size = ty.abiSize(zcu);
342 if (ty_size == 0) return Class.zero_bit;
343 if (ty_size <= 8) return Class.one_integer;
344 if (ty_size <= 16) return Class.two_integers;
345 return Class.stack;
346 },
347 }
348}
349
350fn classifySystemVStruct(
351 result: *[8]Class,
352 starting_byte_offset: u64,
353 loaded_struct: InternPool.LoadedStructType,
354 zcu: *Zcu,
355 target: *const std.Target,
356) u64 {
357 const ip = &zcu.intern_pool;
358 var byte_offset = starting_byte_offset;
359 var field_it = loaded_struct.iterateRuntimeOrder(ip);
360 while (field_it.next()) |field_index| {
361 const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]);
362 const field_align = loaded_struct.field_aligns.getOrNone(ip, field_index);
363 byte_offset = switch (field_align) {
364 .none => field_ty.abiAlignment(zcu),
365 else => field_align,
366 }.forward(byte_offset);
367 if (zcu.typeToStruct(field_ty)) |field_loaded_struct| {
368 switch (field_loaded_struct.layout) {
369 .auto => unreachable,
370 .@"extern" => {
371 byte_offset = classifySystemVStruct(result, byte_offset, field_loaded_struct, zcu, target);
372 continue;
373 },
374 .@"packed" => {},
375 }
376 } else if (zcu.typeToUnion(field_ty)) |field_loaded_union| {
377 switch (field_loaded_union.layout) {
378 .auto => unreachable,
379 .@"extern" => {
380 byte_offset = classifySystemVUnion(result, byte_offset, field_loaded_union, zcu, target);
381 continue;
382 },
383 .@"packed" => {},
384 }
385 } else if (field_ty.zigTypeTag(zcu) == .array) {
386 byte_offset = classifySystemVArray(result, byte_offset, field_ty, zcu, target);
387 continue;
388 }
389 const field_classes = std.mem.sliceTo(&classifySystemV(field_ty, zcu, target, .other), .none);
390 for (result[@intCast(byte_offset / 8)..][0..field_classes.len], field_classes) |*result_class, field_class|
391 result_class.* = result_class.combineSystemV(field_class);
392 byte_offset += field_ty.abiSize(zcu);
393 }
394 const final_byte_offset = starting_byte_offset + loaded_struct.size;
395 std.debug.assert(final_byte_offset == loaded_struct.alignment.forward(byte_offset));
396 return final_byte_offset;
397}
398
399fn classifySystemVUnion(
400 result: *[8]Class,
401 starting_byte_offset: u64,
402 loaded_union: InternPool.LoadedUnionType,
403 zcu: *Zcu,
404 target: *const std.Target,
405) u64 {
406 const ip = &zcu.intern_pool;
407 for (0..loaded_union.field_types.len) |field_index| {
408 const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]);
409 if (zcu.typeToStruct(field_ty)) |field_loaded_struct| {
410 switch (field_loaded_struct.layout) {
411 .auto => unreachable,
412 .@"extern" => {
413 _ = classifySystemVStruct(result, starting_byte_offset, field_loaded_struct, zcu, target);
414 continue;
415 },
416 .@"packed" => {},
417 }
418 } else if (zcu.typeToUnion(field_ty)) |field_loaded_union| {
419 switch (field_loaded_union.layout) {
420 .auto => unreachable,
421 .@"extern" => {
422 _ = classifySystemVUnion(result, starting_byte_offset, field_loaded_union, zcu, target);
423 continue;
424 },
425 .@"packed" => {},
426 }
427 } else if (field_ty.zigTypeTag(zcu) == .array) {
428 _ = classifySystemVArray(result, starting_byte_offset, field_ty, zcu, target);
429 continue;
430 }
431 const field_classes = std.mem.sliceTo(&classifySystemV(field_ty, zcu, target, .other), .none);
432 for (result[@intCast(starting_byte_offset / 8)..][0..field_classes.len], field_classes) |*result_class, field_class|
433 result_class.* = result_class.combineSystemV(field_class);
434 }
435 return starting_byte_offset + loaded_union.size;
436}
437
438fn classifySystemVArray(
439 result: *[8]Class,
440 starting_byte_offset: u64,
441 array_ty: Type,
442 zcu: *Zcu,
443 target: *const std.Target,
444) u64 {
445 const field_classes = std.mem.sliceTo(&classifySystemV(array_ty.childType(zcu), zcu, target, .other), .none);
446 var byte_offset = starting_byte_offset;
447 const elem_size = array_ty.childType(zcu).abiSize(zcu);
448 for (0..@intCast(array_ty.arrayLenIncludingSentinel(zcu))) |_| {
449 for (result[@intCast(byte_offset / 8)..][0..field_classes.len], field_classes) |*result_class, field_class|
450 result_class.* = result_class.combineSystemV(field_class);
451 byte_offset += elem_size;
452 }
453 const final_byte_offset = starting_byte_offset + array_ty.abiSize(zcu);
454 assert(final_byte_offset == byte_offset);
455 return final_byte_offset;
456}
457
458pub const zigcc = struct {
459 pub const stack_align: ?InternPool.Alignment = null;
460 pub const return_in_regs = true;
461 pub const params_in_regs = true;
462
463 const volatile_gpr = gp_regs.len - 5;
464 const volatile_x87 = x87_regs.len - 1;
465 const volatile_sse = sse_avx_regs.len;
466
467 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
468 /// for anything else but stack offset tracking therefore we exclude them from this set.
469 pub const callee_preserved_regs = gp_regs[volatile_gpr..] ++ x87_regs[volatile_x87 .. x87_regs.len - 1] ++ sse_avx_regs[volatile_sse..];
470 /// These registers need to be preserved (saved on the stack) and restored by the caller before
471 /// the caller relinquishes control to a subroutine via call instruction (or similar).
472 /// In other words, these registers are free to use by the callee.
473 pub const caller_preserved_regs = gp_regs[0..volatile_gpr] ++ x87_regs[0..volatile_x87] ++ sse_avx_regs[0..volatile_sse];
474
475 const int_param_regs = gp_regs[0 .. volatile_gpr - 1];
476 const x87_param_regs = x87_regs[0..volatile_x87];
477 const sse_param_regs = sse_avx_regs[0 .. volatile_sse / 2];
478 const int_return_regs = gp_regs[0..volatile_gpr];
479 const x87_return_regs = x87_regs[0..volatile_x87];
480 const sse_return_regs = sse_avx_regs[0..volatile_gpr];
481};
482
483pub const SysV = struct {
484 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
485 /// for anything else but stack offset tracking therefore we exclude them from this set.
486 pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
487 /// These registers need to be preserved (saved on the stack) and restored by the caller before
488 /// the caller relinquishes control to a subroutine via call instruction (or similar).
489 /// In other words, these registers are free to use by the callee.
490 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
491
492 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
493 pub const c_abi_x87_param_regs = x87_regs[0..0];
494 pub const c_abi_sse_param_regs = sse_avx_regs[0..8];
495 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx, .rcx };
496 pub const c_abi_x87_return_regs = x87_regs[0..2];
497 pub const c_abi_sse_return_regs = sse_avx_regs[0..4];
498};
499
500pub const Win64 = struct {
501 /// Note that .rsp and .rbp also belong to this set, however, we never expect to use them
502 /// for anything else but stack offset tracking therefore we exclude them from this set.
503 pub const callee_preserved_regs = [_]Register{ .rbx, .rsi, .rdi, .r12, .r13, .r14, .r15 };
504 /// These registers need to be preserved (saved on the stack) and restored by the caller before
505 /// the caller relinquishes control to a subroutine via call instruction (or similar).
506 /// In other words, these registers are free to use by the callee.
507 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ x87_regs ++ sse_avx_regs;
508
509 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
510 pub const c_abi_x87_param_regs = x87_regs[0..0];
511 pub const c_abi_sse_param_regs = sse_avx_regs[0..4];
512 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx, .rcx };
513 pub const c_abi_x87_return_regs = x87_regs[0..1];
514 pub const c_abi_sse_return_regs = sse_avx_regs[0..4];
515};
516
517pub fn getCalleePreservedRegs(cc: std.lang.CallingConvention.Tag) []const Register {
518 return switch (cc) {
519 .auto => zigcc.callee_preserved_regs,
520 .x86_64_sysv => &SysV.callee_preserved_regs,
521 .x86_64_win => &Win64.callee_preserved_regs,
522 else => unreachable,
523 };
524}
525
526pub fn getCallerPreservedRegs(cc: std.lang.CallingConvention.Tag) []const Register {
527 return switch (cc) {
528 .auto => zigcc.caller_preserved_regs,
529 .x86_64_sysv => &SysV.caller_preserved_regs,
530 .x86_64_win => &Win64.caller_preserved_regs,
531 else => unreachable,
532 };
533}
534
535pub fn getCAbiIntParamRegs(cc: std.lang.CallingConvention.Tag) []const Register {
536 return switch (cc) {
537 .auto => zigcc.int_param_regs,
538 .x86_64_sysv => &SysV.c_abi_int_param_regs,
539 .x86_64_win => &Win64.c_abi_int_param_regs,
540 else => unreachable,
541 };
542}
543
544pub fn getCAbiX87ParamRegs(cc: std.lang.CallingConvention.Tag) []const Register {
545 return switch (cc) {
546 .auto => zigcc.x87_param_regs,
547 .x86_64_sysv => SysV.c_abi_x87_param_regs,
548 .x86_64_win => Win64.c_abi_x87_param_regs,
549 else => unreachable,
550 };
551}
552
553pub fn getCAbiSseParamRegs(cc: std.lang.CallingConvention.Tag, target: *const std.Target) []const Register {
554 return switch (cc) {
555 .auto => switch (target.cpu.arch) {
556 else => unreachable,
557 .x86 => zigcc.sse_param_regs[0 .. zigcc.sse_param_regs.len / 2],
558 .x86_64 => zigcc.sse_param_regs,
559 },
560 .x86_64_sysv => SysV.c_abi_sse_param_regs,
561 .x86_64_win => Win64.c_abi_sse_param_regs,
562 else => unreachable,
563 };
564}
565
566pub fn getCAbiIntReturnRegs(cc: std.lang.CallingConvention.Tag) []const Register {
567 return switch (cc) {
568 .auto => zigcc.int_return_regs,
569 .x86_64_sysv => &SysV.c_abi_int_return_regs,
570 .x86_64_win => &Win64.c_abi_int_return_regs,
571 else => unreachable,
572 };
573}
574
575pub fn getCAbiX87ReturnRegs(cc: std.lang.CallingConvention.Tag) []const Register {
576 return switch (cc) {
577 .auto => zigcc.x87_return_regs,
578 .x86_64_sysv => SysV.c_abi_x87_return_regs,
579 .x86_64_win => Win64.c_abi_x87_return_regs,
580 else => unreachable,
581 };
582}
583
584pub fn getCAbiSseReturnRegs(cc: std.lang.CallingConvention.Tag) []const Register {
585 return switch (cc) {
586 .auto => zigcc.sse_return_regs,
587 .x86_64_sysv => SysV.c_abi_sse_return_regs,
588 .x86_64_win => Win64.c_abi_sse_return_regs,
589 else => unreachable,
590 };
591}
592
593pub fn getCAbiLinkerScratchReg(cc: std.lang.CallingConvention.Tag) Register {
594 return switch (cc) {
595 .auto => zigcc.int_return_regs[zigcc.int_return_regs.len - 1],
596 .x86_64_sysv => SysV.c_abi_int_return_regs[0],
597 .x86_64_win => Win64.c_abi_int_return_regs[0],
598 else => unreachable,
599 };
600}
601
602const gp_regs = [_]Register{
603 .rax, .rdx, .rbx, .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
604};
605const x87_regs = [_]Register{
606 .st0, .st1, .st2, .st3, .st4, .st5, .st6, .st7,
607};
608const sse_avx_regs = [_]Register{
609 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
610 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
611};
612const allocatable_regs = gp_regs ++ x87_regs[0 .. x87_regs.len - 1] ++ sse_avx_regs;
613pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, allocatable_regs);
614
615// Register classes
616const RegisterBitSet = RegisterManager.RegisterBitSet;
617pub const RegisterClass = struct {
618 pub const gp: RegisterBitSet = blk: {
619 var set = RegisterBitSet.empty;
620 for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.general_purpose)) set.set(index);
621 break :blk set;
622 };
623 pub const gphi: RegisterBitSet = blk: {
624 var set = RegisterBitSet.empty;
625 for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.gphi)) set.set(index);
626 break :blk set;
627 };
628 pub const x87: RegisterBitSet = blk: {
629 var set = RegisterBitSet.empty;
630 for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.x87)) set.set(index);
631 break :blk set;
632 };
633 pub const sse: RegisterBitSet = blk: {
634 var set = RegisterBitSet.empty;
635 for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.sse)) set.set(index);
636 break :blk set;
637 };
638};
639
640const builtin = @import("builtin");
641const std = @import("std");
642const assert = std.debug.assert;
643const testing = std.testing;
644
645const InternPool = @import("../../InternPool.zig");
646const Register = @import("bits.zig").Register;
647const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
648const Type = @import("../../Type.zig");
649const Value = @import("../../Value.zig");
650const Zcu = @import("../../Zcu.zig");