mirror of
https://github.com/golang/go
synced 2024-11-22 05:34:39 -07:00
5df4a6376f
Fixes #64283
Change-Id: Ie0167410fc5557143f0b2e17af56ad9fe34605cf
GitHub-Last-Rev: 77144c76d3
GitHub-Pull-Request: golang/go#64296
Reviewed-on: https://go-review.googlesource.com/c/go/+/544115
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
1074 lines
36 KiB
HTML
1074 lines
36 KiB
HTML
<!--{
|
||
"Title": "A Quick Guide to Go's Assembler",
|
||
"Path": "/doc/asm"
|
||
}-->
|
||
|
||
<h2 id="introduction">A Quick Guide to Go's Assembler</h2>
|
||
|
||
<p>
|
||
This document is a quick outline of the unusual form of assembly language used by the <code>gc</code> Go compiler.
|
||
The document is not comprehensive.
|
||
</p>
|
||
|
||
<p>
|
||
The assembler is based on the input style of the Plan 9 assemblers, which is documented in detail
|
||
<a href="https://9p.io/sys/doc/asm.html">elsewhere</a>.
|
||
If you plan to write assembly language, you should read that document although much of it is Plan 9-specific.
|
||
The current document provides a summary of the syntax and the differences with
|
||
what is explained in that document, and
|
||
describes the peculiarities that apply when writing assembly code to interact with Go.
|
||
</p>
|
||
|
||
<p>
|
||
The most important thing to know about Go's assembler is that it is not a direct representation of the underlying machine.
|
||
Some of the details map precisely to the machine, but some do not.
|
||
This is because the compiler suite (see
|
||
<a href="https://9p.io/sys/doc/compiler.html">this description</a>)
|
||
needs no assembler pass in the usual pipeline.
|
||
Instead, the compiler operates on a kind of semi-abstract instruction set,
|
||
and instruction selection occurs partly after code generation.
|
||
The assembler works on the semi-abstract form, so
|
||
when you see an instruction like <code>MOV</code>
|
||
what the toolchain actually generates for that operation might
|
||
not be a move instruction at all, perhaps a clear or load.
|
||
Or it might correspond exactly to the machine instruction with that name.
|
||
In general, machine-specific operations tend to appear as themselves, while more general concepts like
|
||
memory move and subroutine call and return are more abstract.
|
||
The details vary with architecture, and we apologize for the imprecision; the situation is not well-defined.
|
||
</p>
|
||
|
||
<p>
|
||
The assembler program is a way to parse a description of that
|
||
semi-abstract instruction set and turn it into instructions to be
|
||
input to the linker.
|
||
If you want to see what the instructions look like in assembly for a given architecture, say amd64, there
|
||
are many examples in the sources of the standard library, in packages such as
|
||
<a href="/pkg/runtime/"><code>runtime</code></a> and
|
||
<a href="/pkg/math/big/"><code>math/big</code></a>.
|
||
You can also examine what the compiler emits as assembly code
|
||
(the actual output may differ from what you see here):
|
||
</p>
|
||
|
||
<pre>
|
||
$ cat x.go
|
||
package main
|
||
|
||
func main() {
|
||
println(3)
|
||
}
|
||
$ GOOS=linux GOARCH=amd64 go tool compile -S x.go # or: go build -gcflags -S x.go
|
||
"".main STEXT size=74 args=0x0 locals=0x10
|
||
0x0000 00000 (x.go:3) TEXT "".main(SB), $16-0
|
||
0x0000 00000 (x.go:3) MOVQ (TLS), CX
|
||
0x0009 00009 (x.go:3) CMPQ SP, 16(CX)
|
||
0x000d 00013 (x.go:3) JLS 67
|
||
0x000f 00015 (x.go:3) SUBQ $16, SP
|
||
0x0013 00019 (x.go:3) MOVQ BP, 8(SP)
|
||
0x0018 00024 (x.go:3) LEAQ 8(SP), BP
|
||
0x001d 00029 (x.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
|
||
0x001d 00029 (x.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
|
||
0x001d 00029 (x.go:3) FUNCDATA $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
|
||
0x001d 00029 (x.go:4) PCDATA $0, $0
|
||
0x001d 00029 (x.go:4) PCDATA $1, $0
|
||
0x001d 00029 (x.go:4) CALL runtime.printlock(SB)
|
||
0x0022 00034 (x.go:4) MOVQ $3, (SP)
|
||
0x002a 00042 (x.go:4) CALL runtime.printint(SB)
|
||
0x002f 00047 (x.go:4) CALL runtime.printnl(SB)
|
||
0x0034 00052 (x.go:4) CALL runtime.printunlock(SB)
|
||
0x0039 00057 (x.go:5) MOVQ 8(SP), BP
|
||
0x003e 00062 (x.go:5) ADDQ $16, SP
|
||
0x0042 00066 (x.go:5) RET
|
||
0x0043 00067 (x.go:5) NOP
|
||
0x0043 00067 (x.go:3) PCDATA $1, $-1
|
||
0x0043 00067 (x.go:3) PCDATA $0, $-1
|
||
0x0043 00067 (x.go:3) CALL runtime.morestack_noctxt(SB)
|
||
0x0048 00072 (x.go:3) JMP 0
|
||
...
|
||
</pre>
|
||
|
||
<p>
|
||
The <code>FUNCDATA</code> and <code>PCDATA</code> directives contain information
|
||
for use by the garbage collector; they are introduced by the compiler.
|
||
</p>
|
||
|
||
<p>
|
||
To see what gets put in the binary after linking, use <code>go tool objdump</code>:
|
||
</p>
|
||
|
||
<pre>
|
||
$ go build -o x.exe x.go
|
||
$ go tool objdump -s main.main x.exe
|
||
TEXT main.main(SB) /tmp/x.go
|
||
x.go:3 0x10501c0 65488b0c2530000000 MOVQ GS:0x30, CX
|
||
x.go:3 0x10501c9 483b6110 CMPQ 0x10(CX), SP
|
||
x.go:3 0x10501cd 7634 JBE 0x1050203
|
||
x.go:3 0x10501cf 4883ec10 SUBQ $0x10, SP
|
||
x.go:3 0x10501d3 48896c2408 MOVQ BP, 0x8(SP)
|
||
x.go:3 0x10501d8 488d6c2408 LEAQ 0x8(SP), BP
|
||
x.go:4 0x10501dd e86e45fdff CALL runtime.printlock(SB)
|
||
x.go:4 0x10501e2 48c7042403000000 MOVQ $0x3, 0(SP)
|
||
x.go:4 0x10501ea e8e14cfdff CALL runtime.printint(SB)
|
||
x.go:4 0x10501ef e8ec47fdff CALL runtime.printnl(SB)
|
||
x.go:4 0x10501f4 e8d745fdff CALL runtime.printunlock(SB)
|
||
x.go:5 0x10501f9 488b6c2408 MOVQ 0x8(SP), BP
|
||
x.go:5 0x10501fe 4883c410 ADDQ $0x10, SP
|
||
x.go:5 0x1050202 c3 RET
|
||
x.go:3 0x1050203 e83882ffff CALL runtime.morestack_noctxt(SB)
|
||
x.go:3 0x1050208 ebb6 JMP main.main(SB)
|
||
</pre>
|
||
|
||
<h3 id="constants">Constants</h3>
|
||
|
||
<p>
|
||
Although the assembler takes its guidance from the Plan 9 assemblers,
|
||
it is a distinct program, so there are some differences.
|
||
One is in constant evaluation.
|
||
Constant expressions in the assembler are parsed using Go's operator
|
||
precedence, not the C-like precedence of the original.
|
||
Thus <code>3&1<<2</code> is 4, not 0—it parses as <code>(3&1)<<2</code>
|
||
not <code>3&(1<<2)</code>.
|
||
Also, constants are always evaluated as 64-bit unsigned integers.
|
||
Thus <code>-2</code> is not the integer value minus two,
|
||
but the unsigned 64-bit integer with the same bit pattern.
|
||
The distinction rarely matters but
|
||
to avoid ambiguity, division or right shift where the right operand's
|
||
high bit is set is rejected.
|
||
</p>
|
||
|
||
<h3 id="symbols">Symbols</h3>
|
||
|
||
<p>
|
||
Some symbols, such as <code>R1</code> or <code>LR</code>,
|
||
are predefined and refer to registers.
|
||
The exact set depends on the architecture.
|
||
</p>
|
||
|
||
<p>
|
||
There are four predeclared symbols that refer to pseudo-registers.
|
||
These are not real registers, but rather virtual registers maintained by
|
||
the toolchain, such as a frame pointer.
|
||
The set of pseudo-registers is the same for all architectures:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>FP</code>: Frame pointer: arguments and locals.
|
||
</li>
|
||
|
||
<li>
|
||
<code>PC</code>: Program counter:
|
||
jumps and branches.
|
||
</li>
|
||
|
||
<li>
|
||
<code>SB</code>: Static base pointer: global symbols.
|
||
</li>
|
||
|
||
<li>
|
||
<code>SP</code>: Stack pointer: the highest address within the local stack frame.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
All user-defined symbols are written as offsets to the pseudo-registers
|
||
<code>FP</code> (arguments and locals) and <code>SB</code> (globals).
|
||
</p>
|
||
|
||
<p>
|
||
The <code>SB</code> pseudo-register can be thought of as the origin of memory, so the symbol <code>foo(SB)</code>
|
||
is the name <code>foo</code> as an address in memory.
|
||
This form is used to name global functions and data.
|
||
Adding <code><></code> to the name, as in <span style="white-space: nowrap"><code>foo<>(SB)</code></span>, makes the name
|
||
visible only in the current source file, like a top-level <code>static</code> declaration in a C file.
|
||
Adding an offset to the name refers to that offset from the symbol's address, so
|
||
<code>foo+4(SB)</code> is four bytes past the start of <code>foo</code>.
|
||
</p>
|
||
|
||
<p>
|
||
The <code>FP</code> pseudo-register is a virtual frame pointer
|
||
used to refer to function arguments.
|
||
The compilers maintain a virtual frame pointer and refer to the arguments on the stack as offsets from that pseudo-register.
|
||
Thus <code>0(FP)</code> is the first argument to the function,
|
||
<code>8(FP)</code> is the second (on a 64-bit machine), and so on.
|
||
However, when referring to a function argument this way, it is necessary to place a name
|
||
at the beginning, as in <code>first_arg+0(FP)</code> and <code>second_arg+8(FP)</code>.
|
||
(The meaning of the offset—offset from the frame pointer—distinct
|
||
from its use with <code>SB</code>, where it is an offset from the symbol.)
|
||
The assembler enforces this convention, rejecting plain <code>0(FP)</code> and <code>8(FP)</code>.
|
||
The actual name is semantically irrelevant but should be used to document
|
||
the argument's name.
|
||
It is worth stressing that <code>FP</code> is always a
|
||
pseudo-register, not a hardware
|
||
register, even on architectures with a hardware frame pointer.
|
||
</p>
|
||
|
||
<p>
|
||
For assembly functions with Go prototypes, <code>go</code> <code>vet</code> will check that the argument names
|
||
and offsets match.
|
||
On 32-bit systems, the low and high 32 bits of a 64-bit value are distinguished by adding
|
||
a <code>_lo</code> or <code>_hi</code> suffix to the name, as in <code>arg_lo+0(FP)</code> or <code>arg_hi+4(FP)</code>.
|
||
If a Go prototype does not name its result, the expected assembly name is <code>ret</code>.
|
||
</p>
|
||
|
||
<p>
|
||
The <code>SP</code> pseudo-register is a virtual stack pointer
|
||
used to refer to frame-local variables and the arguments being
|
||
prepared for function calls.
|
||
It points to the highest address within the local stack frame, so references should use negative offsets
|
||
in the range [−framesize, 0):
|
||
<code>x-8(SP)</code>, <code>y-4(SP)</code>, and so on.
|
||
</p>
|
||
|
||
<p>
|
||
On architectures with a hardware register named <code>SP</code>,
|
||
the name prefix distinguishes
|
||
references to the virtual stack pointer from references to the architectural
|
||
<code>SP</code> register.
|
||
That is, <code>x-8(SP)</code> and <code>-8(SP)</code>
|
||
are different memory locations:
|
||
the first refers to the virtual stack pointer pseudo-register,
|
||
while the second refers to the
|
||
hardware's <code>SP</code> register.
|
||
</p>
|
||
|
||
<p>
|
||
On machines where <code>SP</code> and <code>PC</code> are
|
||
traditionally aliases for a physical, numbered register,
|
||
in the Go assembler the names <code>SP</code> and <code>PC</code>
|
||
are still treated specially;
|
||
for instance, references to <code>SP</code> require a symbol,
|
||
much like <code>FP</code>.
|
||
To access the actual hardware register use the true <code>R</code> name.
|
||
For example, on the ARM architecture the hardware
|
||
<code>SP</code> and <code>PC</code> are accessible as
|
||
<code>R13</code> and <code>R15</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Branches and direct jumps are always written as offsets to the PC, or as
|
||
jumps to labels:
|
||
</p>
|
||
|
||
<pre>
|
||
label:
|
||
MOVW $0, R1
|
||
JMP label
|
||
</pre>
|
||
|
||
<p>
|
||
Each label is visible only within the function in which it is defined.
|
||
It is therefore permitted for multiple functions in a file to define
|
||
and use the same label names.
|
||
Direct jumps and call instructions can target text symbols,
|
||
such as <code>name(SB)</code>, but not offsets from symbols,
|
||
such as <code>name+4(SB)</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Instructions, registers, and assembler directives are always in UPPER CASE to remind you
|
||
that assembly programming is a fraught endeavor.
|
||
(Exception: the <code>g</code> register renaming on ARM.)
|
||
</p>
|
||
|
||
<p>
|
||
In Go object files and binaries, the full name of a symbol is the
|
||
package path followed by a period and the symbol name:
|
||
<code>fmt.Printf</code> or <code>math/rand.Int</code>.
|
||
Because the assembler's parser treats period and slash as punctuation,
|
||
those strings cannot be used directly as identifier names.
|
||
Instead, the assembler allows the middle dot character U+00B7
|
||
and the division slash U+2215 in identifiers and rewrites them to
|
||
plain period and slash.
|
||
Within an assembler source file, the symbols above are written as
|
||
<code>fmt·Printf</code> and <code>math∕rand·Int</code>.
|
||
The assembly listings generated by the compilers when using the <code>-S</code> flag
|
||
show the period and slash directly instead of the Unicode replacements
|
||
required by the assemblers.
|
||
</p>
|
||
|
||
<p>
|
||
Most hand-written assembly files do not include the full package path
|
||
in symbol names, because the linker inserts the package path of the current
|
||
object file at the beginning of any name starting with a period:
|
||
in an assembly source file within the math/rand package implementation,
|
||
the package's Int function can be referred to as <code>·Int</code>.
|
||
This convention avoids the need to hard-code a package's import path in its
|
||
own source code, making it easier to move the code from one location to another.
|
||
</p>
|
||
|
||
<h3 id="directives">Directives</h3>
|
||
|
||
<p>
|
||
The assembler uses various directives to bind text and data to symbol names.
|
||
For example, here is a simple complete function definition. The <code>TEXT</code>
|
||
directive declares the symbol <code>runtime·profileloop</code> and the instructions
|
||
that follow form the body of the function.
|
||
The last instruction in a <code>TEXT</code> block must be some sort of jump, usually a <code>RET</code> (pseudo-)instruction.
|
||
(If it's not, the linker will append a jump-to-itself instruction; there is no fallthrough in <code>TEXTs</code>.)
|
||
After the symbol, the arguments are flags (see below)
|
||
and the frame size, a constant (but see below):
|
||
</p>
|
||
|
||
<pre>
|
||
TEXT runtime·profileloop(SB),NOSPLIT,$8
|
||
MOVQ $runtime·profileloop1(SB), CX
|
||
MOVQ CX, 0(SP)
|
||
CALL runtime·externalthreadhandler(SB)
|
||
RET
|
||
</pre>
|
||
|
||
<p>
|
||
In the general case, the frame size is followed by an argument size, separated by a minus sign.
|
||
(It's not a subtraction, just idiosyncratic syntax.)
|
||
The frame size <code>$24-8</code> states that the function has a 24-byte frame
|
||
and is called with 8 bytes of argument, which live on the caller's frame.
|
||
If <code>NOSPLIT</code> is not specified for the <code>TEXT</code>,
|
||
the argument size must be provided.
|
||
For assembly functions with Go prototypes, <code>go</code> <code>vet</code> will check that the
|
||
argument size is correct.
|
||
</p>
|
||
|
||
<p>
|
||
Note that the symbol name uses a middle dot to separate the components and is specified as an offset from the
|
||
static base pseudo-register <code>SB</code>.
|
||
This function would be called from Go source for package <code>runtime</code> using the
|
||
simple name <code>profileloop</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Global data symbols are defined by a sequence of initializing
|
||
<code>DATA</code> directives followed by a <code>GLOBL</code> directive.
|
||
Each <code>DATA</code> directive initializes a section of the
|
||
corresponding memory.
|
||
The memory not explicitly initialized is zeroed.
|
||
The general form of the <code>DATA</code> directive is
|
||
|
||
<pre>
|
||
DATA symbol+offset(SB)/width, value
|
||
</pre>
|
||
|
||
<p>
|
||
which initializes the symbol memory at the given offset and width with the given value.
|
||
The <code>DATA</code> directives for a given symbol must be written with increasing offsets.
|
||
</p>
|
||
|
||
<p>
|
||
The <code>GLOBL</code> directive declares a symbol to be global.
|
||
The arguments are optional flags and the size of the data being declared as a global,
|
||
which will have initial value all zeros unless a <code>DATA</code> directive
|
||
has initialized it.
|
||
The <code>GLOBL</code> directive must follow any corresponding <code>DATA</code> directives.
|
||
</p>
|
||
|
||
<p>
|
||
For example,
|
||
</p>
|
||
|
||
<pre>
|
||
DATA divtab<>+0x00(SB)/4, $0xf4f8fcff
|
||
DATA divtab<>+0x04(SB)/4, $0xe6eaedf0
|
||
...
|
||
DATA divtab<>+0x3c(SB)/4, $0x81828384
|
||
GLOBL divtab<>(SB), RODATA, $64
|
||
|
||
GLOBL runtime·tlsoffset(SB), NOPTR, $4
|
||
</pre>
|
||
|
||
<p>
|
||
declares and initializes <code>divtab<></code>, a read-only 64-byte table of 4-byte integer values,
|
||
and declares <code>runtime·tlsoffset</code>, a 4-byte, implicitly zeroed variable that
|
||
contains no pointers.
|
||
</p>
|
||
|
||
<p>
|
||
There may be one or two arguments to the directives.
|
||
If there are two, the first is a bit mask of flags,
|
||
which can be written as numeric expressions, added or or-ed together,
|
||
or can be set symbolically for easier absorption by a human.
|
||
Their values, defined in the standard <code>#include</code> file <code>textflag.h</code>, are:
|
||
</p>
|
||
|
||
<ul>
|
||
<li>
|
||
<code>NOPROF</code> = 1
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
Don't profile the marked function. This flag is deprecated.
|
||
</li>
|
||
<li>
|
||
<code>DUPOK</code> = 2
|
||
<br>
|
||
It is legal to have multiple instances of this symbol in a single binary.
|
||
The linker will choose one of the duplicates to use.
|
||
</li>
|
||
<li>
|
||
<code>NOSPLIT</code> = 4
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
Don't insert the preamble to check if the stack must be split.
|
||
The frame for the routine, plus anything it calls, must fit in the
|
||
spare space remaining in the current stack segment.
|
||
Used to protect routines such as the stack splitting code itself.
|
||
</li>
|
||
<li>
|
||
<code>RODATA</code> = 8
|
||
<br>
|
||
(For <code>DATA</code> and <code>GLOBL</code> items.)
|
||
Put this data in a read-only section.
|
||
</li>
|
||
<li>
|
||
<code>NOPTR</code> = 16
|
||
<br>
|
||
(For <code>DATA</code> and <code>GLOBL</code> items.)
|
||
This data contains no pointers and therefore does not need to be
|
||
scanned by the garbage collector.
|
||
</li>
|
||
<li>
|
||
<code>WRAPPER</code> = 32
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
This is a wrapper function and should not count as disabling <code>recover</code>.
|
||
</li>
|
||
<li>
|
||
<code>NEEDCTXT</code> = 64
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
This function is a closure so it uses its incoming context register.
|
||
</li>
|
||
<li>
|
||
<code>LOCAL</code> = 128
|
||
<br>
|
||
This symbol is local to the dynamic shared object.
|
||
</li>
|
||
<li>
|
||
<code>TLSBSS</code> = 256
|
||
<br>
|
||
(For <code>DATA</code> and <code>GLOBL</code> items.)
|
||
Put this data in thread local storage.
|
||
</li>
|
||
<li>
|
||
<code>NOFRAME</code> = 512
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
Do not insert instructions to allocate a stack frame and save/restore the return
|
||
address, even if this is not a leaf function.
|
||
Only valid on functions that declare a frame size of 0.
|
||
</li>
|
||
<li>
|
||
<code>TOPFRAME</code> = 2048
|
||
<br>
|
||
(For <code>TEXT</code> items.)
|
||
Function is the outermost frame of the call stack. Traceback should stop at this function.
|
||
</li>
|
||
</ul>
|
||
|
||
<h3 id="special-instructions">Special instructions</h3>
|
||
|
||
<p>
|
||
The <code>PCALIGN</code> pseudo-instruction is used to indicate that the next instruction should be aligned
|
||
to a specified boundary by padding with no-op instructions.
|
||
</p>
|
||
|
||
<p>
|
||
It is currently supported on arm64, amd64, ppc64, loong64 and riscv64.
|
||
|
||
For example, the start of the <code>MOVD</code> instruction below is aligned to 32 bytes:
|
||
<pre>
|
||
PCALIGN $32
|
||
MOVD $2, R0
|
||
</pre>
|
||
</p>
|
||
|
||
<h3 id="data-offsets">Interacting with Go types and constants</h3>
|
||
|
||
<p>
|
||
If a package has any .s files, then <code>go build</code> will direct
|
||
the compiler to emit a special header called <code>go_asm.h</code>,
|
||
which the .s files can then <code>#include</code>.
|
||
The file contains symbolic <code>#define</code> constants for the
|
||
offsets of Go struct fields, the sizes of Go struct types, and most
|
||
Go <code>const</code> declarations defined in the current package.
|
||
Go assembly should avoid making assumptions about the layout of Go
|
||
types and instead use these constants.
|
||
This improves the readability of assembly code, and keeps it robust to
|
||
changes in data layout either in the Go type definitions or in the
|
||
layout rules used by the Go compiler.
|
||
</p>
|
||
|
||
<p>
|
||
Constants are of the form <code>const_<i>name</i></code>.
|
||
For example, given the Go declaration <code>const bufSize =
|
||
1024</code>, assembly code can refer to the value of this constant
|
||
as <code>const_bufSize</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Field offsets are of the form <code><i>type</i>_<i>field</i></code>.
|
||
Struct sizes are of the form <code><i>type</i>__size</code>.
|
||
For example, consider the following Go definition:
|
||
</p>
|
||
|
||
<pre>
|
||
type reader struct {
|
||
buf [bufSize]byte
|
||
r int
|
||
}
|
||
</pre>
|
||
|
||
<p>
|
||
Assembly can refer to the size of this struct
|
||
as <code>reader__size</code> and the offsets of the two fields
|
||
as <code>reader_buf</code> and <code>reader_r</code>.
|
||
Hence, if register <code>R1</code> contains a pointer to
|
||
a <code>reader</code>, assembly can reference the <code>r</code> field
|
||
as <code>reader_r(R1)</code>.
|
||
</p>
|
||
|
||
<p>
|
||
If any of these <code>#define</code> names are ambiguous (for example,
|
||
a struct with a <code>_size</code> field), <code>#include
|
||
"go_asm.h"</code> will fail with a "redefinition of macro" error.
|
||
</p>
|
||
|
||
<h3 id="runtime">Runtime Coordination</h3>
|
||
|
||
<p>
|
||
For garbage collection to run correctly, the runtime must know the
|
||
location of pointers in all global data and in most stack frames.
|
||
The Go compiler emits this information when compiling Go source files,
|
||
but assembly programs must define it explicitly.
|
||
</p>
|
||
|
||
<p>
|
||
A data symbol marked with the <code>NOPTR</code> flag (see above)
|
||
is treated as containing no pointers to runtime-allocated data.
|
||
A data symbol with the <code>RODATA</code> flag
|
||
is allocated in read-only memory and is therefore treated
|
||
as implicitly marked <code>NOPTR</code>.
|
||
A data symbol with a total size smaller than a pointer
|
||
is also treated as implicitly marked <code>NOPTR</code>.
|
||
It is not possible to define a symbol containing pointers in an assembly source file;
|
||
such a symbol must be defined in a Go source file instead.
|
||
Assembly source can still refer to the symbol by name
|
||
even without <code>DATA</code> and <code>GLOBL</code> directives.
|
||
A good general rule of thumb is to define all non-<code>RODATA</code>
|
||
symbols in Go instead of in assembly.
|
||
</p>
|
||
|
||
<p>
|
||
Each function also needs annotations giving the location of
|
||
live pointers in its arguments, results, and local stack frame.
|
||
For an assembly function with no pointer results and
|
||
either no local stack frame or no function calls,
|
||
the only requirement is to define a Go prototype for the function
|
||
in a Go source file in the same package. The name of the assembly
|
||
function must not contain the package name component (for example,
|
||
function <code>Syscall</code> in package <code>syscall</code> should
|
||
use the name <code>·Syscall</code> instead of the equivalent name
|
||
<code>syscall·Syscall</code> in its <code>TEXT</code> directive).
|
||
For more complex situations, explicit annotation is needed.
|
||
These annotations use pseudo-instructions defined in the standard
|
||
<code>#include</code> file <code>funcdata.h</code>.
|
||
</p>
|
||
|
||
<p>
|
||
If a function has no arguments and no results,
|
||
the pointer information can be omitted.
|
||
This is indicated by an argument size annotation of <code>$<i>n</i>-0</code>
|
||
on the <code>TEXT</code> instruction.
|
||
Otherwise, pointer information must be provided by
|
||
a Go prototype for the function in a Go source file,
|
||
even for assembly functions not called directly from Go.
|
||
(The prototype will also let <code>go</code> <code>vet</code> check the argument references.)
|
||
At the start of the function, the arguments are assumed
|
||
to be initialized but the results are assumed uninitialized.
|
||
If the results will hold live pointers during a call instruction,
|
||
the function should start by zeroing the results and then
|
||
executing the pseudo-instruction <code>GO_RESULTS_INITIALIZED</code>.
|
||
This instruction records that the results are now initialized
|
||
and should be scanned during stack movement and garbage collection.
|
||
It is typically easier to arrange that assembly functions do not
|
||
return pointers or do not contain call instructions;
|
||
no assembly functions in the standard library use
|
||
<code>GO_RESULTS_INITIALIZED</code>.
|
||
</p>
|
||
|
||
<p>
|
||
If a function has no local stack frame,
|
||
the pointer information can be omitted.
|
||
This is indicated by a local frame size annotation of <code>$0-<i>n</i></code>
|
||
on the <code>TEXT</code> instruction.
|
||
The pointer information can also be omitted if the
|
||
function contains no call instructions.
|
||
Otherwise, the local stack frame must not contain pointers,
|
||
and the assembly must confirm this fact by executing the
|
||
pseudo-instruction <code>NO_LOCAL_POINTERS</code>.
|
||
Because stack resizing is implemented by moving the stack,
|
||
the stack pointer may change during any function call:
|
||
even pointers to stack data must not be kept in local variables.
|
||
</p>
|
||
|
||
<p>
|
||
Assembly functions should always be given Go prototypes,
|
||
both to provide pointer information for the arguments and results
|
||
and to let <code>go</code> <code>vet</code> check that
|
||
the offsets being used to access them are correct.
|
||
</p>
|
||
|
||
<h2 id="architectures">Architecture-specific details</h2>
|
||
|
||
<p>
|
||
It is impractical to list all the instructions and other details for each machine.
|
||
To see what instructions are defined for a given machine, say ARM,
|
||
look in the source for the <code>obj</code> support library for
|
||
that architecture, located in the directory <code>src/cmd/internal/obj/arm</code>.
|
||
In that directory is a file <code>a.out.go</code>; it contains
|
||
a long list of constants starting with <code>A</code>, like this:
|
||
</p>
|
||
|
||
<pre>
|
||
const (
|
||
AAND = obj.ABaseARM + obj.A_ARCHSPECIFIC + iota
|
||
AEOR
|
||
ASUB
|
||
ARSB
|
||
AADD
|
||
...
|
||
</pre>
|
||
|
||
<p>
|
||
This is the list of instructions and their spellings as known to the assembler and linker for that architecture.
|
||
Each instruction begins with an initial capital <code>A</code> in this list, so <code>AAND</code>
|
||
represents the bitwise and instruction,
|
||
<code>AND</code> (without the leading <code>A</code>),
|
||
and is written in assembly source as <code>AND</code>.
|
||
The enumeration is mostly in alphabetical order.
|
||
(The architecture-independent <code>AXXX</code>, defined in the
|
||
<code>cmd/internal/obj</code> package,
|
||
represents an invalid instruction).
|
||
The sequence of the <code>A</code> names has nothing to do with the actual
|
||
encoding of the machine instructions.
|
||
The <code>cmd/internal/obj</code> package takes care of that detail.
|
||
</p>
|
||
|
||
<p>
|
||
The instructions for both the 386 and AMD64 architectures are listed in
|
||
<code>cmd/internal/obj/x86/a.out.go</code>.
|
||
</p>
|
||
|
||
<p>
|
||
The architectures share syntax for common addressing modes such as
|
||
<code>(R1)</code> (register indirect),
|
||
<code>4(R1)</code> (register indirect with offset), and
|
||
<code>$foo(SB)</code> (absolute address).
|
||
The assembler also supports some (not necessarily all) addressing modes
|
||
specific to each architecture.
|
||
The sections below list these.
|
||
</p>
|
||
|
||
<p>
|
||
One detail evident in the examples from the previous sections is that data in the instructions flows from left to right:
|
||
<code>MOVQ</code> <code>$0,</code> <code>CX</code> clears <code>CX</code>.
|
||
This rule applies even on architectures where the conventional notation uses the opposite direction.
|
||
</p>
|
||
|
||
<p>
|
||
Here follow some descriptions of key Go-specific details for the supported architectures.
|
||
</p>
|
||
|
||
<h3 id="x86">32-bit Intel 386</h3>
|
||
|
||
<p>
|
||
The runtime pointer to the <code>g</code> structure is maintained
|
||
through the value of an otherwise unused (as far as Go is concerned) register in the MMU.
|
||
In the runtime package, assembly code can include <code>go_tls.h</code>, which defines
|
||
an OS- and architecture-dependent macro <code>get_tls</code> for accessing this register.
|
||
The <code>get_tls</code> macro takes one argument, which is the register to load the
|
||
<code>g</code> pointer into.
|
||
</p>
|
||
|
||
<p>
|
||
For example, the sequence to load <code>g</code> and <code>m</code>
|
||
using <code>CX</code> looks like this:
|
||
</p>
|
||
|
||
<pre>
|
||
#include "go_tls.h"
|
||
#include "go_asm.h"
|
||
...
|
||
get_tls(CX)
|
||
MOVL g(CX), AX // Move g into AX.
|
||
MOVL g_m(AX), BX // Move g.m into BX.
|
||
</pre>
|
||
|
||
<p>
|
||
The <code>get_tls</code> macro is also defined on <a href="#amd64">amd64</a>.
|
||
</p>
|
||
|
||
<p>
|
||
Addressing modes:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>(DI)(BX*2)</code>: The location at address <code>DI</code> plus <code>BX*2</code>.
|
||
</li>
|
||
|
||
<li>
|
||
<code>64(DI)(BX*2)</code>: The location at address <code>DI</code> plus <code>BX*2</code> plus 64.
|
||
These modes accept only 1, 2, 4, and 8 as scale factors.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
When using the compiler and assembler's
|
||
<code>-dynlink</code> or <code>-shared</code> modes,
|
||
any load or store of a fixed memory location such as a global variable
|
||
must be assumed to overwrite <code>CX</code>.
|
||
Therefore, to be safe for use with these modes,
|
||
assembly sources should typically avoid CX except between memory references.
|
||
</p>
|
||
|
||
<h3 id="amd64">64-bit Intel 386 (a.k.a. amd64)</h3>
|
||
|
||
<p>
|
||
The two architectures behave largely the same at the assembler level.
|
||
Assembly code to access the <code>m</code> and <code>g</code>
|
||
pointers on the 64-bit version is the same as on the 32-bit 386,
|
||
except it uses <code>MOVQ</code> rather than <code>MOVL</code>:
|
||
</p>
|
||
|
||
<pre>
|
||
get_tls(CX)
|
||
MOVQ g(CX), AX // Move g into AX.
|
||
MOVQ g_m(AX), BX // Move g.m into BX.
|
||
</pre>
|
||
|
||
<p>
|
||
Register <code>BP</code> is callee-save.
|
||
The assembler automatically inserts <code>BP</code> save/restore when frame size is larger than zero.
|
||
Using <code>BP</code> as a general purpose register is allowed,
|
||
however it can interfere with sampling-based profiling.
|
||
</p>
|
||
|
||
<h3 id="arm">ARM</h3>
|
||
|
||
<p>
|
||
The registers <code>R10</code> and <code>R11</code>
|
||
are reserved by the compiler and linker.
|
||
</p>
|
||
|
||
<p>
|
||
<code>R10</code> points to the <code>g</code> (goroutine) structure.
|
||
Within assembler source code, this pointer must be referred to as <code>g</code>;
|
||
the name <code>R10</code> is not recognized.
|
||
</p>
|
||
|
||
<p>
|
||
To make it easier for people and compilers to write assembly, the ARM linker
|
||
allows general addressing forms and pseudo-operations like <code>DIV</code> or <code>MOD</code>
|
||
that may not be expressible using a single hardware instruction.
|
||
It implements these forms as multiple instructions, often using the <code>R11</code> register
|
||
to hold temporary values.
|
||
Hand-written assembly can use <code>R11</code>, but doing so requires
|
||
being sure that the linker is not also using it to implement any of the other
|
||
instructions in the function.
|
||
</p>
|
||
|
||
<p>
|
||
When defining a <code>TEXT</code>, specifying frame size <code>$-4</code>
|
||
tells the linker that this is a leaf function that does not need to save <code>LR</code> on entry.
|
||
</p>
|
||
|
||
<p>
|
||
The name <code>SP</code> always refers to the virtual stack pointer described earlier.
|
||
For the hardware register, use <code>R13</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Condition code syntax is to append a period and the one- or two-letter code to the instruction,
|
||
as in <code>MOVW.EQ</code>.
|
||
Multiple codes may be appended: <code>MOVM.IA.W</code>.
|
||
The order of the code modifiers is irrelevant.
|
||
</p>
|
||
|
||
<p>
|
||
Addressing modes:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>R0->16</code>
|
||
<br>
|
||
<code>R0>>16</code>
|
||
<br>
|
||
<code>R0<<16</code>
|
||
<br>
|
||
<code>R0@>16</code>:
|
||
For <code><<</code>, left shift <code>R0</code> by 16 bits.
|
||
The other codes are <code>-></code> (arithmetic right shift),
|
||
<code>>></code> (logical right shift), and
|
||
<code>@></code> (rotate right).
|
||
</li>
|
||
|
||
<li>
|
||
<code>R0->R1</code>
|
||
<br>
|
||
<code>R0>>R1</code>
|
||
<br>
|
||
<code>R0<<R1</code>
|
||
<br>
|
||
<code>R0@>R1</code>:
|
||
For <code><<</code>, left shift <code>R0</code> by the count in <code>R1</code>.
|
||
The other codes are <code>-></code> (arithmetic right shift),
|
||
<code>>></code> (logical right shift), and
|
||
<code>@></code> (rotate right).
|
||
|
||
</li>
|
||
|
||
<li>
|
||
<code>[R0,g,R12-R15]</code>: For multi-register instructions, the set comprising
|
||
<code>R0</code>, <code>g</code>, and <code>R12</code> through <code>R15</code> inclusive.
|
||
</li>
|
||
|
||
<li>
|
||
<code>(R5, R6)</code>: Destination register pair.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<h3 id="arm64">ARM64</h3>
|
||
|
||
<p>
|
||
<code>R18</code> is the "platform register", reserved on the Apple platform.
|
||
To prevent accidental misuse, the register is named <code>R18_PLATFORM</code>.
|
||
<code>R27</code> and <code>R28</code> are reserved by the compiler and linker.
|
||
<code>R29</code> is the frame pointer.
|
||
<code>R30</code> is the link register.
|
||
</p>
|
||
|
||
<p>
|
||
Instruction modifiers are appended to the instruction following a period.
|
||
The only modifiers are <code>P</code> (postincrement) and <code>W</code>
|
||
(preincrement):
|
||
<code>MOVW.P</code>, <code>MOVW.W</code>
|
||
</p>
|
||
|
||
<p>
|
||
Addressing modes:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>R0->16</code>
|
||
<br>
|
||
<code>R0>>16</code>
|
||
<br>
|
||
<code>R0<<16</code>
|
||
<br>
|
||
<code>R0@>16</code>:
|
||
These are the same as on the 32-bit ARM.
|
||
</li>
|
||
|
||
<li>
|
||
<code>$(8<<12)</code>:
|
||
Left shift the immediate value <code>8</code> by <code>12</code> bits.
|
||
</li>
|
||
|
||
<li>
|
||
<code>8(R0)</code>:
|
||
Add the value of <code>R0</code> and <code>8</code>.
|
||
</li>
|
||
|
||
<li>
|
||
<code>(R2)(R0)</code>:
|
||
The location at <code>R0</code> plus <code>R2</code>.
|
||
</li>
|
||
|
||
<li>
|
||
<code>R0.UXTB</code>
|
||
<br>
|
||
<code>R0.UXTB<<imm</code>:
|
||
<code>UXTB</code>: extract an 8-bit value from the low-order bits of <code>R0</code> and zero-extend it to the size of <code>R0</code>.
|
||
<code>R0.UXTB<<imm</code>: left shift the result of <code>R0.UXTB</code> by <code>imm</code> bits.
|
||
The <code>imm</code> value can be 0, 1, 2, 3, or 4.
|
||
The other extensions include <code>UXTH</code> (16-bit), <code>UXTW</code> (32-bit), and <code>UXTX</code> (64-bit).
|
||
</li>
|
||
|
||
<li>
|
||
<code>R0.SXTB</code>
|
||
<br>
|
||
<code>R0.SXTB<<imm</code>:
|
||
<code>SXTB</code>: extract an 8-bit value from the low-order bits of <code>R0</code> and sign-extend it to the size of <code>R0</code>.
|
||
<code>R0.SXTB<<imm</code>: left shift the result of <code>R0.SXTB</code> by <code>imm</code> bits.
|
||
The <code>imm</code> value can be 0, 1, 2, 3, or 4.
|
||
The other extensions include <code>SXTH</code> (16-bit), <code>SXTW</code> (32-bit), and <code>SXTX</code> (64-bit).
|
||
</li>
|
||
|
||
<li>
|
||
<code>(R5, R6)</code>: Register pair for <code>LDAXP</code>/<code>LDP</code>/<code>LDXP</code>/<code>STLXP</code>/<code>STP</code>/<code>STP</code>.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
Reference: <a href="/pkg/cmd/internal/obj/arm64">Go ARM64 Assembly Instructions Reference Manual</a>
|
||
</p>
|
||
|
||
<h3 id="ppc64">PPC64</h3>
|
||
|
||
<p>
|
||
This assembler is used by GOARCH values ppc64 and ppc64le.
|
||
</p>
|
||
|
||
<p>
|
||
Reference: <a href="/pkg/cmd/internal/obj/ppc64">Go PPC64 Assembly Instructions Reference Manual</a>
|
||
</p>
|
||
|
||
<h3 id="s390x">IBM z/Architecture, a.k.a. s390x</h3>
|
||
|
||
<p>
|
||
The registers <code>R10</code> and <code>R11</code> are reserved.
|
||
The assembler uses them to hold temporary values when assembling some instructions.
|
||
</p>
|
||
|
||
<p>
|
||
<code>R13</code> points to the <code>g</code> (goroutine) structure.
|
||
This register must be referred to as <code>g</code>; the name <code>R13</code> is not recognized.
|
||
</p>
|
||
|
||
<p>
|
||
<code>R15</code> points to the stack frame and should typically only be accessed using the
|
||
virtual registers <code>SP</code> and <code>FP</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Load- and store-multiple instructions operate on a range of registers.
|
||
The range of registers is specified by a start register and an end register.
|
||
For example, <code>LMG</code> <code>(R9),</code> <code>R5,</code> <code>R7</code> would load
|
||
<code>R5</code>, <code>R6</code> and <code>R7</code> with the 64-bit values at
|
||
<code>0(R9)</code>, <code>8(R9)</code> and <code>16(R9)</code> respectively.
|
||
</p>
|
||
|
||
<p>
|
||
Storage-and-storage instructions such as <code>MVC</code> and <code>XC</code> are written
|
||
with the length as the first argument.
|
||
For example, <code>XC</code> <code>$8,</code> <code>(R9),</code> <code>(R9)</code> would clear
|
||
eight bytes at the address specified in <code>R9</code>.
|
||
</p>
|
||
|
||
<p>
|
||
If a vector instruction takes a length or an index as an argument then it will be the
|
||
first argument.
|
||
For example, <code>VLEIF</code> <code>$1,</code> <code>$16,</code> <code>V2</code> will load
|
||
the value sixteen into index one of <code>V2</code>.
|
||
Care should be taken when using vector instructions to ensure that they are available at
|
||
runtime.
|
||
To use vector instructions a machine must have both the vector facility (bit 129 in the
|
||
facility list) and kernel support.
|
||
Without kernel support a vector instruction will have no effect (it will be equivalent
|
||
to a <code>NOP</code> instruction).
|
||
</p>
|
||
|
||
<p>
|
||
Addressing modes:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>(R5)(R6*1)</code>: The location at <code>R5</code> plus <code>R6</code>.
|
||
It is a scaled mode as on the x86, but the only scale allowed is <code>1</code>.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<h3 id="mips">MIPS, MIPS64</h3>
|
||
|
||
<p>
|
||
General purpose registers are named <code>R0</code> through <code>R31</code>,
|
||
floating point registers are <code>F0</code> through <code>F31</code>.
|
||
</p>
|
||
|
||
<p>
|
||
<code>R30</code> is reserved to point to <code>g</code>.
|
||
<code>R23</code> is used as a temporary register.
|
||
</p>
|
||
|
||
<p>
|
||
In a <code>TEXT</code> directive, the frame size <code>$-4</code> for MIPS or
|
||
<code>$-8</code> for MIPS64 instructs the linker not to save <code>LR</code>.
|
||
</p>
|
||
|
||
<p>
|
||
<code>SP</code> refers to the virtual stack pointer.
|
||
For the hardware register, use <code>R29</code>.
|
||
</p>
|
||
|
||
<p>
|
||
Addressing modes:
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>
|
||
<code>16(R1)</code>: The location at <code>R1</code> plus 16.
|
||
</li>
|
||
|
||
<li>
|
||
<code>(R1)</code>: Alias for <code>0(R1)</code>.
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
<p>
|
||
The value of <code>GOMIPS</code> environment variable (<code>hardfloat</code> or
|
||
<code>softfloat</code>) is made available to assembly code by predefining either
|
||
<code>GOMIPS_hardfloat</code> or <code>GOMIPS_softfloat</code>.
|
||
</p>
|
||
|
||
<p>
|
||
The value of <code>GOMIPS64</code> environment variable (<code>hardfloat</code> or
|
||
<code>softfloat</code>) is made available to assembly code by predefining either
|
||
<code>GOMIPS64_hardfloat</code> or <code>GOMIPS64_softfloat</code>.
|
||
</p>
|
||
|
||
<h3 id="unsupported_opcodes">Unsupported opcodes</h3>
|
||
|
||
<p>
|
||
The assemblers are designed to support the compiler so not all hardware instructions
|
||
are defined for all architectures: if the compiler doesn't generate it, it might not be there.
|
||
If you need to use a missing instruction, there are two ways to proceed.
|
||
One is to update the assembler to support that instruction, which is straightforward
|
||
but only worthwhile if it's likely the instruction will be used again.
|
||
Instead, for simple one-off cases, it's possible to use the <code>BYTE</code>
|
||
and <code>WORD</code> directives
|
||
to lay down explicit data into the instruction stream within a <code>TEXT</code>.
|
||
Here's how the 386 runtime defines the 64-bit atomic load function.
|
||
</p>
|
||
|
||
<pre>
|
||
// uint64 atomicload64(uint64 volatile* addr);
|
||
// so actually
|
||
// void atomicload64(uint64 *res, uint64 volatile *addr);
|
||
TEXT runtime·atomicload64(SB), NOSPLIT, $0-12
|
||
MOVL ptr+0(FP), AX
|
||
TESTL $7, AX
|
||
JZ 2(PC)
|
||
MOVL 0, AX // crash with nil ptr deref
|
||
LEAL ret_lo+4(FP), BX
|
||
// MOVQ (%EAX), %MM0
|
||
BYTE $0x0f; BYTE $0x6f; BYTE $0x00
|
||
// MOVQ %MM0, 0(%EBX)
|
||
BYTE $0x0f; BYTE $0x7f; BYTE $0x03
|
||
// EMMS
|
||
BYTE $0x0F; BYTE $0x77
|
||
RET
|
||
</pre>
|