2017-11-08 25 views
4

これは、基本的に、メッセージバッファを転送中にバッファのスワップを実行することです。このステートメントは私に困惑していました(私の組み込みアセンブリコードが不慣れであるため)。これは電源PCの指示ですPowerPC用のGNU Cインラインアセンブリマクロの理解stwbrx

#define ASMSWAP32(dest_addr,data) __asm__ volatile ("stwbrx %0, 0, %1" : : "r" (data), "r" (dest_addr)) 
+1

コンパイラとアーキテクチャを組み込むようにタグを変更してください。「埋め込み」と「揮発性」は適切ではないため、置き換えることができます。 –

+0

https://www.ibm.com/support/knowledgecenter/en /ssw_aix_61/com.ibm.aix.alangref/idalangref_stwbrx_stbrx.htmこれは、命令について知る必要があることをすべて伝えます –

+1

チェックアウト[インine assembly](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) –

答えて

5

#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上では「リトルエンディアン」の値を書き込む(「逆エンディアン」に値を書き込みます。

+0

'volatile'はあなたがそれを響かせるほど強力ではありません。 ''メモリ ''クラバーがなければ、他のコードで並べ替えを完全に防ぐことはできません。これは、asm文に副作用があり、コンパイラがその出力を生成したいときだけでなく、ソースが言うように何度も実行しなければならないことを意味します。 –

+1

"0"( '%0'ではなく)がPPCゼロレジスタであることを指摘しているので、アドレスに追加するとアドレスが生成されます。 –

7

このマクロは、バグのために安全ではありませんが、コンパイラが生成するものよりも効率が悪いです。


stwbrx = store word byte-reversedxはインデックスが付けられています。

__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参照してください理由

この種のことはあります。

+0

本当に優れた答えです。 –