MSVC 的内部函数 __emulu 和 GCC/CLang 中的 _umul128

MSVC's instrinsics __emulu and _umul128 in GCC/CLang

在 MSVC 中存在内在函数 __emulu() and _umul128()。第一个做 u32*u32->u64 乘法,第二个 u64*u64->u128 乘法。

CLang/GCC 是否存在相同的内部函数?

我发现最接近的是 Intel's Guide 中提到的 _mulx_u32()_mulx_u64()。但是它们会产生需要 BMI2 支持的 mulx 指令。虽然 MSVC 的内在函数会生成常规的 mul 指令。另外 _mulx_u32()-m64 模式下不可用,而 __emulu()_umul128() 都存在于 MSVC 的 32 位和 64 位模式下。

您可以在线尝试 32-bit code and 64-bit code

当然,对于 32 位可能会做 return uint64_t(a) * uint64_t(b);(参见 online),希望编译器能够正确猜测并优化使用 u32*u32->u64 乘法而不是 u64*u64->u64.但是有没有办法确定这一点?不依赖于编译器猜测两个参数都是 32 位的(即 uint64_t 的较高部分被归零)?拥有一些像 __emulu() 这样的内在函数,让你对代码有把握。

GCC/CLang 中有 __int128(参见代码 online),但我们必须再次依赖编译器的猜测,即我们实际上乘以 64 位数字(即 int128 的较高部分归零)。有没有一种方法可以在没有编译器猜测的情况下确定是否存在一些内在函数?

顺便说一句,uint64_t(对于 32 位)和 __int128(对于 64 位)都产生正确的 mul 指令,而不是 [=55 中的 mulx =].但我们必须再次依赖编译器正确猜测 uint64_t__int128 的较高部分已归零。

当然,我可以查看 GCC/Clang 已经优化和正确猜测的汇编代码,但是查看一次汇编程序并不能保证在所有情况下都会发生同样的情况。而且我不知道 C++ 中有什么方法可以静态断言编译器对汇编指令的猜测是正确的。

你已经有了答案。使用 uint64_t__uint128_t。不需要内在函数。这适用于所有 64 位目标的现代 GCC 和 Clang。参见 Is there a 128 bit integer in gcc?

#include <stdint.h>
typedef __uint128_t uint128_t;

// 32*32=64 multiplication
f(uint32_t a, uint32_t b) {
   uint64_t ab = (uint64_t)a * b;
}

//64*64=128 multiplication
f(uint64_t a, uint64_t b) {
    uint128_t ab = (uint128_t)a * b;
}

请注意,转换必须在操作数上,或至少在一个操作数上。转换结果将不起作用,因为它会与较短的类型相乘并扩展结果。

But is there a way to be sure about this? Not to rely on compiler's guess

您获得与编译器内在函数完全相同的保证:结果值是正确的。从来没有关于优化的任何保证。仅仅因为您使用了内部函数并不能保证编译器会发出“显而易见的”汇编指令。获得此保证的唯一方法是使用内联汇编,对于像这样的简单操作,它可能会损害性能,因为它会限制编译器优化寄存器使用的方式。