为了在 amd64 上的 GCC 中获得可用的 128 位操作,我实现了一些内联函数。像 add_128_128_128。我想让编译器决定使用哪些寄存器作为输入和输出以获得最大的灵活性。所以,我使用了多个替代约束。
inline __uint128_t add_128_128_128(__uint128_t a, __uint128_t b) {
uint64_t a_hi = a >> 64;
uint64_t a_lo = a;
uint64_t b_hi = b >> 64;
uint64_t b_lo = b;
uint64_t retval_hi;
uint64_t retval_lo;
asm (
"\n"
" add %2, %0\n"
" adc %3, %1\n"
: "=r,r,r,r" (retval_lo)
, "=r,r,r,r" (retval_hi)
: "r,0,r,0" (a_lo)
, "0,r,0,r" (b_lo)
, "r,1,1,r" (a_hi)
, "1,r,r,1" (b_hi)
);
return ((__uint128_t)retval_hi) << 64 | retval_lo;
}
现在,生成的汇编器输出是:
_Z11add_128_128oo:
movq %rdx, %rax
movq %rcx, %rdx
add %rdi, %rax
adc %rax, %rdx
ret
令我困惑的是如何修复 adc 指令。考虑到这一点,我得出了一个临时结论,即使匹配的约束也会得到"new"数字,这可以解释 %rax 为 %3 == %0 == %rax。那么,有没有办法告诉 GCC 只计算“r”约束? (我知道我可以通过放弃多个替代约束来使这个内联程序集工作。)
顺便说一句:有没有关于 GCC 内联汇编的有用文档?当涉及到有趣的东西时,带有零示例的官方手册在这种情况下我认为没有什么用处。用谷歌搜索并没有让我找到任何。所有的howtos和东西都只是谈论琐碎的基本事情,但完全省略了更高级的东西,比如多个替代约束。
最佳答案
首先想到的是:
inline __uint128_t add_128_128_128(__uint128_t a, __uint128_t b) {
asm("add %1, %%rax\n\t"
"adc %2, %%rdx"
: "+A"(a)
: "r"((uint64_t)(b >> 64)), "r"((uint64_t)b)
: "cc");
return a;
}
那是因为GCC可以治疗
RDX:RAX
作为双大小寄存器对与 "A"
约束。这是次优的,但特别是对于内联,因为它没有考虑到两个操作数是可互换的,并且总是在 RDX
中返回。 :RAX
它还限制了寄存器的选择。要获得这种交换性,您可以使用
%
约束修饰符:inline __uint128_t add_128_128_128(__uint128_t a, __uint128_t b) {
uint64_t a_lo = a, a_hi = a >> 64, b_lo = b, b_hi = b >> 64;
uint64_t r_lo, r_hi;
asm("add %3, %0\n\t"
"adc %5, %1"
: "=r"(r_lo), "=r"(r_hi)
: "%0" (a_lo), "r"(b_lo), "%1"(a_hi), "r"(b_hi) :
: "cc");
return ((__uint128_t)r_hi) << 64 | r_lo;
}
%
向 GCC 指示此操作数和下一个操作数是可互换的。这将创建以下代码(非内联):
.text 节的反汇编:
0000000000000000
0: 48 89 f8 mov %rdi,%rax
3: 48 01 d0 添加 %rdx,%rax
6: 48 11 ce adc %rcx,%rsi
9: 48 89 f2 mov %rsi,%rdx
c: c3 retq
这看起来很像你想要的?
关于gcc - 尝试使用具有多个替代约束的内联汇编在 amd64 中实现 128 位添加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18887882/