"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)を使用するべきなのかも。