使用immintrin.h 中的int _rdrand64_step (unsigned __int64* val) 而不是编写内联汇编。你不需要它,避免它的原因有很多(包括这个):https://gcc.gnu.org/wiki/DontUseInlineAsm
在这种情况下,问题在于您可能正在编译 32 位代码,所以当然 64 位 rdrand 是不可编码的。但是您使用 inline-asm 的方式最终为您提供了 32 位 rdrand,并为高半部分存储来自另一个寄存器的垃圾。
gcc -Wall -O3 -m32 -march=ivybridge(clang 类似)产生 (on Godbolt):
In function 'rdrand64_step':
7 : <source>:7:1: warning: unsupported size for integer register
rdrand64_step:
push ebx
rdrand ecx; setc al
mov edx, DWORD PTR [esp+8] # load the pointer arg
movzx eax, al
mov DWORD PTR [edx], ecx
mov DWORD PTR [edx+4], ebx # store garbage in the high half of *rand
pop ebx
ret
我猜你用一个碰巧有ebx=0的调用者调用了这个函数。或者你使用了不同的编译器,做了不同的事情。内联后可能会发生其他事情。如果你查看你实际编译的反汇编,你可以准确地解释发生了什么。
如果你使用了内在函数,你会得到 error: '_rdrand64_step' was not declared in this scope,因为 immintrin.h 只在 64 位模式下声明它(并且带有暗示 rdrand 支持的 -march 设置. 或[-mrdrnd]3。最佳选择:使用-march=native,如果您在目标机器上构建)。
对于重试循环,您还将获得更高效的代码,至少使用 clang:
unsigned long long use_intrinsic(void) {
unsigned long long rand;
while(!_rdrand64_step(&rand)); // TODO: retry limit in case RNG is broken.
return rand;
}
use_intrinsic: # @use_intrinsic
.LBB2_1: # =>This Inner Loop Header: Depth=1
rdrand rax
jae .LBB2_1
ret
这避免了 setcc 然后对其进行测试,这当然是多余的。 gcc6 具有从内联 asm 返回标志结果的语法。您也可以使用asm goto 并在asm 中放置一个jcc,跳转到label: return 1; 目标或通过return 0。 (inline-asm 文档有一个这样做的例子。https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html。另见the inline-assembly tag wiki。)
使用您的 inline-asm,clang(在 64 位模式下)将其编译为:
use_asm:
.LBB1_1:
rdrand rax
setb byte ptr [rsp - 1]
cmp byte ptr [rsp - 1], 0
je .LBB1_1
ret
(clang 对包含内存在内的多个选项的约束做出了错误的决定。)
gcc7.2 和 ICC17 实际上最终从 asm 得到的代码比从内在得到的更好。他们使用 cmovc 得到 0 或 1,然后使用 test 得到。这很愚蠢。但这是一个 gcc/ICC 错过的优化,希望如此。