authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-30 00:20:38+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 14:46:09-07:00
logfa30458e84ff2d6dad53dd39d4ab818f75f747f7
treec14aae9b8786527b03e9c91259aa1824ef85b66e
parent0313426ce8c6714f9bd3ea1d0f88f68e8f0c89cd

Merge pull request #13701 from ziglang/arm-win-more-features

Improve aarch64 feature detection based on the readouts from privileged system registers

2 files changed, 273 insertions(+), 179 deletions(-)

lib/std/zig/system/arm.zig+177-6
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const Target = std.Target;
23
3pub const CoreInfo = struct {4pub const CoreInfo = struct {
4 architecture: u8 = 0,5 architecture: u8 = 0,
...@@ -9,14 +10,14 @@ pub const CoreInfo = struct {...@@ -9,14 +10,14 @@ pub const CoreInfo = struct {
910
10pub const cpu_models = struct {11pub const cpu_models = struct {
11 // Shorthands to simplify the tables below.12 // Shorthands to simplify the tables below.
12 const A32 = std.Target.arm.cpu;13 const A32 = Target.arm.cpu;
13 const A64 = std.Target.aarch64.cpu;14 const A64 = Target.aarch64.cpu;
1415
15 const E = struct {16 const E = struct {
16 part: u16,17 part: u16,
17 variant: ?u8 = null, // null if matches any variant18 variant: ?u8 = null, // null if matches any variant
18 m32: ?*const std.Target.Cpu.Model = null,19 m32: ?*const Target.Cpu.Model = null,
19 m64: ?*const std.Target.Cpu.Model = null,20 m64: ?*const Target.Cpu.Model = null,
20 };21 };
2122
22 // implementer = 0x4123 // implementer = 0x41
...@@ -59,7 +60,6 @@ pub const cpu_models = struct {...@@ -59,7 +60,6 @@ pub const cpu_models = struct {
59 E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null },60 E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null },
60 E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 },61 E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 },
61 E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c },62 E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c },
62 // This is a guess based on https://www.notebookcheck.net/Qualcomm-Snapdragon-8cx-Gen-3-Processor-Benchmarks-and-Specs.652916.0.html
63 E{ .part = 0xd4c, .m32 = &A32.cortex_x1c, .m64 = &A64.cortex_x1c },63 E{ .part = 0xd4c, .m32 = &A32.cortex_x1c, .m64 = &A64.cortex_x1c },
64 E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 },64 E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 },
65 E{ .part = 0xd02, .m64 = &A64.cortex_a34 },65 E{ .part = 0xd02, .m64 = &A64.cortex_a34 },
...@@ -111,7 +111,7 @@ pub const cpu_models = struct {...@@ -111,7 +111,7 @@ pub const cpu_models = struct {
111 E{ .part = 0xc01, .m64 = &A64.saphira },111 E{ .part = 0xc01, .m64 = &A64.saphira },
112 };112 };
113113
114 pub fn isKnown(core: CoreInfo, is_64bit: bool) ?*const std.Target.Cpu.Model {114 pub fn isKnown(core: CoreInfo, is_64bit: bool) ?*const Target.Cpu.Model {
115 const models = switch (core.implementer) {115 const models = switch (core.implementer) {
116 0x41 => &ARM,116 0x41 => &ARM,
117 0x42 => &Broadcom,117 0x42 => &Broadcom,
...@@ -132,3 +132,174 @@ pub const cpu_models = struct {...@@ -132,3 +132,174 @@ pub const cpu_models = struct {
132 return null;132 return null;
133 }133 }
134};134};
135
136pub const aarch64 = struct {
137 fn setFeature(cpu: *Target.Cpu, feature: Target.aarch64.Feature, enabled: bool) void {
138 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
139
140 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
141 }
142
143 inline fn bitField(input: u64, offset: u6) u4 {
144 return @truncate(u4, input >> offset);
145 }
146
147 /// Input array should consist of readouts from 12 system registers such that:
148 /// 0 -> MIDR_EL1
149 /// 1 -> ID_AA64PFR0_EL1
150 /// 2 -> ID_AA64PFR1_EL1
151 /// 3 -> ID_AA64DFR0_EL1
152 /// 4 -> ID_AA64DFR1_EL1
153 /// 5 -> ID_AA64AFR0_EL1
154 /// 6 -> ID_AA64AFR1_EL1
155 /// 7 -> ID_AA64ISAR0_EL1
156 /// 8 -> ID_AA64ISAR1_EL1
157 /// 9 -> ID_AA64MMFR0_EL1
158 /// 10 -> ID_AA64MMFR1_EL1
159 /// 11 -> ID_AA64MMFR2_EL1
160 pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, registers: [12]u64) ?Target.Cpu {
161 const info = detectNativeCoreInfo(registers[0]);
162 const model = cpu_models.isKnown(info, true) orelse return null;
163
164 var cpu = Target.Cpu{
165 .arch = arch,
166 .model = model,
167 .features = Target.Cpu.Feature.Set.empty,
168 };
169
170 detectNativeCpuFeatures(&cpu, registers[1..12]);
171 addInstructionFusions(&cpu, info);
172
173 return cpu;
174 }
175
176 /// Takes readout of MIDR_EL1 register as input.
177 fn detectNativeCoreInfo(midr: u64) CoreInfo {
178 var info = CoreInfo{
179 .implementer = @truncate(u8, midr >> 24),
180 .part = @truncate(u12, midr >> 4),
181 };
182
183 blk: {
184 if (info.implementer == 0x41) {
185 // ARM Ltd.
186 const special_bits = @truncate(u4, info.part >> 8);
187 if (special_bits == 0x0 or special_bits == 0x7) {
188 // TODO Variant and arch encoded differently.
189 break :blk;
190 }
191 }
192
193 info.variant |= @intCast(u8, @truncate(u4, midr >> 20)) << 4;
194 info.variant |= @truncate(u4, midr);
195 info.architecture = @truncate(u4, midr >> 16);
196 }
197
198 return info;
199 }
200
201 /// Input array should consist of readouts from 11 system registers such that:
202 /// 0 -> ID_AA64PFR0_EL1
203 /// 1 -> ID_AA64PFR1_EL1
204 /// 2 -> ID_AA64DFR0_EL1
205 /// 3 -> ID_AA64DFR1_EL1
206 /// 4 -> ID_AA64AFR0_EL1
207 /// 5 -> ID_AA64AFR1_EL1
208 /// 6 -> ID_AA64ISAR0_EL1
209 /// 7 -> ID_AA64ISAR1_EL1
210 /// 8 -> ID_AA64MMFR0_EL1
211 /// 9 -> ID_AA64MMFR1_EL1
212 /// 10 -> ID_AA64MMFR2_EL1
213 fn detectNativeCpuFeatures(cpu: *Target.Cpu, registers: *const [11]u64) void {
214 // ID_AA64PFR0_EL1
215 setFeature(cpu, .dit, bitField(registers[0], 48) >= 1);
216 setFeature(cpu, .am, bitField(registers[0], 44) >= 1);
217 setFeature(cpu, .amvs, bitField(registers[0], 44) >= 2);
218 setFeature(cpu, .mpam, bitField(registers[0], 40) >= 1); // MPAM v1.0
219 setFeature(cpu, .sel2, bitField(registers[0], 36) >= 1);
220 setFeature(cpu, .sve, bitField(registers[0], 32) >= 1);
221 setFeature(cpu, .el3, bitField(registers[0], 12) >= 1);
222 setFeature(cpu, .ras, bitField(registers[0], 28) >= 1);
223
224 if (bitField(registers[0], 20) < 0xF) blk: {
225 if (bitField(registers[0], 16) != bitField(registers[0], 20)) break :blk; // This should never occur
226
227 setFeature(cpu, .neon, true);
228 setFeature(cpu, .fp_armv8, true);
229 setFeature(cpu, .fullfp16, bitField(registers[0], 20) > 0);
230 }
231
232 // ID_AA64PFR1_EL1
233 setFeature(cpu, .mpam, bitField(registers[1], 16) > 0 and bitField(registers[0], 40) == 0); // MPAM v0.1
234 setFeature(cpu, .mte, bitField(registers[1], 8) >= 1);
235 setFeature(cpu, .ssbs, bitField(registers[1], 4) >= 1);
236 setFeature(cpu, .bti, bitField(registers[1], 0) >= 1);
237
238 // ID_AA64DFR0_EL1
239 setFeature(cpu, .tracev8_4, bitField(registers[2], 40) >= 1);
240 setFeature(cpu, .spe, bitField(registers[2], 32) >= 1);
241 setFeature(cpu, .perfmon, bitField(registers[2], 8) >= 1 and bitField(registers[2], 8) < 0xF);
242
243 // ID_AA64DFR1_EL1 reserved
244 // ID_AA64AFR0_EL1 reserved / implementation defined
245 // ID_AA64AFR1_EL1 reserved
246
247 // ID_AA64ISAR0_EL1
248 setFeature(cpu, .rand, bitField(registers[6], 60) >= 1);
249 setFeature(cpu, .tlb_rmi, bitField(registers[6], 56) >= 1);
250 setFeature(cpu, .flagm, bitField(registers[6], 52) >= 1);
251 setFeature(cpu, .fp16fml, bitField(registers[6], 48) >= 1);
252 setFeature(cpu, .dotprod, bitField(registers[6], 44) >= 1);
253 setFeature(cpu, .sm4, bitField(registers[6], 40) >= 1 and bitField(registers[6], 36) >= 1);
254 setFeature(cpu, .sha3, bitField(registers[6], 32) >= 1 and bitField(registers[6], 12) >= 2);
255 setFeature(cpu, .rdm, bitField(registers[6], 28) >= 1);
256 setFeature(cpu, .lse, bitField(registers[6], 20) >= 1);
257 setFeature(cpu, .crc, bitField(registers[6], 16) >= 1);
258 setFeature(cpu, .sha2, bitField(registers[6], 12) >= 1 and bitField(registers[6], 8) >= 1);
259 setFeature(cpu, .aes, bitField(registers[6], 4) >= 1);
260
261 // ID_AA64ISAR1_EL1
262 setFeature(cpu, .i8mm, bitField(registers[7], 52) >= 1);
263 setFeature(cpu, .bf16, bitField(registers[7], 44) >= 1);
264 setFeature(cpu, .predres, bitField(registers[7], 40) >= 1);
265 setFeature(cpu, .sb, bitField(registers[7], 36) >= 1);
266 setFeature(cpu, .fptoint, bitField(registers[7], 32) >= 1);
267 setFeature(cpu, .rcpc, bitField(registers[7], 20) >= 1);
268 setFeature(cpu, .rcpc_immo, bitField(registers[7], 20) >= 2);
269 setFeature(cpu, .complxnum, bitField(registers[7], 16) >= 1);
270 setFeature(cpu, .jsconv, bitField(registers[7], 12) >= 1);
271 setFeature(cpu, .pauth, bitField(registers[7], 8) >= 1 or bitField(registers[7], 4) >= 1);
272 setFeature(cpu, .ccpp, bitField(registers[7], 0) >= 1);
273 setFeature(cpu, .ccdp, bitField(registers[7], 0) >= 2);
274
275 // ID_AA64MMFR0_EL1
276 setFeature(cpu, .ecv, bitField(registers[8], 60) >= 1);
277 setFeature(cpu, .fgt, bitField(registers[8], 56) >= 1);
278
279 // ID_AA64MMFR1_EL1
280 setFeature(cpu, .pan, bitField(registers[9], 20) >= 1);
281 setFeature(cpu, .pan_rwv, bitField(registers[9], 20) >= 2);
282 setFeature(cpu, .lor, bitField(registers[9], 16) >= 1);
283 setFeature(cpu, .vh, bitField(registers[9], 8) >= 1);
284 setFeature(cpu, .contextidr_el2, bitField(registers[9], 8) >= 1);
285
286 // ID_AA64MMFR2_EL1
287 setFeature(cpu, .nv, bitField(registers[10], 24) >= 1);
288 setFeature(cpu, .ccidx, bitField(registers[10], 20) >= 1);
289 setFeature(cpu, .uaops, bitField(registers[10], 4) >= 1);
290 }
291
292 fn addInstructionFusions(cpu: *Target.Cpu, info: CoreInfo) void {
293 switch (info.implementer) {
294 0x41 => switch (info.part) {
295 0xd4b, 0xd4c => {
296 // According to A78C/X1C Core Software Optimization Guide, CPU fuses certain instructions.
297 setFeature(cpu, .cmp_bcc_fusion, true);
298 setFeature(cpu, .fuse_aes, true);
299 },
300 else => {},
301 },
302 else => {},
303 }
304 }
305};
lib/std/zig/system/windows.zig+96-173
...@@ -51,23 +51,22 @@ pub fn detectRuntimeVersion() WindowsVersion {...@@ -51,23 +51,22 @@ pub fn detectRuntimeVersion() WindowsVersion {
51// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits51// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
52const max_value_len = 2048;52const max_value_len = 2048;
5353
54const RegistryPair = struct {54fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
55 key: []const u8,55 const ArgsType = @TypeOf(args);
56 value: std.os.windows.ULONG,56 const args_type_info = @typeInfo(ArgsType);
57};57
5858 if (args_type_info != .Struct) {
59fn getCpuInfoFromRegistry(59 @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
60 core: usize,60 }
61 comptime pairs_num: comptime_int,61
62 comptime pairs: [pairs_num]RegistryPair,62 const fields_info = args_type_info.Struct.fields;
63 out_buf: *[pairs_num][max_value_len]u8,63
64) !void {
65 // Originally, I wanted to issue a single call with a more complex table structure such that we64 // Originally, I wanted to issue a single call with a more complex table structure such that we
66 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into65 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
67 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would66 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
68 // end up pulling only the last CPU core info, overwriting everything else.67 // end up pulling only the last CPU core info, overwriting everything else.
69 // If anyone can come up with a solution to this, please do!68 // If anyone can come up with a solution to this, please do!
70 const table_size = 1 + pairs.len;69 const table_size = 1 + fields_info.len;
71 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;70 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
7271
73 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");72 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
...@@ -90,9 +89,9 @@ fn getCpuInfoFromRegistry(...@@ -90,9 +89,9 @@ fn getCpuInfoFromRegistry(
90 .DefaultLength = 0,89 .DefaultLength = 0,
91 };90 };
9291
93 inline for (pairs) |pair, i| {92 inline for (fields_info) |field, i| {
94 const ctx: *anyopaque = blk: {93 const ctx: *anyopaque = blk: {
95 switch (pair.value) {94 switch (@field(args, field.name).value_type) {
96 REG.SZ,95 REG.SZ,
97 REG.EXPAND_SZ,96 REG.EXPAND_SZ,
98 REG.MULTI_SZ,97 REG.MULTI_SZ,
...@@ -121,12 +120,15 @@ fn getCpuInfoFromRegistry(...@@ -121,12 +120,15 @@ fn getCpuInfoFromRegistry(
121 else => unreachable,120 else => unreachable,
122 }121 }
123 };122 };
124 const key_namee = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);123
124 var key_buf: [max_value_len / 2 + 1]u16 = undefined;
125 const key_len = try std.unicode.utf8ToUtf16Le(&key_buf, @field(args, field.name).key);
126 key_buf[key_len] = 0;
125127
126 table[i + 1] = .{128 table[i + 1] = .{
127 .QueryRoutine = null,129 .QueryRoutine = null,
128 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,130 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
129 .Name = @intToPtr([*:0]u16, @ptrToInt(key_namee)),131 .Name = key_buf[0..key_len :0],
130 .EntryContext = ctx,132 .EntryContext = ctx,
131 .DefaultType = REG.NONE,133 .DefaultType = REG.NONE,
132 .DefaultData = null,134 .DefaultData = null,
...@@ -154,16 +156,15 @@ fn getCpuInfoFromRegistry(...@@ -154,16 +156,15 @@ fn getCpuInfoFromRegistry(
154 );156 );
155 switch (res) {157 switch (res) {
156 .SUCCESS => {158 .SUCCESS => {
157 inline for (pairs) |pair, i| switch (pair.value) {159 inline for (fields_info) |field, i| switch (@field(args, field.name).value_type) {
158 REG.NONE => unreachable,
159
160 REG.SZ,160 REG.SZ,
161 REG.EXPAND_SZ,161 REG.EXPAND_SZ,
162 REG.MULTI_SZ,162 REG.MULTI_SZ,
163 => {163 => {
164 var buf = @field(args, field.name).value_buf;
164 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);165 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);
165 const len = try std.unicode.utf16leToUtf8(out_buf[i][0..], entry.Buffer[0 .. entry.Length / 2]);166 const len = try std.unicode.utf16leToUtf8(buf, entry.Buffer[0 .. entry.Length / 2]);
166 out_buf[i][len] = 0;167 buf[len] = 0;
167 },168 },
168169
169 REG.DWORD,170 REG.DWORD,
...@@ -171,12 +172,12 @@ fn getCpuInfoFromRegistry(...@@ -171,12 +172,12 @@ fn getCpuInfoFromRegistry(
171 REG.QWORD,172 REG.QWORD,
172 => {173 => {
173 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);174 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);
174 switch (pair.value) {175 switch (@field(args, field.name).value_type) {
175 REG.DWORD, REG.DWORD_BIG_ENDIAN => {176 REG.DWORD, REG.DWORD_BIG_ENDIAN => {
176 mem.copy(u8, out_buf[i][0..4], entry[0..4]);177 mem.copy(u8, @field(args, field.name).value_buf[0..4], entry[0..4]);
177 },178 },
178 REG.QWORD => {179 REG.QWORD => {
179 mem.copy(u8, out_buf[i][0..8], entry[0..8]);180 mem.copy(u8, @field(args, field.name).value_buf[0..8], entry[0..8]);
180 },181 },
181 else => unreachable,182 else => unreachable,
182 }183 }
...@@ -189,173 +190,95 @@ fn getCpuInfoFromRegistry(...@@ -189,173 +190,95 @@ fn getCpuInfoFromRegistry(
189 }190 }
190}191}
191192
192fn getCpuCount() usize {193fn setFeature(comptime Feature: type, cpu: *Target.Cpu, feature: Feature, enabled: bool) void {
193 return std.os.windows.peb().NumberOfProcessors;194 const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature));
194}
195
196const ArmCpuInfoImpl = struct {
197 cores: [4]CoreInfo = undefined,
198 core_no: usize = 0,
199 have_fields: usize = 0,
200
201 const CoreInfo = @import("arm.zig").CoreInfo;
202 const cpu_models = @import("arm.zig").cpu_models;
203
204 const Data = struct {
205 cp_4000: []const u8,
206 identifier: []const u8,
207 };
208
209 fn parseDataHook(self: *ArmCpuInfoImpl, data: Data) !void {
210 const info = &self.cores[self.core_no];
211 info.* = .{};
212
213 // CPU part
214 info.part = mem.readIntLittle(u16, data.cp_4000[0..2]) >> 4;
215 self.have_fields += 1;
216
217 // CPU implementer
218 info.implementer = data.cp_4000[3];
219 self.have_fields += 1;
220
221 var tokens = mem.tokenize(u8, data.identifier, " ");
222 while (tokens.next()) |token| {
223 if (mem.eql(u8, "Family", token)) {
224 // CPU architecture
225 const family = tokens.next() orelse continue;
226 info.architecture = try std.fmt.parseInt(u8, family, 10);
227 self.have_fields += 1;
228 break;
229 }
230 } else return;
231
232 self.addOne();
233 }
234
235 fn addOne(self: *ArmCpuInfoImpl) void {
236 if (self.have_fields == 3 and self.core_no < self.cores.len) {
237 if (self.core_no > 0) {
238 // Deduplicate the core info.
239 for (self.cores[0..self.core_no]) |it| {
240 if (std.meta.eql(it, self.cores[self.core_no]))
241 return;
242 }
243 }
244 self.core_no += 1;
245 }
246 }
247
248 fn finalize(self: ArmCpuInfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
249 if (self.core_no == 0) return null;
250
251 const is_64bit = switch (arch) {
252 .aarch64, .aarch64_be, .aarch64_32 => true,
253 else => false,
254 };
255
256 var known_models: [self.cores.len]?*const Target.Cpu.Model = undefined;
257 for (self.cores[0..self.core_no]) |core, i| {
258 known_models[i] = cpu_models.isKnown(core, is_64bit);
259 }
260
261 // XXX We pick the first core on big.LITTLE systems, hopefully the
262 // LITTLE one.
263 const model = known_models[0] orelse return null;
264 return Target.Cpu{
265 .arch = arch,
266 .model = model,
267 .features = model.features,
268 };
269 }
270};
271
272const ArmCpuInfoParser = CpuInfoParser(ArmCpuInfoImpl);
273195
274fn CpuInfoParser(comptime impl: anytype) type {196 if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx);
275 return struct {197}
276 fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
277 var obj: impl = .{};
278 var out_buf: [2][max_value_len]u8 = undefined;
279
280 var i: usize = 0;
281 while (i < getCpuCount()) : (i += 1) {
282 try getCpuInfoFromRegistry(i, 2, .{
283 .{ .key = "CP 4000", .value = REG.QWORD },
284 .{ .key = "Identifier", .value = REG.SZ },
285 }, &out_buf);
286
287 const cp_4000 = out_buf[0][0..8];
288 const identifier = mem.sliceTo(out_buf[1][0..], 0);
289
290 try obj.parseDataHook(.{
291 .cp_4000 = cp_4000,
292 .identifier = identifier,
293 });
294 }
295198
296 return obj.finalize(arch);199fn getCpuCount() usize {
297 }200 return std.os.windows.peb().NumberOfProcessors;
298 };
299}201}
300202
301fn genericCpu(comptime arch: Target.Cpu.Arch) Target.Cpu {203/// If the fine-grained detection of CPU features via Win registry fails,
302 return .{204/// we fallback to a generic CPU model but we override the feature set
205/// using `SharedUserData` contents.
206/// This is effectively what LLVM does for all ARM chips on Windows.
207fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu {
208 var cpu = Target.Cpu{
303 .arch = arch,209 .arch = arch,
304 .model = Target.Cpu.Model.generic(arch),210 .model = Target.Cpu.Model.generic(arch),
305 .features = Target.Cpu.Feature.Set.empty,211 .features = Target.Cpu.Feature.Set.empty,
306 };212 };
307}
308213
309pub fn detectNativeCpuAndFeatures() ?Target.Cpu {214 switch (arch) {
310 const current_arch = builtin.cpu.arch;
311 switch (current_arch) {
312 .aarch64, .aarch64_be, .aarch64_32 => {215 .aarch64, .aarch64_be, .aarch64_32 => {
313 var cpu = cpu: {
314 var maybe_cpu = ArmCpuInfoParser.parse(current_arch) catch break :cpu genericCpu(current_arch);
315 break :cpu maybe_cpu orelse genericCpu(current_arch);
316 };
317
318 const Feature = Target.aarch64.Feature;216 const Feature = Target.aarch64.Feature;
319217
320 // Override any features that are either present or absent218 // Override any features that are either present or absent
321 if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {219 setFeature(Feature, &cpu, .neon, IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE));
322 cpu.features.addFeature(@enumToInt(Feature.neon));220 setFeature(Feature, &cpu, .crc, IsProcessorFeaturePresent(PF.ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE));
323 } else {221 setFeature(Feature, &cpu, .crypto, IsProcessorFeaturePresent(PF.ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE));
324 cpu.features.removeFeature(@enumToInt(Feature.neon));222 setFeature(Feature, &cpu, .lse, IsProcessorFeaturePresent(PF.ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE));
325 }223 setFeature(Feature, &cpu, .dotprod, IsProcessorFeaturePresent(PF.ARM_V82_DP_INSTRUCTIONS_AVAILABLE));
326224 setFeature(Feature, &cpu, .jsconv, IsProcessorFeaturePresent(PF.ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE));
327 if (IsProcessorFeaturePresent(PF.ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) {225 },
328 cpu.features.addFeature(@enumToInt(Feature.crc));226 else => {},
329 } else {227 }
330 cpu.features.removeFeature(@enumToInt(Feature.crc));
331 }
332228
333 if (IsProcessorFeaturePresent(PF.ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) {229 return cpu;
334 cpu.features.addFeature(@enumToInt(Feature.crypto));230}
335 } else {
336 cpu.features.removeFeature(@enumToInt(Feature.crypto));
337 }
338231
339 if (IsProcessorFeaturePresent(PF.ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) {232pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
340 cpu.features.addFeature(@enumToInt(Feature.lse));233 const current_arch = builtin.cpu.arch;
341 } else {234 const cpu: ?Target.Cpu = switch (current_arch) {
342 cpu.features.removeFeature(@enumToInt(Feature.lse));235 .aarch64, .aarch64_be, .aarch64_32 => blk: {
343 }236 var cores: [128]Target.Cpu = undefined;
237 const core_count = getCpuCount();
344238
345 if (IsProcessorFeaturePresent(PF.ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) {239 if (core_count > cores.len) break :blk null;
346 cpu.features.addFeature(@enumToInt(Feature.dotprod));
347 } else {
348 cpu.features.removeFeature(@enumToInt(Feature.dotprod));
349 }
350240
351 if (IsProcessorFeaturePresent(PF.ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)) {241 var i: usize = 0;
352 cpu.features.addFeature(@enumToInt(Feature.jsconv));242 while (i < core_count) : (i += 1) {
353 } else {243 // Backing datastore
354 cpu.features.removeFeature(@enumToInt(Feature.jsconv));244 var registers: [12]u64 = undefined;
245
246 // Registry key to system ID register mapping
247 // CP 4000 -> MIDR_EL1
248 // CP 4020 -> ID_AA64PFR0_EL1
249 // CP 4021 -> ID_AA64PFR1_EL1
250 // CP 4028 -> ID_AA64DFR0_EL1
251 // CP 4029 -> ID_AA64DFR1_EL1
252 // CP 402C -> ID_AA64AFR0_EL1
253 // CP 402D -> ID_AA64AFR1_EL1
254 // CP 4030 -> ID_AA64ISAR0_EL1
255 // CP 4031 -> ID_AA64ISAR1_EL1
256 // CP 4038 -> ID_AA64MMFR0_EL1
257 // CP 4039 -> ID_AA64MMFR1_EL1
258 // CP 403A -> ID_AA64MMFR2_EL1
259 getCpuInfoFromRegistry(i, .{
260 .{ .key = "CP 4000", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[0]) },
261 .{ .key = "CP 4020", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[1]) },
262 .{ .key = "CP 4021", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[2]) },
263 .{ .key = "CP 4028", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[3]) },
264 .{ .key = "CP 4029", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[4]) },
265 .{ .key = "CP 402C", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[5]) },
266 .{ .key = "CP 402D", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[6]) },
267 .{ .key = "CP 4030", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[7]) },
268 .{ .key = "CP 4031", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[8]) },
269 .{ .key = "CP 4038", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[9]) },
270 .{ .key = "CP 4039", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[10]) },
271 .{ .key = "CP 403A", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[11]) },
272 }) catch break :blk null;
273
274 cores[i] = @import("arm.zig").aarch64.detectNativeCpuAndFeatures(current_arch, registers) orelse
275 break :blk null;
355 }276 }
356277
357 return cpu;278 // Pick the first core, usually LITTLE in big.LITTLE architecture.
279 break :blk cores[0];
358 },280 },
359 else => {},281 else => null,
360 }282 };
283 return cpu orelse genericCpuAndNativeFeatures(current_arch);
361}284}