将两个整数相除时,Clang 会产生奇怪的输出

Posted

技术标签:

【中文标题】将两个整数相除时,Clang 会产生奇怪的输出【英文标题】:Clang generates strange output when dividing two integers 【发布时间】:2022-01-12 09:29:29 【问题描述】:

我编写了以下非常简单的代码,我正在 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 会生成这个更长的程序集?

【问题讨论】:

【参考方案1】:

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

大概是因为编译器编写者认为额外的检查和潜在的分支超过了不必要的 64 位除法的成本而生成的。

【讨论】:

啊,我现在看的这么清楚了哈哈。非常感谢! 另一方面,任何线索为什么 MSVC 如此多地使用堆栈来实现它们的等价物? godbolt.org/z/Gnnzq84TW@Botje @GaryAllen:你忘了查看 MSVC 的警告输出:ignoring unknown option '-O3'。 MSVC 仅支持 -O2-Ox(以及 0 和 1)。 是的,这是针对宽变量中小整数的常见情况进行优化的,因为在 Ice Lake 之前的 Intel CPU 在[i]div r64 上比r64 慢很多,即使对于相同的数字输入也是如此. uops for integer DIV instruction(AMD 和 Ice Lake 及更高版本,没有这个问题,所以没有任何好处,这就是为什么 clang 不为 -mtune=znver1 godbolt.org/z/YnWa74bbf 这样做,也不应该为 -mtune=icelake-client 这样做,但不幸的是仍然确实;在这些情况下,这纯粹是不利的,因为硬件会处理它。)@GaryAllen @bartop:MSVC 确实理解-O2,通常接受- 而不仅仅是DOS 风格的/。只是3 不是有效的优化级别,而且与 GCC 和 clang 不同,它不会将更高的整数视为最大优化。 (例如,clang -O9 当前与 -O3 相同。)【参考方案2】:

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

【讨论】:

以上是关于将两个整数相除时,Clang 会产生奇怪的输出的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gcc 会产生这个奇怪的程序集 vs clang?

求助:C语言大整数相除

辗转相除

C#编程:定义一个函数,计算两个整数相除并输出结果,然后在主函数中调用该函数 来吧,兄弟们!

实验4-1-1 最大公约数和最小公倍数 (15分)emmm辗转相除法

29. 两数相除