C语言 海湾合作委员会x86_64 asm 中的寄存器分配冲突.生命周期被忽略



我有一个包含小程序集块的函数。GCC 编译的此函数的程序集不正确,因为它将相同的寄存器分配给两个不同的变量。这是函数源:

void *ptr;
uint64_t foo(uint64_t arg1) {
register uint64_t tmp;
asm ("lea TARGET(%%rip), %[tmp];"
"shl  $4, %[arg1];"
"sub  %[arg1], %[tmp];"
"movq %[tmp], %[ptr];"
: [ptr] "+m" (ptr), [tmp] "=r" (tmp)
: [arg1] "r" (arg1)
:);
asm ("TARGET:;");
}

我使用约束"+m"作为全局ptr,因为我写入它,它应该在内存中。tmp的约束是"=r",因为它只被写入。输入arg1的约束只是"r"。这可能很重要。

程序集块的 SSA 伪代码为:

tmp_0 = something
arg1_1 = arg1_0 << 4
tmp_1 = tmp_0 - arg_1
*ptr_0 = tmp_1

此函数在优化O0的编译程序集是

00000000000005fa <foo>:
5fa:   55                      push   %rbp
5fb:   48 89 e5                mov    %rsp,%rbp
5fe:   48 89 7d f8             mov    %rdi,-0x8(%rbp)       # Storing arg1 to stack (optimization level O0)
602:   48 8b 45 f8             mov    -0x8(%rbp),%rax       # arg1_0 is assigned register rax
606:   48 8d 05 0e 00 00 00    lea    0xe(%rip),%rax        # 61b <TARGET>, tmp_0 is ALSO assigned rax
60d:   48 c1 e0 04             shl    $0x4,%rax             # arg1_0 is used, its lifetime ends
611:   48 29 c0                sub    %rax,%rax             # subtracting two vars both assigned the same register
614:   48 89 05 fd 09 20 00    mov    %rax,0x2009fd(%rip)   # 201018 <ptr>, store to global
000000000000061b <TARGET>:
61b:   90                      nop
61c:   5d                      pop    %rbp
61d:   c3                      retq 

检查程序集,我们看到在地址0x602中,寄存器rax被分配给SSA寄存器,arg1_0其生存期一直持续到指令0x60d为止。同时,地址0x606的指令还将寄存器rax分配给SSA寄存器tmp_0。似乎一个物理寄存器在其生命周期内被分配给另一个 SSA 寄存器。

系统信息:

  • x86_64 中央处理器
  • GCC 版本 7.4.0
  • 乌班图 18.04.3

我的问题:

  • 为什么 GCC 会创建这样的程序集输出?
  • 我对程序集输入和输出的约束是否正确?如果没有,为什么?
  • 这是海湾合作委员会的错误吗?

首先,感谢@Jester回答这个问题的完美评论。引用:

手册说:"在所有不得与输入重叠的输出操作数上使用'&'约束修饰符(参见修饰符(。否则,GCC 可能会将输出操作数与不相关的输入操作数在同一寄存器中分配,前提是汇编代码在生成输出之前消耗其输入。如果汇编程序代码实际上由多个指令组成,则此假设可能是错误的。">

简而言之,我的内联程序集不同意gcc假设,即在写入输出之前消耗输入。事实上,gcc提供了适当的,虽然有些晦涩的约束修饰符(&(来表达这一点。

修复:

-       : [ptr] "+m" (ptr), [tmp] "=r" (tmp)
+       : [ptr] "+m" (ptr), [tmp] "=&r" (tmp)

固定组件输出:

00000000000005fa <foo>:
5fa:   55                      push   %rbp
5fb:   48 89 e5                mov    %rsp,%rbp
5fe:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
602:   48 8b 45 f8             mov    -0x8(%rbp),%rax
606:   48 8d 15 0e 00 00 00    lea    0xe(%rip),%rdx        # 61b <TARGET>
60d:   48 c1 e0 04             shl    $0x4,%rax
611:   48 29 c2                sub    %rax,%rdx
614:   48 89 15 fd 09 20 00    mov    %rdx,0x2009fd(%rip)        # 201018 <ptr>
000000000000061b <TARGET>:
61b:   90                      nop
61c:   5d                      pop    %rbp
61d:   c3                      retq   

最新更新