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.
12 "internal/goexperiment"
15 // ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
16 // (platform-default) experiment configuration.
17 type ExperimentFlags struct {
19 baseline goexperiment.Flags
22 // Experiment contains the toolchain experiments enabled for the
25 // (This is not necessarily the set of experiments the compiler itself
28 // experimentBaseline specifies the experiment flags that are enabled by
29 // default in the current toolchain. This is, in effect, the "control"
30 // configuration and any variation from this is an experiment.
31 var Experiment ExperimentFlags = func() ExperimentFlags {
32 flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
35 return ExperimentFlags{}
40 // DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
41 // It is not guaranteed to be canonical.
42 const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
44 // FramePointerEnabled enables the use of platform conventions for
45 // saving frame pointers.
47 // This used to be an experiment, but now it's always enabled on
48 // platforms that support it.
50 // Note: must agree with runtime.framepointer_enabled.
51 var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
53 // ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
54 // configuration tuple and returns the enabled and baseline experiment
57 // TODO(mdempsky): Move to internal/goexperiment.
58 func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
59 // regabiSupported is set to true on platforms where register ABI is
60 // supported and enabled by default.
61 // regabiAlwaysOn is set to true on platforms where register ABI is
63 var regabiSupported, regabiAlwaysOn bool
65 case "amd64", "arm64", "ppc64le", "ppc64", "riscv64":
67 regabiSupported = true
70 baseline := goexperiment.Flags{
71 RegabiWrappers: regabiSupported,
72 RegabiArgs: regabiSupported,
73 CoverageRedesign: true,
77 // Start with the statically enabled set of experiments.
78 flags := &ExperimentFlags{
83 // Pick up any changes to the baseline configuration from the
84 // GOEXPERIMENT environment. This can be set at make.bash time
85 // and overridden at build time.
87 // Create a map of known experiment names.
88 names := make(map[string]func(bool))
89 rv := reflect.ValueOf(&flags.Flags).Elem()
91 for i := 0; i < rt.NumField(); i++ {
93 names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
96 // "regabi" is an alias for all working regabi
97 // subexperiments, and not an experiment itself. Doing
98 // this as an alias make both "regabi" and "noregabi"
99 // do the right thing.
100 names["regabi"] = func(v bool) {
101 flags.RegabiWrappers = v
106 for _, f := range strings.Split(goexp, ",") {
111 // GOEXPERIMENT=none disables all experiment flags.
112 // This is used by cmd/dist, which doesn't know how
113 // to build with any experiment flags.
114 flags.Flags = goexperiment.Flags{}
118 if strings.HasPrefix(f, "no") {
119 f, val = f[2:], false
123 return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
130 flags.RegabiWrappers = true
131 flags.RegabiArgs = true
133 // regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le.
134 if !regabiSupported {
135 flags.RegabiWrappers = false
136 flags.RegabiArgs = false
138 // Check regabi dependencies.
139 if flags.RegabiArgs && !flags.RegabiWrappers {
140 return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
145 // String returns the canonical GOEXPERIMENT string to enable this experiment
146 // configuration. (Experiments in the same state as in the baseline are elided.)
147 func (exp *ExperimentFlags) String() string {
148 return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
151 // expList returns the list of lower-cased experiment names for
152 // experiments that differ from base. base may be nil to indicate no
153 // experiments. If all is true, then include all experiment flags,
154 // regardless of base.
155 func expList(exp, base *goexperiment.Flags, all bool) []string {
157 rv := reflect.ValueOf(exp).Elem()
158 var rBase reflect.Value
160 rBase = reflect.ValueOf(base).Elem()
163 for i := 0; i < rt.NumField(); i++ {
164 name := strings.ToLower(rt.Field(i).Name)
165 val := rv.Field(i).Bool()
168 baseVal = rBase.Field(i).Bool()
170 if all || val != baseVal {
172 list = append(list, name)
174 list = append(list, "no"+name)
181 // Enabled returns a list of enabled experiments, as
182 // lower-cased experiment names.
183 func (exp *ExperimentFlags) Enabled() []string {
184 return expList(&exp.Flags, nil, false)
187 // All returns a list of all experiment settings.
188 // Disabled experiments appear in the list prefixed by "no".
189 func (exp *ExperimentFlags) All() []string {
190 return expList(&exp.Flags, nil, true)