github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/internal/buildcfg/exp.go (about) 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 package buildcfg 6 7 import ( 8 "fmt" 9 "reflect" 10 "strings" 11 12 "internal/goexperiment" 13 ) 14 15 // Experiment contains the toolchain experiments enabled for the 16 // current build. 17 // 18 // (This is not necessarily the set of experiments the compiler itself 19 // was built with.) 20 // 21 // experimentBaseline specifies the experiment flags that are enabled by 22 // default in the current toolchain. This is, in effect, the "control" 23 // configuration and any variation from this is an experiment. 24 var Experiment, experimentBaseline = func() (goexperiment.Flags, goexperiment.Flags) { 25 flags, baseline, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) 26 if err != nil { 27 Error = err 28 } 29 return flags, baseline 30 }() 31 32 const DefaultGOEXPERIMENT = defaultGOEXPERIMENT 33 34 // FramePointerEnabled enables the use of platform conventions for 35 // saving frame pointers. 36 // 37 // This used to be an experiment, but now it's always enabled on 38 // platforms that support it. 39 // 40 // Note: must agree with runtime.framepointer_enabled. 41 var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64" 42 43 // ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT) 44 // configuration tuple and returns the enabled and baseline experiment 45 // flag sets. 46 // 47 // TODO(mdempsky): Move to internal/goexperiment. 48 func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment.Flags, err error) { 49 regabiSupported := false 50 switch goarch { 51 case "amd64", "arm64", "ppc64le", "ppc64": 52 regabiSupported = true 53 } 54 55 baseline = goexperiment.Flags{ 56 RegabiWrappers: regabiSupported, 57 RegabiReflect: regabiSupported, 58 RegabiArgs: regabiSupported, 59 PacerRedesign: true, 60 } 61 62 // Start with the statically enabled set of experiments. 63 flags = baseline 64 65 // Pick up any changes to the baseline configuration from the 66 // GOEXPERIMENT environment. This can be set at make.bash time 67 // and overridden at build time. 68 if goexp != "" { 69 // Create a map of known experiment names. 70 names := make(map[string]func(bool)) 71 rv := reflect.ValueOf(&flags).Elem() 72 rt := rv.Type() 73 for i := 0; i < rt.NumField(); i++ { 74 field := rv.Field(i) 75 names[strings.ToLower(rt.Field(i).Name)] = field.SetBool 76 } 77 78 // "regabi" is an alias for all working regabi 79 // subexperiments, and not an experiment itself. Doing 80 // this as an alias make both "regabi" and "noregabi" 81 // do the right thing. 82 names["regabi"] = func(v bool) { 83 flags.RegabiWrappers = v 84 flags.RegabiReflect = v 85 flags.RegabiArgs = v 86 } 87 88 // Parse names. 89 for _, f := range strings.Split(goexp, ",") { 90 if f == "" { 91 continue 92 } 93 if f == "none" { 94 // GOEXPERIMENT=none disables all experiment flags. 95 // This is used by cmd/dist, which doesn't know how 96 // to build with any experiment flags. 97 flags = goexperiment.Flags{} 98 continue 99 } 100 val := true 101 if strings.HasPrefix(f, "no") { 102 f, val = f[2:], false 103 } 104 set, ok := names[f] 105 if !ok { 106 err = fmt.Errorf("unknown GOEXPERIMENT %s", f) 107 return 108 } 109 set(val) 110 } 111 } 112 113 // regabi is always enabled on amd64. 114 if goarch == "amd64" { 115 flags.RegabiWrappers = true 116 flags.RegabiReflect = true 117 flags.RegabiArgs = true 118 } 119 // regabi is only supported on amd64, arm64, ppc64 and ppc64le. 120 if !regabiSupported { 121 flags.RegabiReflect = false 122 flags.RegabiArgs = false 123 } 124 // Check regabi dependencies. 125 if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiReflect) { 126 err = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabireflect") 127 } 128 return 129 } 130 131 // expList returns the list of lower-cased experiment names for 132 // experiments that differ from base. base may be nil to indicate no 133 // experiments. If all is true, then include all experiment flags, 134 // regardless of base. 135 func expList(exp, base *goexperiment.Flags, all bool) []string { 136 var list []string 137 rv := reflect.ValueOf(exp).Elem() 138 var rBase reflect.Value 139 if base != nil { 140 rBase = reflect.ValueOf(base).Elem() 141 } 142 rt := rv.Type() 143 for i := 0; i < rt.NumField(); i++ { 144 name := strings.ToLower(rt.Field(i).Name) 145 val := rv.Field(i).Bool() 146 baseVal := false 147 if base != nil { 148 baseVal = rBase.Field(i).Bool() 149 } 150 if all || val != baseVal { 151 if val { 152 list = append(list, name) 153 } else { 154 list = append(list, "no"+name) 155 } 156 } 157 } 158 return list 159 } 160 161 // GOEXPERIMENT is a comma-separated list of enabled or disabled 162 // experiments that differ from the baseline experiment configuration. 163 // GOEXPERIMENT is exactly what a user would set on the command line 164 // to get the set of enabled experiments. 165 func GOEXPERIMENT() string { 166 goexp := strings.Join(expList(&Experiment, &experimentBaseline, false), ",") 167 if goexp == "" && DefaultGOEXPERIMENT != "" { 168 goexp = "," // non-empty to override DefaultGOEXPERIMENT 169 } 170 return goexp 171 } 172 173 // EnabledExperiments returns a list of enabled experiments, as 174 // lower-cased experiment names. 175 func EnabledExperiments() []string { 176 return expList(&Experiment, nil, false) 177 } 178 179 // AllExperiments returns a list of all experiment settings. 180 // Disabled experiments appear in the list prefixed by "no". 181 func AllExperiments() []string { 182 return expList(&Experiment, nil, true) 183 } 184 185 // UpdateExperiments updates the Experiment global based on a new GOARCH value. 186 // This is only required for cmd/go, which can change GOARCH after 187 // program startup due to use of "go env -w". 188 func UpdateExperiments(goos, goarch, goexperiment string) { 189 var err error 190 Experiment, experimentBaseline, err = ParseGOEXPERIMENT(goos, goarch, goexperiment) 191 if err != nil { 192 Error = err 193 } 194 }