"volatile"をつけると値がレジスタに浮かない

チラ裏にでも書いてろ、って内容なので何ですが、、、

C言語の変数にvolatileをつけたら値がレジスタにキャッシュされたままにならないね、と言う話を実際に確認してみました、と言う話。

そのままろくに考えないでやってみます。

まず、下記のような意味のないコードを用意してみました。

int n = 0;

int main(){
        n++;
        n--;
        n = 100;
        return 0;
}

これを

arm-none-eabi-gcc -O0 -S test.c

のようにコンパイルしてみると

main:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        ldr     r3, .L3
        ldr     r3, [r3, #0]
        add     r2, r3, #1
        ldr     r3, .L3
        str     r2, [r3, #0]
        ldr     r3, .L3
        ldr     r3, [r3, #0]
        sub     r2, r3, #1
        ldr     r3, .L3
        str     r2, [r3, #0]
        ldr     r3, .L3
        mov     r2, #100
        str     r2, [r3, #0]
        mov     r3, #0
        mov     r0, r3
        add     sp, fp, #0
        ldmfd   sp!, {fp}
        bx      lr

インクリメント・デクリメントが明確にコード化されていて、(`add`,`sub`)それがメモリに直書きされている。(`str`) また、変数位置をいちいちメモリからロードし直している。

これを`-O1`でコンパイルしてみる

arm-none-eabi-gcc -O1 -S test.c

すると

main:
        mov     r2, #100
        ldr     r3, .L2
        str     r2, [r3, #0]
        mov     r0, #0
        bx      lr

あっ、演算どころか、変数値の読み込みすらしてねぇ。。。

最適化オプションつけてみると、、、

arm-none-eabi-gcc -O3 -S test.c

main:
        mov     r2, #100
        ldr     r3, .L2
        str     r2, [r3, #0]
        mov     r0, #0
        bx      lr

まぁ、これ以上削るのは難しいわな。。。

`volatile`版を用意した。

volatile int n = 0;

int main(){
        n++;
        n--;
        n = 100;
        return 0;
}

これをO0でコンパイルしてみる。

arm-none-eabi-gcc -O0 -S testv.c

この結果は、non-volatile版の-O0とまったく同じでした。。。

では、最適化オプションつけてみよう。。。

arm-none-eabi-gcc -O1 -S testv.c
arm-none-eabi-gcc -O3 -S testv.c

下記のコードのように定数値のロードについて少し工夫された様子が見える。
ただ、算術演算やデータ領域との読み書きに使用する。`ldr`や`str`は端折らずに入れられている。

main:
        ldr     r3, .L2
        ldr     r2, [r3, #0]
        add     r2, r2, #1
        str     r2, [r3, #0]
        ldr     r1, [r3, #0]
        mov     r2, #100
        sub     r1, r1, #1
        str     r1, [r3, #0]
        mov     r0, #0
        str     r2, [r3, #0]
        bx      lr
結論

最適化オプションによらずにリード、ライトを確実にしたかったらvolatileをつけるとよさそう。
でも、それ以上の保障をしたければ排他命令(LDREX/STREX,CLREX)を使用するべきなのかも。