Template RiscVFPInstructionTestBase for 32bit vs 64bit scalar registers

PiperOrigin-RevId: 761230549
Change-Id: I209cc4ccd169452c67f66081889481b07e9d9b82
diff --git a/riscv/test/BUILD b/riscv/test/BUILD
index 436a112..fe13e35 100644
--- a/riscv/test/BUILD
+++ b/riscv/test/BUILD
@@ -176,8 +176,9 @@
     tags = ["not_run:arm"],
     deps = [
         ":riscv_fp_test_base",
+        "//riscv:riscv_fp_state",
         "//riscv:riscv_g",
-        "@com_google_absl//absl/strings",
+        "//riscv:riscv_state",
         "@com_google_googletest//:gtest_main",
         "@com_google_mpact-sim//mpact/sim/generic:instruction",
         "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
diff --git a/riscv/test/riscv_d_instructions_test.cc b/riscv/test/riscv_d_instructions_test.cc
index dfb2f8e..c6658ba 100644
--- a/riscv/test/riscv_d_instructions_test.cc
+++ b/riscv/test/riscv_d_instructions_test.cc
@@ -21,6 +21,8 @@
 #include "googlemock/include/gmock/gmock.h"
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "riscv/riscv_fp_info.h"
+#include "riscv/riscv_register.h"
 #include "riscv/test/riscv_fp_test_base.h"
 
 namespace {
@@ -55,7 +57,8 @@
 using ::mpact::sim::riscv::RV32::RiscVDCvtWd;
 using ::mpact::sim::riscv::RV32::RiscVDCvtWud;
 
-class RV32DInstructionTest : public RiscVFPInstructionTestBase {};
+class RV32DInstructionTest
+    : public RiscVFPInstructionTestBase<mpact::sim::riscv::RV32Register> {};
 
 static bool is_snan(double a) {
   if (!std::isnan(a)) return false;
diff --git a/riscv/test/riscv_f_instructions_test.cc b/riscv/test/riscv_f_instructions_test.cc
index 4beb278..ccfc110 100644
--- a/riscv/test/riscv_f_instructions_test.cc
+++ b/riscv/test/riscv_f_instructions_test.cc
@@ -22,6 +22,7 @@
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/type_helpers.h"
 #include "riscv/riscv_fp_info.h"
+#include "riscv/riscv_register.h"
 #include "riscv/test/riscv_fp_test_base.h"
 
 namespace {
@@ -53,7 +54,8 @@
 using ::mpact::sim::riscv::RV32::RiscVFCmplt;
 using ::mpact::sim::riscv::RV32::RiscVFCvtWs;
 using ::mpact::sim::riscv::RV32::RiscVFCvtWus;
-class RV32FInstructionTest : public RiscVFPInstructionTestBase {};
+class RV32FInstructionTest
+    : public RiscVFPInstructionTestBase<mpact::sim::riscv::RV32Register> {};
 
 static bool is_snan(float a) {
   if (!std::isnan(a)) return false;
diff --git a/riscv/test/riscv_fp_test_base.h b/riscv/test/riscv_fp_test_base.h
index f776d16..a408504 100644
--- a/riscv/test/riscv_fp_test_base.h
+++ b/riscv/test/riscv_fp_test_base.h
@@ -340,6 +340,7 @@
   // No return statement, so error will be reported.
 }
 
+template <typename XRegister = RV32Register>
 class RiscVFPInstructionTestBase : public testing::Test {
  public:
   RiscVFPInstructionTestBase() {
@@ -360,7 +361,7 @@
     memory_->Store(kDataLoadAddress - 4096, db);
     db->DecRef();
     for (int i = 1; i < 32; i++) {
-      xreg_[i] = state_->GetRegister<RV32Register>(absl::StrCat("x", i)).first;
+      xreg_[i] = state_->GetRegister<XRegister>(absl::StrCat("x", i)).first;
     }
     for (int i = 1; i < 32; i++) {
       freg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("f", i)).first;
@@ -410,7 +411,7 @@
   }
 
   // named register and sets it to the corresponding value.
-  template <typename T, typename RegisterType = RV32Register>
+  template <typename T, typename RegisterType = XRegister>
   void SetRegisterValues(
       const std::vector<std::tuple<std::string, const T>> &values) {
     for (auto &[reg_name, value] : values) {
@@ -423,7 +424,7 @@
     }
   }
 
-  template <typename T, typename RegisterType = RV32Register>
+  template <typename T, typename RegisterType = XRegister>
   void SetNaNBoxedRegisterValues(
       const std::vector<std::tuple<std::string, const T>> &values) {
     for (auto &[reg_name, value] : values) {
@@ -495,16 +496,16 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     FillArrayWithRandomFPValues<LHS>(lhs_span);
     using LhsInt = typename FPTypeInfo<LHS>::IntType;
     *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
@@ -525,7 +526,7 @@
 
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}});
         SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
             {{kRdName, 0}});
 
@@ -563,16 +564,16 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
     instruction_->AppendDestination(flag_op);
     FillArrayWithRandomFPValues<LHS>(lhs_span);
@@ -595,7 +596,7 @@
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}, {}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}, {}});
         SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
             {{kRdName, 0}});
 
