Clang 在除以两个整数时生成奇怪的输出

Clang generates strange output when dividing two integers

我编写了以下非常简单的代码,我正在 Godbolt 的编译器资源管理器中进行试验:

#include <cstdint>

uint64_t func(uint64_t num, uint64_t den)
{
    return num / den;
}

GCC 产生以下输出,这是我所期望的:

func(unsigned long, unsigned long):
        mov     rax, rdi
        xor     edx, edx
        div     rsi
        ret

但是 Clang 13.0.0 会产生以下内容,包括移位和偶数跳转:

func(unsigned long, unsigned long):                              # @func(unsigned long, unsigned long)
        mov     rax, rdi
        mov     rcx, rdi
        or      rcx, rsi
        shr     rcx, 32
        je      .LBB0_1
        xor     edx, edx
        div     rsi
        ret
.LBB0_1:
        xor     edx, edx
        div     esi
        ret

使用 uint32_t 时,clang 的输出再次“简单”,符合我的预期。

这似乎是某种优化,因为 clang 10.0.1 产生与 GCC 相同的输出,但我不明白发生了什么。为什么 clang 会生成这么长的程序集?

程序集似乎通过右移 32 位然后检查结果数是否为 0 来检查 numden 是否大于 2**32。 根据决定,执行 64 位除法 (div rsi) 或 32 位除法 (div esi)。

据推测生成此代码是因为编译器作者认为额外的检查和潜在的分支超过了进行不必要的 64 位除法的成本。

如果我理解正确,它只是检查是否有任何操作数大于 32 位,并对“最多”32 位和更大的位使用不同的 div。