6

I was looking at H.J. Lu's PATCH: Update x86 rdrand intrinsics. I can't tell if I should be using _rdrand_u64, _rdrand64_step, or if there are other function(s). There does not appear to be test cases written for them.

There also seems to be a lack of man pages (from Ubuntu 14, GCC 4.8.4):

$ man -k rdrand
rdrand: nothing appropriate.

How does one use the RDRAND intrinsics to generate, say, a block of 32 bytes?


A related question is RDRAND and RDSEED intrinsics GCC and Intel C++. But it does not tell me how to use them, or how to generate a block.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • @Filip - Yes, BullRun has got a lot of negative criticism due to Snowden leaks (et al). In this case, I'm using a custom random number generator that extracts then expands entropy. There are multiple sources, so NSA backdoors will not cause a catastrophic generator failure. – jww Jul 03 '15 at 23:03

1 Answers1

7

If you look at <immintrin.h> (mine is in `/usr/lib/gcc/x86_64-linux-gnu/4.9/include/', Ubuntu 15.04 64bit), there are compatible (with MSVC, Intel CC) functions defined which pass data back to GCC built-ins

extern __inline int __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_rdrand64_step (unsigned long long *__P)
{
     return __builtin_ia32_rdrand64_step (__P);
}

for 64bit parameter and two others for 16 bit and 32bit parameters

_rdrand16_step (unsigned short *__P)
_rdrand32_step (unsigned int *__P)

You supposed to use those so your code would be compatible with MSVC, Intel CC and other compilers.

_rdrand64_step will fill 64bit parameter, passed by pointer, with random bits and return error code. Ditto for 32bit and 16bit versions

UPDATE

"These intrinsics generate random numbers of 16/32/64 bit wide random integers. The generated random value is written to the given memory location and the success status is returned: '1' if the hardware returned a valid random value, and '0' otherwise."

https://software.intel.com/en-us/node/523864

UPDATE

Per @vy32 request, this is working for me. Well, my system gt updated since original answer, so now it is Ubuntu 20.04.1, x64, GCC v9.3, compilation flags

gcc -m64 -mrdrnd -O3 a.c

Code

#include <stdio.h>
#include <immintrin.h>

int main() {
    unsigned long long result = 0ULL;

    int rc = _rdrand64_step (&result);

    printf("%i %llu", rc, result);

    return (rc != 1);
}

Concerning CF flag and "Ignoring this is a common implementation error that does not show up in testing, but does show up when you run the DRNG under load", I believe this is what built-in is doing. If you comment out printf and compile to assembler with -S flag, code will look like,

xorl    %eax, %eax
rdrand  %rax
movl    $1, %edx
...    
cmovc   %edx, %eax

which means that %eax is zeroed, %edx is set to 1 and then via cmovc %edx might be set to 0 if CF is raised. And this value is returned from function.

So I believe built-in is already dealing with carry flag in a proper way, and user should just check output of _rdrandXX_step(ull*) as described in manual.

Note that The file immintrin.h is an aggregate of all kinds of header files that define processor intrinsics. The current header that contains the RDRAND intrinsics is the x86gprintrin.h which you should not include.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
  • What are the return values? How do we know if the function succeeded or failed? – jww Jul 05 '15 at 21:09
  • 1
    GCC made a mess of this. Their use of [`unsigned long long`](http://stackoverflow.com/q/38681146) is frustrating. – jww Jul 31 '16 at 12:00
  • @vy32 what exactly are you asking for? Why do you need carryflag wrt random numbers? – Severin Pappadeux Jul 28 '20 at 21:03
  • 1
    @SeverinPappadeux you should read the Intel application note. The DRNG can only deliver 800MB/s of randomness, and it clears the CF if no randomness is available. Ignoring this is a common implementation error that does not show up in testing, but does show up when you run the DRNG under load. – vy32 Jul 29 '20 at 23:55
  • BTW, could you post an entire working example? I'm unable to get the `_rdrand64_step` to work in an actual program. Thanks! – vy32 Jul 29 '20 at 23:55
  • @vy32 and another couple of sentences wrt Carry flag. I believe built-in is handing it properly – Severin Pappadeux Jul 30 '20 at 01:32
  • The builtin (and the intrinsic) return the CF result via the 0 or 1 return value, nothing needs to be "handled". The `cmov` nonsense is to materialize the flag value as a 0 / 1 integer in a register (for printf). This is a pretty inefficient approach, silly gcc/clang! They're using the `0` integer output of RDRAND for the failure case. It's also fully separate from the `rc != 1` return value clang materialize the normal way (xor-zero / rdrand / setc). https://godbolt.org/z/3e6aYb – Peter Cordes Jul 30 '20 at 01:51
  • @vyx32 is very appreciative! – vy32 Jul 31 '20 at 02:28
  • @mohammadsdtmnd not using eclipse myself, but https://www.badprog.com/eclipse-tips-n-tricks-adding-flags-to-the-gcc-compiler#:~:text=You%20may%20want%20to%20add,with%20the%20name%20Other%20flags. – Severin Pappadeux Nov 22 '22 at 14:38
  • @SeverinPappadeux THX, In eclipse I've added `-mrdrnd`, instead of miscelaneous I've added it to Dialect. – mohammadsdtmnd Nov 23 '22 at 05:40
  • @MaartenBodewes - `immintrin.h` has always been an aggregate for as long as it's existing, which pulled in headers for all SSE versions as well as AVX. (I think `immintrin.h` was new with AVX1, around 2010 or 2011; I don't know of any other header for a specific AVX version, but I wasn't doing SIMD development at the time.) Your edit strangely says `immintrin.h` used to have *MMX* intrinsics; maybe you're thinking of `mmintrin.h` which has existed since before SSE1? Intel's guide https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html still lists it for __m64 `_mm_add_pi8`. – Peter Cordes Feb 23 '23 at 03:08
  • @PeterCordes Hmm, I looked in my Linux system and for version 9 of the gcc includes I see the rdrand instructions appear only in `immintrin.h` (I did a grep in all .h files). I looked up what `immintrin.h` stood for and it said that it initially contained just the MMX instructions. which where the first intrinsics used anyway, so that kind of makes sense. There seem to be various files, do note that the gcc header files might differ from Intel and Microsoft header files. – Maarten Bodewes Feb 23 '23 at 03:26
  • @MaartenBodewes: My experience is primarily with GCC (and clang) headers. But all the compilers mostly follow Intel's current documentation as far as if the guide says to include some header for some function, then including it *will* define that intrinsic. At least for vector intrinsics; GCC and clang have some of their scalar intrinsics in `x86intrin.h`, and may not even have the same intrinsics in any header for old instructions like BSF (Intel documents `_bit_scan_forward`). I'm curious where you found a claim that `immintrin.h` was ever only MMX, not SSE or AVX. – Peter Cordes Feb 23 '23 at 03:35
  • @MaartenBodewes: The answers on [Header files for x86 SIMD intrinsics](https://stackoverflow.com/q/11228855) haven't changed for a while, and are accurate AFAIK. And BTW, current GCC 12.2.1's `immintrin.h` starts with `#include ` then `#include `, but I think `x86gprintrin.h` is a recent reorganization. – Peter Cordes Feb 23 '23 at 03:36
  • Yeah, for version 9 or 10; for 9 you get `/usr/lib/gcc/x86_64-linux-gnu/9/include$ grep -rn . -e 'rdrand' ./immintrin.h:161:_rdrand16_step (unsigned short *__P) ./immintrin.h:163: return __builtin_ia32_rdrand16_step (__P);` (etc.) as I've indicated. – Maarten Bodewes Feb 23 '23 at 04:30