gcc - 尝试使用具有多个替代约束的内联汇编在 amd64 中实现 128 位添加

标签 gcc assembly x86-64

为了在 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/

相关文章:

C FILE* 错误

c - 为什么 Linux VmSize VmData 比代码估计的要大?

linux - 在 ASLR 系统上运行非位置无关的可执行文件

assembly - 为什么DOS在加载.COM文件后将SP寄存器设置为0xFFFE?

assembly - 游戏男孩 : Half-carry flag and 16-bit instructions (especially opcode 0xE8)

assembly - x86 程序集 : movsd instruction issue

c - 如何计算我的 frameCount 函数中的堆栈帧数?

c++ - 崩溃为 __cxxabiv1::__cxa_pure_virtual () - vtable ptr 到抽象基类?

c - 关于x86-64汇编语言中的局部变量寄存器的问题

c++ - 如何分配完整的内存页