| 1 | const std = @import("std"); |
| 2 | const Type = @import("../../Type.zig"); |
| 3 | const Zcu = @import("../../Zcu.zig"); |
| 4 | const assert = std.debug.assert; |
| 5 | |
| 6 | pub const Class = union(enum) { |
| 7 | memory, |
| 8 | byval, |
| 9 | i32_array: u8, |
| 10 | }; |
| 11 | |
| 12 | pub const Context = enum { ret, arg }; |
| 13 | |
| 14 | pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 15 | const target = zcu.getTarget(); |
| 16 | std.debug.assert(ty.hasRuntimeBits(zcu)); |
| 17 | |
| 18 | const max_direct_size = target.ptrBitWidth() * 2; |
| 19 | switch (ty.zigTypeTag(zcu)) { |
| 20 | .@"struct" => { |
| 21 | if (ty.containerLayout(zcu) == .@"packed") { |
| 22 | if (ty.bitSize(zcu) > max_direct_size) return .memory; |
| 23 | return .byval; |
| 24 | } |
| 25 | const bit_size = ty.abiSize(zcu) * 8; |
| 26 | if (bit_size > max_direct_size) return .memory; |
| 27 | // TODO: for bit_size <= 32 using byval is more correct, but that needs inreg argument attribute |
| 28 | const count = @as(u8, @intCast(std.mem.alignForward(u64, bit_size, 32) / 32)); |
| 29 | return .{ .i32_array = count }; |
| 30 | }, |
| 31 | .@"union" => { |
| 32 | if (ty.containerLayout(zcu) == .@"packed") { |
| 33 | if (ty.bitSize(zcu) > max_direct_size) return .memory; |
| 34 | return .byval; |
| 35 | } |
| 36 | const bit_size = ty.abiSize(zcu) * 8; |
| 37 | if (bit_size > max_direct_size) return .memory; |
| 38 | return .byval; |
| 39 | }, |
| 40 | .bool => return .byval, |
| 41 | .float => return switch (ty.floatBits(target)) { |
| 42 | else => unreachable, |
| 43 | 16, 32, 64 => .byval, |
| 44 | 80, 128 => switch (max_direct_size) { |
| 45 | else => unreachable, |
| 46 | 64 => .memory, |
| 47 | }, |
| 48 | }, |
| 49 | .int, .@"enum", .error_set => { |
| 50 | return .byval; |
| 51 | }, |
| 52 | .vector => { |
| 53 | const elem_type = ty.childType(zcu); |
| 54 | switch (elem_type.zigTypeTag(zcu)) { |
| 55 | .bool, .int => { |
| 56 | const bit_size = ty.bitSize(zcu); |
| 57 | if (ctx == .ret and bit_size > 128) return .memory; |
| 58 | if (bit_size > 512) return .memory; |
| 59 | // TODO: byval vector arguments with non power of 2 size need inreg attribute |
| 60 | return .byval; |
| 61 | }, |
| 62 | .float => return .memory, |
| 63 | else => unreachable, |
| 64 | } |
| 65 | }, |
| 66 | .optional => { |
| 67 | std.debug.assert(ty.isPtrLikeOptional(zcu)); |
| 68 | return .byval; |
| 69 | }, |
| 70 | .pointer => { |
| 71 | std.debug.assert(!ty.isSlice(zcu)); |
| 72 | return .byval; |
| 73 | }, |
| 74 | .error_union, |
| 75 | .frame, |
| 76 | .@"anyframe", |
| 77 | .noreturn, |
| 78 | .void, |
| 79 | .type, |
| 80 | .comptime_float, |
| 81 | .comptime_int, |
| 82 | .undefined, |
| 83 | .null, |
| 84 | .@"fn", |
| 85 | .@"opaque", |
| 86 | .spirv, |
| 87 | .enum_literal, |
| 88 | .array, |
| 89 | => unreachable, |
| 90 | } |
| 91 | } |