2021-04-06 08:25:01 -04:00
|
|
|
// 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.
|
|
|
|
|
|
2021-04-15 23:05:49 -04:00
|
|
|
package buildcfg
|
2021-04-06 08:25:01 -04:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"internal/goexperiment"
|
|
|
|
|
)
|
|
|
|
|
|
2022-03-16 16:25:47 -04:00
|
|
|
// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
|
|
|
|
|
// (platform-default) experiment configuration.
|
|
|
|
|
type ExperimentFlags struct {
|
|
|
|
|
goexperiment.Flags
|
|
|
|
|
baseline goexperiment.Flags
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-06 08:25:01 -04:00
|
|
|
// Experiment contains the toolchain experiments enabled for the
|
|
|
|
|
// current build.
|
|
|
|
|
//
|
|
|
|
|
// (This is not necessarily the set of experiments the compiler itself
|
|
|
|
|
// was built with.)
|
2021-06-25 13:24:10 -07:00
|
|
|
//
|
2024-07-01 00:49:11 +02:00
|
|
|
// Experiment.baseline specifies the experiment flags that are enabled by
|
2021-04-14 12:35:00 -04:00
|
|
|
// default in the current toolchain. This is, in effect, the "control"
|
|
|
|
|
// configuration and any variation from this is an experiment.
|
2022-03-16 16:25:47 -04:00
|
|
|
var Experiment ExperimentFlags = func() ExperimentFlags {
|
|
|
|
|
flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
|
2021-06-25 13:24:10 -07:00
|
|
|
if err != nil {
|
|
|
|
|
Error = err
|
2022-03-16 16:25:47 -04:00
|
|
|
return ExperimentFlags{}
|
2021-06-25 13:24:10 -07:00
|
|
|
}
|
2022-03-16 16:25:47 -04:00
|
|
|
return *flags
|
2021-06-25 13:24:10 -07:00
|
|
|
}()
|
|
|
|
|
|
2022-03-16 16:25:47 -04:00
|
|
|
// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
|
|
|
|
|
// It is not guaranteed to be canonical.
|
2021-06-25 13:24:10 -07:00
|
|
|
const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
|
2021-04-14 12:35:00 -04:00
|
|
|
|
2021-04-06 08:25:01 -04:00
|
|
|
// 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"
|
|
|
|
|
|
2021-06-25 13:24:10 -07:00
|
|
|
// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
|
|
|
|
|
// configuration tuple and returns the enabled and baseline experiment
|
|
|
|
|
// flag sets.
|
|
|
|
|
//
|
2024-07-01 00:49:11 +02:00
|
|
|
// TODO(mdempsky): Move to [internal/goexperiment].
|
2022-03-16 16:25:47 -04:00
|
|
|
func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
|
2022-03-02 11:59:48 -05:00
|
|
|
// 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 regabiSupported, regabiAlwaysOn bool
|
2021-10-04 12:13:41 -05:00
|
|
|
switch goarch {
|
2023-08-30 17:08:22 +08:00
|
|
|
case "amd64", "arm64", "loong64", "ppc64le", "ppc64", "riscv64":
|
2022-03-02 11:59:48 -05:00
|
|
|
regabiAlwaysOn = true
|
2021-10-04 12:13:41 -05:00
|
|
|
regabiSupported = true
|
|
|
|
|
}
|
2021-06-25 13:24:10 -07:00
|
|
|
|
2024-12-19 12:58:40 -05:00
|
|
|
// Older versions (anything before V16) of dsymutil don't handle
|
|
|
|
|
// the .debug_rnglists section in DWARF5. See
|
|
|
|
|
// https://github.com/golang/go/issues/26379#issuecomment-2677068742
|
|
|
|
|
// for more context. This disables all DWARF5 on mac, which is not
|
|
|
|
|
// ideal (would be better to disable just for cases where we know
|
|
|
|
|
// the build will use external linking). In the GOOS=aix case, the
|
|
|
|
|
// XCOFF format (as far as can be determined) doesn't seem to
|
|
|
|
|
// support the necessary section subtypes for DWARF-specific
|
|
|
|
|
// things like .debug_addr (needed for DWARF 5).
|
2025-03-11 21:19:11 -04:00
|
|
|
dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix")
|
2024-12-19 12:58:40 -05:00
|
|
|
|
2022-03-16 16:25:47 -04:00
|
|
|
baseline := goexperiment.Flags{
|
2025-03-17 11:45:52 -04:00
|
|
|
RegabiWrappers: regabiSupported,
|
|
|
|
|
RegabiArgs: regabiSupported,
|
|
|
|
|
Dwarf5: dwarf5Supported,
|
|
|
|
|
RandomizedHeapBase64: true,
|
2025-10-25 00:49:45 -04:00
|
|
|
RuntimeFree: true,
|
2025-03-17 11:45:52 -04:00
|
|
|
SizeSpecializedMalloc: true,
|
2025-10-09 16:05:35 +08:00
|
|
|
GreenTeaGC: true,
|
2021-06-25 13:24:10 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-06 16:31:40 -04:00
|
|
|
// Start with the statically enabled set of experiments.
|
2022-03-16 16:25:47 -04:00
|
|
|
flags := &ExperimentFlags{
|
|
|
|
|
Flags: baseline,
|
|
|
|
|
baseline: baseline,
|
|
|
|
|
}
|
2021-04-06 08:25:01 -04:00
|
|
|
|
2021-04-06 16:11:17 -04:00
|
|
|
// 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.
|
2021-06-25 13:24:10 -07:00
|
|
|
if goexp != "" {
|
2021-04-06 08:25:01 -04:00
|
|
|
// Create a map of known experiment names.
|
2021-04-15 16:18:30 -04:00
|
|
|
names := make(map[string]func(bool))
|
2022-03-16 16:25:47 -04:00
|
|
|
rv := reflect.ValueOf(&flags.Flags).Elem()
|
2021-04-06 08:25:01 -04:00
|
|
|
rt := rv.Type()
|
|
|
|
|
for i := 0; i < rt.NumField(); i++ {
|
|
|
|
|
field := rv.Field(i)
|
2021-04-15 16:18:30 -04:00
|
|
|
names[strings.ToLower(rt.Field(i).Name)] = field.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.
|
|
|
|
|
names["regabi"] = func(v bool) {
|
|
|
|
|
flags.RegabiWrappers = v
|
2021-04-16 15:26:57 -04:00
|
|
|
flags.RegabiArgs = v
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse names.
|
2025-09-15 14:39:58 +00:00
|
|
|
for f := range strings.SplitSeq(goexp, ",") {
|
2021-04-06 08:25:01 -04:00
|
|
|
if f == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-04-06 16:11:17 -04:00
|
|
|
if f == "none" {
|
2021-04-09 14:00:59 -04:00
|
|
|
// GOEXPERIMENT=none disables all experiment flags.
|
|
|
|
|
// This is used by cmd/dist, which doesn't know how
|
|
|
|
|
// to build with any experiment flags.
|
2022-03-16 16:25:47 -04:00
|
|
|
flags.Flags = goexperiment.Flags{}
|
2021-04-06 16:11:17 -04:00
|
|
|
continue
|
|
|
|
|
}
|
2021-04-06 08:25:01 -04:00
|
|
|
val := true
|
|
|
|
|
if strings.HasPrefix(f, "no") {
|
|
|
|
|
f, val = f[2:], false
|
|
|
|
|
}
|
2021-04-15 16:18:30 -04:00
|
|
|
set, ok := names[f]
|
2021-04-06 08:25:01 -04:00
|
|
|
if !ok {
|
2022-03-16 16:25:47 -04:00
|
|
|
return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
2021-04-15 16:18:30 -04:00
|
|
|
set(val)
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 11:59:48 -05:00
|
|
|
if regabiAlwaysOn {
|
2021-06-09 13:51:59 -04:00
|
|
|
flags.RegabiWrappers = true
|
2021-08-02 12:18:19 -04:00
|
|
|
flags.RegabiArgs = true
|
2021-06-09 13:51:59 -04:00
|
|
|
}
|
2023-08-16 10:39:38 +08:00
|
|
|
// regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le.
|
2021-10-04 12:13:41 -05:00
|
|
|
if !regabiSupported {
|
buildcfg: disable regabiwrappers along with regabiargs
This (1) "just makes sense"
and (2) avoids a weird bug in some name-dependent calling conventions
in wasm code generation, when the local pkg has a real name instead of "".
The calling conventions are triggered for a "wrapper" function, and somehow
an abiwrapper was taken to be a "wrapper" function, resulting in the use of
an invalid register. But abiwrapping has no business being in js/wasm code
generation, so just turn that off.
Updates #51734.
For posterity, that crash is:
GOSSAFUNC=wasmTruncU GOMAXPROCS=1 \
GOOS=js GOARCH=wasm GOEXPERIMENT=regabi,regabiargs
/Users/drchase/work/go-quick/bin/go build \
-gcflags=all=-d=abiwrap -o a.exe \
GOROOT/test/abi/bad_select_crash.go
<autogenerated>:1: internal compiler error: panic: bad Get: invalid register
goroutine 1 [running]:
runtime/debug.Stack()
runtime/debug/stack.go:24 +0x65
cmd/compile/internal/base.FatalfAt({0xc80?, 0x0?}, {0x195c85e, 0x9}, {0xc005ef72c8, 0x1, 0x1})
/Users/drchase/work/go-quick/src/cmd/compile/internal/base/print.go:227 +0x1d7
cmd/compile/internal/base.Fatalf(...)
/Users/drchase/work/go-quick/src/cmd/compile/internal/base/print.go:196
cmd/compile/internal/gc.handlePanic()
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/main.go:48 +0x85
panic({0x18bf3c0, 0x1ad0430})
runtime/panic.go:854 +0x26d
cmd/internal/obj/wasm.assemble(0xc0000f8200, 0xc001c74880, 0x0?)
/Users/drchase/work/go-quick/src/cmd/internal/obj/wasm/wasmobj.go:920 +0x1958
cmd/internal/obj.Flushplist(0xc0000f8200, 0xc005ef79a8, 0xc0022264c0, {0x7ff7bfefdd17, 0x7})
/Users/drchase/work/go-quick/src/cmd/internal/obj/plist.go:151 +0x784
cmd/compile/internal/objw.(*Progs).Flush(...)
/Users/drchase/work/go-quick/src/cmd/compile/internal/objw/prog.go:124
cmd/compile/internal/ssagen.Compile(0xc000707e00, 0xc001b4d620?)
/Users/drchase/work/go-quick/src/cmd/compile/internal/ssagen/pgen.go:208 +0x495
cmd/compile/internal/gc.compileFunctions.func4.1(0xc005ef7a01?)
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/compile.go:153 +0x3a
cmd/compile/internal/gc.compileFunctions.func2(0x0?)
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/compile.go:125 +0x1e
cmd/compile/internal/gc.compileFunctions.func4({0xc004685000, 0x79f, 0xa00?})
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/compile.go:152 +0x53
cmd/compile/internal/gc.compileFunctions()
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/compile.go:163 +0x162
cmd/compile/internal/gc.Main(0x198d3d8)
/Users/drchase/work/go-quick/src/cmd/compile/internal/gc/main.go:297 +0x108a
main.main()
/Users/drchase/work/go-quick/src/cmd/compile/main.go:55 +0xdd
Change-Id: I79f039e2494f78efba60e52ab1110d62656fb7ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/405899
Run-TryBot: David Chase <drchase@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2022-05-12 17:25:04 -04:00
|
|
|
flags.RegabiWrappers = false
|
2021-04-06 16:31:40 -04:00
|
|
|
flags.RegabiArgs = false
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
|
|
|
|
// Check regabi dependencies.
|
2022-03-15 13:27:53 -04:00
|
|
|
if flags.RegabiArgs && !flags.RegabiWrappers {
|
2022-03-16 16:25:47 -04:00
|
|
|
return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
2022-03-16 16:25:47 -04:00
|
|
|
return flags, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String returns the canonical GOEXPERIMENT string to enable this experiment
|
|
|
|
|
// configuration. (Experiments in the same state as in the baseline are elided.)
|
|
|
|
|
func (exp *ExperimentFlags) String() string {
|
|
|
|
|
return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-06 16:11:17 -04:00
|
|
|
// expList returns the list of lower-cased experiment names for
|
|
|
|
|
// experiments that differ from base. base may be nil to indicate no
|
2021-04-14 13:25:31 -04:00
|
|
|
// experiments. If all is true, then include all experiment flags,
|
|
|
|
|
// regardless of base.
|
|
|
|
|
func expList(exp, base *goexperiment.Flags, all bool) []string {
|
2021-04-06 08:25:01 -04:00
|
|
|
var list []string
|
2021-04-06 16:11:17 -04:00
|
|
|
rv := reflect.ValueOf(exp).Elem()
|
|
|
|
|
var rBase reflect.Value
|
|
|
|
|
if base != nil {
|
|
|
|
|
rBase = reflect.ValueOf(base).Elem()
|
|
|
|
|
}
|
2021-04-06 08:25:01 -04:00
|
|
|
rt := rv.Type()
|
|
|
|
|
for i := 0; i < rt.NumField(); i++ {
|
2021-04-06 16:11:17 -04:00
|
|
|
name := strings.ToLower(rt.Field(i).Name)
|
2021-04-06 08:25:01 -04:00
|
|
|
val := rv.Field(i).Bool()
|
2021-04-06 16:11:17 -04:00
|
|
|
baseVal := false
|
|
|
|
|
if base != nil {
|
|
|
|
|
baseVal = rBase.Field(i).Bool()
|
|
|
|
|
}
|
2021-04-14 13:25:31 -04:00
|
|
|
if all || val != baseVal {
|
2021-04-06 16:11:17 -04:00
|
|
|
if val {
|
|
|
|
|
list = append(list, name)
|
|
|
|
|
} else {
|
|
|
|
|
list = append(list, "no"+name)
|
|
|
|
|
}
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-16 16:25:47 -04:00
|
|
|
// Enabled returns a list of enabled experiments, as
|
2021-04-06 08:25:01 -04:00
|
|
|
// lower-cased experiment names.
|
2022-03-16 16:25:47 -04:00
|
|
|
func (exp *ExperimentFlags) Enabled() []string {
|
|
|
|
|
return expList(&exp.Flags, nil, false)
|
2021-04-06 08:25:01 -04:00
|
|
|
}
|
2021-04-15 23:05:49 -04:00
|
|
|
|
2022-03-16 16:25:47 -04:00
|
|
|
// All returns a list of all experiment settings.
|
2021-04-15 23:05:49 -04:00
|
|
|
// Disabled experiments appear in the list prefixed by "no".
|
2022-03-16 16:25:47 -04:00
|
|
|
func (exp *ExperimentFlags) All() []string {
|
|
|
|
|
return expList(&exp.Flags, nil, true)
|
2021-06-21 14:48:54 -07:00
|
|
|
}
|