GOEXPERIMENT lifecycles
GOEXPERIMENT flags are how most large runtime and compiler changes were staged. Each one goes from added (off by default, opt-in), to default-on (added to the baseline in internal/buildcfg/exp.go, so you opt out with GOEXPERIMENT=noX), to flag removed (the old code path is deleted). This table was derived by diffing src/internal/goexperiment/flags.go and the baseline set in internal/buildcfg/exp.go at every cycle boundary. Each transition is attributed to the cycle in which its commit landed on master.
| Experiment | What it gates | Added | Default-on | Flag removed | State at HEAD |
|---|---|---|---|---|---|
RegabiWrappers, RegabiArgs (+ RegabiG, RegabiReflect, RegabiDefer) |
Register-based calling convention (ABIInternal) | 1.16 2b8b06ebbf (one regabi flag); split into sub-experiments 1.17 14dbd6e776; amd64 on linux/darwin/windows default-on in 1.17 (fd3612e433, 9dd71ba913), android 9c12f1b433 |
amd64, all OSes: 1.18 382c5dd5f7; arm64: 1.18 4d2b528795; ppc64: 1.18 ae83301ab2; riscv64: 1.19 53f13128a7; loong64: 1.22 e5615ad876; s390x: 1.26 89552911b3 |
RegabiDefer: 1.18 a9de78ac88; RegabiG: 1.18 c93d5d1a52; RegabiReflect: 1.19 3684abbf6c |
Baseline = regabiSupported; 386, arm, mips*, wasm still use ABI0 |
PacerRedesign |
New GC pacer (PI controller over total heap work, including stacks and globals) | 1.18 a108b280bc |
1.18 961aab26bf |
1.19 3334afd760 |
Old pacer deleted |
HeapMinimum512KiB |
Smaller minimum heap target | 1.18 c5c1955077 |
— | — | Still an opt-in flag |
Unified |
Unified IR frontend (noder → export data → IR) | 1.18 804ecc2581 |
1.20 833367e98a |
1.21 4f467f1082 |
Only frontend |
CoverageRedesign |
Compiler-instrumented coverage with meta-data/counter files | 1.20 a3434b71a3 |
1.20 9910f8a403 |
1.25 41298239cf |
Only implementation |
Arenas |
arena package (manual region allocation) |
1.20 e0d01b8467 |
— | — | Still opt-in, never enabled |
PageTrace |
Page-allocation tracing | 1.20 e4435cb844 |
— | 1.23 97c13cfb25 |
Deleted |
CgoCheck2 |
cgocheck=2 write-barrier checking moved from GODEBUG to a build-time experiment |
1.21 55044288ad |
— | — | Opt-in |
LoopVar |
Per-iteration loop variables | 1.21 c20d959163 |
Tied to the module’s Go version (≥1.22) in 1.22 fe1daf2e43, not via the baseline |
— | Flag kept (forces semantics regardless of version) |
NewInliner |
Heuristics-based inliner (inline/inlheur) |
1.22 fbf9076ee8 |
never | — | Still opt-in |
Range → RangeFunc |
Range-over-func / range-over-int | 1.22 (renamed 45320c23f2) |
Language-version gated in 1.23 d278d5bbdd |
Range 1.23 d428a63875; RangeFunc 1.26 25816d401c |
Language feature |
AllocHeaders |
Malloc headers / per-span metadata instead of the heap bitmap | 1.22 25867485a7 |
1.22 e3585c6757 |
1.23 9f3f4c64db |
Only implementation |
ExecTracer2 |
New partitioned execution tracer | 1.22 43ffe2a892 |
1.22 41f58b2295 |
1.23 55e4097cba |
Only tracer |
AliasTypeParams |
Generic type aliases | 1.23 c3591cb30c |
1.24 4a1167dfe1 |
1.26 00a7bdcb55 |
Language feature |
SwissMap |
Swiss-table map implementation | 1.24 057b703407 |
1.24 99d60c24e2 |
1.26 2ae059ccaf |
Only map implementation |
SpinbitMutex |
New runtime-internal mutex (spin bit + waiter list in one word) |
1.24 18c2461af3 |
1.24 fd050b3c6d (where 8-bit Xchg exists); all platforms 1.25 5e98202455 |
1.25 7a177114df |
Only implementation |
SyncHashTrieMap |
sync.Map reimplemented over a concurrent hash-trie |
1.24 aecb8faa91 |
1.24 | 1.26 e15a14c4dd |
Only implementation |
Synctest |
testing/synctest bubbles |
1.24 76e4efdc77 |
— | 1.26 4c0fd3a2b4 (graduated) |
Package is standard |
Dwarf5 |
DWARF 5 debug info | 1.25 bdb4676043 |
1.25 ca19f987ca |
— | Baseline where supported |
GreenTeaGC |
Span-oriented (“Green Tea”) marking | 1.25 f1ebad19bd |
1.26 7fbf54bfeb (loong64 c53cb642de) |
— | Default-on, nogreenteagc still available |
RandomizedHeapBase64 |
Randomized heap base address on 64-bit | 1.26 6669aa3b14 |
1.26 72ba117bda |
— | Default-on |
SizeSpecializedMalloc |
Generated per-size-class mallocgc variants |
1.26 60c1ee9183 |
Enabled on master in 1.26 (a7917eed70), but go1.26.0 shipped with it off (release-branch cherry-pick 3604782621; master counterpart 4af8ad24ee); re-enabled 1.27 2a93576965 |
1.28 4a7645b730 |
Only implementation (dev tree) |
RuntimeFreegc |
Compiler-directed explicit freeing of heap memory | 1.26 5dcaf9a01b |
off (5a347b775e) |
— | Opt-in |
RuntimeSecret |
runtime/secret (erase secrets from registers/stack/heap) |
1.26 a3fb92a710 |
— | — | Opt-in |
GoroutineLeakProfile |
GC-based goroutine-leak detection profile | 1.26 8c68a1c1ab |
1.27 253aa2a12a |
1.27 16449179ec / afcf04cb64 |
Graduated |
SIMD |
simd/archsimd intrinsics package (dev.simd) |
1.26 4d2c71ebf9 |
toggled on the branch; off at merge (1d8711e126, c59ae5c5a7) |
— | Opt-in |
GenericMethods |
Methods with their own type parameters | 1.27 aa62c18749 |
1.27 e212a16d1e |
1.27 012d142e78 |
Language feature |
MapSplitGroup |
Swiss-map group slot layout KKKKVVVV instead of KVKVKVKV | 1.27 5560073398 |
1.28 26c2ec0508 |
— | Default in dev tree |
CgoBindings |
New cgo bindings mechanism | 1.28 6bbd163b33 |
— | — | Opt-in (dev tree) |
Default-on experiments as actually shipped. This is the baseline in each release tag. It can differ from master at the end of the cycle when the release branch changes a default.
| Tag | Default-on experiments |
|---|---|
| go1.18 | RegabiWrappers, RegabiReflect, RegabiArgs (where supported), PacerRedesign |
| go1.19 | RegabiWrappers, RegabiArgs |
| go1.20 | Regabi*, Unified, CoverageRedesign |
| go1.21.0 | Regabi*, CoverageRedesign |
| go1.22.0 | Regabi*, CoverageRedesign, AllocHeaders, ExecTracer2 |
| go1.23.0 | Regabi*, CoverageRedesign |
| go1.24.0 | Regabi*, CoverageRedesign, AliasTypeParams, SwissMap, SpinbitMutex (where 8-bit Xchg exists), SyncHashTrieMap |
| go1.25.0 | Regabi*, AliasTypeParams, SwissMap, SyncHashTrieMap, Dwarf5 |
| go1.26.0 | Regabi*, Dwarf5, RandomizedHeapBase64, GreenTeaGC |
| go1.27.0 | Regabi*, Dwarf5, RandomizedHeapBase64, GreenTeaGC, JSONv2, SizeSpecializedMalloc |
| HEAD (1.28-dev) | Regabi*, Dwarf5, RandomizedHeapBase64, GreenTeaGC, JSONv2, MapSplitGroup, GoListExportNewFormat |
A flag that drops out of this list without being disabled was removed: the feature became the only implementation. Examples are PacerRedesign after 1.18, AllocHeaders and ExecTracer2 after 1.22, and SwissMap after 1.25.
The same history also records these non-experiment knobs (GODEBUG settings and environment variables): GOAMD64 microarchitecture levels (1.18, b1bedc0774), GODEBUG=harddecommit (1.18, f063e0da28), GOPPC64=power10 (1.20, 0eb56ca468), GODEBUG=dontfreezetheworld (1.21, 5f882d8266), GODEBUG=disablethp (1.22, c915215af6), GORISCV64 (1.23, b4e7d630bc; rva23u64 added in 1.25, 664aebab7d), GOARM64 (1.23, e8b5bc63be), GODEBUG=decoratemappings (1.25, c40a3731f4), and GOWASM=stackswitch (1.28, 5408ea597b).
Before 1.18: experiments as a table inside the toolchain
Before Go 1.17 there was no baseline. An experiment was an entry in a table compiled into the toolchain, and it was off unless GOEXPERIMENT named it when the toolchain was built. The table lived in src/cmd/gc/lex.c up to Go 1.4. For Go 1.5 it moved to src/cmd/internal/obj/go.go, and dba9eb3369 re-implemented GOEXPERIMENT in the runtime and liblink for the frame-pointer experiment. From Go 1.9 the table lived in src/cmd/internal/objabi/util.go. In Go 1.17, 6304b401e4 consolidated the experiments into internal/goexperiment, a8e55538af made GOEXPERIMENT a diff against per-platform defaults (the baseline used in the table above), and 5159c83641 added the active set to runtime.Version(). This table was derived by reading the experiment table at every release tag from go1 to go1.17. Each transition is attributed to the first release tag that contains its commit.
| Experiment | What it gates | Added | Default-on | Flag removed | State at HEAD |
|---|---|---|---|---|---|
rune32 |
rune as an alias of int32 instead of int |
1.0 (2011-10) 6ed3fa6553 |
1.0 (2011-12) 012798a325: rune is always int32 and the table entry is commented out |
the commented-out entry disappeared with lex.c |
— |
fieldtrack |
Struct field tracking: fields tagged go:"track" are reported by the linker when used |
1.1 3d40062c68 |
never | — | Still opt-in (FieldTrack) |
precisestack |
Fully precise stack-frame scanning (no reads of uninitialised pointer slots) | 1.2 30ecb4cd05: call-site bitmaps were not ready, so it shipped opt-in |
1.3 53061193f1 (first attempt ecf700b5ee was undone by aad23e708c) |
1.4 15b76ad94b, together with copystack |
Assumed everywhere |
framepointer |
Saving BP frame pointers on amd64 |
1.5 3c0fee10db |
1.7 7fdec6216c (amd64) |
1.16 5c2c6d3fbf (hard-coded on) |
buildcfg.FramePointerEnabled (amd64, arm64) |
preemptibleloops |
Reschedule checks on loop back edges | 1.8 7f1ff65c39 |
never | — | Still opt-in (PreemptibleLoops) |
clobberdead |
Clobbering dead pointer slots at safepoints, to debug liveness maps | 1.9 1e72bf6218 |
never | 1.12 57c8eb92b7 |
— |
debugcpu |
GODEBUGCPU for disabling CPU features |
1.11 f045ddc624 |
— | 1.12 a5248acd91 (GODEBUGCPU available in every build) |
— |
staticlockranking |
Static lock-rank checking in the runtime | 1.15 0a820007e7 |
never | — | Still opt-in (StaticLockRanking) |
regabi |
Register-based calling convention | 1.16 2b8b06ebbf |
see the table above | split in 1.17 14dbd6e776 |
see the table above |