Black Lives Matter. Support the Equal Justice Initiative.

Source file src/internal/buildcfg/exp.go

Documentation: internal/buildcfg

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

View as plain text