@@ -646,21 +647,21 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kR2Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR2Name}, {});
+      AppendRegisterOperands<XRegister>({kR2Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
     instruction_->AppendDestination(flag_op);
     FillArrayWithRandomFPValues<LHS>(lhs_span);
@@ -680,7 +681,7 @@
 
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}});
         SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
             {{kRdName, 0}});
 
@@ -726,21 +727,21 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kR2Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR2Name}, {});
+      AppendRegisterOperands<XRegister>({kR2Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
     instruction_->AppendDestination(flag_op);
     FillArrayWithRandomFPValues<LHS>(lhs_span);
@@ -761,7 +762,7 @@
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}, {}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}, {}});
         SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
             {{kRdName, 0}});
 
@@ -815,26 +816,26 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kR2Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR2Name}, {});
+      AppendRegisterOperands<XRegister>({kR2Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
     }
     if (kR3Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR3Name}, {});
+      AppendRegisterOperands<XRegister>({kR3Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR3Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     FillArrayWithRandomFPValues<LHS>(lhs_span);
     FillArrayWithRandomFPValues<MHS>(mhs_span);
     FillArrayWithRandomFPValues<RHS>(rhs_span);
@@ -854,7 +855,7 @@
 
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}});
         SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
 
         inst->Execute(nullptr);
@@ -898,26 +899,26 @@
     // This is used for the rounding mode operand.
     const std::string kRmName = absl::StrCat("x", 10);
     if (kR1Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR1Name}, {});
+      AppendRegisterOperands<XRegister>({kR1Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
     }
     if (kR2Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR2Name}, {});
+      AppendRegisterOperands<XRegister>({kR2Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
     }
     if (kR3Name[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({kR3Name}, {});
+      AppendRegisterOperands<XRegister>({kR3Name}, {});
     } else {
       AppendRegisterOperands<RVFpRegister>({kR3Name}, {});
     }
     if (kRdName[0] == 'x') {
-      AppendRegisterOperands<RV32Register>({}, {kRdName});
+      AppendRegisterOperands<XRegister>({}, {kRdName});
     } else {
       AppendRegisterOperands<RVFpRegister>({}, {kRdName});
     }
-    AppendRegisterOperands<RV32Register>({kRmName}, {});
+    AppendRegisterOperands<XRegister>({kRmName}, {});
     auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
     instruction_->AppendDestination(flag_op);
     FillArrayWithRandomFPValues<LHS>(lhs_span);
@@ -940,7 +941,7 @@
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-        SetRegisterValues<int, RV32Register>({{kRmName, rm}});
+        SetRegisterValues<int, XRegister>({{kRmName, rm}});
         SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
             {{kRdName, 0}});
 
@@ -971,9 +972,7 @@
     }
   }
 
-  absl::Span<RV32Register *> xreg() {
-    return absl::Span<RV32Register *>(xreg_);
-  }
+  absl::Span<XRegister *> xreg() { return absl::Span<XRegister *>(xreg_); }
 
   absl::Span<RVFpRegister *> freg() {
     return absl::Span<RVFpRegister *>(freg_);
@@ -1151,7 +1150,7 @@
   }
 
  protected:
-  RV32Register *xreg_[32];
+  XRegister *xreg_[32];
   RV64Register *dreg_[32];
   RVFpRegister *freg_[32];
   RiscVState *state_;
diff --git a/riscv/test/riscv_zfh_instructions_test.cc b/riscv/test/riscv_zfh_instructions_test.cc
index 1e80aa3..bf8365a 100644
--- a/riscv/test/riscv_zfh_instructions_test.cc
+++ b/riscv/test/riscv_zfh_instructions_test.cc
@@ -136,13 +136,14 @@
   FPRoundingMode rounding_mode_;
 };
 
