Skip to content

Commit

Permalink
cpu: add GODEBUG options to disable use of instruction set extensions
Browse files Browse the repository at this point in the history
The GODEBUG environment variable can be used to disable usage of
specific processor features of Go programs that use the sys/cpu package.
This is useful for testing and benchmarking different code paths that
are guarded by sys/cpu variable checks.

Use of processor features can not be enabled through GODEBUG.

To disable usage of AVX and SSE41 cpu features on GOARCH amd64 use:
GODEBUG=cpu.avx=off,cpu.sse41=off

The special "all" option can be used to disable all options:
GODEBUG=all=off

This aligns the support of GODEBUG for sys/cpu with existing support
for GODEBUG in the Go standard library package internal/cpu.

Fixes golang/go#33963

Change-Id: I618b71af397bf06c57a49b2a300d032a16d05664
Reviewed-on: https://go-review.googlesource.com/c/sys/+/245237
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
  • Loading branch information
polliosa authored and martisch committed Aug 5, 2020
1 parent 64077c9 commit 0cf7623
Show file tree
Hide file tree
Showing 15 changed files with 260 additions and 10 deletions.
96 changes: 96 additions & 0 deletions cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
// various CPU architectures.
package cpu

import (
"os"
"strings"
)

// Initialized reports whether the CPU features were initialized.
//
// For some GOOS/GOARCH combinations initialization of the CPU features depends
Expand Down Expand Up @@ -169,3 +174,94 @@ var S390X struct {
HasVXE bool // vector-enhancements facility 1
_ CacheLinePad
}

func init() {
archInit()
initOptions()
processOptions()
}

// options contains the cpu debug options that can be used in GODEBUG.
// Options are arch dependent and are added by the arch specific initOptions functions.
// Features that are mandatory for the specific GOARCH should have the Required field set
// (e.g. SSE2 on amd64).
var options []option

// Option names should be lower case. e.g. avx instead of AVX.
type option struct {
Name string
Feature *bool
Specified bool // whether feature value was specified in GODEBUG
Enable bool // whether feature should be enabled
Required bool // whether feature is mandatory and can not be disabled
}

func processOptions() {
env := os.Getenv("GODEBUG")
field:
for env != "" {
field := ""
i := strings.IndexByte(env, ',')
if i < 0 {
field, env = env, ""
} else {
field, env = env[:i], env[i+1:]
}
if len(field) < 4 || field[:4] != "cpu." {
continue
}
i = strings.IndexByte(field, '=')
if i < 0 {
print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n")
continue
}
key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on"

var enable bool
switch value {
case "on":
enable = true
case "off":
enable = false
default:
print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n")
continue field
}

if key == "all" {
for i := range options {
options[i].Specified = true
options[i].Enable = enable || options[i].Required
}
continue field
}

for i := range options {
if options[i].Name == key {
options[i].Specified = true
options[i].Enable = enable
continue field
}
}

print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n")
}

for _, o := range options {
if !o.Specified {
continue
}

if o.Enable && !*o.Feature {
print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n")
continue
}

if !o.Enable && o.Required {
print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n")
continue
}

*o.Feature = o.Enable
}
}
4 changes: 1 addition & 3 deletions cpu/cpu_aix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@

package cpu

const cacheLineSize = 128

const (
// getsystemcfg constants
_SC_IMPL = 2
_IMPL_POWER8 = 0x10000
_IMPL_POWER9 = 0x20000
)

