Skip to content

Commit 021ce95

Browse files
committed
cpu: support darwin/arm64 CPU feature detection
Support ARM64 features detection. The CPU features which are supported by Apple Silicon M1 are assumed as the minimal set of features for Go programs running on darwin/arm64. The ARM64 supporting features are referred to https://en.wikichip.org/wiki/arm/armv8#ARMv8_Extensions_and_Processor_Features
1 parent 59db8d7 commit 021ce95

9 files changed

+195
-14
lines changed

cpu/cpu.go

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ var ARM64 struct {
100100
HasSHA512 bool // SHA512 hardware implementation
101101
HasSVE bool // Scalable Vector Extensions
102102
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
103+
HasFMA bool // Fused Multiply Add
103104
_ CacheLinePad
104105
}
105106

cpu/cpu_android_arm64.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2021 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+
// +build arm64
6+
// +build android
7+
8+
package cpu
9+
10+
// HWCAP/HWCAP2 bits. These are exposed by Linux.
11+
const (
12+
hwcap_FP = 1 << 0
13+
hwcap_ASIMD = 1 << 1
14+
hwcap_EVTSTRM = 1 << 2
15+
hwcap_AES = 1 << 3
16+
hwcap_PMULL = 1 << 4
17+
hwcap_SHA1 = 1 << 5
18+
hwcap_SHA2 = 1 << 6
19+
hwcap_CRC32 = 1 << 7
20+
hwcap_ATOMICS = 1 << 8
21+
hwcap_FPHP = 1 << 9
22+
hwcap_ASIMDHP = 1 << 10
23+
hwcap_CPUID = 1 << 11
24+
hwcap_ASIMDRDM = 1 << 12
25+
hwcap_JSCVT = 1 << 13
26+
hwcap_FCMA = 1 << 14
27+
hwcap_LRCPC = 1 << 15
28+
hwcap_DCPOP = 1 << 16
29+
hwcap_SHA3 = 1 << 17
30+
hwcap_SM3 = 1 << 18
31+
hwcap_SM4 = 1 << 19
32+
hwcap_ASIMDDP = 1 << 20
33+
hwcap_SHA512 = 1 << 21
34+
hwcap_SVE = 1 << 22
35+
hwcap_ASIMDFHM = 1 << 23
36+
)
37+
38+
func osInit() {
39+
if err := readHWCAP(); err != nil {
40+
// failed to read /proc/self/auxv, try reading registers directly
41+
readARM64Registers()
42+
return
43+
}
44+
45+
// Use HWCap information since reading aarch64 system registers
46+
// is not supported in user space on older linux kernels.
47+
ARM64.HasFP = isSet(hwCap, hwcap_FP)
48+
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
49+
ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM)
50+
ARM64.HasAES = isSet(hwCap, hwcap_AES)
51+
ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL)
52+
ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1)
53+
ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2)
54+
ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32)
55+
ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP)
56+
ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP)
57+
ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM)
58+
ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT)
59+
ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA)
60+
ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC)
61+
ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP)
62+
ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3)
63+
ARM64.HasSM3 = isSet(hwCap, hwcap_SM3)
64+
ARM64.HasSM4 = isSet(hwCap, hwcap_SM4)
65+
ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP)
66+
ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512)
67+
ARM64.HasSVE = isSet(hwCap, hwcap_SVE)
68+
ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM)
69+
70+
// The Samsung S9+ kernel reports support for atomics, but not all cores
71+
// actually support them, resulting in SIGILL. See issue #28431.
72+
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
73+
ARM64.HasATOMICS = false
74+
}
75+
76+
func isSet(hwc uint, value uint) bool {
77+
return hwc&value != 0
78+
}

