Apparently, on macOS 15 or newer, Rosetta 2 supports AVX1 and 2.
However, neither CPUID nor the Apple-recommended sysctl says it
has AVX. If AVX is used without checking the CPU feature, it may
run fine without SIGILL, but the runtime doesn't know AVX is
available therefore save and restore its states. This may lead to
value corruption.
Check if we are running under Rosetta 2 on macOS 15 or newer. If so,
report AVX1 and 2 as supported.
Change-Id: Ib981379405b1ae28faa378f051096827d760a4cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/700055
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
package cpu
-import _ "unsafe" // for linkname
-
func osInit() {
// macOS 12 moved these to the hw.optional.arm tree, but as of Go 1.24 we
// still support macOS 11. See [Determine Encryption Capabilities].
ARM64.HasSHA1 = true
ARM64.HasSHA2 = true
}
-
-//go:noescape
-func getsysctlbyname(name []byte) (int32, int32)
-
-// sysctlEnabled should be an internal detail,
-// but widely used packages access it using linkname.
-// Notable members of the hall of shame include:
-// - github.com/bytedance/gopkg
-// - github.com/songzhibin97/gkit
-//
-// Do not remove or change the type signature.
-// See go.dev/issue/67401.
-//
-//go:linkname sysctlEnabled
-func sysctlEnabled(name []byte) bool {
- ret, value := getsysctlbyname(name)
- if ret < 0 {
- return false
- }
- return value > 0
-}
--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin && !ios
+
+package cpu
+
+import _ "unsafe" // for linkname
+
+// Pushed from runtime.
+//
+//go:noescape
+func sysctlbynameInt32(name []byte) (int32, int32)
+
+// Pushed from runtime.
+//
+//go:noescape
+func sysctlbynameBytes(name, out []byte) int32
+
+// sysctlEnabled should be an internal detail,
+// but widely used packages access it using linkname.
+// Notable members of the hall of shame include:
+// - github.com/bytedance/gopkg
+// - github.com/songzhibin97/gkit
+//
+// Do not remove or change the type signature.
+// See go.dev/issue/67401.
+//
+//go:linkname sysctlEnabled
+func sysctlEnabled(name []byte) bool {
+ ret, value := sysctlbynameInt32(name)
+ if ret < 0 {
+ return false
+ }
+ return value > 0
+}
+
+// darwinKernelVersionCheck reports if Darwin kernel version is at
+// least major.minor.patch.
+//
+// Code borrowed from x/sys/cpu.
+func darwinKernelVersionCheck(major, minor, patch int) bool {
+ var release [256]byte
+ ret := sysctlbynameBytes([]byte("kern.osrelease\x00"), release[:])
+ if ret < 0 {
+ return false
+ }
+
+ var mmp [3]int
+ c := 0
+Loop:
+ for _, b := range release[:] {
+ switch {
+ case b >= '0' && b <= '9':
+ mmp[c] = 10*mmp[c] + int(b-'0')
+ case b == '.':
+ c++
+ if c > 2 {
+ return false
+ }
+ case b == 0:
+ break Loop
+ default:
+ return false
+ }
+ }
+ if c != 2 {
+ return false
+ }
+ return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch)
+}
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
+ osInit()
return
}
X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
if maxID < 7 {
+ osInit()
return
}
maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0)
if maxExtendedInformation < 0x80000001 {
+ osInit()
return
}
X86.HasAVXVNNI = isSet(4, eax71)
}
}
+
+ osInit()
}
func isSet(hwc uint32, value uint32) bool {
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (386 || amd64) && darwin && !ios
+
+package cpu
+
+func osInit() {
+ if isRosetta() && darwinKernelVersionCheck(24, 0, 0) {
+ // Apparently, on macOS 15 (Darwin kernel version 24) or newer,
+ // Rosetta 2 supports AVX1 and 2. However, neither CPUID nor
+ // sysctl says it has AVX. Detect this situation here and report
+ // AVX1 and 2 as supported.
+ // TODO: check if any other feature is actually supported.
+ X86.HasAVX = true
+ X86.HasAVX2 = true
+ }
+}
+
+func isRosetta() bool {
+ return sysctlEnabled([]byte("sysctl.proc_translated\x00"))
+}
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (386 || amd64) && (!darwin || ios)
+
+package cpu
+
+func osInit() {}
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "runtime"
+ "testing"
+)
+
+func TestHasAVX(t *testing.T) {
+ t.Parallel()
+ output := runTestProg(t, "testprog", "CheckAVX")
+ ok := output == "OK\n"
+ if *runtime.X86HasAVX != ok {
+ t.Fatalf("x86HasAVX: %v, CheckAVX got:\n%s", *runtime.X86HasAVX, output)
+ }
+}
func TraceStack(gp *G, tab *TraceStackTable) {
traceStack(0, gp, (*traceStackTable)(tab))
}
+
+var X86HasAVX = &x86HasAVX
return ret, out
}
-//go:linkname internal_cpu_getsysctlbyname internal/cpu.getsysctlbyname
-func internal_cpu_getsysctlbyname(name []byte) (int32, int32) {
+func sysctlbynameBytes(name, out []byte) int32 {
+ nout := uintptr(len(out))
+ ret := sysctlbyname(&name[0], &out[0], &nout, nil, 0)
+ return ret
+}
+
+//go:linkname internal_cpu_sysctlbynameInt32 internal/cpu.sysctlbynameInt32
+func internal_cpu_sysctlbynameInt32(name []byte) (int32, int32) {
return sysctlbynameInt32(name)
}
+//go:linkname internal_cpu_sysctlbynameBytes internal/cpu.sysctlbynameBytes
+func internal_cpu_sysctlbynameBytes(name, out []byte) int32 {
+ return sysctlbynameBytes(name, out)
+}
+
const (
_CTL_HW = 6
_HW_NCPU = 3
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+func init() {
+ register("CheckAVX", CheckAVX)
+}
+
+func CheckAVX() {
+ checkAVX()
+ fmt.Println("OK")
+}
+
+func checkAVX()
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·checkAVX(SB), NOSPLIT|NOFRAME, $0-0
+ VXORPS X1, X2, X3
+ RET