| ... | ... | @@ -53,7 +53,7 @@ const SparcCpuinfoImpl = struct { |
| 53 | 53 | // At the moment we only support 64bit SPARC systems. |
| 54 | 54 | assert(self.is_64bit); |
| 55 | 55 | |
| 56 | | const model = self.model orelse Target.Cpu.Model.generic(arch); |
| 56 | const model = self.model orelse return null; |
| 57 | 57 | return Target.Cpu{ |
| 58 | 58 | .arch = arch, |
| 59 | 59 | .model = model, |
| ... | ... | @@ -65,7 +65,7 @@ const SparcCpuinfoImpl = struct { |
| 65 | 65 | const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); |
| 66 | 66 | |
| 67 | 67 | test "cpuinfo: SPARC" { |
| 68 | | try testParser(SparcCpuinfoParser, &Target.sparc.cpu.niagara2, |
| 68 | try testParser(SparcCpuinfoParser, .sparcv9, &Target.sparc.cpu.niagara2, |
| 69 | 69 | \\cpu : UltraSparc T2 (Niagara2) |
| 70 | 70 | \\fpu : UltraSparc T2 integrated FPU |
| 71 | 71 | \\pmu : niagara2 |
| ... | ... | @@ -119,7 +119,7 @@ const PowerpcCpuinfoImpl = struct { |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | 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; |
| 123 | 123 | return Target.Cpu{ |
| 124 | 124 | .arch = arch, |
| 125 | 125 | .model = model, |
| ... | ... | @@ -131,13 +131,13 @@ const PowerpcCpuinfoImpl = struct { |
| 131 | 131 | const PowerpcCpuinfoParser = CpuinfoParser(PowerpcCpuinfoImpl); |
| 132 | 132 | |
| 133 | 133 | test "cpuinfo: PowerPC" { |
| 134 | | try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.@"970", |
| 134 | try testParser(PowerpcCpuinfoParser, .powerpc, &Target.powerpc.cpu.@"970", |
| 135 | 135 | \\processor	: 0 |
| 136 | 136 | \\cpu		: PPC970MP, altivec supported |
| 137 | 137 | \\clock		: 1250.000000MHz |
| 138 | 138 | \\revision	: 1.1 (pvr 0044 0101) |
| 139 | 139 | ); |
| 140 | | try testParser(PowerpcCpuinfoParser, &Target.powerpc.cpu.pwr8, |
| 140 | try testParser(PowerpcCpuinfoParser, .powerpc64le, &Target.powerpc.cpu.pwr8, |
| 141 | 141 | \\processor	: 0 |
| 142 | 142 | \\cpu		: POWER8 (raw), altivec supported |
| 143 | 143 | \\clock		: 2926.000000MHz |
| ... | ... | @@ -145,9 +145,218 @@ test "cpuinfo: PowerPC" { |
| 145 | 145 | ); |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | | fn testParser(parser: anytype, expected_model: *const Target.Cpu.Model, input: []const u8) !void { |
| 148 | const 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 | // implementer = 0x41 |
| 167 | const ARM = .{ |
| 168 | .{ 0x926, &A32.arm926ej_s, null }, |
| 169 | .{ 0xb02, &A32.mpcore, null }, |
| 170 | .{ 0xb36, &A32.arm1136j_s, null }, |
| 171 | .{ 0xb56, &A32.arm1156t2_s, null }, |
| 172 | .{ 0xb76, &A32.arm1176jz_s, null }, |
| 173 | .{ 0xc05, &A32.cortex_a5, null }, |
| 174 | .{ 0xc07, &A32.cortex_a7, null }, |
| 175 | .{ 0xc08, &A32.cortex_a8, null }, |
| 176 | .{ 0xc09, &A32.cortex_a9, null }, |
| 177 | .{ 0xc0d, &A32.cortex_a17, null }, |
| 178 | .{ 0xc0f, &A32.cortex_a15, null }, |
| 179 | .{ 0xc0e, &A32.cortex_a17, null }, |
| 180 | .{ 0xc14, &A32.cortex_r4, null }, |
| 181 | .{ 0xc15, &A32.cortex_r5, null }, |
| 182 | .{ 0xc17, &A32.cortex_r7, null }, |
| 183 | .{ 0xc18, &A32.cortex_r8, null }, |
| 184 | .{ 0xc20, &A32.cortex_m0, null }, |
| 185 | .{ 0xc21, &A32.cortex_m1, null }, |
| 186 | .{ 0xc23, &A32.cortex_m3, null }, |
| 187 | .{ 0xc24, &A32.cortex_m4, null }, |
| 188 | .{ 0xc27, &A32.cortex_m7, null }, |
| 189 | .{ 0xc60, &A32.cortex_m0plus, null }, |
| 190 | .{ 0xd01, &A32.cortex_a32, null }, |
| 191 | .{ 0xd03, &A32.cortex_a53, &A64.cortex_a53 }, |
| 192 | .{ 0xd04, &A32.cortex_a35, &A64.cortex_a35 }, |
| 193 | .{ 0xd05, &A32.cortex_a55, &A64.cortex_a55 }, |
| 194 | .{ 0xd07, &A32.cortex_a57, &A64.cortex_a57 }, |
| 195 | .{ 0xd08, &A32.cortex_a72, &A64.cortex_a72 }, |
| 196 | .{ 0xd09, &A32.cortex_a73, &A64.cortex_a73 }, |
| 197 | .{ 0xd0a, &A32.cortex_a75, &A64.cortex_a75 }, |
| 198 | .{ 0xd0b, &A32.cortex_a76, &A64.cortex_a76 }, |
| 199 | .{ 0xd0c, &A32.neoverse_n1, null }, |
| 200 | .{ 0xd0d, &A32.cortex_a77, &A64.cortex_a77 }, |
| 201 | .{ 0xd13, &A32.cortex_r52, null }, |
| 202 | .{ 0xd20, &A32.cortex_m23, null }, |
| 203 | .{ 0xd21, &A32.cortex_m33, null }, |
| 204 | .{ 0xd41, &A32.cortex_a78, &A64.cortex_a78 }, |
| 205 | .{ 0xd4b, &A32.cortex_a78c, &A64.cortex_a78c }, |
| 206 | .{ 0xd44, &A32.cortex_x1, &A64.cortex_x1 }, |
| 207 | .{ 0xd02, null, &A64.cortex_a34 }, |
| 208 | .{ 0xd06, null, &A64.cortex_a65 }, |
| 209 | .{ 0xd43, null, &A64.cortex_a65ae }, |
| 210 | }; |
| 211 | |
| 212 | fn isKnown(implementer: u8, part: u16, is_64bit: bool) ?*const Target.Cpu.Model { |
| 213 | const models = switch (implementer) { |
| 214 | 0x41 => ARM, |
| 215 | else => return null, |
| 216 | }; |
| 217 | |
| 218 | inline for (models) |model| { |
| 219 | if (model[0] == part) |
| 220 | return if (is_64bit) model[2] else model[1]; |
| 221 | } |
| 222 | |
| 223 | return null; |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | fn addOne(self: *ArmCpuinfoImpl) void { |
| 228 | if (self.have_fields == 4 and self.core_no < self.cores.len) { |
| 229 | if (self.core_no > 0) { |
| 230 | // Deduplicate the core info. |
| 231 | for (self.cores[0..self.core_no]) |it| { |
| 232 | if (std.meta.eql(it, self.cores[self.core_no])) |
| 233 | return; |
| 234 | } |
| 235 | } |
| 236 | self.core_no += 1; |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | fn line_hook(self: *ArmCpuinfoImpl, key: []const u8, value: []const u8) !bool { |
| 241 | const info = &self.cores[self.core_no]; |
| 242 | |
| 243 | if (mem.eql(u8, key, "processor")) { |
| 244 | // Handle both old-style and new-style cpuinfo formats. |
| 245 | // The former prints a sequence of "processor: N" lines for each |
| 246 | // core and then the info for the core that's executing this code(!) |
| 247 | // while the latter prints the infos for each core right after the |
| 248 | // "processor" key. |
| 249 | self.have_fields = 0; |
| 250 | self.cores[self.core_no] = .{}; |
| 251 | } else if (mem.eql(u8, key, "CPU implementer")) { |
| 252 | info.implementer = try fmt.parseInt(u8, value, 0); |
| 253 | self.have_fields += 1; |
| 254 | } else if (mem.eql(u8, key, "CPU architecture")) { |
| 255 | // "AArch64" on older kernels. |
| 256 | info.architecture = if (mem.startsWith(u8, value, "AArch64")) |
| 257 | 8 |
| 258 | else |
| 259 | try fmt.parseInt(u8, value, 0); |
| 260 | self.have_fields += 1; |
| 261 | } else if (mem.eql(u8, key, "CPU variant")) { |
| 262 | info.variant = try fmt.parseInt(u8, value, 0); |
| 263 | self.have_fields += 1; |
| 264 | } else if (mem.eql(u8, key, "CPU part")) { |
| 265 | info.part = try fmt.parseInt(u16, value, 0); |
| 266 | self.have_fields += 1; |
| 267 | } else if (mem.eql(u8, key, "model name")) { |
| 268 | // ARMv6 cores report "CPU architecture" equal to 7. |
| 269 | if (mem.indexOf(u8, value, "(v6l)")) |_| { |
| 270 | info.is_really_v6 = true; |
| 271 | } |
| 272 | } else if (mem.eql(u8, key, "CPU revision")) { |
| 273 | // This field is always the last one for each CPU section. |
| 274 | _ = self.addOne(); |
| 275 | } |
| 276 | |
| 277 | return true; |
| 278 | } |
| 279 | |
| 280 | fn finalize(self: *ArmCpuinfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu { |
| 281 | if (self.core_no == 0) return null; |
| 282 | |
| 283 | const is_64bit = switch (arch) { |
| 284 | .aarch64, .aarch64_be, .aarch64_32 => true, |
| 285 | else => false, |
| 286 | }; |
| 287 | |
| 288 | var known_models: [self.cores.len]?*const Target.Cpu.Model = undefined; |
| 289 | for (self.cores[0..self.core_no]) |core, i| { |
| 290 | known_models[i] = |
| 291 | cpu_models.isKnown(core.implementer, core.part, is_64bit); |
| 292 | } |
| 293 | |
| 294 | // XXX We pick the first core on big.LITTLE systems, hopefully the |
| 295 | // LITTLE one. |
| 296 | const model = known_models[0] orelse return null; |
| 297 | return Target.Cpu{ |
| 298 | .arch = arch, |
| 299 | .model = model, |
| 300 | .features = model.features, |
| 301 | }; |
| 302 | } |
| 303 | }; |
| 304 | |
| 305 | const ArmCpuinfoParser = CpuinfoParser(ArmCpuinfoImpl); |
| 306 | |
| 307 | test "cpuinfo: ARM" { |
| 308 | try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.arm1176jz_s, |
| 309 | \\processor : 0 |
| 310 | \\model name : ARMv6-compatible processor rev 7 (v6l) |
| 311 | \\BogoMIPS : 997.08 |
| 312 | \\Features : half thumb fastmult vfp edsp java tls |
| 313 | \\CPU implementer : 0x41 |
| 314 | \\CPU architecture: 7 |
| 315 | \\CPU variant : 0x0 |
| 316 | \\CPU part : 0xb76 |
| 317 | \\CPU revision : 7 |
| 318 | ); |
| 319 | try testParser(ArmCpuinfoParser, .arm, &Target.arm.cpu.cortex_a7, |
| 320 | \\processor	: 0 |
| 321 | \\model name	: ARMv7 Processor rev 3 (v7l) |
| 322 | \\BogoMIPS	: 18.00 |
| 323 | \\Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae |
| 324 | \\CPU implementer	: 0x41 |
| 325 | \\CPU architecture: 7 |
| 326 | \\CPU variant	: 0x0 |
| 327 | \\CPU part	: 0xc07 |
| 328 | \\CPU revision	: 3 |
| 329 | \\ |
| 330 | \\processor	: 4 |
| 331 | \\model name	: ARMv7 Processor rev 3 (v7l) |
| 332 | \\BogoMIPS	: 90.00 |
| 333 | \\Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae |
| 334 | \\CPU implementer	: 0x41 |
| 335 | \\CPU architecture: 7 |
| 336 | \\CPU variant	: 0x2 |
| 337 | \\CPU part	: 0xc0f |
| 338 | \\CPU revision	: 3 |
| 339 | ); |
| 340 | try testParser(ArmCpuinfoParser, .aarch64, &Target.aarch64.cpu.cortex_a72, |
| 341 | \\processor : 0 |
| 342 | \\BogoMIPS : 108.00 |
| 343 | \\Features : fp asimd evtstrm crc32 cpuid |
| 344 | \\CPU implementer : 0x41 |
| 345 | \\CPU architecture: 8 |
| 346 | \\CPU variant : 0x0 |
| 347 | \\CPU part : 0xd08 |
| 348 | \\CPU revision : 3 |
| 349 | ); |
| 350 | } |
| 351 | |
| 352 | fn testParser( |
| 353 | parser: anytype, |
| 354 | arch: Target.Cpu.Arch, |
| 355 | expected_model: *const Target.Cpu.Model, |
| 356 | input: []const u8, |
| 357 | ) !void { |
| 149 | 358 | var fbs = io.fixedBufferStream(input); |
| 150 | | const result = try parser.parse(.powerpc, fbs.reader()); |
| 359 | const result = try parser.parse(arch, fbs.reader()); |
| 151 | 360 | testing.expectEqual(expected_model, result.?.model); |
| 152 | 361 | testing.expect(expected_model.features.eql(result.?.features)); |
| 153 | 362 | } |
| ... | ... | @@ -186,6 +395,9 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { |
| 186 | 395 | |
| 187 | 396 | const current_arch = std.Target.current.cpu.arch; |
| 188 | 397 | switch (current_arch) { |
| 398 | .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => { |
| 399 | return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null; |
| 400 | }, |
| 189 | 401 | .sparcv9 => { |
| 190 | 402 | return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null; |
| 191 | 403 | }, |