cgccx86inline-assemblyrdrand

error: invalid 'asm': operand number missing after %-letter


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

Solution

  • 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.