我有一个包含一个小的汇编块的函数。由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:;");
}
因为我写入全局ptr
,所以对它使用约束“ + m”,它应该在内存中。因为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 ALSO的指令还将寄存器rax
分配给SSA寄存器tmp_0
。似乎在其生存期内,物理寄存器已分配给另一个SSA寄存器。
系统信息:
我的问题:
首先,感谢@Jester的完美回答,回答了这个问题。引用:
manual说:“在不能与输入重叠的所有输出操作数上使用'&'约束修饰符(请参见修饰符)。否则,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