func init() {
func archInit() {
impl := getsystemcfg(_SC_IMPL)
if impl&_IMPL_POWER8 != 0 {
PPC64.IsPOWER8 = true
Expand Down
33 changes: 33 additions & 0 deletions cpu/cpu_arm.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,36 @@ const (
hwcap2_SHA2 = 1 << 3
hwcap2_CRC32 = 1 << 4
)

func initOptions() {
options = []option{
{Name: "pmull", Feature: &ARM.HasPMULL},
{Name: "sha1", Feature: &ARM.HasSHA1},
{Name: "sha2", Feature: &ARM.HasSHA2},
{Name: "swp", Feature: &ARM.HasSWP},
{Name: "thumb", Feature: &ARM.HasTHUMB},
{Name: "thumbee", Feature: &ARM.HasTHUMBEE},
{Name: "tls", Feature: &ARM.HasTLS},
{Name: "vfp", Feature: &ARM.HasVFP},
{Name: "vfpd32", Feature: &ARM.HasVFPD32},
{Name: "vfpv3", Feature: &ARM.HasVFPv3},
{Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16},
{Name: "vfpv4", Feature: &ARM.HasVFPv4},
{Name: "half", Feature: &ARM.HasHALF},
{Name: "26bit", Feature: &ARM.Has26BIT},
{Name: "fastmul", Feature: &ARM.HasFASTMUL},
{Name: "fpa", Feature: &ARM.HasFPA},
{Name: "edsp", Feature: &ARM.HasEDSP},
{Name: "java", Feature: &ARM.HasJAVA},
{Name: "iwmmxt", Feature: &ARM.HasIWMMXT},
{Name: "crunch", Feature: &ARM.HasCRUNCH},
{Name: "neon", Feature: &ARM.HasNEON},
{Name: "idivt", Feature: &ARM.HasIDIVT},
{Name: "idiva", Feature: &ARM.HasIDIVA},
{Name: "lpae", Feature: &ARM.HasLPAE},
{Name: "evtstrm", Feature: &ARM.HasEVTSTRM},
{Name: "aes", Feature: &ARM.HasAES},
{Name: "crc32", Feature: &ARM.HasCRC32},
}

}
31 changes: 30 additions & 1 deletion cpu/cpu_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,36 @@ import "runtime"

const cacheLineSize = 64

func init() {
func initOptions() {
options = []option{
{Name: "fp", Feature: &ARM64.HasFP},
{Name: "asimd", Feature: &ARM64.HasASIMD},
{Name: "evstrm", Feature: &ARM64.HasEVTSTRM},
{Name: "aes", Feature: &ARM64.HasAES},
{Name: "fphp", Feature: &ARM64.HasFPHP},
{Name: "jscvt", Feature: &ARM64.HasJSCVT},
{Name: "lrcpc", Feature: &ARM64.HasLRCPC},
{Name: "pmull", Feature: &ARM64.HasPMULL},
{Name: "sha1", Feature: &ARM64.HasSHA1},
{Name: "sha2", Feature: &ARM64.HasSHA2},
{Name: "sha3", Feature: &ARM64.HasSHA3},
{Name: "sha512", Feature: &ARM64.HasSHA512},
{Name: "sm3", Feature: &ARM64.HasSM3},
{Name: "sm4", Feature: &ARM64.HasSM4},
{Name: "sve", Feature: &ARM64.HasSVE},
{Name: "crc32", Feature: &ARM64.HasCRC32},
{Name: "atomics", Feature: &ARM64.HasATOMICS},
{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
{Name: "cpuid", Feature: &ARM64.HasCPUID},
{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM},
{Name: "fcma", Feature: &ARM64.HasFCMA},
{Name: "dcpop", Feature: &ARM64.HasDCPOP},
{Name: "asimddp", Feature: &ARM64.HasASIMDDP},
{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
}
}

func archInit() {
switch runtime.GOOS {
case "android", "darwin", "netbsd":
// Android and iOS don't seem to allow reading these registers.
Expand Down
2 changes: 1 addition & 1 deletion cpu/cpu_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

package cpu

func init() {
func archInit() {
if err := readHWCAP(); err != nil {
return
}
Expand Down
2 changes: 0 additions & 2 deletions cpu/cpu_linux_ppc64x.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

package cpu

const cacheLineSize = 128

// HWCAP/HWCAP2 bits. These are exposed by the kernel.
const (
// ISA Level
Expand Down
2 changes: 0 additions & 2 deletions cpu/cpu_linux_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

package cpu

const cacheLineSize = 256

const (
// bit mask values from /usr/include/bits/hwcap.h
hwcap_ZARCH = 2
Expand Down
6 changes: 6 additions & 0 deletions cpu/cpu_mips64x.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@
package cpu

const cacheLineSize = 32

func initOptions() {
options = []option{
{Name: "msa", Feature: &MIPS64X.HasMSA},
}
}
2 changes: 2 additions & 0 deletions cpu/cpu_mipsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
package cpu

const cacheLineSize = 32

func initOptions() {}
9 changes: 9 additions & 0 deletions cpu/cpu_other_arm.go
Original file line number Diff line number Diff line change
@@ -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 !linux,arm

package cpu

func archInit() {}
16 changes: 16 additions & 0 deletions cpu/cpu_ppc64x.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 ppc64 ppc64le

package cpu

const cacheLineSize = 128

func initOptions() {
options = []option{
{Name: "darn", Feature: &PPC64.HasDARN},
{Name: "scv", Feature: &PPC64.HasSCV},
}
}
2 changes: 2 additions & 0 deletions cpu/cpu_riscv64.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
package cpu

const cacheLineSize = 32

func initOptions() {}
30 changes: 30 additions & 0 deletions cpu/cpu_s390x.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 cacheLineSize = 256

func initOptions() {
options = []option{
{Name: "zarch", Feature: &S390X.HasZARCH},
{Name: "stfle", Feature: &S390X.HasSTFLE},
{Name: "ldisp", Feature: &S390X.HasLDISP},
{Name: "eimm", Feature: &S390X.HasEIMM},
{Name: "dfp", Feature: &S390X.HasDFP},
{Name: "etf3eh", Feature: &S390X.HasETF3EH},
{Name: "msa", Feature: &S390X.HasMSA},
{Name: "aes", Feature: &S390X.HasAES},
{Name: "aescbc", Feature: &S390X.HasAESCBC},
{Name: "aesctr", Feature: &S390X.HasAESCTR},
{Name: "aesgcm", Feature: &S390X.HasAESGCM},
{Name: "ghash", Feature: &S390X.HasGHASH},
{Name: "sha1", Feature: &S390X.HasSHA1},
{Name: "sha256", Feature: &S390X.HasSHA256},
{Name: "sha3", Feature: &S390X.HasSHA3},
{Name: "sha512", Feature: &S390X.HasSHA512},
{Name: "vx", Feature: &S390X.HasVX},
{Name: "vxe", Feature: &S390X.HasVXE},
}
}
4 changes: 4 additions & 0 deletions cpu/cpu_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ package cpu
// rules are good enough.

const cacheLineSize = 0

func initOptions() {}

func archInit() {}
31 changes: 30 additions & 1 deletion cpu/cpu_x86.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,37 @@

package cpu

import "runtime"

const cacheLineSize = 64

func init() {
func initOptions() {
options = []option{
{Name: "adx", Feature: &X86.HasADX},
{Name: "aes", Feature: &X86.HasAES},
{Name: "avx", Feature: &X86.HasAVX},
{Name: "avx2", Feature: &X86.HasAVX2},
{Name: "bmi1", Feature: &X86.HasBMI1},
{Name: "bmi2", Feature: &X86.HasBMI2},
{Name: "erms", Feature: &X86.HasERMS},
{Name: "fma", Feature: &X86.HasFMA},
{Name: "osxsave", Feature: &X86.HasOSXSAVE},
{Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ},
{Name: "popcnt", Feature: &X86.HasPOPCNT},
{Name: "rdrand", Feature: &X86.HasRDRAND},
{Name: "rdseed", Feature: &X86.HasRDSEED},
{Name: "sse3", Feature: &X86.HasSSE3},
{Name: "sse41", Feature: &X86.HasSSE41},
{Name: "sse42", Feature: &X86.HasSSE42},
{Name: "ssse3", Feature: &X86.HasSSSE3},

// These capabilities should always be enabled on amd64:
{Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"},
}
}

func archInit() {

Initialized = true

maxID, _, _, _ := cpuid(0, 0)
Expand Down Expand Up @@ -52,6 +80,7 @@ func init() {
X86.HasERMS = isSet(9, ebx7)
X86.HasRDSEED = isSet(18, ebx7)
X86.HasADX = isSet(19, ebx7)

}

func isSet(bitpos uint, value uint32) bool {
Expand Down

0 comments on commit 0cf7623

Please sign in to comment.