これは、基本的に、メッセージバッファを転送中にバッファのスワップを実行することです。このステートメントは私に困惑していました(私の組み込みアセンブリコードが不慣れであるため)。これは電源PCの指示ですPowerPC用のGNU Cインラインアセンブリマクロの理解stwbrx
#define ASMSWAP32(dest_addr,data) __asm__ volatile ("stwbrx %0, 0, %1" : : "r" (data), "r" (dest_addr))
これは、基本的に、メッセージバッファを転送中にバッファのスワップを実行することです。このステートメントは私に困惑していました(私の組み込みアセンブリコードが不慣れであるため)。これは電源PCの指示ですPowerPC用のGNU Cインラインアセンブリマクロの理解stwbrx
#define ASMSWAP32(dest_addr,data) __asm__ volatile ("stwbrx %0, 0, %1" : : "r" (data), "r" (dest_addr))
#define ASMSWAP32(dest_addr,data)
...
この部分は透明であるべきである
__asm__ volatile (
...: : "r" (data), "r" (dest_addr))
これは、実際のインラインアセンブリである:
二つの値がassmblyコードに渡されます。アセンブリコードから値が返されません(これは実際のアセンブリコードの後のコロンです)。
両方のパラメータがレジスタに渡されます("r"
)。式%0
は、data
の値を含むレジスタに置き換えられ、式%1
は、dest_addr
の値(この場合はポインタ)を含むレジスタに置き換えられます。
ここでvolatile
は、この時点でアセンブリコードを実行しなければならず、別の場所に移動できないことを意味します。
ASMSWAP(&a, b);
...次のアセンブラコードが生成されます:
# write the address of a to register 5 (for example)
...
# write the value of b to register 6
...
stwbrx 6, 0, 5
のでstwbrx
命令の最初の引数がありますが、Cソースに次のコードを使用するのであれば
b
の値で、最後の引数はa
のアドレスです。
stwbrx x, 0, y
この命令は、レジスタy
に格納されたアドレスをレジスタx
に値を書き込みます。 ...
uint32 a;
ASMSWAP32(&a, 0x12345678);
のでa = 0x78563412
につながるはずです。しかし、それは、次のコード
ビッグエンディアンのCPU上では「リトルエンディアン」の値を書き込む(「逆エンディアン」に値を書き込みます。
'volatile'はあなたがそれを響かせるほど強力ではありません。 ''メモリ ''クラバーがなければ、他のコードで並べ替えを完全に防ぐことはできません。これは、asm文に副作用があり、コンパイラがその出力を生成したいときだけでなく、ソースが言うように何度も実行しなければならないことを意味します。 –
"0"( '%0'ではなく)がPPCゼロレジスタであることを指摘しているので、アドレスに追加するとアドレスが生成されます。 –
このマクロは、バグのために安全ではありませんが、コンパイラが生成するものよりも効率が悪いです。
stwbrx
= store word byte-reversed。 x
はインデックスが付けられています。
__builtin_bswap32
を使用してこの命令を発行することができるGNU Cでは、このためにインラインasmは必要ありません。 gcc4.8.5 -O3 -mregnames
でコンパイル
void swapstore_asm(int a, int *p) {
ASMSWAP32(p, a);
}
void swapstore_c(int a, int *p) {
*p = __builtin_bswap32(a);
}
、我々は両方の機能(Godbolt compiler explorer)から、同一のコードを取得する:
swapstore:
stwbrx %r3, 0, %r4
blr
swapstore_c:
stwbrx %r3,0,%r4
blr
しかし、(off
が整数関数引数であるp[off]
に格納し、)より複雑なアドレスを使用して、コンパイラは両方のレジスタ入力を使用する方法を知っていますが、マクロはコンパイラにアドレスを1つのレジスタに格納するよう強制します。
void swapstore_offset(int a, int *p, int off) {
= __builtin_bswap32(a);
}
swapstore_offset:
slwi %r5,%r5,2 # *4 = sizeof(int)
stwbrx %r3,%r4,%r5 # use an indexed addressing mode, with both registers non-zero
blr
swapstore_offset_asm:
slwi %r5,%r5,2
add %r4,%r4,%r5 # extra instruction forced by using the macro
stwbrx %r3, 0, %r4
blr
ところで、GNU Cインラインasmテンプレートの理解に問題がある場合、コンパイラのasm出力を見ると、置換されるものを見るのに便利な方法です。コンパイラasm出力の読み方については、How to remove "noise" from GCC/clang assembly output?を参照してください。
はまた、このマクロはバグがあることに注意してください:それは店ため"memory"
クロバーが欠落しています。そして、はい、それでもasm volatile
でそれが必要です。コンパイラは、あなたが指示しない限り、*dest_addr
が変更されているとは仮定しないので、このinsnより先に*dest_addr
の不揮発性ロードを持ち上げることができます。代わりに"memory"
クロバー(ともvolatile
を除外)の(例えば、あなたがこの、コンパイラこの命令の後かもしれない実際にゼロでそれを保存する前にバッファをゼロ場合。)
することは、あなたはどのコンパイラを言うことができますのメモリ位置を=m" (*dest_addr)
オペランドでダミーオペランドとして指定するか、またはアドレッシングモードの制約でreg+reg
として使用することができます。 (IDK PPCは、通常"=m"
の展開先を知るのに十分です)
ほとんどの場合、このバグはあなたを噛んではいませんが、まだバグです。コンパイラのバージョンをアップグレードしたり、リンク時の最適化を使用すると、ソースレベルの変更を加えずにバグが発生する可能性があります。 https://gcc.gnu.org/wiki/DontUseInlineAsm
もhttps://stackoverflow.com/tags/inline-assembly/info参照してください理由
この種のことはあります。
本当に優れた答えです。 –
コンパイラとアーキテクチャを組み込むようにタグを変更してください。「埋め込み」と「揮発性」は適切ではないため、置き換えることができます。 –
https://www.ibm.com/support/knowledgecenter/en /ssw_aix_61/com.ibm.aix.alangref/idalangref_stwbrx_stbrx.htmこれは、命令について知る必要があることをすべて伝えます –
チェックアウト[インine assembly](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) –