何时在扩展GCC内联汇编中使用earlyclobber约束?

Vil*_*ray 9 c assembly gcc inline-assembly

我知道何时使用补鞋匠列表(例如列出在程序集中修改的寄存器,以便它不被选择用作输入寄存器等),但我无法绕过早期约束条件&.如果列出输出,那是否已经意味着输入不能使用所选寄存器(除了匹配数字约束)?

例如:

asm(
    "movl $1, %0;"
    "addl $3, %0;"
    "addl $4, %1;"
    "addl %1, %0;"
    : "=g"(num_out)
    : "g"(num_in)
    :
);
Run Code Online (Sandbox Code Playgroud)

&甚至需要为输出变量?编译器应该知道为输出选择的寄存器,因此知道不要将它用于输入.

R..*_*R.. 14

默认情况下,编译器假定在写入任何输出寄存器之前将消耗所有输入,因此允许它们使用相同的寄存器.这样可以在可能的情况下获得更好的代码,但如果假设是错误的,事情将会灾难性地失败."早期clobber"标记是告诉编译器在所有输入被消耗之前将写入此输出的一种方式,因此它不能与任何输入共享寄存器.

  • 您的代码错误有多种原因.例如,您正在修改输入寄存器. (2认同)

Cir*_*四事件 5

最少的教育例子

在这里,我提供了一个最小的教育示例,试图使/sf/answers/1107395901/提到的内容更加清楚。

该代码在实践中当然没有用,并且可以通过一条lea 1(%q[in]), %out指令更有效地实现。

main.c

#include <assert.h>
#include <inttypes.h>

int main(void) {
    uint64_t in = 1;
    uint64_t out;
    __asm__ (
        "mov %[in], %[out];" /* out = in */
        "inc %[out];"        /* out++ */
        "mov %[in], %[out];" /* out = in */
        "inc %[out];"        /* out++ */
        : [out] "=&r" (out)
        : [in] "r" (in)
        :
    );
    assert(out == 2);
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

gcc -ggdb3 -std=c99 -O3 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Run Code Online (Sandbox Code Playgroud)

该程序是正确的,并且断言通过,因为&强制编译器为in和选择不同的寄存器out

这是因为&告诉编译器inout写入之后可能会使用的情况,在这里实际上就是这种情况。

因此,唯一避免错误修改的方法in是将inout放入不同的寄存器中。

拆卸:

gdb -nh -batch -ex 'disassemble/rs main' main.out
Run Code Online (Sandbox Code Playgroud)

包含:

   0x0000000000001055 <+5>:     48 89 d0        mov    %rdx,%rax
   0x0000000000001058 <+8>:     48 ff c0        inc    %rax
   0x000000000000105b <+11>:    48 89 d0        mov    %rdx,%rax
   0x000000000000105e <+14>:    48 ff c0        inc    %rax
Run Code Online (Sandbox Code Playgroud)

这表明GCC raxout和选择rdxin

如果我们删除了&,则行为是不确定的。

在我的测试系统中,断言实际上失败了,因为编译器试图将寄存器的使用减至最少,并编译为:

   0x0000000000001055 <+5>:     48 89 c0        mov    %rax,%rax
   0x0000000000001058 <+8>:     48 ff c0        inc    %rax
   0x000000000000105b <+11>:    48 89 c0        mov    %rax,%rax
   0x000000000000105e <+14>:    48 ff c0        inc    %rax
Run Code Online (Sandbox Code Playgroud)

因此rax用于inout

其结果out是增加了两次,并且等于3而不是2最后。

在Ubuntu 18.10 amd64和GCC 8.2.0中进行了测试。

更实际的例子

  • @CiroSantilli新疆改造中心六四事件法轮功`=&amp;r`的另一种用法,似乎没有人明确提到过,它具有临时寄存器变量,可以通过有意义的名称引用,例如`%[running_sum]`而不是硬-编码`%r8`。 (2认同)