mirror of
https://github.com/golang/go
synced 2024-11-23 04:00:03 -07:00
internal/goexperiment,cmd: consolidate GOEXPERIMENTs into a new package
Currently there's knowledge about the list of GOEXPERIMENTs in a few different places. This CL introduces a new package and consolidates the list into one place: the internal/goexperiment.Flags struct type. This package gives us a central place to document the experiments as well as the GOEXPERIMENT environment variable itself. It will also give us a place to put built-time constants derived from the enabled experiments. Now the objabi package constructs experiment names by reflecting over this struct type rather than having a separate list of these names (this is similar to how the compiler handles command-line flags and debug options). We also expose a better-typed API to the toolchain for propagating enabled experiments. Change-Id: I06e026712b59fe2bd7cd11a869aedb48ffe5a4b7 Reviewed-on: https://go-review.googlesource.com/c/go/+/307817 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
0c4a08cb74
commit
6304b401e4
@ -46,31 +46,18 @@ func NewInput(name string) *Input {
|
||||
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 {
|
||||
|
1
src/cmd/dist/buildtool.go
vendored
1
src/cmd/dist/buildtool.go
vendored
@ -58,6 +58,7 @@ var bootstrapDirs = []string{
|
||||
"debug/macho",
|
||||
"debug/pe",
|
||||
"go/constant",
|
||||
"internal/goexperiment",
|
||||
"internal/goversion",
|
||||
"internal/race",
|
||||
"internal/unsafeheader",
|
||||
|
@ -277,8 +277,8 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
||||
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,
|
||||
@ -1251,8 +1251,8 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
|
||||
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
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func BuildInit() {
|
||||
@ -53,7 +52,7 @@ 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)
|
||||
}
|
||||
}
|
||||
|
132
src/cmd/internal/objabi/exp.go
Normal file
132
src/cmd/internal/objabi/exp.go
Normal file
@ -0,0 +1,132 @@
|
||||
// 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)
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
package objabi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
@ -32,12 +31,6 @@ var (
|
||||
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 (
|
||||
@ -128,175 +121,3 @@ func gowasm() (f gowasmFeatures) {
|
||||
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
|
||||
}
|
||||
|
72
src/internal/goexperiment/flags.go
Normal file
72
src/internal/goexperiment/flags.go
Normal file
@ -0,0 +1,72 @@
|
||||
// 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user