Eci*_*ana 5 c optimization gcc x86-64 clang
考虑以下代码:
unsigned long long div(unsigned long long a, unsigned long long b, unsigned long long c) {
unsigned __int128 d = (unsigned __int128)a*(unsigned __int128)b;
return d/c;
}
Run Code Online (Sandbox Code Playgroud)
当使用 x86-64 gcc 10 或 clang 10 编译时,两者都带有-O3,它会发出 __udivti3, 而不是DIVQ指令:
div:
mov rax, rdi
mov r8, rdx
sub rsp, 8
xor ecx, ecx
mul rsi
mov r9, rax
mov rsi, rdx
mov rdx, r8
mov rdi, r9
call __udivti3
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)
至少在我的测试中,前者比后者(已经)慢得多,因此问题是:有没有办法让现代编译器发出DIVQ上述代码?
编辑:假设商适合 64 位寄存器。
div如果商不适合 64 位,则会出错。在一般情况下,使用(a*b) / cmul + 单个 div 并不安全(不会为每个可能的输入实现抽象机语义),因此编译器无法为 x86-64 生成那样的 asm。
即使您确实为编译器提供了足够的信息来确定除法不能溢出(即high_half < divisor),不幸的是 gcc/clang 仍然不会将其优化为div具有非零高半除数(RDX)的单个 a 。
您需要一个内在或内联汇编来显式执行 128 / 64 位 => 64 位除法。例如,128 乘法和除法的内在函数具有 GNU C 内联汇编,看起来分别适合低/高半部分。
不幸的是,GNU C 没有为此的内在函数。不过,MSVC 确实如此:64 位机器上的无符号 128 位除法具有链接。