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

use*_*392 4 c embedded assembly powerpc endianness

这基本上是在传输消息缓冲区时执行缓冲区的交换.这句话让我感到困惑(因为我不熟悉c中的嵌入式汇编代码).这是一个power pc指令

#define ASMSWAP32(dest_addr,data) __asm__ volatile ("stwbrx %0, 0, %1" : : "r" (data), "r" (dest_addr))
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 7

除了因为一个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);
}
Run Code Online (Sandbox Code Playgroud)

使用gcc4.8.5编译-O3 -mregnames,我们从两个函数(Godbolt编译器资源管理器)获得相同的代码 :

swapstore:
    stwbrx %r3, 0, %r4
    blr
swapstore_c:
    stwbrx %r3,0,%r4
    blr
Run Code Online (Sandbox Code Playgroud)

但是对于更复杂的地址(存储到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
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果您在理解GNU C内联asm模板时遇到问题,查看编译器的asm输出可能是查看替换内容的有用方法.请参阅如何从GCC/clang程序集输出中删除"noise"?有关读取编译器asm输出的更多信息.


还要注意,这个宏是错误的:它缺少"memory"商店的破坏.是的,你还需要它asm volatile.*dest_addr除非你告诉它,否则编译器不会假设它被修改,所以它可以*dest_addr在此insn之前提升非易失性负载,或者更可能是一个真正的问题,在它之后接收存储.(例如,如果在使用此存储器将存储器归零之前,编译器可能此指令之后实际为零.)

您可以告诉编译器您使用操作数修改哪个内存位置,或者使用操作数作为伪操作数,或者在寻址模式下使用约束,而不是使用"memory"clobber(也可以省略volatile),这样您就可以使用它.(IDK PPC足以知道通常会扩展到什么.)=m" (*dest_addr)reg+reg"=m"

在大多数情况下,这个bug不会咬你,但它仍然是一个bug.升级您的编译器版本或使用链接时优化可能会使您的程序错误,没有源级别的更改.

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

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


Mar*_*nau 5

#define ASMSWAP32(dest_addr,data) ...

这部分应该清楚

__asm__ volatile ( ... : : "r" (data), "r" (dest_addr))

这是实际的内联汇编:

两个值传递给汇编代码; 汇编代码中没有返回任何值(这是实际汇编代码之后的冒号).

两个参数都在寄存器("r")中传递.该表达式%0将被包含值的寄存器替换,data而表达式%1将被包含值的寄存器替换dest_addr(在这种情况下将是指针).

volatile意味着汇编代码必须在此时执行,不能移动到其他地方.

因此,如果您在C源代码中使用以下代码:

ASMSWAP(&a, b);
Run Code Online (Sandbox Code Playgroud)

...将生成以下汇编代码:

# write the address of a to register 5 (for example)
...
# write the value of b to register 6
...
stwbrx 6, 0, 5
Run Code Online (Sandbox Code Playgroud)

所以stwbrx指令的第一个参数是值,b最后一个参数是地址a.

stwbrx x, 0, y

该指令将寄存器中的值写入寄存器x中存储的地址y; 但是它将值写入"反向端"(在大端CPU上它写入值"little endian").

以下代码:

uint32 a;
ASMSWAP32(&a, 0x12345678);
Run Code Online (Sandbox Code Playgroud)

......因此应该导致a = 0x78563412.