我使用以下代码实现了多精度加法:
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
掩盖了所有标志)。