我使用以下代码实现了多精度加法:

    bool carry{};
    std::array<uint64_t, N> r{};

    for (auto i = 0; i < N; ++i) {
       uint64_t aa = a[i];
       __uint128_t res = static_cast<__uint128_t>(aa) + b[i] + carry;
       carry = res >> 64;
       r[i] = res;
    }


clang ++ 6.0产生了以下程序集:

400a49: 4c 01 c1                add    %r8,%rcx
400a4c: 66 49 0f 38 f6 c1       adcx   %r9,%rax
400a52: 66 49 0f 38 f6 f2       adcx   %r10,%rsi
400a58: 66 48 0f 38 f6 d7       adcx   %rdi,%rdx


谁能解释为什么clang选择使用adcx而不是adc?
据我所知,boto具有相同的执行时间,但adc的编码为3个字节,而adcx的编码为6个字节。

更新:我玩了更多,而且似乎行为是随机的。
如果将args作为const引用传递,则得到adcx
https://godbolt.org/g/noFZNS
如果我按值传递,我会得到adc:

https://godbolt.org/g/RkBWhV

如果代码不在函数内,而只是在main内联,则完全是一团糟。

最佳答案

对我来说,这似乎是一个错过的优化。我认为adc是更好的选择。在Skylake上,根据一些快速的吞吐量测试(xor eax,eax / times 4 adcx eax,edx循环),它们具有相同的性能特征。 Agner Fog奇怪地没有在他的指令表(http://agner.org/optimize/)中列出adox / adcx,在SKL ADC / ADCX / ADOX上,p0 / p6均为1 uop,延迟为1c。

如果有的话,写所有标志而不是仅写CF不太可能导致性能问题。

您应该在https://bugs.llvm.org/buglist.cgi上报告此情况。

在有两个并行的dep链时,除非clang知道如何与ADOX真正交织,否则在ADCX上花费额外的代码大小毫无意义。

我可以想象一个罕见的情况,保留其他标志很有用,而最近的Intel CPU似乎在处理部分标志方面非常有效,甚至不需要合并uop。但这是非常利基的,而不是这里发生的事情(add掩盖了所有标志)。

08-15 22:10