GCC のインラインアセンブラでは出力 (output operands) リスト、入力 (input operands) リストの他に、アセンブラブロックの中でどのレジスタが破壊されるか(つまり、どのレジスタをコンパイラ側で復元しなければいけないか)を指定する 破壊 (clobber) リストを指定することができます。

ここで、破壊リストに指定できるレジスタは出力リストにも入力リストにも出現してはいけない、という仕様になっています。出力リストに出現してはいけないのは分かる(機能的に排他なので)のですが、入力リストに出現してはいけないという制約は不便です。そのままではアセンブラブロック側で対処しなければいけなくなります。

ではどうするかというと、リンク先をよく読むと分かりますが、出力リストにも指定してしまえば同じことができます。

例えば以下のコードでは2つのasm文で eax に同じ値 (10) を渡すように指示しており、さらに1つ目のasm文では出力リストも破壊リストも空にしています。そのため、最適化をかけた場合は2つ目のasm文のところで eax に 10 を保存する命令を省略します。実際には1つ目のアセンブリブロックで eax の値は変更されているため、期待される結果 (11) にはなりません。

[umezawa@devcent7:pts/0 ~]$ cat clobber.c
int main(void)
{
        int ret;

        asm volatile(
                "incl %%eax\n\t"
        :
        : "a"(10));

        asm volatile(
                "incl %%eax\n\t"
        : "=a"(ret)
        : "a"(10));

        return ret;
}
[umezawa@devcent7:pts/0 ~]$ gcc -S -o- -O2 clobber.c
        .file   "clobber.c"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        movl    $10, %eax
#APP
# 5 "clobber.c" 1
        incl %eax

# 0 "" 2
# 10 "clobber.c" 1
        incl %eax

# 0 "" 2
#NO_APP
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-11)"
        .section        .note.GNU-stack,"",@progbits

一方、1つ目のasm文で出力リストに eax を指定すると、暗黙的に eax はアセンブリブロック内で変更されると見なされ、2つ目のasm文の前に改めて eax に値を設定するようになります。レジスタから変数に移動する処理が無駄のように思えますが、後で変数を使わないのであれば、最適化をかけるとその処理は消滅するので問題ありません。

[umezawa@devcent7:pts/0 ~]$ cat clobber.c
int main(void)
{
        int ret;
        void* clobber;

        asm volatile(
                "incl %%eax\n\t"
        : "=a"(clobber)
        : "a"(10));

        asm volatile(
                "incl %%eax\n\t"
        : "=a"(ret)
        : "a"(10));

        return ret;
}
[umezawa@devcent7:pts/0 ~]$ gcc -S -o- -O2 clobber.c
        .file   "clobber.c"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        movl    $10, %edx
        movl    %edx, %eax
#APP
# 6 "clobber.c" 1
        incl %eax

# 0 "" 2
#NO_APP
        movl    %edx, %eax
#APP
# 11 "clobber.c" 1
        incl %eax

# 0 "" 2
#NO_APP
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-11)"
        .section        .note.GNU-stack,"",@progbits

ちなみに、出力先の変数を複数のレジスタで共用しても特にエラーは出ません。(いいのか?)

Trackback

no comment untill now

Add your comment now