Go Assembler メモ

GoのアセンブラPlan 9のものを引き継いでいる。

疑似レジスタ

  • FP: Frame pointer
    • arguments and locals.
  • PC: Program counter
    • jumps and branches.
  • SB: Static base pointer
    • global symbols.
  • SP: Stack pointer
    • top of stack.

引数

引数はFP擬似レジスタからのオフセットで指定する

第一引数 0(FP) 第二引数 8(FP) ※ 64bit環境の場合

変数

変数はSP疑似レジスタからのオフセットで指定する。 SPはローカルフレームスタックの戦闘を指すので、参照するときは負のオフセットを指定する。

SPというマシンレジスタがある場合は、名前プレフィックスの有無で区別する。

x-8(SP) は疑似レジスタ -8(S) はマシンレジスタ

名前プレフィックス

引数や変数を指すときは名前つけができる。 Goのプロトタイプを持っている関数なら、go vetでオフセットが正しいかチェックできる。

// func add(x, y int) int
TEXT ·add(SB),NOSPLIT,$0
    MOVQ x+0(FP), AX
    ADDQ y+8(FP), AX
    MOVQ AX, ret+16(FP)
    RET

返り値が名前付きじゃない場合、 暗黙的にretが返り値の名前になる

シンボル名

Goのオブジェクトファイルとバイナリではシンボルの名前は、fmt.Printf, math/rand.Int のようにパッケージ名を.でつないだ名前になる。

アセンブラ./を区切り文字として使うので、中黒文字(MIDDLE DOT: U+00B7)と割り算文字(DIVISION SLASH: U+2215)がピリオドとスラッシュに置換される。

fmt.Printf → fmt·Printf math/rand.Int → math∕rand·Int

関数

関数はTEXTディレクティブで宣言する。

シンボルのあとには、フラグとフレームサイズの定数を書く。 フレームサイズの後ろには引数のサイズをフレームサイズのあとにマイナス記号で区切ってから指定する。NOSPLITを指定していない場合に引数のサイズは必須。

$24-$8が指定された場合、24バイトのローカルフレームを持ち、8バイトの引数を持つ。

シンボルの宣言のあとに、関数の中身を書く。関数は最後にジャンプ命令でなければならない。

// func add(x, y int) int
TEXT ·add(SB),NOSPLIT,$0-24
    MOVQ x+0(FP), AX
    ADDQ y+8(FP), AX
    MOVQ AX, ret+16(FP)
    RET

NOSPLIT

スタックオーバーフローのチェックを外す指定。

https://golang.org/cmd/compile/

The //go:nosplit directive specifies that the next function declared in the file must not include a stack overflow check. This is most commonly used by low-level runtime sources invoked at times when it is unsafe for the calling goroutine to be preempted.

値はDATAディレクティブで宣言する。

シンボルのあとにスラッシュで区切って、データサイズをバイト数で指定する。 カンマまで区切ったあとには、シンボルに束縛される値を書く。

DATA ・spam(SB)/4, $1

GLOBLディレクティブ

GLOBLディレクティブはシンボルがグローバルであることを宣言する。 GLOBLディレクティブは対応するDATAディレクティブのあとに宣言しなければならない。

DATA    array+0(SB)/4, $"abc\z"
GLOBL   array(SB), $4