Go Assembly: A Semi-Abstract Instruction Set
Go's assembler isn't a direct mapping to machine code; it's a semi-abstract instruction set. A `MOV` might become a `clear` or `load`. This is what you see with `go tool compile -S`. The footgun is assuming your assembly maps 1:1 to the final machine code.
WHY IT EXISTS Go's compiler suite was designed without a traditional, separate assembler pass. Instead, the compiler generates code in a semi-abstract instruction set. Go assembly is the human-readable syntax for this intermediate form, which is then processed by the linker to create the final machine code.
THE MENTAL MODEL Think of Go assembly not as writing machine code, but as providing high-level hints to the Go compiler's final code generation stage. You write MOV, but the compiler decides the most efficient way to move that data, which might not be a literal MOV instruction on the CPU. It's an abstraction layer above the metal, based on the Plan 9 assembler style.
HOW IT WORKS The Go assembler parses files written in its specific syntax. It understands abstract operations like MOV (move data) and TEXT (define a function), as well as machine-specific instructions. The key is that the final instruction selection happens later in the toolchain. This allows the compiler to inject runtime metadata, like the FUNCDATA and PCDATA directives used by the garbage collector, and make late-stage optimization decisions.
WHEN TO USE IT Use Go assembly for two main purposes. First, to inspect the compiler's output with go tool compile -S to understand performance and see what your Go code becomes. Second, for hand-tuning performance-critical, low-level routines that require specific CPU features, as seen in standard library packages like runtime and math/big.
WHEN NOT TO USE IT Avoid writing Go assembly for general application logic. It is not a direct representation of the underlying machine, making it a poor choice for precise hardware control. It's also non-portable and bypasses many of Go's safety guarantees. Reserve it for deep optimization after profiling proves it's necessary.
ONE CANONICAL EXAMPLE To see the compiler's assembly output for a simple program, run go tool compile -S my_program.go. The output will show abstract instructions like TEXT "".main(SB), $16-0 and MOVQ (TLS), CX, mixed with runtime directives like FUNCDATA that are meaningless to the CPU but vital for the Go runtime.
Read the original → go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.