| ... | ... | @@ -2,11 +2,30 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Target = std.Target; |
| 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; |
| 5 | /// Only covers EAX for now. |
| 6 | const Xcr0 = packed struct(u32) { |
| 7 | x87: bool, |
| 8 | sse: bool, |
| 9 | avx: bool, |
| 10 | bndreg: bool, |
| 11 | bndcsr: bool, |
| 12 | opmask: bool, |
| 13 | zmm_hi256: bool, |
| 14 | hi16_zmm: bool, |
| 15 | pt: bool, |
| 16 | pkru: bool, |
| 17 | pasid: bool, |
| 18 | cet_u: bool, |
| 19 | cet_s: bool, |
| 20 | hdc: bool, |
| 21 | uintr: bool, |
| 22 | lbr: bool, |
| 23 | hwp: bool, |
| 24 | xtilecfg: bool, |
| 25 | xtiledata: bool, |
| 26 | apx: bool, |
| 27 | _reserved: u12, |
| 28 | }; |
| 10 | 29 | |
| 11 | 30 | fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void { |
| 12 | 31 | const idx = @as(Target.Cpu.Feature.Set.Index, @intFromEnum(feature)); |
| ... | ... | @@ -339,12 +358,6 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 339 | 358 | |
| 340 | 359 | leaf = cpuid(1, 0); |
| 341 | 360 | |
| 342 | | setFeature(cpu, .cx8, bit(leaf.edx, 8)); |
| 343 | | setFeature(cpu, .cmov, bit(leaf.edx, 15)); |
| 344 | | setFeature(cpu, .mmx, bit(leaf.edx, 23)); |
| 345 | | setFeature(cpu, .fxsr, bit(leaf.edx, 24)); |
| 346 | | setFeature(cpu, .sse, bit(leaf.edx, 25)); |
| 347 | | setFeature(cpu, .sse2, bit(leaf.edx, 26)); |
| 348 | 361 | setFeature(cpu, .sse3, bit(leaf.ecx, 0)); |
| 349 | 362 | setFeature(cpu, .pclmul, bit(leaf.ecx, 1)); |
| 350 | 363 | setFeature(cpu, .ssse3, bit(leaf.ecx, 9)); |
| ... | ... | @@ -356,13 +369,20 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 356 | 369 | setFeature(cpu, .aes, bit(leaf.ecx, 25)); |
| 357 | 370 | setFeature(cpu, .rdrnd, bit(leaf.ecx, 30)); |
| 358 | 371 | |
| 372 | setFeature(cpu, .cx8, bit(leaf.edx, 8)); |
| 373 | setFeature(cpu, .cmov, bit(leaf.edx, 15)); |
| 374 | setFeature(cpu, .mmx, bit(leaf.edx, 23)); |
| 375 | setFeature(cpu, .fxsr, bit(leaf.edx, 24)); |
| 376 | setFeature(cpu, .sse, bit(leaf.edx, 25)); |
| 377 | setFeature(cpu, .sse2, bit(leaf.edx, 26)); |
| 378 | |
| 359 | 379 | const has_xsave = bit(leaf.ecx, 27); |
| 360 | 380 | const has_avx = bit(leaf.ecx, 28); |
| 361 | 381 | |
| 362 | 382 | // Make sure not to call xgetbv if xsave is not supported |
| 363 | | const xcr0_eax = if (has_xsave and has_avx) getXCR0() else 0; |
| 383 | const xcr0: Xcr0 = if (has_xsave and has_avx) @bitCast(getXCR0()) else @bitCast(@as(u32, 0)); |
| 364 | 384 | |
| 365 | | const has_avx_save = hasMask(xcr0_eax, XCR0_XMM | XCR0_YMM); |
| 385 | const has_avx_save = xcr0.sse and xcr0.avx; |
| 366 | 386 | |
| 367 | 387 | // LLVM approaches avx512_save by hardcoding it to true on Darwin, |
| 368 | 388 | // because the kernel saves the context even if the bit is not set. |
| ... | ... | @@ -384,22 +404,26 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 384 | 404 | // Darwin lazily saves the AVX512 context on first use: trust that the OS will |
| 385 | 405 | // save the AVX512 context if we use AVX512 instructions, even if the bit is not |
| 386 | 406 | // set right now. |
| 387 | | const has_avx512_save = switch (os_tag.isDarwin()) { |
| 388 | | true => true, |
| 389 | | false => hasMask(xcr0_eax, XCR0_MASKREG | XCR0_ZMM0_15 | XCR0_ZMM16_31), |
| 390 | | }; |
| 407 | const has_avx512_save = if (os_tag.isDarwin()) |
| 408 | true |
| 409 | else |
| 410 | xcr0.zmm_hi256 and xcr0.hi16_zmm; |
| 411 | |
| 412 | // AMX requires additional context to be saved by the OS. |
| 413 | const has_amx_save = xcr0.xtilecfg and xcr0.xtiledata; |
| 391 | 414 | |
| 392 | 415 | setFeature(cpu, .avx, has_avx_save); |
| 393 | | setFeature(cpu, .fma, has_avx_save and bit(leaf.ecx, 12)); |
| 416 | setFeature(cpu, .fma, bit(leaf.ecx, 12) and has_avx_save); |
| 394 | 417 | // Only enable XSAVE if OS has enabled support for saving YMM state. |
| 395 | | setFeature(cpu, .xsave, has_avx_save and bit(leaf.ecx, 26)); |
| 396 | | setFeature(cpu, .f16c, has_avx_save and bit(leaf.ecx, 29)); |
| 418 | setFeature(cpu, .xsave, bit(leaf.ecx, 26) and has_avx_save); |
| 419 | setFeature(cpu, .f16c, bit(leaf.ecx, 29) and has_avx_save); |
| 397 | 420 | |
| 398 | 421 | leaf = cpuid(0x80000000, 0); |
| 399 | 422 | const max_ext_level = leaf.eax; |
| 400 | 423 | |
| 401 | 424 | if (max_ext_level >= 0x80000001) { |
| 402 | 425 | leaf = cpuid(0x80000001, 0); |
| 426 | |
| 403 | 427 | setFeature(cpu, .sahf, bit(leaf.ecx, 0)); |
| 404 | 428 | setFeature(cpu, .lzcnt, bit(leaf.ecx, 5)); |
| 405 | 429 | setFeature(cpu, .sse4a, bit(leaf.ecx, 6)); |
| ... | ... | @@ -409,11 +433,21 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 409 | 433 | setFeature(cpu, .fma4, bit(leaf.ecx, 16) and has_avx_save); |
| 410 | 434 | setFeature(cpu, .tbm, bit(leaf.ecx, 21)); |
| 411 | 435 | setFeature(cpu, .mwaitx, bit(leaf.ecx, 29)); |
| 436 | |
| 412 | 437 | setFeature(cpu, .@"64bit", bit(leaf.edx, 29)); |
| 413 | 438 | } else { |
| 414 | 439 | for ([_]Target.x86.Feature{ |
| 415 | | .sahf, .lzcnt, .sse4a, .prfchw, .xop, |
| 416 | | .lwp, .fma4, .tbm, .mwaitx, .@"64bit", |
| 440 | .sahf, |
| 441 | .lzcnt, |
| 442 | .sse4a, |
| 443 | .prfchw, |
| 444 | .xop, |
| 445 | .lwp, |
| 446 | .fma4, |
| 447 | .tbm, |
| 448 | .mwaitx, |
| 449 | |
| 450 | .@"64bit", |
| 417 | 451 | }) |feat| { |
| 418 | 452 | setFeature(cpu, feat, false); |
| 419 | 453 | } |
| ... | ... | @@ -422,10 +456,16 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 422 | 456 | // Misc. memory-related features. |
| 423 | 457 | if (max_ext_level >= 0x80000008) { |
| 424 | 458 | leaf = cpuid(0x80000008, 0); |
| 459 | |
| 425 | 460 | setFeature(cpu, .clzero, bit(leaf.ebx, 0)); |
| 461 | setFeature(cpu, .rdpru, bit(leaf.ebx, 4)); |
| 426 | 462 | setFeature(cpu, .wbnoinvd, bit(leaf.ebx, 9)); |
| 427 | 463 | } else { |
| 428 | | for ([_]Target.x86.Feature{ .clzero, .wbnoinvd }) |feat| { |
| 464 | for ([_]Target.x86.Feature{ |
| 465 | .clzero, |
| 466 | .rdpru, |
| 467 | .wbnoinvd, |
| 468 | }) |feat| { |
| 429 | 469 | setFeature(cpu, feat, false); |
| 430 | 470 | } |
| 431 | 471 | } |
| ... | ... | @@ -444,6 +484,7 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 444 | 484 | setFeature(cpu, .rtm, bit(leaf.ebx, 11)); |
| 445 | 485 | // AVX512 is only supported if the OS supports the context save for it. |
| 446 | 486 | setFeature(cpu, .avx512f, bit(leaf.ebx, 16) and has_avx512_save); |
| 487 | setFeature(cpu, .evex512, bit(leaf.ebx, 16) and has_avx512_save); |
| 447 | 488 | setFeature(cpu, .avx512dq, bit(leaf.ebx, 17) and has_avx512_save); |
| 448 | 489 | setFeature(cpu, .rdseed, bit(leaf.ebx, 18)); |
| 449 | 490 | setFeature(cpu, .adx, bit(leaf.ebx, 19)); |
| ... | ... | @@ -470,8 +511,8 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 470 | 511 | setFeature(cpu, .avx512vnni, bit(leaf.ecx, 11) and has_avx512_save); |
| 471 | 512 | setFeature(cpu, .avx512bitalg, bit(leaf.ecx, 12) and has_avx512_save); |
| 472 | 513 | setFeature(cpu, .avx512vpopcntdq, bit(leaf.ecx, 14) and has_avx512_save); |
| 473 | | setFeature(cpu, .avx512vp2intersect, bit(leaf.edx, 8) and has_avx512_save); |
| 474 | 514 | setFeature(cpu, .rdpid, bit(leaf.ecx, 22)); |
| 515 | setFeature(cpu, .kl, bit(leaf.ecx, 23)); |
| 475 | 516 | setFeature(cpu, .cldemote, bit(leaf.ecx, 25)); |
| 476 | 517 | setFeature(cpu, .movdiri, bit(leaf.ecx, 27)); |
| 477 | 518 | setFeature(cpu, .movdir64b, bit(leaf.ecx, 28)); |
| ... | ... | @@ -487,32 +528,153 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 487 | 528 | // leaves using cpuid, since that information is ignored while |
| 488 | 529 | // detecting features using the "-march=native" flag. |
| 489 | 530 | // For more info, see X86 ISA docs. |
| 490 | | setFeature(cpu, .pconfig, bit(leaf.edx, 18)); |
| 491 | 531 | setFeature(cpu, .uintr, bit(leaf.edx, 5)); |
| 532 | setFeature(cpu, .avx512vp2intersect, bit(leaf.edx, 8) and has_avx512_save); |
| 533 | setFeature(cpu, .serialize, bit(leaf.edx, 14)); |
| 534 | setFeature(cpu, .tsxldtrk, bit(leaf.edx, 16)); |
| 535 | setFeature(cpu, .pconfig, bit(leaf.edx, 18)); |
| 536 | setFeature(cpu, .amx_bf16, bit(leaf.edx, 22) and has_amx_save); |
| 537 | setFeature(cpu, .avx512fp16, bit(leaf.edx, 23) and has_avx512_save); |
| 538 | setFeature(cpu, .amx_tile, bit(leaf.edx, 24) and has_amx_save); |
| 539 | setFeature(cpu, .amx_int8, bit(leaf.edx, 25) and has_amx_save); |
| 492 | 540 | |
| 493 | | // TODO I feel unsure about this check. |
| 494 | | // It doesn't really seem to check for 7.1, just for 7. |
| 495 | | // Is this a sound assumption to make? |
| 496 | | // Note that this is what other implementations do, so I kind of trust it. |
| 497 | | const has_leaf_7_1 = max_level >= 7; |
| 498 | | if (has_leaf_7_1) { |
| 541 | if (leaf.eax >= 1) { |
| 499 | 542 | leaf = cpuid(0x7, 0x1); |
| 543 | |
| 544 | setFeature(cpu, .sha512, bit(leaf.eax, 0)); |
| 545 | setFeature(cpu, .sm3, bit(leaf.eax, 1)); |
| 546 | setFeature(cpu, .sm4, bit(leaf.eax, 2)); |
| 547 | setFeature(cpu, .raoint, bit(leaf.eax, 3)); |
| 548 | setFeature(cpu, .avxvnni, bit(leaf.eax, 4) and has_avx_save); |
| 500 | 549 | setFeature(cpu, .avx512bf16, bit(leaf.eax, 5) and has_avx512_save); |
| 550 | setFeature(cpu, .cmpccxadd, bit(leaf.eax, 7)); |
| 551 | setFeature(cpu, .amx_fp16, bit(leaf.eax, 21) and has_amx_save); |
| 552 | setFeature(cpu, .hreset, bit(leaf.eax, 22)); |
| 553 | setFeature(cpu, .avxifma, bit(leaf.eax, 23) and has_avx_save); |
| 554 | |
| 555 | setFeature(cpu, .avxvnniint8, bit(leaf.edx, 4) and has_avx_save); |
| 556 | setFeature(cpu, .avxneconvert, bit(leaf.edx, 5) and has_avx_save); |
| 557 | setFeature(cpu, .amx_complex, bit(leaf.edx, 8) and has_amx_save); |
| 558 | setFeature(cpu, .avxvnniint16, bit(leaf.edx, 10) and has_avx_save); |
| 559 | setFeature(cpu, .prefetchi, bit(leaf.edx, 14)); |
| 560 | setFeature(cpu, .usermsr, bit(leaf.edx, 15)); |
| 561 | setFeature(cpu, .avx10_1_256, bit(leaf.edx, 19)); |
| 562 | // APX |
| 563 | setFeature(cpu, .egpr, bit(leaf.edx, 21)); |
| 564 | setFeature(cpu, .push2pop2, bit(leaf.edx, 21)); |
| 565 | setFeature(cpu, .ppx, bit(leaf.edx, 21)); |
| 566 | setFeature(cpu, .ndd, bit(leaf.edx, 21)); |
| 567 | setFeature(cpu, .ccmp, bit(leaf.edx, 21)); |
| 568 | setFeature(cpu, .cf, bit(leaf.edx, 21)); |
| 501 | 569 | } else { |
| 502 | | setFeature(cpu, .avx512bf16, false); |
| 570 | for ([_]Target.x86.Feature{ |
| 571 | .sha512, |
| 572 | .sm3, |
| 573 | .sm4, |
| 574 | .raoint, |
| 575 | .avxvnni, |
| 576 | .avx512bf16, |
| 577 | .cmpccxadd, |
| 578 | .amx_fp16, |
| 579 | .hreset, |
| 580 | .avxifma, |
| 581 | |
| 582 | .avxvnniint8, |
| 583 | .avxneconvert, |
| 584 | .amx_complex, |
| 585 | .avxvnniint16, |
| 586 | .prefetchi, |
| 587 | .usermsr, |
| 588 | .avx10_1_256, |
| 589 | .egpr, |
| 590 | .push2pop2, |
| 591 | .ppx, |
| 592 | .ndd, |
| 593 | .ccmp, |
| 594 | .cf, |
| 595 | }) |feat| { |
| 596 | setFeature(cpu, feat, false); |
| 597 | } |
| 503 | 598 | } |
| 504 | 599 | } else { |
| 505 | 600 | for ([_]Target.x86.Feature{ |
| 506 | | .fsgsbase, .sgx, .bmi, .avx2, |
| 507 | | .bmi2, .invpcid, .rtm, .avx512f, |
| 508 | | .avx512dq, .rdseed, .adx, .avx512ifma, |
| 509 | | .clflushopt, .clwb, .avx512pf, .avx512er, |
| 510 | | .avx512cd, .sha, .avx512bw, .avx512vl, |
| 511 | | .prefetchwt1, .avx512vbmi, .pku, .waitpkg, |
| 512 | | .avx512vbmi2, .shstk, .gfni, .vaes, |
| 513 | | .vpclmulqdq, .avx512vnni, .avx512bitalg, .avx512vpopcntdq, |
| 514 | | .avx512vp2intersect, .rdpid, .cldemote, .movdiri, |
| 515 | | .movdir64b, .enqcmd, .pconfig, .avx512bf16, |
| 601 | .fsgsbase, |
| 602 | .sgx, |
| 603 | .bmi, |
| 604 | .avx2, |
| 605 | .smep, |
| 606 | .bmi2, |
| 607 | .invpcid, |
| 608 | .rtm, |
| 609 | .avx512f, |
| 610 | .evex512, |
| 611 | .avx512dq, |
| 612 | .rdseed, |
| 613 | .adx, |
| 614 | .smap, |
| 615 | .avx512ifma, |
| 616 | .clflushopt, |
| 617 | .clwb, |
| 618 | .avx512pf, |
| 619 | .avx512er, |
| 620 | .avx512cd, |
| 621 | .sha, |
| 622 | .avx512bw, |
| 623 | .avx512vl, |
| 624 | |
| 625 | .prefetchwt1, |
| 626 | .avx512vbmi, |
| 627 | .pku, |
| 628 | .waitpkg, |
| 629 | .avx512vbmi2, |
| 630 | .shstk, |
| 631 | .gfni, |
| 632 | .vaes, |
| 633 | .vpclmulqdq, |
| 634 | .avx512vnni, |
| 635 | .avx512bitalg, |
| 636 | .avx512vpopcntdq, |
| 637 | .rdpid, |
| 638 | .kl, |
| 639 | .cldemote, |
| 640 | .movdiri, |
| 641 | .movdir64b, |
| 642 | .enqcmd, |
| 643 | |
| 644 | .uintr, |
| 645 | .avx512vp2intersect, |
| 646 | .serialize, |
| 647 | .tsxldtrk, |
| 648 | .pconfig, |
| 649 | .amx_bf16, |
| 650 | .avx512fp16, |
| 651 | .amx_tile, |
| 652 | .amx_int8, |
| 653 | |
| 654 | .sha512, |
| 655 | .sm3, |
| 656 | .sm4, |
| 657 | .raoint, |
| 658 | .avxvnni, |
| 659 | .avx512bf16, |
| 660 | .cmpccxadd, |
| 661 | .amx_fp16, |
| 662 | .hreset, |
| 663 | .avxifma, |
| 664 | |
| 665 | .avxvnniint8, |
| 666 | .avxneconvert, |
| 667 | .amx_complex, |
| 668 | .avxvnniint16, |
| 669 | .prefetchi, |
| 670 | .usermsr, |
| 671 | .avx10_1_256, |
| 672 | .egpr, |
| 673 | .push2pop2, |
| 674 | .ppx, |
| 675 | .ndd, |
| 676 | .ccmp, |
| 677 | .cf, |
| 516 | 678 | }) |feat| { |
| 517 | 679 | setFeature(cpu, feat, false); |
| 518 | 680 | } |
| ... | ... | @@ -520,21 +682,55 @@ fn detectNativeFeatures(cpu: *Target.Cpu, os_tag: Target.Os.Tag) void { |
| 520 | 682 | |
| 521 | 683 | if (max_level >= 0xD and has_avx_save) { |
| 522 | 684 | leaf = cpuid(0xD, 0x1); |
| 685 | |
| 523 | 686 | // Only enable XSAVE if OS has enabled support for saving YMM state. |
| 524 | 687 | setFeature(cpu, .xsaveopt, bit(leaf.eax, 0)); |
| 525 | 688 | setFeature(cpu, .xsavec, bit(leaf.eax, 1)); |
| 526 | 689 | setFeature(cpu, .xsaves, bit(leaf.eax, 3)); |
| 527 | 690 | } else { |
| 528 | | for ([_]Target.x86.Feature{ .xsaveopt, .xsavec, .xsaves }) |feat| { |
| 691 | for ([_]Target.x86.Feature{ |
| 692 | .xsaveopt, |
| 693 | .xsavec, |
| 694 | .xsaves, |
| 695 | }) |feat| { |
| 529 | 696 | setFeature(cpu, feat, false); |
| 530 | 697 | } |
| 531 | 698 | } |
| 532 | 699 | |
| 533 | 700 | if (max_level >= 0x14) { |
| 534 | 701 | leaf = cpuid(0x14, 0); |
| 702 | |
| 535 | 703 | setFeature(cpu, .ptwrite, bit(leaf.ebx, 4)); |
| 536 | 704 | } else { |
| 537 | | setFeature(cpu, .ptwrite, false); |
| 705 | for ([_]Target.x86.Feature{ |
| 706 | .ptwrite, |
| 707 | }) |feat| { |
| 708 | setFeature(cpu, feat, false); |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | if (max_level >= 0x19) { |
| 713 | leaf = cpuid(0x19, 0); |
| 714 | |
| 715 | setFeature(cpu, .widekl, bit(leaf.ebx, 2)); |
| 716 | } else { |
| 717 | for ([_]Target.x86.Feature{ |
| 718 | .widekl, |
| 719 | }) |feat| { |
| 720 | setFeature(cpu, feat, false); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | if (max_level >= 0x24) { |
| 725 | leaf = cpuid(0x24, 0); |
| 726 | |
| 727 | setFeature(cpu, .avx10_1_512, bit(leaf.ebx, 18)); |
| 728 | } else { |
| 729 | for ([_]Target.x86.Feature{ |
| 730 | .avx10_1_512, |
| 731 | }) |feat| { |
| 732 | setFeature(cpu, feat, false); |
| 733 | } |
| 538 | 734 | } |
| 539 | 735 | } |
| 540 | 736 | |