0

在尝试使用 rdrand 内在函数时,我遇到了以下烦恼。

使用我当前的编译器unsigned long并且unsigned long long都是 64 位的。但是, uint64_t 被定义为unsigned longwhile_rdrand64_step需要一个指向unsigned long long.

在英特尔的网站上,该功能被定义为int _rdrand64_step (unsigned __int64* val). 我怎样才能以便携的方式处理这个问题?

 #include <immintrin.h>
 #include <stdint.h>

 uint64_t example_rdrand64()
 {
     uint64_t n;
     _rdrand64_step(&n);
     return n;
 }

铿锵 11.0 -march=ivybridge -O2( https://godbolt.org/z/55sjsG ):

error: no matching function for call to '_rdrand64_step'
note: candidate function not viable: no known conversion
from 'unsigned int *' to 'unsigned long long *' for 1st argument
_rdrand64_step(unsigned long long *__p)
4

1 回答 1

1

使用unsigned long long n; 您仍然可以将其作为uint64_t.

在 Godbolt 编译器浏览器上运行良好,适用于所有 4 个主要 x86 编译器(GCC、clang、ICC、MSVC)的当前版本。请注意,_rdrand64_step这只适用于 x86-64 C++ 实现,因此极大地限制了可移植性问题的范围。

所有 4 个主流 x86 编译器都定义_rdrand64_step了与 兼容的类型unsigned long long,因此在这种情况下,只需遵循 clang 的标头即可。

不幸的是(或不是),gcc/clang 的 immintrin.h 实际上并没有定义一个__int64类型来匹配 Intel 的内部文档,否则你可以使用它。ICC 和 MSVC 确实让您真正使用unsigned __int64 n. (https://godbolt.org/z/v4xnc5


immintrin.h完全可用意味着关于编译器环境和类型宽度许多其他事情,并且极不可能(但并非不可能)某些未来的 x86-64 C 实现会产生unsigned long long除 qword (uint64_t) 之外的任何东西。

尽管如果他们这样做了,也许他们只会将 Intel 映射__int64到不同的类型,因为 Intel 的文档从不使用longor long long,只是__int64AVX2 _mm256_maskload_epi64(__int64 const* mem_addr, __m256i mask)。(甚至__m128i*对于movq负载 内在
__m128i _mm_loadl_epi64 (__m128i const* mem_addr)
__m128i _mm_loadu_si64 (void const* mem_addr)

但是,一个unsigned long long不完全是 64 位的 C++ 实现可能会破坏一些内在代码,所以这不是你需要花任何时间真正担心的问题。在这种情况下,如果它更宽,那仍然可以。你只需要返回它的低 64 位,_rdrand64_step(&n);把结果放在哪里。(或者,如果该 C++ 实现具有内在的 takeunsigned long或者它们定义uint64_t而不是 ,那么您会得到一个编译错误unsigned long long)。

因此,在任何假设的未来 C++ 实现中,静默数据损坏/截断的可能性为零。 ISO C++ 保证至少unsigned long long是64 位类型。(实际上是通过 value-range 指定的,并且无符号表示它的 value bits 是纯二进制,但相同的区别。)

您不需要移植到 DeathStation 9000,只需要移植到任何人可能实际想要使用的任何假设的未来编译器,这几乎意味着它希望与现有的 Intel 内在代码库兼容,如果它提供了这种风格的内在函数一点也不。(而不是使用不同的名称和类型从头开始重新设计,在这种情况下,您必须更改此函数中的 2 行才能使其正常工作。)

于 2021-03-14T06:02:57.313 回答