// 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 buildcfg

import (
	
	
	

	
)

// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
// (platform-default) experiment configuration.
type ExperimentFlags struct {
	goexperiment.Flags
	baseline goexperiment.Flags
}

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var Experiment ExperimentFlags = func() ExperimentFlags {
	,  := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
	if  != nil {
		Error = 
		return ExperimentFlags{}
	}
	return *
}()

// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
// It is not guaranteed to be canonical.
const DefaultGOEXPERIMENT = defaultGOEXPERIMENT

// 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"

// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
// configuration tuple and returns the enabled and baseline experiment
// flag sets.
//
// TODO(mdempsky): Move to internal/goexperiment.
func (, ,  string) (*ExperimentFlags, error) {
	// regabiSupported is set to true on platforms where register ABI is
	// supported and enabled by default.
	// regabiAlwaysOn is set to true on platforms where register ABI is
	// always on.
	var ,  bool
	switch  {
	case "amd64", "arm64", "ppc64le", "ppc64", "riscv64":
		 = true
		 = true
	case "loong64":
		 = true
	}

	 := goexperiment.Flags{
		RegabiWrappers:   ,
		RegabiArgs:       ,
		CoverageRedesign: true,
		AllocHeaders:     true,
		ExecTracer2:      true,
	}

	// Start with the statically enabled set of experiments.
	 := &ExperimentFlags{
		Flags:    ,
		baseline: ,
	}

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	if  != "" {
		// Create a map of known experiment names.
		 := make(map[string]func(bool))
		 := reflect.ValueOf(&.Flags).Elem()
		 := .Type()
		for  := 0;  < .NumField(); ++ {
			 := .Field()
			[strings.ToLower(.Field().Name)] = .SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		["regabi"] = func( bool) {
			.RegabiWrappers = 
			.RegabiArgs = 
		}

		// Parse names.
		for ,  := range strings.Split(, ",") {
			if  == "" {
				continue
			}
			if  == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				.Flags = goexperiment.Flags{}
				continue
			}
			 := true
			if strings.HasPrefix(, "no") {
				,  = [2:], false
			}
			,  := []
			if ! {
				return nil, fmt.Errorf("unknown GOEXPERIMENT %s", )
			}
			()
		}
	}

	if  {
		.RegabiWrappers = true
		.RegabiArgs = true
	}
	// regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le.
	if ! {
		.RegabiWrappers = false
		.RegabiArgs = false
	}
	// Check regabi dependencies.
	if .RegabiArgs && !.RegabiWrappers {
		return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
	}
	return , nil
}

// String returns the canonical GOEXPERIMENT string to enable this experiment
// configuration. (Experiments in the same state as in the baseline are elided.)
func ( *ExperimentFlags) () string {
	return strings.Join(expList(&.Flags, &.baseline, false), ",")
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(,  *goexperiment.Flags,  bool) []string {
	var  []string
	 := reflect.ValueOf().Elem()
	var  reflect.Value
	if  != nil {
		 = reflect.ValueOf().Elem()
	}
	 := .Type()
	for  := 0;  < .NumField(); ++ {
		 := strings.ToLower(.Field().Name)
		 := .Field().Bool()
		 := false
		if  != nil {
			 = .Field().Bool()
		}
		if  ||  !=  {
			if  {
				 = append(, )
			} else {
				 = append(, "no"+)
			}
		}
	}
	return 
}

// Enabled returns a list of enabled experiments, as
// lower-cased experiment names.
func ( *ExperimentFlags) () []string {
	return expList(&.Flags, nil, false)
}

// All returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func ( *ExperimentFlags) () []string {
	return expList(&.Flags, nil, true)
}