internal/cpu: consolidate arm64 feature detection

Move code to detect and mask arm64 CPU features from
runtime to internal/cpu.

Change-Id: Ib784e2ff056e8def125d68827b852f07a3eff0db
Reviewed-on: https://go-review.googlesource.com/c/go/+/261878
Trust: Martin Möhrmann <moehrmann@google.com>
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
This commit is contained in:
Martin Möhrmann 2020-10-13 22:30:23 +02:00
parent 55b2d479d7
commit de932da453
17 changed files with 110 additions and 223 deletions

View File

@ -0,0 +1,7 @@
// 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.
package cpu
const GOOS = "android"

View File

@ -6,13 +6,11 @@ package cpu
const CacheLinePadSize = 64
// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
// These are initialized by archauxv and should not be changed after they are
// initialized.
// HWCap may be initialized by archauxv and
// should not be changed after it was initialized.
var HWCap uint
var HWCap2 uint
// HWCAP/HWCAP2 bits. These are exposed by Linux.
// HWCAP bits. These are exposed by Linux.
const (
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
@ -32,15 +30,66 @@ func doinit() {
{Name: "atomics", Feature: &ARM64.HasATOMICS},
}
// HWCAP feature bits
ARM64.HasAES = isSet(HWCap, hwcap_AES)
ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL)
ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1)
ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2)
ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32)
ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS)
switch GOOS {
case "linux", "android":
// HWCap was populated by the runtime from the auxillary vector.
// Use HWCap information since reading aarch64 system registers
// is not supported in user space on older linux kernels.
ARM64.HasAES = isSet(HWCap, hwcap_AES)
ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL)
ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1)
ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2)
ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32)
// The Samsung S9+ kernel reports support for atomics, but not all cores
// actually support them, resulting in SIGILL. See issue #28431.
// TODO(elias.naur): Only disable the optimization on bad chipsets on android.
ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) && GOOS != "android"
case "freebsd":
// Retrieve info from system register ID_AA64ISAR0_EL1.
isar0 := getisar0()
// ID_AA64ISAR0_EL1
switch extractBits(isar0, 4, 7) {
case 1:
ARM64.HasAES = true
case 2:
ARM64.HasAES = true
ARM64.HasPMULL = true
}
switch extractBits(isar0, 8, 11) {
case 1:
ARM64.HasSHA1 = true
}
switch extractBits(isar0, 12, 15) {
case 1, 2:
ARM64.HasSHA2 = true
}
switch extractBits(isar0, 16, 19) {
case 1:
ARM64.HasCRC32 = true
}
switch extractBits(isar0, 20, 23) {
case 2:
ARM64.HasATOMICS = true
}
default:
// Other operating systems do not support reading HWCap from auxillary vector
// or reading privileged aarch64 system registers in user space.
}
}
func extractBits(data uint64, start, end uint) uint {
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}
func getisar0() uint64

View File

@ -0,0 +1,12 @@
// 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.
#include "textflag.h"
// func getisar0() uint64
TEXT ·getisar0(SB),NOSPLIT,$0
// get Instruction Set Attributes 0 into R0
MRS ID_AA64ISAR0_EL1, R0
MOVD R0, ret+0(FP)
RET

View File

@ -0,0 +1,7 @@
// 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.
package cpu
const GOOS = "freebsd"

View File

@ -0,0 +1,9 @@
// 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.
// +build !android
package cpu
const GOOS = "linux"

View File

@ -0,0 +1,11 @@
// 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.
// +build !linux
// +build !freebsd
// +build !android
package cpu
const GOOS = "other"

View File

@ -7,7 +7,6 @@
// +build !dragonfly
// +build !freebsd
// +build !netbsd
// +build !openbsd !arm64
// +build !solaris
package runtime

View File

