Add support for random number generation.

Arm8.5 introduces RNDR and RNDRRS which both return a 64-bit random number.

Change-Id: I0d942c742232efae5f605233410935512c79b576
diff --git a/src/aarch64/constants-aarch64.h b/src/aarch64/constants-aarch64.h
index f1361c3..5c3bd49 100644
--- a/src/aarch64/constants-aarch64.h
+++ b/src/aarch64/constants-aarch64.h
@@ -400,7 +400,9 @@
 // multiple fields (Op0<0>, Op1, Crn, Crm, Op2).
 enum SystemRegister {
   NZCV = SystemRegisterEncoder<3, 3, 4, 2, 0>::value,
-  FPCR = SystemRegisterEncoder<3, 3, 4, 4, 0>::value
+  FPCR = SystemRegisterEncoder<3, 3, 4, 4, 0>::value,
+  RNDR = SystemRegisterEncoder<3, 3, 2, 4, 0>::value,    // Random number.
+  RNDRRS = SystemRegisterEncoder<3, 3, 2, 4, 1>::value   // Reseeded random number.
 };
 
 template<int op1, int crn, int crm, int op2>
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
index 3fa4e54..0a55a78 100644
--- a/src/aarch64/cpu-features-auditor-aarch64.cc
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -1118,6 +1118,15 @@
         scope.Record(CPUFeatures::kAXFlag);
         break;
     }
+  } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) {
+    if (instr->Mask(SystemSysRegMask) == MRS) {
+      switch (instr->GetImmSystemRegister()) {
+        case RNDR:
+        case RNDRRS:
+          scope.Record(CPUFeatures::kRNG);
+          break;
+      }
+    }
   }
 }
 
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index 43654f3..c3527ee 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -5585,6 +5585,12 @@
         case FPCR:
           AppendToOutput("fpcr");
           break;
+        case RNDR:
+          AppendToOutput("rndr");
+          break;
+        case RNDRRS:
+          AppendToOutput("rndrrs");
+          break;
         default:
           AppendToOutput("S%d_%d_c%d_c%d_%d",
                          instr->GetSysOp0(),
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index e7f9e9c..dcf2ef8 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -113,6 +113,11 @@
   print_exclusive_access_warning_ = true;
 
   guard_pages_ = false;
+
+  // Initialize the common state of RNDR and RNDRRS.
+  uint16_t seed[3] = {11, 22, 33};
+  VIXL_STATIC_ASSERT(sizeof(seed) == sizeof(rndr_state_));
+  memcpy(rndr_state_, seed, sizeof(rndr_state_));
 }
 
 
@@ -3908,6 +3913,19 @@
           case FPCR:
             WriteXRegister(instr->GetRt(), ReadFpcr().GetRawValue());
             break;
+          case RNDR:
+          case RNDRRS: {
+            uint64_t high = jrand48(rndr_state_);
+            uint64_t low = jrand48(rndr_state_);
+            uint64_t rand_num = (high << 32) | (low & 0xffffffff);
+            WriteXRegister(instr->GetRt(), rand_num);
+            // Simulate successful random number generation.
+            // TODO: Return failure occasionally as a random number cannot be
+            // returned in a period of time.
+            ReadNzcv().SetRawValue(NoFlag);
+            LogSystemRegister(NZCV);
+            break;
+          }
           default:
             VIXL_UNIMPLEMENTED();
         }
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 075a606..13803e4 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -3354,6 +3354,9 @@
 
   CPUFeaturesAuditor cpu_features_auditor_;
   std::vector<CPUFeatures> saved_cpu_features_;
+
+  // The simulated state of RNDR and RNDRRS for generating a random number.
+  uint16_t rndr_state_[3];
 };
 
 #if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) && __cplusplus < 201402L
diff --git a/src/cpu-features.h b/src/cpu-features.h
index 25c49d7..cd82d3c 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -100,7 +100,9 @@
   /* Branch target identification.                                          */ \
   V(kBTI,                 "BTI",                    NULL)                      \
   /* Flag manipulation instructions: {AX,XA}FLAG                            */ \
-  V(kAXFlag,              "AXFlag",                 NULL)
+  V(kAXFlag,              "AXFlag",                 NULL)                      \
+  /* Random number generation extension,                                    */ \
+  V(kRNG,                 "RNG",                    NULL)
 // clang-format on
 
 
diff --git a/test/aarch64/test-assembler-aarch64.cc b/test/aarch64/test-assembler-aarch64.cc
index 5def129..20f4057 100644
--- a/test/aarch64/test-assembler-aarch64.cc
+++ b/test/aarch64/test-assembler-aarch64.cc
@@ -15026,6 +15026,45 @@
   TEARDOWN();
 }
 
+TEST(system_rng) {
+  SETUP_WITH_FEATURES(CPUFeatures::kRNG);
+
+  START();
+  // Random number.
+  __ Mrs(x1, RNDR);
+  // Assume that each generation is successful now.
+  // TODO: Return failure occasionally.
+  __ Mrs(x2, NZCV);
+  __ Mrs(x3, RNDR);
+  __ Mrs(x4, NZCV);
+
+  // Reseeded random number.
+  __ Mrs(x5, RNDRRS);
+  // Assume that each generation is successful now.
+  // TODO: Return failure occasionally.
+  __ Mrs(x6, NZCV);
+  __ Mrs(x7, RNDRRS);
+  __ Mrs(x8, NZCV);
+  END();
+
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
+  RUN();
+  // Random number generation series.
+  // Check random numbers have been generated and aren't equal when reseed has
+  // happened.
+  // NOTE: With a different architectural implementation, there may be a
+  // collison.
+  // TODO: Return failure occasionally. Set ZFlag and return UNKNOWN value.
+  ASSERT_NOT_EQUAL_64(x1, x3);
+  ASSERT_EQUAL_64(NoFlag, x2);
+  ASSERT_EQUAL_64(NoFlag, x4);
+  ASSERT_NOT_EQUAL_64(x5, x7);
+  ASSERT_EQUAL_64(NoFlag, x6);
+  ASSERT_EQUAL_64(NoFlag, x8);
+#endif
+
+  TEARDOWN();
+}
 
 TEST(cfinv) {
   SETUP_WITH_FEATURES(CPUFeatures::kFlagM);
diff --git a/test/aarch64/test-disasm-aarch64.cc b/test/aarch64/test-disasm-aarch64.cc
index cdddf86..2dfedfc 100644
--- a/test/aarch64/test-disasm-aarch64.cc
+++ b/test/aarch64/test-disasm-aarch64.cc
@@ -3232,6 +3232,8 @@
   COMPARE(mrs(x0, NZCV), "mrs x0, nzcv");
   COMPARE(mrs(x30, NZCV), "mrs x30, nzcv");
   COMPARE(mrs(x15, FPCR), "mrs x15, fpcr");
+  COMPARE(mrs(x20, RNDR), "mrs x20, rndr");
+  COMPARE(mrs(x5, RNDRRS), "mrs x5, rndrrs");
 
   // Test mrs that use system registers we haven't named.
   COMPARE(dci(MRS | (0x5555 << 5)), "mrs x0, S3_2_c10_c10_5");
diff --git a/test/test-api.cc b/test/test-api.cc
index 219d4df..9b3d55d 100644
--- a/test/test-api.cc
+++ b/test/test-api.cc
@@ -393,7 +393,7 @@
       // Armv8.4
       "RCpc (imm), FlagM, USCAT, FHM, DIT, "
       // Armv8.5
-      "BTI, AXFlag",
+      "BTI, AXFlag, RNG",
       CPUFeatures::All());
 }