Wails 3 arrives after nearly four years
#â622 â October 30, 2026
Read the Web Version
Go Weekly
Wails 3.0: Build Desktop Apps with Go and Native Web Views â Nearly four years after its alpha, Wails 3 lets you build desktop apps with a Go backend and web frontend, rendered in a native WebView. 3.0 adds multiple windows, system tray support, an in-app updater, and generated JavaScript and TypeScript bindings for Go services. Mobile support remains experimental.
Lea Anthony
Stop Hand-Rolling Webhooks in Your Go Service â Your webhooks want one package, one call per event, and idiomatic Go with typed models. Svix handles retries, signing, delivery logs, and a portal for your customers, so you donât build any of it. Clerk has run its webhooks on Svix for 3+ years.
Svix sponsor
How Go Shipped Its First URGENT Track Security Release â As featured last week, Go hardcoded a block on eMudhra's broken intermediate certs. Now the Go security team explains why the notes were vague, how the fix shipped hours after they were alerted, and its plan for revocation checking on Linux.
Roland Shoemaker (Go Team)
IN BRIEF:
đ golang.org/x/net v0.62.0 fixes two bugs in its html package: a quadratic slowdown on crafted input and a raw text rendering flaw.
Filippo Valsorda has proposed friendlier crypto/aead packages for symmetric encryption, plus crypto/cobblestone for messages too big to hold in memory.
đ Emma Twersky, familiar to many from Angular's DevRel team, now leads Frameworks and Languages DevRel at Google, covering Go alongside Flutter and Dart.
đ Go on Linux and CVE-2026-98432: Patch Your Kernel â Go enables Multipath TCP by default for listeners in apps whose main module declares go 1.24 or later. Anthropic's remote root exploit for a new Linux kernel bug used such a listener, but the bug isn't MPTCP-specific. Update your kernel, as GODEBUG=multipathtcp=0 is only "a fragile mitigation".
Neal Patel (Go Security Team)
đĄ An issue that proposes Go turn the default off is open, but the Go team says any change would be premature for now. The issue is ultimately with Linux, not Go.
Go Concurrency Distilled â A handy interactive refresher where Anton boils Go's numerous concurrency concepts and mechanisms down into a single page, covering everything from channels and select to context, synctest and the scheduler, with examples you can edit and run in place.
Anton Zhiyanov
đ Measuring Go 1.28's HTTP/2 'Parking' Memory Savings â Thanks to a change by Brad Fitzpatrick, an idle HTTP/2 server connection in Go 1.28 can let its serve goroutine exit. With 5,000 idle connections, a dev build ran 5,000 fewer goroutines and used about 40% less RSS.
Mohammad Abdolirad
Proposal: Let go test Run Your Project's Own Analyzers â //go:analyze directives would add custom analyzers to go test, and libraries could ship checks that run on packages that import them, like vet checks Printf calls. It's still early but being actively discussed.
Alan Donovan (Go Team)
đș Profiling Go with pprof: Flame Graphs and Memory Leaks â A quick, six minute primer that finds a CPU hog and leak in an app. HĂŒseyin Babal
đ Partial Updates in Go with Field Masks â How to separate update intent from values in PATCH requests. Redowan Delowar
đ Where Go 1.27's HTTP/1 Connection Reuse Stops Working Lleg
đ Code & Tools
gohawk: Static Analysis for Leaks and Lock Bugs â Finds leaked files, unjoined goroutines, unreaped processes and lock inversions, using Infer-style function summaries to track ownership across packages. It's a young project, but has already led to merged fixes in Moby and Caddy. (GitHub repo.)
James Ko
TinyGo 0.44: Smaller, Faster, Better â The Go compiler for microcontrollers and WebAssembly now does more init work at compile time for smaller binaries that start faster, drops needless slice bounds checks, and adds multicore goroutines on the ESP32-S3. Support for the Arduino Uno R4 also brings TinyGo's first Renesas CPU support.
The TinyGo Team
You Chose Go for Simplicity. Keep Your Data Stack Simple Too â TimescaleDB extends Postgres with compression and continuous aggregates. Your Go code doesnât change. Get $1000 credit.
Tiger Data (creators of TimescaleDB) sponsor
noalloc: Keep Your Hot Paths Allocation-Free â Mark a function with //noalloc:root and this CLI checks its call tree for heap allocations, flagging offending lines. It's a days-old, AI-written experiment that borrows an idea from OxCaml's zero_alloc.
Phil Eaton (The Consensus)
đ Echo 5.4.4 â The popular web framework has had four patch releases in eight days, including a fix for a critical access control bypass in its proxy middleware's WebSocket handling.
go-json 0.12 â Fast encoding/json-compatible library. Adds a jsontext package and an early encoding/json/v2-style API.
cel-go 0.33 â Google's Common Expression Language for Go gets an opt-in parser that's 8-19x faster.
Huma 2.40 â Framework for building HTTP APIs with OpenAPI 3 specs.
mockery 3.9 â The popular mock generator for Go interfaces. (Docs.)
đą Elsewhere in the ecosystem
âš Richard Seroter, Google Cloud's Chief Evangelist and the exec with product and engineering responsibility for Go, đ says Go has just become the most-used language on Google Cloud by raw SDK API requests, with Java not far behind.
Before 'distributed SQLite' became a busy, venture-funded category, there was rqlite, built in Go in 2014. Its creator tells the story of the ecosystem's growth.
Lisette is a Rust-inspired language with algebraic data types, pattern matching and no nil that compiles to readable Go and can call Go packages directly.
â ïž A reminder that the 2026 Go Developer Survey closes for submissions on November 3, so there are a few more days to have your say.
https://golangweekly.com/issues/622
#nearlyfour #fouryears #wails #weeklywails #team #builddesktop #securityteam #desktopapps #webviews #linux
#rss #feeds #linksharing #qwibbit
#nearlyfour
#fouryears
#wails
#weeklywails
#team
GoWire4d
Our Response To The eMudhra Key Disclosure Incident
The Go Blog
Our Response To The eMudhra Key Disclosure Incident
Roland Shoemaker, on behalf of the Go Security team
26 October 2026
On October 19, the Go team initiated an expedited security release to protect Go users and their applications from a critical public key infrastructure (PKI) compromise involving certificates issued by eMudhra.
Within approximately seven and a half hours of being notified, we shipped a targeted patch, which was included in our first-ever URGENT track security release.
Here is the context behind the incident, why Go stepped in at the runtime level to protect our users and how our incident response unfolded.
eMudhra Incident
On October 17, Mozilla added eight intermediate certificates (six used for TLS, and two used for code signing) issued by a root certificate controlled by eMudhra to OneCRL, effectively blocking their usage in Firefox. Soon after, a number of security researchers noticed this and began investigating what mightâve caused this.
On October 19, the Go Security team was made aware of these investigations and their conclusions, which showed that the keys associated with these intermediates were trivially weak, allowing recovery of the private keys in microseconds.
Due to the fact that the eMudhra root certificate was present in most trusted root programs, recovering the private keys would allow any attacker to sign arbitrary TLS certificates for any domain name, without the usual requirement to show proof of control of said domain name. This would allow an attacker to man-in-the-middle (MITM) TLS connections and present a valid certificate for a domain name they did not control, effectively allowing them to impersonate any TLS server the targeted machine attempted to connect to.
Go ships a TLS certificate verifier in the standard library. When users use net/http or crypto/tls to connect to a TLS service, the verifier is called to check if the certificates the other side of the connection presents are valid. On Windows and macOS we delegate these checks to the platform APIs, and on Linux, which lacks platform APIs, we use our own verifier implementation.
Since Linux does not have a uniform built-in mechanism to handle revocation checking, and in order to provide a timely fix for users on other platforms that might not receive platform updates, we decided the best way to protect Go users was to do an URGENT track security release which similarly removed trust.
Incident Timeline
A typical Go security release follows a multi-day process, with advanced pre-announcements in order to allow those who rely on Go to prepare. In this case, because we believed that private key exposure was imminent, our team chose to use the URGENT track for the first time in order to ship protections before active public exploitation could begin.
11:45 PDT
Our Security team was alerted by an independent external researcher, citing unreported actions by Mozilla and other organizations to remove trust in eight eMudhra intermediates. It became clear quickly that this was due to weak private keys they used.
12:30 PDT
Mozilla independently reported the issue to our Security team, confirming the issue, and asking that we not immediately disclose the issue, but encouraging us to release a fix as soon as possible.
14:00 PDT
We finalized a patch which blocked the usage of certificates which chained to the compromised intermediates, and thanks to the researcher who initially alerted us to the issue, had a set of certificates signed using the recovered keys to test against.
15:00 PDT
We began the process to release minor Go versions 1.27.3 and 1.26.10 containing our patch. Due to the urgent nature of this issue and its exploitability, and with the belief that the private keys would likely become publicly known within hours, we decided an immediate release was the most prudent option.
19:13 PDT
We finalized the release and announced it publicly. At this time the underlying issue was still not widely known. In an effort to give other defenders a chance to protect as many users as possible without revealing to attackers how the issue could be exploited, our patch and communications were somewhat opaque, warning users that we were âblocking trust in a small set of intermediate certificates due to specific security concerns around their operationâ and urging users to immediately upgrade to this release.
In under seven and a half hours from initial notification, patched Go releases were live worldwide, giving users everything they needed to secure their deployments before the keys became public.
By the morning of October 20, numerous independent researchers had publicly disclosed the weak keys.
Lessons Learned and Future Improvements
This was our first ever URGENT track security release, and during the release process we ran into a number of issues which caused the release to take longer than expected.
While we are happy we were able to get a release out the same day we were notified of the issue, in the modern security environment we feel that we should be able to provide releases in a more compressed timeline. The issues we ran into caused our release process to take just under four and a quarter hours from start to finish.
These issues had previously not been problematic, since the typical release process is split up over a number of days.
Weâve subsequently made a number of changes to improve our release efficiency, which should significantly reduce the time it takes us to push out a release, enhancing our ability to quickly respond to urgent security threats.
PRIVATE track security fixes are pre-announced three to seven days before their release. URGENT track fixes happen on a shorter time schedule, but we could have pre-announced the upcoming release when we began the release process, giving users at least a few hours to prepare.
While on Windows and macOS we rely on the revocation mechanisms the platform APIs provide, on Linux we donât currently have a similar mechanism. The lack of this support was one of our major drivers to issue an expedited security release, as we did not want to leave Linux users exposed to this issue longer than was necessary.
Hence we are planning on prioritizing work to bring parity to revocation support on Linux, to help mitigate this class of issues in the future. Part of this work will involve investigating adding support for the upki project, which aims to âprovide reliable, privacy-preserving and efficient certificate revocationâ for Linux (see issue #82402).
Upgrading and Verifying Your Deployments
If you run Go binaries which interact with network services using TLS (such as directly via net/http or crypto/tls or via a dependency such as gRPC), ensure you are running on the latest minor releases. Binaries rebuilt with Go 1.27.3 and 1.26.10 will automatically reject certificates issued by the compromised eMudhra intermediates.
You can verify your current toolchain version using go version and you can check the version of Go a binary was built with using go version -m .
The govulncheck tool can also be used to determine if a binary is affected by this issue:
$ GOTOOLCHAIN=go1.27.3+auto go install golang.org/x/vuln/cmd/govulncheck@latest
$ govulncheck -mode=binary
Securing Go Deployments Everywhere
For us, securing Go and its users extends beyond fixing direct security vulnerabilities in the runtime and libraries we ship. In order to keep the ecosystem secure, we believe we also need to provide users with the right tools to write secure software, look towards the future of cryptographic algorithms and protocols, and proactively respond to ecosystem wide issues when we can.
We are grateful to the independent security researchers and the Mozilla security team for this rapid notification and coordination during this incident.
Previous article: Help set Go's priorities for next year
Blog Index
https://go.dev/blog/emudhra-incident
#rss #feeds #linksharing #qwibbit
#82402
#rss
#feeds
#linksharing
#qwibbit
GoWire7d
Why Go had to hardcode a block on broken certs
#â621 â October 23, 2026
Read the Web Version
Go Weekly
Interactive Refactoring in gopls â LSP refactorings can't 'ask' you anything, so "Add struct tags" in gopls just guessed json and camelCase. gopls can now pop up dialogs to collect your input. It uses a stateless, form-like protocol extension the Go and Dart teams have proposed as an LSP standard.
Hongxiang Jiang (Go Team)
đĄ For now it works in VS Code, with Neovim and Emacs support at proof-of-concept stage. A Move declaration refactoring is coming soon, with Change signature to follow.
Deploy Self-Healing Apps on Kubernetes â Learn Kubernetes by building a real production setup. Erik Reinert takes you from a local Kind cluster to Amazon EKS with health probes, autoscaling, safe rollbacks, and GitOps with Argo CD.
Master.dev sponsor
đ« Go Hardcodes a Block on Broken eMudhra Certs â Eight intermediate CA certificates had trivially recoverable private keys, so Go 1.27.3 and 1.26.10 now refuse chains through them. It's hardcoded because, as Filippo Valsorda notes, Linux trust stores have no way to pass revocations on to apps.
The Go Team
IN BRIEF:
đș The GopherCon Europe 2026 talks are now online, including â¶ïž using Go to build for the Sega Dreamcast(!) and a â¶ïž panel with the Go team.
đźđł GopherCon India 2026 takes place in Hyderabad on November 22, with tickets now on sale.
Fiber has marked its v2 module as deprecated, so Go's tooling will now nudge v2 users toward Fiber v3.
GO 1.28 & PROPOSALS:
Go 1.28 will let you omit a composite literal's type when the context supplies it (so p = {1, 2} will work).
iter.Seq could gain Map and Filter methods now that Go has generic methods, with a new proposal's thread already debating where to stop. Fold? Keys? Map2?
net/http is getting Server.HandleRequest (accepted last week), while maps.Grow and an RSS-based memory profile for runtime/pprof are both likely accepts.
Better HTTP/1 Connection Reuse â You can now (mostly) stop draining HTTP/1 response bodies in Go 1.27 as the client now drains the leftovers in the background, within limits, making io.Copy(io.Discard, resp.Body) helpers largely redundant when you close the body.
Nicholas Husin and Damien Neil (Go Team)
đĄ The release notes add a caveat the post skips: programs that let idle connections pile up (say, with MaxIdleConns set to 0 or a new Client per request) may get slower.
SIMD and Go: How the New Packages Work Under the Hood â A heavily illustrated tour of Go's experimental SIMD support, covering what vector instructions actually do, the operations offered by simd/archsimd and the new portable simd package, and how the compiler turns them into machine code.
JesĂșs Espino
One Less System to Operate. Everything in SQL â TimescaleDB extends Postgres so your metrics and app data share one database and one SQL join. Get $1000 credit.
Tiger Data (creators of TimescaleDB) sponsor
đ Spinning is a Bounded Bet: Why Go Hides Its Spinlocks â Part one of a four-part series on spinning versus parking. Alex Rios
đ ConcurrentML: Improving on Go's Concurrency Model â Makes the case for composable events as a fix for the limits of select. Nikhil Marathe
đ Cutting a Go Git Server's RAM Usage by Two-Thirds â Using pprof to take a push from 617MB to 199MB. Xe Iaso (Tigris)
đ Why a Healthy Go Heap Can Still Get OOM-Killed in Kubernetes Gulcan Topcu
đ Dancing Backwards with Go: Why TDD Feels Weird John Arundel
đ Code & Tools
MyGo: Build Desktop Apps in Go with No Cgo Required â Build macOS, Windows and Linux apps with a UI drawn on the GPU from pure Go, or a web frontend in the system webview that calls Go via a generated TypeScript client. Apps can be just a few megabytes. GitHub repo.
EGOIST
TinyGo 0.43: Go for Microcontrollers and WebAssembly â The Go compiler 'for small places' closes more gaps with regular Go, adding testing/synctest, parallel tests, panic recovery on Wasm, and more. Much of the work comes from Jake Bailey of TypeScript's Go port fame.
The TinyGo Team
đĄ For some TinyGo fun, Hoani Bryson's Trick Shot II won this year's Nokia 3310 Jam, built on his own Go engine that even runs on a Pico inside a real 3310.
coregex: A Faster Drop-In Replacement for regexp â Brings the multi-engine design of Rust's regex crate to Go, with lazy DFA, PikeVM, one-pass matching and SIMD prefilters. The author claims 15â240x speedups.
Andrey Kolkov
đĄ The standard library may close some of that gap: a port of RE2's lazy DFA to regexp is up for review, with early benchmarks showing big performance improvements.
gopls 0.24 â The official Go language server moves to LSP 3.18 and adds hover, highlighting and references in Go assembly files (interactive refactoring is covered in the feature at the top of the issue).
GoReleaser 2.19 â The release automation tool graduates several experimental features and adds tunable zstd compression for archives.
fasthttp 1.76 â The fast HTTP package gets extra hardening against request smuggling and message-framing attacks.
decimal 1.6 â Arbitrary-precision fixed-point decimals, now with Sqrt, Root and BSON Decimal128.
chi 5.5 â Lightweight router, now with RFC 9745 deprecation middleware.
đ° Classifieds
Build your Golang app faster. WarpBuild runners are twice as fast at half the cost. Get $50 in free credits with code START50.
CodeAF, an open-source coding harness written in Go: #1 of ten on DeepSWE on the same model, at 2x to 25x less per fix than the others.
đȘ Send webhooks from your Go service in minutes. Svix handles retries, signing, delivery logs, and a portal for your customers.
â And finallyâŠ
Help Set Go's Priorities with the 2026 Go Developer Survey â The Go team uses the results to decide what to work on. Last year, the top frustration was keeping Go code idiomatic and following best practices, which led to the rewritten go fix in Go 1.26 and more modernizers in 1.27.
Cameron Balahan (Go Team)
https://golangweekly.com/issues/621
#rss #feeds #linksharing #qwibbit
#1
#rss
#feeds
#linksharing
#qwibbit
GoWire9d
Help set Go's priorities for next year
The Go Blog
Help set Go's priorities for next year
Cameron Balahan, on behalf of the Go team
21 October 2026
Hi Gophers! The 2026 Go Developer Survey is now open. We run this survey every year to understand how you use Go and the challenges you face. Your answers directly shape how we plan and prioritize our work.
Last year, 5,379 of you responded, and your top frustration was making sure your code follows best practices and Go idioms (33% of respondents). Since then, we shipped a rewritten go fix in Go 1.26 that automatically modernizes your code, and added more modernizers in Go 1.27. You can read everything we learned in the 2025 results.
Take the survey here. Itâs open through November 3, takes about 10â20 minutes, and every question is optional. Weâll share the results on this blog in early 2027.
Please share the survey with your colleagues, friends, and online communitiesâthe more Go developers we hear from, the better we understand what you need.
Thank you for helping shape the future of Go!
Previous article: Interactive refactoring in gopls
Blog Index
https://go.dev/blog/survey2026-announce
#nextyear #cameronbalahan #set #survey #blogindex #october #developersurvey #gophers #november #interactive
#rss #feeds #linksharing #qwibbit
#nextyear
#cameronbalahan
#set
#survey
#blogindex
GoWire14d
Interactive refactoring in gopls
The Go Blog
Interactive refactoring in gopls
Hongxiang Jiang
16 October 2026
When editing code in a modern IDE or editor, developers rely on its refactoring
features to automate common transformations. Today, we are excited to introduce
interactive refactoring in gopls, powered by a new capability we are
proposing for the Language Server Protocol (LSP)
that brings dynamic, multi-step dialogs directly into your favorite editor.
Crucially, we designed this capability (in collaboration with the Dart team)
not just for Go, but as an open standard for all programming languages and
editor ecosystems. Working support is already available in gopls v0.24.0 and
the official Go extension for VS Code v0.56.0.
What is the Language Server Protocol (LSP)?
The LSP is an open standard that decouples language-specific
features from specific text editors. As you navigate and edit code, your
editor (such as VS Code, Neovim, or Emacs) and a language server (like gopls)
engage in a continuous JSON-RPC exchange to provide features like autocomplete,
hover documentation, diagnostics, and code actions.
Multi-step code transformation
Today, almost every code transformation in the LSP is like a function of no
parameters: you trigger the operation, and the server immediately computes the
edits. (Renaming a symbol is the sole exception: because it is a function of one
parameter, the desired new name, it required its own dedicated PrepareRename
RPC in the protocol.)
"Add struct tags", without questions
The language server must deduce the userâs intent from context alone, namely
the cursor position and the enclosing syntax tree. When that context is not
enough, the server is forced into one of two choices: apply a one-size-fits-all
default, or refrain from offering the transformation altogether. The limitation
lies not in goplsâs ability to compute the necessary edits, but in the
LSPâs lack of a mechanism for language servers to ask the user for input.
For many transformations, this parameterless model works well. But many others
need additional input from the user: which struct tags to add and in what
naming convention, which interface to implement, which file to move a
declaration to. Without a way to ask, gopls could only guessââAdd struct
tagsâ, for example, always produced json tags in camelCaseâor not offer the
refactoring at all. With interactive refactoring, language servers can now
easily provide new operations with one or more parameters.
"Add struct tags", with questions
While some language extensions have worked around this limitation by embedding
custom, language-specific dialog logic directly into individual editors, doing
so leaves behind other editors (such as Neovim and Emacs) that stick strictly
to the LSP with minimal custom client code. More importantly, editor-specific
workarounds go against the fundamental purpose of the LSP: to decouple
language-specific features from editors so that each server improvement benefits
every client.
Our LSP interactive refactoring proposal
(microsoft/language-server-protocol#1164)
solves this by establishing a standard mechanism for
language servers to collect user input before computing text edits. For Go
developers, this unlocks a suite of long-awaited refactorings, including:
golang/go#70583: Move Declaration
golang/go#38028: Change Signature
golang/vscode-go#1547: Implement Interface
golang/vscode-go#2002: Modify Struct Tags
among many others.
Interactive refactorings in action
Adding and removing struct tags (gopls v0.23.0)
When configuring serialization formats, developers frequently need custom tags
and casing conventions. Previously, running âAdd struct tagsâ unconditionally
injected json tags using camelCase formatting.
With interactive refactoring:
Place your cursor on any struct definition and select the
âAdd struct tagsâ code action.
An input dialog appears, pre-filled with json, allowing you to customize
any combination of tags (such as json,yaml,db).
A second dialog lets you select your desired naming convention (such as
camelCase, snake_case, or PascalCase).
Gopls generates and applies the formatted struct tags across your type
definition.
Interactive "Add struct tags" in VS Code
Here is the before-and-after transformation:
type UserProfile struct {
ID int64
FirstName string
LastName string
EmailAddr string
}
type UserProfile struct {
ID int64 `json:"id" db:"id"`
FirstName string `json:"first_name" db:"first_name"`
LastName string `json:"last_name" db:"last_name"`
EmailAddr string `json:"email_addr" db:"email_addr"`
}
Similarly, âRemove struct tagsâ now asks you for the specific tag keys
you wish to delete (for example, removing only db tags while preserving the json
tag), rather than wiping out all struct tags indiscriminately.
Interactive "Remove struct tags" in VS Code
Implementing interfaces with dynamic symbol search
When creating a type to satisfy an interface, developers previously had to copy
the interface declaration and manually turn it into a set of methods on the
receiver type (or rely on external tools like impl by manually typing the
receiver and qualifying the name of the interface).
Since gopls v0.23.0, with interactive refactoring:
Place your cursor on any concrete type and select the
âAdd methods to X to implement an interfaceâŠâ code action.
A text entry field appears, inviting you to choose the name of an interface
type. It makes dynamic queries to gopls to auto-complete names of types in
your workspace and the standard library (for example, maphash.Hasher).
If the selected interface has type parameters, gopls asks for a
concrete type argument for each type parameter (such as Foo).
Gopls generates complete method stubs with appropriate receiver names and
instantiated method signatures.
Interactive "Add methods to implement an interface..." in
VS Code
type Foo struct{}
type MyHasher struct{}
type Foo struct{}
type MyHasher struct{}
// Equal implements [maphash.Hasher].
func (m *MyHasher) Equal(x Foo, y Foo) bool {
panic("unimplemented")
}
// Hash implements [maphash.Hasher].
func (m *MyHasher) Hash(*maphash.Hash, Foo) {
panic("unimplemented")
}
Moving declarations across files and packages (preview)
Moving declarations manually by cutting and pasting can easily break references
and requires tedious import cleanup across multiple files. While scheduled for
an upcoming gopls release, our working implementation already demonstrates how
interactive file selection streamlines cross-file and cross-package moves:
Place your cursor on any top-level declaration and select the
âMove declaration Xâ code action.
A file picker dialog lets you choose an existing destination .go file or
specify a new file path.
Gopls moves the declaration along with its dependent declarations (such as
unexported helpers or methods) while leaving unrelated symbols in place,
updates references across your workspace, and cleans up imports in both the
source and destination files.
Interactive "Move declaration" in VS Code
In the example below, we move the type Foo from package from to package
to. Its method Print and the unexported constant bar, which only Foo
uses, move along with it, while the unrelated function Baz stays behind:
// from/from.go
package from
import (
"fmt"
"strings"
)
const bar = "bar"
// Doc comment for Foo
type Foo struct{}
func (f Foo) Print() {
fmt.Println(bar)
}
func Baz() string {
return strings.ToUpper("baz")
}
// from/from.go
package from
import "strings"
func Baz() string {
return strings.ToUpper("baz")
}
// to/to.go
package to
import "fmt"
const bar = "bar"
// Doc comment for Foo
type Foo struct{}
func (f Foo) Print() {
fmt.Println(bar)
}
Slaying the dragon (gopls v0.24.0)
As a demonstration of multi-step interactive dialogs and a self-contained test
case for client editor implementations, gopls v0.24.0 offers a âSlay the
dragonâ code action when you place your cursor on a package-level variable
named dragonSlayer (for example, var dragonSlayer string). Happy hunting!
Whatâs on the Horizon
Interactive refactoring establishes a foundational capability in gopls,
opening up several exciting directions on our roadmap:
Change signature refactoring (#38028): Adding, removing,
or reordering function parameters across a workspace requires input that
cannot be deduced from syntax alone, such as the new parameter order or the
default argument expression to insert at existing call sites. Interactive
forms allow gopls to collect these details before rewriting the declaration
and all call sites.
Interactive conflict resolution: When a code transformation encounters
naming collisions, identifier shadowing, or ambiguous references, gopls can
ask the user for guidance, such as asking for permission to proceed or
offering to rename conflicting symbols on the fly.
Interactive commands beyond code edits: Because interactivity is modeled
at the LSP command
layer, it naturally generalizes to non-edit server
operations that require user configuration, such as configuring test or
benchmark runs with profiling flags (CPU, memory, tracing).
Getting Started and Sharing Feedback
Interactive refactoring is available in gopls v0.24.0
and vscode-go v0.56.0
(with proof-of-concept client integrations also explored for
Neovim and
Emacs).
We would love to hear your thoughts as we continue
refining interactive code actions in gopls:
For general feedback or feature requests, file an issue on the Go issue tracker.
To read the full technical specificationâincluding the stateless form
protocol design and alternative approaches we exploredâor to join the
discussion with the broader LSP community, check out the
LSP Interactive Code Actions design document
and the ongoing conversation in microsoft/language-server-protocol#1164.
(Special thanks to Alan Donovan, Alex Putman, Brian Wilkerson, Danny Tuppeny,
Hana Kim, Madeline Kalil, Peter Weinberger, and Robert Findley for their
insights, discussions, feedback, and collaboration on this work.)
Happy refactoring!
Previous article: Better HTTP/1 connection reuse
Blog Index
https://go.dev/blog/interactive-refactoring
#rss #feeds #linksharing #qwibbit
#38028
#rss
#feeds
#linksharing
#qwibbit
GoWire15d
Building cgo code without a C compiler?
#â617 â September 11, 2026
Read the Web Version
Go Weekly
Could Go Build cgo Packages Without a C Compiler? â Cross-compiling with import "C" can be painful as it needs a C toolchain. This proposal would let packages that only call pre-built C libraries describe the interface in annotated Go files, so anyone building the package wouldn't need a C compiler.
Matloob, Mui, et al. (Go Team)
đĄ Another approach is to skip cgo entirely, which the SIMD and Ebitengine items later in this issue do in two quite different waysâŠ
Become a Confident Go Backend Engineer â Build a production-grade Go backend and learn by coding. Practice the skills you need to guide your AI coding agents. Hands-on online training from the authors of Watermill. Opens in the coming weeks. Join the waitlist.
Three Dots Labs sponsor
How Debian Code Search Got Fast TurboPFor in Pure Go with SIMD â One for performance fans! A detailed write-up of using Goâs experimental simd/archsimd package to replace a C library with native Go that runs faster. Covers PGO surprises, generics for compile-time specialization, AVX2/AVX512 kernels, and more.
Michael Stapelberg
IN BRIEF:
đ JetBrains recently held a Go 1.27 'release party' featuring some of the Go team. â¶ïž Watch the recording here, including Robert Griesemer who explains why generic methods took so long to arrive.
Long-time contributor thepudds (a.k.a. PJ) has joined the core Go team. Russ Cox is a fan, saying "What a great hire for the Go team."
Go 1.28 seems likely to reject string(x) for integers other than byte or rune, in line with go vet's warnings. (Rob Pike proposed this 14 years ago!)
Jake Bailey has some CLs in review to make encoding/json/v2 marshal slices ~4% faster than v1, after Daniel Lemire found it 1.5x slower recently.
đčïž Ebitengine 2.10: Now Pure Go on Desktop Platforms â After years of effort, the popular 2D game engine no longer needs cgo on macOS, Linux or BSD (joining Windows in this regard) so cross-compilation is a lot easier. Thereâs also a new ability to run apps as a controllable guest process, either headlessly for testing or embedded into other apps.
Hajime Hoshi
đĄ The cgo-free approach is made possible by purego, the Ebitengine team's library for calling C functions from Go without cgo.
Open Source Hits Frontier-Level Code Review at a Tenth of the Cost â PR-AF places #2 of 42 on Martian's Code-Review-Bench. Verified findings only, self-hosted, any open model.
AgentField.ai sponsor
đ Four Ways to Do Super Fast Packet Processing in Go â AF_PACKET, AF_XDP, DPDK and Mellanox Direct Verbs compared, with real throughput numbers, and a library wrapping them. Andree Toonk
đ Sloc Cloc and Code: Did I Just Get Buster Scrugged? â A tale of profiling scc after a Rust rival counted code 2.5x faster, and winning back the lead. Ben Boyter
đ Implementing Type Contracts via Mutually Referencing Type Parameters Nick Tobey (DoltHub)
đ I Contributed to Go: Here's How You Can Too Alex Pliutau
đ The 14-Year Road to encoding/json/v2 Yoshi Yamaguchi
đ Code & Tools
gomodjail 2.0: An Experimental Jail for Go Modules â From the creator of Lima comes an interesting way to confine Go dependencies: mark a module with a comment in go.mod and a gomodjail analyze check fails if it can reach the filesystem, network, raw syscalls, etc.
Akihiro Suda
VHS 0.12: The 'Home Video Recorder' for Your CLI â A tool and scripting language for performing actions on the terminal that get recorded into an animated GIF, MP4, WebM, or PNG frames. Great for demos or tutorials. v0.12.0 lets you size the output by rows and columns (instead of just pixels).
Charm
đ€ yzma 1.26: Local LLM Inference from Go, Now in the Browser â Run vision and language models from Go through llama.cpp with no cgo required (thanks to purego and ffi). Notably, v1.26.0 adds WebAssembly support, so a TinyGo build can run inference directly on a web page.
The Hybrid Group
go-oracledb: An Official Pure Go Driver for Oracle Database â It's beta for now, but interesting to see an official Go client from Oracle. The established third party options are go-ora (also pure Go) and godror (which uses Oracleâs client libraries via cgo).
Oracle
đ° Classifieds
Your Go stack is minimal. Your data layer can be too. TimescaleDB extends Postgres for metrics and analytics. Get $1000 credit.
Software Engineer - Join our "kick ass" team! Our software team operates from 17 countries and we're looking for more exceptional engineers to join our team.
đ Oto 3.5 â Library to play audio on multiple platforms. Used by Ebitengine, it's now cgo-free on Linux and BSD using PulseAudio, with an ALSA fallback.
gofumpt 0.12 â Stricter formatter for Go that enforces additional rules beyond gofmt. Now based on Go 1.27's gofmt but works on Go 1.26 and up.
go-tui 0.20 â Build declarative terminal UIs with a React-inspired approach.
workers-go 0.35 â Run HTTP servers written in Go on Cloudflare Workers.
đ TZF 2.0 â Fast timezone finder from lat/long coordinates.
go-brrr 1.1 â Pure Go Brotli compression/decompression.
https://golangweekly.com/issues/617
GoWire15d
Generic Methods
The Go Blog
Generic Methods
Mark Freeman
26 August 2026
Generics was a profound change for Goâit fundamentally expanded the kinds of
programs that gophers could write by adding type parameters to the language.
One could now express generic types and functions in Go. These constructs
decreased the need for specialized data types and functions, reducing program
verbosity and improving the ergonomics of the language in those cases.
To illustrate, different kinds of linked lists could now be condensed into a
single type definition:
// before Go 1.18
type ListOfInts struct {
elem int
next *ListOfInts
}
type ListOfStrings struct {
elem string
next *ListOfStrings
}
// ...
// after Go 1.18
type List[E any] struct {
elem E
next *List[E]
}
Likewise, sorting different kinds of ordered data could be expressed as a single
function:
// before Go 1.18
func SortInts(s []int) { /* ... */ }
func SortStrings(s []string) { /* ... */ }
// ...
// after Go 1.18
func Sort[E cmp.Ordered](s []E) { /* ... */ }
However, no such capability came to methods.
The generics proposal reasoned that because generic interface
methods are difficult to implement efficiently (discussed
later), it didnât make sense to
add type parameters to non-interface (or âconcreteâ) methods either. In that
view, methods are primarily the means of implementing an interface.
Go 1.27 takes a different view and consequently adds generic methods to Go. In
this post, weâll explain this change of view and demonstrate some uses of this
new feature.
Methods for organization
Methods enable organizing functionality around types. To illustrate, letâs
reconsider the linked list from above (adding some conveniences):
type List[E any] struct {
elem E
next *List[E]
}
func NewList[E any](elems ...E) List[E] { /* ... */ }
func (List[E]) String() string { /* ... */ }
Suppose that one wanted to map this structure to hold a value of some other
type, like a string. A method like ToString would do:
func (List[E]) ToString(f func(E) string) List[string] { /* ... */ }
By providing a âtransformâ function f, ToString can be customized with
âoff-the-shelfâ routines.
For example, strconv.Itoa might work for a List[int]:
func main() {
fmt.Println(NewList(1, 2, 3).ToString(strconv.Itoa)) // [1 2 3]
}
For a List[[]byte], more options come to mind (depending on the use case):
func main() {
l := NewList([]byte("Hallo Welt"), []byte("HellĂł vilĂĄg"))
fmt.Println(l.ToString(hex.EncodeToString)) // [48616c6c6f2057656c74 48656c6cc3b32076696cc3a167]
fmt.Println(l.ToString(base64.StdEncoding.EncodeToString)) // [SGFsbG8gV2VsdA== SGVsbMOzIHZpbMOhZw==]
}
Note that parameterizing List allowed generalization of the source type, but
not the destination type, since that depends on the transformation being
applied. This might be reasonable with just a single destination type, but what
if there were many?
In Go 1.18, a generic function could be used for this:
// after Go 1.18
func MapList[E, R any](l List[E], f func(E) R) List[R] { /* ... */ }
But using a function has the disadvantage of moving MapList into the package
scopeâif many data types support mapping operations, things could get crowded.
Furthermore, chained calls must be written âinside outâ:
func main() {
fmt.Println(MapList(MapList(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]
}
Because Go 1.18 didnât support generic methods, we needed a generic function as
a workaround. This is now remedied in Go 1.27:
// after Go 1.27
func (List[E]) Map[R any](f func(E) R) List[R] { /* ... */ }
This is compact, expressive, and locally scoped. Additionally, chained calls
are more readable, as they can be written more naturally from left to right:
func main() {
fmt.Println(NewList(0, 2, 4).Map(add(2)).Map(divideBy(2))) // [1 2 3]
}
Prefer the âinside outâ form? A method expression can convert any method,
including a generic one, into its function equivalent. Thus, a function call
structure can be recovered if desired:
func main() {
f := List[int].Map[int]
fmt.Println(f(f(NewList(0, 2, 4), add(2)), divideBy(2))) // [1 2 3]
}
Generic methods, like other generics in Go, must be instantiated (either
explicitly or implicitly) before they are used (called or converted to a
function).
Decoupling things
If one views methods also as an organizational tool, then Go 1.18âs reasoning
appears overly restrictive. While a generic concrete method canât help implement
an interface (without generic interface methods), it can still be useful for
code organization. In other words, these concerns can be decoupled.
To elaborate, letâs consider a simple interface:
type I interface {
M()
}
type T struct{} // a struct, not an interface
func (T) M[P any]() { /* ... */ } // a generic *concrete* method
Here, T.M is parameterized by P. Any instantiation of T.M would result in
a method signature identical to that of I.M:
func main() {
T{}.M[int]()
}
Both T.M[int] and I.M have the signature func M(). However, this doesnât
mean T implements I. Interface implementation is a property of a (possibly
instantiated) type, not of any particular method. Importantly, T does not
declare T.M[int], but rather its generic (and thus uninstantiated) counterpart
T.M.
For T.M to participate in interface implementation, there would need to exist
a suitable generic method I.M:
type I interface {
M[P any]() // a generic *interface* method
}
type T struct{}
func (T) M[P any]() { /* ... */ }
Since the Go 1.27 syntax doesnât allow interface methods to declare type
parameters, this code is impossible to write.
But why canât Go have generic interface methods? To understand, a bit of a
detour is needed.
The trouble with generic interface methods
An interface value is a box that can hold some other valueâthe boxed value can
be of any type, as long as that type implements the interface. This means that
the declared methods on the type are a superset of those declared on the
interface.
To illustrate, T implements I below:
type I interface {
M()
}
type T struct{}
func (T) M() { /* ... */ }
One can call methods on interface values:
func main() {
F(T{})
}
func F(i I) {
i.M() // "backed" by T.M
}
Above, itâs easy to observe that the call to i.M will be routed to T.M. But
in more complex programs, the relation between an interface value method call
and its possible targets is significantly less obvious. This relation
typically spans across packages, making it difficult or impossible to deduce in
the general case.
To illustrate, letâs introduce a package boundary:
// -- package main --
import "p"
func main() {
p.F(T{})
}
type T struct{}
func (T) M() { /* ... */ }
// -- package p --
func F(i I) {
i.M()
}
type I interface {
M()
}
Because the two packages are compiled separately, the main package wonât know
how p.F uses T{}. To ensure the existence of any method that p.F might
call, the compiler generates code for all non-generic methods of a type at its
declaration (or instantiation). That way, regardless of what p.F does with
T{}, the necessary code will exist at runtime.
Now, suppose that I.M were generic:
// -- package main --
import "p"
func main() {
p.F(T{})
}
type T struct{}
func (T) M[P any]() { /* ... */ }
// -- package p --
func F(i I) {
i.M[int]()
}
type I interface {
M[P any]()
}
Again, the main package wonât know how p.F uses T{}âincluding how it might
instantiate T.M. To handle any usage of T{} inside p.F, the compiler
would need to instantiate T.M with every possible type argument.
This is impractical with Goâs approach to instantiation, wherein the compiler
generates specific code for each method based on the type arguments. If method
arguments were âboxedâ (i.e. passed as values of their constraint interfaces),
each instantiation would share the same code. This would avoid having too many
instantiations at the cost of indirect call overheadâeven for direct calls to
instantiated methods.
Conclusion
Go 1.27 introduces type parameters on concrete methodsâthis feature has been
highly desired by the Go community, as it permits more ergonomic and readable
code. Although we canât support generic interface methods, we decided that
allowing type parameters for concrete methods was still worthwhile.
We hope you enjoy using Goâs generic methods and find helpful ways to use them
in your projects!
Next article: Goroutine Leak Profiles
Previous article: Go 1.27 is released
Blog Index
https://go.dev/blog/generic-methods
GoWire15d
Platform-independent SIMD in Go
The Go Blog
Platform-independent SIMD in Go
David Chase and Junyang Shao
24 September 2026
Go 1.26 and 1.27 include experimental APIs for Single Instruction Multiple Data (SIMD) operations. SIMD is a native feature of many modern CPUs that allows software to perform uniform operations across vectors of data very quickly, such as adding 8 pairs of float64 values in a single instruction. It can significantly speed up many computationally-intensive tasks, ranging from cryptography to data processing to AI. In fact, Goâs Green Tea garbage collector even makes use of SIMD to accelerate scanning memory for live objects.
Prior to these new experimental APIs, the only way to access this functionality from Go was by writing Go assembly. This was only worth it for truly performance-critical compute kernels, which meant plenty of software that could benefit from SIMD simply left a lot of the CPU unused.
Go 1.26 introduced a SIMD API for amd64, and Go 1.27 added APIs for arm64 (specifically NEON) and wasm. However, a basic challenge for a SIMD API is the enormous variation between platforms, not simply in what operations they support, but even in how vectors are represented. Some platforms provide fixed-size vectors, typically between 128 bits and 512 bits, while on others the vector size isnât known at build time and must be queried when the program starts. To provide full access to the breadth of these platforms, these APIs live in an architecture-dependent archsimd package.
But Go 1.27 goes beyond these architecture-dependent APIs and introduces an experimental, fully portable, platform- and size-agnostic SIMD interface,
loosely based on Highway for C++. The goal is to support write-once near-asm-performance âsimdâ code on platforms with SIMD support, and to provide a competent emulation on those platforms that do not (yet) have SIMD support. The simd package currently supports AVX, AVX2, and AVX512 on amd64, NEON on arm64, and wasmâs SIMD instructions.
Motivation: variation among SIMD architectures
SIMD architectures vary in several dimensions. Some provide a single fixed vector size (wasm, PowerPC, and s390x, 128 bits). Some provide several fixed vector sizes (amd64, with 128, 256, and 512; loong64 with 128 and 256). Riscv64 supports vectors of unspecified size between 128 and 65536 bits, though the length is limited to powers of 2. Arm64 supports one fixed size (128 bits, NEON), and one variable size (128-2048 bits, powers of two only, SVE). On a given instance of a particular architecture, determining what sizes that particular instance happens to support requires feature checks: amd64, but is it AVX, AVX2, or AVX512? Arm64, but is it NEON or SVE? If SVE, how large? Which variant of SVE: SVE, SVE2, or SVE2.1?
Different SIMD architectures vary in how they handle vector masking. For vectors, if-then-else across a vector can be implemented with masks; do the operation, but only assign the result (or load, or store) where the mask is âtrueâ. Some SIMD variants do not provide masks; all operations work across all elements, and âmaskingâ is done with vector bitmasks and vector boolean operations (wasm, AVX, AVX2, NEON). Some provide special mask registers, with one bit governing operations on one vector element (AVX512 and RVV). Others (SVE) allocate one bit per vector byte, but the least-significant bit of each elementâs mask bits governs masked operations. AVX2 also supports masked loads and stores, but using a plain vector as the mask, and with the most-significant bit governing the operation.
A third source of variation is in the operations themselves. Each architecture provides its own primitives for rearranging vector elements; some require constant inputs, others support variable inputs. Different SIMD architectures support different crypto-related operations. Even basic arithmetic can have varying support; for example wasm lacks comparisons for vectors of 64-bit integers. Even for a given vector length on a particular architecture, instruction support depends on âfeaturesâ that must be checked.
Even though Goâs architecture-dependent archsimd package was designed to be as uniform as possible across architectures, many of these quirks remain, and make designing, writing, and testing code for multiplatform SIMD onerous. We could do more in the archsimd package to make the different architectures appear more similar, but we can only go so far without compromising efficiency.
Overview
The new simd package hides these differences by removing fixed-size vectors from the type system, and by only supporting those operations that are in the intersection of all the different platforms, and fills gaps in the intersection with efficient emulation in terms of other SIMD instructions. The goal is a set of operations that is
adequate to support many data processing algorithms that benefit from a vectorized implementation (but are not tied to a particular vector size),
is as efficient as assembly language when the source code operations match the underlying hardware,
is otherwise emulated as well as possible,
and is easy to read and understand (even/especially if an LLM ends up writing the code).
On platforms that lack SIMD instructions or that lack support in archsimd, all of the operations are emulated,
so that code written using the simd package will always run.
To use this experimental package, set GOEXPERIMENT=simd at build time, just like using the experimental archsimd package.
The simd vector types are just capitalized, plural, primitive types, for example simd.Uint8s or simd.Float32s. Vectors are loaded from and stored to slices, for example:
// innerProduct returns the inner product of x and y.
func innerProduct(x, y []float32) float32 {
var a simd.Float32s
var i int
for i = 0; i < len(x)-a.Len()+1; i += a.Len() {
u := simd.LoadFloat32s(x[i : i+a.Len()])
v := simd.LoadFloat32s(y[i : i+a.Len()])
a = u.MulAdd(v, a)
}
if i < len(x) {
u, _ := simd.LoadFloat32sPart(x[i:])
v, _ := simd.LoadFloat32sPart(y[i:])
a = u.MulAdd(v, a)
}
return sum(a)
}
// sum returns scalar sum of elements of x.
func sum(x simd.Float32s) float32 {
s := make([]float32, x.Len())
x.Store(s)
var r float32
for _, e := range s {
r += e
}
return r
}
This example also shows one of the limitations of the first experimental release of this package; because thereâs no common way to sum across all the elements of a vector, itâs not supported by simd in Go 1.27, though ReduceSum will appear in the next release so sum can be replaced with just simd.ReduceSum.
SIMD comparisons produce mask values, which are specific to the corresponding vector element width, so that comparisons of Int8s produce Mask8s, etc., and mask values can be used to select and filter vectors.
Supported simd package operations as of Go 1.27
In this table, V and U are vector types, M is a mask type, E is a scalar type, and W is a width.
Package-Level Load / Broadcast Functions
Function
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
LoadV([]E) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
LoadVPart([]E) (V, int)
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
BroadcastV(E) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Store/String operations
(x V).Method(...)
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
Store(s []E)
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
StorePart(s []E) int
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
String() string
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Arithmetic operations
(x V).Method(...) V
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
Abs() V
Y
Y
Y
Y
Y
Add(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
AddSaturated(y V) V
Y
Y
Y
Y
Average(y V) V
Y
Y
Div(y V) V
Y
Y
IfElse(mask MaskWs, y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Len() int
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Masked(mask MaskWs) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Max(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Min(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Mul(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
MulAdd(y V, z V) V
Y
Y
Neg() V
Y
Y
Y
Y
Y
Y
Not() V
Y
Y
Y
Y
Y
Y
Y
Y
Or(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Sqrt() V
Y
Y
Sub(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
SubSaturated(y V) V
Y
Y
Y
Y
Xor(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Boolean and vector masking operations
(x V).Method(...) V
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
And(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
AndNot(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
CarrylessMultiplyEven(y V) V
Y
CarrylessMultiplyOdd(y V) V
Y
IfElse(mask MaskWs, y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Masked(mask MaskWs) V
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Not() V
Y
Y
Y
Y
Y
Y
Y
Y
Or(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Xor(y V) V
Y
Y
Y
Y
Y
Y
Y
Y
Comparison operations
(x V).Method(...) M
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
Equal(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Greater(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
GreaterEqual(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
Less(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
LessEqual(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
NotEqual(y V) MaskWs
Y
Y
Y
Y
Y
Y
Y
Y
Y
Y
Conversion operations
(x V).Method(...) U
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
ConvertToFloatW() FloatWs
Y
ConvertToIntW() IntWs
Y
Y
Y
Y
Y
ConvertToUintW() UintWs
Y
Y
Y
Y
ToMask() (to MaskWs)
Y
Y
Y
Y
Mask Methods
(m M).Method(...) M)
Mask8s
Mask16s
Mask32s
Mask64s
And(y M) M
Y
Y
Y
Y
Or(y V) V
Y
Y
Y
Y
String() string
Y
Y
Y
Y
ToIntWs() (to IntWs)
Y
Y
Y
Y
Shift and rotate operations
(x V).Method() V
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
RotateAllLeft(dist uint64) V
Y
Y
Y
Y
Y
Y
RotateAllRight(dist uint64) V
Y
Y
Y
Y
Y
Y
ShiftAllLeft(dist uint64) V
Y
Y
Y
Y
Y
Y
ShiftAllRight(dist uint64) V
Y
Y
Y
Y
Y
Zero-cost reshaping operations
(x V).Method(...) U
Int8s
Int16s
Int32s
Int64s
Uint8s
Uint16s
Uint32s
Uint64s
Float32s
Float64s
ToBits() UintWs
Y
Y
Y
Y
Y
Y
ReshapeToUint8s() Uint8s
Y
Y
Y
ReshapeToUint16s() Uint16s
Y
Y
Y
ReshapeToUint32s() Uint32s
Y
Y
Y
ReshapeToUint64s() Uint64s
Y
Y
Y
BitsToFloatW() FloatWs
Y
Y
BitsToIntW() IntWs
Y
Y
Y
Y
Transition to/from platform-specific code
It may happen that the simd package is too limited for all parts of a particular application, or that we have not yet provided an adequate emulation for some necessary feature. For that case, the simd package supports transition to and from architecture-specific SIMD. Each vector type in the simd package has a conversion method ToArch() returning an any. That any can be type-asserted to one of the architecture-specific types for a platform. To convert back, use one of the simd.FromArch functions. For portable code this creates an obligation to write architecture-specific code for each of the platforms, including an emulation.
Hereâs a complete example for a method/function that is currently missing, but should be added in Go 1.28. Suppose your algorithm needs Int8s.OnesCount() (which simd in Go 1.27 lacks). Rather than rewriting the entire algorithm for each platform, itâs possible to just implement the missing operation.
First, for amd64, which lacks the instruction for AVX and AVX2, but not AVX512:
//go:build goexperiment.simd && amd64
package simd_test
import (
"simd"
"simd/archsimd"
)
var popcnt4x16 = [16]int8{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
var popcnt4x32 = [32]int8{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
}
// OnesCount returns the number of one bits for each element.
func OnesCount(v simd.Int8s) simd.Int8s {
switch x := v.ToArch().(type) {
case archsimd.Int8x16:
lut := archsimd.LoadInt8x16Array(&popcnt4x16)
mask0f := archsimd.BroadcastInt8x16(0x0f)
lo := x.And(mask0f)
hi := x.ToBits().ReshapeToUint16s().ShiftAllRight(4).
ReshapeToUint8s().BitsToInt8().And(mask0f)
return simd.Int8sFromArch(lut.PermuteOrZero(lo).
Add(lut.PermuteOrZero(hi)))
case archsimd.Int8x32:
lut := archsimd.LoadInt8x32Array(&popcnt4x32)
mask0f := archsimd.BroadcastInt8x32(0x0f)
lo := x.And(mask0f)
hi := x.ToBits().ReshapeToUint16s().ShiftAllRight(4).
ReshapeToUint8s().BitsToInt8().And(mask0f)
return simd.Int8sFromArch(lut.PermuteOrZeroGrouped(lo).
Add(lut.PermuteOrZeroGrouped(hi)))
case archsimd.Int8x64:
return simd.Int8sFromArch(x.OnesCount())
default:
// GODEBUG=simd=0 emulation
return OnesCountEmulated(v)
}
}
The interface conversion and type switch look like they should be inefficient, but the compiler-side implementation of simd specializes code and optimizes away the type switch.
NEON and Wasm both support Int8s.OnesCount(), so their implementation is much simpler, though it still uses Int8s.ToArch and Int8sFromArch.
//go:build goexperiment.simd && (wasm || arm64)
package simd_test
import (
"simd"
"simd/archsimd"
)
// OnesCount returns the number of one bits for each element.
func OnesCount(v simd.Int8s) simd.Int8s {
// TODO when SVE is added, this won't work
switch x := v.ToArch().(type) {
case archsimd.Int8x16:
return simd.Int8sFromArch(x.OnesCount())
default:
// GODEBUG=simd=0 emulation
return OnesCountEmulated(v)
}
}
Donât forget that some people donât have hardware SIMD support:
//go:build goexperiment.simd && !(amd64 || wasm || arm64)
package simd_test
import (
"simd"
)
// OnesCount returns the number of one bits for each element.
func OnesCount(v simd.Int8s) simd.Int8s {
return OnesCountEmulated(v)
}
And to complete the exercise, a separate emulation function shared as a fallback across all implementations:
//go:build goexperiment.simd
package simd_test
import (
"simd"
)
// OnesCountEmulated returns the number of one bits for each element.
func OnesCountEmulated(v simd.Int8s) simd.Int8s {
a := [2]uint64{}
v.ToBits().ReshapeToUint64s().Store(a[:])
a0, a1 := a[0], a[1]
m1 := uint64(0x5555555555555555)
m2 := uint64(0x3333333333333333)
m4 := uint64(0x0f0f0f0f0f0f0f0f)
a0 = (a0 & m1) + ((a0 >> 1) & m1)
a1 = (a1 & m1) + ((a1 >> 1) & m1)
a0 = (a0 & m2) + ((a0 >> 2) & m2)
a1 = (a1 & m2) + ((a1 >> 2) & m2)
a0 = (a0 & m4) + ((a0 >> 4) & m4)
a1 = (a1 & m4) + ((a1 >> 4) & m4)
a[0], a[1] = a0, a1
return simd.LoadUint64s(a[:]).ReshapeToUint8s().BitsToInt8()
}
API intersection and method emulation
Whatever operations the simd package offers need to run acceptably well on most architectures. As a first step, any operation that is supported everywhere, can easily be supported on simd. This tends to include loads, stores, arithmetic, and comparisons (but not all comparisons!).
A naive intersection across SIMD methods from different architectures still leaves plenty of holes.
These are filled by adding emulations to the various architecture-specific archsimd APIs.
These APIs already contain many trivial emulations to simplify life for Go programmers;
signed and unsigned integer addition use the same instruction,
but in the same way that Go supports the + operator for both int and uint,
the archsimd package provides both Int8x16.Add(Int8x16) and Uint8x16.Add(Uint8x16),
even though those compile to the same instruction.
Modern programming languages also donât expect programmers to know how to implement floating point negation and absolute value with bit fiddling,
so archsimd implements that where necessary, or âemulatesâ if you look at it just so.
There are many emulations that require just 2 or 3 instructions; for example, some architectures support only a same-value shift distance across vector elements, while others support a different shift distance for each vector element. To support scalar shifting in simd, we emulate scalar shift with vector shift. Some architectures lack some unsigned comparisonsâthese are just signed comparison, plus two XORs with a constant.
Not all missing instructions are that simple. The âcarryless multiplyâ instruction is important to cryptography and CRC checksumming, but it isnât always supported. Leaving that out of the simd API would prevent its use for some important algorithms. Therefore, we provide an emulation, and because one important use is in crypto, its run time does not vary depending on its inputs.
In other cases, rather than implement a primitive instruction like âadd pairsâ (also called âhorizontal additionâ), for the simd package in the next release we will provide the higher level operation that add pairs is usually used for, which is sum reduction. This also helps insulate users from vector-length dependence; even given the hardware instruction for adding pairs, the number of reduction steps depends on the vector length.
The constraint of supporting all platforms, including ones that we predict will appear in archsimd within the next year or so, forces a somewhat conservative approach to which method
https://go.dev/blog/simd-experiment
GoWire15d
Arch-specific SIMD in Go
The Go Blog
Arch-specific SIMD in Go
Junyang Shao and David Chase
2 October 2026
Since Go 1.26, we have supported an experimental amd64 SIMD API. Now in Go 1.27, we support two more architectures: arm64 and wasm.
The new APIs can be accessed by setting GOEXPERIMENT=simd when building a program. They are defined in the simd/archsimd package.
archsimd is the lower-level infrastructure that simd builds on, essentially the intrinsics layer in other languages. For exotic operations that only exist on a specific architecture, users can access them by transitioning from simd to archsimd (via ToArch and FromArch). For details, please refer to the simd blog post.
For readers with prior knowledge of SIMD intrinsics in other languages, you might find that our archsimd API does not directly mirror the underlying hardware instructions:
We give our APIs sensible names: e.g., instead of _mm512_slli_epi64, we just call it ShiftAllLeft.
We leave some architecture details to compiler optimizations rather than exposing them in the API: e.g., instead of _mm512_maskz_add_ps(m, x, y), we optimize x.Add(y).Masked(m) into the same instruction.
These design decisions make our APIs smaller and more accessible, and they also happily make the simd implementation much easier.
For readers without prior knowledge of SIMD intrinsics, we hope our low-level API is a natural and pleasant journey for you.
Please share your feedback and comments. Anything about usability, performance, etc., is appreciated. The parent issue for this project is #73787.
What is SIMD
SIMD stands for Single Instruction, Multiple Data. SIMD instructions operate on wide registers (128-bit, 256-bit, or more) containing multiple 8- to 64-bit elements, performing operations across all elements in parallel. Wider registers and parallel execution provide a significant performance boost to algorithms that can take advantage of them.
Until now, anyone who wanted to use SIMD in Go had to endure the friction of writing assembly language. The new archsimd package is here to change that.
Goals
The goal is to support as many SIMD instructions across different architectures as possible. Currently, we have amd64, arm64, and wasm available. Our amd64 support has the largest API surface, covering AVX, AVX2, and many AVX-512 extensions. Our arm64 support currently covers NEON, with SVE and some SVE2 already on the way. Our wasm support is also mostly complete, thanks to its well-defined 128-bit SIMD abstraction.
Some users might be interested in matrix extensions on certain architectures, such as AMX and SME. We havenât fully settled on how to represent matrices effectively in Go, so they are not yet supported, but we might support them in the future.
API Design and Naming
Types
The architecture-specific SIMD API is imported from simd/archsimd. archsimd uses distinct struct types, such as Float32x4, Int32x8, or Uint8x16, to signal the shape and element type of each vector, and defines operations as methods on those types. At this moment, archsimd supports only fixed-width vector extensions. Scalable vector extensions like arm64 SVE and RVV will have different struct types to represent vectors. Mask types also come with vector-like shapes, e.g., Mask32x4.
Methods
Across different vector widths, element types, and architectures, we use the same method name whenever operations are semantically identical:
Regardless of vector width or architecture, element-wise addition is simply x.Add(y); the type of x determines which instruction to emit. Instead of 18 different function names for addition, there is only one method name.
When instructions on different architectures look similar at a glance but differ in edge-case behavior, we give them distinct names so differences donât silently bite you. For example, 16-byte table lookup on amd64 (VPSHUFB) zeros the output byte when the index is negative (index < 0) and wraps modulo 16 otherwise, so we call it PermuteOrZero. On arm64 NEON (VTBL) and wasm (i8x16.swizzle), any out-of-range index (index < 0 || index >= 16) zeros the output byte, so we call it LookupOrZero.
On amd64, many 256-bit and 512-bit instructions operate independently within 128-bit lanes rather than across the entire register. We explicitly suffix those methods with Grouped (such as InterleaveLoGrouped or PermuteOrZeroGrouped) to make the 128-bit lane boundary obvious.
Methods require a receiver value, which doesnât work for creating a vector from memory or a scalar. For those, archsimd provides package-level functions named with the target vector type:
Slices (the default): In Go 1.27, loading and storing slices use the shortest names: archsimd.LoadFloat32x4(s []float32) Float32x4 and x.Store(s []float32). For tail elements that may be shorter than a full vector, archsimd.LoadFloat32x4Part(s []float32) (Float32x4, int) zero-fills the remaining lanes and returns the number of elements loaded, paired with x.StorePart(s []float32) int.
Arrays and Broadcasts: Fixed-size array pointers use the Array suffix (archsimd.LoadFloat32x4Array(y *[4]float32) and x.StoreArray(y *[4]float32)), and archsimd.BroadcastFloat32x4(v float32) broadcasts a scalar to all lanes.
Type Reinterpretation
Reinterpretations are type casts without any cost. SIMD algorithms frequently reinterpret bits across element types and widths.
In the Go 1.26 experiment, we implemented this with As methods, which suffered from a quadratic explosion of type pairs and couldnât generalize to width-agnostic vectors in simd or SVE.
In Go 1.27, we replace them with composable zero-cost conversions:
ToBits() reinterprets a signed integer or float vector as an unsigned integer vector of the same element width (e.g., Int32x4.ToBits() -> Uint32x4), and BitsToInt32() / BitsToFloat32() converts back.
ReshapeToUints() on unsigned vectors changes the element width within the same register width. For example, if x is an archsimd.Uint8x16, then x.ReshapeToUint32s().BitsToFloat32() reinterprets the bits of x as an archsimd.Float32x4 at zero runtime cost.
Masks
Different architectures encode vector masks very differently, e.g. k mask registers on AVX-512, full vector bitmasks on AVX/AVX2, NEON, and wasm, and predicate registers on SVE. To hide this hardware detail, archsimd provides opaque Mask types (such as Mask32x4) matching each vector shape:
Comparisons like x.Greater(y) produce a Mask.
x.Masked(m) zeros elements where m is false, and x.IfElse(m, y) (which replaces Merge from Go 1.26) selects elements from x where m is true and from y where m is false.
The compiler peepholes mask operations with the surrounding instructions: on AVX-512, x.Add(y).Masked(m) or x.Add(y).IfElse(m, z) compiles into a single zero-masked or merge-masked VPADD instruction; on AVX2, NEON, or wasm, it lowers to the appropriate bitwise AND or blend/bitselect instruction.
Examples
Portable algorithms like inner product can easily be implemented using the portable simd package, as shown in the companion simd blog post. However, we still expose archsimd to tap into exotic, architecture-specific instructions that do not fit into a portable intersection API, or instructions that can simply do things faster than what simd provides.
On amd64, the GFNI extension exposes GaloisFieldAffineTransform operations. Putting aside what a Galois Field is, these operations do a simple thing:
// Each element of A is interpreted as a matrix of 8 rows of vectors of 8 bits.
// Each element of x is interpreted as a vector of 8 bits.
// The b argument is likewise a vector of 8 bits.
// The result is z[i] = (A[i/8] * x[i]) + b.
//
// The matrix * and vector + follow the usual rules for linear algebra,
// but based on AND and XOR for scalar * and +
//
// Asm: VGF2P8AFFINEQB, CPU Feature: AVX512GFNI
func (x Uint8x16) GaloisFieldAffineTransform(A Uint64x2, b uint8) Uint8x16
Each 8x8 bit matrix A is packed row-by-row into a single uint64. Because any linear combination or permutation of the 8 bits within a byte can be written as an 8x8 bit matrix, this single instruction is a Swiss Army knife for byte-level bit manipulation.
For example, reversing the bit order of every byte (bits.Reverse8) normally requires nibble lookup tables, masks, shifts, and ORs in SIMD. With GaloisFieldAffineTransform, we can simply multiply each byte by the 8x8 anti-diagonal identity matrix (0x8040201008040201) to reverse all 8 bits of 64 bytes at a time in a single instruction:
/*
Anti-diagonal 8x8 bit matrix (0x8040201008040201):
[ 0 0 0 0 0 0 0 1 ] [ x7 ] [ x0 ]
[ 0 0 0 0 0 0 1 0 ] [ x6 ] [ x1 ]
[ 0 0 0 0 0 1 0 0 ] [ x5 ] [ x2 ]
[ 0 0 0 0 1 0 0 0 ] * [ x4 ] = [ x3 ]
[ 0 0 0 1 0 0 0 0 ] [ x3 ] [ x4 ]
[ 0 0 1 0 0 0 0 0 ] [ x2 ] [ x5 ]
[ 0 1 0 0 0 0 0 0 ] [ x1 ] [ x6 ]
[ 1 0 0 0 0 0 0 0 ] [ x0 ] [ x7 ]
*/
func ReverseBits(dst, src []uint8) {
if !archsimd.X86.AVX512GFNI() {
// A slow fallback emulation impl, details omitted.
slowReverseBits(dst, src)
return
}
if len(src) > len(dst) {
// Make sure src and dst are the same size
src = src[:len(dst)]
}
revMatrix := archsimd.BroadcastUint64x8(0x8040201008040201)
var v archsimd.Uint8x64
var i int
for i = 0; i < len(src)-v.Len()+1; i += v.Len() {
v = archsimd.LoadUint8x64(src[i : i+v.Len()])
v.GaloisFieldAffineTransform(revMatrix, 0).Store(dst[i : i+v.Len()])
}
if i < len(src) {
v, _ = archsimd.LoadUint8x64Part(src[i:])
v.GaloisFieldAffineTransform(revMatrix, 0).StorePart(dst[i:])
}
}
Another example is matrix transpose. On amd64, the permutation instructions are shaped in a non-portable way, so using simd is cumbersome and might incur emulations. An efficient implementation can be written more directly in archsimd. The example below transposes an 8-by-8 matrix of 32-bit integers in registers on amd64 using AVX2âs 128-bit lane-grouped interleaves (InterleaveLoGrouped, InterleaveHiGrouped) and cross-lane permutations (ConcatPermuteScalarsGrouped, ConcatPermute128Scalars):
func Transpose8(a0, a1, a2, a3, a4, a5, a6, a7 archsimd.Int32x8) (
b0, b1, b2, b3, b4, b5, b6, b7 archsimd.Int32x8) {
if !archsimd.X86.AVX2() {
// A slow fallback emulation impl, details omitted.
return slowTranspose8(a0, a1, a2, a3, a4, a5, a6, a7)
}
/*
LOW HIGH
a0: abcd efgh
a1: ijkl mnop
a2: qrst uvwx
a3: 0123 4567
a4: ABCD EFGH
a5: IJKL MNOP
a6: QRST UVWX
a7: 89yz YZ$@
*/
t0 := a0.InterleaveLoGrouped(a1) // t0 = aibj emfn
t1 := a0.InterleaveHiGrouped(a1) // t1 = ckdl gohp
t2 := a2.InterleaveLoGrouped(a3) // t2 = q0r1 u4v5
t3 := a2.InterleaveHiGrouped(a3) // t3 = s2t3 w6x7
t4 := a4.InterleaveLoGrouped(a5) // t4 = AIBJ EMFN
t5 := a4.InterleaveHiGrouped(a5) // t5 = CKDL GOHP
t6 := a6.InterleaveLoGrouped(a7) // t6 = Q8R9 UYVZ
t7 := a6.InterleaveHiGrouped(a7) // t7 = SyTz W$X@
a0 = t0.ConcatPermuteScalarsGrouped(0, 1, 4, 5, t2) // a0 = aiq0 emu4
a1 = t0.ConcatPermuteScalarsGrouped(2, 3, 6, 7, t2) // a1 = bjr1 fnv5
a2 = t1.ConcatPermuteScalarsGrouped(0, 1, 4, 5, t3) // a2 = cks2 gow6
a3 = t1.ConcatPermuteScalarsGrouped(2, 3, 6, 7, t3) // a3 = dlt3 hpx7
a4 = t4.ConcatPermuteScalarsGrouped(0, 1, 4, 5, t6) // a4 = AIQ8 EMUY
a5 = t4.ConcatPermuteScalarsGrouped(2, 3, 6, 7, t6) // a5 = BJR9 FNVZ
a6 = t5.ConcatPermuteScalarsGrouped(0, 1, 4, 5, t7) // a6 = CKSy GOW$
a7 = t5.ConcatPermuteScalarsGrouped(2, 3, 6, 7, t7) // a7 = DLTz HPX@
b0 = a0.ConcatPermute128Scalars(0, 2, a4) // b0 = aiq0 AIQ8
b1 = a1.ConcatPermute128Scalars(0, 2, a5) // b1 = bjr1 BJR9
b2 = a2.ConcatPermute128Scalars(0, 2, a6) // b2 = cks2 CKSy
b3 = a3.ConcatPermute128Scalars(0, 2, a7) // b3 = dlt3 DLTz
b4 = a0.ConcatPermute128Scalars(1, 3, a4) // b4 = emu4 EMUY
b5 = a1.ConcatPermute128Scalars(1, 3, a5) // b5 = fnv5 FNVZ
b6 = a2.ConcatPermute128Scalars(1, 3, a6) // b6 = gow6 GOW$
b7 = a3.ConcatPermute128Scalars(1, 3, a7) // b7 = hpx7 HPX@
return
}
Good Practices
When walking through the examples above, you might have a few questions:
What is archsimd.X86.AVX512GFNI(), and can I omit it?
Why is the strided loop written as i = 0; i < len(src)-v.Len()+1; i += v.Len(), instead of i = 0; i + v.Len() <= len(src); i += v.Len()?
Why does Transpose8 take 8 vector parameters rather than a struct or an [8]archsimd.Int32x8 array?
At first glance these choices might look like minor stylistic preferences, but each of them is chosen for an important performance or correctness reason:
CPU feature checks (archsimd.X86.*, archsimd.ARM64.*)
On amd64, we provide runtime feature checks like archsimd.X86.AVX(), AVX2(), AVX512(), and extension checks like AVX512GFNI() or AVX512VNNI(); on arm64, NEON is always available, and we provide checks for optional extensions like archsimd.ARM64.PMULL(), SVE(), and SVE2(). The compiler doesnât know ahead of time whether the machine running your binary supports optional SIMD extensions. If you donât guard your SIMD code with the appropriate feature check, your program may crash with SIGILL on hardware that lacks the instruction.
Just as importantly, CPU feature checks act as compiler optimization hints. For example, AVX-512 supports hardware merge-masking on instruction outputs. How the compiler lowers x.Add(y).IfElse(m, z) on a 128-bit or 256-bit vector depends on which CPU features are known to be available. If the code is not guarded by archsimd.X86.AVX512(), the compiler must conservatively emit a vector VPADD followed by a VPBLEND instruction. Inside an if archsimd.X86.AVX512() block, however, the compiler knows AVX-512 is available and fuses the sequence into a single merge-masked AVX-512 VPADD instruction.
Bounds check elimination in strided loops
How you write the loop bound determines whether the compilerâs prove pass can eliminate slice bounds checks inside the loop. In i + v.Len() <= len(src), the addition i + v.Len() could theoretically overflow to a negative int if len(src) were near math.MaxInt, which prevents the compiler from proving that i is always non-negative and in-bounds. Writing i < len(src)-v.Len()+1 avoids that potential overflow, allowing the loop body to compile with zero bounds checks.
Keeping vectors in registers (avoiding large composite types)
Transpose8 passes 8 vector parameters rather than a struct of 8 fields or an [8]archsimd.Int32x8 array because Goâs ABI and SSA backend currently place large composite types in memory rather than in registers (a known issue tracked at #24416). Because SIMD vectors are 16 to 64 bytes each, spilling them to the stack hurts performance much more than spilling scalar values. This applies to both function parameters and local variables, so avoid wrapping SIMD vectors inside large arrays or structs in hot loops. There are ongoing efforts to promote composite memory accesses back into registers, which have not yet landed in Go 1.27.
Trying it out
In Go 1.27, GOEXPERIMENT=simd supports amd64 (AVX, AVX2, and AVX-512), arm64 (NEON), and wasm (WebAssembly 128-bit SIMD) out of the box. Whether you are on an Intel/AMD machine, an arm64 server or Apple Silicon Mac (M1/M2/M3/M4), or targeting WebAssembly, you can try archsimd natively right away:
GOEXPERIMENT=simd go test simd/archsimd/...
You can also cross-test wasm (or amd64 via Rosetta emulation on Apple Silicon) by setting GOARCH:
# Run wasm SIMD tests using a WASI runtime:
GOOS=wasip1 GOARCH=wasm GOEXPERIMENT=simd go test simd/archsimd/...
# Or run amd64 AVX2 tests on Apple Silicon via Rosetta:
GOARCH=amd64 GOEXPERIMENT=simd go test simd/archsimd/...
Weâre interested in all sorts of feedback! Early users have already helped us catch bugs (such as #77582) and identify places where code generation or API ergonomics could be improved. We tried hard to pick clear, consistent names and cover the most useful instructions, but archsimd is a massive API surface and there is always room to improve.
Future work
For Go 1.28 and beyond, we are actively working on completing arm64 SVE and SVE2 support in archsimd (introducing width-agnostic scalable vector types like archsimd.Float32s backed directly by hardware SVE registers and predicates) and wiring it into simd. We also plan to expand archsimd to additional architectures such as riscv64, ppc64, s390x, and loong64, improve register promotion for composite types, and continue filling in instructions and compiler optimizations based on community feedback.
Next article: Better HTTP/1 connection reuse
Previous article: Platform-independent SIMD in Go
Blog Index
https://go.dev/blog/archsimd
GoWire15d
Go 1.27's portable SIMD
#â619 â September 25, 2026
Read the Web Version
đ We're taking a break next week, so we'll be back on Friday, October 9.
__
Your editor, Peter Cooper
Go Weekly
Platform-Independent SIMD in Go â SIMD lets a CPU perform an operation on a vector of values at once. Go 1.27 brings experimental SIMD support to arm64 and wasm, plus a portable simd package that emulates SIMD where unavailable, so you don't need to write a plain Go fallback.
David Chase and Junyang Shao (Go Team)
RELATED:
hash/adler32's SIMD implementation is a good, brief example of using SIMD to get over double the speed on larger inputs.
Ben Boyter explains how he borrowed fzf's SIMD byte-matching routine to help cut his code search tool's user CPU time by nearly 4x.
Sylvain Kerkour shows off 7.5x faster ChaCha20 encryption built on Go's SIMD.
Ingest and Analytics in One Postgres. No Pipeline â Go compiles to one binary. Your data layer can be just as lean. TimescaleDB extends the Postgres you already run so ingest and analytics live in one database, not a warehouse and a pipeline. Same SQL, one system to run. Get $1000 credit to start.
Tiger Data (creators of TimescaleDB) sponsor
đą Proposals and Updates
How Do You Add a New Keyword to Go? (For Sum Types) â Damien's design for sum types is being actively worked through. The latest sticking point is figuring out how to add a union keyword without breaking existing code or gofmt, with some useful input from Dart's Bob Nystrom.
Damien Neil et al.
Is Go's FIPS 140 Code Bloating Your Binaries? â Using crypto/rand pulls in extra crypto code even with FIPS off, so WireGuard's creator wants a build-time opt-in. Filippo Valsorda has trimmed some dependencies but argues separate builds would increase testing and maintenance work.
Jason A. Donenfeld et al.
AND MORE:
Tests are getting automatic goroutine labels so profiles and tracebacks show which test, benchmark or fuzz run was responsible.
string(x) on arbitrary integers will be restricted to byte, rune, and untyped rune constants in Go 1.28, with the proposal being accepted yesterday.
Brad Fitzpatrick is prototyping Mutex.LockChan() so a select can wait on a lock and a channel at once. Just a demo for now.
Rhys Hiltner has a stack of CLs to speed up the GC's stop-the-world phase transitions.
đ Code & Tools
AX: Google's Open Agentic Orchestration Runtime â Designed for large-scale throughput, AX runs AI agents akin to how Kubernetes runs pods. Declare tasks, workspaces, and models in YAML and AX sandboxes the agents and wires it all up. GitHub repo.
Jaana Dogan et al.
CodeAF: The Open-Source Software Factory for Open Models â Instead of juggling a terminal for every agent, get one window for every project. Many models, one session: the right one for each call.
AgentField.ai sponsor
đ Hister: A Go-Powered Search Engine for Your Own Stuff â The creator of SearX (and Colly) is back with a self-hosted personal search engine for re-finding your files or pages youâve visited. GitHub repo.
Adam Tauber
RobotGo 1.1: Native Cross-Platform GUI Automation â Provides an API to control the mouse and keyboard, read the screen, grab screenshots, etc. on macOS, Windows, and Linux.
V Caesar et al.
NATS Server 2.15 â High-performance messaging system. There's an upgrade guide for existing users. JetStream stream moves and scaling are now safer to cancel mid-flight, and streams default to a limit of 1,000 consumers.
Terratest 2.0 â Write automated tests for your infrastructure code (think Terraform, Kubernetes, Docker, etc). Now split into 16 modules so you only pull in the SDKs you need. (Homepage.)
garble 0.18 â A tool to obfuscate Go builds so binaries have "as little information about the original source code as possible". Now requires Go 1.27, with 10-15% faster builds.
compress 1.20 â Optimized (de)compression packages, now with support for LZW and Microsoft Xpress.
đ€ Crush 0.96 â Charm's agentic coding harness now has themes!
â And finallyâŠ
The First Official TinyGo Hardware 'Starter Kit' â If you want to do some hardware hacking with TinyGo, this official kit includes a RISC-V board and a variety of modules, along with tutorials for building stuff with it. If youâve already got some boards or want to source your own, TinyGo also has a list of recommended and supported boards you can use.
TinyGo Team and Seeed Studio
https://golangweekly.com/issues/619
Welcome to GoWire spacestr profile!
About Me
Go programming, tools, and developer learning. Unofficial automated fan feed. Content belongs to its respective owners; not affiliated with source publishers.