func predefine(defines flags.MultiFlag) map[string]*Macro {
macros := make(map[string]*Macro)
- if *flags.CompilingRuntime && objabi.Regabi_enabled != 0 {
- const name = "GOEXPERIMENT_REGABI"
- macros[name] = &Macro{
- name: name,
- args: nil,
- tokens: Tokenize("1"),
+ // Set macros for various GOEXPERIMENTs so we can easily
+ // switch runtime assembly code based on them.
+ if *flags.CompilingRuntime {
+ set := func(name string) {
+ macros[name] = &Macro{
+ name: name,
+ args: nil,
+ tokens: Tokenize("1"),
+ }
+ }
+ if objabi.Experiment.RegabiWrappers {
+ set("GOEXPERIMENT_REGABI_WRAPPERS")
+ }
+ if objabi.Experiment.RegabiG {
+ set("GOEXPERIMENT_REGABI_G")
+ }
+ if objabi.Experiment.RegabiReflect {
+ set("GOEXPERIMENT_REGABI_REFLECT")
+ }
+ if objabi.Experiment.RegabiDefer {
+ set("GOEXPERIMENT_REGABI_DEFER")
+ }
+ if objabi.Experiment.RegabiArgs {
+ set("GOEXPERIMENT_REGABI_ARGS")
}
}
}
p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
} else if !isPlan9 && cnt <= int64(8*types.RegSize) {
- if objabi.Regabi_enabled == 0 && *state&x15 == 0 {
+ if !objabi.Experiment.RegabiG && *state&x15 == 0 {
p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0)
*state |= x15
}
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
}
} else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
- if objabi.Regabi_enabled == 0 && *state&x15 == 0 {
+ if !objabi.Experiment.RegabiG && *state&x15 == 0 {
p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0)
*state |= x15
}
if s.ABI != obj.ABIInternal {
v.Fatalf("MOVOstorezero can be only used in ABIInternal functions")
}
- if !(objabi.Regabi_enabled == 1 && base.Flag.ABIWrap) {
- // zeroing X15 manually if wrappers are not used
+ if !objabi.Experiment.RegabiG {
+ // zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
p := s.Prog(v.Op.Asm())
if s.ABI != obj.ABIInternal {
v.Fatalf("MOVOconst can be only used in ABIInternal functions")
}
- if !(objabi.Regabi_enabled == 1 && base.Flag.ABIWrap) {
- // zeroing X15 manually if wrappers are not used
+ if !objabi.Experiment.RegabiG {
+ // zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
off := duffStart(v.AuxInt)
// Closure pointer is DX.
ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpAMD64LoweredGetG:
- if objabi.Regabi_enabled == 1 && base.Flag.ABIWrap {
+ if objabi.Experiment.RegabiG {
v.Fatalf("LoweredGetG should not appear in new ABI")
}
r := v.Reg()
getgFromTLS(s, r)
case ssa.OpAMD64CALLstatic:
- if objabi.Regabi_enabled == 1 && s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
+ if objabi.Experiment.RegabiG && s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
s.Call(v)
- if objabi.Regabi_enabled == 1 && s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
+ if objabi.Experiment.RegabiG && s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
case ssa.BlockRet:
s.Prog(obj.ARET)
case ssa.BlockRetJmp:
- if objabi.Regabi_enabled == 1 && s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
+ if objabi.Experiment.RegabiG && s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
// Longer names
- ABIWrap bool "help:\"enable generation of ABI wrappers\""
ABIWrapLimit int "help:\"emit at most N ABI wrappers (for debugging)\""
AsmHdr string "help:\"write assembly header to `file`\""
Bench string "help:\"append benchmark times to `file`\""
Flag.LowerP = &Ctxt.Pkgpath
Flag.LowerV = &Ctxt.Debugvlog
- Flag.ABIWrap = objabi.Regabi_enabled != 0
Flag.Dwarf = objabi.GOARCH != "wasm"
Flag.DwarfBASEntries = &Ctxt.UseBASEntries
Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
import (
"cmd/compile/internal/abi"
- "cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
c.floatParamRegs = paramFloatRegAMD64
c.FPReg = framepointerRegAMD64
c.LinkReg = linkRegAMD64
- c.hasGReg = base.Flag.ABIWrap
+ c.hasGReg = objabi.Experiment.RegabiG
case "386":
c.PtrSize = 4
c.RegSize = 4
(IsInBounds idx len) => (SETB (CMPQ idx len))
(IsSliceInBounds idx len) => (SETBE (CMPQ idx len))
(NilCheck ...) => (LoweredNilCheck ...)
-(GetG mem) && !(objabi.Regabi_enabled == 1 && base.Flag.ABIWrap) => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register.
+(GetG mem) && !objabi.Experiment.RegabiG => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register.
(GetClosurePtr ...) => (LoweredGetClosurePtr ...)
(GetCallerPC ...) => (LoweredGetCallerPC ...)
(GetCallerSP ...) => (LoweredGetCallerSP ...)
import "math"
import "cmd/internal/objabi"
-import "cmd/compile/internal/base"
import "cmd/compile/internal/types"
func rewriteValueAMD64(v *Value) bool {
func rewriteValueAMD64_OpGetG(v *Value) bool {
v_0 := v.Args[0]
// match: (GetG mem)
- // cond: !(objabi.Regabi_enabled == 1 && base.Flag.ABIWrap)
+ // cond: !objabi.Experiment.RegabiG
// result: (LoweredGetG mem)
for {
mem := v_0
- if !(!(objabi.Regabi_enabled == 1 && base.Flag.ABIWrap)) {
+ if !(!objabi.Experiment.RegabiG) {
break
}
v.reset(OpAMD64LoweredGetG)
// useNewABIWrapGen returns TRUE if the compiler should generate an
// ABI wrapper for the function 'f'.
func useABIWrapGen(f *ir.Func) bool {
- if !base.Flag.ABIWrap {
+ if !objabi.Experiment.RegabiWrappers {
return false
}
func InitLSym(f *ir.Func, hasBody bool) {
// FIXME: for new-style ABI wrappers, we set up the lsym at the
// point the wrapper is created.
- if f.LSym != nil && base.Flag.ABIWrap {
+ if f.LSym != nil && objabi.Experiment.RegabiWrappers {
return
}
staticdata.NeedFuncSym(f.Sym())
}
func regabiEnabledForAllCompilation() bool {
- // TODO compiler does not yet change behavior for GOEXPERIMENT=regabi
- return false && objabi.Regabi_enabled != 0
+ return objabi.Experiment.RegabiArgs
}
// getParam returns the Field of ith param of node n (which is a
//
func callTargetLSym(callee *ir.Name, callerLSym *obj.LSym) *obj.LSym {
lsym := callee.Linksym()
- if !base.Flag.ABIWrap {
+ if !objabi.Experiment.RegabiWrappers {
return lsym
}
fn := callee.Func
var regg int16
if !p.From.Sym.NoSplit() || (p.From.Sym.Wrapper() && !p.From.Sym.ABIWrapper()) {
- if ctxt.Arch.Family == sys.AMD64 && objabi.Regabi_enabled != 0 && cursym.ABI() == obj.ABIInternal {
+ if ctxt.Arch.Family == sys.AMD64 && objabi.Experiment.RegabiG && cursym.ABI() == obj.ABIInternal {
regg = REGG // use the g register directly in ABIInternal
} else {
p = obj.Appendp(p, newprog)
regg = REG_CX
if ctxt.Arch.Family == sys.AMD64 {
- // Using this register means that stacksplit works w/ //go:registerparams even when objabi.Regabi_enabled == 0
+ // Using this register means that stacksplit works w/ //go:registerparams even when !objabi.Experiment.RegabiG
regg = REGG // == REG_R14
}
p = load_g(ctxt, p, newprog, regg) // load g into regg
// regabi is only supported on amd64.
if GOARCH != "amd64" {
- Regabi_enabled = 0
+ 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
+ // Not ready yet:
+ //Experiment.RegabiDefer = true
+ //Experiment.RegabiArgs = true
+ }
+ // Check regabi dependencies.
+ if Experiment.RegabiG && !Experiment.RegabiWrappers {
+ panic("GOEXPERIMENT regabig requires regabiwrappers")
+ }
+ if Experiment.RegabiArgs && !(Experiment.RegabiWrappers && Experiment.RegabiReflect && Experiment.RegabiDefer) {
+ panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabireflect,regabidefer")
}
// Set GOEXPERIMENT to the parsed and canonicalized set of experiments.
Fieldtrack_enabled int
Preemptibleloops_enabled int
Staticlockranking_enabled int
- Regabi_enabled int
)
+// Experiment contains flags for GOEXPERIMENTs.
+//
+// TODO(austin): Move the package-level experiment flags into this.
+var Experiment ExpFlags
+
+type ExpFlags struct {
+ // 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, reflect, defer.
+ RegabiArgs bool
+}
+
// Toolchain experiments.
// These are controlled by the GOEXPERIMENT environment
// variable recorded when the toolchain is built.
{"fieldtrack", &Fieldtrack_enabled},
{"preemptibleloops", &Preemptibleloops_enabled},
{"staticlockranking", &Staticlockranking_enabled},
- {"regabi", &Regabi_enabled},
+ {"regabi", &Experiment.regabi},
+ {"regabiwrappers", &Experiment.RegabiWrappers},
+ {"regabig", &Experiment.RegabiG},
+ {"regabireflect", &Experiment.RegabiReflect},
+ {"regabidefer", &Experiment.RegabiDefer},
+ {"regabiargs", &Experiment.RegabiArgs},
}
var defaultExpstring string
default:
log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups)
}
- if !*flagAbiWrap || ctxt.linkShared {
+ if !objabi.Experiment.RegabiWrappers || ctxt.linkShared {
// Use ABI aliases if ABI wrappers are not used.
// TODO: for now we still use ABI aliases in shared linkage, even if
// the wrapper is enabled.
// collect text symbol ABI versions.
symabi := make(map[string]int) // map (unmangled) symbol name to version
- if *flagAbiWrap {
+ if objabi.Experiment.RegabiWrappers {
for _, elfsym := range syms {
if elf.ST_TYPE(elfsym.Info) != elf.STT_FUNC {
continue
symname := elfsym.Name // (unmangled) symbol name
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type.") {
ver = sym.SymVerABIInternal
- } else if *flagAbiWrap && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC {
+ } else if objabi.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC {
if strings.HasSuffix(elfsym.Name, ".abiinternal") {
ver = sym.SymVerABIInternal
symname = strings.TrimSuffix(elfsym.Name, ".abiinternal")
// mangle Go function names in the .so to include the
// ABI.
if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && ver == 0 {
- if *flagAbiWrap {
+ if objabi.Experiment.RegabiWrappers {
if _, ok := symabi[symname]; ok {
continue // only use alias for functions w/o ABI wrappers
}
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
- flagAbiWrap = flag.Bool("abiwrap", objabi.Regabi_enabled != 0, "support ABI wrapper functions")
benchmarkFlag = flag.String("benchmark", "", "set to 'mem' or 'cpu' to enable phase benchmarking")
benchmarkFileFlag = flag.String("benchmarkprofile", "", "emit phase profiles to `base`_phase.{cpu,mem}prof")
)
// sym or marker relocation to associate the wrapper with the
// wrapped function.
//
- if *flagAbiWrap {
+ if objabi.Experiment.RegabiWrappers {
if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT {
// First case
if ldr.SymVersion(x) == sym.SymVerABIInternal {
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build goexperiment.regabi
-// +build goexperiment.regabi
+//go:build goexperiment.regabireflect
+// +build goexperiment.regabireflect
package abi
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !goexperiment.regabi
-// +build !goexperiment.regabi
+//go:build !goexperiment.regabireflect
+// +build !goexperiment.regabireflect
package abi
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build goexperiment.regabi
-//go:build goexperiment.regabi
+//go:build goexperiment.regabireflect
+// +build goexperiment.regabireflect
// This file contains tests specific to making sure the register ABI
// works in a bunch of contexts in the runtime.
MOVL $0, DX
JMP runtime·morestack(SB)
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_REFLECT
// spillArgs stores return values from registers to a *internal/abi.RegArgs in R12.
TEXT spillArgs<>(SB),NOSPLIT,$0-0
MOVQ AX, 0(R12)
// or else unwinding from systemstack_switch is incorrect.
// Smashes R9.
TEXT gosave_systemstack_switch<>(SB),NOSPLIT,$0
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R14)
MOVQ g(R14), R14
#endif
// signals. It is quite painful to set X15 in the signal context,
// so we do it here.
TEXT ·sigpanic0<ABIInternal>(SB),NOSPLIT,$0-0
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
get_tls(R14)
MOVQ g(R14), R14
XORPS X15, X15
MOVQ R13, 104(SP)
// TODO: Consider passing g.m.p in as an argument so they can be shared
// across a sequence of write barriers.
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
MOVQ g_m(R14), R13
#else
get_tls(R13)
// If addr (RARG1) is out of range, do nothing.
// Otherwise, setup goroutine context and invoke racecall. Other arguments already set.
TEXT racecalladdr<>(SB), NOSPLIT, $0-0
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
// R11 = caller's return address
TEXT racefuncenter<>(SB), NOSPLIT, $0-0
MOVQ DX, BX // save function entry context (for closures)
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
// func runtime·racefuncexit()
// Called from instrumented code.
TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
JAE racecallatomic_ignore
racecallatomic_ok:
// Addr is within the good range, call the atomic function.
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
// An attempt to synchronize on the address would cause crash.
MOVQ AX, BX // remember the original function
MOVQ $__tsan_go_ignore_sync_begin(SB), AX
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
// Switches SP to g0 stack and calls (AX). Arguments already set.
TEXT racecall<>(SB), NOSPLIT, $0-0
-#ifndef GOEXPERIMENT_REGABI
+#ifndef GOEXPERIMENT_REGABI_G
get_tls(R12)
MOVQ g(R12), R14
#endif
MOVQ SP, R12 // Save old SP; R12 unchanged by C code.
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
MOVQ g_m(R14), BX // BX unchanged by C code.
#else
get_tls(CX)
MOVQ CX, m_vdsoPC(BX)
MOVQ DX, m_vdsoSP(BX)
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
CMPQ R14, m_curg(BX) // Only switch if on curg.
#else
CMPQ AX, m_curg(BX) // Only switch if on curg.
MOVQ SP, R12 // Save old SP; R12 unchanged by C code.
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
MOVQ g_m(R14), BX // BX unchanged by C code.
#else
get_tls(CX)
MOVQ CX, m_vdsoPC(BX)
MOVQ DX, m_vdsoSP(BX)
-#ifdef GOEXPERIMENT_REGABI
+#ifdef GOEXPERIMENT_REGABI_G
CMPQ R14, m_curg(BX) // Only switch if on curg.
#else
CMPQ AX, m_curg(BX) // Only switch if on curg.