Paths not taken
Parts I and II only cover work that landed. This part covers work that was built or designed and then did not land in the form attempted: the request-oriented collector, lock-free channels, a NUMA-aware scheduler, the generics prototypes that came before the shipped design, and many smaller attempts. Each entry says what was tried, how far it got, and why it stopped, citing the record. Where the record gives no reason, the entry says so.
Outcome labels.
- never landed The idea is still absent from Go.
- superseded The problem was later solved by a different design, which is linked.
- partially landed Some pieces made it in, the core did not.
- landed later A later version of the same work landed years after the attempts described here stalled.
- still open The proposal or CL series has had activity in the last year; the entry covers the attempts so far.
- dormant The work is still open (CLs open, or a proposal on hold) but has had no activity for a year or more.
How the efforts were found.
- Gerrit (2015 on). Every never-merged change in the in-scope directories was fetched, including old path names. Changes were dropped when a landed commit carried the same subject or Change-Id, or when they sat on release branches. That left 4,223 CLs: 3,054 abandoned and 1,169 still open. They were grouped mechanically into 781 candidate clusters (1,983 CLs), by shared issue, Gerrit topic, dev branch, or same author and package within 90 days.
- Rietveld (2009–2014). The pre-Gerrit code-review site was searched for CLs by the core runtime and compiler authors (18,874 CLs). This gave 427 in-scope CLs that were never submitted, 364 of which have no landed commit with the same subject.
- Branches. Every dev branch was compared with master. Three kinds were set aside:
- the
dev.boringcryptobranches, which are out of scope; - branches whose content landed as rewritten commits (
dev.typealias,dev.gcfe); - one- and two-commit leftovers (
dev.inline,dev.types,dev.tls,dev.link,dev.simd).
dev.garbage(ROC) anddev.go2go(the generics translator). - the
- Proposals and design docs. The search covered 327 runtime, compiler, linker,
syncandreflectproposals that were never accepted (declined, withdrawn, on hold or open), and the design docs in golang/proposal and the curated Google Docs that no landed thread cites. - Reading. Six Claude Sonnet readers went through all of the above and proposed 225 candidate efforts. Candidates were then:
- merged across sources (for example ROC, which appears on Gerrit, on
dev.garbageand in the README); - checked against the full landed history with fuzzy subject matching. This dropped 33 that had in fact landed under different wording, such as batched write barriers in Go 1.21, or were duplicates.
- merged across sources (for example ROC, which appears on Gerrit, on
Gaps.
- Lost review reasons. Many old CLs were closed in bulk cleanups, for example in 2018, with the bare message “Abandoned”. For those, the reason a CL stopped is not in the record.
- Rietveld coverage. The Rietveld search could not reach every author. Notably, Rick Hudson’s and Dave Cheney’s pre-2015 Rietveld histories, and that of the author of the 2012 precise-GC patch, are missing.
- Outside scope.
- Language proposals with no compiler or runtime prototype, such as
check/handlewithout code. The error-handling entry covers them only becausetryand?had prototypes. - Work done only on mailing lists or in private branches.
- Out-of-scope directories such as
cmd/go.
- Language proposals with no compiler or runtime prototype, such as
- Unreviewed table summaries. The small-attempt tables come from the first-pass reading and were not reviewed entry by entry.
Memory management
Request-oriented collector (ROC)
What was tried. The request oriented collector (ROC) rested on the “request hypothesis”: objects created while serving a request tend to die when the request ends. The design doc “Request Oriented Collector (ROC) Algorithm” (Hudson and Clements, June 2016) tracked each goroutine’s allocations between an initial and a current sweep pointer in the span’s mark bitmap. A write barrier kept the invariant that every “published” object (one reachable from another goroutine or from the shared heap) had its mark bit set, transitively marking the referent before the pointer was installed. When the goroutine exited, resetting the current sweep pointer to the initial one freed all unpublished objects without a GC cycle.
How far it got. Hudson started with “direct bitmap allocation” (CL 15560, October 2015, +2041/-232, lit up with GODEBUG=gctoc=1), which its message called “roughly code complete” but “not debugged so don’t expect to run anything”. From April 2016 the work moved to the dev.garbage branch as a series of small CLs: the publish write barrier (CL 22343), the checkpoint/rollback code (CL 22345), startG/recycleG/publishG (CL 25212, CL 25260, CL 25303), stack-pointer publishing (CL 25304), counters and trace output (CL 33312, CL 33318) and a GODEBUG=gcroc=n switch (CL 33319). The last CL in the record, lazy publish on syscall (CL 37101), dates from February 2017. All were marked “DO NOT REVIEW” or work in progress.
Why it stopped. No CL or review message states a verdict. The reason comes from Hudson’s ISMM 2018 keynote (“Getting to Go”), which he introduced as “our failures”. He said ROC’s write barrier “was always on”, and that publishing a private object meant a transitive walk that “could cause many cache misses”. The RPC benchmark scaled well with ROC enabled, but the compiler benchmarks were “exactly the programs that ROC did not do well at”, with “30, 40, 50% and more slowdowns”, which he called unacceptable because “Go is proud of how fast its compiler is”. On the wider benchmark corpus, “the numbers were uniformly bad and ROC wasn’t going to become a winner.” He added: “It’s true we scaled but we only had 4 to 12 hardware thread system so we couldn’t overcome the write barrier tax.” The CL records show only the later clean-up: Hudson abandoned the accidental master-branch re-uploads on 2017-03-29, and in October 2018 Brad Fitzpatrick closed several of the dev.garbage CLs with the bare message “Abandoned” (others stayed open as work in progress).
Lessons. Hudson’s conclusion was that ROC “was a losing proposition” on 4 to 12 thread machines, because the write barrier tax outweighed the scaling gain. He said that on “128 core systems” the scaling might be a win and “we might come back and revisit this”.
Related threads: Concurrent mark: write barriers and the dev.garbage collector
Non-moving generational GC with card marking
What was tried. After ROC, Hudson tried a non-moving generational collector that kept Go’s latency goals. It used what Hudson called “sticky bits”: an object with its mark/allocation bit set was mature, an unset one was young, and a minor GC promoted a young object by setting its bit when a mature object pointed to it, with a transitive walk. A fast buffered write barrier flushed pointers into a card table when no GC cycle was running. A second variant, from Austin Clements, did card marking without a write barrier: it kept a hash of the mature pointers in each card, so a changed hash marked the card dirty. This exchanged write-barrier cost for hashing cost.
How far it got. The work was Hudson’s scratch CLs, all “DO NOT REVIEW” or “DO NOT SUBMIT” and none merged: heap characterization for a June 2017 design doc (CL 47270, CL 56592), card-marking types (CL 56591), a “generational scratch pad” (CL 65010) and card marking (CL 105357). CL 105367 dropped card marking entirely and scanned the whole mature space. In September 2018 a chain began with CL 137476 and continued with CL 137478, CL 137479 and CL 137482, adding full-versus-generational scheduling and card shards. Clements prototyped card marking in the write barrier (CL 40294) and an inlined version (CL 40295, “This code is awful”). The last CLs only measured young-object mortality (CL 159377) and pointer hashing time (CL 162159).
Why it stopped. In the ISMM 2018 keynote Hudson said that on Go’s own benchmarks “things didn’t go as well”: “The write barrier was fast but it simply wasn’t fast enough,” and it was hard to optimize because with a safepoint at every instruction “there really wasn’t any write barrier that we could elide”. He added that escape analysis keeps young objects on the stack: “It isn’t that the generational hypothesis isn’t true for Go, it’s just that the young objects live and die young on the stack.” He also argued that growing the heap lowers mark cost below a write barrier’s constant cost. For the hash-based card variant he reported “a couple of outliers” on the performance benchmarks. In the review of CL 137482 on 2019-03-15, Hudson wrote that using hashing “to filter out cards that don’t need to scanned is not as effective as we had hoped”. The excerpt does not record a final decision to stop; every CL stayed open.
Lessons. Hudson said generational GC “greatly reduces the long STW times of full GC cycles but it doesn’t necessarily improve throughput”, and Go, having no such stop-the-world problem, had to look at throughput. He also named the write barrier as the obstacle (“Gee wouldn’t this be great if it wasn’t for the write barrier”).
Related threads: Hybrid write barrier and buffered write barrier · Green Tea: span-based marking with vector scan kernels
Allocation-site lifetime prediction and goroutine-local heaps
What was tried. Three separate prototypes tried to use object lifetime or ownership information in the allocator. Dmitry Vyukov’s “allocation site learning” (CL 5290, February 2015) referred to a Google Doc design. Michael Knyszek’s prototype (CL 251060, August 2020) predicted which allocation sites produce short-lived objects, defined as objects that “die before the GC can mark it the first time”, so they could be separated from long-lived ones. Austin Clements’s experiment (CL 501315, June 2023) measured “perfect goroutine-local heaps”: each object was owned by its allocating goroutine, and the GOEXPERIMENT=cgocheck2 write barrier detected when allocations leaked between goroutines.
How far it got. All three were small prototypes marked “DO NOT REVIEW”. CL 5290 was +244/-142. CL 251060 was +380/-33 and was kept in the work-in-progress state. Its message listed its own weaknesses: a dynamic allocation-site index about “~4x binary size”, two-level lookup, and incomplete coverage (maps and reflection were not handled). It said a compiler-generated site index would be much smaller and would avoid the lookup. The predictor ran only while the GC was off, to avoid skew from objects allocated black. CL 501315 was +587/-9. No measurement results appear in the CLs.
Why it stopped. No reason is recorded in the CLs. None of the three has a review message giving a verdict. Russ Cox asked in April 2015 that CL 5290 be labelled “DO NOT REVIEW” to keep it off review dashboards. On 2017-02-24 he abandoned it with the message “Two years old, abandoning to avoid appearing in search results on Gerrit”, a housekeeping closure that states nothing about the idea. CL 251060 and CL 501315 remain open. No design doc, issue or talk discusses results from any of them.
Heap limits before GOMEMLIMIT: SetMaxHeap, GC backpressure, a memory target
- design doc User-configurable memory target (Michael Knyszek)
- proposal #21795 proposal: runtime: memory awareness exposure in runtime like Objective-C `didReceiveMemoryWarning`
- proposal #23044 proposal: runtime: add a mechanism for specifying a minimum target heap size
- proposal #29696 proposal: runtime: add way for applications to respond to GC backpressure
- proposal #37452 proposal: runtime: mechanism for monitoring system memory
- proposal #44309 proposal: runtime/debug: user-configurable memory target
What was tried. Several designs let programs control heap size before GOMEMLIMIT. SetMaxHeap (CL 46751) set a soft limit that used “the lower of the GOGC-computed GC target and the heap limit”. Because the GC could no longer amortize its cost against heap growth, callers had to register for notifications that the collector was under pressure and “shed load”. #23044 asked for the opposite, a minimum heap target. #29696 (opened by josharian as a discussion) asked for a way to respond to GC backpressure. Knyszek’s design doc “User-configurable memory target” (#44309, February 2021) proposed debug.SetMemoryTarget, a hint of total Go memory use that the pacer would turn into a heap goal.
How far it got. Clements’s SetMaxHeap CL (June 2017, +459/-35) went through about 22 patch sets and was rebased onto Go 1.14 in April 2020, but it kept “DO NOT SUBMIT” and its stated purpose was “to get some experience with the API and figure out if this is even a reasonable primitive”. It absorbed Knyszek’s fixes, including the lock-order deadlock fix CL 156917. The memory-target proposal drew support from one reviewer but was never implemented.
Why it stopped. On #23044, Russ Cox closed the minimum-heap request on 2020-02-26 in favour of #29696. Cox later explained: “if we evaluate every single API in isolation, what you end up with is a LOT of APIs, each of them justifiable on its own but collectively not.” Knyszek wrote that a fixed minimum “unfortunately not as simple” because it causes “significant issues with the GC pacer”. On #29696, Cox wrote in June 2020 that SetMaxHeap was built for backpressure, but “the people who have tried it and used it are all not watching for or responding to backpressure”. That issue is still open and on hold. Knyszek postponed the memory-target doc after the pacer redesign slipped (“I definitely need to push this back”, 2021-04-15), then withdrew it on 2021-11-10: “Given that #48409 has been accepted, and I haven’t received any feedback … that this would better suit some use-case, I’m going to withdraw this proposal.” Clements’s reply on #21795 (2017) noted that Linux has no memory-warning API; that issue was closed by a bot after a timeout.
What landed instead. The soft memory limit (#48409) shipped as GOMEMLIMIT and debug.SetMemoryLimit: see Soft memory limit and GC CPU limiter.
Related threads: Soft memory limit and GC CPU limiter
Arena API prototypes, 2017–2022
What was tried. A series of proof-of-concept arenas gave programs a way to allocate groups of objects with a shared lifetime and free them at once. Austin Clements’s first version (CL 63651, September 2017) was “a simple, type-safe arena allocation API” meant for “deserializers of irregular structures, which often have to construct complex and irregular object graphs, all with very similar lifetimes”. It was built in the runtime and reflect packages. Later versions kept that shape and varied the runtime support.
How far it got. Keith Randall’s version (CL 114002, May 2018, +264) was based on Clements’s. A CL titled “Dan’s arena” (CL 224577, March 2020, +941/-702) followed. Dan Scales’s amd64 prototype (CL 387975, February 2022, +1228/-38) was “a sample implementation of arenas” that “may have various issues and limitations”. Michael Knyszek then wrote CL 390035 (fault, rather than unmap, freed arena address space) and CL 390036 (recycle arena address space once no pointers remain), and CL 423358 (August 2022, +1041/-60), an arena package behind GOEXPERIMENT=arena, “based on one by” Scales. Each earlier CL was labelled “DO NOT REVIEW”; the first two were described as “not documented or fully tested”.
Why it stopped. No reason is recorded in the CLs for the early prototypes; they were left open as experiments. CL 423358 was closed by Knyszek on 2022-08-12 with the bare message “Abandoned”, and the record gives no explanation for it. Open prototypes CL 63651 and CL 114002 show no verdict, only a 2018 review from Patrik Nyblom about getting CL 63651 to run.
What landed instead. An arena package landed as an experiment, not as these CLs: see Memory arenas experiment.
Related threads: Memory arenas experiment
Asynchronous sweeping for small allocations
What was tried. Clements’s CL 138962 restructured sweeping so object reclaiming left the allocation critical path. Before it, taking a span from an mcentral could sweep “an unbounded number of spans” to find free space, which put sweeping on the allocation path, caused convoys across Ps, contended on mcentral locks and was non-preemptible; the message cited “5+ second effective STWs for dense 100+ GB heaps”. The CL had allocation do only the proportional sweep needed to finish before the next mark, allocate from known-free space, and otherwise grow the heap. It also made span ownership explicit and removed the “backwards ‘empty/nonempty’ terminology” in mcentral.
How far it got. One CL (+176/-156, master branch) was uploaded in October 2018 and revised through at least November; it was the first of a stack of CLs. Reviewers were Dmitry Vyukov and Rick Hudson.
Why it stopped. Vyukov raised a concern on 2018-10-06: if full spans are not swept before allocating, it “can also easily triple or even quadruple heap size with GOGC=100, and this in turn means killed jobs”. Clements replied on 2018-11-09 that the CL still sweeps full spans; “the effect of this CL is just that we’re less willing to spend a really long time sweeping and more willing to grow the heap instead”. Hudson asked for comments on the locking protocols (2018-11-13). The record does not show the concern being resolved. The CL was abandoned on 2023-06-22 by Dmitri Shuralyov in a bulk clean-up of the stack, whose tip CL 148898 had been closed with the rationale “Cleaning out very old changes. If the change is still relevant, please submit a new CL for re-review.” No technical reason is recorded.
Related threads: Concurrent sweep and proportional sweep · Sweeper termination and pacing
Scavenger and heap-lock experiments before the page allocator rewrite
What was tried. Between October 2018 and December 2019 Knyszek tried many ways to make scavenging (returning free memory to the OS) prompt without hurting allocation latency, mostly by shrinking how long the global heap lock was held. Examples: marking freshly mapped memory as scavenged (CL 140197), eagerly scavenging by a global debt budget (CL 141937), dropping the heap lock around scavenging syscalls (CL 142957, CL 172478, CL 185617), scavenging least-recently-used spans (CL 160498), a pairing heap (CL 160497), tracking spans larger than a huge page separately (CL 170859), and unscavenging without the heap lock (CL 184442). It also included reintroducing mSpanLists for small spans (CL 151538, fixing #28479).
How far it got. Thirteen CLs, most of them +10 to +220 lines, were uploaded against #16930, #14045 and #30333. CL 151538 had two Code-Review+2 votes before they were removed on 2019-04-05. None of the series landed in this form.
Why it stopped. The abandon messages give short reasons. CL 140197: “This potentially breaks the property of HeapReleased that it is the lower bound of non-backed pages.” CL 141937: “Not necessary.” CL 142957: “There are better ways to do this.” CL 160498: “No longer necessary.” CL 172478: “Stale and not relevant.” CL 151538: “Should be unnecessary for the most part.” CL 170859: “Superceded by 170860.” CL 176957 (counting the scavenger as idle for deadlock detection) was abandoned as “Superseded by golang.org/cl/177097” after Knyszek wrote on #31966 that Ian Lance Taylor “noted offline that the deadlock detector already doesn’t work for many people in many situations”, so it was better to mark newly mapped memory as scavenged. CL 207999 was abandoned on 2019-11-21 with no message. CL 212639, “disable the scavenger”, was an open “DO NOT SUBMIT” scratch change.
What landed instead. The heap-lock problem was removed by the bitmap page allocator, and the scavenger itself was reworked in Scavenger redesign.
Related threads: The bitmap page allocator · Scavenger redesign
Persistent-memory Go (go-pmem)
What was tried. The go-pmem proposal (#43810, design doc “43810-go-pmem”, January 2021) added native support for persistent memory (byte-addressable, durable memory such as Intel Optane) to a fork of Go 1.15. It had a garbage-collected persistent heap, with mspan gaining a memtype field and separate mcache/mcentral/mheap metadata for persistent and volatile spans. Allocation used new keywords pnew and pmake, and a txn block marked crash-consistent updates, which the compiler instrumented through a new SSA pass. Allocator and GC metadata were stored in the persistent arenas so the heap could be recovered after a restart.
How far it got. The authors had a working implementation (jerrinsg/go-pmem, described in a 2020 USENIX ATC paper) and reported that a Redis built with it offered “more than 5x throughput compared to Redis running on NVMe SSD”. Only the proposal was submitted to Go; no CLs against the Go tree are in the record. The design doc itself listed downsides, including the persistent metadata layout being tied to the allocator’s data structures.
Why it stopped. The proposal was declined. Early comments objected to the new keywords. Ian Lance Taylor asked whether a single root was sufficient, what a failing transaction means, and what stops a persistent object from holding a pointer to volatile memory. Other participants argued that a file-like API in a package would do, since on Linux persistent memory is a mapped file. On 2021-02-03 Russ Cox wrote that “we should probably decline this full proposal but could easily add the MAP_SYNC flag and perhaps even the ‘flush memory region’ operation”, and marked it a likely decline. After the authors replied that direct load/store access to a DAX mapping is what gives the performance, Cox closed it on 2021-02-10: “No change in consensus, so declined.” He invited separate proposals for MAP_SYNC and a flush API.
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
Runtime string interning |
2013-03 → 2019-01 | josharian | superseded | #5160 #61063 | Weak pointers and the unique package |
|
Malloc experiments of 2013–14: object headers, transfer cache, size classes |
2013-05 → 2014-08 | Dmitry Vyukov | partially landed | CL 9372043 CL 9640043 CL 9745043 CL 102470048 | Size classes, tiny allocator and mcache/mcentral, Heap bitmap rewrite and allocation headers |
|
Custom write barriers for small objects (again) |
2015-04 → 2016-10 | Russ Cox | never landed | CL 9322 | Hybrid write barrier and buffered write barrier, Write barriers, precise stacks and liveness (compiler side) |
|
GC mark-phase micro-experiments (checkmark bits, mark workers, bulk greying) |
2016-01 → 2016-10 | Austin Clements | never landed | CL 24171 CL 24172 CL 24704 CL 24706 #17503 | GC pacer: trigger controller, assists and background utilisation, Mark work distribution: parfor, work buffers, gcWork and root jobs |
|
Garbage-collecting goroutines blocked forever |
2017-03 → 2017-04 | partially landed | #19702 | Goroutine leak profile (GC-based leak detection) | |
|
GC scan-loop and contention micro-optimisations |
2017-09 → 2024-09 | Michael Knyszek, Ilya Tocar | never landed | CL 407509 CL 407735 CL 567236 CL 525435 | Mark work distribution: parfor, work buffers, gcWork and root jobs, Mark worker scheduling and GC work queues |
|
Page size 64 KiB and huge-page backing experiments |
2019-08 → 2023-04 | Michael Knyszek | never landed | CL 190617 CL 459786 CL 459787 CL 467456 | Transparent huge page management, The bitmap page allocator |
|
Arm MTE (Memory Tagging Extension) support in the Go runtime |
2023-03 → open | dormant | #59090 | memmove / memclr / memequal / internal/bytealg assembly optimisation | |
|
Pointer-target GC and removing GC programs |
2024-02 → 2024-09 | Keith Randall | never landed | CL 565737 CL 565736 CL 605756 CL 605001 | Heap bitmap rewrite and allocation headers |
|
Adopt externally-allocated off-heap memory in the runtime |
2024-11 → open | mknyszek | never landed | #70224 |
Scheduler and concurrency
Lock-free channels and finer-grained select locking
- design doc Go channels on steroids
- discussion golang-dev: update on "lock-free channels"
- tracking issue #8899 runtime: lock-free channels
What was tried. Dmitry Vyukov redesigned channels around lock-free fast paths. In his January 2014 design doc “Go channels on steroids”, buffered channels became a ring buffer whose send and receive positions (sendx/recvx) are advanced by CAS, with a per-element “lap” counter telling producers and consumers whether a slot is ready; the mutex remained only for wait queues. Select stopped locking all channels at once: it polled channels one by one, then enqueued itself on each and rechecked, using a select-wide CAS word so only one partner could complete it. In March 2015 he separately tried “fine-grained locking in select” that never holds more than one channel lock, removing lock ordering and channel sorting.
How far it got. Vyukov posted prototypes from 2011 (CL 4841045, “faster channels and selects”) and three lock-free-chans CLs in August 2013, the main one being CL 12544043. Its message reported, for example, SelectUncontended 232 to 153 ns (-34%) and ChanContended-2 295 to 114 ns, but also ChanContended-8 311 to 570 ns (+83%). CL 74180043 (“faster sync chans”) followed in March 2014. The 2015 select rewrite, CL 7570 (+759/-341), reported SelectUncontended -25.7% and SelectSyncContended-16 -78%. The design doc is tracked in #8899.
Why it stopped. For CL 7570, Keith Randall gave +1 but wrote that “there are a ton of subtle things going on in this code, and it makes me nervous”. On 2015-04-27 Russ Cox gave Code-Review-2: “I don’t believe we can get this into Go 1.5”, saying the code structure did not support fine-grained select locking well and that the code needed to be factored so the higher-level algorithms were clearer. Vyukov marked it Abandoned two days later. For the 2013 lock-free CL, Vyukov wrote on #8899 (2017-04-07) that it “was constantly deprioritized over other runtime changes, thus constantly get outdated and I wasn’t able to keep up updating it.” He also said the algorithm lacked the fairness properties of the then-current code (CL 16740), as a commenter recalled him saying in 2017. In 2016 a commenter noted the CL had “gone stale” because of runtime changes. The issue is still open. No Go team member’s rejection of the 2013 design is recorded.
Lessons. The design doc itself listed “make channels completely lock-free” as a non-goal because “this would significantly complicate implementation and make it slower for common cases”.
Related threads: Channels and select
Dmitry Vyukov’s 2013–14 scheduler experiments
- design doc Go Preemptive Scheduler Design Doc
What was tried. In 2013-14 Dmitry Vyukov prototyped several scheduler changes. The “Go Preemptive Scheduler Design Doc” (2013-05-15) proposed that sysmon poison g->stackguard when a P ran the same goroutine too long, so the next function-entry stack check would enter morestack and yield; the GC would do the same for all running goroutines at stop-the-world. Other CLs tried to avoid creating threads, to let external threads wake goroutines, and to merge timers with the netpoller.
How far it got. The prototype was CL 9136045 (May 2013). The doc reported no slowdown on the Go1 benchmarks and GC stop-the-world times falling from roughly 300-940 ms to 72-126 ms in a test program, and said “Points 1-9 are implemented before Go1.2.” Other CLs: CL 8891044 (“do not create threads”, April 2013), CL 49020044 (“allow external threads to wake up goroutines”, January 2014, with cgo callback benchmarks around 1 ms/op and hundreds of threads), and CL 115040043 (“merge timers and netpoll”, July 2014). All except the preemption work remained open in Rietveld.
Why it stopped. For the preemption design, the doc itself left points 10 and 11 unimplemented: it said checks on function entry “should be sufficient for most practical cases, so it’s unclear whether checks on back edges are required.” No source states why the other CLs were not pursued. CL 115040043 listed its own open problems: the “deadlock detector is disabled once any timer is created (there is always a thread in netpoll)” and a “timer can be deleted once in ready queue (leads to nil deref)”. The CLs carry no review decisions and no abandonment message.
What landed instead. Function-entry preemption shipped as the cooperative scheme, later replaced for tight loops by signal-based preemption; see Preemption. Timers were eventually moved onto Ps, see Timers.
Related threads: Preemption: cooperative stack-check preemption (1.2) to signal-based asynchronous preemption (1.14) · Timers: timerproc goroutine → per-bucket heaps → timers on Ps
NUMA-aware scheduler
- design doc NUMA-aware scheduler for Go
What was tried. Dmitry Vyukov’s September 2014 design doc “NUMA-aware scheduler for Go” proposed making the runtime topology-aware. The heap, global run queue, stack caches and pool of Ms would be partitioned per NUMA node; Ms would be hard-bound to nodes and Ps strictly partitioned between nodes, with a lastm field to keep P-to-M affinity. Work stealing would prefer the same node (per-node queue, then Ps on the node, then remote nodes), local scheduling would become mostly LIFO with an occasional pop from the other end for fairness, and netpoll and timers would become per-P so stolen goroutines return to their home P. GC workers and gray queues would also be per node.
How far it got. Only groundwork was written: CL 5461, “runtime: system topology discovery” (+1548/-12, February to April 2015), whose entire message is “DO NOT REVIEW”. It stayed open. The doc describes the design as an infrastructure proposal to be refined “once we implement it and gather more information”, and no scheduler, allocator or GC changes were posted.
Why it stopped. No reason is recorded in the CL or elsewhere: CL 5461 has no review messages and no abandonment, and the design was never implemented. The only stated reservations are in the doc’s own “Risks” section. Several processes could pick the same NUMA node and oversubscribe it while other nodes sat idle, and a large structure allocated on one node and used by all goroutines would make that node’s memory bus a bottleneck.
Lessons. Vyukov’s doc warned that “even this relatively modest affinity scheme has potential to degrade an application performance”, and that benefits “significantly diminish” if affinity fails at any level of the core, M, P, G and memory chain.
Related threads: The G-M-P scheduler: from global-lock M:N to per-P run queues, spinning Ms and work stealing
Per-CPU sharded values: sync.Cache, distributed RWMutex, percpu.Sharded
- design doc Proposal: percpu.Sharded, an API for reducing cache contention
- proposal #18590 proposal: runtime: expose current thread id or processor id
- proposal #18802 proposal: sync: support for sharded values
- proposal #68952 proposal: runtime/local: new package to provide transparent access to Per-P Local Storage
What was tried. Several attempts aimed to remove cache-line contention on write-mostly shared state by keeping one copy per P. Dmitry Vyukov’s sync.Cache and sync.Counter (2011, 2013) held per-P caches of interchangeable objects and per-P counters; his distributed RWMutex used one reader slot per P. Ian Lance Taylor changed reflect’s ptrMap into a distributed map. In 2020 Sanjay Menakuru built an RWMutex that starts with a local reader count and, after seeing contention, switches to a table of per-P read counts (idea from Folly’s SharedMutex) on a reusable procLocal abstraction extracted from sync.Pool. Proposal #18802 asked for a percpu.Sharded/sync.Sharded type, and #68952 for a runtime/local package of per-P storage.
How far it got. CL 10299043 reported BenchmarkCache-4 375 to 12 ns against a chan-based cache. CL 33411 was abandoned in December 2016. The RWMutex series (CL 215361, CL 215362, CL 215364, CL 215360, CL 215359, January 2020, all marked “DO NOT REVIEW”) responded to #17973. #18802 remains open; on 2026-08-13 cagedmantis wrote that over the past couple of months they had explored implementations and APIs and proposed a generic NewSharded with a Merger interface, which Ian Lance Taylor suggested replacing with an explicit merge function.
Why it stopped. No reason is recorded for the sync.Cache CLs, which remain open drafts; Vyukov called one “a draft CL”. On CL 33411, Russ Cox gave Code-Review-2 because it “changes the cost of a sequence of n PtrTo calls from O(n) to O(n^2)”; Taylor abandoned it with “bcmills has a better approach anyhow.” On #17973, Menakuru wrote on 2020-02-13 that RWMutex also had a size problem (40% of a cache line), that tackling it first made more sense, and that he intended to return once #37142 was resolved; no later work appears. For the thread-ID alternative, Cox closed #18590 on 2017-02-13: “The valid uses of this function are far outweighed by the buggy invalid uses”, preferring “a few higher-level APIs that are easier to use correctly, like the ShardedValue proposal”. As of the latest comments, no sharded-value API has shipped.
Lessons. Cox’s stated trade-off was that one low-level per-P identifier API would be “very hard to use correctly”, so the project preferred higher-level types.
Related threads: sync.Pool · sync.Mutex, RWMutex, WaitGroup, Cond, Once · sync package API evolution
Loop preemption by compiler-inserted checks and page faults
What was tried. Before Go 1.14, goroutines were preemptible only at function calls, so a tight loop could stall stop-the-world (#10958). Ian Lance Taylor suggested a loop counter that calls a preempt check every 256 iterations; Austin Clements suggested a compare of the preempt flag with loop unrolling. David Chase’s CL 33093 inserted rescheduling checks on every reducible loop backedge under GOEXPERIMENT=preemptibleloops. Austin Clements’s October 2017 series instead unmapped a “loop preemption page” so that a load in each loop faulted, and the SIGSEGV handler redirected the loop to a preemption path.
How far it got. CL 33093 measured geomean +5% on amd64 (RevComp +45%, MandelBrot +20%) and +4% on ppc64; it was meant for Go 1.8 as a diagnostic tool after #17831. The fault-based series (CL 68011 to CL 68020, +164 lines for CL 68016) covered stop-the-world only, with Windows indirection, a GDB signal workaround, and later forEachP and stack-scan preemption. Chase also tried loop unrolling (CL 40996), a fault-branch cost experiment (CL 43050) and loop rotation (CL 44016, CL 47512) to cut the check cost. In 2019 Clements wrote CLs making preemption requests robust and prompt (CL 172284, CL 172984, CL 172991).
Why it stopped. Chase abandoned CL 33093 on 2017-02-23: “Obsolete, replaced with better CLs”. For CL 40996 he wrote that it “does not appear to generally be worth doing in its present form”. The 2019 CLs were abandoned on 2020-01-08 “in favor of golang.org/cl/201762 series”. On 2022-06-14 Clements abandoned the fault-based series with: “This was an old approach to preemption that we’ve long since abandoned.” During review, Alessandro Arzilli had warned that lldb and delve on darwin could not resume from the SIGSEGV. On 2020-01-02 Clements closed #10958 because “tight loops are preemptible now”.
What landed instead. Signal-based asynchronous preemption in Go 1.14; see Preemption and Asynchronous preemption.
Related threads: Preemption: cooperative stack-check preemption (1.2) to signal-based asynchronous preemption (1.14) · Asynchronous preemption: signal plumbing, injected calls and per-arch support
Concurrent and global timer heaps (2024)
What was tried. During the 2024 timer rewrite, Russ Cox tried two alternatives to the per-P timer heaps: a concurrent timer heap (CL 570278) and a single global timers heap (CL 570655). Austin Clements separately wrote a binary log of timer operations and an analyzer that could replay them in simulations of alternate timer structures, including a lock-free, path-compressed radix tree he called a “critrad tree” (CL 575355).
How far it got. Cox posted CL 570278 (+545/-875) and CL 570655 (+25/-136) on 2024-03-11 and abandoned both the same day, titling each “terrible!” and stating he pushed the first “just to save the code” as “a negative result to be sure”. CL 575355 (+2921, linux/amd64 only, marked WIP) was uploaded 2024-03-30 and last updated 2024-05-03; it stayed open.
Why it stopped. On CL 570278, Cox wrote: “This approach is terrible. Tons of contention on ts.lock(1).” On CL 570655 he wrote only “Another experiment with a negative result.” No measurements are in the CL messages. No reason is recorded for CL 575355 not landing: it has no review messages and no abandonment note, and the commit message describes it as tooling and simulation.
Related threads: Timer rewrite: per-P timer heap, timer.lock, and GC-able synchronous timer channels
Deeper-sleep futex mutex
What was tried. The runtime’s futex mutex was tri-state (unlocked, locked, sleeping). Rhys Hiltner found that with many threads contending for a fast critical section, every unlock found the “sleeping” state and woke a thread even if others were already spinning, wasting CPU and collapsing performance on one cache line (#68578). His CLs turned the states into nearly orthogonal bits and added a “spinning” bit, so that arriving threads could sleep immediately and unlock could skip the wake syscall. To bound the resulting starvation risk he planned a waiter list, and one version merged the futex and semaphore lock implementations.
How far it got. CL 601597 (+485/-120, July to October 2024) implemented the bits version; CL 622997 (+663/-7, October 2024) was the “unify lock2, allow deep sleep, wake tail” version. Hiltner also posted microbenchmark experiments: CL 629416 (CAS-only slow path) and CL 629417 (shorter procyield) were marked DO NOT SUBMIT, and their messages said the MutexCapture benchmark showed throughput costs, e.g. about +106% to +126% ns/op for faster spinning at 2-6 threads. CL 623255 tested removing osyield (#69268). Earlier, CL 588856 and CL 588857 tried cheaper sampling for the mutex contention profile.
Why it stopped. Hiltner abandoned CL 622997 on 2026-09-03 with “Merged CL 620435 instead”. He abandoned CL 588856 and CL 588857 on 2026-08-27 as “For #67585, which was fixed instead by a revert”. #67585 was a performance regression traced to the mutex-profiling CL stack, which was reverted in May 2024. No reason is recorded for CL 601597, which stayed open after Commit-Queue toggles in October 2024.
What landed instead. The “spinbit” mutex, see Runtime mutex. A March 2025 CL (#68578) committed to the spinbitmutex GOEXPERIMENT, and Hiltner wrote in December 2025 that the one runtime mutex not using it was sched.lock.
Related threads: Runtime mutex: tri-state futex/sema locks → “spinbit” mutex
Detecting coroutines in channel code
What was tried. Russ Cox’s experiment made the compiler and runtime detect coroutine-style use of channels, where two goroutines alternate strictly through channel send and receive, and hand off directly from one goroutine to the other instead of going through the scheduler.
How far it got. One CL, CL 515255 (+221/-27, 2023-08-02, work in progress), titled “detect coroutine behavior in channels” and marked “DO NOT REVIEW”. It was never merged and has no review messages.
Why it stopped. The CL message gives the reason: “This was an interesting experiment but only produced a 2X speedup in true coroutine behavior. We can do better with explicit API. Posting here for possible future reference.”
What landed instead. Explicit coroutine support in the runtime for iter.Pull; see Coroutines.
Lessons. Cox’s conclusion in the CL message was that an explicit API beat inferring coroutines from channel behavior.
Related threads: Coroutines (iter.Pull runtime support)
Main-thread APIs: RunWithMainThreadOnly to mainthread.Do
- proposal #14163 proposal: runtime: add RunWithMainThreadOnly
- proposal #23160 proposal: runtime: add ForEachOSThread and LockThreadAndDescendentsExclusive
- proposal #49848 proposal: runtime: allow N goroutines to be simultaneously locked to the same OS thread
- proposal #64755 proposal: runtime: add LockMainOSThread to take control of the main thread
- proposal #70089 proposal: runtime/mainthread: add mainthread.Do for mediating access to the main thread
What was tried. A series of proposals asked for a way to run code on the process’s main OS thread, or on every thread. #14163 (Richard Gooch, 2016) proposed runtime.RunWithMainThreadOnly for thread-scoped system calls such as setuid and setns. #23160 proposed ForEachOSThread and LockThreadAndDescendentsExclusive. #49848 proposed locking N goroutines to one OS thread. For GUI toolkits (AppKit, UIKit) and Windows services, Elias Naur proposed runtime.LockMainOSThread (#64755, December 2023), then a mainthread.Do(f) callback API (#70089, October 2024).
How far it got. Only proposals and discussion exist; there are no CLs. Two were declined, one was retracted, and #70089 was still open in January 2026.
Why it stopped. Russ Cox closed #14163 on 2016-03-09: “It’s a workaround for the problem that certain things in package syscall don’t work. We should fix those instead”, adding that RunWithMainThreadOnly and LockOSThread are “fundamentally incompatible”. On #23160 he wrote “There’s no way to implement ForEachOSThread. What if the thread is off doing something that can’t be interrupted?” In that issue Ian Lance Taylor said the runtime would not run arbitrary code with preemption disabled, calling that “a footgun”. For #49848, Cox wrote that “there are significant semantic problems with this”, and it was declined on 2021-12-15 as “no change in consensus”. On #64755, Austin Clements preferred the approach in principle but noted that “we don’t currently have any mechanism to say” a computation must run on a specific thread; Naur retracted it on 2024-05-29 in favor of #67694. Whether #70089 should panic or return an error when the runtime does not own the main thread was still being argued in late 2025 and January 2026; no decision is recorded.
Related threads: Goroutine and M lifecycle: caches, ids, LockOSThread, Goexit, cgo extra Ms, deadlock detection
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
Synthetic / fake time in the runtime |
2012-12 → 2013-04 | Dmitry Vyukov, Andrew Gerrand | superseded | CL 8795047 CL 6892048 | testing/synctest bubbles |
|
Reduce scheduling overhead on large ppc64 systems |
2016-07 → 2018-07 | bcbrock | never landed | #16476 | The G-M-P scheduler: from global-lock M:N to per-P run queues, spinning Ms and work stealing |
|
SchedStats API |
2017-03 → 2017-06 | Austin Clements | never landed | CL 38180 #15490 | Scheduler diagnostics: schedtrace, GODEBUG, execution tracer, block/mutex profiles |
|
Remove sync.Cond |
2017-07 → 2023-07 | never landed | #21165 | sync package API evolution | |
|
RWMutex upgrade/downgrade |
2018-01 → 2021-02 | never landed | #23513 #38891 | sync.Mutex, RWMutex, WaitGroup, Cond, Once | |
|
sync.Pool alternatives |
2018-02 → 2019-08 | Carlo Alberto Ferraris | never landed | CL 95579 CL 100036 CL 162919 #22950 | sync.Pool |
|
Runtime lock-order logging |
2019-08 → 2021-04 | Austin Clements | partially landed | CL 192704 | Runtime lock ranking (static lock order checking), Runtime lock ranking |
Data structures and type metadata
Hash-map alternatives before Swiss tables
What was tried. Several attempts to speed up or replace the bucketed hash map before Swiss tables. In 2013 the ideas were a two-stage map (hash value to a dense range of cell IDs, then cell IDs to key/value pairs in a growable array), a cached last-computed hash, parallel hashing of large keys, and running map operations on the g0 stack. In 2018 the idea was to compare all eight tophash bytes of a bucket at once, with IndexByte or SWAR bit tricks. Later experiments covered multiple map implementations behind one runtime type and lowering map[byte]struct{} to a bitset.
How far it got. The 2013 Rietveld CLs (CL 7304099, CL 12119043, CL 8160043, CL 23250043, CL 13603044) were proofs of concept or experiments; the first claimed lookup “~20% faster” and insert “about 50% faster”. In 2018 a contributor’s CL 115616 used IndexByte in mapassign_fast32, and Keith Randall wrote CL 115895 and CL 118357/CL 118358 (+351/-258) using bit-parallel matching. In 2019 Josh Bleecher Snyder posted a CL 170280 “toy specialized map implementation”; in April 2024 Jorropo posted the WIP bitset CL CL 580575.
Why it stopped. The record gives per-CL reasons, not one verdict. CL 8160043 was labelled “Proof of concept. NOT FOR SUBMISSION”, and its own table shows string hashing up to 12.5% slower when the cache did not trigger. CL 23250043, running map operations on the g0 stack, measured “an overhead of about 3-6ns per call” (BenchmarkMegEmptyMap +153%). For CL 118358 Randall wrote “I’m not wowed by the performance. It’s a mixed bag”: about 30% slower when the key is first in the bucket, better only sporadically when last, with assign and delete “more solidly better”. On 2018-10-31 he abandoned CL 115895, CL 118357 and CL 118358 with “Went with tombstone mechanism instead”. CL 115616 was abandoned in favour of CL 118358. The 2019 toy was abandoned as “for discussion only”; its message names the sticking point that all map implementations “must be the same size and look the same to the GC”. CL 580575 says it is “bugged … and unfinished” and has no further activity. On #19495, Randall said a special-case map for dense integer keys would be “a lot of complexity to add for a speedup in a fairly special case”.
What landed instead. Open-addressing Swiss-table maps replaced the bucket design, see Swiss-table maps; the bucket design that these experiments tried to speed up is described in Hash map core.
Lessons. Randall’s stated trade-off on #19495 was that a specialised map implementation adds a lot of complexity for a speedup in a fairly special case.
Related threads: Hash map core: from the first hashmap.c to the 8-entry bucket design · Swiss-table maps (GOEXPERIMENT=swissmap → only implementation)
A two-word reflect.Value
What was tried. Changing the size of reflect.Value. In CL 155530044 (“reflect: shorten Value to 2 words”, 2014-10) the Value was cut to two words. Method values were not yet implemented; the message planned to represent them as a pointer to a struct holding the method index and the original Value, so that Method would have to allocate, “but that’s not too big a loss”.
How far it got. There are four Rietveld CLs, all still open: CL 9207048 “reflect: new slice/string interface” (2013-05), CL 52670045 “use interface{} in Value to get back to 3 word Values” (2014-01), CL 119050043 “shrink reflect.Value back to 3 words” (2014-07), and the 2-word experiment of 2014-10. The 2-word CL described itself as “an experiment” and measured it as “more compact but a little slower”: BenchmarkGobDecode 0.98x, GobEncode 0.97x, JSONEncode 0.96x, JSONDecode 0.96x, Template 1.03x. The other three have titles only, with no message body or review.
Why it stopped. No reason is recorded in the CLs, and no issue is attached. The only evidence is the benchmark table in CL 155530044, which shows the 2-word Value slower on four of five benchmarks, and the unfinished method-value design. The CLs were never merged or abandoned with a comment.
Related threads: reflect redesign: from string parsing to Type/Value structs
GOEXPERIMENT=largeiface
What was tried. #45494 proposed GOEXPERIMENT=largeiface: add a two-word sdat [2]uintptr scalar payload to runtime.iface and runtime.eface, so small non-pointer values could be stored directly in the interface instead of being allocated on the heap. The proposal’s motivation was that “storing scalar (non-pointer) values into any interface-typed variable forces the value to be stored indirectly, usually allocated on the heap”.
How far it got. The issue was opened in April 2021. Ian Lance Taylor took it out of the proposal process, as a decision for the runtime and compiler team, and Matthew Dempsky called it “a pretty involved experiment, touching all of {cmd/compile, runtime, reflect}”. In August 2021 the author uploaded a compiler-side CL (343070, cited in the issue) and the runtime half, CL 343071 (+2204/-787), which introduced leface/liface types and alternately named entry points. The message says runtime tests passed with the experiment disabled, and that the runtime “passes type checking with it enabled, but does not currently link due to the different implicit symbols”.
Why it stopped. No reason is recorded in the CL or the issue. CL 343071 is marked “DO NOT SUBMIT” and was closed by its author on 2024-03-13 with the bare message “Abandoned”. The issue is still open. Concerns raised at the start, not as a verdict: in the discussion on the issue it was said that the proposal might be declined “primarily because of the amount of work it will be to implement and maintain”, and Josh Bleecher Snyder asked how structs such as struct{a [10]byte; p *int; b [2]byte} and [2]int64 would be handled (re-packing versus reordering fields).
Related threads: Interface values, itabs and type assertions · Interface conversion: itabs, type switch and assertion caches
Alternative slice, string and interface layouts
What was tried. Three families of experiments on how slices, strings and interfaces are represented. Michael Munday (2019) represented slices in SSA as (ptr, len, cap-len) and replaced OpSlicemask with a branch, so that pointer-only reslicing in a loop only updates pointer and length. David Chase (2022-23) swapped the len and cap fields in memory (GOEXPERIMENT=swaplencap) and raised the alignment of slices, strings and interfaces to 16 bytes. Keith Randall (2023) used 16-byte loads and stores for strings, interfaces and slice ptr+cap, and tried checking len <= cap on every load.
How far it got. Munday’s CL 170125 (+342/-153) and CL 168117 were WIP; the first listed benchmarks among its TODOs. Chase’s CL 456240 (+879/-141) passed GOEXPERIMENT=swaplencap ./run.bash with a patched swig, and he reported that Minio and part of Kubernetes worked after changes to third-party code (protobuf, swig, reflect2, goldmark, easyjson); CL 470300 and CL 472677 added alignment. Randall’s CL 461678, CL 481095 and CL 481215 were WIP experiments.
Why it stopped. No source states a reason. Munday abandoned both 2019 CLs on 2020-11-06 with the bare message “Abandoned”. The 2022-23 CLs remain open and flagged “DO NOT SUBMIT”; the record contains only their limitations. CL 456240 “works” only on amd64 and “works much worse on 32-bit?/not-register-abi?”. CL 472677 “still has a bad day in the race detector, unclear if the bug is real or not”, and CL 470300 says race tests “fail for lack of properly aligned stacks”. CL 461678 lists that bulk string copies and zeroing are not handled and that the write barrier code is “not quite optimal”. Munday’s own CL 170125 message names two disadvantages: it moves SSA away from the in-memory layout, and “can lead to extra arithmetic” when reslicing the length. CL 168117 warned the branch “might hurt performance where the branch would have been very predictable”.
Related threads: Lowering of copies, strings and slices; intrinsics · Memory-op optimization: memcombine, dead stores, store-to-load forwarding, alias analysis, mem2reg
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
reflect API and allocation ideas not merged |
2015-04 → 2023-06 | Sebastien Binet, xie cui | never landed | CL 9254 CL 482375 CL 483255 CL 191199 #49340 #33136 | reflect implementation: performance, allocation and escape behaviour, reflect performance, caches and API additions |
|
itab table split into hot and cold |
2017-05 → 2017-09 | Keith Randall | never landed | CL 44338 CL 44339 #20505 | Interface values, itabs and type assertions |
|
reflect: atomic creation of recursive types (Bind) |
2020-06 → 2020-08 | cosmos72 | dormant | #39528 #39717 | reflect dynamic types: ArrayOf, ChanOf, MapOf, SliceOf, FuncOf, StructOf |
|
Generic variants of sync.Pool/Map/atomic.Value |
2021-08 → 2025-01 | never landed | #47657 #52262 | Concurrent hash-trie map (internal/sync.HashTrieMap) | |
|
reflect.DeepCopy |
2022-03 → 2022-04 | never landed | #51520 | ||
|
reflect: generic type argument info on Type |
2022-08 → 2025-08 | never landed | #54393 | Interface values, itabs and type assertions | |
|
ChaCha8 global rand runtime demonstration |
2023-07 | Filippo Valsorda | superseded | CL 514295 | Runtime randomness: fastrand, chacha8rand and math/rand/v2 |
|
Map GetOrPut operations and combining map access/assign calls |
2025-03 → 2025-04 | Mateusz Poliwczak | dormant | CL 662995 CL 664918 #5147 #70837 | Map fast paths and compiler/runtime specialization |
Diagnostics and knobs
Background CPU profiling API
What was tried. A runtime API for “always-on” background CPU profiling: low-rate sampling that stays enabled in production, kept separate from the user-started StartCPUProfile so that an occasional ordinary profile would not disturb it. It used a new profile-buffer structure (ProfBuf) and a recorded sampling rate (cpuprof.hz).
How far it got. An undescribed Rietveld CL, CL 101380044 (“support for always-on background profiling”, June 2014), got review comments from Dmitry Vyukov, who worried about a deadlock if a program enabled background profiling at startup while a monitoring system concurrently called setcpuprofilerate. Russ Cox then posted CL 14322 (September 2015, +678/-36, “DO NOT REVIEW”) and CL 43751 (May 2017, +489/-9). The latter’s message said: “This is an experimental API being tested at Google. Others are welcome to test it as well. It’s not slated for any particular Go release at the moment.” Austin Clements gave it an LGTM on 2017-05-23. In March 2018 Hyang-Ah Hana Kim re-uploaded it as CL 102755 (+540/-9) with a fix: cpuprof.hz had never been set, so intermittent ordinary profiling could break the background profile.
Why it stopped. No reason is recorded. Cox abandoned CL 43751 on 2017-05-23, shortly after Clements’s LGTM, with the bare message “Abandoned”. CL 14322 was abandoned by Cox on 2018-11-13 and CL 102755 by Kim on 2022-09-30, each with the bare message “Abandoned”. The CLs were marked DO NOT SUBMIT from the start, so no submission was planned and no issue discusses a decision.
Related threads: CPU profiler and runtime/pprof writers (legacy format to compressed protobuf) · CPU profiling, goroutine profiling, pprof labels and the protobuf writer
Hardware-counter (PMU / perf_events) profiling
What was tried. Use hardware performance counters through Linux perf_events, so that Go CPU profiles could sample on cycles, instructions, cache misses or branch misses rather than the OS timer. Austin Clements’s CL 77651 (November 2017, +770, “DO NOT REVIEW”) gave the runtime internal access to counting and sampling events, with a preliminary perf.data writer. Milind Chabbi (Uber) proposed PMU-based profiles in #36821 in January 2020, arguing OS timer interrupts have low resolution and can be delivered to a thread other than the one whose timer expired. #53286 extended the accepted configurable CPU profile (#42502) to carry perf events.
How far it got. In June 2022 Eric Fang (erifan) posted a draft stack: CL 410796 (a CPU profile configuration struct), CL 410797 (perfEventOpen, fcntl, ioctl and constants for linux/arm, arm64, 386 and amd64) and CL 410799 (PMU profiling of non-Go threads by reading the perf mmap ring buffer, marked DO-NOT-SUBMIT). None merged; all were still open.
Why it stopped. Russ Cox put #53286 “on hold” on 2023-02-01: “We will hold the final decision for that.” On 2023-03-29 Fang reported that C threads created in cgo could not capture the PMU signal on some kernel versions (listing 5.15.0-53 to -60 as bad and 6.1.12 as good), without knowing which kernel change was responsible. In September 2024, Chabbi raised the skid between the PMU sample and signal delivery, and whether the leaf PC should come from the signal handler or the ring buffer. On 2024-09-30 Michael Pratt summarized that Linux has the perf events API, Windows has an API nobody on the team knew, and macOS has no public one. Both issues are open and no team decision is recorded. Separately, CL 269437, a CL to add a rate parameter to StartCPUProfile, was abandoned by Ian Lance Taylor because it “would break the Go 1 compatibility guarantee”.
Related threads: CPU profiling, goroutine profiling, pprof labels and the protobuf writer
Smaller attempts
OS layer, stacks and panics
Splitting the runtime package with sockdrawer
What was tried. Splitting package runtime into several packages by running Alan Donovan’s sockdrawer refactoring tool on it. A “clusters” file defined which runtime symbols would go into which package, and the tool rewrote the code mechanically; Michael Matloob then hand-fixed compile errors. Smaller attempts moved only the atomic functions into a separate package.
How far it got. CL 3027 (“SUPER HACKY”, +28975/-52855, darwin/amd64 only) was the full attempt; by 21 August 2015 its message reported that the refactorings could be applied on tip, but “cmd/go compiles but immediately fails”. CL 14070 and CL 14071 (“EXTRA HACKY”) tried a smaller cluster of five atomic functions. CL 15046 (+447/-1616) put the atomics in internal/atomic; its status per architecture was: amd64 compiled and passed tests except a segfault in the race detector test, ppc compiled, 386 and arm64 failed to link, and arm was “a bit harder” because it depends on mutex, which depends on g, m and p. It also needed “hacks in the toolchain to get multiple runtime packages to work” (CL 15045).
Why it stopped. All four CLs were marked do-not-submit. CL 14070 was closed as a duplicate. On 2015-11-04 Matloob closed CL 15046: “we’re going to go with runtime/internal/atomic”. He abandoned CL 14071 on 2025-10-15 with “This was done in CL 14204”, and CL 3027 the same day with the bare message “Abandoned”. No reason is recorded for dropping the whole-runtime split beyond the status quoted above.
What landed instead. The atomics were moved out of package runtime into runtime/internal/atomic by CL 14204, which the abandoned CLs refer to; see sync/atomic and runtime atomics and Assembly-level runtime entry points.
Related threads: Assembly-level runtime entry points: memequal/cmp/IndexByte, hashing, atomics and ABI wrappers · sync/atomic and runtime atomics
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
Segmented-stack hot-split mitigations |
2011-03 → 2013-05 | Russ Cox, Dmitry Vyukov | superseded | CL 4247059 CL 9187048 CL 9442043 | Segmented stacks to contiguous (copied) stacks |
|
vDSO clock fast paths not merged |
2016-02 → 2024-02 | Yuval Pavel Zholkover, Russ Cox | never landed | CL 19959 CL 422978 CL 518135 CL 520065 #50947 #61765 |
vDSO, time sources and process-wide randomness, vDSO, vgetrandom and fast clocks
|
|
Keep linux/armv5 and armv6 support (drop proposal) |
2016-09 → 2017-03 | never landed | #17082 | CPU feature detection, GOAMD64 checks and cpu GODEBUG options | |
|
Frame-pointer unwinding and shadow stacks (early prototypes) |
2016-12 → 2025-03 | Dan Scales, Keith Randall | superseded | CL 33809 CL 212301 CL 588615 #16638 | Frame-pointer unwinding in tracing and profiling; profile stack depth, Frame pointers: explicit NOFRAME, FP maintenance and FP unwinding |
|
Variable-sized frames for reflectcall / generics |
2020-08 → 2021-09 | Keith Randall | never landed | CL 246738 | |
|
Predicting goroutine stack sizes |
2021-08 → 2022-05 | Gerrit Bot (GitHub PR) | superseded | CL 341990 | Goroutine stack growth, shrinking, GC stack barriers and stack objects |
|
Wasm shared-memory atomics for threads |
2023-01 → 2024-05 | Garet Halliday | dormant | CL 463743 #28631 |
Compiler frontend
Generics implementations not taken: contracts parser, go2go, early stenciling demos
What was tried. A series of prototypes and demonstrations on the road to generics, most of them never meant for submission. In 2018 Robert Griesemer’s CL 149638 taught the syntax package to parse and print type parameters and contracts from the GopherCon 2018 draft (“FOR EXPLORATION ONLY”). In 2020-22 came prototypes for the compiler: converting types2 types to the compiler’s types under -G=3 (CL 276653), a minimal “hidden parameter” dictionary-passing demo (CL 332629), delayed dictionary entry loading with shape types (CL 337233), a UseDicts flag to return to pure stenciling (CL 385657), and a way to canonicalise constructed types containing shapes (CL 388114). Griesemer’s types2 snapshots covered interfaces as type sets (Interface2, CL 317529, CL 319276) and type parameters with underlying type sets (CL 355254).
How far it got. The largest prototype was the dev.go2go branch: 502 commits never merged to master, mostly by Robert Griesemer and Ian Lance Taylor, from December 2018 to May 2021. It held a generics-aware go/types and, from January 2020, a go2go tool that translated generic code into ordinary Go, which also ran the go2go playground. The June 2020 Go blog post “The Next Step for Generics” released it as an experimentation tool and said: “The real implementation of generics, if they are accepted into the language, will work differently.” Beside it, 26 Gerrit CLs were nearly all marked “DO NOT REVIEW” and “DO NOT SUBMIT”, on master, dev.typeparams and dev.unified. Matthew Dempsky’s CL 324670 showed the compiler still working with about 12 kloc removed because of unified IR; CL 423377 (-13296 lines) did the same for the non-unified frontend.
Why it stopped. dev.go2go was a translation prototype by design; its last commit (May 2021, #46346) documented in the branch README that it was no longer maintained. For the Gerrit prototypes the sources give a reason per CL. The 2018 parser CL has no recorded outcome; there is no abandonment message for it. Dempsky uploaded CL 332629 “just because it’s working, and so I have it to refer back to later”. Keith Randall abandoned CL 337233 with “Going with early dictionary loading for now.” and CL 352871 as “Obsolete”. CL 423377 was abandoned because it “fails to bootstrap via make.bash for some reason. It’s just for proof-of-concept anyway”. Griesemer abandoned the type-set snapshots as replaced by other CLs, “Not needed anymore”, or, for CL 343109, “We may still do this, but the code base has changed enough that it’s worthwhile rethinking/redoing it”. Ian Lance Taylor bulk-abandoned CL 324670 and CL 325213 in March 2025: “The dev.typeparams branch has served its purpose.” Dan Scales’s CL 388114 noted it “still doesn’t work in issue23536.go” (inlining hides the shape type).
What landed instead. Generics shipped with type parameters and constraints (the draft’s contracts did not), type-checked by types2 (types2 bring-up), implemented by GC-shape stenciling with dictionaries (shape stenciling) and written through unified IR (Unified IR); see also dev.typeparams bring-up.
Related threads: Generics bring-up in the compiler (dev.typeparams): parser, -G, types2, irgen, first stenciling · Bringing up generics in types2 (type-parameter operands, core/structural types, tparam-as-interface) · Generics via GC-shape stenciling and runtime dictionaries · Unified IR: one serialization for noding, stenciling, inlining and export
The dev.regabi Node-accessor demonstration
What was tried. A demonstration sequence of mostly automated CLs, written by Russ Cox on master in November 2020, that showed how the compiler could be cleaned up before “significant work on generics”. It converted every use of the Node struct fields to accessors (CL 271586 for Type, CL 271580 for Left), then introduced an INode interface (CL 271606), packages base (CL 270952) and ir (CL 270954), and custom node types such as NameNode (CL 271617), Closure (CL 271624) and ContinueStmt (CL 271608).
How far it got. There are 16 CLs, 11 on master and 5 later ones on dev.regabi, ranging from +79 to +6329 lines. Each master CL said: “DO NOT REVIEW. This is part of a demonstration CL sequence exploring ways we might clean up the compiler in preparation for significant work on generics. There is no intent right now to submit this any time soon.” CL 270954 stated the premise: “If we want to break up package gc at all, we will need to move the compiler IR … into a separate package”.
Why it stopped. On 2020-11-25 Cox abandoned each master CL with the message “Redone, better, on dev.regabi.” The later dev.regabi CLs were abandoned for individual reasons. CL 273246 (BranchStmt): “BranchStmt was added in CL 274091” (Matthew Dempsky). CL 274111, removing init lists from most nodes: “I’ll come back after more cleanup that will help clean this up better.” CL 279233: “This got replaced by more specific CLs.” Dempsky’s CL 280295 (merge SelectorExpr, CallPartExpr and MethodExpr): “This is being broken up into smaller CLs”. CL 275378, titled “prove that general traversals are gone”, was closed on 2021-01-08 with the bare message “Abandoned”.
What landed instead. The work landed on the dev.regabi branch and then master as the ir.Node interface with concrete node types (dev.regabi: from *Node to ir.Node) and the splitting of package gc into base, ir, typecheck, noder and others (breaking up package gc).
Related threads: dev.regabi: from *Node struct to the ir.Node interface and concrete node types · dev.regabi: breaking up package gc into base, ir, types, typecheck, noder, inline, staticdata, reflectdata, pkginit
Error-handling syntax prototypes: try and ?
- background [ On | No ] syntactic support for error handling (Go blog, 2025)
- design doc Proposal: A built-in Go error check function, `try` (Robert Griesemer)
- design doc Error Handling — Draft Design
- proposal #32437 Proposal: A built-in Go error check function, "try"
- proposal #71203 proposal: spec: reduce error handling boilerplate using ?
What was tried. Two prototyped language changes for error handling. In 2019 the try built-in (#32437) would return from the enclosing function when its last argument, an error, was non-nil. In 2025 Ian Lance Taylor’s ? proposal (#71203) added a statement QMarkStmt = [ ExpressionStmt | Assignment | ShortVarDecl ] "?" [ Block ]. Both followed the 2018 check/handle draft design, which Griesemer’s blog post says was “deemed too complicated”.
How far it got. For try, Griesemer wrote the tryhard rewriter and posted CL 182717 (+5978/-22627) and CL 185177 (+3410/-12567, vendored code ignored) to show the standard library using it. For ?, he sent three CLs to the syntax package (CL 642500), types2 (CL 642501) and the compiler (CL 642502, rewriting the statement into an IfStmt before unified IR is written), and Taylor sent CL 644076 (+15883/-23765), a demonstration of the standard library using it. Tim King’s CL 546175 was a 2023 WIP trial of another idea, abandoned in 2025-01 as “Idea was tried out. No longer needed to share.”
Why it stopped. Griesemer closed the try CLs on 2019-08-06: “try proposal was not accepted”. In the June 2025 Go blog post he wrote that try “affected control flow by returning from the enclosing function … from potentially deeply nested expressions, thus hiding this control flow from view”, and that “the user sentiment towards the proposal was very strongly not in favor”. Rob Pike’s review comment on CL 182717 argued that, excluding generated and vendored code, there were “remarkably few” candidates. Taylor closed #71203 on 2025-01-27 and moved it to discussion #71460, writing that he “should probably have started this as a discussion”. The post states: “None of the error handling proposals reached anything close to a consensus”, and “For the foreseeable future, the Go team will stop pursuing syntactic language changes for error handling.” The ? CLs stayed open with -Commit-Queue votes.
Lessons. Griesemer wrote that “in retrospect it might have been better to introduce a new keyword” and that language changes “are very expensive”, citing Russ Cox’s conclusion that try “should have been a second draft design, not a proposal with an implementation timeline”.
Related threads: Frontend support for new language features and spec changes
Quaternion types
- proposal #19813 proposal: all: quaternions
What was tried. #19813, filed on 2017-03-31, proposed adding quaternions to the language: j and k imaginary-literal suffixes, quaternion constants, predeclared types quaternion128 and quaternion256, and extended arithmetic operators.
How far it got. The same day Dempsky uploaded CL 39217 (+1894/-527), which implemented the change in the compiler, runtime and reflect. Robert Griesemer (“LGTM. Some nitpicks”) and Cherry Mui both gave it Code-Review+1.
Why it stopped. On 2017-04-06 Dempsky abandoned the CL with the message “Proposal rejected.” Russ Cox’s comment on the issue, on 2017-04-03, was “April 1 is over.”; later a commenter wrote that “it is not a joke” and asked for quaternions. In the discussion, btracey argued that “Quaternions can be implemented at the package level, albeit with less nice syntax. This is the cost of no operator overloading”, and in 2017-06 Dempsky wrote that “Almost everything about this proposal can be done without any dedicated language or standard library support.” The record does not say more about the decision.
Related threads: Language changes and rules as implemented in the compiler
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
Compiler constant and Node representation changes: *big.Int in Val, interface{} Val |
2015-03 → 2015-05 | Robert Griesemer, Josh Bleecher Snyder | superseded | CL 7801 CL 8445 |
Constant arithmetic, Mpint/Mpflt and floating-point constants, Compiler data structures: Node, Nodes, Type, and the types package
|
|
Intern types / initialization order / assignment order / variadic normalisation experiments |
2016-04 → 2019-12 | David Chase, Chad Whitacre | never landed | CL 22114 CL 156328 CL 168238 CL 31261 #22326 #599 | Typecheck/walk separation and package-level declaration order |
|
checkptr: pointer validity on conversion |
2016-12 → 2019-11 | Adin Scannell | superseded | CL 34719 | checkptr instrumentation and compiler debugging flags |
|
Position representation redesign |
2016-12 → 2019-11 | Robert Griesemer, Alberto Donizetti | superseded | CL 34016 CL 34024 CL 38316 CL 40356 |
The syntax package, position representation and parse-time checks
|
|
embed.Bytes/String/Files intrinsics instead of //go:embed |
2020-12 | Matthew Dempsky | never landed | CL 276835 | |
|
Compiler-side coverage instrumentation pass |
2021-09 → 2022-04 | Than McIntosh | superseded | CL 353451 CL 353458 CL 353455 CL 354573 | Coverage redesign: runtime/coverage, internal/coverage, covdata |
|
Lightweight function literal syntax experiments (gofmt rewrites of std) |
2022-05 → 2025-05 | Robert Griesemer | dormant | CL 406076 CL 406397 CL 406395 CL 677577 | |
|
Function-to-single-method-interface conversion prototypes |
2023-07 → 2025-07 | Carlos Amedee | never landed | CL 513759 CL 572835 CL 574742 CL 590337 #47487 | |
|
Language prototypes: tuples with unpack, predeclared zero |
2023-08 → 2023-11 | Robert Griesemer | never landed | CL 546076 CL 546077 CL 546078 CL 546079 #64457 #61372 | |
|
Type-parameterized alias export data (ObjGenericAlias) |
2024-07 → 2025-01 | Tim King | superseded | CL 599915 #68526 |
Generic type aliases and Alias type nodes, Unified IR: one serialization for noding, stenciling, inlining and export
|
Middle end and SSA optimizer
Loop-invariant code motion before 2026
What was tried. Moving computations whose inputs are all defined outside a loop into the block before the loop, in the SSA backend. Tocar’s 2016 pass was a standalone SSA pass that hoisted loop-invariant values to a preheader. Later attempts hoisted to the block dominating the loop, and the largest one combined hoisting with loop rotation, loop-closed SSA form and type-based alias analysis so that loads and stores could move as well.
How far it got. Ilya Tocar sent CL 27656 (+580) in August 2016, measured at about +0.5% compile time (Template +0.69%, MakeBash +0.58%) and -19% on math/big BitLen. David Chase revived an older hoisting pass as CL 37338 (+489) in 2017 and carried it until 2022. A Gerrit Bot CL (CL 478815, 2023) and a 3.3k-line CL (CL 551381, “Updates #63670”, open since 2023-12) followed; Jorropo’s trivial-loop unrolling (CL 451035) and tail-call-to-loop pass (CL 467535) were work in progress.
Why it stopped. In the review of CL 27656 Keith Randall asked whether too much would move out of loops: “I could see situations where this makes register pressure worse.” On 2017-10-02 Tocar abandoned it: “This version has bit-rotted, and results are not good enough to update it.” Chase’s message on CL 37338 says it “does not reliably produce improvements on amd64”, and on 2017-12-07 he reported a “big regression in BaseTest2KB” on PPC64. On 2020-03-03 he wrote that he was “not getting the sort of performance improvements from this that I would expect/hope”. In #63670 the author of the 2023 work said the earlier patch’s “performance improvement was not compelling” and that he would revert the type-based alias analysis because its core rule “does not hold in Golang”. On 2025-06-17 Randall wrote: “I am not excited about this at the moment. It seems like a lot of complexity for not much gain”, and suggested unrolling small loops 2x, which would “effectively lift half of the loop-invariant code”. CL 478815 was abandoned on 2023-09-26 and CL 467535 on 2023-05-16 with no reason recorded. Dan Peterson abandoned CL 850345 (empty counted-loop removal) on 2026-10-22 with “Probably not worth a new phase.”
What landed instead. In April 2026 Keith Randall landed loop-invariant code motion as CL 697235 (commit f9f351b130, see Loop-invariant code motion (licm)). Its message says: “I copied Ilya Tocar’s CL 27656 and heavily modified it”, and the commit keeps Tocar as author with his 2016 author date. The landed pass hoists only an allow-list of liftable operations; the loop rotation, unrolling, tail-call and empty-loop passes did not land.
Related threads: Loop-invariant code motion (licm)
Alias analysis and store-to-load forwarding (2017–19)
What was tried. Adding pointer alias analysis to the SSA backend and using it for three things: tightening loads toward their uses, forwarding stored values to later loads, and removing dead stores across blocks.
How far it got. Philip Hofer sent CL 38074 (March 2017, “DO NOT SUBMIT”, +434), a basic alias analysis plus a store-forwarding pass. He abandoned it for CL 38448 (+847), which used the analysis only to tighten loads, and promised store forwarding and dead-store elimination as follow-ups. On linux/arm the CL’s benchmarks were mixed (GobDecode -13.4%, FmtFprintfFloat +4.3%); Josh Bleecher Snyder’s compile-time numbers on 2017-03-24 showed Flate +3.64%, GoTypes +1.21% and XML +1.74%, and he noted about 1% more allocations. In March 2018 Daniel Martí started CL 98595 to let the dead-store pass see chains of plain blocks.
Why it stopped. CL 38448 was never abandoned; it stayed open after Ian Lance Taylor wrote on 2017-10-13: “Do you want to carry this forward? Alias analysis in a compiler is a complex topic that ought to be discussed on golang-dev rather than in the context of a specific CL.” No reply is recorded. CL 38074 was abandoned because it was superseded by CL 38448. Martí’s CL had no measurable effect: its message says “this improvement to the dead store pass alone does not translate into better code, as the newly detected cases were already handled by the ‘elim unread autos’ and ‘late copyelim’ passes”, and the binary did not shrink. He abandoned it on 2019-04-03: “This is massively out of date. I might revive it, but at that point I might as well just start from scratch.”
What landed instead. Dead-store elimination, store-to-load forwarding and alias queries arrived later through the work described in memory-op optimization and memory-op optimizations, not from these CLs.
Related threads: Memory-op optimization: memcombine, dead stores, store-to-load forwarding, alias analysis, mem2reg · Memory-op optimizations: memmove, Move/Zero, dead stores, nil checks
Poset-based bounds in the prove pass
What was tried. Two ways to give the prove pass more knowledge. Giovanni Bajo’s series added a Bounds method to the poset (the union-find structure prove uses to record relations between SSA values), so that min/max limits could be derived through transitive relations, and then replaced prove’s separate ft.limits map, which only recorded direct comparisons to constants. Zach Jones’s series added a recursive trace method that learned facts on demand about the operands of a branch condition (OpAdd64, OpMakeSlice, OpIsSliceInBounds).
How far it got. Bajo mailed CL 196597 (+1009), CL 196679 (use poset bounds, “removes about 120 additional bound checks from std+cmd”) and CL 196680 (delete 152 lines of limit tracking) in September 2019. Jones sent CL 190657, CL 193838 (removes 152 bounds checks) and CL 193839 (72 more), all marked “DO NOT SUBMIT”.
Why it stopped. Keith Randall asked in 2020-03 for running-time numbers: “I can’t wrap my brain around the call order of this api”. On 2020-04-02 Bajo put Code-Review-2 on his own CLs after benchmarks “look awful”: Compiler +4.21%, SSA +14.29%. David Chase confirmed the next day that it “does hit compile time, and there’s a worry that it (like regalloc) has a quadratic in it”. The CLs stayed open. The trace series lost its motivating case when Bajo wrote CL 196784 (learn facts from OpSliceMake), which he said achieved the same result “without trace”; he remained “bullish on the trace idea” but wanted to see more ops handled. On 2019-09-26, in #24660, he wrote that bound calculations in poset were “getting hairy” and that “it might not be worth it”, and that he had a smaller change to ft.limits that would cover the remaining case.
What landed instead. Prove did not take either design. Its later development is described in prove pass from poset relations to a dense limit table and in the prove thread. The issue #19126 was closed in August 2024 by a different CL stack (CL 599255).
Related threads: Prove pass: unsatisfiability, partial-order facts, loop bounds-check elimination · Prove pass and bounds-check elimination: from poset relations to a dense limit table, topo-sorted walk, and division-aware proofs
Escape-analysis precision experiments
What was tried. A set of attempts to make escape analysis more precise. In 2015 Dmitry Vyukov sent about ten cmd/gc CLs (ignoring strings, flow from input to output parameters, closure parameters for #7710 and #7714, delaying heap promotion of variables). Chase’s CL 17581 replaced the pessimistic treatment of recursive calls with an optimistic fixed-point iteration. Later attempts were CL 193179 (parameter tags with up to 254 indirection levels), CL 174530 (special-casing fmt.Printf), thepudds’s 2023 stack analysing interface method receivers (CL 524945) with Dempsky’s parallel CL 526520, and CL 649077, propagating constants into make.
How far it got. Vyukov’s CLs were mostly “NOT READY FOR REVIEW”. Chase’s CL 17581 received a Code-Review+1 but cut allocations by only 0.2% compiling net/http. Keith Randall’s CL 2500 (a static table of small integers so interface conversions need not allocate) was abandoned on 2015-04-15 with “Probably won’t do”, after Cox called it “overfitting to existing benchmarks”.
Why it stopped. On Vyukov’s CLs Russ Cox wrote on 2015-02-11: “I’m not willing to make major changes to escape analysis right now. I intend to ask someone to take a look at the whole thing and see whether it should be changed/rewritten/fixed/etc”; they were abandoned on 2015-03-20 with Chase to take over the fixes. On CL 4115 he wrote “Maybe after the compiler is converted to Go.” Chase relayed Cox’s verdict on CL 17581 on 2015-12-15, “too much risk, too little reward” for 1.6, and Chase wrote on 2016-05-18 that it “doesn’t do enough in general”: “if any content reachable from the recursion escapes, then it all escapes”. He abandoned it on 2016-09-22. Dempsky abandoned CL 193179 on 2019-09-27 “in favor of CL 197680” (CL 197680, “reimplement parameter leak encoding”). Dempsky’s CL 526520 says it needs “a solution to #62501”, because the extra call-graph edges create cycles that can make functions “analyzed less precisely”. The record gives no reason for the end of CL 649077 (abandoned 2025-05-21) or for CL 174530, which Chase described as unfinished.
What landed instead. The precision work moved into the new escape analysis (escape.go rewrite), which fixed #7714 (Dempsky, 2019-04-16: “fixed with -newescape=true”) and the leak-encoding rework continued in CL 197680. Later modelling work is in call modelling and closures. The constant-propagation idea in #71359 was followed by further CLs in June 2025.
Related threads: Escape analysis (esc.c → esc.go) · Escape analysis: the esc.go fixes, the escape.go rewrite, and the move to package escape · Escape analysis: call modelling, closures, func-variable callees, and solver speed
Register arguments prototype (2016)
What was tried. Passing function arguments and results in registers, as an experiment in the SSA backend. The CL message lists planned parts: a pragma marking which functions use registers, generic Load/Store ops signalling register use with “Arg” ops on the callee side, and automatic wrappers for legacy assembly functions used as function pointers.
How far it got. David Chase’s CL 28832 (+1544/-218, 32 patch sets, “DO NOT SUBMIT”) was uploaded in September 2016. It was tested on amd64, with intermittent PPC64LE and ARM64 runs. On 2016-10-01 Chase reported 2 to 2.5% on BenchmarkBinaryTree for arm64 and 7.7% on PPC64, in both cases with the benchmark and the GC modified.
Why it stopped. In the review of CL 28832 on 2017-06-26, when asked about Go 1.10, Chase wrote: “It’s not looking super-likely; this is a large chunk of work that affects compiler, bits of the runtime, and user-written assembly language, and it’s dependent on getting the better DWARF support in, which didn’t quite make the cut for 1.9. Worse, while talking about how we might implement generics, realized that this also potentially interacts with a likely/plausible implementation of generic methods.” The CL was retargeted to go1.10 and left open; nothing later in the record states a decision. On 2016-09-22 Chase had said that two stack-trace tool vendors he talked to (Delve and Backtrace.io) use DWARF, “so we just say what we’re doing in Dwarf”.
What landed instead. The register ABI landed years later as ABIInternal (see register-based ABI) together with late call expansion in the SSA backend.
Related threads: Register-based ABI (ABIInternal) and ABI wrappers · Late call expansion and the SSA side of the register ABI
PGO beyond inlining: block layout, edge profiles, function ordering
What was tried. Using profile data beyond call-site inlining. Basic-block counters were taken from the pprof CPU profile (matched by inline tree, line and column or a “discriminator”) and used to pick likely branches and order blocks; Shao added a frequency-propagation pass for an edge profile and an instrumentation-based edge-profile prototype built on loopreschedchecks. A separate greedy Pettis-Hansen layout (CL 572975, #66420) and a profile-driven function ordering in the linker (CL 571395, +821) were also written.
How far it got. Michael Pratt’s prototype CL 560781 (February 2024) plumbed discriminators into pprof (#59612). Alex Markin proposed counters in #65466 and sent CL 564055, then CL 602015 (+1612) and the merged CL 605555 (+1769). Jin Lin sent CL 571535, and Junyang Shao followed with CL 628655, CL 628715, CL 632515 and CL 634535 in late 2024. The benchmark rows visible in the issue comments, run on the Sweet suite, mostly show no significant change (“~”).
Why it stopped. No decision is recorded in the CLs. Markin abandoned CL 564055 on 2024-07-31 because a “new version of basic block profile was implemented”, and CL 605555 on 2025-05-15 with the bare message “Abandoned”. The other CLs were left open as work in progress and were last updated between December 2024 and May 2025. In #65466 on 2024-12-05 Cherry Mui wrote of the instrumentation: “Currently there is no plan to add instrumentation-based approach to the compiler or general PGO workflow, although the future possibility is open. The plan for directly supporting edge profiles is to add support of hardware LBR profiles”. In #66420 Keith Randall measured a 3.2% larger go test -c runtime binary with the greedy layout and wrote “I think we still have to figure out the right way to decide when to turn it on.”
Related threads: Profile-guided optimization (PGO): hot-call inlining, devirtualization, profile preprocessing · Register allocator, scheduler and block-level backend passes · Linker performance and memory
Smaller attempts
Backends and assemblers
A dedicated g register on amd64 (2014–15)
What was tried. Reserving one amd64 register for the current goroutine pointer g. In 2014 Russ Cox had also dedicated R15 as a “top-of-stack register” in the C-based toolchain (6c, 6g, runtime) to make some dynamic analysis easier.
How far it got. The 2014 attempts (Rietveld 135270043 and 140820043) were marked “Not for submit”. CL 8981 (+295/-176, April 2015) was an experiment for darwin/amd64 only. Its message reports mixed go1 results: BinaryTree17 -0.84%, FmtFprintfEmpty -5.61%, GobDecode -3.56%, but Fannkuch11 +4.74%.
Why it stopped. The commit message of CL 8981 states the result: “Faster in big programs, but slower in functions that were using R15. For example, Fannkuch is using R15 to hold ‘flips’.” Cox uploaded it with “DO NOT REVIEW” and abandoned it the same day: “Uploaded to Gerrit for safe keeping. Not for review.” He suggested that the allocator might make better choices, “or could allow the use of R15 provided it is restored before each CALL or RET”. The 2014 CL was abandoned by Cox on 2014-09-01 with no reason given.
What landed instead. A fixed g register came with the register-based ABI of Go 1.17, which on amd64 keeps g in R14 (see Register-based ABI (ABIInternal) and ABI wrappers).
Lessons. Cox’s conclusion in CL 8981 was: “Probably the register allocator needs to be improved and then this experiment should be done again.”
Related threads: Register-based ABI (ABIInternal) and ABI wrappers
64-bit field alignment on 32-bit platforms
- design doc Proposal: Make 64-bit fields be 64-bit aligned on 32-bit systems, add //go:packed, //go:align directives (Dan Scales (with input from many others))
- proposal #19057 proposal: runtime: add AlignedN types that can be used to increase alignment
- proposal #36606 proposal: cmd/compile: make 64-bit fields be 64-bit aligned on 32-bit systems, add //go:packed directive on structs
- proposal #62172 proposal: cmd/compile,unsafe: support padding for generic structs
What was tried. Two linked proposals to stop 64-bit atomics from faulting on 32-bit platforms. Dan Scales’s design (June 2020, #36606) would have aligned 64-bit struct fields to 8 bytes on 32-bit systems, left the alignment of stack variables and heap allocations unchanged so the calling convention did not change, and added //go:packed and //go:align directives for compatibility and finer control. The older #19057 proposed AlignedN types to raise the alignment of a field.
How far it got. The record has the design document, 65 comments on #19057 and a long discussion on #36606; no implementation CLs appear. The proposal had been taken off hold by Russ Cox and then put back.
Why it stopped. Cox wrote on 2020-10-14: “Was previously on hold for design doc, and I was late taking it off hold for that. But now placing back on hold per discussion with @danscales. The focus of the compiler/runtime team right now is on figuring out how to implement generics, so they don’t have bandwidth for this.” Scales had noted on 2020-10-07 that “we never did come to a consensus on what syntax we should use to insure alignment”. Matthew Dempsky argued the same use cases could be met by the opt-in #19057 alone, without a change of defaults for all Go code. Austin Clements wrote on 2020-10-15 that the change “seems like the ideal thing to do” but “we learned through this process that it has a lot of unfortunate consequences and complexity”. Josh Bleecher Snyder had argued in January 2020 against breaking Go assembly twice, once for alignment and once for a new ABI. Both issues were still open in 2024, and #62172 (2023) asks for padding control in generic structs.
Related threads: sync/atomic and runtime atomics
A new arm64 assembler encoding path
What was tried. Reworking how the arm64 assembler encodes instructions. Eric Fang’s proposal (#44734, March 2021) wanted a clearer framework because roughly 1000 instructions, including NEON and SVE, were missing and adding each was costly. The first approach refactored the assembler so each Prog was unfolded into machine instructions before encoding. The second kept the old optab and added a new path where one Prog maps to one machine instruction, with a generated instruction table, a new register-encoding format, and a disassembler sharing the table.
How far it got. The refactor went through CL 297776, CL 347490 (+12.7k/-7.6k) and CL 347531. The new path was CL 424137 (+78k lines), with CL 424138, CL 506118, CL 518118, CL 527256, CL 527257, CL 535615 and the [DO-NOT-SUBMIT] disassembler CL 538456. Sebastian Nickolls of Arm sent a separate static-table SVE assembler in November 2024 (CL 630520 to CL 630523, +11.5k). Older instruction-set CLs had also been abandoned: SIMD (CL 33596, 2016), SVE (CL 153358, 2018) and an assembler/disassembler test framework (CL 275454).
Why it stopped. Fang abandoned CL 297776 on 2021-12-24: “This version breaks assembly -S printing and restartable sequence, which is unacceptable, so abandon it.” CL 347490 and CL 347531 were abandoned in 2022 and 2023 with no reason recorded. The new-path CLs stayed open after late 2023 with no stated decision. On 2025-02-06 Nickolls wrote in #44734: “The patches you see from me represent our contributions to address this issue. The other patches are deprecated… We stopped our effort and are open for anyone interested to take over the issue.” CL 153358 was abandoned in 2023 because it was converted to CL 480855 and CL 480857, and Russ Cox had asked in 2016 to wait on the SIMD CL (CL 33596) “to give us more time to set up a proper test suite”. In January and February 2026 new CLs adding an “arm64 XML ISA manual parser” were linked to the issue.
Related threads: arm64 backend and assembler · arm64 code generation and assembler
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
ARM Thumb support and SIGILL-based FP emulation |
2012-04 → 2012-07 | Shenghou Ma (minux) | never landed | CL 5990056 CL 6445054 | The arm64 (ARMv8) port (7g/7a/7l), Platform ports: OS and architecture matrix |
|
cmd/new6g draft (dev.cc) |
2015-02 | Russ Cox | never landed | CL 5543 | C→Go translation of the toolchain (liblink, assemblers, compilers; dev.cc) |
|
dev.ssa experiments: spill slot reuse, 2-address instructions, write barrier redo |
2015-04 → 2016-07 | Keith Randall, Josh Bleecher Snyder | superseded | CL 8380 CL 15825 CL 18364 CL 19173 #14855 | Register allocation, flag allocation and stack slots, Write-barrier insertion: from gc to the SSA writebarrier pass |
|
Disassembler and objdump tooling: arm64 VIXL-based disassembler (RFC), power64asm, .o disassembly, objdump -stats |
2015-05 → 2020-08 | Zheng Xu, Minux Ma | never landed | CL 35073 CL 9679 CL 22685 CL 213830 #13862 | ARM64 assembler |
|
Early backend port attempts (mips32, 386, PPC in dev.ssa) |
2016-06 → 2016-10 | Vladimir Stefanovic, Alberto Donizetti | superseded | CL 28236 CL 23901 CL 24732 | Multi-architecture SSA ports: ARM, 386, ARM64 (dev.ssa), Multi-architecture SSA ports: PPC64, MIPS64, MIPS32, s390x |
|
DWARF/line-info compiler experiments |
2016-09 → 2019-08 | Heschi Kreinick, Jeremy Faller | partially landed | CL 87156 CL 187117 CL 48032 CL 51150 #12899 #17240 | DWARF location lists from SSA, DWARF variable locations, statement positions and inline info from the backend |
|
Intrinsicify user-defined assembly functions |
2016-10 → 2017-01 | btracey | never landed | #17373 | Intrinsics for atomics, math/bits, math and runtime helpers |
|
s390x assembler/compiler enhancements: EXRL, branch/conditional-move mnemonics, FP load merging |
2017-02 → 2020-12 | Ruixin Bao, Michael Munday | never landed | CL 199647 CL 38008 CL 36951 #19483 | s390x, MIPS, 386 and 32-bit ARM backends, s390x backend: compare-and-branch, intrinsics, assembler |
|
x86 CMOV mnemonic order swap and suffix syntax |
2017-04 → 2019-04 | Damien Lespiau, Dmitry Matrenichev | dormant | CL 42092 CL 66451 CL 171732 #20173 | x86 assembler: AVX, AVX2, AVX-512 and the instruction table |
|
amd64 indexed load/op fusion |
2018-01 → 2020-03 | Josh Bleecher Snyder, Ben Shi | never landed | CL 167089 CL 167090 CL 167091 CL 167093 #23325 | amd64 and 386 instruction selection, Memory-op combining, byte-order idioms and load/store folding |
|
ARM64 load/store pair pass |
2018-04 | Geoff Berry | superseded | CL 105255 CL 106377 | ARM64 load/store pairing (LDP/STP) |
|
i386 and math/bits multiword intrinsics |
2018-04 → 2019-02 | Keith Randall, Nikhil Benesch | never landed | CL 158238 CL 118356 CL 106376 #24813 | Intrinsics for atomics, math/bits, math and runtime helpers |
|
Unconditional new amd64 instructions experiment |
2018-06 | Deleted User | superseded | CL 117295 | CPU feature detection, GOAMD64 checks and cpu GODEBUG options |
|
amd64 addressing-mode selection as a separate pass (prototype) |
2020-01 → 2020-04 | Josh Bleecher Snyder | never landed | CL 214042 | amd64 code generation and GOAMD64 microarchitecture levels |
|
x86 function and jump alignment (nop-only) |
2020-02 | David Chase | dormant | CL 219229 #37121 | amd64 code generation and GOAMD64 microarchitecture levels |
|
ABI0/ABIInternal wrapper generation prototypes and register-ABI asm tests |
2020-02 → 2021-03 | Dan Scales, Michael Knyszek | superseded | CL 217977 CL 252757 CL 272569 | Register-based ABI (ABIInternal) and ABI wrappers, Late call expansion, AuxCall and the expand_calls pass |
|
Register allocator and scheduler experiments (regalloc overhead metadata, call-arg order, free dead registers, escape-analysis constraints on scheduling) |
2020-05 → 2023-07 | Michael Pratt, David Chase | dormant | CL 475959 CL 480256 CL 508925 CL 234580 #59297 | SSA infrastructure passes: layout, regalloc, scheduling, flagalloc, block shape, Register allocator, scheduler and block-level backend passes |
|
Late call expansion / expand_calls rework (regabi) |
2020-08 → 2021-02 | David Chase | superseded | CL 249459 CL 249460 CL 253217 CL 256077 #41474 #30548 | Late call expansion and the SSA side of the register ABI, Register-based ABI (ABIInternal) and ABI wrappers |
|
MIPS ISA level options, bit-count intrinsics and race detector for mips64le |
2020-10 → 2025-04 | Julian Zhu, YunQiang Su | dormant | CL 485595 CL 485635 CL 508095 CL 481535 #60072 | Multi-architecture SSA ports: PPC64, MIPS64, MIPS32, s390x, Race detector, ASan, MSan and Valgrind integration |
|
Asyncify for goroutine switching on wasm |
2020-12 → open | never landed | #43033 | WebAssembly backend | |
|
PPC64 backend reworks (jump tables, compare-with-zero merge, carry bit) |
2022-06 → 2023-08 | Paul Murphy | never landed | CL 411914 CL 425482 CL 439255 CL 443317 | PPC64 / Power10 backend (GOPPC64, prefixed instructions, PC-relative code), Jump tables for switch statements on more architectures |
|
wasm32 port (32-bit pointers) |
2024-02 → 2024-05 | Evan Phoenix | dormant | CL 560118 CL 570835 CL 570838 CL 570836 | WebAssembly backend, WebAssembly backend |
|
internal/simd generator prototype |
2025-02 | David Chase | superseded | CL 653635 | simdgen: the instruction-table-driven generator, dev.simd branch, GOEXPERIMENT=simd lifecycle and merges to master |
Linker and object files
Linker speed experiments of 2016: mmap input, section-oriented objects
What was tried. Several attempts in spring 2016 to make the linker read object files faster or with less memory: memory-mapping input and avoiding copies, reading each object file fully into memory, a section-oriented object format, an uncompressed zip as the object container, and splitting object sections into independent length-prefixed chunks so they could be parsed in parallel.
How far it got. David Crawshaw’s CL 20104 cut LinkJujud by 2.67% (-6% at one patch set). Hudson-Doyle’s CL 20126 reported linking juju 21% faster (35% with -w) but failed the gdb runtime test. Kohanim sent CL 20175, CL 21020 and CL 21752 (LinkCmdGo -3.62%); Hudson-Doyle’s CL 21444 measured LinkCmdGo MaxRSS -10.6% but LinkJuju +7.4%. Most were abandoned or marked “DO NOT SUBMIT”.
Why it stopped. Each CL ended differently. Crawshaw wrote on 2016-03-16 that following review suggestions “has slowed down the code so now this CL is a wash” and that “linker performance is a diversion from my binary size goals”; Brad Fitzpatrick answered “I think we can get the same speedup or more via other cleanups”, and it was abandoned on 2016-03-21. Kohanim abandoned CL 20175 because it “slows down linking with a cleared page cache”; Fitzpatrick had been “wary of slurping everything into memory”. Hudson-Doyle abandoned CL 20126: “All the interesting bits of this have been done in other branches now”, and called CL 21444 “very dead” in 2019. Russ Cox wrote on CL 21020 that it was “a major rewrite of the object file format and is possibly incompatible with other plans for converting the overall outer archive to .zip” (the zip proposal was #14386). On 2016-10-12 he marked CL 21020 and CL 21752 “R=close”, writing “I’d like to look at all of this again in Go 1.9”. In #11123 on 2016-12-19 he wrote: “This is a good idea, but right now we don’t have the bandwidth to help redesign the object format and support the resulting churn in the code base… Marking this declined for now.”
What landed instead. The object-format redesign was done later in the linker work described in the new object file format and loader; the speed work of the period is in linker speed and memory.
Related threads: Linker speed and memory (Go 1.7 focus) · New object file format and the loader (dev.link)
DWARF type generation in the compiler
What was tried. Moving generation of DWARF type DIEs out of the linker (cmd/link/internal/ld/dwarf.go) and into the compiler, so that each compilation unit emits its own type information. The series defined a dwarf.Type interface in cmd/internal/dwarf, implemented it for the compiler’s types.Type, synthesized the pseudo types for map, slice, string and channel in the compiler, and finally deleted the linker code. The stated motivation in #52209 was that dynamically linked programs (plugins, -linkshared) could not be debugged, and, as the issue quotes, @thanm had floated the move earlier.
How far it got. guangyuan zhou opened #52209 on 2022-04-07, with a working initial version, then split it into a series of about 31 CLs from 2022-04, including CL 399059, CL 399294, CL 399302, CL 399275, CL 400137 and CL 400138 (-916 linker lines), plus -linkshared support (CL 400634) and tests (CL 404217). It was rebased in August 2022. The last CL updates are in April and May 2023.
Why it stopped. No reason is recorded in the CLs or the issue. Ian Lance Taylor wrote on 2022-04-07 that this “doesn’t have to go through the proposal process” and that the linker/compiler/runtime team could decide. Matthew Dempsky put Code-Review-2 on CL 399877 (moving map and slice prototypes into the compiler’s builtins) on 2022-04-14 without a recorded comment. The author asked for review on 2022-08-12 and again on 2022-09-26 (“Could we continue to review these and make it more closer to merging?”); the issue records no reply, and 14 of the 17 CLs remained open with no further activity.
Related threads: DWARF location lists from SSA
Smaller attempts
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
Faster legacy linker and lower compiler memory |
2013-08 | Dmitry Vyukov | superseded | CL 13157043 CL 12820047 CL 12994044 CL 12849046 | Linker speed and memory (Go 1.7 focus), Compiler performance, formatting and concurrency |
|
Zip-based Go package archives |
2016-02 | Russ Cox | never landed | #14386 | |
|
BUILD_PATH_PREFIX_MAP support in the linker |
2017-10 → 2017-11 | Michael Stapelberg | never landed | CL 73291 #22491 | Build identity, buildinfo and reproducibility |
|
Do not write DWARF by default in the linker |
2018-06 → open | never landed | #26074 | DWARF quality for debuggers (types, variables, trampolines) | |
|
Linker debugging and statistics options (-stats, -showdead, -dumpsymsat, -debugdead, -dumpinitlive) |
2018-07 → 2024-12 | Than McIntosh | never landed | CL 124377 CL 174438 CL 252824 CL 599436 | Linker performance and memory engineering |
|
MSVC toolchain support for cgo and external linking on Windows |
2018-09 → 2019-11 | Caleb Champlin | dormant | CL 133938 CL 133945 CL 133944 CL 133942 #20982 | PE / Windows |
|
Linker symbol-name interning to save memory (2019) |
2019-02 → 2019-08 | Than McIntosh | superseded | CL 163698 CL 163697 CL 171155 CL 171217 | New object file format and the loader (dev.link), Linker performance and memory engineering |
|
Minimal DWARF mode (-minimaldwarf / -dwarfonlyline) for profiling |
2021-01 → 2021-05 | Than McIntosh | dormant | CL 283775 CL 283776 CL 285397 CL 285398 | DWARF generation in the linker |
|
ELF/DWARF metadata for external tools: .eh_frame for asmcgocall, ARM64 mapping symbols, cgo CFI unwinding |
2021-06 → 2025-02 | Quim Muntal, Gerrit Bot | never landed | CL 334231 CL 343150 CL 471715 #47166 | C-to-Go callbacks: extra-M reuse, system-stack bounds, init gating, DWARF quality for debuggers (types, variables, trampolines) |
Recent and still open
| Attempt | When | Who | Outcome | Record | Related thread |
|---|---|---|---|---|---|
|
RISC-V assembler extensions: RVV 1.0, vector crypto, Zfa/Zicond, compressed, bitmanip, syntax checker |
2021-08 → 2025-12 | Pengcheng Wang, Joel Sing | partially landed | CL 594696 CL 605496 CL 605497 CL 559657 #47560 #71105 | riscv64 compiler and assembler (RVA profiles, vector, compressed), riscv64 port: assembler and compiler |
|
WebAssembly platform additions: wasip3 syscall/os/net layer and //go:wasmimport (dev.link) |
2020-07 → 2026-03 | Jelle van den Hooff, Vedant Roy | superseded | CL 750821 CL 243677 CL 248739 CL 252828 #38248 | WebAssembly backend, Per-platform object formats and internal linking (Mach-O, PE, XCOFF, wasm, ppc64, riscv64, loong64) |
|
Generated ARM64 assembler data and spec-driven SVE encodings (2026) |
2026-02 → 2026-03 | Junyang Shao | never landed | CL 747180 CL 748601 CL 747920 CL 748602 | ARM64 SVE: scalable vectors and predicate masks, arm64 code generation and assembler |
|
Swiss-map optimisation experiments not merged |
2024-02 → 2026-05 | Michael Pratt, Keith Randall | never landed | CL 567855 CL 580916 CL 611192 CL 622838 #54766 | Swiss-table maps (GOEXPERIMENT=swissmap → only implementation) |
|
sync.Map storing values directly |
2024-10 → 2026-08 | qiu laidongfeng | superseded | CL 617695 #21031 | Concurrent hash-trie map (internal/sync.HashTrieMap) |
|
Type descriptor size reductions |
2016-02 → 2026-06 | Josh Bleecher Snyder, David Crawshaw | dormant | CL 19698 CL 19852 CL 20335 CL 20825 #6853 #57916 | internal/abi type layout unification and type metadata (rtype → abi.Type), Compiler-generated equality and hash functions |
|
CPU profiler timer skew workarounds |
2019-10 → 2026-06 | Rhys Hiltner, Felix Geisendorfer | never landed | CL 204279 CL 342055 CL 334769 CL 789201 #35057 #49065 | CPU profiler and runtime/pprof writers (legacy format to compressed protobuf), CPU profiling, goroutine profiling, pprof labels and the protobuf writer |
|
Pure-Go race detector without cgo |
2025-12 → open | kolkov | never landed | #76786 | Race detector, ASan, MSan and Valgrind integration |
|
Separate dynimport object and early export data / linkobj-based build pipeline in cmd/go and cmd/link |
2021-06 → 2026-10 | Michael Matloob | never landed | CL 722580 CL 765940 CL 761462 CL 800223 #52863 #58619 | Dead code elimination and reflection-aware pruning |
|
Windows linker: PIE/ASLR, PE host-object import thunks, debug/pe rewrite, PE DWARF, PE section handling |
2015-08 → 2026-03 | Jason Donenfeld, Alex Brainman | dormant | CL 152759 CL 203602 CL 203606 CL 190481 #27144 #35192 | PE / Windows, Per-platform object formats and internal linking (Mach-O, PE, XCOFF, wasm, ppc64, riscv64, loong64) |
|
Size-specialized malloc micro-experiments |
2024-10 → 2026-06 | Michael Matloob, Hai Tu | partially landed | CL 761820 CL 773260 CL 776020 CL 776021 #79286 | mallocgc decomposition and size-specialized malloc |
|
Size-specialized malloc experiments (generated 16-byte and tiny allocators) |
2025-05 → 2026-04 | Michael Matloob | partially landed | CL 676959 CL 772440 CL 766980 | mallocgc decomposition and size-specialized malloc |
|
Adaptive heap resizing with GC throughput control |
2026-02 → open | ByteDance | never landed | #77587 | GC pacer redesign |
|
SSA liveness SCC decomposition |
2022-11 → 2026-08 | Frank Kuehnel | never landed | CL 454055 CL 731660 | Stack slot merging, liveness and stack maps |
|
Per-package restructuring of SSA/ssagen and removal of ir.CurFunc/base.Pos globals (2026) |
2017-03 → 2026-09 | Matthew Dempsky, Michael Matloob | never landed | CL 795070 CL 795068 CL 795071 CL 795076 #19683 | Rewrite-rule generator, typed Aux/AuxInt, and rule syntax, SSA package decomposition and compiler-infrastructure refactor |
|
SROA / mem2reg / decomposeaddr SSA passes |
2026-01 → 2026-10 | Junyang Shao | partially landed | CL 737441 CL 820245 CL 820244 CL 820247 | Memory-op optimization: memcombine, dead stores, store-to-load forwarding, alias analysis, mem2reg |
|
SIMD auto-vectorization (SLP vectorizer) prototype |
2026-05 → 2026-07 | Arseny Samoylov | never landed | CL 783220 | Compiler SIMD type system, ABI and register-allocation support, dev.simd branch, GOEXPERIMENT=simd lifecycle and merges to master |
|
Prove-pass and extension-elimination rule experiments |
2020-03 → 2025-11 | Michael Munday, Daniel Morsing | dormant | CL 227159 CL 227161 CL 227498 CL 224878 #38721 | Prove pass and bounds-check elimination: from poset relations to a dense limit table, topo-sorted walk, and division-aware proofs, Prove pass: unsatisfiability, partial-order facts, loop bounds-check elimination |
|
SSA package decomposition to speed up make.bash |
2026-06 → 2026-07 | Michael Matloob | superseded | CL 792100 | SSA package split into subpackages (issue #80409), SSA package decomposition and compiler-infrastructure refactor |
|
fmt formatting overhead reduction via escape analysis of string operands |
2026-07 | Egon Elbre | never landed | CL 807624 | Escape analysis: call modelling, closures, func-variable callees, and solver speed |
|
Darwin libSystem thread creation and pure-Go runtime/cgo |
2017-07 → 2026-03 | Hiroshi Ioka, George Adams | superseded | CL 50290 CL 744980 #17490 | Darwin and OpenBSD move to libc; Solaris and AIX libcall; syscall wrapper layer |
|
Windows runtime exceptions, callbacks and minidumps |
2022-09 → 2026-09 | Quim Muntal | never landed | CL 788700 CL 457875 CL 458955 #12516 #47576 | Windows runtime port |
|
wasip3 scheduler and poller |
2026-02 → 2026-03 | Jelle van den Hooff | never landed | CL 747828 CL 747827 CL 750820 | Netpoller |
|
plan9/arm64 port |
2026-05 | Rafael Diniz | never landed | CL 778160 | New, ported and removed platforms |
|
runtime/jit: user frames for JIT compilers |
2026-03 → open | randall77 | never landed | #78189 |