gccでのループ処理のアセンブリを見てみる

cの実装

簡単なループ処理です。

int main(void)
{
    int r = 0;
    for (int i = 0; i < 10; i++)
    {
        r++;
    }
    return r;
}

gccコンパイル

↓のようにコンパイルします。

gcc -S -masm=intel test.c

-Sオプションをつけるとアセンブリソースファイルを作成してくれます。
アセンブルは行いません。

-masmオプションでアセンブリの方言を指定することができます。
ここではintel記法を指定しています。

筆者の環境ではデフォルトだとAT&T記法になるようでした。

出力結果

  .file   "test.c"
    .intel_syntax noprefix
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    mov DWORD PTR [rbp-4], 0
    mov DWORD PTR [rbp-8], 0
    jmp .L2
.L3:
    add DWORD PTR [rbp-4], 1
    add DWORD PTR [rbp-8], 1
.L2:
    cmp DWORD PTR [rbp-8], 9
    jle .L3
    mov eax, DWORD PTR [rbp-4]
    pop rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 7.3.1 20180712 (Red Hat 7.3.1-9)"
    .section    .note.GNU-stack,"",@progbits

ローカル変数

  • 変数r
    • DWORD PTR [rbp-4]
  • 変数i
    • DWORD PTR [rbp-8]

ローカル変数はスタック領域にスタックされている。

ラベル

全て接頭辞.Lが付けられています。
.Lがあるとラベルがファイルスコープになります。

for文の制御構造がどのラベルに対応しているか

  • 初期化式
    • .LFB0の末尾
  • 条件式
    • .L2の頭
  • forブロック内のプログラム
    • .L3の頭
  • 再設定式
    • .L3の末尾

初期化式

for (int i = 0; i < 10; i++)int i = 0の部分。

  mov DWORD PTR [rbp-8], 0
    jmp .L2

変数iに0を入れています。

初期化が終わってすぐに.L2にジャンプします。

条件式

for (int i = 0; i < 10; i++)i < 10の部分。

.L2:
    cmp DWORD PTR [rbp-8], 9
    jle .L3

変数iを9と比較して9以下であれば.L3にジャンプするという命令です。

比較命令cmpは引数の二つの値を比較します。
比較命令の結果はフラグレジスタにセットされます。

jleはフラグレジスタの値を評価して比較の結果が等しいかそれより小さい場合に指定した位置にジャンプするという命令です。

ブロック内の処理と再設定式

{r++;}for (int i = 0; i < 10; i++)i++の部分。

.L3:
    add DWORD PTR [rbp-4], 1
    add DWORD PTR [rbp-8], 1

変数rに1を足してます。

変数iに1を足してます。

順番はブロック内の処理->再設定式です。