About this report
This page reconstructs how the Go runtime and compiler toolchain evolved, from the first compiler and runtime commits in 2008 to the current development tree (Go 1.28, unreleased as of 2026-10-30). It is built only from the commit history: commit subjects, commit messages (which often carry the design rationale and benchmark tables), and diffs. Release notes were deliberately not used. Internal changes they skip are included and tagged internal; changes to user-facing API, behaviour or flags are tagged user-visible.
The page covers 28,950 in-scope commits: 19,100 before Go 1.18 and 9,850 from Go 1.18 on. The two periods were read separately, with the same method, and are joined at the 1.17 → 1.18 seam.
Method (map → reduce).
- Map. Every in-scope commit was tagged with topic areas by the files it touches. For example,
mgc*.gogoes to the GC,internal/runtime/mapsto data structures, andssa/_gen/ARM64.rulesto backends. A commit can belong to more than one area. Historic paths were first mapped to their modern equivalents:src/pkg/runtime/*.c/.goc→ runtime,src/cmd/gc→ frontend,5g/6g/8g/9g→ backends,5l/6l/8l,src/cmd/ld→ linker,src/liblink→cmd/internal/obj. Files of the old monolithicgcpackage (esc.go,inl.go,walk.go,ssa.go,plive.go,reflect.go, …) were mapped by file name. - Reduce. For each area, a reader went through every non-noise commit subject in the area, plus the full messages of the design-heavy commits and, in some areas, the diffs. It picked the commits that matter and clustered them into threads: coherent bodies of work such as the concurrent GC, contiguous stacks, the SSA backend, the new linker, the GC pacer, Swiss maps or PGO. Each thread gets:
- its design;
- a timeline listing, per release, what landed (Done) and what was still unfinished afterwards (Left open), based on TODOs, “for now”, “follow-up” and “disabled by default” notes in the messages, or on what the next stage had to fix;
- its status: at HEAD for 1.18+ threads, at go1.17 for earlier ones.
[release-branch]cherry-picks, and subjects matching patterns such as typo, comment, rename, clean up, skip, update, undo or revert. A later audit found 259 larger commits among them, each changing 30 or more lines, whose subjects matched only a weak pattern (for example “remove locks from netpoll hotpaths” or “replace func-based write barrier skipping”). A separate reader described those 259 and added them to the “Other notable changes” items. The remaining reverts, renames, clean-ups and test changes are not cited. - Eras and seams. Before 1.18, the large areas were split into era slices at the big rewrites: the compiler’s C → Go translation (1.5), the SSA backend (1.7), the new linker (1.15–1.16), and the register ABI and types2 work (1.17). Where a thread continues across a slice boundary, or into the 1.18+ report, the thread carries Earlier history and Continues in links.
- Who read what. For 1.18+, the GC/allocator chapter and most of the compiler-optimization chapter were read by Claude Opus. The other 1.18+ areas, two gap passes, and all 16 pre-1.18 slices were read by Claude Sonnet. Each area in Part I opens with a short note giving its reader, how many of its commits it cites, and any caveats the reader reported. Every pre-1.18 slice cites all of its non-noise commits.
- Views. The Thread × release maps (one for 1.0–1.17, one for 1.18–1.28) show at a glance which releases moved which thread. Part I reads thread by thread, area by area, oldest era first. Part II regroups the same stages by release, adding changes that don’t belong to any thread. For pre-1.18 releases it also opens each area with a one-line digest. Part III (Paths not taken) covers the work that did not land: abandoned CL series, unmerged branches, declined or held proposals, and design docs that were never implemented. It explains why each one stopped, from the review record. Landed threads link to the Part III entries that relate to them.
How releases are delimited: Go 1.18 onwards. A “release” is a development cycle: the commits on master between the internal/goversion bump that opened the cycle and the bump that opened the next one, which happens when the previous release branch is cut. Freeze-period fixes count toward the cycle they landed in. Point-release backports (cherry-picks onto release-branch.go1.N) are not covered. Work developed on feature branches (dev.typeparams, dev.unified, dev.simd, …) counts toward the cycle where it merged to master, so author dates can be older than the cycle.
| Release | Commit range on master | Cycle opened | In-scope commits |
|---|---|---|---|
| Go 1.18 | 46fd547d89..936c7fbc1c |
2021-08-12 | 1,569 |
| Go 1.19 | 936c7fbc1c..d723df76da |
2022-02-28 | 735 |
| Go 1.20 | d723df76da..83f8688915 |
2022-08-02 | 953 |
| Go 1.21 | 83f8688915..21ff9be0eb |
2023-01-17 | 967 |
| Go 1.22 | 21ff9be0eb..1653833811 |
2023-07-18 | 883 |
| Go 1.23 | 1653833811..c622f5c341 |
2024-01-19 | 667 |
| Go 1.24 | c622f5c341..9da530f11a |
2024-07-22 | 718 |
| Go 1.25 | 9da530f11a..2696a11a97 |
2025-02-03 | 650 |
| Go 1.26 | 2696a11a97..83c44ec032 |
2025-07-23 | 1,082 |
| Go 1.27 | 83c44ec032..6c87434d24 |
2026-01-21 | 861 |
| Go 1.28 (dev) | 6c87434d24..4494c64f5a (HEAD) |
2026-07-14 | 766 |
How releases are delimited: Go 1.0 – 1.17. Before Go 1.13 there was no internal/goversion bump to cut cycles on, and early release branches were often merged back into master. So for these releases a commit belongs to the first release tag that contains it: git rev-list go1.N ^go1.(N-1). The 54 cherry-picks made on release branches ([release-branch.go1.N] subjects) are treated as noise. “Go 1.0” means everything up to the go1 tag: the whole pre-release history, from the first compiler and runtime commits in June 2008, through the open-sourcing in November 2009 and the weekly snapshots, to Go 1. Joke commits dated 1972–1988 at the root of the repository are excluded. The two conventions meet without a gap: every in-scope commit reachable from the 1.18 cycle’s opening commit is contained in the go1.17 tag.
| Release | Commits | Tagged | In-scope commits |
|---|---|---|---|
| Go 1.0 | go1 (2008-06 → 2012-03) |
2012-03-28 | 3,138 |
| Go 1.1 | go1.1 ^go1 |
2013-05-13 | 967 |
| Go 1.2 | go1.2 ^go1.1 |
2013-11-29 | 531 |
| Go 1.3 | go1.3 ^go1.2 |
2014-06-19 | 599 |
| Go 1.4 | go1.4 ^go1.3 |
2014-12-11 | 644 |
| Go 1.5 | go1.5 ^go1.4 |
2015-08-19 | 1,617 |
| Go 1.6 | go1.6 ^go1.5 |
2016-02-17 | 719 |
| Go 1.7 | go1.7 ^go1.6 |
2016-08-15 | 1,594 |
| Go 1.8 | go1.8 ^go1.7 |
2017-02-16 | 1,012 |
| Go 1.9 | go1.9 ^go1.8 |
2017-08-24 | 1,136 |
| Go 1.10 | go1.10 ^go1.9 |
2018-02-15 | 793 |
| Go 1.11 | go1.11 ^go1.10 |
2018-08-24 | 991 |
| Go 1.12 | go1.12 ^go1.11 |
2019-02-25 | 659 |
| Go 1.13 | go1.13 ^go1.12 |
2019-09-03 | 567 |
| Go 1.14 | go1.14 ^go1.13 |
2020-02-25 | 826 |
| Go 1.15 | go1.15 ^go1.14 |
2020-08-11 | 963 |
| Go 1.16 | go1.16 ^go1.15 |
2021-02-16 | 862 |
| Go 1.17 | go1.17 ^go1.16 |
2021-08-16 | 1,482 |
Paths in scope:
src/runtimesrc/internal/{runtime,abi,goarch,goos,cpu,bytealg,buildcfg,goexperiment,trace,sync,chacha8rand,race,asan,msan,synctest,simd,reflectlite,pkgbits,types,coverage,profile}src/{unique,weak,sync,iter,simd,reflect}src/cmd/compile,src/cmd/internal/{obj,objabi,src,dwarf,goobj,goobj2,sys},src/cmd/link,src/cmd/asm- Historic equivalents:
src/pkg/{runtime,reflect,sync},src/lib/{runtime,reflect,sync}src/cmd/gc,src/cmd/{5,6,8,9}{g,l,a,c},src/cmd/cc,src/cmd/ld,src/liblinksrc/cmd/internal/{gc,ld,asm,arch},src/cmd/new*/old*(the transitional copies made during the C → Go translation)
Out of scope: cmd/go, cmd/cgo, cmd/dist, go/types (except as the mirror of types2), and the rest of the standard library.
Reading the citations. Every claim cites the commits it rests on, and each hash links to go.googlesource.com. A bullet often condenses dozens of commits. Benchmark figures are quoted from the commit messages and reflect the author’s measurement at the time. A small superscript after a hash (for example 1.9) marks a commit that landed in a different release from the bullet that cites it, usually as context. About 160 of the roughly 23,000 pre-1.18 citations carry one, most of them off by a single release.
Design docs and discussions. Threads with a traceable design document or discussion show them in a box above their design section, and the Design docs and discussions page lists them all: 556 links on 226 threads. They come from three sources. The first is each thread’s own commits, through the issues those commits reference and the design docs (golang/proposal, Google Docs, go.dev/s short links), proposals, and golang-dev, golang-nuts and GitHub discussion threads those issues link to. The second is the curated links of Go: A Documentary. The third is the golang/proposal repository. A Claude Sonnet reader picked the relevant ones per thread from that pool by title, and each link says where it was found. These are pointers only: the thread text itself was written from the commits alone.
Contents
Part I — Topics
- Memory management
Go 1.0–1.17 · Go 1.18–1.28 - Scheduler and concurrency
Go 1.0–1.17 · Go 1.18–1.28 - Data structures and type metadata
Go 1.0–1.17 · Go 1.18–1.28 - Diagnostics and knobs
Go 1.0–1.17 · Go 1.18–1.28 - OS layer, stacks and panics
Go 1.0–1.4 · Go 1.5–1.17 · Go 1.18–1.28 - Compiler frontend
Go 1.0: 2008–2012 · Go 1.1–1.9 · Go 1.10–1.17 · Go 1.18–1.28 · types2 · Go 1.18–1.28 · syntax, IR, generics - Middle end and SSA optimizer
Go 1.0–1.9 · Go 1.10–1.17 · Go 1.18–1.28 - Backends and assemblers
Go 1.0–1.6 · Go 1.7–1.11 · Go 1.12–1.17 · Go 1.18–1.28 · architectures, ABI · Go 1.18–1.28 · SIMD - Linker and object files
Go 1.0–1.9 · Go 1.10–1.17 · Go 1.18–1.28
Overview
This section summarizes the 436 threads in Part I: 268 for Go 1.0–1.17 and 168 for Go 1.18 onwards. It covers two things: the big arcs that cut across the runtime and the compiler, and what defines each release. Each link jumps to the full thread, which has the design and the commit-by-commit timeline.
The big arcs
Each arc is a chain of the stages it went through. “→” separates releases, and HEAD means the development tree as of 2026-10-30. Arcs that began before 1.18 open with a Before 1.18 line.
The toolchain rewrites itself (before 1.18).
- The C frontend
gcwith yacc grammar, Plan 9 C compilers for the runtime’s C code (1.0). - Runtime converted from C to Go: the scheduler, channels, maps and other data structures and panics, symbol tables and locks in 1.4; the allocator and GC and the remaining OS, arch and signal layers in 1.5.
- Compiler, linker and assemblers translated to Go;
cmd/compileandcmd/linkappear and the C compilers are deleted (1.5). - Hand-written parser replaces yacc (1.6) →
syntaxpackage (1.8) → dev.regabi:ir.Nodeinterface and the split of package gc (1.17).
Generics, end to end.
Before 1.18: type aliases (
=>experiment reverted in 1.8,type A = Bin 1.9) → types2, the port of go/types and the dev.typeparams bring-up: type parameters insyntax, the-Gflag,irgenand the first stenciling, all off by default (1.17).Compiler: shape stenciling + dictionaries is built on the old typechecker with
-G=3(1.18) → reimplemented on unified IR, which becomes the default (1.20) → unified IR is the only frontend and the knob is gone (1.21) → export data versions V2 (1.24), V4 (1.27, for generic methods) and V5 (1.28).Type checker: bring-up (1.18) → inference rewritten around the unifier: reverse inference, interface inference, untyped-constant defaulting (1.21) →
Aliasnodes (1.22, opt-in) → generic type aliases (1.24) → core types dropped from the checker (1.25) →gotypesaliasremoved (1.27) → generic methods (1.27).
GC: pacing, then the heap layout, then the marking algorithm.
Before 1.18: stop-the-world mark-sweep, made parallel (1.0–1.4) with precise heap scanning (1.1 →) and precise stack scanning (opt-in 1.2, default 1.3) → concurrent sweep (1.3) → concurrent mark with write barriers and the pacer with assists and 25% background utilisation (1.5) → decentralised GC state machine (1.6) → hybrid write barrier ends stack rescans (1.8), buffered write barrier (1.10) → mark termination simplified (1.12) → pacer isolated as
gcControllerState(1.17).Pacer redesign: stacks and globals count toward the goal (1.18) → old pacer deleted (1.19) → cons/mark smoothing goes from a PI controller (1.18) to a 2-cycle average (1.20) to the max of 5 cycles (1.21).
Soft memory limit + GC CPU limiter (1.19), with the scavenger redesigned alongside it (1.18–1.22).
Allocation headers replace the heap bitmap (1.22 default → 1.23 only scheme → GC programs removed 1.24).
Green Tea span-based marking: experiment (1.25) → default with AVX-512 scan kernels (1.26) → kernel hardening (1.27). The
nogreenteagcfallback still exists at HEAD.
Allocation fast path. mallocgc split into specialized paths (1.24) → generated size-specialized malloc (on in master during 1.26 but off in go1.26.0 → on in 1.27 → flag removed in 1.28). Alongside it:
- Before 1.18: tcmalloc-style size classes with mcache/mcentral/mheap (1.0), per-P caches (1.2), tiny allocator (1.3) → allocating from mark bits (1.7) → sparse heap arenas, no heap-size limit (1.11) → background scavenger (1.13) → bitmap page allocator (1.14) → lock-free mcentral (1.15) → consistent sharded heap statistics (1.16).
- compiler-directed
freegc(1.26, opt-in); - stack-allocated
make/appendbacking stores (1.25–1.26, withmove2heapfor slices that escape late).
Memory-management APIs. unique (1.23) → public weak and AddCleanup (1.24) → checkfinalizer and parallel cleanup goroutines (1.25). Also Pinner (1.21) and heap-base randomization (1.26).
Concurrency runtime.
- Before 1.18:
- G-M-P scheduler: global-lock M:N (1.0) → per-P run queues (1.1) → lock-free local run queue (1.3) →
GOMAXPROCSdefaults toNumCPU,runnext(1.5). - Preemption: cooperative via
stackguard0(1.2) → signal-based asynchronous preemption (1.14). - Timers: a
timerprocgoroutine → per-P timer buckets (1.10) → timers on Ps (1.14). - Also the runtime-integrated netpoller (1.1),
sync.Pool(1.3),sync.Map(1.9) and static lock ranking (1.15).
- G-M-P scheduler: global-lock M:N (1.0) → per-P run queues (1.1) → lock-free local run queue (1.3) →
- Timers: per-timer lock, per-P heap of
{timer, when}, GC-able timer channels with synchronous semantics (1.23) →asynctimerchanremoved (1.27). - Runtime mutex: first futex redesign reverted (1.23) → “spinbit” mutex behind an experiment (1.24) → only implementation (1.25).
- Container-aware GOMAXPROCS (1.25).
- synctest bubbles: experiment (1.24) → graduated (1.26).
- Coroutines for
iter.Pull(1.22–1.23). - Goroutine-leak profile found by the GC: experiment (1.26) → default (1.27).
Maps and hashing.
- Before 1.18: hashmap with 8-entry buckets, incremental growth and AES hash (1.1) → same-size growth to bound overflow chains (1.8) → open-addressed itab table (1.10); type descriptors shrunk with
nameOff/typeOff(1.7); reflect’s Type/Value redesign (1.0). - Swiss tables: experiment and default (1.24) → old maps deleted (1.26) →
mapsplitgroupKKKKVVVV layout (1.27 experiment, 1.28 default). - Hashing: moved into
internal/runtime/maps(1.27) → wyhash for small keys (1.28). HashTrieMapbacksuniqueandsync.Map(1.24).- ChaCha8 runtime randomness (1.22).
abi.Typeunification (1.21).
Observability.
- Before 1.18: CPU and heap profiling (1.0) → race detector (1.1) → execution tracer v1 (1.5) → pprof protobuf output (1.8) → profile labels (1.9) →
runtime/metrics(1.16). - Execution tracer v2: groundwork (1.20–1.21) → new tracer default (1.22) → v1 deleted (1.23) → flight recorder (1.25) → in-band end-of-generation (1.26).
- Frame-pointer unwinding: tracer (1.21) → block/mutex profilers and stack depth 128 (1.23).
- Runtime-internal lock contention in the mutex profile (1.22 behind a GODEBUG → unlocker blame and GODEBUG removed, 1.25).
- Coverage redesign (1.20).
Stacks and panics.
- Before 1.18:
- Segmented stacks → copied stacks (1.3), the only mechanism from 1.4, with 2 KB minimum stacks → stack barriers, concurrent shrinking, stack objects (1.5–1.12).
- Defer: heap records → stack-allocated defer records (1.13) → open-coded defers (1.14) → go/defer wrapped into argument-less closures (1.17).
- Unwinder rewrite:
gentraceback→ iterator (1.21) → pcvalue cache on the M (1.22). - Defer/panic re-implementation: argless-closure defers (1.18) →
PanicNilError(1.21) → shared defer state machine (1.22) → recover viadeferreturnPC and logical frames (1.25–1.26). - Explicit frame pointers (1.21 onward).
Compiler middle end.
- Before 1.18: escape analysis and the first inliner in C (1.0) → the SSA backend on amd64 (1.7) → SSA on every architecture, with the legacy backend deleted (1.8) → prove reworked around a poset (1.11) → the new escape analysis (1.13) → mid-stack inlining tuned (1.12) and extended to recursion cycles (1.15) → late call expansion (1.16).
- PGO: hot-call inlining (1.20) → interface devirtualization (1.21) → function-value devirtualization (1.22) →
cmd/preprofile(1.23) → Linux perf profiles (1.28). - Inliner: closures strongly favored and an interleaved inline/devirtualize driver (1.24). The heuristic “new inliner” has existed since 1.22 and was never enabled.
- prove: from relations to a dense limit table (1.24) → induction-variable pairs (1.27).
- New passes:
- SCCP, 1.22;
- memcombine/DSE/mem2reg, 1.20 onward;
- known-bits, 1.27;
- LICM, 1.27;
- switch lookup tables, 1.27.
Backends.
- Before 1.18: 6g/8g/5g with reg.c/peep.c registerization → liblink (1.3) →
cmd/internal/objandcmd/asmin Go (1.5) → per-architecture SSA ports (1.7–1.8) → register ABI on amd64 with ABI wrappers (1.17). Ports: ppc64 and arm64 (1.5), mips64 (1.6), wasm (1.11), riscv64 (1.14). - Register ABI ports: arm64 and ppc64 (1.18) → riscv64 (1.19) → loong64 (1.22) → s390x (1.26). 386, arm and mips still use ABI0.
- Microarchitecture levels unlock instructions: GOAMD64 (1.18), GORISCV64 and GOARM64 (1.23).
- Duff’s device replaced by generated loops and the
panicBoundsscheme (1.26). - SIMD: amd64
archsimd(1.26) → NEON, wasm and a portablesimdlayer (1.27) → SVE (1.28). Still behindGOEXPERIMENT=simdat HEAD.
Linker.
- Before 1.18: 5l/6l/8l and
src/cmd/ld→ liblink moves instruction selection into the compilers (1.3) →cmd/linkin Go (1.5) → build modes: c-archive/c-shared/shared (1.5), plugin (1.8) → new object file format and loader (1.14–1.16) → pclntab restructured (1.16). - pclntab without relocations (1.18 → 1.26).
//go:linknamelockdown (1.23 onward).- DWARF 5 (1.25; still off on darwin, ios and aix).
What defines each release
The links go to the release in Part II.
Go 1.0 — Everything up to the
go1tag, 2008–2012. 6g/8g/5g with the C frontendgc, Plan 9-derived C compilers and assemblers. Segmented stacks. A tcmalloc-style allocator with a stop-the-world mark-sweep collector. A global-lock M:N scheduler.hashmap.cand the 2009 and 2011 reflect redesigns. Escape analysis and a first inliner. ELF/Mach-O/PE linkers with DWARF and dead-code elimination.runtime/pprof.Go 1.1 — Per-P scheduler with local run queues. Runtime-integrated network poller. Parallel GC and the first precise heap scanning. 8-entry-bucket hashmap with incremental growth, plus AES hash. Race detector. External (host) linking.
intis 64 bits on amd64. Method values.Go 1.2 — Cooperative preemption via
stackguard0. Funcdata/pcdata and pointer maps; precise stack scanning built but shipped opt-in (precisestack). New in-memory symbol table and pclntab. Three-index slices. Per-P allocator caches andpersistentalloc.Go 1.3 — Contiguous (copied) stacks. Precise stack scanning on by default. Concurrent sweep, tiny allocator, 8K pages. liblink: instruction selection moves from the linkers into the compilers and assemblers, with a new object format.
sync.Pool. NaCl and Solaris.Go 1.4 — Most of the runtime converted from C to Go (scheduler, channels, maps, panics, signals). Stack copying is the only mechanism, with 2 KB minimum stacks. The compiler emits write barriers.
atomic.Value. Android.Go 1.5 — The toolchain is Go: compilers, linker and assemblers translated (
cmd/compile,cmd/link,cmd/asm,cmd/internal/obj), C compilers deleted. Concurrent GC with pacer, assists and stack barriers.GOMAXPROCSdefaults toNumCPU. Execution tracer. Build modes c-archive, c-shared and shared. ppc64 and arm64 ports. Frame-pointer experiment.Go 1.6 — GC pause work: root jobs in workers, decentralised state machine. Hand-written parser replaces yacc. cgo pointer checks (
cgocheck). mips64. PIE and shared libraries on more architectures. MSan. Concurrent-map-misuse detection.Go 1.7 — SSA backend for amd64: CSE, nil-check elimination, prove/BCE, a global register allocator. Mark-bit-based allocation (
allocBits/freeindex). Smaller type descriptors (nameOff/typeOff). Binary export data by default. Linker speed and memory campaign. Frame pointers on by default on amd64.Go 1.8 — SSA is the only backend on every architecture, and the legacy backend is deleted. Hybrid write barrier, so no more STW stack rescans. New
syntaxpackage and noder. Plugins. Cheaper defers and cgo calls. pprof protobuf output. Mutex profile. Same-size map growth.Go 1.9 — Type aliases.
sync.Map.math/bitsintrinsics. Inline-aware tracebacks andCallersFrames. Stack barriers removed. Concurrentruntime.GC(). Profile labels. Write-barrier insertion moved into SSA. Devirtualization. Groundwork for the concurrent backend. Monotonic time.Go 1.10 — Buffered write barrier. Soft and hard heap goals. Per-P timer buckets. Threads can exit and
LockOSThreadnests. DWARF location lists. AVX/AVX2 in the assembler. Open-addressed itab table. Linker code-health refactor and content-based build IDs.Go 1.11 — Sparse heap arenas and no heap-size limit. Indexed export format. Concurrent compilation of all functions. Safepoints almost everywhere, with register liveness maps. Prove pass reworked. WebAssembly port. Darwin syscalls through libc. Trace user annotations. DWARF compression.
internal/bytealg. Debugger call injection.Go 1.12 — Mark termination simplified, page reclaimer, more aggressive scavenger. Stack objects. ABI0/ABIInternal symbol split. Binary export removed;
-lang. AIX port.MADV_FREE. macOSsyscallvia libSystem.Go 1.13 — New escape analysis on by default. Stack-allocated defer records. Background scavenger. Go 2 number literals and signed shift counts. Spec-compliant package init order.
sync.Poolvictim cache.internal/reflectlite.Go 1.14 — Asynchronous (signal-based) preemption. Timers on Ps. Open-coded defers. Bitmap page allocator. riscv64 port. First pieces of the new object file format. Overlapping interfaces.
checkptr.Go 1.15 — The new linker: dev.link’s conversion to the loader is complete and the old object format is deleted. Static lock ranking. Lock-free mcentral. Typed aux and the rulegen rewrite. Small-integer interface conversions without allocation. darwin/386 and darwin/arm removed.
Go 1.16 —
runtime/metrics. Late call expansion. pclntab restructured andsym.Symboldeleted.//go:embed. 387 removed in favour of softfloat.GOOS=ios.MADV_DONTNEEDby default again. Early devirtualization. Frame pointers no longer an experiment.Go 1.17 — Register-based ABI on amd64. The dev.regabi split of the compiler into packages. dev.typeparams: types2,
-G,irgenand first stenciling, all off by default. Pacer extracted intogcControllerState.unsafe.Add/unsafe.Slice. Slice-to-array-pointer conversion. windows/arm64. go/defer wrapped into closures.Go 1.18 — Generics land: types2 plus GC-shape stenciling on the old typechecker. The register ABI spreads to arm64 and ppc64. The pacer is redesigned to count stacks and globals.
GOAMD64levels appear. Defers become argless closures. pclntab starts losing relocations.Go 1.19 — Soft memory limit (
GOMEMLIMIT) and GC CPU limiter. Scavenger index redesign. Jump tables for switches. riscv64 register ABI. linux/loong64 port. Unified IR gains full generics support.Go 1.20 — Unified IR becomes the default frontend with real dictionaries. PGO preview (hot-call inlining). Coverage redesign. Arenas experiment. 1-bit heap bitmap. Pacer smoothing via moving average.
clearis type-checked here but gated to go1.21.Go 1.21 — Inference rewrite plus
min/max/clear. Unified IR is the only frontend.abi.Typeunification. Unwinder iterator replacesgentraceback. PGO devirtualization, and the compiler itself is built with PGO.PanicNilError.Pinner. wasip1. Tracer groundwork and FP unwinding.Go 1.22 — Per-iteration loop variables. Range over int. Range-over-func as an experiment with coroutines. Allocation headers. New execution tracer by default. ChaCha8 randomness. Type-switch and assertion caches. SCCP. loong64 register ABI.
Go 1.23 — Timer rewrite with GC-able synchronous timer channels. Range-over-func ships.
uniqueand internal weak pointers. Linkname lockdown. v1 tracer deleted. Profile stack depth 128. First spinbit attempt (reverted). Stack-slot merging.GOARM64/GORISCV64.Go 1.24 — Swiss-table maps. Spinbit mutex.
HashTrieMap-backedsync.Map.weakandAddCleanup. synctest experiment. Generic type aliases. prove’s limit table. mallocgc specialization. vDSOgetrandom. Closure-favoring inliner.Go 1.25 — Green Tea GC experiment. Container-aware GOMAXPROCS. Flight recorder. Spinbit everywhere. DWARF 5. Core types gone from the checker. Unlocker-blame runtime-lock profiling. Allocation-free interface conversions of constants and composite literals.
Go 1.26 — Green Tea by default. SIMD (
archsimd, amd64) lands behind an experiment. Old maps deleted.move2heapstack-allocated appends.panicBounds. Duff’s device removed. s390x register ABI.runtime/secret, heap-base randomization andfreegcexperiments. Goroutine-leak profile experiment.new(expr). Size-specialized malloc: on in master, off in the release.Go 1.27 — Generic methods. Size-specialized malloc on. Goroutine-leak profile by default.
asynctimerchanandgotypesaliasremoved.mapsplitgroupexperiment. Known-bits and LICM passes. Switch lookup tables. NEON, wasm and portable SIMD. Promoted fields as composite-literal keys.Go 1.28 (dev) —
mapsplitgroupby default. SSA package split intossacompileand others. ARM64 SVE. The size-specialized-malloc flag is removed. Wyhash for small keys. Inlinablerecovercallers. Composite-literal type elision in progress.string(int)restricted. Linux perf profiles accepted for PGO.