MSVC 为某些乘法和除法生成奇怪/慢速二进制

MSVC generates strange/slow binary for some multiplications and divisions

本文关键字:二进制 除法 MSVC      更新时间:2023-10-16

我使用 MSVC 2010 SP1,我有以下C++行代码:

int32_t c = (int64_t(a)*int64_t(b))>>2;

ab不是常量时,MSVC 会正确生成 32 位imulshrd指令。但是当ab是常量时,它会生成对_allmull的调用,而不是imul指令。这有什么原因吗?如何强制/引导它始终生成良好的代码?困扰我的是,为什么当它有更多的编译时信息时,它会生成更糟糕的代码。我发现_allmull函数执行 64 位乘法,但我认为在这种情况下不需要它。

我还注意到,对于int32_t c = (int64_t(a)*int64_t(b))/4;行,它甚至会生成除以 4 的_alldiv。

编辑:这似乎是编译器错误。我已经填写了错误报告。

部分相关:如果你想确保利用执行 32x32=>64 位乘法的imul功能,你可以使用Int32x32To64"假 API"(实际上是一个宏):

将两个有符号 32 位整数相乘,返回有符号 64 位整数结果。该函数在 32 位 Windows 上以最佳方式执行。

此函数在所有平台上通过最佳内联代码实现:返回 64 位结果的单个乘法指令。

顺便问一下,您是否启用了优化?如果在启用优化的情况下,编译器无法自行弄清楚,我会感到非常困惑。

<小时 />

编辑

有趣的是,在 winnt.h 中寻找Int32x32To64,您会发现 x86:

//
// The x86 C compiler understands inline assembler. Therefore, inline functions
// that employ inline assembler are used for shifts of 0..31.  The multiplies
// rely on the compiler recognizing the cast of the multiplicand to int64 to
// generate the optimal code inline.
//
#define Int32x32To64( a, b ) (LONGLONG)((LONGLONG)(LONG)(a) * (LONG)(b))
#define UInt32x32To64( a, b ) (ULONGLONG)((ULONGLONG)(DWORD)(a) * (DWORD)(b))

因此,如果即使平台SDK也信任编译器做正确的事情,它肯定会产生imul

<小时 />

再次编辑

如果你需要确保得到一个imul,你可以使用__emul编译器内部函数。

如果我在没有优化的情况下运行编译器,我会看到 allmul,但是使用/Ox,我会看到移位和添加的组合,这取决于常量部分的值。

我认为您需要提供特定的代码以及您使用的编译器选项。

您是否

尝试过解决方法:

int32_t c = (int64_t(int32_t(a))*int64_t(int32_t(b)))>>2;