# Operands: grammar, pseudo-registers, addressing and symbols Layer 1, the common language. Verified against `go tool asm` of Go 1.27.1 and against gasm's parser. The operand grammar is the part of the language that varies most between targets, so this page fixes the common grammar and the pseudo-registers; the per architecture pages carry the register names and the addressing quirks each target adds. ## The four operand kinds Every operand is one of four kinds: ```text R1 register $4 immediate label branch target or symbol -8(BX)(DI*4) memory ``` **Operands go source first, destination last**: `MOVQ x+0(FP), AX` loads the argument into AX. This is the opposite of Intel order and the same order as AT&T, with the sigils removed: registers are bare names, immediates take `$`, memory is `offset(base)`. ## Registers A register operand is its bare name, with no prefix: `AX`, `X15`, `R14` on amd64; `R0` to `R30`, `ZR`, `V0` to `V31` on arm64; `X0` to `X31`, `F0` to `F31`, `V0` on riscv64; `R0` to `R31`, `F0` to `F31`, `V0` on loong64. Sub-register and width selection rides the mnemonic, not the operand: the amd64 family spells `MOVB`, `MOVW`, `MOVL`, `MOVQ`, and the arm64 family suffices `B`, `H`, `S`, `D`, `Q` on the shared forms. Each architecture page lists its registers and the reserved ones. ## Immediates `$` introduces a constant: `$42`, `$-1`, `$0x2a`, `$'A'`, `$bufSize`. The `$` applies to the whole constant expression that follows, so `$(4*8+reader__size)` is one immediate. Without the `$`, a number in operand position is an address, not a value; the classic error `ADDQ 1, AX` asks the assembler for the byte at address 1. The one place a `$` number is not an immediate is the frame and argument size field of TEXT, `$16-24`, which is two separate constants and not a subtraction; DIRECTIVES.md specifies it. ## Memory ```text offset(base) offset(base)(index*scale) ``` Both parts are optional where the target allows them: `(BX)` is the memory at BX, `foo+16(SB)` is a global, and on amd64 `foo+32(SP)(R9*8)` adds a scaled index. `offset` is a constant expression, optionally carrying a symbol name. The extensions beyond `offset(base)` are where the targets diverge, and each belongs to its architecture page: amd64 carries the `index*scale` form with scale 1, 2, 4 or 8 and its own rules on which registers may index; loong64 writes base plus index as `(R4)(R5)`; the ARM family attaches shift amounts to the index register in its own spelling. The address arithmetic is on **byte addresses**: the offset is added to the base as it stands, whatever the operand width of the instruction. Loading the third 8-byte word of an array at BX is `16(BX)`, not `2(BX)`. ## The four pseudo-registers Four names denote locations no target register holds, and they mean the same on every architecture: - **FP**, the frame pointer: the arguments and results of the current function, at positive offsets, in the order the Go prototype declares them. Every FP reference must carry a name: `x+0(FP)`, and an unnamed `0(FP)` is rejected. Results follow arguments; an unnamed result is called `ret`. - **SP**, the virtual stack pointer: the high end of the function's local frame, so locals live at negative offsets, `x-8(SP)`. A reference without a name and without a plus, `-8(SP)`, addresses the **hardware** stack pointer instead: the two spellings are one character apart and mean different registers. That is the sharpest edge in the language and the source of the deepest bugs. - **SB**, the static base: the origin of memory, used for globals and cross-package symbols, always with a name: `foo(SB)`, `foo+4(SB)`. - **PC**, the program counter: branch targets, and the explicit relative form `1(PC)`. ## Symbol names A symbol's full name is the package path, a period, and the base name. In source, the period is written U+00B7 (`·`) and a slash in the path U+2215 (`∕`), because the parser treats the ASCII forms as punctuation. Inside the package's own file, `·Name` is enough and is the preferred spelling, since it survives a rename of the import path. | Spelling | Meaning | |---|---| | `·Name(SB)` | this package's Name | | `runtime·morestack(SB)` | another package's morestack | | `sourcedock.dev∕petrbalvin∕pkg·Name(SB)` | fully qualified | | `msg<>(SB)` | file-local, the static of this language; `<>` also makes the ABI field static in the object | | `Name(SB)` | ABI-qualified reference, the ABI in angle brackets after the name | The object file these symbols produce, with the index rules that decide what is referenced by name and what by index, is specified in [GOOBJ.md](../GOOBJ.md). ## What vet adds in Go Inside a Go package, `go vet`'s asmdecl analyzer checks every FP offset and name against the Go prototype, and checks the declared argument area against the frame. That layer, the prototype requirement and `go_asm.h`, belongs to RUNTIME.md; the grammar above is the whole of what the assembler itself requires.