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 来检查 num
或 den
是否大于 2**32。
根据决定,执行 64 位除法 (div rsi
) 或 32 位除法 (div esi
)。
据推测生成此代码是因为编译器作者认为额外的检查和潜在的分支超过了进行不必要的 64 位除法的成本。
如果我理解正确,它只是检查是否有任何操作数大于 32 位,并对“最多”32 位和更大的位使用不同的 div。
我编写了以下非常简单的代码,我正在 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 来检查 num
或 den
是否大于 2**32。
根据决定,执行 64 位除法 (div rsi
) 或 32 位除法 (div esi
)。
据推测生成此代码是因为编译器作者认为额外的检查和潜在的分支超过了进行不必要的 64 位除法的成本。
如果我理解正确,它只是检查是否有任何操作数大于 32 位,并对“最多”32 位和更大的位使用不同的 div。