基本的に__asm__ __volatile__ ()
は何をし、ARMアーキテクチャ)の"memory"
の重要性は何ですか?
_asm volatile("" ::: "memory");
_
コンパイラレベルのメモリバリアを作成し、バリアを越えてメモリアクセスを並べ替えないようにオプティマイザを強制します。
たとえば、特定の順序でいくつかのアドレスにアクセスする必要がある場合(おそらく、そのメモリ領域は実際にはメモリではなく別のデバイスによってバッキングされているため)、コンパイラにこれを伝える必要があります。効率化のため。
このシナリオでは、アドレスの値をインクリメントし、何かを読み取って、隣接アドレスの別の値をインクリメントする必要があると想定します。
_int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
d[1] += 1;
return r;
}
_
問題は、コンパイラ(この場合はgcc
)がメモリアクセスを再配置して、要求するとパフォーマンスを向上できることです(_-O
_)。以下のような一連の指示につながる可能性があります。
_00000000 <c>:
0: 4603 mov r3, r0
2: c805 ldmia r0, {r0, r2}
4: 3001 adds r0, #1
6: 3201 adds r2, #1
8: 6018 str r0, [r3, #0]
a: 6808 ldr r0, [r1, #0]
c: 605a str r2, [r3, #4]
e: 4770 bx lr
_
_d[0]
_と_d[1]
_の上記の値は同時にロードされます。これを回避したい場合は、メモリアクセスの順序を変更しないようコンパイラーに指示する必要があります。つまり、asm volatile("" ::: "memory")
を使用します。
_int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
asm volatile("" ::: "memory");
d[1] += 1;
return r;
}
_
したがって、命令シーケンスを次のように取得します。
_00000000 <c>:
0: 6802 ldr r2, [r0, #0]
2: 4603 mov r3, r0
4: 3201 adds r2, #1
6: 6002 str r2, [r0, #0]
8: 6808 ldr r0, [r1, #0]
a: 685a ldr r2, [r3, #4]
c: 3201 adds r2, #1
e: 605a str r2, [r3, #4]
10: 4770 bx lr
12: bf00 nop
_
メモリをフラッシュしたり、ロードまたはストアが完了するのを待つための追加のハードウェアレベルの命令を配置しないため、コンパイラはメモリアクセスの順序を変更することを避けるため、これはコンパイル時のメモリバリアにすぎないことに注意してください。 CPUは、アーキテクチャ機能があり、メモリアドレスが_strongly ordered
_またはnormal
( ref )ではなくdevice
タイプである場合、メモリアクセスを並べ替えることができます。
このシーケンスは、Udoが参照している記事に記載されているように、コンパイラメモリアクセスのスケジューリングの障壁です。これはGCC固有のものです-他のコンパイラーはそれらを記述する他の方法があり、それらのいくつかはより明確な(そしてより難解な)ステートメントで記述されています。
__asm__
は、アセンブリ言語ステートメントをCコード内にネストして入力できるようにするためのgcc拡張です。ここでは、コンパイラが特定のタイプの最適化を実行できないようにする副作用を指定できるという特性のために使用します不正なコードを生成します)。
__volatile__
は、asmステートメント自体が他の揮発性アクセスで再配列されないようにするために必要です(C言語での保証)。
memory
はGCCに対する命令で、インラインasmシーケンスはグローバルメモリに副作用があるため、ローカル変数への影響だけを考慮する必要はありません。
意味は次のとおりです。
http://en.wikipedia.org/wiki/Memory_ordering
基本的には、アセンブリコードが期待どおりに実行されることを意味します。コンパイラーに命令を並べ替えないように指示します。これは、このコードが実行される前にコーディングされ、実行後にコーディングされるものです。