mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.simd] internal/cpu: report AVX1 and 2 as supported on macOS 15 Rosetta 2
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>
This commit is contained in:
parent
b509516b2e
commit
9125351583
10 changed files with 170 additions and 25 deletions
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
package cpu
|
package cpu
|
||||||
|
|
||||||
import _ "unsafe" // for linkname
|
|
||||||
|
|
||||||
func osInit() {
|
func osInit() {
|
||||||
// macOS 12 moved these to the hw.optional.arm tree, but as of Go 1.24 we
|
// 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].
|
// still support macOS 11. See [Determine Encryption Capabilities].
|
||||||
|
|
@ -29,24 +27,3 @@ func osInit() {
|
||||||
ARM64.HasSHA1 = true
|
ARM64.HasSHA1 = true
|
||||||
ARM64.HasSHA2 = 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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
72
src/internal/cpu/cpu_darwin.go
Normal file
72
src/internal/cpu/cpu_darwin.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
@ -114,6 +114,7 @@ func doinit() {
|
||||||
maxID, _, _, _ := cpuid(0, 0)
|
maxID, _, _, _ := cpuid(0, 0)
|
||||||
|
|
||||||
if maxID < 1 {
|
if maxID < 1 {
|
||||||
|
osInit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +159,7 @@ func doinit() {
|
||||||
X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
|
X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
|
||||||
|
|
||||||
if maxID < 7 {
|
if maxID < 7 {
|
||||||
|
osInit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,6 +196,7 @@ func doinit() {
|
||||||
maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0)
|
maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0)
|
||||||
|
|
||||||
if maxExtendedInformation < 0x80000001 {
|
if maxExtendedInformation < 0x80000001 {
|
||||||
|
osInit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +220,8 @@ func doinit() {
|
||||||
X86.HasAVXVNNI = isSet(4, eax71)
|
X86.HasAVXVNNI = isSet(4, eax71)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSet(hwc uint32, value uint32) bool {
|
func isSet(hwc uint32, value uint32) bool {
|
||||||
|
|
|
||||||
23
src/internal/cpu/cpu_x86_darwin.go
Normal file
23
src/internal/cpu/cpu_x86_darwin.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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"))
|
||||||
|
}
|
||||||
9
src/internal/cpu/cpu_x86_other.go
Normal file
9
src/internal/cpu/cpu_x86_other.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// 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() {}
|
||||||
19
src/runtime/cpuflags_amd64_test.go
Normal file
19
src/runtime/cpuflags_amd64_test.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1940,3 +1940,5 @@ func (t *TraceStackTable) Reset() {
|
||||||
func TraceStack(gp *G, tab *TraceStackTable) {
|
func TraceStack(gp *G, tab *TraceStackTable) {
|
||||||
traceStack(0, gp, (*traceStackTable)(tab))
|
traceStack(0, gp, (*traceStackTable)(tab))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var X86HasAVX = &x86HasAVX
|
||||||
|
|
|
||||||
|
|
@ -157,11 +157,22 @@ func sysctlbynameInt32(name []byte) (int32, int32) {
|
||||||
return ret, out
|
return ret, out
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname internal_cpu_getsysctlbyname internal/cpu.getsysctlbyname
|
func sysctlbynameBytes(name, out []byte) int32 {
|
||||||
func internal_cpu_getsysctlbyname(name []byte) (int32, 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)
|
return sysctlbynameInt32(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname internal_cpu_sysctlbynameBytes internal/cpu.sysctlbynameBytes
|
||||||
|
func internal_cpu_sysctlbynameBytes(name, out []byte) int32 {
|
||||||
|
return sysctlbynameBytes(name, out)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_CTL_HW = 6
|
_CTL_HW = 6
|
||||||
_HW_NCPU = 3
|
_HW_NCPU = 3
|
||||||
|
|
|
||||||
18
src/runtime/testdata/testprog/cpuflags_amd64.go
vendored
Normal file
18
src/runtime/testdata/testprog/cpuflags_amd64.go
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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()
|
||||||
9
src/runtime/testdata/testprog/cpuflags_amd64.s
vendored
Normal file
9
src/runtime/testdata/testprog/cpuflags_amd64.s
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue