Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu: support darwin/arm64 feature detection #202

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ var X86 struct {
}

// ARM64 contains the supported CPU features of the
// current ARMv8(aarch64) platform. If the current platform
// current ARMv8 (aarch64) platform. If the current platform
// is not arm64 then all feature flags are false.
var ARM64 struct {
_ CacheLinePad
Expand Down
2 changes: 1 addition & 1 deletion cpu/cpu_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func archInit() {
switch runtime.GOOS {
case "freebsd":
readARM64Registers()
case "linux", "netbsd", "openbsd":
case "darwin", "linux", "netbsd", "openbsd":
doinit()
default:
// Many platforms don't seem to allow reading these registers.
Expand Down
65 changes: 65 additions & 0 deletions cpu/cpu_darwin_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2022 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 cpu

func doinit() {
c := readCaps()
ARM64.HasFP = c.has(commpageHasVFP)
ARM64.HasASIMD = c.has(commpageHasNeon)
ARM64.HasCRC32 = c.has(commpageHasARMv8CRC32)
ARM64.HasATOMICS = c.has(commpageHasARMv81Atomics)
ARM64.HasFPHP = c.has(commpageHasNeonFP16)
ARM64.HasASIMDHP = c.has(commpageHasNeonHPFP)
ARM64.HasSHA3 = c.has(commpageHasARMv82SHA3)
ARM64.HasSHA512 = c.has(commpageHasARMv82SHA512)
ARM64.HasASIMDFHM = c.has(commpageHasARMv82FHM)
ARM64.HasSVE = c.has(commpageHasEvent)

// As of xnu-7195.101.1, there aren't any commpage values for
// the following features.
//
// Assume the following features are available on Apple M1.
ARM64.HasEVTSTRM = true
ARM64.HasAES = true
ARM64.HasPMULL = true
ARM64.HasSHA1 = true
ARM64.HasSHA2 = true
ARM64.HasCPUID = true
ARM64.HasASIMDRDM = true
ARM64.HasJSCVT = true
ARM64.HasLRCPC = true
ARM64.HasDCPOP = true
ARM64.HasSM3 = true
ARM64.HasSM4 = true
ARM64.HasASIMDDP = true
}

// Constants taken from
// https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/osfmk/arm/cpu_capabilities.h
const (
commpageHasNeonFP16 caps = 0x00000008 // ARM v8.2 NEON FP16
commpageHasNeon caps = 0x00000100 // Advanced SIMD
commpageHasNeonHPFP caps = 0x00000200 // Advanced SIMD half-precision
commpageHasVFP caps = 0x00000400 // VFP
commpageHasEvent caps = 0x00001000 // WFE/SVE and period event wakeup
commpageHasFMA caps = 0x00002000 // Fused multiply add
commpageHasARMv82FHM caps = 0x00004000 // Optional ARMv8.2 FMLAL/FMLSL instructions (required in ARMv8.4)
commpageHasARMv8Crypto caps = 0x01000000 // Optional ARMv8 Crypto extensions
commpageHasARMv81Atomics caps = 0x02000000 // ARMv8.1 Atomic instructions
commpageHasARMv8CRC32 caps = 0x04000000 // Optional ARMv8 crc32 instructions (required in ARMv8.1)
commpageHasARMv82SHA512 caps = 0x80000000 // Optional ARMv8.2 SHA512 instructions
commpageHasARMv82SHA3 caps = 0x0000000100000000 // Optional ARMv8.2 SHA3 instructions
)

// caps is the set of commpage capabilities.
type caps uint64

// has reports whether the capability is enabled.
func (c caps) has(x caps) bool {
return c&x == x
}

// readCaps loads the current capabilities from commmpage.
func readCaps() (res caps)
48 changes: 48 additions & 0 deletions cpu/cpu_darwin_arm64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2022 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"

// Constants taken from
// https://github.com/apple-oss-distributions/xnu/blob/1031c584a5e37aff177559b9f69dbd3c8c3fd30a/osfmk/arm/cpu_capabilities.h

#define _COMM_PAGE64_BASE_ADDRESS 0x0000000FFFFFC000
#define _COMM_PAGE_START_ADDRESS _COMM_PAGE64_BASE_ADDRESS
#define _COMM_PAGE_CPU_CAPABILITIES64 (_COMM_PAGE_START_ADDRESS+0x010)
#define _COMM_PAGE_CPU_CAPABILITIES32 (_COMM_PAGE_START_ADDRESS+0x020)
#define _COMM_PAGE_VERSION (_COMM_PAGE_START_ADDRESS+0x01E)

// func readCaps() (res caps)
TEXT ·readCaps(SB), NOSPLIT, $0-8
#define ptr R0
#define caps R1

MOVD ZR, ptr
MOVD ZR, caps

// We can't check the 64-bit capabilities on iOS because they
// might not exist. They were added in xnu-7195.50.7.100.1
// (iOS 14 and macOS 11), and Go supports older iOS versions.
// _COMM_PAGE_VERSION has stayed the same (3) across kernel
// versions, so there doesn't appear to be a way to determine
// whether the 64-bit capabilities exist.
//
// The story is different for macOS because Apple's M1 CPUs
// only support Big Sur and newer.
#ifdef GOOS_ios
MOVWU $_COMM_PAGE_CPU_CAPABILITIES32, ptr
MOVWU (ptr), caps

#else
MOVD $_COMM_PAGE_CPU_CAPABILITIES64, ptr
MOVD (ptr), caps

#endif

done:
MOVD caps, res+0(FP)
RET

#undef ptr
#undef caps
2 changes: 1 addition & 1 deletion cpu/cpu_other_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build !linux && !netbsd && !openbsd && arm64
//go:build !darwin && !linux && !netbsd && !openbsd && arm64

package cpu

Expand Down
2 changes: 1 addition & 1 deletion cpu/cpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestAVX512HasAVX2AndAVX(t *testing.T) {
}

func TestARM64minimalFeatures(t *testing.T) {
if runtime.GOARCH != "arm64" || runtime.GOOS == "ios" {
if runtime.GOARCH != "arm64" {
return
}
if !cpu.ARM64.HasASIMD {
Expand Down