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

时间:2017-11-08 08:12:35

标签: c assembly embedded endianness powerpc

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

public function all_village_info($id = NULL) {
    $this->db->select('tbl_village.*', FALSE);
    $this->db->select('tbl_school.*', FALSE);
    $this->db->from('tbl_village');
    $this->db->join('tbl_school', 'tbl_school.school_id=tbl_village.school_id', 'left');
    if (!empty($id)) {
        $this->db->where('tbl_village.village_id', $id);
        $query_result = $this->db->get();
        $result = $query_result->row();
    } else {
        $query_result = $this->db->get();
        $result = $query_result->result();
    }
    return $result;
}

2 个答案:

答案 0 :(得分:7)

除了因为错误而不安全之外,这个宏的效率也低于编译器为你生成的效率。

stwbrx = store word byte-reversedx代表索引。

你不需要在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 compiler explorer)获得相同的代码:

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输出可能是查看替换内容的有用方法。请参阅How to remove "noise" from GCC/clang assembly output?了解更多信息关于读取编译器asm输出。

另请注意,此宏存在错误:它缺少商店的"memory" clobber 。是的,您仍然需要使用asm volatile。除非你告诉它,否则编译器不会假设*dest_addr被修改,所以它可以在此insn之前提升*dest_addr的非易失性负载,或者更可能是一个真正的问题,在它之后沉没一家商店。 (例如,如果在将缓冲区存储到其之前将其归零,则编译器可能会在此指令之后实际为零。)

您可以告诉编译器使用"memory"操作数修改哪个内存位置,而不是volatile clobber(并且还省略=m" (*dest_addr)),要么作为虚拟操作数,要么对寻址模式有约束,因此可以将其用作reg+reg。 (IDK PPC足以知道"=m"通常扩展到的内容。)

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

这就是https://gcc.gnu.org/wiki/DontUseInlineAsm

的原因

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

答案 1 :(得分:5)

  

__asm__ volatile ( ...

这部分应该清楚

  

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

这是实际的内联汇编:

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

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

这里的ASMSWAP(&a, b); 意味着汇编代码必须在此时执行,不能移动到其他地方。

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

# 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中的值写入寄存器uint32 a; ASMSWAP32(&a, 0x12345678); 中存储的地址;但它将值写入“反向字节序”(在大端CPU上,它会写入值“little endian”。

以下代码:

a = 0x78563412

...因此应该导致location ~ /(api|browser|_logic|ping|_authtokens) { proxy_pass http://docker-restheart; }