authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-05 03:17:32-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-05 03:17:32-04:00
log368e13a9a1db7feec3ce71481d075a7608f0e70d
treeb827ab129a7a1fbd23a281ef8f4693d1c73b83e6
parente863204c47eb89bfefd762739943f6b2f99f2c16
parent3eed61340784af95a92e2035712eeaeab7cf0ad3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8671 from LemonBoy/arm-host-det

ARM/AArch64 CPU host detection

4 files changed, 320 insertions(+), 9 deletions(-)

lib/std/Thread.zig+4-2
......@@ -75,8 +75,10 @@ pub fn spinLoopHint() callconv(.Inline) void {
7575 },
7676 .arm, .armeb, .thumb, .thumbeb => {
7777 // `yield` was introduced in v6k but are also available on v6m.
78 const can_yield = comptime std.Target.arm.featureSetHas(std.Target.current.cpu.features, .has_v6m);
79 if (can_yield) asm volatile ("yield" ::: "memory");
78 const can_yield = comptime std.Target.arm.featureSetHasAny(std.Target.current.cpu.features, .{ .has_v6k, .has_v6m });
79 if (can_yield) asm volatile ("yield" ::: "memory")
80 // Fallback.
81 else asm volatile ("" ::: "memory");
8082 },
8183 .aarch64, .aarch64_be, .aarch64_32 => {
8284 asm volatile ("isb" ::: "memory");
lib/std/target/aarch64.zig+18
......@@ -1621,6 +1621,16 @@ pub const cpu = struct {
16211621 .apple_a7,
16221622 }),
16231623 };
1624 pub const emag = CpuModel{
1625 .name = "emag",
1626 .llvm_name = null,
1627 .features = featureSet(&[_]Feature{
1628 .crc,
1629 .crypto,
1630 .perfmon,
1631 .v8a,
1632 }),
1633 };
16241634 pub const exynos_m1 = CpuModel{
16251635 .name = "exynos_m1",
16261636 .llvm_name = null,
......@@ -1867,4 +1877,12 @@ pub const cpu = struct {
18671877 .v8_2a,
18681878 }),
18691879 };
1880 pub const xgene1 = CpuModel{
1881 .name = "xgene1",
1882 .llvm_name = null,
1883 .features = featureSet(&[_]Feature{
1884 .perfmon,
1885 .v8a,
1886 }),
1887 };
18701888};
lib/std/zig/system/linux.zig+276-7
......@@ -53,7 +53,7 @@ const SparcCpuinfoImpl = struct {
5353 // At the moment we only support 64bit SPARC systems.
5454 assert(self.is_64bit);
5555
56 const model = self.model orelse Target.Cpu.Model.generic(arch);
56 const model = self.model orelse return null;
5757 return Target.Cpu{
5858 .arch = arch,
5959 .model = model,
......@@ -65,7 +65,7 @@ const SparcCpuinfoImpl = struct {
6565const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);
6666
6767test "cpuinfo: SPARC" {
68 try testParser(SparcCpuinfoParser, &Target.sparc.cpu.niagara2,
68 try testParser(SparcCpuinfoParser, .sparcv9, &Target.sparc.cpu.niagara2,
6969 \\cpu : UltraSparc T2 (Niagara2)
7070 \\fpu : UltraSparc T2 integrated FPU
7171 \\pmu : niagara2
......@@ -119,7 +119,7 @@ const PowerpcCpuinfoImpl = struct {
119119 }
120120
121121 fn finalize(self: *const PowerpcCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
122 const model = self.model orelse Target.Cpu.Model.generic(arch);
122 const model = self.model orelse return null;
123123 return Target.Cpu{
124124 .arch = arch,
125125 .model = model,
......@@ -131,13 +131,13 @@ const PowerpcCpuinfoImpl = struct {
131131const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl);
132132
133133test "cpuinfo: PowerPC" {
134 try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.@"970",
134 try testParser(PowerpcCpuinfoParser, .powerpc, &Target.powerpc.cpu.@"970",
135135 \\processor : 0
136136 \\cpu : PPC970MP, altivec supported
137137 \\clock : 1250.000000MHz
138138 \\revision : 1.1 (pvr 0044 0101)
139139 );
140 try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.pwr8,
140 try testParser(PowerpcCpuinfoParser, .powerpc64le, &Target.powerpc.cpu.pwr8,
141141 \\processor : 0
142142 \\cpu : POWER8 (raw), altivec supported
143143 \\clock : 2926.000000MHz
......@@ -145,9 +145,275 @@ test "cpuinfo: PowerPC" {
145145 );
146146}
147147
148fn testParser(parser: anytype, expected_model: *const Target.Cpu.Model, input: []const u8) !void {
148const ArmCpuinfoImpl = struct {
149 cores: [4]CoreInfo = undefined,
150 core_no: usize = 0,
151 have_fields: usize = 0,
152
153 const CoreInfo = struct {
154 architecture: u8 = 0,
155 implementer: u8 = 0,
156 variant: u8 = 0,
157 part: u16 = 0,
158 is_really_v6: bool = false,
159 };
160
161 const cpu_models = struct {
162 // Shorthands to simplify the tables below.
163 const A32 = Target.arm.cpu;
164 const A64 = Target.aarch64.cpu;
165
166 const E = struct {
167 part: u16,
168 variant: ?u8 = null, // null if matches any variant
169 m32: ?*const Target.Cpu.Model = null,
170 m64: ?*const Target.Cpu.Model = null,
171 };
172
173 // implementer = 0x41
174 const ARM = [_]E{
175 E{ .part = 0x926, .m32 = &A32.arm926ej_s, .m64 = null },
176 E{ .part = 0xb02, .m32 = &A32.mpcore, .m64 = null },
177 E{ .part = 0xb36, .m32 = &A32.arm1136j_s, .m64 = null },
178 E{ .part = 0xb56, .m32 = &A32.arm1156t2_s, .m64 = null },
179 E{ .part = 0xb76, .m32 = &A32.arm1176jz_s, .m64 = null },
180 E{ .part = 0xc05, .m32 = &A32.cortex_a5, .m64 = null },
181 E{ .part = 0xc07, .m32 = &A32.cortex_a7, .m64 = null },
182 E{ .part = 0xc08, .m32 = &A32.cortex_a8, .m64 = null },
183 E{ .part = 0xc09, .m32 = &A32.cortex_a9, .m64 = null },
184 E{ .part = 0xc0d, .m32 = &A32.cortex_a17, .m64 = null },
185 E{ .part = 0xc0f, .m32 = &A32.cortex_a15, .m64 = null },
186 E{ .part = 0xc0e, .m32 = &A32.cortex_a17, .m64 = null },
187 E{ .part = 0xc14, .m32 = &A32.cortex_r4, .m64 = null },
188 E{ .part = 0xc15, .m32 = &A32.cortex_r5, .m64 = null },
189 E{ .part = 0xc17, .m32 = &A32.cortex_r7, .m64 = null },
190 E{ .part = 0xc18, .m32 = &A32.cortex_r8, .m64 = null },
191 E{ .part = 0xc20, .m32 = &A32.cortex_m0, .m64 = null },
192 E{ .part = 0xc21, .m32 = &A32.cortex_m1, .m64 = null },
193 E{ .part = 0xc23, .m32 = &A32.cortex_m3, .m64 = null },
194 E{ .part = 0xc24, .m32 = &A32.cortex_m4, .m64 = null },
195 E{ .part = 0xc27, .m32 = &A32.cortex_m7, .m64 = null },
196 E{ .part = 0xc60, .m32 = &A32.cortex_m0plus, .m64 = null },
197 E{ .part = 0xd01, .m32 = &A32.cortex_a32, .m64 = null },
198 E{ .part = 0xd03, .m32 = &A32.cortex_a53, .m64 = &A64.cortex_a53 },
199 E{ .part = 0xd04, .m32 = &A32.cortex_a35, .m64 = &A64.cortex_a35 },
200 E{ .part = 0xd05, .m32 = &A32.cortex_a55, .m64 = &A64.cortex_a55 },
201 E{ .part = 0xd07, .m32 = &A32.cortex_a57, .m64 = &A64.cortex_a57 },
202 E{ .part = 0xd08, .m32 = &A32.cortex_a72, .m64 = &A64.cortex_a72 },
203 E{ .part = 0xd09, .m32 = &A32.cortex_a73, .m64 = &A64.cortex_a73 },
204 E{ .part = 0xd0a, .m32 = &A32.cortex_a75, .m64 = &A64.cortex_a75 },
205 E{ .part = 0xd0b, .m32 = &A32.cortex_a76, .m64 = &A64.cortex_a76 },
206 E{ .part = 0xd0c, .m32 = &A32.neoverse_n1, .m64 = null },
207 E{ .part = 0xd0d, .m32 = &A32.cortex_a77, .m64 = &A64.cortex_a77 },
208 E{ .part = 0xd13, .m32 = &A32.cortex_r52, .m64 = null },
209 E{ .part = 0xd20, .m32 = &A32.cortex_m23, .m64 = null },
210 E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null },
211 E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 },
212 E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c },
213 E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 },
214 E{ .part = 0xd02, .m64 = &A64.cortex_a34 },
215 E{ .part = 0xd06, .m64 = &A64.cortex_a65 },
216 E{ .part = 0xd43, .m64 = &A64.cortex_a65ae },
217 };
218 // implementer = 0x42
219 const Broadcom = [_]E{
220 E{ .part = 0x516, .m64 = &A64.thunderx2t99 },
221 };
222 // implementer = 0x43
223 const Cavium = [_]E{
224 E{ .part = 0x0a0, .m64 = &A64.thunderx },
225 E{ .part = 0x0a2, .m64 = &A64.thunderxt81 },
226 E{ .part = 0x0a3, .m64 = &A64.thunderxt83 },
227 E{ .part = 0x0a1, .m64 = &A64.thunderxt88 },
228 E{ .part = 0x0af, .m64 = &A64.thunderx2t99 },
229 };
230 // implementer = 0x46
231 const Fujitsu = [_]E{
232 E{ .part = 0x001, .m64 = &A64.a64fx },
233 };
234 // implementer = 0x48
235 const HiSilicon = [_]E{
236 E{ .part = 0xd01, .m64 = &A64.tsv110 },
237 };
238 // implementer = 0x4e
239 const Nvidia = [_]E{
240 E{ .part = 0x004, .m64 = &A64.carmel },
241 };
242 // implementer = 0x50
243 const Ampere = [_]E{
244 E{ .part = 0x000, .variant = 3, .m64 = &A64.emag },
245 E{ .part = 0x000, .m64 = &A64.xgene1 },
246 };
247 // implementer = 0x51
248 const Qualcomm = [_]E{
249 E{ .part = 0x06f, .m32 = &A32.krait },
250 E{ .part = 0x201, .m64 = &A64.kryo, .m32 = &A64.kryo },
251 E{ .part = 0x205, .m64 = &A64.kryo, .m32 = &A64.kryo },
252 E{ .part = 0x211, .m64 = &A64.kryo, .m32 = &A64.kryo },
253 E{ .part = 0x800, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
254 E{ .part = 0x801, .m64 = &A64.cortex_a73, .m32 = &A64.cortex_a73 },
255 E{ .part = 0x802, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
256 E{ .part = 0x803, .m64 = &A64.cortex_a75, .m32 = &A64.cortex_a75 },
257 E{ .part = 0x804, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
258 E{ .part = 0x805, .m64 = &A64.cortex_a76, .m32 = &A64.cortex_a76 },
259 E{ .part = 0xc00, .m64 = &A64.falkor },
260 E{ .part = 0xc01, .m64 = &A64.saphira },
261 };
262
263 fn isKnown(core: CoreInfo, is_64bit: bool) ?*const Target.Cpu.Model {
264 const models = switch (core.implementer) {
265 0x41 => &ARM,
266 0x42 => &Broadcom,
267 0x43 => &Cavium,
268 0x46 => &Fujitsu,
269 0x48 => &HiSilicon,
270 0x50 => &Ampere,
271 0x51 => &Qualcomm,
272 else => return null,
273 };
274
275 for (models) |model| {
276 if (model.part == core.part and
277 (model.variant == null or model.variant.? == core.variant))
278 return if (is_64bit) model.m64 else model.m32;
279 }
280
281 return null;
282 }
283 };
284
285 fn addOne(self: *ArmCpuinfoImpl) void {
286 if (self.have_fields == 4 and self.core_no < self.cores.len) {
287 if (self.core_no > 0) {
288 // Deduplicate the core info.
289 for (self.cores[0..self.core_no]) |it| {
290 if (std.meta.eql(it, self.cores[self.core_no]))
291 return;
292 }
293 }
294 self.core_no += 1;
295 }
296 }
297
298 fn line_hook(self: *ArmCpuinfoImpl, key: []const u8, value: []const u8) !bool {
299 const info = &self.cores[self.core_no];
300
301 if (mem.eql(u8, key, "processor")) {
302 // Handle both old-style and new-style cpuinfo formats.
303 // The former prints a sequence of "processor: N" lines for each
304 // core and then the info for the core that's executing this code(!)
305 // while the latter prints the infos for each core right after the
306 // "processor" key.
307 self.have_fields = 0;
308 self.cores[self.core_no] = .{};
309 } else if (mem.eql(u8, key, "CPU implementer")) {
310 info.implementer = try fmt.parseInt(u8, value, 0);
311 self.have_fields += 1;
312 } else if (mem.eql(u8, key, "CPU architecture")) {
313 // "AArch64" on older kernels.
314 info.architecture = if (mem.startsWith(u8, value, "AArch64"))
315 8
316 else
317 try fmt.parseInt(u8, value, 0);
318 self.have_fields += 1;
319 } else if (mem.eql(u8, key, "CPU variant")) {
320 info.variant = try fmt.parseInt(u8, value, 0);
321 self.have_fields += 1;
322 } else if (mem.eql(u8, key, "CPU part")) {
323 info.part = try fmt.parseInt(u16, value, 0);
324 self.have_fields += 1;
325 } else if (mem.eql(u8, key, "model name")) {
326 // ARMv6 cores report "CPU architecture" equal to 7.
327 if (mem.indexOf(u8, value, "(v6l)")) |_| {
328 info.is_really_v6 = true;
329 }
330 } else if (mem.eql(u8, key, "CPU revision")) {
331 // This field is always the last one for each CPU section.
332 _ = self.addOne();
333 }
334
335 return true;
336 }
337
338 fn finalize(self: *ArmCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
339 if (self.core_no == 0) return null;
340
341 const is_64bit = switch (arch) {
342 .aarch64, .aarch64_be, .aarch64_32 => true,
343 else => false,
344 };
345
346 var known_models: [self.cores.len]?*const Target.Cpu.Model = undefined;
347 for (self.cores[0..self.core_no]) |core, i| {
348 known_models[i] = cpu_models.isKnown(core, is_64bit);
349 }
350
351 // XXX We pick the first core on big.LITTLE systems, hopefully the
352 // LITTLE one.
353 const model = known_models[0] orelse return null;
354 return Target.Cpu{
355 .arch = arch,
356 .model = model,
357 .features = model.features,
358 };
359 }
360};
361
362const ArmCpuinfoParser = CpuinfoParser(ArmCpuinfoImpl);
363
364test "cpuinfo: ARM" {
365 try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.arm1176jz_s,
366 \\processor : 0
367 \\model name : ARMv6-compatible processor rev 7 (v6l)
368 \\BogoMIPS : 997.08
369 \\Features : half thumb fastmult vfp edsp java tls
370 \\CPU implementer : 0x41
371 \\CPU architecture: 7
372 \\CPU variant : 0x0
373 \\CPU part : 0xb76
374 \\CPU revision : 7
375 );
376 try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.cortex_a7,
377 \\processor : 0
378 \\model name : ARMv7 Processor rev 3 (v7l)
379 \\BogoMIPS : 18.00
380 \\Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae
381 \\CPU implementer : 0x41
382 \\CPU architecture: 7
383 \\CPU variant : 0x0
384 \\CPU part : 0xc07
385 \\CPU revision : 3
386 \\
387 \\processor : 4
388 \\model name : ARMv7 Processor rev 3 (v7l)
389 \\BogoMIPS : 90.00
390 \\Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae
391 \\CPU implementer : 0x41
392 \\CPU architecture: 7
393 \\CPU variant : 0x2
394 \\CPU part : 0xc0f
395 \\CPU revision : 3
396 );
397 try testParser(ArmCpuinfoParser, .aarch64, &Target.aarch64.cpu.cortex_a72,
398 \\processor : 0
399 \\BogoMIPS : 108.00
400 \\Features : fp asimd evtstrm crc32 cpuid
401 \\CPU implementer : 0x41
402 \\CPU architecture: 8
403 \\CPU variant : 0x0
404 \\CPU part : 0xd08
405 \\CPU revision : 3
406 );
407}
408
409fn testParser(
410 parser: anytype,
411 arch: Target.Cpu.Arch,
412 expected_model: *const Target.Cpu.Model,
413 input: []const u8,
414) !void {
149415 var fbs = io.fixedBufferStream(input);
150 const result = try parser.parse(.powerpc, fbs.reader());
416 const result = try parser.parse(arch, fbs.reader());
151417 testing.expectEqual(expected_model, result.?.model);
152418 testing.expect(expected_model.features.eql(result.?.features));
153419}
......@@ -186,6 +452,9 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
186452
187453 const current_arch = std.Target.current.cpu.arch;
188454 switch (current_arch) {
455 .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => {
456 return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null;
457 },
189458 .sparcv9 => {
190459 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
191460 },
tools/update_cpu_features.zig+22
......@@ -239,6 +239,28 @@ const llvm_targets = [_]LlvmTarget{
239239 "zcz_fp",
240240 },
241241 },
242 .{
243 .llvm_name = null,
244 .zig_name = "xgene1",
245 .features = &.{
246 "fp_armv8",
247 "neon",
248 "perfmon",
249 "v8a",
250 },
251 },
252 .{
253 .llvm_name = null,
254 .zig_name = "emag",
255 .features = &.{
256 "crc",
257 "crypto",
258 "fp_armv8",
259 "neon",
260 "perfmon",
261 "v8a",
262 },
263 },
242264 },
243265 },
244266 .{