| 1 | const assert = std.debug.assert; |
| 2 | const std = @import("std"); |
| 3 | const InternPool = @import("../../InternPool.zig"); |
| 4 | const Type = @import("../../Type.zig"); |
| 5 | const Zcu = @import("../../Zcu.zig"); |
| 6 | |
| 7 | pub const Class = union(enum) { |
| 8 | memory, |
| 9 | byval, |
| 10 | integer, |
| 11 | double_integer, |
| 12 | float_array: u8, |
| 13 | }; |
| 14 | |
| 15 | /// For `float_array` the second element will be the amount of floats. |
| 16 | pub fn classifyType(ty: Type, zcu: *Zcu) Class { |
| 17 | assert(ty.hasRuntimeBits(zcu)); |
| 18 | |
| 19 | switch (ty.zigTypeTag(zcu)) { |
| 20 | .@"struct" => { |
| 21 | if (ty.containerLayout(zcu) == .@"packed") return .byval; |
| 22 | if (countFloats(ty, zcu)) |float| return .{ .float_array = float.count }; |
| 23 | |
| 24 | const bit_size = ty.abiSize(zcu) * 8; |
| 25 | if (bit_size > 128) return .memory; |
| 26 | if (bit_size > 64) return .double_integer; |
| 27 | return .integer; |
| 28 | }, |
| 29 | .@"union" => { |
| 30 | if (ty.containerLayout(zcu) == .@"packed") return .byval; |
| 31 | if (countFloats(ty, zcu)) |float| return .{ .float_array = float.count }; |
| 32 | |
| 33 | const bit_size = ty.abiSize(zcu) * 8; |
| 34 | if (bit_size > 128) return .memory; |
| 35 | if (bit_size > 64) return .double_integer; |
| 36 | return .integer; |
| 37 | }, |
| 38 | .int, .@"enum", .error_set, .bool => return .byval, |
| 39 | .float => return switch (ty.floatBits(zcu.getTarget())) { |
| 40 | else => unreachable, |
| 41 | 16, 32, 64, 128 => .byval, |
| 42 | 80 => .double_integer, |
| 43 | }, |
| 44 | .vector => { |
| 45 | const bit_size = ty.bitSize(zcu); |
| 46 | // TODO is this controlled by a cpu feature? |
| 47 | if (bit_size > 128) return .memory; |
| 48 | return .byval; |
| 49 | }, |
| 50 | .optional => { |
| 51 | assert(ty.isPtrLikeOptional(zcu)); |
| 52 | return .byval; |
| 53 | }, |
| 54 | .pointer => { |
| 55 | assert(!ty.isSlice(zcu)); |
| 56 | return .byval; |
| 57 | }, |
| 58 | .error_union, |
| 59 | .frame, |
| 60 | .@"anyframe", |
| 61 | .noreturn, |
| 62 | .void, |
| 63 | .type, |
| 64 | .comptime_float, |
| 65 | .comptime_int, |
| 66 | .undefined, |
| 67 | .null, |
| 68 | .@"fn", |
| 69 | .@"opaque", |
| 70 | .spirv, |
| 71 | .enum_literal, |
| 72 | .array, |
| 73 | => unreachable, |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | const CountFloatsResult = struct { |
| 78 | ty: Type, |
| 79 | count: std.math.IntFittingRange(0, max_count), |
| 80 | |
| 81 | const none: CountFloatsResult = .{ .ty = .void, .count = 0 }; |
| 82 | |
| 83 | const max_count = 4; |
| 84 | }; |
| 85 | fn countFloats(ty: Type, zcu: *Zcu) ?CountFloatsResult { |
| 86 | const ip = &zcu.intern_pool; |
| 87 | if (!ty.hasRuntimeBits(zcu)) return .none; |
| 88 | switch (ty.zigTypeTag(zcu)) { |
| 89 | .@"union" => { |
| 90 | const loaded_union = zcu.typeToUnion(ty).?; |
| 91 | var result: CountFloatsResult = .none; |
| 92 | for (loaded_union.field_types.get(ip)) |field_ty| { |
| 93 | const float = countFloats(Type.fromInterned(field_ty), zcu) orelse return null; |
| 94 | if (result.ty.toIntern() == .void_type) { |
| 95 | result.ty = float.ty; |
| 96 | } else if (result.ty.bitSize(zcu) != float.ty.bitSize(zcu)) return null; |
| 97 | result.count = @max(result.count, float.count); |
| 98 | } |
| 99 | if (ty.abiSize(zcu) != result.ty.abiSize(zcu) * result.count) return null; |
| 100 | return result; |
| 101 | }, |
| 102 | .@"struct" => { |
| 103 | var result: CountFloatsResult = .none; |
| 104 | var field_it: InternPool.LoadedStructType.RuntimeOrderIterator = if (zcu.typeToStruct(ty)) |loaded_struct| |
| 105 | loaded_struct.iterateRuntimeOrder(ip) |
| 106 | else |
| 107 | .{ .runtime_order = null, .fields_len = ty.structFieldCount(zcu), .next_index = 0 }; |
| 108 | while (field_it.next()) |field_index| { |
| 109 | if (ty.structFieldOffset(field_index, zcu) != result.ty.abiSize(zcu) * result.count) return null; |
| 110 | const field_ty = ty.fieldType(field_index, zcu); |
| 111 | const float = countFloats(field_ty, zcu) orelse return null; |
| 112 | if (result.ty.toIntern() == .void_type) { |
| 113 | result.ty = float.ty; |
| 114 | } else if (result.ty.bitSize(zcu) != float.ty.bitSize(zcu)) return null; |
| 115 | if (float.count > CountFloatsResult.max_count - result.count) return null; |
| 116 | result.count += float.count; |
| 117 | } |
| 118 | if (ty.abiSize(zcu) != result.ty.abiSize(zcu) * result.count) return null; |
| 119 | return result; |
| 120 | }, |
| 121 | .float => return .{ .ty = ty, .count = 1 }, |
| 122 | else => return null, |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | pub fn getFloatArrayType(ty: Type, zcu: *Zcu) ?Type { |
| 127 | const ip = &zcu.intern_pool; |
| 128 | switch (ty.zigTypeTag(zcu)) { |
| 129 | .@"union" => { |
| 130 | const loaded_union = zcu.typeToUnion(ty).?; |
| 131 | for (loaded_union.field_types.get(ip)) |field_ty| { |
| 132 | if (getFloatArrayType(Type.fromInterned(field_ty), zcu)) |some| return some; |
| 133 | } |
| 134 | return null; |
| 135 | }, |
| 136 | .@"struct" => { |
| 137 | var field_it: InternPool.LoadedStructType.RuntimeOrderIterator = if (zcu.typeToStruct(ty)) |loaded_struct| |
| 138 | loaded_struct.iterateRuntimeOrder(ip) |
| 139 | else |
| 140 | .{ .runtime_order = null, .fields_len = ty.structFieldCount(zcu), .next_index = 0 }; |
| 141 | while (field_it.next()) |field_index| { |
| 142 | const field_ty = ty.fieldType(field_index, zcu); |
| 143 | if (getFloatArrayType(field_ty, zcu)) |some| return some; |
| 144 | } |
| 145 | return null; |
| 146 | }, |
| 147 | .float => return ty, |
| 148 | else => return null, |
| 149 | } |
| 150 | } |