golang/go
Assembler
cmd/asm (in src/cmd/asm/) turns Go's pseudo-assembly (.s files) into object code. It is invoked by the go command once per assembly source file in a package, just like cmd/compile is invoked once per Go source file.
Purpose
Go has its own assembly dialect — derived from the Plan 9 toolchain — that abstracts over the underlying machine. The same source (asm_<arch>.s style) is processed by cmd/asm to produce architecture-specific object code. The dialect is deliberately uniform across architectures: same comment style, same directives, same syntax for labels.
This pseudo-assembly is what's in src/runtime/asm_amd64.s, src/runtime/sys_linux_arm64.s, and so on. Outside the runtime, very little Go uses assembly directly — it's almost always for hot performance paths in crypto/..., math/..., runtime/..., and a few other places.
Directory layout
src/cmd/asm/
├── doc.go # User-facing docs, mirrors 'go tool asm -help'
├── main.go # Entry; calls into internal/asm
└── internal/
├── asm/ # The driver + parser; uses cmd/internal/obj for the back end
│ ├── asm.go # Operand parsing
│ ├── operand.go # Operand normalization
│ ├── parse.go # Tokenizer + parser
│ └── testdata/ # Per-arch encoding tests
├── arch/ # Per-arch instruction set helpers
├── flags/ # Asm command-line flags
└── lex/ # Tiny lexer (also handles cpp-style includes)The actual encoding tables live one level up, in src/cmd/internal/obj/<arch>/. Those packages are shared with the linker and compiler.
How it fits in the build
graph LR
Go[Go source: foo.go] --> Compile[cmd/compile]
S[Assembly: foo_amd64.s] --> Asm[cmd/asm]
Compile --> Obj[obj.Prog stream]
Asm --> Obj
Obj --> Goobj[*.o object via cmd/internal/goobj]
Goobj --> Archive[Package archive .a]
Archive --> Link[cmd/link]When a package contains both .go and .s files, cmd/go invokes cmd/compile for the Go files and cmd/asm for the assembly files. Both produce obj.Prog streams, which the linker eventually consumes.
Pseudo-assembly basics
The dialect uses uppercase mnemonics, AT&T-style operand order (source then destination on most arches), and special pseudo-registers:
| Pseudo-register | Meaning |
|---|---|
SP |
The Go stack pointer (logical), distinct from hardware SP |
FP |
Frame pointer for accessing parameters |
SB |
The "static base" pseudo-register for global symbols |
PC |
Logical PC, used in branches |
Symbols are referenced as name+offset(SB). Local frame variables as name+offset(SP). Frame size is declared in the function header:
TEXT runtime·hello(SB), NOSPLIT, $32-16
// 32 bytes of local frame, 16 bytes of args+results
MOVQ x+0(FP), AX
RETThe middle dot (·) in runtime·hello is the package-qualified name — Plan 9 convention.
Annotations:
NOSPLIT— no stack-grow prologue (corresponds to//go:nosplitin Go).WRAPPER— this is a wrapper function; runtime walks through it for tracebacks.NEEDCTXT— function needs the closure context.TLSBSS— symbol is thread-local (Linux/etc).
Documented in doc/asm.html (in-tree).
Per-arch handling
Each architecture has:
src/cmd/asm/internal/arch/<arch>.go— registers, mnemonics, special tokens.src/cmd/internal/obj/<arch>/— the encoder. Many of these are partially generated:anames.go— instruction names.asm.go/asm5.go/asm7.go— encoder logic.inst.go,inst_gen.go— instruction tables.- Per-arch
*_gen.gofiles driven by_gen/subdirectories.
Adding a new instruction usually means updating the per-arch table in cmd/internal/obj/<arch>/ and possibly its _gen/ source.
Key abstractions
| Abstraction | Where | Purpose |
|---|---|---|
obj.Prog |
cmd/internal/obj/link.go |
One assembly instruction |
obj.Addr |
same | One operand |
obj.LSym |
same | A symbol (function, data) |
arch.Arch |
cmd/asm/internal/arch/ |
Per-arch metadata |
lex.Tokenizer |
cmd/asm/internal/lex/ |
Source tokens |
asm.Parser |
cmd/asm/internal/asm/parse.go |
Recursive-descent parser |
Key source files
| File | Purpose |
|---|---|
src/cmd/asm/main.go |
Driver: parse flags, invoke parser per file |
src/cmd/asm/internal/asm/parse.go |
The parser |
src/cmd/asm/internal/asm/asm.go |
Operand handling |
src/cmd/internal/obj/link.go |
obj.Prog, obj.Addr, obj.LSym |
src/cmd/internal/obj/<arch>/asm.go |
Per-arch encoder |
src/cmd/asm/internal/asm/testdata/ |
Encoding tests, including SVE/AVX |
doc/asm.html |
Pseudo-assembly user reference |
Diagnostics
go tool asm -S— print assembly back after parsing (canonical form).go tool asm -dynlink— emit instructions for dynamic linking.go build -gcflags=-S— compiler-emitted assembly.go tool objdump <binary> <symbol>— disassemble a binary.
Integration points
- The compiler (compiler) emits the same
obj.Progstream thatcmd/asmdoes, then both feedcmd/internal/objfor object emission. - The linker (linker) is the consumer.
- Runtime assembly is the largest user of the assembler — every
src/runtime/*.sfile passes throughcmd/asm. go vetruns an asm-aware check (frame-size and arg-offset declarations) over.sfiles.
Entry points for modification
- Adding a new instruction: per-arch table in
cmd/internal/obj/<arch>/and possibly the generator under_gen/. - ARM64 SVE additions and similar new vector ISAs are often the kind of change you'll see here; the testdata files like
arm64sveerror.sandarm64sveenc.scover error-path and encoding-correctness cases.
Where to read next
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.