@ -4,149 +4,6 @@
package runtime
import "internal/cpu"
const (
hwcap_FP = 1 << 0
hwcap_ASIMD = 1 << 1
hwcap_EVTSTRM = 1 << 2
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_FPHP = 1 << 9
hwcap_ASIMDHP = 1 << 10
hwcap_CPUID = 1 << 11
hwcap_ASIMDRDM = 1 << 12
hwcap_JSCVT = 1 << 13
hwcap_FCMA = 1 << 14
hwcap_LRCPC = 1 << 15
hwcap_DCPOP = 1 << 16
hwcap_SHA3 = 1 << 17
hwcap_SM3 = 1 << 18
hwcap_SM4 = 1 << 19
hwcap_ASIMDDP = 1 << 20
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
)
func getisar0() uint64
func getisar1() uint64
func getpfr0() uint64
// no hwcap support on FreeBSD aarch64, we need to retrieve the info from
// ID_AA64ISAR0_EL1, ID_AA64ISAR1_EL1 and ID_AA64PFR0_EL1
func archauxv(tag, val uintptr) {
var isar0, isar1, pfr0 uint64
isar0 = getisar0()
isar1 = getisar1()
pfr0 = getpfr0()
// ID_AA64ISAR0_EL1
switch extractBits(isar0, 4, 7) {
case 1:
cpu.HWCap |= hwcap_AES
case 2:
cpu.HWCap |= hwcap_PMULL | hwcap_AES
}
switch extractBits(isar0, 8, 11) {
case 1:
cpu.HWCap |= hwcap_SHA1
}
switch extractBits(isar0, 12, 15) {
case 1:
cpu.HWCap |= hwcap_SHA2
case 2:
cpu.HWCap |= hwcap_SHA2 | hwcap_SHA512
}
switch extractBits(isar0, 16, 19) {
case 1:
cpu.HWCap |= hwcap_CRC32
}
switch extractBits(isar0, 20, 23) {
case 2:
cpu.HWCap |= hwcap_ATOMICS
}
switch extractBits(isar0, 28, 31) {
case 1:
cpu.HWCap |= hwcap_ASIMDRDM
}
switch extractBits(isar0, 32, 35) {
case 1:
cpu.HWCap |= hwcap_SHA3
}
switch extractBits(isar0, 36, 39) {
case 1:
cpu.HWCap |= hwcap_SM3
}
switch extractBits(isar0, 40, 43) {
case 1:
cpu.HWCap |= hwcap_SM4
}
switch extractBits(isar0, 44, 47) {
case 1:
cpu.HWCap |= hwcap_ASIMDDP
}
// ID_AA64ISAR1_EL1
switch extractBits(isar1, 0, 3) {
case 1:
cpu.HWCap |= hwcap_DCPOP
}
switch extractBits(isar1, 12, 15) {
case 1:
cpu.HWCap |= hwcap_JSCVT
}
switch extractBits(isar1, 16, 19) {
case 1:
cpu.HWCap |= hwcap_FCMA
}
switch extractBits(isar1, 20, 23) {
case 1:
cpu.HWCap |= hwcap_LRCPC
}
// ID_AA64PFR0_EL1
switch extractBits(pfr0, 16, 19) {
case 0:
cpu.HWCap |= hwcap_FP
case 1:
cpu.HWCap |= hwcap_FP | hwcap_FPHP
}
switch extractBits(pfr0, 20, 23) {
case 0:
cpu.HWCap |= hwcap_ASIMD
case 1:
cpu.HWCap |= hwcap_ASIMD | hwcap_ASIMDHP
}
switch extractBits(pfr0, 32, 35) {
case 1:
cpu.HWCap |= hwcap_SVE
}
}
func extractBits(data uint64, start, end uint) uint {
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
}
//go:nosplit
func cputicks() int64 {
// Currently cputicks() is used in blocking profiler and to seed fastrand().

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// +build freebsd
// +build !arm,!arm64
// +build !arm
package runtime

View File

@ -11,19 +11,7 @@ import "internal/cpu"
func archauxv(tag, val uintptr) {
switch tag {
case _AT_HWCAP:
// arm64 doesn't have a 'cpuid' instruction equivalent and relies on
// HWCAP/HWCAP2 bits for hardware capabilities.
hwcap := uint(val)
if GOOS == "android" {
// The Samsung S9+ kernel reports support for atomics, but not all cores
// actually support them, resulting in SIGILL. See issue #28431.
// TODO(elias.naur): Only disable the optimization on bad chipsets.
const hwcap_ATOMICS = 1 << 8
hwcap &= ^uint(hwcap_ATOMICS)
}
cpu.HWCap = hwcap
case _AT_HWCAP2:
cpu.HWCap2 = uint(val)
cpu.HWCap = uint(val)
}
}

View File

@ -359,7 +359,6 @@ func sysargs(argc int32, argv **byte) {
// now argv+n is auxv
auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
sysauxv(auxv[:])
archauxv(auxv[:])
}
const (

View File

@ -14,6 +14,3 @@ func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintp
mc.__gregs[_REG_EDX] = uint32(uintptr(unsafe.Pointer(gp)))
mc.__gregs[_REG_ESI] = uint32(fn)
}
func archauxv(auxv []uintptr) {
}

View File

@ -14,6 +14,3 @@ func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintp
mc.__gregs[_REG_R9] = uint64(uintptr(unsafe.Pointer(gp)))
mc.__gregs[_REG_R12] = uint64(fn)
}
func archauxv(auxv []uintptr) {
}

View File

@ -32,6 +32,3 @@ func cputicks() int64 {
// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
return nanotime()
}
func archauxv(auxv []uintptr) {
}

View File

@ -4,10 +4,7 @@
package runtime
import (
"internal/cpu"
"unsafe"
)
import "unsafe"
func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) {
// Machine dependent mcontext initialisation for LWP.
@ -24,10 +21,3 @@ func cputicks() int64 {
// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
return nanotime()
}
func archauxv(auxv []uintptr) {
// NetBSD does not supply AT_HWCAP, however we still need to initialise cpu.HWCaps.
// For now specify the bare minimum until we add some form of capabilities
// detection. See issue https://golang.org/issue/30824#issuecomment-494901591
cpu.HWCap = 1<<1 | 1<<0 // ASIMD, FP
}

View File

@ -4,20 +4,9 @@
package runtime
import (
"internal/cpu"
)
//go:nosplit
func cputicks() int64 {
// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
return nanotime()
}
func sysargs(argc int32, argv **byte) {
// OpenBSD does not have auxv, however we still need to initialise cpu.HWCaps.
// For now specify the bare minimum until we add some form of capabilities
// detection. See issue #31746.
cpu.HWCap = 1<<1 | 1<<0 // ASIMD, FP
}

View File

@ -515,24 +515,3 @@ TEXT runtime·getCntxct(SB),NOSPLIT,$0
MOVW R0, ret+8(FP)
RET
// func getisar0() uint64
TEXT runtime·getisar0(SB),NOSPLIT,$0
// get Instruction Set Attributes 0 into R0
MRS ID_AA64ISAR0_EL1, R0
MOVD R0, ret+0(FP)
RET
// func getisar1() uint64
TEXT runtime·getisar1(SB),NOSPLIT,$0
// get Instruction Set Attributes 1 into R0
MRS ID_AA64ISAR1_EL1, R0
MOVD R0, ret+0(FP)
RET
// func getpfr0() uint64
TEXT runtime·getpfr0(SB),NOSPLIT,$0
// get Processor Feature Register 0 into R0
MRS ID_AA64PFR0_EL1, R0
MOVD R0, ret+0(FP)
RET