golang/go
Compiler
The Go compiler, called "gc" (Go compiler — not garbage collector), lives under src/cmd/compile/. It is the reference implementation of Go and the compiler that ships with go build.
Purpose
gc takes one Go package's source files and emits one object archive (.a) plus an export-data file. The downstream tool (the linker, or another compile invocation that imports this package) consumes those.
The compiler is a single binary, compile, invoked once per package by cmd/go. It does not build whole programs — that's the go command's job.
Directory layout
src/cmd/compile/
├── README.md # The canonical compiler overview
├── default.pgo # PGO profile used to build the toolchain itself
├── doc.go # Package doc / man page
├── main.go # Tiny entry point, calls into internal/gc
└── internal/
├── syntax/ # Lexer, parser, syntax tree
├── types2/ # Type checker (port of go/types)
├── importer/ # Package import (reads export data)
├── noder/ # Convert syntax+types2 → IR (Unified IR)
├── ir/ # Compiler IR node types
├── types/ # Compiler types (parallel to types2)
├── inline/ # Function inlining
├── devirtualize/ # Devirtualize known interface calls
├── escape/ # Escape analysis
├── walk/ # Order-of-eval + desugaring
├── ssagen/ # IR → SSA
├── ssa/ # SSA passes, rewrites, codegen
│ └── _gen/ # Source rules; go generate produces *.go
├── reflectdata/ # Type descriptors for runtime/reflect
├── liveness/ # Pointer liveness analysis
├── dwarfgen/ # DWARF debug info generation
├── pgoir/ # PGO data ingestion + use
├── inline/inlheur/ # Heuristics for inlining
├── coverage/ # Coverage instrumentation
├── deadlocals/ # Dead local-variable elimination
├── rangefunc/ # Lowering of range-over-func iterators
├── loopvar/ # New per-iteration loop var semantics
├── pkginit/ # Package init function generation
├── staticdata/, staticinit/ # Compile-time data and init handling
├── typecheck/ # Legacy typecheck (mostly retired)
├── amd64/, arm/, arm64/, ppc64/, ... # Per-arch SSA codegen
└── gc/ # The driver: orchestrates all phasesThe compilation pipeline
Roughly, one compile invocation runs these phases. Numbers and names match src/cmd/compile/README.md.
graph TD
Source[*.go source files] --> Syntax[1. syntax: lex + parse]
Syntax --> Types2[2. types2: type check]
Types2 --> Noder[3. noder: build Unified IR]
Noder --> Middle[4. middle end: escape, inline, devirt]
Middle --> Walk[5. walk: order + desugar]
Walk --> Ssagen[6a. ssagen: IR → SSA]
Ssagen --> SsaGeneric[6b. ssa generic passes]
SsaGeneric --> SsaArch[7. ssa lowering + arch passes]
SsaArch --> Obj[obj.Prog]
Obj --> Output[*.a object + export data]1. Syntax (internal/syntax)
Pure-Go lexer + parser, distinct from the public go/parser. Produces a syntax-AST with position information for diagnostics.
Key types in cmd/compile/internal/syntax/nodes.go:
*syntax.File— one source file's AST root.*syntax.FuncDecl,*syntax.TypeDecl,*syntax.VarDecl,*syntax.ConstDecl— top-level declarations.*syntax.Stmt,*syntax.Expr— statement and expression interfaces.*syntax.PosBase— position information shared by all nodes.
2. Type checking (internal/types2)
types2 is a near-port of the public go/types package, adapted to consume syntax.Expr instead of go/ast.Expr. It performs full type inference, including for generics. Result is a typed AST plus an *types2.Info with type/object resolution.
This is where most "before-IR" semantic analysis happens: name resolution, type checking, generic instantiation, constant folding for typed constants.
3. Noder (internal/noder)
The "noder" walks the typed syntax tree and produces the compiler's internal IR. It does this through Unified IR:
noder/writer.goserializes typed syntax into a binary IR stream.noder/reader.godeserializes that stream intoir.Nodes.- The same format is also used for export data — what the compiler writes to summarize this package for downstream importers.
Unified IR is the rendezvous point between front-end (types2 + syntax) and middle/back-end (ir/ssa). It also drives generics: a generic function is exported as Unified IR, and instantiations are read back into the importing package.
4. Middle end
A handful of optimization passes operate on ir.Node:
internal/escape— escape analysis. Decides whether each Go value can stay on the stack or must be heap-allocated. Influences allocation strategy and write-barrier emission.internal/inlineandinternal/inline/inlheur— function inlining. PGO data, when available, biases inlining toward hot call sites.internal/devirtualize— replaces interface method calls with direct calls when the dynamic type can be inferred.internal/deadlocals— eliminates locals never used.
5. Walk (internal/walk)
The walk pass does two things:
- Order of evaluation. Decomposes complex expressions into simpler ones, introducing temporaries where Go's evaluation rules require them. (Older codebase term: "order.")
- Desugaring. Replaces high-level constructs with primitives the back end understands:
range→ conventionalforloops.switch→ balanced binary searches or jump tables.- Map ops → calls into
runtime.mapaccess*,runtime.mapassign*. - Channel ops → calls into
runtime.chansend*,runtime.chanrecv*. - Defer/recover → calls to
runtime.deferproc,runtime.deferreturn. - String concatenation, conversion, type assertions → runtime helpers.
- Range over function (Go 1.23+) → state-machine transform via
internal/rangefunc.
6. SSA construction and generic passes
internal/ssagen lowers IR into Static Single Assignment form. This is where the compiler crosses from "Go-shaped IR" to "compiler-shaped IR." Then internal/ssa runs a sequence of passes:
- Generic optimization passes that don't depend on architecture: dead code elimination, common subexpression elimination, copy propagation, branch elimination, bounds check elimination, nil check removal, prove pass (range/null facts).
- The "rewrite" engine, driven by
*.rulesfiles.internal/ssa/_gen/generic.rulesholds machine-independent peepholes; per-arch files (AMD64.rules,ARM64.rules, …) hold arch-specific ones.
7. Architecture-specific lowering and codegen
Each internal/ssa/_gen/<ARCH>.rules and internal/<arch>/ssa.go together define how generic SSA is lowered into architecture-specific opcodes. The internal/ssa package then runs scheduling, register allocation (regalloc.go), stack frame layout, and pointer liveness analysis.
The output is a sequence of obj.Prog instructions per function. These are handed to cmd/internal/obj (the assembler back-end) to produce the final object code.
7a. Export data
In addition to the object file, compile writes export data: a Unified IR stream containing:
- Public type declarations.
- Public function signatures (and bodies, for inline candidates).
- Generic function bodies (so importers can instantiate them).
- Escape information for parameters of inline candidates.
The format is internally versioned. golang.org/x/tools/go/internal/... mirrors a reader for tools like gopls.
Key abstractions
| Abstraction | Where | Purpose |
|---|---|---|
syntax.File, syntax.Expr |
cmd/compile/internal/syntax/nodes.go |
Front-end AST |
types2.Type, types2.Object, types2.Info |
cmd/compile/internal/types2/ |
Types and resolved identifiers |
ir.Node, ir.Func, ir.Name |
cmd/compile/internal/ir/ |
Compiler IR |
types.Type, types.Sym |
cmd/compile/internal/types/ |
Compiler types (parallel to types2) |
ssa.Func, ssa.Block, ssa.Value |
cmd/compile/internal/ssa/ |
SSA representation |
obj.Prog, obj.Addr |
cmd/internal/obj/ |
Pseudo-assembly handed to the linker/assembler |
pgo.Profile |
cmd/compile/internal/pgoir/ |
Loaded PGO data |
Key source files
| File | Purpose |
|---|---|
src/cmd/compile/main.go |
Command entry; calls gc.Main |
src/cmd/compile/internal/gc/main.go |
Top-level pipeline driver |
src/cmd/compile/internal/syntax/parser.go |
The hand-written recursive-descent parser |
src/cmd/compile/internal/types2/check.go |
Type-checking entry |
src/cmd/compile/internal/noder/writer.go |
Unified IR encoder |
src/cmd/compile/internal/noder/reader.go |
Unified IR decoder |
src/cmd/compile/internal/escape/escape.go |
Escape analysis |
src/cmd/compile/internal/inline/inl.go |
Inlining decisions |
src/cmd/compile/internal/walk/walk.go |
Order + desugar entry |
src/cmd/compile/internal/ssagen/ssa.go |
IR → SSA |
src/cmd/compile/internal/ssa/compile.go |
SSA pass pipeline |
src/cmd/compile/internal/ssa/_gen/generic.rules |
Architecture-independent rewrites |
src/cmd/compile/internal/ssa/regalloc.go |
Register allocator |
src/cmd/compile/internal/dwarfgen/dwarf.go |
DWARF emission |
Useful diagnostics
The compiler exposes a lot of internal state to the user via -gcflags:
go build -gcflags=-m=2 # inlining + escape
go build -gcflags='all=-m=1 -l' # all packages, no inlining
go build -gcflags=-d=ssa/check_bce/debug=1 # bounds-check elimination decisions
GOSSAFUNC=funcName go build # write ssa.html for one function
go build -gcflags=-S # dump final assembly
go tool compile -d help # list all -d knobs
go tool compile -d ssa/help # list all SSA debug knobsSee Debugging for more.
Integration points
- Linker (cmd/link) consumes object files and export data the compiler emits.
- Runtime (runtime) is called from compiler-emitted code (channel ops, map ops, panic/recover, write barriers, GC scan helpers, etc.).
reflectpackage consumes the type metadata thatinternal/reflectdataemits.- DWARF tools (delve, addr2line) consume
internal/dwarfgenoutput. - PGO uses pprof CPU profiles produced via
runtime/pprof; the compiler reads them throughinternal/pgoir.
Entry points for modification
If you want to add an optimization, add it as an SSA pass or rewrite rule:
- For a generic peephole, edit
cmd/compile/internal/ssa/_gen/generic.rules, then rungo generate ./...from_gen/. - For an arch-specific peephole, edit the corresponding
<ARCH>.rules. - For a new pass, add a function in
cmd/compile/internal/ssa/and register it incompile.go.
If you're touching the front end:
- Type-checker bugs: start in
cmd/compile/internal/types2. - Generic semantics: same place; instantiation lives in
noder. - Parser bugs:
cmd/compile/internal/syntax.
For test layout, see Testing. Compiler regression tests usually go under test/ (run by cmd/internal/testdir).
Where to read next
src/cmd/compile/README.md— the in-tree compiler tour with phase-by-phase code references.src/cmd/compile/internal/ssa/README.md— the SSA package's internal design.src/cmd/compile/internal/noder/README.md— Unified IR.- Linker — what consumes the compiler's output.
- Runtime — what compiler-generated code calls into.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.