| 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; |
| 3 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 4 | const Type = @import("../../Type.zig"); |
| 5 | const Zcu = @import("../../Zcu.zig"); |
| 6 | |
| 7 | pub 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 | |
| 23 | pub const Context = enum { ret, arg }; |
| 24 | |
| 25 | pub 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 | |
| 125 | const byval_float_count = 4; |
| 126 | fn 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 | } |