]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/buildcfg/exp.go
513070c8af7b57afbe2ea30fb27d625fbb278d6c
[gostls13.git] / src / internal / buildcfg / exp.go
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 // ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
16 // (platform-default) experiment configuration.
17 type ExperimentFlags struct {
18         goexperiment.Flags
19         baseline goexperiment.Flags
20 }
21
22 // Experiment contains the toolchain experiments enabled for the
23 // current build.
24 //
25 // (This is not necessarily the set of experiments the compiler itself
26 // was built with.)
27 //
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))
33         if err != nil {
34                 Error = err
35                 return ExperimentFlags{}
36         }
37         return *flags
38 }()
39
40 // DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
41 // It is not guaranteed to be canonical.
42 const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
43
44 // FramePointerEnabled enables the use of platform conventions for
45 // saving frame pointers.
46 //
47 // This used to be an experiment, but now it's always enabled on
48 // platforms that support it.
49 //
50 // Note: must agree with runtime.framepointer_enabled.
51 var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
52
53 // ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
54 // configuration tuple and returns the enabled and baseline experiment
55 // flag sets.
56 //
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
62         // always on.
63         var regabiSupported, regabiAlwaysOn bool
64         switch goarch {
65         case "amd64", "arm64", "ppc64le", "ppc64", "riscv64":
66                 regabiAlwaysOn = true
67                 regabiSupported = true
68         }
69
70         baseline := goexperiment.Flags{
71                 RegabiWrappers:   regabiSupported,
72                 RegabiArgs:       regabiSupported,
73                 CoverageRedesign: true,
74         }
75
76         // Start with the statically enabled set of experiments.
77         flags := &ExperimentFlags{
78                 Flags:    baseline,
79                 baseline: baseline,
80         }
81
82         // Pick up any changes to the baseline configuration from the
83         // GOEXPERIMENT environment. This can be set at make.bash time
84         // and overridden at build time.
85         if goexp != "" {
86                 // Create a map of known experiment names.
87                 names := make(map[string]func(bool))
88                 rv := reflect.ValueOf(&flags.Flags).Elem()
89                 rt := rv.Type()
90                 for i := 0; i < rt.NumField(); i++ {
91                         field := rv.Field(i)
92                         names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
93                 }
94
95                 // "regabi" is an alias for all working regabi
96                 // subexperiments, and not an experiment itself. Doing
97                 // this as an alias make both "regabi" and "noregabi"
98                 // do the right thing.
99                 names["regabi"] = func(v bool) {
100                         flags.RegabiWrappers = v
101                         flags.RegabiArgs = v
102                 }
103
104                 // Parse names.
105                 for _, f := range strings.Split(goexp, ",") {
106                         if f == "" {
107                                 continue
108                         }
109                         if f == "none" {
110                                 // GOEXPERIMENT=none disables all experiment flags.
111                                 // This is used by cmd/dist, which doesn't know how
112                                 // to build with any experiment flags.
113                                 flags.Flags = goexperiment.Flags{}
114                                 continue
115                         }
116                         val := true
117                         if strings.HasPrefix(f, "no") {
118                                 f, val = f[2:], false
119                         }
120                         set, ok := names[f]
121                         if !ok {
122                                 return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
123                         }
124                         set(val)
125                 }
126         }
127
128         if regabiAlwaysOn {
129                 flags.RegabiWrappers = true
130                 flags.RegabiArgs = true
131         }
132         // regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le.
133         if !regabiSupported {
134                 flags.RegabiWrappers = false
135                 flags.RegabiArgs = false
136         }
137         // Check regabi dependencies.
138         if flags.RegabiArgs && !flags.RegabiWrappers {
139                 return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
140         }
141         return flags, nil
142 }
143
144 // String returns the canonical GOEXPERIMENT string to enable this experiment
145 // configuration. (Experiments in the same state as in the baseline are elided.)
146 func (exp *ExperimentFlags) String() string {
147         return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
148 }
149
150 // expList returns the list of lower-cased experiment names for
151 // experiments that differ from base. base may be nil to indicate no
152 // experiments. If all is true, then include all experiment flags,
153 // regardless of base.
154 func expList(exp, base *goexperiment.Flags, all bool) []string {
155         var list []string
156         rv := reflect.ValueOf(exp).Elem()
157         var rBase reflect.Value
158         if base != nil {
159                 rBase = reflect.ValueOf(base).Elem()
160         }
161         rt := rv.Type()
162         for i := 0; i < rt.NumField(); i++ {
163                 name := strings.ToLower(rt.Field(i).Name)
164                 val := rv.Field(i).Bool()
165                 baseVal := false
166                 if base != nil {
167                         baseVal = rBase.Field(i).Bool()
168                 }
169                 if all || val != baseVal {
170                         if val {
171                                 list = append(list, name)
172                         } else {
173                                 list = append(list, "no"+name)
174                         }
175                 }
176         }
177         return list
178 }
179
180 // Enabled returns a list of enabled experiments, as
181 // lower-cased experiment names.
182 func (exp *ExperimentFlags) Enabled() []string {
183         return expList(&exp.Flags, nil, false)
184 }
185
186 // All returns a list of all experiment settings.
187 // Disabled experiments appear in the list prefixed by "no".
188 func (exp *ExperimentFlags) All() []string {
189         return expList(&exp.Flags, nil, true)
190 }