authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-28 17:03:14+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-28 17:07:35+01:00
log7bf12b1197823a5b0554dc3f7f67074df5fcafb1
tree1ade8a0dd66fc913855d475e569d693e4195e0b4
parent7fbd2955fae13cdc184dbd648743fb187b3ce33d

arm: move cpu model table into system/arm.zig

Now we can reuse the table between CPU model parsers on Linux and Windows. Use similar parsing structure for Windows as we do for Linux. On Windows, we rely on two entries in the registry per CPU core: `CP 4000` and `Identifier`. Collating the data from the two allows us recreating most of the `/proc/cpuinfo` data natively on Windows. Additionally, we still allow for overwriting any CPU features as flagged by pulling the feature data embedded in `SharedUserData`.

4 files changed, 292 insertions(+), 291 deletions(-)

lib/std/target/aarch64.zig-15
......@@ -2252,19 +2252,4 @@ pub const cpu = struct {
22522252 .v8a,
22532253 }),
22542254 };
2255
2256 pub const microsoft_sq3 = CpuModel{
2257 .name = "microsoft_sq3",
2258 .llvm_name = "generic",
2259 .features = featureSet(&[_]Feature{
2260 .aes,
2261 .crc,
2262 .crypto,
2263 .dotprod,
2264 .fp_armv8,
2265 .lse,
2266 .neon,
2267 .sha2,
2268 }),
2269 };
22702255};
lib/std/zig/system/arm.zig created+134
......@@ -0,0 +1,134 @@
1const std = @import("std");
2
3pub const CoreInfo = struct {
4 architecture: u8 = 0,
5 implementer: u8 = 0,
6 variant: u8 = 0,
7 part: u16 = 0,
8};
9
10pub const cpu_models = struct {
11 // Shorthands to simplify the tables below.
12 const A32 = std.Target.arm.cpu;
13 const A64 = std.Target.aarch64.cpu;
14
15 const E = struct {
16 part: u16,
17 variant: ?u8 = null, // null if matches any variant
18 m32: ?*const std.Target.Cpu.Model = null,
19 m64: ?*const std.Target.Cpu.Model = null,
20 };
21
22 // implementer = 0x41
23 const ARM = [_]E{
24 E{ .part = 0x926, .m32 = &A32.arm926ej_s, .m64 = null },
25 E{ .part = 0xb02, .m32 = &A32.mpcore, .m64 = null },
26 E{ .part = 0xb36, .m32 = &A32.arm1136j_s, .m64 = null },
27 E{ .part = 0xb56, .m32 = &A32.arm1156t2_s, .m64 = null },
28 E{ .part = 0xb76, .m32 = &A32.arm1176jz_s, .m64 = null },
29 E{ .part = 0xc05, .m32 = &A32.cortex_a5, .m64 = null },
30 E{ .part = 0xc07, .m32 = &A32.cortex_a7, .m64 = null },
31 E{ .part = 0xc08, .m32 = &A32.cortex_a8, .m64 = null },
32 E{ .part = 0xc09, .m32 = &A32.cortex_a9, .m64 = null },
33 E{ .part = 0xc0d, .m32 = &A32.cortex_a17, .m64 = null },
34 E{ .part = 0xc0f, .m32 = &A32.cortex_a15, .m64 = null },
35 E{ .part = 0xc0e, .m32 = &A32.cortex_a17, .m64 = null },
36 E{ .part = 0xc14, .m32 = &A32.cortex_r4, .m64 = null },
37 E{ .part = 0xc15, .m32 = &A32.cortex_r5, .m64 = null },
38 E{ .part = 0xc17, .m32 = &A32.cortex_r7, .m64 = null },
39 E{ .part = 0xc18, .m32 = &A32.cortex_r8, .m64 = null },
40 E{ .part = 0xc20, .m32 = &A32.cortex_m0, .m64 = null },
41 E{ .part = 0xc21, .m32 = &A32.cortex_m1, .m64 = null },
42 E{ .part = 0xc23, .m32 = &A32.cortex_m3, .m64 = null },
43 E{ .part = 0xc24, .m32 = &A32.cortex_m4, .m64 = null },
44 E{ .part = 0xc27, .m32 = &A32.cortex_m7, .m64 = null },
45 E{ .part = 0xc60, .m32 = &A32.cortex_m0plus, .m64 = null },
46 E{ .part = 0xd01, .m32 = &A32.cortex_a32, .m64 = null },
47 E{ .part = 0xd03, .m32 = &A32.cortex_a53, .m64 = &A64.cortex_a53 },
48 E{ .part = 0xd04, .m32 = &A32.cortex_a35, .m64 = &A64.cortex_a35 },
49 E{ .part = 0xd05, .m32 = &A32.cortex_a55, .m64 = &A64.cortex_a55 },
50 E{ .part = 0xd07, .m32 = &A32.cortex_a57, .m64 = &A64.cortex_a57 },
51 E{ .part = 0xd08, .m32 = &A32.cortex_a72, .m64 = &A64.cortex_a72 },
52 E{ .part = 0xd09, .m32 = &A32.cortex_a73, .m64 = &A64.cortex_a73 },
53 E{ .part = 0xd0a, .m32 = &A32.cortex_a75, .m64 = &A64.cortex_a75 },
54 E{ .part = 0xd0b, .m32 = &A32.cortex_a76, .m64 = &A64.cortex_a76 },
55 E{ .part = 0xd0c, .m32 = &A32.neoverse_n1, .m64 = &A64.neoverse_n1 },
56 E{ .part = 0xd0d, .m32 = &A32.cortex_a77, .m64 = &A64.cortex_a77 },
57 E{ .part = 0xd13, .m32 = &A32.cortex_r52, .m64 = null },
58 E{ .part = 0xd20, .m32 = &A32.cortex_m23, .m64 = null },
59 E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null },
60 E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 },
61 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 },
64 E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 },
65 E{ .part = 0xd02, .m64 = &A64.cortex_a34 },
66 E{ .part = 0xd06, .m64 = &A64.cortex_a65 },
67 E{ .part = 0xd43, .m64 = &A64.cortex_a65ae },
68 };
69 // implementer = 0x42
70 const Broadcom = [_]E{
71 E{ .part = 0x516, .m64 = &A64.thunderx2t99 },
72 };
73 // implementer = 0x43
74 const Cavium = [_]E{
75 E{ .part = 0x0a0, .m64 = &A64.thunderx },
76 E{ .part = 0x0a2, .m64 = &A64.thunderxt81 },
77 E{ .part = 0x0a3, .m64 = &A64.thunderxt83 },
78 E{ .part = 0x0a1, .m64 = &A64.thunderxt88 },
79 E{ .part = 0x0af, .m64 = &A64.thunderx2t99 },
80 };
81 // implementer = 0x46
82 const Fujitsu = [_]E{
83 E{ .part = 0x001, .m64 = &A64.a64fx },
84 };
85 // implementer = 0x48
86 const HiSilicon = [_]E{
87 E{ .part = 0xd01, .m64 = &A64.tsv110 },
88 };
89 // implementer = 0x4e
90 const Nvidia = [_]E{
91 E{ .part = 0x004, .m64 = &A64.carmel },
92 };
93 // implementer = 0x50
94 const Ampere = [_]E{
95 E{ .part = 0x000, .variant = 3, .m64 = &A64.emag },
96 E{ .part = 0x000, .m64 = &A64.xgene1 },
97 };
98 // implementer = 0x51
99 const Qualcomm = [_]E{
100 E{ .part = 0x06f, .m32 = &A32.krait },
101 E{ .part = 0x201, .m64 = &A64.kryo, .m32 = &A64.kryo },
102 E{ .part = 0x205, .m64 = &A64.kryo, .m32 = &A64.kryo },
103 E{ .part = 0x211, .m64 = &A64.kryo, .m32 = &A64.kryo },
104 E{ .part = 0x800, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
105 E{ .part = 0x801, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
106 E{ .part = 0x802, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
107 E{ .part = 0x803, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
108 E{ .part = 0x804, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
109 E{ .part = 0x805, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
110 E{ .part = 0xc00, .m64 = &A64.falkor },
111 E{ .part = 0xc01, .m64 = &A64.saphira },
112 };
113
114 pub fn isKnown(core: CoreInfo, is_64bit: bool) ?*const std.Target.Cpu.Model {
115 const models = switch (core.implementer) {
116 0x41 => &ARM,
117 0x42 => &Broadcom,
118 0x43 => &Cavium,
119 0x46 => &Fujitsu,
120 0x48 => &HiSilicon,
121 0x50 => &Ampere,
122 0x51 => &Qualcomm,
123 else => return null,
124 };
125
126 for (models) |model| {
127 if (model.part == core.part and
128 (model.variant == null or model.variant.? == core.variant))
129 return if (is_64bit) model.m64 else model.m32;
130 }
131
132 return null;
133 }
134};
lib/std/zig/system/linux.zig+7-124
......@@ -159,129 +159,7 @@ const ArmCpuinfoImpl = struct {
159159 is_really_v6: bool = false,
160160 };
161161
162 const cpu_models = struct {
163 // Shorthands to simplify the tables below.
164 const A32 = Target.arm.cpu;
165 const A64 = Target.aarch64.cpu;
166
167 const E = struct {
168 part: u16,
169 variant: ?u8 = null, // null if matches any variant
170 m32: ?*const Target.Cpu.Model = null,
171 m64: ?*const Target.Cpu.Model = null,
172 };
173
174 // implementer = 0x41
175 const ARM = [_]E{
176 E{ .part = 0x926, .m32 = &A32.arm926ej_s, .m64 = null },
177 E{ .part = 0xb02, .m32 = &A32.mpcore, .m64 = null },
178 E{ .part = 0xb36, .m32 = &A32.arm1136j_s, .m64 = null },
179 E{ .part = 0xb56, .m32 = &A32.arm1156t2_s, .m64 = null },
180 E{ .part = 0xb76, .m32 = &A32.arm1176jz_s, .m64 = null },
181 E{ .part = 0xc05, .m32 = &A32.cortex_a5, .m64 = null },
182 E{ .part = 0xc07, .m32 = &A32.cortex_a7, .m64 = null },
183 E{ .part = 0xc08, .m32 = &A32.cortex_a8, .m64 = null },
184 E{ .part = 0xc09, .m32 = &A32.cortex_a9, .m64 = null },
185 E{ .part = 0xc0d, .m32 = &A32.cortex_a17, .m64 = null },
186 E{ .part = 0xc0f, .m32 = &A32.cortex_a15, .m64 = null },
187 E{ .part = 0xc0e, .m32 = &A32.cortex_a17, .m64 = null },
188 E{ .part = 0xc14, .m32 = &A32.cortex_r4, .m64 = null },
189 E{ .part = 0xc15, .m32 = &A32.cortex_r5, .m64 = null },
190 E{ .part = 0xc17, .m32 = &A32.cortex_r7, .m64 = null },
191 E{ .part = 0xc18, .m32 = &A32.cortex_r8, .m64 = null },
192 E{ .part = 0xc20, .m32 = &A32.cortex_m0, .m64 = null },
193 E{ .part = 0xc21, .m32 = &A32.cortex_m1, .m64 = null },
194 E{ .part = 0xc23, .m32 = &A32.cortex_m3, .m64 = null },
195 E{ .part = 0xc24, .m32 = &A32.cortex_m4, .m64 = null },
196 E{ .part = 0xc27, .m32 = &A32.cortex_m7, .m64 = null },
197 E{ .part = 0xc60, .m32 = &A32.cortex_m0plus, .m64 = null },
198 E{ .part = 0xd01, .m32 = &A32.cortex_a32, .m64 = null },
199 E{ .part = 0xd03, .m32 = &A32.cortex_a53, .m64 = &A64.cortex_a53 },
200 E{ .part = 0xd04, .m32 = &A32.cortex_a35, .m64 = &A64.cortex_a35 },
201 E{ .part = 0xd05, .m32 = &A32.cortex_a55, .m64 = &A64.cortex_a55 },
202 E{ .part = 0xd07, .m32 = &A32.cortex_a57, .m64 = &A64.cortex_a57 },
203 E{ .part = 0xd08, .m32 = &A32.cortex_a72, .m64 = &A64.cortex_a72 },
204 E{ .part = 0xd09, .m32 = &A32.cortex_a73, .m64 = &A64.cortex_a73 },
205 E{ .part = 0xd0a, .m32 = &A32.cortex_a75, .m64 = &A64.cortex_a75 },
206 E{ .part = 0xd0b, .m32 = &A32.cortex_a76, .m64 = &A64.cortex_a76 },
207 E{ .part = 0xd0c, .m32 = &A32.neoverse_n1, .m64 = &A64.neoverse_n1 },
208 E{ .part = 0xd0d, .m32 = &A32.cortex_a77, .m64 = &A64.cortex_a77 },
209 E{ .part = 0xd13, .m32 = &A32.cortex_r52, .m64 = null },
210 E{ .part = 0xd20, .m32 = &A32.cortex_m23, .m64 = null },
211 E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null },
212 E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 },
213 E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c },
214 E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 },
215 E{ .part = 0xd02, .m64 = &A64.cortex_a34 },
216 E{ .part = 0xd06, .m64 = &A64.cortex_a65 },
217 E{ .part = 0xd43, .m64 = &A64.cortex_a65ae },
218 };
219 // implementer = 0x42
220 const Broadcom = [_]E{
221 E{ .part = 0x516, .m64 = &A64.thunderx2t99 },
222 };
223 // implementer = 0x43
224 const Cavium = [_]E{
225 E{ .part = 0x0a0, .m64 = &A64.thunderx },
226 E{ .part = 0x0a2, .m64 = &A64.thunderxt81 },
227 E{ .part = 0x0a3, .m64 = &A64.thunderxt83 },
228 E{ .part = 0x0a1, .m64 = &A64.thunderxt88 },
229 E{ .part = 0x0af, .m64 = &A64.thunderx2t99 },
230 };
231 // implementer = 0x46
232 const Fujitsu = [_]E{
233 E{ .part = 0x001, .m64 = &A64.a64fx },
234 };
235 // implementer = 0x48
236 const HiSilicon = [_]E{
237 E{ .part = 0xd01, .m64 = &A64.tsv110 },
238 };
239 // implementer = 0x4e
240 const Nvidia = [_]E{
241 E{ .part = 0x004, .m64 = &A64.carmel },
242 };
243 // implementer = 0x50
244 const Ampere = [_]E{
245 E{ .part = 0x000, .variant = 3, .m64 = &A64.emag },
246 E{ .part = 0x000, .m64 = &A64.xgene1 },
247 };
248 // implementer = 0x51
249 const Qualcomm = [_]E{
250 E{ .part = 0x06f, .m32 = &A32.krait },
251 E{ .part = 0x201, .m64 = &A64.kryo, .m32 = &A64.kryo },
252 E{ .part = 0x205, .m64 = &A64.kryo, .m32 = &A64.kryo },
253 E{ .part = 0x211, .m64 = &A64.kryo, .m32 = &A64.kryo },
254 E{ .part = 0x800, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
255 E{ .part = 0x801, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
256 E{ .part = 0x802, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
257 E{ .part = 0x803, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
258 E{ .part = 0x804, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
259 E{ .part = 0x805, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
260 E{ .part = 0xc00, .m64 = &A64.falkor },
261 E{ .part = 0xc01, .m64 = &A64.saphira },
262 };
263
264 fn isKnown(core: CoreInfo, is_64bit: bool) ?*const Target.Cpu.Model {
265 const models = switch (core.implementer) {
266 0x41 => &ARM,
267 0x42 => &Broadcom,
268 0x43 => &Cavium,
269 0x46 => &Fujitsu,
270 0x48 => &HiSilicon,
271 0x50 => &Ampere,
272 0x51 => &Qualcomm,
273 else => return null,
274 };
275
276 for (models) |model| {
277 if (model.part == core.part and
278 (model.variant == null or model.variant.? == core.variant))
279 return if (is_64bit) model.m64 else model.m32;
280 }
281
282 return null;
283 }
284 };
162 const cpu_models = @import("arm.zig").cpu_models;
285163
286164 fn addOne(self: *ArmCpuinfoImpl) void {
287165 if (self.have_fields == 4 and self.core_no < self.cores.len) {
......@@ -346,7 +224,12 @@ const ArmCpuinfoImpl = struct {
346224
347225 var known_models: [self.cores.len]?*const Target.Cpu.Model = undefined;
348226 for (self.cores[0..self.core_no]) |core, i| {
349 known_models[i] = cpu_models.isKnown(core, is_64bit);
227 known_models[i] = cpu_models.isKnown(.{
228 .architecture = core.architecture,
229 .implementer = core.implementer,
230 .variant = core.variant,
231 .part = core.part,
232 }, is_64bit);
350233 }
351234
352235 // XXX We pick the first core on big.LITTLE systems, hopefully the
lib/std/zig/system/windows.zig+151-152
......@@ -45,54 +45,6 @@ pub fn detectRuntimeVersion() WindowsVersion {
4545 return @intToEnum(WindowsVersion, version);
4646}
4747
48const Armv8CpuInfoImpl = struct {
49 cores: [8]*const Target.Cpu.Model = undefined,
50 core_no: usize = 0,
51
52 const cpu_family_models = .{
53 // Family, Model, Revision
54 .{ 8, "D4C", 0, &Target.aarch64.cpu.microsoft_sq3 },
55 };
56
57 fn parseOne(self: *Armv8CpuInfoImpl, identifier: []const u8) void {
58 if (mem.indexOf(u8, identifier, "ARMv8") == null) return; // Sanity check
59
60 var family: ?usize = null;
61 var model: ?[]const u8 = null;
62 var revision: ?usize = null;
63
64 var tokens = mem.tokenize(u8, identifier, " ");
65 while (tokens.next()) |token| {
66 if (mem.eql(u8, token, "Family")) {
67 const raw = tokens.next() orelse continue;
68 family = std.fmt.parseInt(usize, raw, 10) catch null;
69 }
70 if (mem.eql(u8, token, "Model")) {
71 model = tokens.next();
72 }
73 if (mem.eql(u8, token, "Revision")) {
74 const raw = tokens.next() orelse continue;
75 revision = std.fmt.parseInt(usize, raw, 10) catch null;
76 }
77 }
78
79 if (family == null or model == null or revision == null) return;
80
81 inline for (cpu_family_models) |set| {
82 if (set[0] == family.? and mem.eql(u8, set[1], model.?) and set[2] == revision.?) {
83 self.cores[self.core_no] = set[3];
84 self.core_no += 1;
85 break;
86 }
87 }
88 }
89
90 fn finalize(self: Armv8CpuInfoImpl) ?*const Target.Cpu.Model {
91 if (self.core_no != 8) return null; // Implies we have seen a core we don't know much about
92 return self.cores[0];
93 }
94};
95
9648// Technically, a registry value can be as long as 1MB. However, MS recommends storing
9749// values larger than 2048 bytes in a file rather than directly in the registry, and since we
9850// are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
......@@ -169,44 +121,16 @@ fn getCpuInfoFromRegistry(
169121 else => unreachable,
170122 }
171123 };
172 const default: struct { ptr: *anyopaque, len: u32 } = blk: {
173 switch (pair.value) {
174 REG.SZ,
175 REG.EXPAND_SZ,
176 REG.MULTI_SZ,
177 => {
178 const def = std.unicode.utf8ToUtf16LeStringLiteral("Unknown");
179 var buf: [def.len + 1]u16 = undefined;
180 mem.copy(u16, &buf, def);
181 buf[def.len] = 0;
182 break :blk .{ .ptr = &buf, .len = @intCast(u32, (buf.len + 1) * 2) };
183 },
184
185 REG.DWORD,
186 REG.DWORD_BIG_ENDIAN,
187 => {
188 var buf: [4]u8 = [_]u8{0} ** 4;
189 break :blk .{ .ptr = &buf, .len = 4 };
190 },
191
192 REG.QWORD => {
193 var buf: [8]u8 = [_]u8{0} ** 8;
194 break :blk .{ .ptr = &buf, .len = 8 };
195 },
196
197 else => unreachable,
198 }
199 };
200 const key_name = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);
124 const key_namee = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);
201125
202126 table[i + 1] = .{
203127 .QueryRoutine = null,
204 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT,
205 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
128 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
129 .Name = @intToPtr([*:0]u16, @ptrToInt(key_namee)),
206130 .EntryContext = ctx,
207 .DefaultType = pair.value,
208 .DefaultData = default.ptr,
209 .DefaultLength = default.len,
131 .DefaultType = REG.NONE,
132 .DefaultData = null,
133 .DefaultLength = 0,
210134 };
211135 }
212136
......@@ -261,102 +185,177 @@ fn getCpuInfoFromRegistry(
261185 else => unreachable,
262186 };
263187 },
264 else => return std.os.windows.unexpectedStatus(res),
188 else => return error.Unexpected,
265189 }
266190}
267191
268fn detectCpuModelArm64() !*const Target.Cpu.Model {
269 // Pull the CPU identifier from the registry.
270 // Assume max number of cores to be at 8.
271 const max_cpu_count = 8;
272 const cpu_count = getCpuCount();
192fn getCpuCount() usize {
193 return std.os.windows.peb().NumberOfProcessors;
194}
273195
274 if (cpu_count > max_cpu_count) return error.TooManyCpus;
196const ArmCpuInfoImpl = struct {
197 cores: [4]CoreInfo = undefined,
198 core_no: usize = 0,
199 have_fields: usize = 0,
275200
276 // Parse the models from strings
277 var parser = Armv8CpuInfoImpl{};
201 const CoreInfo = @import("arm.zig").CoreInfo;
202 const cpu_models = @import("arm.zig").cpu_models;
278203
279 var out_buf: [3][max_value_len]u8 = undefined;
204 const Data = struct {
205 cp_4000: []const u8,
206 identifier: []const u8,
207 };
280208
281 var i: usize = 0;
282 while (i < cpu_count) : (i += 1) {
283 try getCpuInfoFromRegistry(i, 3, .{
284 .{ .key = "CP 4000", .value = REG.QWORD },
285 .{ .key = "Identifier", .value = REG.SZ },
286 .{ .key = "VendorIdentifier", .value = REG.SZ },
287 }, &out_buf);
209 fn parseDataHook(self: *ArmCpuInfoImpl, data: Data) !void {
210 const info = &self.cores[self.core_no];
211 info.* = .{};
288212
289 const hex = out_buf[0][0..8];
290 const identifier = mem.sliceTo(out_buf[1][0..], 0);
291 const vendor_identifier = mem.sliceTo(out_buf[2][0..], 0);
292 std.log.warn("{d} => {x}, {s}, {s}", .{ i, std.fmt.fmtSliceHexLower(hex), identifier, vendor_identifier });
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();
293233 }
294234
295 return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64);
296}
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 }
297247
298fn detectNativeCpuAndFeaturesArm64() Target.Cpu {
299 const Feature = Target.aarch64.Feature;
248 fn finalize(self: ArmCpuInfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
249 if (self.core_no == 0) return null;
300250
301 const model = detectCpuModelArm64() catch Target.Cpu.Model.generic(.aarch64);
251 const is_64bit = switch (arch) {
252 .aarch64, .aarch64_be, .aarch64_32 => true,
253 else => false,
254 };
302255
303 var cpu = Target.Cpu{
304 .arch = .aarch64,
305 .model = model,
306 .features = model.features,
307 };
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 }
308260
309 // Override any features that are either present or absent
310 if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {
311 cpu.features.addFeature(@enumToInt(Feature.neon));
312 } else {
313 cpu.features.removeFeature(@enumToInt(Feature.neon));
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 };
314269 }
270};
315271
316 if (IsProcessorFeaturePresent(PF.ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) {
317 cpu.features.addFeature(@enumToInt(Feature.crc));
318 } else {
319 cpu.features.removeFeature(@enumToInt(Feature.crc));
320 }
272const ArmCpuInfoParser = CpuInfoParser(ArmCpuInfoImpl);
321273
322 if (IsProcessorFeaturePresent(PF.ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) {
323 cpu.features.addFeature(@enumToInt(Feature.crypto));
324 } else {
325 cpu.features.removeFeature(@enumToInt(Feature.crypto));
326 }
274fn CpuInfoParser(comptime impl: anytype) type {
275 return struct {
276 fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
277 var obj: impl = .{};
278 var out_buf: [2][max_value_len]u8 = undefined;
327279
328 if (IsProcessorFeaturePresent(PF.ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) {
329 cpu.features.addFeature(@enumToInt(Feature.lse));
330 } else {
331 cpu.features.removeFeature(@enumToInt(Feature.lse));
332 }
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);
333286
334 if (IsProcessorFeaturePresent(PF.ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) {
335 cpu.features.addFeature(@enumToInt(Feature.dotprod));
336 } else {
337 cpu.features.removeFeature(@enumToInt(Feature.dotprod));
338 }
287 const cp_4000 = out_buf[0][0..8];
288 const identifier = mem.sliceTo(out_buf[1][0..], 0);
339289
340 if (IsProcessorFeaturePresent(PF.ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)) {
341 cpu.features.addFeature(@enumToInt(Feature.jsconv));
342 } else {
343 cpu.features.removeFeature(@enumToInt(Feature.jsconv));
344 }
290 try obj.parseDataHook(.{
291 .cp_4000 = cp_4000,
292 .identifier = identifier,
293 });
294 }
345295
346 return cpu;
296 return obj.finalize(arch);
297 }
298 };
347299}
348300
349fn getCpuCount() usize {
350 return std.os.windows.peb().NumberOfProcessors;
301fn genericCpu(comptime arch: Target.Cpu.Arch) Target.Cpu {
302 return .{
303 .arch = arch,
304 .model = Target.Cpu.Model.generic(arch),
305 .features = Target.Cpu.Feature.Set.empty,
306 };
351307}
352308
353309pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
354 switch (builtin.cpu.arch) {
355 .aarch64 => return detectNativeCpuAndFeaturesArm64(),
356 else => |arch| return .{
357 .arch = arch,
358 .model = Target.Cpu.Model.generic(arch),
359 .features = Target.Cpu.Feature.Set.empty,
310 const current_arch = builtin.cpu.arch;
311 switch (current_arch) {
312 .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;
319
320 // Override any features that are either present or absent
321 if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {
322 cpu.features.addFeature(@enumToInt(Feature.neon));
323 } else {
324 cpu.features.removeFeature(@enumToInt(Feature.neon));
325 }
326
327 if (IsProcessorFeaturePresent(PF.ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) {
328 cpu.features.addFeature(@enumToInt(Feature.crc));
329 } else {
330 cpu.features.removeFeature(@enumToInt(Feature.crc));
331 }
332
333 if (IsProcessorFeaturePresent(PF.ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) {
334 cpu.features.addFeature(@enumToInt(Feature.crypto));
335 } else {
336 cpu.features.removeFeature(@enumToInt(Feature.crypto));
337 }
338
339 if (IsProcessorFeaturePresent(PF.ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)) {
340 cpu.features.addFeature(@enumToInt(Feature.lse));
341 } else {
342 cpu.features.removeFeature(@enumToInt(Feature.lse));
343 }
344
345 if (IsProcessorFeaturePresent(PF.ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) {
346 cpu.features.addFeature(@enumToInt(Feature.dotprod));
347 } else {
348 cpu.features.removeFeature(@enumToInt(Feature.dotprod));
349 }
350
351 if (IsProcessorFeaturePresent(PF.ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)) {
352 cpu.features.addFeature(@enumToInt(Feature.jsconv));
353 } else {
354 cpu.features.removeFeature(@enumToInt(Feature.jsconv));
355 }
356
357 return cpu;
360358 },
359 else => {},
361360 }
362361}