繁体   English   中英

如何理解PowerPC stwbrx的这个GNU C内联汇编宏

[英]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。 升级您的编译器版本或使用链接时优化可能会使您的程序错误,没有源级别的更改。

这种事情是为什么https://gcc.gnu.org/wiki/DontUseInlineAsm

另请参见https://stackoverflow.com/tags/inline-assembly/info

#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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM