[英]How to understand this GNU C inline assembly macro for PowerPC stwbrx
這基本上是在傳輸消息緩沖區時執行緩沖區的交換。 這句話讓我感到困惑(因為我不熟悉c中的嵌入式匯編代碼)。 這是一個power pc指令
#define ASMSWAP32(dest_addr,data) __asm__ volatile ("stwbrx %0, 0, %1" : : "r" (data), "r" (dest_addr))
除了因為一個bug而不安全之外,這個宏的效率也低於編譯器為你生成的效率。
stwbrx
= 存儲字字節反轉 。 x
代表索引。
在GNU C中你不需要內聯asm,你可以使用__builtin_bswap32
讓編譯器為你發出這個指令。
void swapstore_asm(int a, int *p) {
ASMSWAP32(p, a);
}
void swapstore_c(int a, int *p) {
*p = __builtin_bswap32(a);
}
使用gcc4.8.5 -O3 -mregnames
編譯,我們從兩個函數(Godbolt編譯器資源管理器)獲得相同的代碼:
swapstore:
stwbrx %r3, 0, %r4
blr
swapstore_c:
stwbrx %r3,0,%r4
blr
但是對於更復雜的地址(存儲到p[off]
,其中off
是整數函數arg),編譯器知道如何使用兩個寄存器輸入,而宏強制編譯器將地址放在一個寄存器中:
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輸出可能是查看替換內容的有用方法。請參閱如何從GCC / clang程序集輸出中刪除“noise”? 有關讀取編譯器asm輸出的更多信息。
還要注意這個宏是錯誤的:它缺少商店的"memory"
破壞 。 是的,你仍然需要asm volatile
。 編譯器不會假設*dest_addr
被修改,除非你告訴它,所以它可以在此insn之前提升*dest_addr
的非易失性加載,或者更可能是真正的問題,在它之后接收存儲。 (例如,如果在使用此存儲器將存儲器歸零之前,編譯器可能在此指令之后實際為零。)
您可以告訴編譯器您使用=m" (*dest_addr)
操作數修改哪個內存位置,或者作為偽操作數或者在尋址模式上使用約束,而不是"memory"
clobber(並且也省略volatile
)您可以將它用作reg+reg
。(IDK PPC足以知道"=m"
通常會擴展到什么。)
在大多數情況下,這個bug不會咬你,但它仍然是一個bug。 升級您的編譯器版本或使用鏈接時優化可能會使您的程序錯誤,沒有源級別的更改。
#define ASMSWAP32(dest_addr,data)
......
這部分應該清楚
__asm__ volatile (
...: : "r" (data), "r" (dest_addr))
這是實際的內聯匯編:
兩個值傳遞給匯編代碼; 匯編代碼中沒有返回任何值(這是實際匯編代碼之后的冒號)。
兩個參數都在寄存器( "r"
)中傳遞。 表達式%0
將被包含data
值的寄存器替換,而表達式%1
將被包含dest_addr
值的寄存器替換(在這種情況下將是指針)。
這里的volatile
意味着匯編代碼必須在此時執行,不能移動到其他地方。
因此,如果您在C源代碼中使用以下代碼:
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
指令的值b
和最后一個參數是地址a
。
stwbrx x, 0, y
該指令將寄存器x
的值寫入寄存器y
存儲的地址; 但是它將值寫入“反向端”(在大端CPU上它寫入值“little endian”)。
以下代碼:
uint32 a;
ASMSWAP32(&a, 0x12345678);
...因此應該導致a = 0x78563412
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.