blob: 21a70d5dfece46a5fcdfebb50791f79e5e477f1b [file] [log] [blame]
// 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)
}