将 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;
特别是这样写的:
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;
我有这个函数(RDRand - 由
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;
特别是这样写的:
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;