0

I am trying to implement Intel DRNG in c++.

According to its guide to generate a 64 bit unsigned long long the code should be:

int rdrand64_step (unsigned long long *rand)
{
    unsigned char ok;
    asm volatile ("rdrand %0; setc %1"
                  : "=r" (*rand), "=qm" (ok));
    return (int) ok;
}

However the output of this function rand is only giving me an output of only 32 bits as shown.

        bd4a749d
        d461c2a8
        8f666eee
        d1d5bcc4
        c6f4a412

any reason why this is happening?

more info: the IDE I'm using is codeblocks

albusSimba
  • 441
  • 4
  • 14
  • On which operating system and which version of C++ and which compiler ? C++11 has [``](http://en.cppreference.com/w/cpp/numeric/random), better use it (since it is portable). [Code::Blocks](http://www.codeblocks.org/) is *not* a compiler (it runs one, perhaps [GCC](http://gcc.gnu.org/)) but an IDE – Basile Starynkevitch Sep 25 '17 at 15:23
  • ***the compiler I'm using is codeblocks*** Code::Blocks is an IDE. I assume the compiler is some version of mingw. – drescherjm Sep 25 '17 at 15:26
  • Linux has `/dev/random` see [random(4)](http://man7.org/linux/man-pages/man4/random.4.html), and [mingw](http://mingw.org/) is -or contains- a port of [GCC](http://gcc.gnu.org/). Run `g++ -v` or `g++ --version` in a terminal. A recent enough mingw should provide a C++11 compiler – Basile Starynkevitch Sep 25 '17 at 15:26
  • I believe this will not work in 32 bit code. Although I may be reading the documentation wrong. – drescherjm Sep 25 '17 at 15:29
  • But *why* do you need to *implement* some DRNG? Why can't you *use* `` ?? Please edit your question to explain! – Basile Starynkevitch Sep 25 '17 at 15:31
  • @basile it's a simple how to implement DRNG in which I do need to use DRNG and I'm on a windows platform so I can use /dev/random. I would appreciate if u can help rather then trying to rant on my post. – albusSimba Sep 25 '17 at 17:53
  • @drescherjm how do u know its a 32 bit code? – albusSimba Sep 25 '17 at 17:53
  • The mingw that is bundled with Code::Blocks is likely 32 bit. – drescherjm Sep 25 '17 at 17:58
  • @drescherjm is there any 64 bit complier that I can use? – albusSimba Sep 25 '17 at 18:02
  • There are several 64 bit compilers that will work with Code::Blocks. – drescherjm Sep 25 '17 at 18:03
  • My point in that RNG is a very delicate subject. I would recommend trusting an existing implementation – Basile Starynkevitch Sep 25 '17 at 18:09
  • Is it wise to tweak the code in this way? 1) left shit 32 bits 2) run the DRNG again for the lower bits. The only problem is if I do that do I still maintain the entropy of the random number generator? – albusSimba Sep 26 '17 at 00:44

1 Answers1

3

Use int _rdrand64_step (unsigned __int64* val) from immintrin.h instead of writing inline asm. You don't need it, and there are many reasons (including this one) to avoid it: https://gcc.gnu.org/wiki/DontUseInlineAsm


In this case, the problem is that you're probably compiling 32-bit code, so of course 64-bit rdrand is not encodeable. But the way you used inline-asm ended up giving you a 32-bit rdrand, and storing garbage from another register for the high half.

gcc -Wall -O3 -m32 -march=ivybridge (and similar for clang) produces (on Godbolt):

In function 'rdrand64_step':
7 : <source>:7:1: warning: unsupported size for integer register

rdrand64_step:
    push    ebx
    rdrand ecx; setc al
    mov     edx, DWORD PTR [esp+8]    # load the pointer arg
    movzx   eax, al
    mov     DWORD PTR [edx], ecx
    mov     DWORD PTR [edx+4], ebx    # store garbage in the high half of *rand
    pop     ebx
    ret

