| ... | ... | @@ -2,6 +2,12 @@ const std = @import("std"); |
| 2 | 2 | const Target = std.Target; |
| 3 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | |
| 5 | const XCR0_XMM = 0x02; |
| 6 | const XCR0_YMM = 0x04; |
| 7 | const XCR0_MASKREG = 0x20; |
| 8 | const XCR0_ZMM0_15 = 0x40; |
| 9 | const XCR0_ZMM16_31 = 0x80; |
| 10 | |
| 5 | 11 | fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void { |
| 6 | 12 | const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature)); |
| 7 | 13 | |
| ... | ... | @@ -12,6 +18,10 @@ inline fn bit(input: u32, offset: u5) bool { |
| 12 | 18 | return (input >> offset) & 1 != 0; |
| 13 | 19 | } |
| 14 | 20 | |
| 21 | inline fn hasMask(input: u32, mask: u32) bool { |
| 22 | return (input & mask) == mask; |
| 23 | } |
| 24 | |
| 15 | 25 | pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) Target.Cpu { |
| 16 | 26 | var cpu = Target.Cpu{ |
| 17 | 27 | .arch = arch, |
| ... | ... | @@ -309,7 +319,6 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 309 | 319 | |
| 310 | 320 | leaf = cpuid(1, 0); |
| 311 | 321 | |
| 312 | | setFeature(cpu, .cx8, bit(leaf.edx, 8)); |
| 313 | 322 | setFeature(cpu, .cx8, bit(leaf.edx, 8)); |
| 314 | 323 | setFeature(cpu, .cmov, bit(leaf.edx, 15)); |
| 315 | 324 | setFeature(cpu, .mmx, bit(leaf.edx, 23)); |
| ... | ... | @@ -327,11 +336,13 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 327 | 336 | setFeature(cpu, .aes, bit(leaf.ecx, 25)); |
| 328 | 337 | setFeature(cpu, .rdrnd, bit(leaf.ecx, 30)); |
| 329 | 338 | |
| 330 | | // If the CPU supports XSAVE/XRESTORE (bit 27) and AVX (bit 28) also check |
| 331 | | // if the AVX registers are saved & restored on context switch |
| 332 | | const has_avx_save = bit(leaf.ecx, 27) and |
| 333 | | bit(leaf.ecx, 28) and |
| 334 | | ((getXCR0() & 0x6) == 0x6); |
| 339 | const has_xsave = bit(leaf.ecx, 27); |
| 340 | const has_avx = bit(leaf.ecx, 28); |
| 341 | |
| 342 | // Make sure not to call xgetbv if xsave is not supported |
| 343 | const xcr0_eax = if (has_xsave and has_avx) getXCR0() else 0; |
| 344 | |
| 345 | const has_avx_save = hasMask(xcr0_eax, XCR0_XMM | XCR0_YMM); |
| 335 | 346 | |
| 336 | 347 | // LLVM approaches avx512_save by hardcoding it to true on Darwin, |
| 337 | 348 | // because the kernel saves the context even if the bit is not set. |
| ... | ... | @@ -355,7 +366,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 355 | 366 | // set right now. |
| 356 | 367 | const has_avx512_save = switch (os_tag.isDarwin()) { |
| 357 | 368 | true => true, |
| 358 | | false => has_avx_save and ((leaf.eax & 0xE0) == 0xE0), |
| 369 | false => hasMask(xcr0_eax, XCR0_MASKREG | XCR0_ZMM0_15 | XCR0_ZMM16_31), |
| 359 | 370 | }; |
| 360 | 371 | |
| 361 | 372 | setFeature(cpu, .avx, has_avx_save); |
| ... | ... | @@ -530,6 +541,7 @@ fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf { |
| 530 | 541 | [leaf_ptr] "r" (&cpuid_leaf) |
| 531 | 542 | : "eax", "ebx", "ecx", "edx" |
| 532 | 543 | ); |
| 544 | |
| 533 | 545 | return cpuid_leaf; |
| 534 | 546 | } |
| 535 | 547 | |