From e13bdde02631a6a92dea8fa21aaca6e7ae00764f Mon Sep 17 00:00:00 2001 From: Eric Lagergren Date: Sat, 2 Apr 2022 20:04:40 -0700 Subject: [PATCH] cpu: support darwin/arm64 feature detection Use commpage to determine which CPU features are enabled. Additionally, enable some safe features for the Apple M1 CPU. Builds off CL 332729. Fixes golang/go#43046 Change-Id: I72c89e78e26d6681cb396e37a1fa9a0bc4b68e69 --- cpu/cpu.go | 2 +- cpu/cpu_arm64.go | 2 +- cpu/cpu_darwin_arm64.go | 65 +++++++++++++++++++++++++++++++++++++++++ cpu/cpu_darwin_arm64.s | 48 ++++++++++++++++++++++++++++++ cpu/cpu_other_arm64.go | 2 +- cpu/cpu_test.go | 2 +- 6 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 cpu/cpu_darwin_arm64.go create mode 100644 cpu/cpu_darwin_arm64.s diff --git a/cpu/cpu.go b/cpu/cpu.go index 8fa707aa4..3e7c73eab 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -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 diff --git a/cpu/cpu_arm64.go b/cpu/cpu_arm64.go index 0e27a21e1..820cd2291 100644 --- a/cpu/cpu_arm64.go +++ b/cpu/cpu_arm64.go @@ -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. diff --git a/cpu/cpu_darwin_arm64.go b/cpu/cpu_darwin_arm64.go new file mode 100644 index 000000000..282660a8d --- /dev/null +++ b/cpu/cpu_darwin_arm64.go @@ -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) diff --git a/cpu/cpu_darwin_arm64.s b/cpu/cpu_darwin_arm64.s new file mode 100644 index 000000000..f408edf20 --- /dev/null +++ b/cpu/cpu_darwin_arm64.s @@ -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 diff --git a/cpu/cpu_other_arm64.go b/cpu/cpu_other_arm64.go index 5341e7f88..de279c017 100644 --- a/cpu/cpu_other_arm64.go +++ b/cpu/cpu_other_arm64.go @@ -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 diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go index ba2555150..952454186 100644 --- a/cpu/cpu_test.go +++ b/cpu/cpu_test.go @@ -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 {