I'm trying to write this inline assembly, which returns a random number using the rdrand
instruction. The number is stored in the eax
register, and then moved to the rng_num
variable. But I get the error that is in the title.
uint32_t rng_num;
asm volatile("movl $100, %ecx\n\t"
"__trng_cpu_ret:\n\t"
"rdrand %%eax\n\t"
"jnc .__trng_cpu_end\n\t"
"loop __trng_cpu_ret\n\t"
".__trng_cpu_fail:\n\t"
"movl $0, %%eax\n\t"
".__trng_cpu_end:\n\t"
"ret\n\t"
: "=r" (rng_num)
:
:"%eax");
This is the original x86 Intel syntax code:
mov ecx, 100 ;number of retries
retry:
rdrand eax
jnc .done ;carry flag is clear on success
loop retry
.fail:
;no random number available
.done:
;random number is is EAX
The correct answer, as mentioned by fuz and Peter in the comments, is to not use inline assembly.
But here are a couple ways to write this in inline assembly.
uint32_t rng_num;
int iterations = 100;
asm volatile("1: rdrand %0\n\t"
"dec %1\n\t"
"ja 1b\n\t" // jump if CF=0 (from rdrand) and ZF=0 (from dec)
: "=r" (rng_num), "+r"(iterations));
// alternative that doesn't need partial-flag merging
asm volatile("1: rdrand %0\n\t"
"jc 2f\n\t"
"dec %1\n\t"
"jnz 1b\n\t"
"2:\n\t"
: "=r" (rng_num), "+r"(iterations));
Notes:
- These rely on rdrand
setting the destination to 0 when it fails.
- The ja
instruction checks both the C flag from the rdrand
instruction and also the Z flag from the dec
. This may be less efficient than using two separate branches, as in the second example, depending on the cost of combining the two partial registers. I'm sure Peter can provide details. (Peter says: no partial flag stalls on CPUs new enough to have RDRAND, should be fine.)
Here's a list of problems in the code in the question:
- Doesn't use %%
prefix on ecx
register name.
- Uses ecx
without a clobber.
- Checks CF=0 for success of rdrand
instead of CF=1.
- Uses label names that are not local to the inline assembly.
- Doesn't use output register.
- Returns zero to indicate timeout instead of using a separate error indication. [Note, I didn't fix this one.]
- Uses loop
instruction.
- Uses ret
instruction within inline assembly.