I guess you called this function with a caller that happened to have ebx=0. Or else you used a different compiler that did something different. Maybe something else happens after inlining. If you looked at disassembly of what you actually compiled, you could explain exactly what's going on.


If you'd used the intrinsic, you would have gotten error: '_rdrand64_step' was not declared in this scope, because immintrin.h only declares it in 64-bit mode (and with a -march setting that implies rdrand support. Or [-mrdrnd]3. Best option: use -march=native if you're building on the target machine).

You'd also get significantly more efficient code for a retry loop, at least with clang:

unsigned long long use_intrinsic(void) {
    unsigned long long rand;
    while(!_rdrand64_step(&rand));  // TODO: retry limit in case RNG is broken.
    return rand;
}

use_intrinsic:                          # @use_intrinsic
.LBB2_1:                                # =>This Inner Loop Header: Depth=1
    rdrand  rax
    jae     .LBB2_1
    ret

That avoids setcc and then testing that, which is of course redundant. gcc6 has syntax for returning flag results from inline asm. You can also use asm goto and put a jcc inside the asm, jumping to a label: return 1; target or falling through to a return 0. (The inline-asm docs have an example of doing this. https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html. See also the inline-assembly tag wiki.)

Using your inline-asm, clang (in 64-bit mode) compiles it to:

use_asm:
.LBB1_1:
    rdrand  rax
    setb    byte ptr [rsp - 1]
    cmp     byte ptr [rsp - 1], 0
    je      .LBB1_1
    ret

(clang makes bad decisions for constraints with multiple options that include memory.)

gcc7.2 and ICC17 actually end up with better code from the asm than from the intrinsic. They use cmovc to get a 0 or 1 and then test that. It's pretty dumb. But that's a gcc/ICC missed optimization that will hopefully be.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • yea it appears i am using a 32-bit compiler i tried to change that using [minGW64](https://stackoverflow.com/questions/26414511/how-do-i-compile-for-64bit-using-g-w-codeblocks) but when i test run it using `cout << “sizeof(void*) = ” << sizeof(void*) << ";" << endl;` i get 4 instead of the expected [8](http://wiki.codeblocks.org/index.php/64Bit_Windows) and using my code i still only get a 32 bit return. its it because my IDE is 32 bit? – albusSimba Sep 26 '17 at 11:09
  • @albusSimba: your IDE has to pass `-m64` if its default is `-m32`. IDK anything about your IDE, but there's no reason a 32-bit process would have trouble running a 64-bit executable. It doesn't load the compiler as a library (does it?) – Peter Cordes Sep 26 '17 at 11:16
  • Yea i have alr pass `-m64` but it still doesn't pass the simple test of `cout << “sizeof(void*) = ” << sizeof(void*) << ";" << endl;` which means it is still running on 32 bits – albusSimba Sep 26 '17 at 11:52
  • @albusSimba: Also pass `-v` (verbose) to see if it's appending a `-m32` after user-supplied options. You could also check if `__x86_64__` is defined by the preprocessor, in case it's doing something weird like 32-bit pointers in 64-bit mode (like the [Linux x32 ABI](https://en.wikipedia.org/wiki/X32_ABI)). Or just google more about using your IDE. – Peter Cordes Sep 26 '17 at 12:08
  • `inlining failed in call to always_inline 'int _rdrand64_step(long long unsigned int*)` i swap to clion and my code seem to have worked but i was wondering why using `immintrin.h` throws me back this error it's the 1st time i've seen this error. – albusSimba Sep 26 '17 at 14:25
  • @albusSimba: that happens when you use an intrinsic for an instruction the target doesn't support. That's why I used `-march=ivybridge` in my answer. Use `-march=native`. – Peter Cordes Sep 26 '17 at 14:29
  • where do i input `-march=native`? in the cmake file? do u have an example? – albusSimba Sep 26 '17 at 14:46
  • @albusSimba: On the compiler command line, however you do that in your build system. For an example, click the godbolt link, where `-march=ivybridge` is in the compiler command-line options box, along with `-O3 -Wall`. – Peter Cordes Sep 26 '17 at 14:51