发出 DIV 指令,而不是 __udivti3

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 位寄存器。

Pet*_*des 4

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 位除法具有链接。