1
2
3
4
5 package buildcfg
6
7 import (
8 "fmt"
9 "reflect"
10 "strings"
11
12 "internal/goexperiment"
13 )
14
15
16
17 type ExperimentFlags struct {
18 goexperiment.Flags
19 baseline goexperiment.Flags
20 }
21
22
23
24
25
26
27
28
29
30
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
41
42 const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
43
44
45
46
47
48
49
50
51 var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
52
53
54
55
56
57
58 func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
59
60
61
62
63 var regabiSupported, regabiAlwaysOn bool
64 switch goarch {
65 case "amd64", "arm64", "loong64", "ppc64le", "ppc64", "riscv64":
66 regabiAlwaysOn = true
67 regabiSupported = true
68 }
69
70 haveThreads := goarch != "wasm"
71
72
73
74
75
76
77
78
79
80
81 dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix")
82
83 baseline := goexperiment.Flags{
84 RegabiWrappers: regabiSupported,
85 RegabiArgs: regabiSupported,
86 AliasTypeParams: true,
87 SwissMap: true,
88 SpinbitMutex: haveThreads,
89 SyncHashTrieMap: true,
90 Dwarf5: dwarf5Supported,
91 }
92
93
94 flags := &ExperimentFlags{
95 Flags: baseline,
96 baseline: baseline,
97 }
98
99
100
101
102 if goexp != "" {
103
104 names := make(map[string]func(bool))
105 rv := reflect.ValueOf(&flags.Flags).Elem()
106 rt := rv.Type()
107 for i := 0; i < rt.NumField(); i++ {
108 field := rv.Field(i)
109 names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
110 }
111
112
113
114
115
116 names["regabi"] = func(v bool) {
117 flags.RegabiWrappers = v
118 flags.RegabiArgs = v
119 }
120
121
122 for _, f := range strings.Split(goexp, ",") {
123 if f == "" {
124 continue
125 }
126 if f == "none" {
127
128
129
130 flags.Flags = goexperiment.Flags{}
131 continue
132 }
133 val := true
134 if strings.HasPrefix(f, "no") {
135 f, val = f[2:], false
136 }
137 set, ok := names[f]
138 if !ok {
139 return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
140 }
141 set(val)
142 }
143 }
144
145 if regabiAlwaysOn {
146 flags.RegabiWrappers = true
147 flags.RegabiArgs = true
148 }
149
150 if !regabiSupported {
151 flags.RegabiWrappers = false
152 flags.RegabiArgs = false
153 }
154
155 if flags.RegabiArgs && !flags.RegabiWrappers {
156 return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
157 }
158 return flags, nil
159 }
160
161
162
163 func (exp *ExperimentFlags) String() string {
164 return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
165 }
166
167
168
169
170
171 func expList(exp, base *goexperiment.Flags, all bool) []string {
172 var list []string
173 rv := reflect.ValueOf(exp).Elem()
174 var rBase reflect.Value
175 if base != nil {
176 rBase = reflect.ValueOf(base).Elem()
177 }
178 rt := rv.Type()
179 for i := 0; i < rt.NumField(); i++ {
180 name := strings.ToLower(rt.Field(i).Name)
181 val := rv.Field(i).Bool()
182 baseVal := false
183 if base != nil {
184 baseVal = rBase.Field(i).Bool()
185 }
186 if all || val != baseVal {
187 if val {
188 list = append(list, name)
189 } else {
190 list = append(list, "no"+name)
191 }
192 }
193 }
194 return list
195 }
196
197
198
199 func (exp *ExperimentFlags) Enabled() []string {
200 return expList(&exp.Flags, nil, false)
201 }
202
203
204
205 func (exp *ExperimentFlags) All() []string {
206 return expList(&exp.Flags, nil, true)
207 }
208
View as plain text