-class RVZfhInstructionTestBase : public RiscVFPInstructionTestBase {
+template <typename XRegister>
+class RVZfhInstructionTestBase : public RiscVFPInstructionTestBase<XRegister> {
  protected:
   template <typename AddressType, typename ValueType>
   void SetupMemory(AddressType, ValueType);
 
-  template <typename ReturnType, typename IntegerRegister>
-  ReturnType LoadHalfHelper(typename IntegerRegister::ValueType, int16_t);
+  template <typename ReturnType>
+  ReturnType LoadHalfHelper(typename XRegister::ValueType, int16_t);
 
   template <typename DestRegisterType, typename LhsRegisterType, typename R,
             typename LHS>
@@ -159,63 +160,64 @@
 
   uint32_t GetOperationFlags(std::function<void(void)> operation);
 
-  template <typename ScalarRegister, typename FPType>
+  template <typename FPType>
   void FmvHxNanBoxHelper();
 
-  template <typename ScalarRegister>
   void FmvXhHelper();
 
-  template <typename ScalarRegister, typename SourceIntegerType>
+  template <typename SourceIntegerType>
   void CvtHwHelper(absl::string_view);
 
-  template <typename ScalarRegister, typename DestinationIntegerType>
+  template <typename DestinationIntegerType>
   void CvtWhHelper(absl::string_view);
 
-  template <typename ScalarRegister>
   void CmpEqHelper();
 
-  template <typename ScalarRegister>
   void CmpLtHelper();
 
-  template <typename ScalarRegister>
   void CmpLeHelper();
 
-  template <typename ScalarRegister>
   void ClassHelper();
 };
 
+template <typename XRegister>
 template <typename AddressType, typename ValueType>
-void RVZfhInstructionTestBase::SetupMemory(AddressType address,
-                                           ValueType value) {
-  DataBuffer *mem_db = state_->db_factory()->Allocate<ValueType>(1);
+void RVZfhInstructionTestBase<XRegister>::SetupMemory(AddressType address,
+                                                      ValueType value) {
+  DataBuffer *mem_db =
+      this->state_->db_factory()->template Allocate<ValueType>(1);
   mem_db->Set<ValueType>(0, value);
-  state_->StoreMemory(instruction_, address, mem_db);
+  this->state_->StoreMemory(this->instruction_, address, mem_db);
   mem_db->DecRef();
 }
 
-template <typename ReturnType, typename IntegerRegister>
-ReturnType RVZfhInstructionTestBase::LoadHalfHelper(
-    typename IntegerRegister::ValueType base, int16_t offset) {
+template <typename XRegister>
+template <typename ReturnType>
+ReturnType RVZfhInstructionTestBase<XRegister>::LoadHalfHelper(
+    typename XRegister::ValueType base, int16_t offset) {
   // Technically the offset for FL* is a 12 bit signed integer but we'll use 16
   // bits for testing.
   const std::string kRs1Name("x1");
   const std::string kFrdName("f5");
-  AppendRegisterOperands<IntegerRegister>({kRs1Name}, {});
-  AppendRegisterOperands<RVFpRegister>(child_instruction_, {}, {kFrdName});
+  this->template AppendRegisterOperands<XRegister>({kRs1Name}, {});
+  this->template AppendRegisterOperands<RVFpRegister>(this->child_instruction_,
+                                                      {}, {kFrdName});
 
   ImmediateOperand<int16_t> *offset_source_operand =
       new ImmediateOperand<int16_t>(offset);
-  instruction_->AppendSource(offset_source_operand);
+  this->instruction_->AppendSource(offset_source_operand);
 
-  SetRegisterValues<typename IntegerRegister::ValueType, IntegerRegister>(
-      {{kRs1Name, static_cast<IntegerRegister::ValueType>(base)}});
-  SetRegisterValues<uint64_t, RVFpRegister>({{kFrdName, 0}});
+  this->template SetRegisterValues<typename XRegister::ValueType, XRegister>(
+      {{kRs1Name, static_cast<XRegister::ValueType>(base)}});
+  this->template SetRegisterValues<RVFpRegister::ValueType, RVFpRegister>(
+      {{kFrdName, 0}});
 
-  instruction_->Execute(nullptr);
+  this->instruction_->Execute(nullptr);
 
-  ReturnType observed_val = state_->GetRegister<RVFpRegister>(kFrdName)
-                                .first->data_buffer()
-                                ->template Get<ReturnType>(0);
+  ReturnType observed_val =
+      this->state_->template GetRegister<RVFpRegister>(kFrdName)
+          .first->data_buffer()
+          ->template Get<ReturnType>(0);
   return observed_val;
 }
 
@@ -223,25 +225,27 @@
 // determine the flags set by an operation. Enables targeted capture of flags
 // that are set by an operation. The Simulation flags are restored to the
 // initial state after the operation is executed.
-uint32_t RVZfhInstructionTestBase::GetOperationFlags(
+template <typename XRegister>
+uint32_t RVZfhInstructionTestBase<XRegister>::GetOperationFlags(
     std::function<void(void)> operation) {
-  uint32_t initial_fflags = rv_fp_->fflags()->GetUint32();
+  uint32_t initial_fflags = this->rv_fp_->fflags()->GetUint32();
   uint32_t delta_fflags = 0;
-  rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+  this->rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
   {
-    ScopedFPStatus sfpstatus(rv_fp_->host_fp_interface(),
-                             rv_fp_->GetRoundingMode());
+    ScopedFPStatus sfpstatus(this->rv_fp_->host_fp_interface(),
+                             this->rv_fp_->GetRoundingMode());
     operation();
   }
-  delta_fflags = rv_fp_->fflags()->GetUint32();
-  rv_fp_->fflags()->Write(initial_fflags);
+  delta_fflags = this->rv_fp_->fflags()->GetUint32();
+  this->rv_fp_->fflags()->Write(initial_fflags);
   return delta_fflags;
 }
 
 // Helper for unary instructions that go between floats and integers.
+template <typename XRegister>
 template <typename DestRegisterType, typename LhsRegisterType, typename R,
           typename LHS>
-void RVZfhInstructionTestBase::UnaryOpWithFflagsMixedTestHelper(
+void RVZfhInstructionTestBase<XRegister>::UnaryOpWithFflagsMixedTestHelper(
     absl::string_view name, Instruction *inst,
     absl::Span<const absl::string_view> reg_prefixes, int delta_position,
     std::function<std::tuple<R, uint32_t>(LHS, uint32_t)> operation) {
@@ -254,21 +258,22 @@
   // This is used for the rounding mode operand.
   const std::string kRmName = absl::StrCat("x", 10);
   if (kR1Name[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({kR1Name}, {});
+    this->template AppendRegisterOperands<XRegister>({kR1Name}, {});
   } else {
-    AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
+    this->template AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
   }
   if (kRdName[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({}, {kRdName});
+    this->template AppendRegisterOperands<XRegister>({}, {kRdName});
   } else {
-    AppendRegisterOperands<RVFpRegister>({}, {kRdName});
+    this->template AppendRegisterOperands<RVFpRegister>({}, {kRdName});
   }
-  AppendRegisterOperands<RV32Register>({kRmName}, {});
-  auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
-  instruction_->AppendDestination(flag_op);
+  this->template AppendRegisterOperands<XRegister>({kRmName}, {});
+  auto *flag_op =
+      this->rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
+  this->instruction_->AppendDestination(flag_op);
   if constexpr (std::is_integral<LHS>::value) {
     for (auto &lhs : lhs_span) {
-      lhs = absl::Uniform(absl::IntervalClosed, bitgen_,
+      lhs = absl::Uniform(absl::IntervalClosed, this->bitgen_,
                           std::numeric_limits<LHS>::min(),
                           std::numeric_limits<LHS>::max());
     }
@@ -281,7 +286,7 @@
     *reinterpret_cast<LHS *>(&lhs_span[6]) = 1024;
     *reinterpret_cast<LHS *>(&lhs_span[7]) = 65000;
   } else {
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
+    this->template FillArrayWithRandomFPValues<LHS>(lhs_span);
     *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
     *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
     *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
@@ -293,31 +298,34 @@
   }
   for (int i = 0; i < kTestValueLength; i++) {
     if constexpr (std::is_integral<LHS>::value) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
+      this->template SetRegisterValues<LHS, LhsRegisterType>(
+          {{kR1Name, lhs_span[i]}});
     } else {
-      SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
+      this->template SetNaNBoxedRegisterValues<LHS, LhsRegisterType>(
+          {{kR1Name, lhs_span[i]}});
     }
 
     for (int rm : {0, 1, 2, 3, 4}) {
-      rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-      rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-      SetRegisterValues<int, RV32Register>({{kRmName, rm}, {}});
-      SetRegisterValues<typename DestRegisterType::ValueType, DestRegisterType>(
-          {{kRdName, 0}});
+      this->rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+      this->rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+      this->template SetRegisterValues<int, XRegister>({{kRmName, rm}, {}});
+      this->template SetRegisterValues<typename DestRegisterType::ValueType,
+                                       DestRegisterType>({{kRdName, 0}});
 
       inst->Execute(nullptr);
-      auto instruction_fflags = rv_fp_->fflags()->GetUint32();
+      auto instruction_fflags = this->rv_fp_->fflags()->GetUint32();
 
       R op_val;
       uint32_t test_operation_fflags;
       {
-        ScopedFPRoundingMode scoped_rm(rv_fp_->host_fp_interface(), rm);
+        ScopedFPRoundingMode scoped_rm(this->rv_fp_->host_fp_interface(), rm);
         std::tie(op_val, test_operation_fflags) = operation(lhs_span[i], rm);
       }
 
-      auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                         .first->data_buffer()
-                         ->template Get<R>(0);
+      auto reg_val =
+          this->state_->template GetRegister<DestRegisterType>(kRdName)
+              .first->data_buffer()
+              ->template Get<R>(0);
       FPCompare<R>(
           op_val, reg_val, delta_position,
           absl::StrCat(name, "  ", i, ": ",
@@ -333,8 +341,9 @@
   }
 }
 
+template <typename XRegister>
 template <typename R, typename LHS, typename MHS, typename RHS>
-void RVZfhInstructionTestBase::TernaryOpWithFflagsFPTestHelper(
+void RVZfhInstructionTestBase<XRegister>::TernaryOpWithFflagsFPTestHelper(
     absl::string_view name, Instruction *inst,
     absl::Span<const absl::string_view> reg_prefixes, int delta_position,
     std::function<std::tuple<R, uint32_t>(LHS, MHS, RHS)> operation) {
@@ -353,33 +362,34 @@
   const std::string kR3Name = absl::StrCat(reg_prefixes[2], 3);
   const std::string kRdName = absl::StrCat(reg_prefixes[3], 5);
   if (kR1Name[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({kR1Name}, {});
+    this->template AppendRegisterOperands<XRegister>({kR1Name}, {});
   } else {
-    AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
+    this->template AppendRegisterOperands<RVFpRegister>({kR1Name}, {});
   }
   if (kR2Name[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({kR2Name}, {});
+    this->template AppendRegisterOperands<XRegister>({kR2Name}, {});
   } else {
-    AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
+    this->template AppendRegisterOperands<RVFpRegister>({kR2Name}, {});
   }
   if (kR3Name[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({kR3Name}, {});
+    this->template AppendRegisterOperands<XRegister>({kR3Name}, {});
   } else {
-    AppendRegisterOperands<RVFpRegister>({kR3Name}, {});
+    this->template AppendRegisterOperands<RVFpRegister>({kR3Name}, {});
   }
   if (kRdName[0] == 'x') {
-    AppendRegisterOperands<RV32Register>({}, {kRdName});
+    this->template AppendRegisterOperands<XRegister>({}, {kRdName});
   } else {
-    AppendRegisterOperands<RVFpRegister>({}, {kRdName});
+    this->template AppendRegisterOperands<RVFpRegister>({}, {kRdName});
   }
   TestRoundingModeSourceOperand *rm_source_operand =
       new TestRoundingModeSourceOperand();
-  instruction_->AppendSource(rm_source_operand);
-  auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
-  instruction_->AppendDestination(flag_op);
-  FillArrayWithRandomFPValues<LHS>(lhs_span);
-  FillArrayWithRandomFPValues<MHS>(mhs_span);
-  FillArrayWithRandomFPValues<RHS>(rhs_span);
+  this->instruction_->AppendSource(rm_source_operand);
+  auto *flag_op =
+      this->rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
+  this->instruction_->AppendDestination(flag_op);
+  this->template FillArrayWithRandomFPValues<LHS>(lhs_span);
+  this->template FillArrayWithRandomFPValues<MHS>(mhs_span);
+  this->template FillArrayWithRandomFPValues<RHS>(rhs_span);
   using LhsInt = typename FPTypeInfo<LHS>::IntType;
   *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
   *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
@@ -408,31 +418,35 @@
   *reinterpret_cast<RhsInt *>(&rhs_span[16 + 6]) = FPTypeInfo<RHS>::kPosDenorm;
   *reinterpret_cast<RhsInt *>(&rhs_span[16 + 7]) = FPTypeInfo<RHS>::kNegDenorm;
   for (int i = 0; i < kTestValueLength; i++) {
-    SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-    SetNaNBoxedRegisterValues<MHS, MhsRegisterType>({{kR2Name, mhs_span[i]}});
-    SetNaNBoxedRegisterValues<RHS, RhsRegisterType>({{kR3Name, rhs_span[i]}});
+    this->template SetNaNBoxedRegisterValues<LHS, LhsRegisterType>(
+        {{kR1Name, lhs_span[i]}});
+    this->template SetNaNBoxedRegisterValues<MHS, MhsRegisterType>(
+        {{kR2Name, mhs_span[i]}});
+    this->template SetNaNBoxedRegisterValues<RHS, RhsRegisterType>(
+        {{kR3Name, rhs_span[i]}});
 
     for (int rm : {0, 1, 2, 3, 4}) {
-      rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
+      this->rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
       rm_source_operand->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-      rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-      SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
-          {{kRdName, 0}});
+      this->rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+      this->template SetRegisterValues<DestRegisterType::ValueType,
+                                       DestRegisterType>({{kRdName, 0}});
 
       inst->Execute(nullptr);
       // Get the fflags for the instruction execution.
-      auto instruction_fflags = rv_fp_->fflags()->GetUint32();
-      rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
+      auto instruction_fflags = this->rv_fp_->fflags()->GetUint32();
+      this->rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
       R op_val;
       uint32_t test_operation_fflags;
       {
-        ScopedFPRoundingMode scoped_rm(rv_fp_->host_fp_interface(), rm);
+        ScopedFPRoundingMode scoped_rm(this->rv_fp_->host_fp_interface(), rm);
         std::tie(op_val, test_operation_fflags) =
             operation(lhs_span[i], mhs_span[i], rhs_span[i]);
       }
-      auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                         .first->data_buffer()
-                         ->template Get<R>(0);
+      auto reg_val =
+          this->state_->template GetRegister<DestRegisterType>(kRdName)
+              .first->data_buffer()
+              ->template Get<R>(0);
       FPCompare<R>(
           op_val, reg_val, delta_position,
           absl::StrCat(name, "  ", i, " (rm=", rm,
@@ -456,18 +470,21 @@
 }
 
 // Verify that the fmv.h.x instruction NaN boxes the converted value.
-template <typename ScalarRegister, typename FPType>
-void RVZfhInstructionTestBase::FmvHxNanBoxHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  AppendRegisterOperands<RVFpRegister>({}, {"f5"});
-  AppendRegisterOperands<ScalarRegister>({"x5"}, {});
-  instruction_->AppendSource(new TestRoundingModeSourceOperand());
+template <typename XRegister>
+template <typename FPType>
+void RVZfhInstructionTestBase<XRegister>::FmvHxNanBoxHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  this->template AppendRegisterOperands<RVFpRegister>({}, {"f5"});
+  this->template AppendRegisterOperands<XRegister>({"x5"}, {});
+  this->instruction_->AppendSource(new TestRoundingModeSourceOperand());
   for (int i = 0; i < 128; i++) {
-    ScalarRegisterType scalar = absl::Uniform<ScalarRegisterType>(bitgen_);
-    SetRegisterValues<ScalarRegisterType, ScalarRegister>({{"x5", scalar}});
-    SetRegisterValues<uint64_t, RVFpRegister>({{"f5", 0}});
-    instruction_->Execute(nullptr);
-    FPType observed_fp = state_->GetRegister<RVFpRegister>("f5")
+    ScalarRegisterType scalar =
+        absl::Uniform<ScalarRegisterType>(this->bitgen_);
+    this->template SetRegisterValues<ScalarRegisterType, XRegister>(
+        {{"x5", scalar}});
+    this->template SetRegisterValues<uint64_t, RVFpRegister>({{"f5", 0}});
+    this->instruction_->Execute(nullptr);
+    FPType observed_fp = this->state_->template GetRegister<RVFpRegister>("f5")
                              .first->data_buffer()
                              ->template Get<FPType>(0);
     EXPECT_TRUE(std::isnan(observed_fp));
@@ -476,11 +493,11 @@
 
 // Helper to test the movement of an IEEE encoded half precision value from a
 // float register to an integer register.
-template <typename ScalarRegister>
-void RVZfhInstructionTestBase::FmvXhHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  UnaryOpFPTestHelper<ScalarRegisterType, HalfFP>(
-      "fmv.x.h", instruction_, {"f", "x"}, 32,
+template <typename XRegister>
+void RVZfhInstructionTestBase<XRegister>::FmvXhHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  this->template UnaryOpFPTestHelper<ScalarRegisterType, HalfFP>(
+      "fmv.x.h", this->instruction_, {"f", "x"}, 32,
       [](HalfFP half_fp) -> ScalarRegisterType {
         bool sign = 1 & (half_fp.value >> (FPTypeInfo<HalfFP>::kBitSize - 1));
         // Fill the upper XLEN-16 bits with the sign bit as per the spec.
@@ -493,11 +510,12 @@
 
 // Helper to test conversion of a 32bit integer value to a half precision float
 // value.
-template <typename ScalarRegister, typename SourceIntegerType>
-void RVZfhInstructionTestBase::CvtHwHelper(absl::string_view name) {
-  UnaryOpWithFflagsMixedTestHelper<RVFpRegister, ScalarRegister, HalfFP,
+template <typename XRegister>
+template <typename SourceIntegerType>
+void RVZfhInstructionTestBase<XRegister>::CvtHwHelper(absl::string_view name) {
+  UnaryOpWithFflagsMixedTestHelper<RVFpRegister, XRegister, HalfFP,
                                    SourceIntegerType>(
-      name, instruction_, {"x", "f"}, 32,
+      name, this->instruction_, {"x", "f"}, 32,
       [](SourceIntegerType input_int, int rm) -> std::tuple<HalfFP, uint32_t> {
         uint32_t fflags = 0;
         HalfFP result = FpConversionsTestHelper(static_cast<double>(input_int))
@@ -509,29 +527,31 @@
 
 // Helper to test conversion of a half precision float value to a 32bit integer
 // value.
-template <typename ScalarRegister, typename DestinationIntegerType>
-void RVZfhInstructionTestBase::CvtWhHelper(absl::string_view name) {
-  UnaryOpWithFflagsMixedTestHelper<ScalarRegister, RVFpRegister,
+template <typename XRegister>
+template <typename DestinationIntegerType>
+void RVZfhInstructionTestBase<XRegister>::CvtWhHelper(absl::string_view name) {
+  UnaryOpWithFflagsMixedTestHelper<XRegister, RVFpRegister,
                                    DestinationIntegerType, HalfFP>(
-      name, instruction_, {"f", "x"}, 32,
+      name, this->instruction_, {"f", "x"}, 32,
       [this](HalfFP input,
              int rm) -> std::tuple<DestinationIntegerType, uint32_t> {
         uint32_t fflags = 0;
         double input_double =
             FpConversionsTestHelper(input).ConvertWithFlags<double>(fflags);
         const DestinationIntegerType val =
-            RoundToInteger<double, DestinationIntegerType>(input_double, rm,
-                                                           fflags);
+            this->template RoundToInteger<double, DestinationIntegerType>(
+                input_double, rm, fflags);
         return std::make_tuple(val, fflags);
       });
 }
 
 // Helper to test the comparison of two half precision values for equality.
-template <typename ScalarRegister>
-void RVZfhInstructionTestBase::CmpEqHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>(
-      "feq.h", instruction_, {"f", "f", "x"}, 32,
+template <typename XRegister>
+void RVZfhInstructionTestBase<XRegister>::CmpEqHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  this->template BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP,
+                                                HalfFP>(
+      "feq.h", this->instruction_, {"f", "f", "x"}, 32,
       [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> {
         uint32_t fflags = 0;
         double a_f =
@@ -547,11 +567,12 @@
 }
 
 // Helper to test the comparison of two half precision values for less than.
-template <typename ScalarRegister>
-void RVZfhInstructionTestBase::CmpLtHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>(
-      "flt.h", instruction_, {"f", "f", "x"}, 32,
+template <typename XRegister>
+void RVZfhInstructionTestBase<XRegister>::CmpLtHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  this->template BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP,
+                                                HalfFP>(
+      "flt.h", this->instruction_, {"f", "f", "x"}, 32,
       [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> {
         uint32_t fflags = 0;
         double a_f =
@@ -572,11 +593,12 @@
 
 // Helper to test the comparison of two half precision values for less than or
 // equal to.
-template <typename ScalarRegister>
-void RVZfhInstructionTestBase::CmpLeHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP, HalfFP>(
-      "fle.h", instruction_, {"f", "f", "x"}, 32,
+template <typename XRegister>
+void RVZfhInstructionTestBase<XRegister>::CmpLeHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  this->template BinaryOpWithFflagsFPTestHelper<ScalarRegisterType, HalfFP,
+                                                HalfFP>(
+      "fle.h", this->instruction_, {"f", "f", "x"}, 32,
       [](HalfFP a, HalfFP b) -> std::tuple<ScalarRegisterType, uint32_t> {
         uint32_t fflags = 0;
         double a_f =
@@ -596,12 +618,12 @@
 }
 
 // Helper to test the classification of the half precision value.
-template <typename ScalarRegister>
-void RVZfhInstructionTestBase::ClassHelper() {
-  using ScalarRegisterType = ScalarRegister::ValueType;
-  UnaryOpWithFflagsMixedTestHelper<ScalarRegister, RVFpRegister,
-                                   ScalarRegisterType, HalfFP>(
-      "fclass.h", instruction_, {"f", "x"}, 32,
+template <typename XRegister>
+void RVZfhInstructionTestBase<XRegister>::ClassHelper() {
+  using ScalarRegisterType = XRegister::ValueType;
+  UnaryOpWithFflagsMixedTestHelper<XRegister, RVFpRegister, ScalarRegisterType,
+                                   HalfFP>(
+      "fclass.h", this->instruction_, {"f", "x"}, 32,
       [](HalfFP input, int rm) -> std::tuple<ScalarRegisterType, uint32_t> {
         uint16_t sign_mask =
             ~(FPTypeInfo<HalfFP>::kExpMask | FPTypeInfo<HalfFP>::kSigMask);
@@ -633,7 +655,7 @@
       });
 }
 
-class RV32ZfhInstructionTest : public RVZfhInstructionTestBase {
+class RV32ZfhInstructionTest : public RVZfhInstructionTestBase<RV32Register> {
  protected:
   // Test conversion instructions. The instance variable semantic_function_ is
   // used to set the semantic function for the instruction and should be set
@@ -724,7 +746,7 @@
   SetupMemory<uint32_t, uint16_t>(0xFF, 0xBEEF);
 
   HalfFP observed_val =
-      LoadHalfHelper<HalfFP, RV32Register>(/* base */ 0x0, /* offset */ 0xFF);
+      LoadHalfHelper<HalfFP>(/* base */ 0x0, /* offset */ 0xFF);
   EXPECT_EQ(observed_val.value, 0xBEEF);
 }
 
@@ -737,8 +759,7 @@
 
   SetupMemory<uint32_t, uint16_t>(0xFF, 0xBEEF);
 
-  float observed_val =
-      LoadHalfHelper<float, RV32Register>(/* base */ 0xFF, /* offset */ 0);
+  float observed_val = LoadHalfHelper<float>(/* base */ 0xFF, /* offset */ 0);
   EXPECT_TRUE(std::isnan(observed_val));
 }
 
@@ -751,7 +772,7 @@
 
   SetupMemory<uint32_t, uint16_t>(0xFF, 0xBEEF);
 
-  double observed_val = LoadHalfHelper<double, RV32Register>(
+  double observed_val = LoadHalfHelper<double>(
       /* base */ 0x0100, /* offset */ -1);
   EXPECT_TRUE(std::isnan(observed_val));
 }
@@ -760,7 +781,7 @@
 // encoding is preserved in the integer register.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvxh) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvxh);
-  FmvXhHelper<RV32Register>();
+  FmvXhHelper();
 }
 
 // Move half precision from an integer register (lower 16 bits) to a float
@@ -777,14 +798,14 @@
 // that the destination value is NaN boxed.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx);
-  FmvHxNanBoxHelper<RV32Register, float>();
+  FmvHxNanBoxHelper<float>();
 }
 
 // Move half precision from an integer register to a float register. Confirm
 // that the destination value is NaN boxed.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFMvhx_double_nanbox) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFMvhx);
-  FmvHxNanBoxHelper<RV32Register, double>();
+  FmvHxNanBoxHelper<double>();
 }
 
 // Half precision to single precision conversion.
@@ -1324,49 +1345,49 @@
 // Test conversion from signed 32 bit integer to half precision.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtHw) {
   SetSemanticFunction(&RiscVZfhCvtHw);
-  CvtHwHelper<RV32Register, int32_t>("fcvt.h.w");
+  CvtHwHelper<int32_t>("fcvt.h.w");
 }
 
 // Test conversion from unsigned 32 bit integer to half precision.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtHwu) {
   SetSemanticFunction(&RiscVZfhCvtHwu);
-  CvtHwHelper<RV32Register, uint32_t>("fcvt.h.wu");
+  CvtHwHelper<uint32_t>("fcvt.h.wu");
 }
 
 // Test conversion from half precision to signed 32 bit integer.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtWh) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhCvtWh);
-  CvtWhHelper<RV32Register, int32_t>("fcvt.w.h");
+  CvtWhHelper<int32_t>("fcvt.w.h");
 }
 
 // Test conversion from half precision to unsigned 32 bit integer.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhCvtWuh) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhCvtWuh);
-  CvtWhHelper<RV32Register, uint32_t>("fcvt.wu.h");
+  CvtWhHelper<uint32_t>("fcvt.wu.h");
 }
 
 // Test equality comparison for half precision values.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmpeq) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmpeq);
-  CmpEqHelper<RV32Register>();
+  CmpEqHelper();
 }
 
 // Test less than comparison for half precision values.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmplt) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmplt);
-  CmpLtHelper<RV32Register>();
+  CmpLtHelper();
 }
 
 // Test less than or equal to comparison for half precision values.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFcmple) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFcmple);
-  CmpLeHelper<RV32Register>();
+  CmpLeHelper();
 }
 
 // Test classification of half precision values.
 TEST_F(RV32ZfhInstructionTest, RiscVZfhFclass) {
   SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVZfhFclass);
-  ClassHelper<RV32Register>();
+  ClassHelper();
 }
 
 // Test fused multiply add for half precision values.
@@ -1509,13 +1530,13 @@
       });
 }
 
-class RV64ZfhInstructionTest : public RVZfhInstructionTestBase {};
+class RV64ZfhInstructionTest : public RVZfhInstructionTestBase<RV64Register> {};
 
 // Move half precision from a float register to an integer register. The IEEE754
 // encoding is preserved in the integer register.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvxh) {
   SetSemanticFunction(&mpact::sim::riscv::RV64::RiscVZfhFMvxh);
-  FmvXhHelper<RV64Register>();
+  FmvXhHelper();
 }
 
 // Move half precision from an integer register (lower 16 bits) to a float
@@ -1532,14 +1553,14 @@
 // that the destination value is NaN boxed.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx_float_nanbox) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx);
-  FmvHxNanBoxHelper<RV64Register, float>();
+  FmvHxNanBoxHelper<float>();
 }
 
 // Move half precision from an integer register to a float register. Confirm
 // that the destination value is NaN boxed.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFMvhx_double_nanbox) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFMvhx);
-  FmvHxNanBoxHelper<RV64Register, double>();
+  FmvHxNanBoxHelper<double>();
 }
 
 // Test the FP16 load instruction. The semantic functions should match the isa
@@ -1552,7 +1573,7 @@
   SetupMemory<uint64_t, uint16_t>(0xFF, 0xBEEF);
 
   HalfFP observed_val =
-      LoadHalfHelper<HalfFP, RV64Register>(/* base */ 0x0, /* offset */ 0xFF);
+      LoadHalfHelper<HalfFP>(/* base */ 0x0, /* offset */ 0xFF);
   EXPECT_EQ(observed_val.value, 0xBEEF);
 }
 
@@ -1565,8 +1586,7 @@
 
   SetupMemory<uint64_t, uint16_t>(0xFF, 0xBEEF);
 
-  float observed_val =
-      LoadHalfHelper<float, RV64Register>(/* base */ 0xFF, /* offset */ 0);
+  float observed_val = LoadHalfHelper<float>(/* base */ 0xFF, /* offset */ 0);
   EXPECT_TRUE(std::isnan(observed_val));
 }
 
@@ -1579,7 +1599,7 @@
 
   SetupMemory<uint64_t, uint16_t>(0xFF, 0xBEEF);
 
-  double observed_val = LoadHalfHelper<double, RV64Register>(
+  double observed_val = LoadHalfHelper<double>(
       /* base */ 0x0100, /* offset */ -1);
   EXPECT_TRUE(std::isnan(observed_val));
 }
@@ -1587,49 +1607,49 @@
 // Test conversion from signed 32 bit integer to half precision.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHw) {
   SetSemanticFunction(&RiscVZfhCvtHw);
-  CvtHwHelper<RV64Register, int32_t>("fcvt.h.w");
+  CvtHwHelper<int32_t>("fcvt.h.w");
 }
 
 // Test conversion from unsigned 32 bit integer to half precision.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtHwu) {
   SetSemanticFunction(&RiscVZfhCvtHwu);
-  CvtHwHelper<RV64Register, uint32_t>("fcvt.h.wu");
+  CvtHwHelper<uint32_t>("fcvt.h.wu");
 }
 
 // Test conversion from half precision to signed 32 bit integer.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtWh) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhCvtWh);
-  CvtWhHelper<RV64Register, int32_t>("fcvt.w.h");
+  CvtWhHelper<int32_t>("fcvt.w.h");
 }
 
 // Test conversion from half precision to unsigned 32 bit integer.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhCvtWuh) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhCvtWuh);
-  CvtWhHelper<RV64Register, uint32_t>("fcvt.wu.h");
+  CvtWhHelper<uint32_t>("fcvt.wu.h");
 }
 
 // Test equality comparison for half precision values.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmpeq) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmpeq);
-  CmpEqHelper<RV64Register>();
+  CmpEqHelper();
 }
 
 // Test less than comparison for half precision values.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmplt) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmplt);
-  CmpLtHelper<RV64Register>();
+  CmpLtHelper();
 }
 
 // Test less than or equal to comparison for half precision values.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFcmple) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFcmple);
-  CmpLeHelper<RV64Register>();
+  CmpLeHelper();
 }
 
 // Test classification of half precision values.
 TEST_F(RV64ZfhInstructionTest, RiscVZfhFclass) {
   SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVZfhFclass);
-  ClassHelper<RV64Register>();
+  ClassHelper();
 }
 
 }  // namespace