Commit 3e0227f6 authored by Martin Möhrmann's avatar Martin Möhrmann

internal/cpu: add invalid option warnings and support to enable cpu features

This CL adds the ability to enable the cpu feature FEATURE by specifying
FEATURE=on in GODEBUGCPU. Syntax support to enable cpu features is useful
in combination with a preceeding all=off to disable all but some specific
cpu features. Example:

GODEBUGCPU=all=off,sse3=on

This CL implements printing of warnings for invalid GODEBUGCPU settings:
- requests enabling features that are not supported with the current CPU
- specifying values different than 'on' or 'off' for a feature
- settings for unkown cpu feature names

Updates #27218

Change-Id: Ic13e5c4c35426a390c50eaa4bd2a408ef2ee21be
Reviewed-on: https://go-review.googlesource.com/c/141800
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent f81d73e8
...@@ -153,16 +153,18 @@ var options []option ...@@ -153,16 +153,18 @@ var options []option
// Option names should be lower case. e.g. avx instead of AVX. // Option names should be lower case. e.g. avx instead of AVX.
type option struct { type option struct {
Name string Name string
Feature *bool Feature *bool
Specified bool // Stores if feature value was specified in GODEBUGCPU.
Enable bool // Stores if feature should be enabled.
} }
// processOptions disables CPU feature values based on the parsed env string. // processOptions enables or disables CPU feature values based on the parsed env string.
// The env string is expected to be of the form feature1=off,feature2=off... // The env string is expected to be of the form feature1=value1,feature2=value2...
// where feature names is one of the architecture specifc list stored in the // where feature names is one of the architecture specifc list stored in the
// cpu packages options variable. If env contains all=off then all capabilities // cpu packages options variable and values are either 'on' or 'off'.
// referenced through the options variable are disabled. Other feature // If env contains all=off then all cpu features referenced through the options
// names and values other than 'off' are silently ignored. // variable are disabled. Other feature names and values result in warning messages.
func processOptions(env string) { func processOptions(env string) {
field: field:
for env != "" { for env != "" {
...@@ -175,26 +177,52 @@ field: ...@@ -175,26 +177,52 @@ field:
} }
i = indexByte(field, '=') i = indexByte(field, '=')
if i < 0 { if i < 0 {
print("GODEBUGCPU: no value specified for \"", field, "\"\n")
continue continue
} }
key, value := field[:i], field[i+1:] key, value := field[:i], field[i+1:]
// Only allow turning off CPU features by specifying 'off'. var enable bool
if value == "off" { switch value {
if key == "all" { case "on":
for _, v := range options { enable = true
*v.Feature = false case "off":
} enable = false
return default:
} else { print("GODEBUGCPU: value \"", value, "\" not supported for option ", key, "\n")
for _, v := range options { continue field
if v.Name == key { }
*v.Feature = false
continue field if key == "all" {
} for i := range options {
} options[i].Specified = true
options[i].Enable = enable
} }
continue field
}
for i := range options {
if options[i].Name == key {
options[i].Specified = true
options[i].Enable = enable
continue field
}
}
print("GODEBUGCPU: unknown cpu feature \"", key, "\"\n")
}
for _, o := range options {
if !o.Specified {
continue
} }
if o.Enable && !*o.Feature {
print("GODEBUGCPU: can not enable \"", o.Name, "\", missing hardware support\n")
continue
}
*o.Feature = o.Enable
} }
} }
......
...@@ -21,8 +21,8 @@ const ( ...@@ -21,8 +21,8 @@ const (
func doinit() { func doinit() {
options = []option{ options = []option{
{"vfpv4", &ARM.HasVFPv4}, {Name: "vfpv4", Feature: &ARM.HasVFPv4},
{"idiva", &ARM.HasIDIVA}, {Name: "idiva", Feature: &ARM.HasIDIVA},
} }
// HWCAP feature bits // HWCAP feature bits
......
...@@ -42,32 +42,32 @@ const ( ...@@ -42,32 +42,32 @@ const (
func doinit() { func doinit() {
options = []option{ options = []option{
{"evtstrm", &ARM64.HasEVTSTRM}, {Name: "evtstrm", Feature: &ARM64.HasEVTSTRM},
{"aes", &ARM64.HasAES}, {Name: "aes", Feature: &ARM64.HasAES},
{"pmull", &ARM64.HasPMULL}, {Name: "pmull", Feature: &ARM64.HasPMULL},
{"sha1", &ARM64.HasSHA1}, {Name: "sha1", Feature: &ARM64.HasSHA1},
{"sha2", &ARM64.HasSHA2}, {Name: "sha2", Feature: &ARM64.HasSHA2},
{"crc32", &ARM64.HasCRC32}, {Name: "crc32", Feature: &ARM64.HasCRC32},
{"atomics", &ARM64.HasATOMICS}, {Name: "atomics", Feature: &ARM64.HasATOMICS},
{"fphp", &ARM64.HasFPHP}, {Name: "fphp", Feature: &ARM64.HasFPHP},
{"asimdhp", &ARM64.HasASIMDHP}, {Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
{"cpuid", &ARM64.HasCPUID}, {Name: "cpuid", Feature: &ARM64.HasCPUID},
{"asimdrdm", &ARM64.HasASIMDRDM}, {Name: "asimdrdm", Feature: &ARM64.HasASIMDRDM},
{"jscvt", &ARM64.HasJSCVT}, {Name: "jscvt", Feature: &ARM64.HasJSCVT},
{"fcma", &ARM64.HasFCMA}, {Name: "fcma", Feature: &ARM64.HasFCMA},
{"lrcpc", &ARM64.HasLRCPC}, {Name: "lrcpc", Feature: &ARM64.HasLRCPC},
{"dcpop", &ARM64.HasDCPOP}, {Name: "dcpop", Feature: &ARM64.HasDCPOP},
{"sha3", &ARM64.HasSHA3}, {Name: "sha3", Feature: &ARM64.HasSHA3},
{"sm3", &ARM64.HasSM3}, {Name: "sm3", Feature: &ARM64.HasSM3},
{"sm4", &ARM64.HasSM4}, {Name: "sm4", Feature: &ARM64.HasSM4},
{"asimddp", &ARM64.HasASIMDDP}, {Name: "asimddp", Feature: &ARM64.HasASIMDDP},
{"sha512", &ARM64.HasSHA512}, {Name: "sha512", Feature: &ARM64.HasSHA512},
{"sve", &ARM64.HasSVE}, {Name: "sve", Feature: &ARM64.HasSVE},
{"asimdfhm", &ARM64.HasASIMDFHM}, {Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
// These capabilities should always be enabled on arm64: // These capabilities should always be enabled on arm64:
// {"fp", &ARM64.HasFP}, // {Name: "fp", Feature: &ARM64.HasFP},
// {"asimd", &ARM64.HasASIMD}, // {Name: "asimd", Feature: &ARM64.HasASIMD},
} }
// HWCAP feature bits // HWCAP feature bits
......
...@@ -34,17 +34,17 @@ const ( ...@@ -34,17 +34,17 @@ const (
func doinit() { func doinit() {
options = []option{ options = []option{
{"htm", &PPC64.HasHTM}, {Name: "htm", Feature: &PPC64.HasHTM},
{"htmnosc", &PPC64.HasHTMNOSC}, {Name: "htmnosc", Feature: &PPC64.HasHTMNOSC},
{"darn", &PPC64.HasDARN}, {Name: "darn", Feature: &PPC64.HasDARN},
{"scv", &PPC64.HasSCV}, {Name: "scv", Feature: &PPC64.HasSCV},
// These capabilities should always be enabled on ppc64 and ppc64le: // These capabilities should always be enabled on ppc64 and ppc64le:
// {"vmx", &PPC64.HasVMX}, // {Name: "vmx", Feature: &PPC64.HasVMX},
// {"dfp", &PPC64.HasDFP}, // {Name: "dfp", Feature: &PPC64.HasDFP},
// {"vsx", &PPC64.HasVSX}, // {Name: "vsx", Feature: &PPC64.HasVSX},
// {"isel", &PPC64.HasISEL}, // {Name: "isel", Feature: &PPC64.HasISEL},
// {"vcrypto", &PPC64.HasVCRYPTO}, // {Name: "vcrypto", Feature: &PPC64.HasVCRYPTO},
} }
// HWCAP feature bits // HWCAP feature bits
......
...@@ -107,14 +107,14 @@ func klmdQuery() queryResult ...@@ -107,14 +107,14 @@ func klmdQuery() queryResult
func doinit() { func doinit() {
options = []option{ options = []option{
{"zarch", &S390X.HasZArch}, {Name: "zarch", Feature: &S390X.HasZArch},
{"stfle", &S390X.HasSTFLE}, {Name: "stfle", Feature: &S390X.HasSTFLE},
{"ldisp", &S390X.HasLDisp}, {Name: "ldisp", Feature: &S390X.HasLDisp},
{"msa", &S390X.HasMSA}, {Name: "msa", Feature: &S390X.HasMSA},
{"eimm", &S390X.HasEImm}, {Name: "eimm", Feature: &S390X.HasEImm},
{"dfp", &S390X.HasDFP}, {Name: "dfp", Feature: &S390X.HasDFP},
{"etf3eh", &S390X.HasETF3Enhanced}, {Name: "etf3eh", Feature: &S390X.HasETF3Enhanced},
{"vx", &S390X.HasVX}, {Name: "vx", Feature: &S390X.HasVX},
} }
aes := []function{aes128, aes192, aes256} aes := []function{aes128, aes192, aes256}
......
...@@ -30,7 +30,10 @@ func runDebugOptionsTest(t *testing.T, test string, options string) { ...@@ -30,7 +30,10 @@ func runDebugOptionsTest(t *testing.T, test string, options string) {
cmd.Env = append(cmd.Env, env) cmd.Env = append(cmd.Env, env)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
got := strings.TrimSpace(string(output)) lines := strings.Fields(string(output))
lastline := lines[len(lines)-1]
got := strings.TrimSpace(lastline)
want := "PASS" want := "PASS"
if err != nil || got != want { if err != nil || got != want {
t.Fatalf("%s with %s: want %s, got %v", test, env, want, got) t.Fatalf("%s with %s: want %s, got %v", test, env, want, got)
......
...@@ -40,23 +40,23 @@ const ( ...@@ -40,23 +40,23 @@ const (
func doinit() { func doinit() {
options = []option{ options = []option{
{"adx", &X86.HasADX}, {Name: "adx", Feature: &X86.HasADX},
{"aes", &X86.HasAES}, {Name: "aes", Feature: &X86.HasAES},
{"avx", &X86.HasAVX}, {Name: "avx", Feature: &X86.HasAVX},
{"avx2", &X86.HasAVX2}, {Name: "avx2", Feature: &X86.HasAVX2},
{"bmi1", &X86.HasBMI1}, {Name: "bmi1", Feature: &X86.HasBMI1},
{"bmi2", &X86.HasBMI2}, {Name: "bmi2", Feature: &X86.HasBMI2},
{"erms", &X86.HasERMS}, {Name: "erms", Feature: &X86.HasERMS},
{"fma", &X86.HasFMA}, {Name: "fma", Feature: &X86.HasFMA},
{"pclmulqdq", &X86.HasPCLMULQDQ}, {Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ},
{"popcnt", &X86.HasPOPCNT}, {Name: "popcnt", Feature: &X86.HasPOPCNT},
{"sse3", &X86.HasSSE3}, {Name: "sse3", Feature: &X86.HasSSE3},
{"sse41", &X86.HasSSE41}, {Name: "sse41", Feature: &X86.HasSSE41},
{"sse42", &X86.HasSSE42}, {Name: "sse42", Feature: &X86.HasSSE42},
{"ssse3", &X86.HasSSSE3}, {Name: "ssse3", Feature: &X86.HasSSSE3},
// sse2 set as last element so it can easily be removed again. See code below. // sse2 set as last element so it can easily be removed again. See code below.
{"sse2", &X86.HasSSE2}, {Name: "sse2", Feature: &X86.HasSSE2},
} }
// Remove sse2 from options on amd64(p32) because SSE2 is a mandatory feature for these GOARCHs. // Remove sse2 from options on amd64(p32) because SSE2 is a mandatory feature for these GOARCHs.
......
...@@ -45,3 +45,20 @@ func TestSSE2DebugOption(t *testing.T) { ...@@ -45,3 +45,20 @@ func TestSSE2DebugOption(t *testing.T) {
t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got) t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got)
} }
} }
func TestDisableSSE3(t *testing.T) {
runDebugOptionsTest(t, "TestSSE3DebugOption", "sse3=off")
}
func TestSSE3DebugOption(t *testing.T) {
MustHaveDebugOptionsSupport(t)
if os.Getenv("GODEBUGCPU") != "sse3=off" {
t.Skipf("skipping test: GODEBUGCPU=sse3=off not set")
}
want := false
if got := X86.HasSSE3; got != want {
t.Errorf("X86.HasSSE3 expected %v, got %v", want, got)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment