func predefine(defines flags.MultiFlag) map[string]*Macro {
macros := make(map[string]*Macro)
- // Set macros for various GOEXPERIMENTs so we can easily
- // switch runtime assembly code based on them.
+ // Set macros for GOEXPERIMENTs so we can easily switch
+ // runtime assembly code based on them.
if *flags.CompilingRuntime {
- set := func(name string) {
+ for _, exp := range objabi.EnabledExperiments() {
+ // Define macro.
+ name := "GOEXPERIMENT_" + exp
macros[name] = &Macro{
name: name,
args: nil,
tokens: Tokenize("1"),
}
}
- if objabi.Experiment.RegabiWrappers {
- set("GOEXPERIMENT_regabiwrappers")
- }
- if objabi.Experiment.RegabiG {
- set("GOEXPERIMENT_regabig")
- }
- if objabi.Experiment.RegabiReflect {
- set("GOEXPERIMENT_regabireflect")
- }
- if objabi.Experiment.RegabiDefer {
- set("GOEXPERIMENT_regabidefer")
- }
- if objabi.Experiment.RegabiArgs {
- set("GOEXPERIMENT_regabiargs")
- }
}
for _, name := range defines {
"debug/macho",
"debug/pe",
"go/constant",
+ "internal/goexperiment",
"internal/goversion",
"internal/race",
"internal/unsafeheader",
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
- if objabi.GOEXPERIMENT != "" {
- fmt.Fprintf(h, "GOEXPERIMENT=%q\n", objabi.GOEXPERIMENT)
+ if goexperiment := objabi.GOEXPERIMENT(); goexperiment != "" {
+ fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment)
}
// TODO(rsc): Convince compiler team not to add more magic environment variables,
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
- if objabi.GOEXPERIMENT != "" {
- fmt.Fprintf(h, "GOEXPERIMENT=%q\n", objabi.GOEXPERIMENT)
+ if goexperiment := objabi.GOEXPERIMENT(); goexperiment != "" {
+ fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment)
}
// The linker writes source file paths that say GOROOT_FINAL, but
"os"
"path/filepath"
"runtime"
- "strings"
)
func BuildInit() {
// used for compiling alternative files for the experiment. This allows
// changes for the experiment, like extra struct fields in the runtime,
// without affecting the base non-experiment code at all.
- for _, expt := range strings.Split(objabi.GOEXPERIMENT, ",") {
+ for _, expt := range objabi.EnabledExperiments() {
cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, "goexperiment."+expt)
}
}
--- /dev/null
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package objabi
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "strings"
+
+ "internal/goexperiment"
+)
+
+// Experiment contains the toolchain experiments enabled for the
+// current build.
+//
+// (This is not necessarily the set of experiments the compiler itself
+// was built with.)
+var Experiment goexperiment.Flags
+
+var defaultExpstring string // Set by package init
+
+// FramePointerEnabled enables the use of platform conventions for
+// saving frame pointers.
+//
+// This used to be an experiment, but now it's always enabled on
+// platforms that support it.
+//
+// Note: must agree with runtime.framepointer_enabled.
+var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
+
+func init() {
+ // Capture "default" experiments.
+ defaultExpstring = Expstring()
+
+ goexperiment := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)
+
+ // GOEXPERIMENT=none overrides all experiments enabled at dist time.
+ if goexperiment != "none" {
+ // Create a map of known experiment names.
+ names := make(map[string]reflect.Value)
+ rv := reflect.ValueOf(&Experiment).Elem()
+ rt := rv.Type()
+ for i := 0; i < rt.NumField(); i++ {
+ field := rv.Field(i)
+ names[strings.ToLower(rt.Field(i).Name)] = field
+ }
+
+ // Parse names.
+ for _, f := range strings.Split(goexperiment, ",") {
+ if f == "" {
+ continue
+ }
+ val := true
+ if strings.HasPrefix(f, "no") {
+ f, val = f[2:], false
+ }
+ field, ok := names[f]
+ if !ok {
+ fmt.Printf("unknown experiment %s\n", f)
+ os.Exit(2)
+ }
+ field.SetBool(val)
+ }
+ }
+
+ // regabi is only supported on amd64.
+ if GOARCH != "amd64" {
+ Experiment.Regabi = false
+ Experiment.RegabiWrappers = false
+ Experiment.RegabiG = false
+ Experiment.RegabiReflect = false
+ Experiment.RegabiDefer = false
+ Experiment.RegabiArgs = false
+ }
+ // Setting regabi sets working sub-experiments.
+ if Experiment.Regabi {
+ Experiment.RegabiWrappers = true
+ Experiment.RegabiG = true
+ Experiment.RegabiReflect = true
+ Experiment.RegabiDefer = true
+ // Not ready yet:
+ //Experiment.RegabiArgs = true
+ }
+ // Check regabi dependencies.
+ if Experiment.RegabiG && !Experiment.RegabiWrappers {
+ panic("GOEXPERIMENT regabig requires regabiwrappers")
+ }
+ if Experiment.RegabiArgs && !(Experiment.RegabiWrappers && Experiment.RegabiG && Experiment.RegabiReflect && Experiment.RegabiDefer) {
+ panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
+ }
+}
+
+// expList returns the list of enabled GOEXPERIMENTs names.
+func expList(flags *goexperiment.Flags) []string {
+ var list []string
+ rv := reflect.ValueOf(&Experiment).Elem()
+ rt := rv.Type()
+ for i := 0; i < rt.NumField(); i++ {
+ val := rv.Field(i).Bool()
+ if val {
+ field := rt.Field(i)
+ list = append(list, strings.ToLower(field.Name))
+ }
+ }
+ return list
+}
+
+// Expstring returns the GOEXPERIMENT string that should appear in Go
+// version signatures. This always starts with "X:".
+func Expstring() string {
+ list := expList(&Experiment)
+ if len(list) == 0 {
+ return "X:none"
+ }
+ return "X:" + strings.Join(list, ",")
+}
+
+// GOEXPERIMENT returns a comma-separated list of enabled experiments.
+// This is derived from the GOEXPERIMENT environment variable if set,
+// or the value of GOEXPERIMENT when make.bash was run if not.
+func GOEXPERIMENT() string {
+ return strings.Join(expList(&Experiment), ",")
+}
+
+// EnabledExperiments returns a list of enabled experiments, as
+// lower-cased experiment names.
+func EnabledExperiments() []string {
+ return expList(&Experiment)
+}
package objabi
import (
- "fmt"
"log"
"os"
"strings"
GOWASM = gowasm()
GO_LDSO = defaultGO_LDSO
Version = version
-
- // GOEXPERIMENT is a comma-separated list of enabled
- // experiments. This is derived from the GOEXPERIMENT
- // environment variable if set, or the value of GOEXPERIMENT
- // when make.bash was run if not.
- GOEXPERIMENT string // Set by package init
)
const (
func Getgoextlinkenabled() string {
return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
}
-
-func init() {
- // Capture "default" experiments.
- defaultExpstring = Expstring()
-
- goexperiment := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)
-
- // GOEXPERIMENT=none overrides all experiments enabled at dist time.
- if goexperiment != "none" {
- for _, f := range strings.Split(goexperiment, ",") {
- if f != "" {
- addexp(f)
- }
- }
- }
-
- // regabi is only supported on amd64.
- if GOARCH != "amd64" {
- Experiment.regabi = false
- Experiment.RegabiWrappers = false
- Experiment.RegabiG = false
- Experiment.RegabiReflect = false
- Experiment.RegabiDefer = false
- Experiment.RegabiArgs = false
- }
- // Setting regabi sets working sub-experiments.
- if Experiment.regabi {
- Experiment.RegabiWrappers = true
- Experiment.RegabiG = true
- Experiment.RegabiReflect = true
- Experiment.RegabiDefer = true
- // Not ready yet:
- //Experiment.RegabiArgs = true
- }
- // Check regabi dependencies.
- if Experiment.RegabiG && !Experiment.RegabiWrappers {
- panic("GOEXPERIMENT regabig requires regabiwrappers")
- }
- if Experiment.RegabiArgs && !(Experiment.RegabiWrappers && Experiment.RegabiG && Experiment.RegabiReflect && Experiment.RegabiDefer) {
- panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
- }
-
- // Set GOEXPERIMENT to the parsed and canonicalized set of experiments.
- GOEXPERIMENT = expList()
-}
-
-// FramePointerEnabled enables the use of platform conventions for
-// saving frame pointers.
-//
-// This used to be an experiment, but now it's always enabled on
-// platforms that support it.
-//
-// Note: must agree with runtime.framepointer_enabled.
-var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
-
-func addexp(s string) {
- // We could do general integer parsing here, but there's no need yet.
- v, vb := 1, true
- name := s
- if len(name) > 2 && name[:2] == "no" {
- v, vb = 0, false
- name = name[2:]
- }
- for i := 0; i < len(exper); i++ {
- if exper[i].name == name {
- switch val := exper[i].val.(type) {
- case *int:
- *val = v
- case *bool:
- *val = vb
- default:
- panic("bad GOEXPERIMENT type for " + s)
- }
- return
- }
- }
-
- fmt.Printf("unknown experiment %s\n", s)
- os.Exit(2)
-}
-
-// Experiment contains flags for GOEXPERIMENTs.
-var Experiment = ExpFlags{}
-
-type ExpFlags struct {
- FieldTrack bool
- PreemptibleLoops bool
- StaticLockRanking bool
-
- // regabi is split into several sub-experiments that can be
- // enabled individually. GOEXPERIMENT=regabi implies the
- // subset that are currently "working". Not all combinations work.
- regabi bool
- // RegabiWrappers enables ABI wrappers for calling between
- // ABI0 and ABIInternal functions. Without this, the ABIs are
- // assumed to be identical so cross-ABI calls are direct.
- RegabiWrappers bool
- // RegabiG enables dedicated G and zero registers in
- // ABIInternal.
- //
- // Requires wrappers because it makes the ABIs incompatible.
- RegabiG bool
- // RegabiReflect enables the register-passing paths in
- // reflection calls. This is also gated by intArgRegs in
- // reflect and runtime (which are disabled by default) so it
- // can be used in targeted tests.
- RegabiReflect bool
- // RegabiDefer enables desugaring defer and go calls
- // into argument-less closures.
- RegabiDefer bool
- // RegabiArgs enables register arguments/results in all
- // compiled Go functions.
- //
- // Requires wrappers (to do ABI translation), g (because
- // runtime assembly that's been ported to ABIInternal uses the
- // G register), reflect (so reflection calls use registers),
- // and defer (because the runtime doesn't support passing
- // register arguments to defer/go).
- RegabiArgs bool
-}
-
-// Toolchain experiments.
-// These are controlled by the GOEXPERIMENT environment
-// variable recorded when the toolchain is built.
-var exper = []struct {
- name string
- val interface{} // Must be *int or *bool
-}{
- {"fieldtrack", &Experiment.FieldTrack},
- {"preemptibleloops", &Experiment.PreemptibleLoops},
- {"staticlockranking", &Experiment.StaticLockRanking},
- {"regabi", &Experiment.regabi},
- {"regabiwrappers", &Experiment.RegabiWrappers},
- {"regabig", &Experiment.RegabiG},
- {"regabireflect", &Experiment.RegabiReflect},
- {"regabidefer", &Experiment.RegabiDefer},
- {"regabiargs", &Experiment.RegabiArgs},
-}
-
-var defaultExpstring string
-
-// expList returns the list of enabled GOEXPERIMENTS as a
-// commas-separated list.
-func expList() string {
- buf := ""
- for i := range exper {
- switch val := exper[i].val.(type) {
- case *int:
- if *val != 0 {
- buf += "," + exper[i].name
- }
- case *bool:
- if *val {
- buf += "," + exper[i].name
- }
- }
- }
- if len(buf) == 0 {
- return ""
- }
- return buf[1:]
-}
-
-// Expstring returns the GOEXPERIMENT string that should appear in Go
-// version signatures. This always starts with "X:".
-func Expstring() string {
- list := expList()
- if list == "" {
- return "X:none"
- }
- return "X:" + list
-}
--- /dev/null
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package goexperiment implements support for toolchain experiments.
+//
+// Toolchain experiments are controlled by the GOEXPERIMENT
+// environment variable. GOEXPERIMENT is a comma-separated list of
+// experiment names. GOEXPERIMENT can be set at make.bash time, which
+// sets the default experiments for binaries built with the tool
+// chain; or it can be set at build time. GOEXPERIMENT can also be set
+// to "none", which disables any experiments that were enabled at
+// make.bash time.
+//
+// Experiments are exposed to the build in the following ways:
+//
+// - Build tag goexperiment.x is set if experiment x (lower case) is
+// enabled.
+//
+// - In runtime assembly, the macro GOEXPERIMENT_x is defined if
+// experiment x (lower case) is enabled.
+//
+// - TODO(austin): More to come.
+//
+// In the toolchain, the set of experiments enabled for the current
+// build should be accessed via objabi.Experiment.
+//
+// For the set of experiments supported by the current toolchain, see
+// go doc internal/experiment.Flags.
+package goexperiment
+
+// Flags is the set of experiments that can be enabled or disabled in
+// the current toolchain.
+//
+// When specified in the GOEXPERIMENT environment variable or as build
+// tags, experiments use the strings.ToLower of their field name.
+type Flags struct {
+ FieldTrack bool
+ PreemptibleLoops bool
+ StaticLockRanking bool
+
+ // Regabi is split into several sub-experiments that can be
+ // enabled individually. GOEXPERIMENT=regabi implies the
+ // subset that are currently "working". Not all combinations work.
+ Regabi bool
+ // RegabiWrappers enables ABI wrappers for calling between
+ // ABI0 and ABIInternal functions. Without this, the ABIs are
+ // assumed to be identical so cross-ABI calls are direct.
+ RegabiWrappers bool
+ // RegabiG enables dedicated G and zero registers in
+ // ABIInternal.
+ //
+ // Requires wrappers because it makes the ABIs incompatible.
+ RegabiG bool
+ // RegabiReflect enables the register-passing paths in
+ // reflection calls. This is also gated by intArgRegs in
+ // reflect and runtime (which are disabled by default) so it
+ // can be used in targeted tests.
+ RegabiReflect bool
+ // RegabiDefer enables desugaring defer and go calls
+ // into argument-less closures.
+ RegabiDefer bool
+ // RegabiArgs enables register arguments/results in all
+ // compiled Go functions.
+ //
+ // Requires wrappers (to do ABI translation), g (because
+ // runtime assembly that's been ported to ABIInternal uses the
+ // G register), reflect (so reflection calls use registers),
+ // and defer (because the runtime doesn't support passing
+ // register arguments to defer/go).
+ RegabiArgs bool
+}