我使用以下代码实现了多精度添加:
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:
如果代码不在函数内部,只是在main中内联,它就是一团糟。
答案 0 :(得分:2)
这看起来像是对我的错过优化。我认为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上花费额外的代码大小是没有意义的。
我可以想象一个罕见的情况,其中保留其他标志是有用的,并且最近的英特尔CPU似乎在部分标记的东西非常有效,甚至不需要合并的uop。但这是非常适合的,而不是这里发生的事情(add
所有标志都被扼杀了。