[英]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.