Skip to content

Instantly share code, notes, and snippets.

@greatroar
Last active August 13, 2021 06:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greatroar/8cd33029f1f3181174a2fe1edd14f317 to your computer and use it in GitHub Desktop.
Save greatroar/8cd33029f1f3181174a2fe1edd14f317 to your computer and use it in GitHub Desktop.
Notes on the Go ARM assembler

Notes on the Go ARM assembler

This is a set of notes on the Go assembler for the ARM (GOARCH=arm, Aarch32) architecture. The documentation of the assembler is the Quick Guide. It is quite incomplete, hence these notes.

General notes

Instructions are generally written with their arguments reversed compared to the ARM/GNU convention. If the final two arguments are the same, only one needs to be provided: ADD R0, R1 and ADD R0, R1, R1 are the same.

To inspect the assembler that the compiler generates, the easiest option is go build -gcflags=-S, but that actually shows pseudo-assembler. The more accurate option is go tool objdump on a binary or object archive. That has some quirks, though, mainly in using ARM syntax in some cases: MULA is spelled MLA, MOVM is spelled LDM or STM (with Rn! syntax for post-increment) and shift constants have a leading $.

Registers

The ARM has 16 general-purpose registers, called R0-R15 in Go. Ten of these, R0-R7, R9 and R12, are available for Go assembly routines. Four others are reserved. Two are quasi-reserved for pseudo-operations; of these, the Quick Guide fails to mention R8.

Go name Reserved for
R8 Division pseudo-ops
R10 g (thread-local storage)
R11 Pseudo-ops
R13 Stack pointer (RSP)
R14 Link register (return address)
R15 Program counter

Loads, stores and moves

Loads, stores and moves are all written MOV.

ARM Go Remarks
MOV MOVW
LDR MOVW
STR MOVW
LDRB MOVBU
LDRSB MOVB
STRB MOVB Can also be spelled MOVBU.
LDRH MOVHU
LDRSH MOVH
STRH MOVH Can also be spelled MOVHU.
LDM MOVM
STM MOVM
MOV32 MOVW Pseudo-operation.

There doesn't seem to be a Go name for LDRD/STRD.

MOVM can take .IA, .IB, .DA and .DB modifiers.

Write-back is enabled by .P (post-increment) and .W (pre-increment) modifiers:

MOVW.P 8(R0), R1

is the same as

MOVW (R0), R1
ADD  $8, R0

Pseudo-operations

Some operations aren't really ARM instructions, but pseudo-instructions that compile to instruction sequences.

The Go assembler allows pseudo-immediates that are larger than the immediates that fit in an ARM instruction:

MOVW $0x55554444, R0

becomes something like

MOVW 0x8(R15), R0
// rest of the function
MOVBU.PL -0x444(R5), R4 // 0x55554444

An additional instruction has been generated after the return instruction that encodes to the value of pseudo-immediate. A PC-relative MOVW loads it into R0. This is similar to how the ARM mov32 instruction works with literal pools.

When a large immediate is used with other instructions, the assembler may generate a load through R11:

ADD  $0x11112222, R0

becomes

MOVW 0xc(R15), R11
ADD R11, R0, R0
// rest of the function
TST.NE R2>>$4, R1 // 0x11112222

The DIV, MOD, DIVU and MODU pseudo-instructions perform division in a way that works even on older ARMs that don't have division hardware:

DIV R0, R1, R2 // R2 = R1/R0

generates a call to runtime._div. That function saves and restores the registers it uses, except that it wants one of its arguments in R8 and stores its return value in R11 (the Quick Guide doesn't mention R8 in this regard). The generated code looks as follows:

MOVW 0x18(R10), R11 // R10 = g.
MOVW R0, 0x20(R11)  // Push denominator.
MOVW R1, R8         // Pass numerator as R8.
BL runtime._div(SB)
MOVW R11, R2        // Move return value to final location.

Condition codes don't work on DIV/MOD/DIVU/MODU. The condition code is moved to the first instruction (the MOVW), so the call gets made with the wrong argument.

Leaf functions

A function that calls no other functions is a leaf function. As the Quick Guide explains, the special value $-4 for the frame size declares a leaf function. Go >=1.11 has the NOFRAME macro for the TEXT directive argument, which has the same effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment