cpu_darwin_x86.go

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