发出 DIV 指令,而不是 __udivti3

Emit DIV instruction, instead of __udivti3

考虑以下代码:

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;
}

当使用 x86-64 gcc 10 或 clang 10 编译时,都使用 -O3,它 emits __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

至少在我的测试中,前者比后者(已经)慢得多,因此问题是:有没有办法让现代编译器为上述代码发出 DIVQ

编辑:假设商适合 64 位寄存器。

如果商不适合 64 位,

div 将出错。在一般情况下,使用 mul + 单个 div 执行 (a*b) / c 是不安全的(不会为每个可能的输入实现抽象机器语义),因此编译器无法以这种方式生成 asm对于 x86-64。

即使您确实给了编译器足够的信息来确定 division 不会溢出,不幸的是 gcc/clang 仍然不会将其优化为 div具有非零高半 dividend (RDX)。

您需要一个内部或内联 asm 来显式执行 128 / 64 位 => 64 位 division。例如Intrinsics for 128 multiplication and division 有 GNU C 内联汇编,分别看起来适合 low/high 一半。

不幸的是,GNU C 没有这个的内在函数。不过,MSVC 确实如此:Unsigned 128-bit division on 64-bit machine 有链接。