让编译器生成 adc 指令

Posted

技术标签:

【中文标题】让编译器生成 adc 指令【英文标题】:Getting a compiler to generate adc instruction 【发布时间】:2013-06-28 18:45:05 【问题描述】:

有什么方法可以让 Clang、GCC 或 VS 仅使用 Standard-C++(98/11/14) 生成 adc(带进位相加)指令? (编辑:我的意思是在 x64 模式下,如果不清楚,请见谅。)

【问题讨论】:

那些是c 编译器 @Taylor:“GCC”和“Visual Studio”是编译器集合的名称,其中包含 C++ 编译器等。 clang 是一个 C++ 编译器。 您有什么特别的理由要这样做吗? C 或 C++ 的全部意义在于将程序员与特定于平台的程序集隔离开来。 C 或 C++ 标准从未提及 ADC(这是一个实现细节),因此答案是。无法保证特定处理器提供 ADC 指令... @OliCharlesworth 是的,我想在 ISO-C++ 中实现高效的任意精度算术,但我认为目前不可能。 【参考方案1】:

如果您的代码进行比较并将比较结果添加到某物,则 gcc 5 通常会发出 adc(顺便说一下,gcc 4.8 不会在此处发出 adc)。例如,

unsigned foo(unsigned a, unsigned b, unsigned c, unsigned d)

    return (a + b + (c < d));

组装到

foo:
    cmpl    %ecx, %edx
    movl    %edi, %eax
    adcl    %esi, %eax
    ret

但是,让 gcc 真正发出 adc 有点棘手。

【讨论】:

是的,对于像上面这样的条件增量,很难让 gcc 发出 adc。我认为 gcc 有一个窥视孔或其他阶段,它采用adc0 立即并变成(在现代硬件上通常更糟)xor eax, eax; setb al; add ..., eax 序列。例如,如果你从上面删除b,所以它是(a + (c &lt; d)),自然顺序是使用adc0,但gcc 做了奇怪的setb 事情。对于像a += (c &lt; d) ? 1 : 0 这样的三元表达式也是如此。但是,如果您使用a += (c &lt; d) ? 3 : 2 或任何其他非一值,它确实 使用adc eax, 2 所以似乎只有当0 会立即出现这种奇怪现象。此外,如果您使用分支,例如if (c &lt; d) a++,它使用adc!我试过many examples on godbolt。【参考方案2】:

在 GCC 上有一个 __int128_t 类型可用于 amd64 和其他 64 位目标,它将使用一对 add/adc 指令进行简单的添加。 (请参阅下面的 Godbolt 链接)。

此外,这个纯 ISO C 代码可以编译为 adc:

uint64_t adc(uint64_t a, uint64_t b)

    a += b;
    if (a < b) /* should simplify to nothing (setting carry is implicit in the add) */
        a++; /* should simplify to adc r0, 0 */
    return a;

对我 (ARM) 来说,它产生了一些愚蠢的东西,但它针对 x86-64(在 Godbolt compiler explorer 上)编译为:

    mov     rax, rdi  # a, a
    add     rax, rsi  # a, b
    adc     rax, 0    # a,
    ret

【讨论】:

我要补充一点,我认为-funsafe-math-optimizations 可能会杀死整个if 块,但似乎没有。不过,我认为 C 可能允许这样的优化,所以请注意。 无符号环绕在 C/C++ 中被明确定义为二进制整数。它保证正常工作。 -funsafe-math-optimizations 只影响浮点行为。也许您正在考虑有符号整数环绕是未定义的行为? -fwrapv 使其定义明确(作为 2 的补码),但默认情况下启用。【参考方案3】:

如果您为 X86(C++ 11 中的 int64_t)编译 64 位有符号加法,编译后的代码将包含 adc 指令。

编辑:代码示例:

int64_t add_numbers(int64_t x, int64_t y) 
    return x + y;

在 X86 上,加法是使用 add 指令后跟 adc 指令来实现的。在 X64 上,只使用了一条 add 指令。

【讨论】:

你能给出一个最小的示例函数吗?我从来没有在我的任何二进制文件中看到过它,只有 lea(q) 和类似的。 请看我的编辑,我之前应该包含这个。这里的背景是看看是否可以在 Standard-C++ 中构建高效的任意整数运算。 :)

以上是关于让编译器生成 adc 指令的主要内容,如果未能解决你的问题,请参考以下文章

linux内核编译,怎么样 从o文件直接生成ko文

让编译器以合理的方式自动矢量化代码

一行机器指令感受下内存操作到底有多慢

装配ADC(随附进位)到C ++

生成视图重新编译的指令

JSP编译指令--------page编译指令