cpu/cpu_arm64.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ func archInit() {
4141
switch runtime.GOOS {
4242
case "freebsd":
4343
readARM64Registers()
44-
case "linux", "netbsd":
45-
doinit()
44+
case "linux", "netbsd", "android", "darwin":
45+
osInit()
4646
default:
4747
// Most platforms don't seem to allow reading these registers.
4848
//
@@ -52,13 +52,6 @@ func archInit() {
5252
}
5353
}
5454

55-
// setMinimalFeatures fakes the minimal ARM64 features expected by
56-
// TestARM64minimalFeatures.
57-
func setMinimalFeatures() {
58-
ARM64.HasASIMD = true
59-
ARM64.HasFP = true
60-
}
61-
6255
func readARM64Registers() {
6356
Initialized = true
6457

@@ -170,3 +163,10 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) {
170163
func extractBits(data uint64, start, end uint) uint {
171164
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
172165
}
166+
167+
// setMinimalFeatures fakes the minimal ARM64 features expected by
168+
// TestARM64minimalFeatures.
169+
func setMinimalFeatures() {
170+
ARM64.HasASIMD = true
171+
ARM64.HasFP = true
172+
}

cpu/cpu_darwin_arm64.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2021 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+
// +build arm64
6+
// +build darwin
7+
// +build !ios
8+
9+
package cpu
10+
11+
const (
12+
commpageHasNeonFP16 uint64 = 0x00000008 // ARM v8.2 NEON FP16 supported
13+
commpageHasNeon uint64 = 0x00000100 // Advanced SIMD is supported
14+
commpageHasNeonHPFP uint64 = 0x00000200 // Advanced SIMD half-precision
15+
commpageHasVfp uint64 = 0x00000400 // VFP is supported
16+
commpageHasEvent uint64 = 0x00001000 // WFE/SVE and period event wakeup
17+
commpageHasFMA uint64 = 0x00002000 // Fused multiply add is supported
18+
commpageHasARMv82FHM uint64 = 0x00004000 // Optional ARMv8.2 FMLAL/FMLSL instructions (required in ARMv8.4)
19+
commpageHasARMv8Crypto uint64 = 0x01000000 // Optional ARMv8 Crypto extensions
20+
commpageHasARMv81Atomics uint64 = 0x02000000 // ARMv8.1 Atomic instructions supported
21+
commpageHasARMv8Crc32 uint64 = 0x04000000 // Optional ARMv8 crc32 instructions (required in ARMv8.1)
22+
commpageHasARMv82SHA512 uint64 = 0x80000000 // Optional ARMv8.2 SHA512 instructions
23+
commpageHasARMv82SHA3 uint64 = 0x0000000100000000 // Optional ARMv8.2 SHA3 instructions
24+
)
25+
26+
func osInit() {
27+
ARM64.HasFP = darwinCheckFeatureEnabled(commpageHasVfp)
28+
ARM64.HasASIMD = darwinCheckFeatureEnabled(commpageHasNeon)
29+
ARM64.HasCRC32 = darwinCheckFeatureEnabled(commpageHasARMv8Crc32)
30+
ARM64.HasATOMICS = darwinCheckFeatureEnabled(commpageHasARMv81Atomics)
31+
ARM64.HasFPHP = darwinCheckFeatureEnabled(commpageHasNeonFP16)
32+
ARM64.HasASIMDHP = darwinCheckFeatureEnabled(commpageHasNeonHPFP)
33+
ARM64.HasSHA3 = darwinCheckFeatureEnabled(commpageHasARMv82SHA3)
34+
ARM64.HasSHA512 = darwinCheckFeatureEnabled(commpageHasARMv82SHA512)
35+
ARM64.HasASIMDFHM = darwinCheckFeatureEnabled(commpageHasARMv82FHM)
36+
ARM64.HasSVE = darwinCheckFeatureEnabled(commpageHasEvent)
37+
ARM64.HasFMA = darwinCheckFeatureEnabled(commpageHasFMA)
38+
39+
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
40+
// to detect their supported state dynamically. Assume the CPU features that
41+
// Apple Silicon M1 supports to be available as a minimal set of features
42+
// to all Go programs running on darwin/arm64.
43+
ARM64.HasEVTSTRM = true
44+
ARM64.HasAES = true
45+
ARM64.HasPMULL = true
46+
ARM64.HasSHA1 = true
47+
ARM64.HasSHA2 = true
48+
ARM64.HasCPUID = true
49+
ARM64.HasASIMDRDM = true
50+
ARM64.HasJSCVT = true
51+
ARM64.HasFCMA = true
52+
ARM64.HasLRCPC = true
53+
ARM64.HasDCPOP = true
54+
ARM64.HasSM3 = true
55+
ARM64.HasSM4 = true
56+
ARM64.HasASIMDDP = true
57+
}
58+
59+
func darwinCheckFeatureEnabled(feature_vec uint64) bool

cpu/cpu_darwin_arm64.s

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//go:build arm64 && gc
2+
// +build arm64
3+
// +build gc
4+
5+
#include "textflag.h"
6+
7+
// func darwinCheckFeatureEnabled(feature_vec uint64) bool
8+
TEXT ·darwinCheckFeatureEnabled(SB), NOSPLIT, $0-8
9+
MOVD feature_vec+0(FP), R0
10+
MOVD $0, ret+0(FP) // default to false
11+
MOVD $1, R3 // set R3 as true boolean constan
12+
#ifdef GOOS_darwin // return if not darwin
13+
#ifdef GOARCH_arm64 // return if not amd64
14+
// These values from:
15+
// https://github.com/apple/darwin-xnu/blob/main/osfmk/arm/cpu_capabilities.h
16+
#define commpage64_base_address 0x0000000fffffc000
17+
#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010)
18+
MOVD $commpage64_cpu_capabilities64, R1
19+
AND R0, R1, R2
20+
CBZ R2, no_feature
21+
MOVD R3, ret+0(FP)
22+
no_feature:
23+
#endif
24+
#endif
25+
RET

cpu/cpu_freebsd_arm64.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2021 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+
// +build arm64
6+
// +build freebsd
7+
8+
package cpu
9+
10+
func osInit() {
11+
readARM64Registers()
12+
}

cpu/cpu_linux_arm64.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
// +build arm64
6+
// +build linux
7+
// +build !android
8+
59
package cpu
610

711
// HWCAP/HWCAP2 bits. These are exposed by Linux.
@@ -32,7 +36,7 @@ const (
3236
hwcap_ASIMDFHM = 1 << 23
3337
)
3438

35-
func doinit() {
39+
func osInit() {
3640
if err := readHWCAP(); err != nil {
3741
// failed to read /proc/self/auxv, try reading registers directly
3842
readARM64Registers()

cpu/cpu_other_arm64.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
//go:build !linux && !netbsd && arm64
6-
// +build !linux,!netbsd,arm64
5+
//go:build !linux && !netbsd && !darwin && arm64
6+
// +build !linux,!netbsd,!darwin,arm64
77

88
package cpu
99

10-
func doinit() {}
10+
func osInit() {
11+
setMinimalFeatures()
12+
}

cpu/cpu_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestAVX512HasAVX2AndAVX(t *testing.T) {
4242
}
4343

4444
func TestARM64minimalFeatures(t *testing.T) {
45-
if runtime.GOARCH != "arm64" || (runtime.GOOS == "darwin" || runtime.GOOS == "ios") {
45+
if runtime.GOARCH != "arm64" || runtime.GOOS == "ios" {
4646
return
4747
}
4848
if !cpu.ARM64.HasASIMD {

0 commit comments

Comments
 (0)