将 ASM 指令 RDRand 转换为 Win64

converting ASM instruction RDRand to Win64

我有这个函数(RDRand - 由 编写)在 32 位中接缝工作正常,但在 64 位中失败:

function TryRdRand(out Value: Cardinal): Boolean;
{$IF defined(CPU64BITS)}
asm .noframe
{$else}
asm
{$ifend}
  db   [=10=]f
  db   $c7
  db   $f1
  jc   @success
  xor  eax,eax
  ret
@success:
  mov  [eax],ecx
  mov  eax,1
end;

函数的文档在这里:https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide

特别是这样写的:

Essentially, developers invoke this instruction with a single operand: the destination register where the random value will be stored. Note that this register must be a general purpose register, and the size of the register (16, 32, or 64 bits) will determine the size of the random value returned.

After invoking the RDRAND instruction, the caller must examine the carry flag (CF) to determine whether a random value was available at the time the RDRAND instruction was executed. As Table 3 shows, a value of 1 indicates that a random value was available and placed in the destination register provided in the invocation. A value of 0 indicates that a random value was not available. In current architectures the destination register will also be zeroed as a side effect of this condition.

我对 ASM 的了解很低,我错过了什么?

我也不太明白这条指令:

  ...
  xor  eax,eax
  ret
  ...

它的具体作用是什么?

如果您想要一个性能完全相同的函数,那么我认为它看起来像这样:

function TryRdRand(out Value: Cardinal): Boolean;
asm
{$if defined(WIN64)}
  .noframe
  // rdrand eax
  db   [=10=]f
  db   $c7
  db   $f0
  jnc  @fail
  mov  [rcx],eax
{$elseif defined(WIN32)}
  // rdrand ecx
  db   [=10=]f
  db   $c7
  db   $f1
  jnc  @fail
  mov  [eax],ecx
{$else}
{$Message Fatal 'TryRdRand not implemented for this platform'}
{$endif}
  mov  eax,1
  ret
@fail:
  xor  eax,eax
end;

Peter Cordes 提出的在 asm 中实现重试循环的建议对我来说似乎很明智。我不会在这里尝试实现它,因为我认为它在某种程度上超出了您的问题范围。

此外,Peter 指出,在 x64 中,您可以读取带有 REX.W=1 前缀的 64 位随机值。看起来像这样:

function TryRdRand(out Value: NativeUInt): Boolean;
asm
{$if defined(WIN64)}
  .noframe
  // rdrand rax
  db     // REX.W = 1
  db   [=11=]f
  db   $c7
  db   $f0
  jnc  @fail
  mov  [rcx],rax
{$elseif defined(WIN32)}
  // rdrand ecx
  db   [=11=]f
  db   $c7
  db   $f1
  jnc  @fail
  mov  [eax],ecx
{$else}
{$Message Fatal 'TryRdRand not implemented for this platform'}
{$endif}
  mov  eax,1
  ret
@fail:
  xor  eax,eax
end;