chiark / gitweb /
rand/rand.c (rdrand_quick): Improve the loop.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 6 Jun 2016 10:01:46 +0000 (11:01 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 6 Jun 2016 10:06:17 +0000 (11:06 +0100)
The `RDRAND' instruction can fail, leaving carry clear.  Previously, I
just exposed the carry flag in a register (with `SETC'), and looped
around in C.

Rewrite the loop in assembler.  This is makes the flow cleaner, and
(coincidentally) avoids a dependency on the `SETcc' instructions (though
if I thought a processor might have `RDRAND' and not `SETcc', I wouldn't
have written the original code the way I did).  But the main benefit is
that I don't have nightmares about seeing

...; setc al; test al, al; ...

sequences any more.  There's still the issue of `i' being tested for
zero twice, but I don't think I can fix that without resorting to `asm
goto', and that has its own problems.

rand/rand.c

index 29b180d8a8775449467d62a91080d439b834f903..f9f16d5def0541553a0e364fe0fb2fb23eea14be 100644 (file)
@@ -165,17 +165,13 @@ static int trivial_quick(rand_pool *r) { return (-1); }
 static int rdrand_quick(rand_pool *r)
 {
   unsigned long rr;
-  unsigned char w;
-  int i;
-
-  for (i = 0; i < 16; i++) {
-    __asm__ ("rdrand %0; setc %1" : "=r" (rr), "=g" (w) : : "cc");
-    if (w) {
-      rand_add(r, &rr, sizeof(rr), 8*sizeof(rr));
-      return (0);
-    }
-  }
-  return (-1);
+  int i = 16;
+
+  __asm__ ("0: rdrand %0; jc 9f; dec %1; jnz 0b; 9:"
+          : "=r" (rr), "=r" (i) : "1" (i) : "cc");
+  if (!i) return (-1);
+  rand_add(r, &rr, sizeof(rr), 8*sizeof(rr));
+  return (0);
 }
 #endif