cpu_darwin_x86.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. // Copyright 2024 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build darwin && amd64 && gc
  5. package cpu
  6. // darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl
  7. // call (see issue 43089). It also restricts AVX512 support for Darwin to
  8. // kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233).
  9. //
  10. // Background:
  11. // Darwin implements a special mechanism to economize on thread state when
  12. // AVX512 specific registers are not in use. This scheme minimizes state when
  13. // preempting threads that haven't yet used any AVX512 instructions, but adds
  14. // special requirements to check for AVX512 hardware support at runtime (e.g.
  15. // via sysctl call or commpage inspection). See issue 43089 and link below for
  16. // full background:
  17. // https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240
  18. //
  19. // Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0
  20. // (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption
  21. // of the AVX512 mask registers (K0-K7) upon signal return. For this reason
  22. // AVX512 is considered unsafe to use on Darwin for kernel versions prior to
  23. // 21.3.0, where a fix has been confirmed. See issue 49233 for full background.
  24. func darwinSupportsAVX512() bool {
  25. return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0)
  26. }
  27. // Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies
  28. func darwinKernelVersionCheck(major, minor, patch int) bool {
  29. var release [256]byte
  30. err := darwinOSRelease(&release)
  31. if err != nil {
  32. return false
  33. }
  34. var mmp [3]int
  35. c := 0
  36. Loop:
  37. for _, b := range release[:] {
  38. switch {
  39. case b >= '0' && b <= '9':
  40. mmp[c] = 10*mmp[c] + int(b-'0')
  41. case b == '.':
  42. c++
  43. if c > 2 {
  44. return false
  45. }
  46. case b == 0:
  47. break Loop
  48. default:
  49. return false
  50. }
  51. }
  52. if c != 2 {
  53. return false
  54. }
  55. return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch)
  56. }