Internal change

PiperOrigin-RevId: 548872652
diff --git a/sim/test/BUILD b/sim/test/BUILD
index 735c876..c365fd9 100644
--- a/sim/test/BUILD
+++ b/sim/test/BUILD
@@ -107,6 +107,23 @@
 )
 
 cc_test(
+    name = "kelvin_vector_memory_instructions_test",
+    srcs = ["kelvin_vector_memory_instructions_test.cc"],
+    copts = [
+        "-Werror",
+        "-Wvla-extension",
+    ],
+    deps = [
+        ":kelvin_vector_instructions_test_base",
+        "//sim:kelvin_instructions",
+        "@com_google_absl//absl/functional:bind_front",
+        "@com_google_absl//absl/strings",
+        "@com_google_googletest//:gtest_main",
+        "@com_google_mpact-sim//mpact/sim/generic:instruction",
+    ],
+)
+
+cc_test(
     name = "kelvin_log_instructions_test",
     srcs = ["kelvin_log_instructions_test.cc"],
     copts = [
diff --git a/sim/test/kelvin_vector_instructions_test.cc b/sim/test/kelvin_vector_instructions_test.cc
index 250ffab..f4c4c0b 100644
--- a/sim/test/kelvin_vector_instructions_test.cc
+++ b/sim/test/kelvin_vector_instructions_test.cc
@@ -10,7 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "sim/kelvin_vector_memory_instructions.h"
 #include "sim/test/kelvin_vector_instructions_test_base.h"
 #include "googletest/include/gtest/gtest.h"
 #include "absl/functional/bind_front.h"
@@ -18,14 +17,13 @@
 #include "absl/strings/string_view.h"
 #include "mpact/sim/generic/instruction.h"
 
-// This file contains the tests for testing kelvin vector instructions.
+// This file contains the tests for testing kelvin vector binary instructions.
 
 namespace {
 
 using mpact::sim::generic::Instruction;
 
 // Semantic functions.
-using kelvin::sim::KelvinGetVl;
 using kelvin::sim::KelvinVAbsd;
 using kelvin::sim::KelvinVAcc;
 using kelvin::sim::KelvinVAdd;
@@ -45,8 +43,6 @@
 using kelvin::sim::KelvinVGt;
 using kelvin::sim::KelvinVHadd;
 using kelvin::sim::KelvinVHsub;
-using kelvin::sim::KelvinVLd;
-using kelvin::sim::KelvinVLdRegWrite;
 using kelvin::sim::KelvinVLe;
 using kelvin::sim::KelvinVLt;
 using kelvin::sim::KelvinVMacc;
@@ -78,8 +74,6 @@
 using kelvin::sim::KelvinVSra;
 using kelvin::sim::KelvinVSrans;
 using kelvin::sim::KelvinVSrl;
-using kelvin::sim::KelvinVSt;
-using kelvin::sim::KelvinVStQ;
 using kelvin::sim::KelvinVSub;
 using kelvin::sim::KelvinVSubs;
 using kelvin::sim::KelvinVSubsu;
@@ -106,23 +100,6 @@
 class KelvinVectorInstructionsTest
     : public kelvin::sim::test::KelvinVectorInstructionsTestBase {
  public:
-  template <typename T>
-  absl::string_view KelvinTestTypeSuffix() {
-    absl::string_view type_suffix = "Unknown";
-    switch (sizeof(T)) {
-      case 4:
-        type_suffix = "W";
-        break;
-      case 2:
-        type_suffix = "H";
-        break;
-      case 1:
-        type_suffix = "B";
-        break;
-    }
-    return type_suffix;
-  }
-
   template <template <typename, typename, typename> class F, typename TD,
             typename TS1, typename TS2>
   void KelvinVectorBinaryOpHelper(absl::string_view name) {
@@ -477,7 +454,9 @@
     KelvinVAbsd<Vs1>(scalar, strip_mine, inst);
   }
 };
-TEST_F(KelvinVectorInstructionsTest, VAbsd) {
+
+// TODO(b/291683818): Check the implementation and re-enable the logic.
+TEST_F(KelvinVectorInstructionsTest, DISABLED_VAbsd) {
   KelvinVectorBinaryOpHelper<VAbsdOp, uint8_t, int8_t, int8_t, uint16_t,
                              int16_t, int16_t, uint32_t, int32_t, int32_t>(
       "VAbsd");
@@ -1682,391 +1661,4 @@
   KelvinShuffleOpHelper<VZipOp, int8_t, int16_t, int32_t>("VZip", kWidenDst);
 }
 
-class KelvinVectorInstructionsMemoryTest : public KelvinVectorInstructionsTest {
- public:
-  template <typename T>
-  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool has_length,
-                                   bool has_stride, bool strip_mine,
-                                   bool post_increment, bool x_variant,
-                                   bool is_load, bool is_quad) {
-    InstructionPtr child_instruction(
-        new Instruction(next_instruction_address_, state_),
-        [](Instruction *inst) { inst->DecRef(); });
-    child_instruction->set_size(4);
-    auto instruction = CreateInstruction();
-
-    if (is_load) {
-      child_instruction->set_semantic_function(
-          absl::bind_front(&KelvinVLdRegWrite<T>, strip_mine));
-      instruction->set_semantic_function(
-          absl::bind_front(&KelvinVLd<T>, has_length, has_stride, strip_mine));
-      instruction->AppendChild(child_instruction.get());
-    } else {
-      if (is_quad) {
-        instruction->set_semantic_function(
-            absl::bind_front(&KelvinVStQ<T>, strip_mine));
-      } else {
-        instruction->set_semantic_function(absl::bind_front(
-            &KelvinVSt<T>, has_length, has_stride, strip_mine));
-      }
-    }
-
-    // Setup source and child instruction operands.
-    const uint32_t num_ops = strip_mine ? 4 : 1;
-    if (is_load) {
-      AppendVectorRegisterOperands(
-          child_instruction.get(), num_ops, 1 /* src1_widen_factor */, {}, {},
-          false /* widen_dst */, {kelvin::sim::test::kVd});
-    } else {  // Store
-      AppendVectorRegisterOperands(
-          instruction.get(), num_ops, 1 /* src1_widen_factor */,
-          kelvin::sim::test::kVd, {}, false /* widen_dst */, {});
-    }
-    AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name},
-                           {});
-    if (!x_variant) {
-      AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs2Name},
-                             {});
-    }
-
-    if (post_increment) {
-      AppendRegisterOperands(instruction.get(), {},
-                             {kelvin::sim::test::kRs1Name});
-    }
-
-    // x variant can't have length or stride fields.
-    if (x_variant && (has_length || has_stride)) {
-      GTEST_FAIL();
-    }
-
-    // xx variant can't have no length, no stride, and no post_increment
-    // encoding
-    if (!x_variant && !has_length && !has_stride && !post_increment) {
-      GTEST_FAIL();
-    }
-
-    // length and stride fields can't coexist without post_increment
-    if (has_length && has_stride && !post_increment) {
-      GTEST_FAIL();
-    }
-
-    // Quad store need to have stride specified and no length
-    if (is_quad && is_load) {
-      GTEST_FAIL();
-    }
-    if ((is_quad && has_length) || (is_quad && !has_stride)) {
-      GTEST_FAIL();
-    }
-    const uint32_t vector_length_in_bytes = state_->vector_length() / 8;
-    const uint32_t vd_size = vector_length_in_bytes / sizeof(T);
-    const uint32_t len_or_strides[] = {0,       1,           vd_size - 1,
-                                       vd_size, 2 * vd_size, 4 * vd_size};
-
-    // Check with different values for length and stride if applicable.
-    for (int test = 0;
-         test < (has_length || has_stride ? std::size(len_or_strides) : 1);
-         test++) {
-      // Store stride can't be smaller than vd_size
-      if ((is_quad && len_or_strides[test] < vd_size / 4) ||
-          (!is_load && has_stride && len_or_strides[test] < vd_size)) {
-        continue;
-      }
-      // Set input register values.
-      SetRegisterValues<uint32_t>(
-          {{kelvin::sim::test::kRs1Name, kelvin::sim::test::kDataLoadAddress}});
-
-      if (!x_variant) {
-        SetRegisterValues<uint32_t>(
-            {{kelvin::sim::test::kRs2Name, len_or_strides[test]}});
-      }
-
-      // Fill vector register(s) with random values.
-      std::vector<T> vd_value(vector_length_in_bytes / sizeof(T) * num_ops);
-      auto vd_span = absl::Span<T>(vd_value);
-      FillArrayWithRandomValues<T>(vd_span);
-      for (int i = 0; i < num_ops; i++) {
-        auto vd_name = absl::StrCat("v", kelvin::sim::test::kVd + i);
-        SetVectorRegisterValues<T>(
-            {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
-      }
-
-      // Execute instruction.
-      instruction->Execute();
-
-      // Compute memory values. For load test it is the expected output; for
-      // store test it is the actual output.
-      std::vector<T> memory_values(vd_size * num_ops);
-      uint32_t addr = kelvin::sim::test::kDataLoadAddress;
-      uint32_t rs2_value = len_or_strides[test];
-      uint32_t count = vd_size * num_ops;
-      if (has_length) {
-        count = std::min(count, rs2_value);
-      }
-      uint32_t left = count;
-      for (int op_num = 0; op_num < num_ops; op_num++) {
-        const int n = std::min(vd_size, left);
-        if (is_quad) {
-          const uint32_t quad_size = vd_size / 4;
-          for (int i = 0; i < 4; ++i) {
-            for (int j = 0; j < quad_size; ++j) {
-              memory_values[op_num * vd_size + i * quad_size + j] =
-                  GetSavedMemoryValue<T>(addr +
-                                         (i * quad_size + j) * sizeof(T));
-            }
-            // Stride increase per quad_size.
-            addr += rs2_value * sizeof(T);
-          }
-        } else {
-          for (int i = 0; i < vd_size; ++i) {
-            if (is_load) {
-              memory_values[op_num * vd_size + i] =
-                  i < n ? GetDefaultMemoryValue<T>(addr + i * sizeof(T)) : 0;
-            } else {
-              memory_values[op_num * vd_size + i] =
-                  i < n ? GetSavedMemoryValue<T>(addr + i * sizeof(T)) : 0;
-            }
-          }
-          left -= n;
-          if (has_stride) {
-            addr += rs2_value * sizeof(T);
-          } else {
-            addr += n * sizeof(T);
-          }
-        }
-      }
-
-      uint32_t expected_rs1_value = kelvin::sim::test::kDataLoadAddress;
-      if (post_increment && count) {
-        if (has_length && has_stride) {  // .tp
-          expected_rs1_value += vd_size * sizeof(T);
-        } else if (!has_length && !has_stride && x_variant) {  // .p.x
-          expected_rs1_value += vd_size * sizeof(T) * num_ops;
-        } else if (has_length) {  // .lp
-          expected_rs1_value += count * sizeof(T);
-        } else if (has_stride) {  // .sp
-          const uint32_t quad_scale = is_quad ? 4 : 1;
-          expected_rs1_value += rs2_value * sizeof(T) * num_ops * quad_scale;
-        } else {  // .p.xx
-          expected_rs1_value += rs2_value * sizeof(T);
-        }
-      }
-
-      // Check result
-      left = count;
-      for (int op_num = 0; op_num < num_ops; op_num++) {
-        auto vreg_num = kelvin::sim::test::kVd + op_num;
-        auto test_vreg = vreg_[vreg_num];
-        auto vreg_span = test_vreg->data_buffer()->Get<T>();
-        if (is_load) {
-          for (int element_index = 0; element_index < vd_size;
-               element_index++) {
-            auto vreg_element_index = op_num * vd_size + element_index;
-            EXPECT_EQ(memory_values[vreg_element_index],
-                      vreg_span[element_index])
-                << absl::StrCat(name, "[", vreg_element_index, "] != reg[",
-                                vreg_num, "*", element_index, "]");
-          }
-        } else {  // Store
-          const int n = std::min(vd_size, left);
-          for (int element_index = 0;
-               element_index < vd_size && element_index < n; element_index++) {
-            auto total_element_index = op_num * vd_size + element_index;
-            EXPECT_EQ(memory_values[total_element_index],
-                      vreg_span[element_index])
-                << absl::StrCat(name, " mem at ", total_element_index,
-                                " != vreg[", vreg_num, "][", element_index,
-                                "]");
-          }
-          left -= n;
-        }
-      }
-
-      if (post_increment) {
-        // Check rs1 value.
-        auto *reg = state_
-                        ->GetRegister<kelvin::sim::test::RV32Register>(
-                            kelvin::sim::test::kRs1Name)
-                        .first;
-        EXPECT_EQ(expected_rs1_value, reg->data_buffer()->Get<uint32_t>()[0])
-            << absl::StrCat(name, " post incremented rs1 is incorrect.");
-      }
-    }
-  }
-
-  template <typename T>
-  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
-    constexpr bool kNoLength = false;
-    constexpr bool kLength = true;
-    constexpr bool kNoStride = false;
-    constexpr bool kStride = true;
-    constexpr bool kPostIncrement = true;
-    constexpr bool kXVariant = true;
-    constexpr bool kNotXVariant = false;
-    constexpr bool kNotQuad = false;
-
-    const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
-
-    for (auto strip_mine : {false, true}) {
-      for (auto post_increment : {false, true}) {
-        // .x variants.
-        auto subname = absl::StrCat(name_with_type, post_increment ? "P" : "",
-                                    "X", strip_mine ? "M" : "");
-        MemoryLoadStoreOpTestHelper<T>(subname, kNoLength, kNoStride,
-                                       strip_mine, post_increment, kXVariant,
-                                       is_load, kNotQuad);
-      }
-      // .xx variants
-      for (auto len_stride_post :
-           {std::tuple(false, false, true), std::tuple(false, true, false),
-            std::tuple(false, true, true), std::tuple(true, false, false),
-            std::tuple(true, false, true)}) {
-        auto has_length = std::get<0>(len_stride_post);
-        auto has_stride = std::get<1>(len_stride_post);
-        auto post_increment = std::get<2>(len_stride_post);
-        auto subname = absl::StrCat(name_with_type,
-                                    has_length   ? "L"
-                                    : has_stride ? "S"
-                                                 : "",
-                                    post_increment ? "P" : "", "XX",
-                                    strip_mine ? "M" : "");
-        MemoryLoadStoreOpTestHelper<T>(subname, has_length, has_stride,
-                                       strip_mine, post_increment, kNotXVariant,
-                                       is_load, kNotQuad);
-      }
-
-      // .tp variants.
-      auto subname =
-          absl::StrCat(name_with_type, "TP", "XX", strip_mine ? "M" : "");
-      MemoryLoadStoreOpTestHelper<T>(subname, kLength, kStride, strip_mine,
-                                     kPostIncrement, kNotXVariant, is_load,
-                                     kNotQuad);
-    }
-  }
-
-  template <typename T>
-  void StoreQuadOpTestHelper(absl::string_view name) {
-    const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
-    constexpr bool kNotLength = false;
-    constexpr bool kStride = true;
-    constexpr bool kNotXVariant = false;
-    constexpr bool kNotLoad = false;
-    constexpr bool kIsQuad = true;
-    for (auto strip_mine : {false, true}) {
-      for (auto post_increment : {false, true}) {
-        auto subname =
-            absl::StrCat(name_with_type, "S", post_increment ? "P" : "", "XX",
-                         strip_mine ? "M" : "");
-        MemoryLoadStoreOpTestHelper<T>(subname, kNotLength, kStride, strip_mine,
-                                       post_increment, kNotXVariant, kNotLoad,
-                                       kIsQuad);
-      }
-    }
-  }
-
-  template <typename T1, typename TNext1, typename... TNext>
-  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
-    MemoryLoadStoreOpTestHelper<T1>(name, is_load);
-    MemoryLoadStoreOpTestHelper<TNext1, TNext...>(name, is_load);
-  }
-
-  template <typename T1, typename TNext1, typename... TNext>
-  void StoreQuadOpTestHelper(absl::string_view name) {
-    StoreQuadOpTestHelper<T1>(name);
-    StoreQuadOpTestHelper<TNext1, TNext...>(name);
-  }
-
- protected:
-  template <typename T>
-  T GetDefaultMemoryValue(int address) {
-    T value = 0;
-    uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
-    for (int j = 0; j < sizeof(T); j++) {
-      ptr[j] = (address + j) & 0xff;
-    }
-    return value;
-  }
-
-  template <typename T>
-  T GetSavedMemoryValue(int address) {
-    auto *db = state_->db_factory()->Allocate<T>(1);
-    memory_->Load(address, db, nullptr, nullptr);
-    T data = db->template Get<T>(0);
-    db->DecRef();
-    return data;
-  }
-};
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VLd) {
-  MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VLd",
-                                                        /*is_load=*/true);
-}
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VSt) {
-  MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VSt",
-                                                        /*is_load=*/false);
-}
-
-TEST_F(KelvinVectorInstructionsMemoryTest, VStQ) {
-  StoreQuadOpTestHelper<int8_t, int16_t, int32_t>("VStQ");
-}
-
-class KelvinGetVlInstructionTest : public KelvinVectorInstructionsTest {
- public:
-  template <typename T>
-  void GetVlTestHelper() {
-    constexpr char kRdName[] = "x8";
-    constexpr uint32_t kMaxVlenInBytes = kelvin::sim::kVectorLengthInBits / 8;
-    auto instruction = CreateInstruction();
-    AppendRegisterOperands(
-        instruction.get(),
-        {kelvin::sim::test::kRs1Name, kelvin::sim::test::kRs2Name}, {kRdName});
-    for (auto strip_mine : {false, true}) {
-      for (auto is_rs1 : {false, true}) {
-        for (auto is_rs2 : {false, true}) {
-          uint32_t rs1_value = RandomValue();
-          uint32_t rs2_value = RandomValue();
-          SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, rs1_value},
-                                       {kelvin::sim::test::kRs2Name, rs2_value},
-                                       {kRdName, UINT32_MAX}});
-          instruction->set_semantic_function(
-              absl::bind_front(&KelvinGetVl<T>, strip_mine, is_rs1, is_rs2));
-          uint32_t expected_vlen =
-              kMaxVlenInBytes / sizeof(T) * (strip_mine ? 4 : 1);
-          if (is_rs1) {
-            expected_vlen = std::min(expected_vlen, rs1_value);
-          }
-          if (is_rs2) {
-            expected_vlen = std::min(expected_vlen, rs2_value);
-          }
-          // Execute instruction.
-          instruction->Execute(nullptr);
-          EXPECT_EQ(xreg_[8]->data_buffer()->Get<uint32_t>(0), expected_vlen)
-              << "Test failed with type "
-              << (sizeof(T) == 4 ? "W" : (sizeof(T) == 2 ? "H" : "B"))
-              << ", strip_mine: " << strip_mine << ", rs1_set: " << is_rs1
-              << ", rs2_set: " << is_rs2;
-        }
-      }
-    }
-  }
-
-  template <typename T1, typename TNext1, typename... TNext>
-  void GetVlTestHelper() {
-    GetVlTestHelper<T1>();
-    GetVlTestHelper<TNext1, TNext...>();
-  }
-
- protected:
-  // Create a random value in the valid range for the type.
-  uint32_t RandomValue() {
-    return absl::Uniform(absl::IntervalClosed, bitgen_,
-                         std::numeric_limits<uint32_t>::lowest(),
-                         std::numeric_limits<uint32_t>::max());
-  }
-};
-
-TEST_F(KelvinGetVlInstructionTest, GetVl) {
-  GetVlTestHelper<int8_t, int16_t, int32_t>();
-}
-
 }  // namespace
diff --git a/sim/test/kelvin_vector_instructions_test_base.h b/sim/test/kelvin_vector_instructions_test_base.h
index 9afde0b..7335a28 100644
--- a/sim/test/kelvin_vector_instructions_test_base.h
+++ b/sim/test/kelvin_vector_instructions_test_base.h
@@ -76,6 +76,23 @@
     }
   }
 
+  template <typename T>
+  absl::string_view KelvinTestTypeSuffix() {
+    absl::string_view type_suffix = "Unknown";
+    switch (sizeof(T)) {
+      case 4:
+        type_suffix = "W";
+        break;
+      case 2:
+        type_suffix = "H";
+        break;
+      case 1:
+        type_suffix = "B";
+        break;
+    }
+    return type_suffix;
+  }
+
   template <typename Vd, typename Vs1, typename Vs2>
   static std::pair<Vs1, Vs2> CommonBinaryOpArgsGetter(
       int num_ops, int op_num, int dest_reg_sub_index, int element_index,
diff --git a/sim/test/kelvin_vector_memory_instructions_test.cc b/sim/test/kelvin_vector_memory_instructions_test.cc
new file mode 100644
index 0000000..4b4fc82
--- /dev/null
+++ b/sim/test/kelvin_vector_memory_instructions_test.cc
@@ -0,0 +1,420 @@
+#include "sim/kelvin_vector_memory_instructions.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "sim/test/kelvin_vector_instructions_test_base.h"
+#include "googletest/include/gtest/gtest.h"
+#include "absl/functional/bind_front.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "mpact/sim/generic/instruction.h"
+
+// This file contains the tests for testing kelvin vector memory instructions.
+
+namespace {
+
+using mpact::sim::generic::Instruction;
+
+// Semantic functions.
+using kelvin::sim::KelvinGetVl;
+using kelvin::sim::KelvinVLd;
+using kelvin::sim::KelvinVLdRegWrite;
+using kelvin::sim::KelvinVSt;
+using kelvin::sim::KelvinVStQ;
+
+class KelvinVectorMemoryInstructionsTest
+    : public kelvin::sim::test::KelvinVectorInstructionsTestBase {
+ public:
+  template <typename T>
+  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool has_length,
+                                   bool has_stride, bool strip_mine,
+                                   bool post_increment, bool x_variant,
+                                   bool is_load, bool is_quad) {
+    InstructionPtr child_instruction(
+        new Instruction(next_instruction_address_, state_),
+        [](Instruction *inst) { inst->DecRef(); });
+    child_instruction->set_size(4);
+    auto instruction = CreateInstruction();
+
+    if (is_load) {
+      child_instruction->set_semantic_function(
+          absl::bind_front(&KelvinVLdRegWrite<T>, strip_mine));
+      instruction->set_semantic_function(
+          absl::bind_front(&KelvinVLd<T>, has_length, has_stride, strip_mine));
+      instruction->AppendChild(child_instruction.get());
+    } else {
+      if (is_quad) {
+        instruction->set_semantic_function(
+            absl::bind_front(&KelvinVStQ<T>, strip_mine));
+      } else {
+        instruction->set_semantic_function(absl::bind_front(
+            &KelvinVSt<T>, has_length, has_stride, strip_mine));
+      }
+    }
+
+    // Setup source and child instruction operands.
+    const uint32_t num_ops = strip_mine ? 4 : 1;
+    if (is_load) {
+      AppendVectorRegisterOperands(
+          child_instruction.get(), num_ops, 1 /* src1_widen_factor */, {}, {},
+          false /* widen_dst */, {kelvin::sim::test::kVd});
+    } else {  // Store
+      AppendVectorRegisterOperands(
+          instruction.get(), num_ops, 1 /* src1_widen_factor */,
+          kelvin::sim::test::kVd, {}, false /* widen_dst */, {});
+    }
+    AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name},
+                           {});
+    if (!x_variant) {
+      AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs2Name},
+                             {});
+    }
+
+    if (post_increment) {
+      AppendRegisterOperands(instruction.get(), {},
+                             {kelvin::sim::test::kRs1Name});
+    }
+
+    // x variant can't have length or stride fields.
+    if (x_variant && (has_length || has_stride)) {
+      GTEST_FAIL();
+    }
+
+    // xx variant can't have no length, no stride, and no post_increment
+    // encoding
+    if (!x_variant && !has_length && !has_stride && !post_increment) {
+      GTEST_FAIL();
+    }
+
+    // length and stride fields can't coexist without post_increment
+    if (has_length && has_stride && !post_increment) {
+      GTEST_FAIL();
+    }
+
+    // Quad store need to have stride specified and no length
+    if (is_quad && is_load) {
+      GTEST_FAIL();
+    }
+    if ((is_quad && has_length) || (is_quad && !has_stride)) {
+      GTEST_FAIL();
+    }
+    const uint32_t vector_length_in_bytes = state_->vector_length() / 8;
+    const uint32_t vd_size = vector_length_in_bytes / sizeof(T);
+    const uint32_t len_or_strides[] = {0,       1,           vd_size - 1,
+                                       vd_size, 2 * vd_size, 4 * vd_size};
+
+    // Check with different values for length and stride if applicable.
+    for (int test = 0;
+         test < (has_length || has_stride ? std::size(len_or_strides) : 1);
+         test++) {
+      // Store stride can't be smaller than vd_size
+      if ((is_quad && len_or_strides[test] < vd_size / 4) ||
+          (!is_load && has_stride && len_or_strides[test] < vd_size)) {
+        continue;
+      }
+      // Set input register values.
+      SetRegisterValues<uint32_t>(
+          {{kelvin::sim::test::kRs1Name, kelvin::sim::test::kDataLoadAddress}});
+
+      if (!x_variant) {
+        SetRegisterValues<uint32_t>(
+            {{kelvin::sim::test::kRs2Name, len_or_strides[test]}});
+      }
+
+      // Fill vector register(s) with random values.
+      std::vector<T> vd_value(vector_length_in_bytes / sizeof(T) * num_ops);
+      auto vd_span = absl::Span<T>(vd_value);
+      FillArrayWithRandomValues<T>(vd_span);
+      for (int i = 0; i < num_ops; i++) {
+        auto vd_name = absl::StrCat("v", kelvin::sim::test::kVd + i);
+        SetVectorRegisterValues<T>(
+            {{vd_name, vd_span.subspan(vd_size * i, vd_size)}});
+      }
+
+      // Execute instruction.
+      instruction->Execute();
+
+      // Compute memory values. For load test it is the expected output; for
+      // store test it is the actual output.
+      std::vector<T> memory_values(vd_size * num_ops);
+      uint32_t addr = kelvin::sim::test::kDataLoadAddress;
+      uint32_t rs2_value = len_or_strides[test];
+      uint32_t count = vd_size * num_ops;
+      if (has_length) {
+        count = std::min(count, rs2_value);
+      }
+      uint32_t left = count;
+      for (int op_num = 0; op_num < num_ops; op_num++) {
+        const int n = std::min(vd_size, left);
+        if (is_quad) {
+          const uint32_t quad_size = vd_size / 4;
+          for (int i = 0; i < 4; ++i) {
+            for (int j = 0; j < quad_size; ++j) {
+              memory_values[op_num * vd_size + i * quad_size + j] =
+                  GetSavedMemoryValue<T>(addr +
+                                         (i * quad_size + j) * sizeof(T));
+            }
+            // Stride increase per quad_size.
+            addr += rs2_value * sizeof(T);
+          }
+        } else {
+          for (int i = 0; i < vd_size; ++i) {
+            if (is_load) {
+              memory_values[op_num * vd_size + i] =
+                  i < n ? GetDefaultMemoryValue<T>(addr + i * sizeof(T)) : 0;
+            } else {
+              memory_values[op_num * vd_size + i] =
+                  i < n ? GetSavedMemoryValue<T>(addr + i * sizeof(T)) : 0;
+            }
+          }
+          left -= n;
+          if (has_stride) {
+            addr += rs2_value * sizeof(T);
+          } else {
+            addr += n * sizeof(T);
+          }
+        }
+      }
+
+      uint32_t expected_rs1_value = kelvin::sim::test::kDataLoadAddress;
+      if (post_increment && count) {
+        if (has_length && has_stride) {  // .tp
+          expected_rs1_value += vd_size * sizeof(T);
+        } else if (!has_length && !has_stride && x_variant) {  // .p.x
+          expected_rs1_value += vd_size * sizeof(T) * num_ops;
+        } else if (has_length) {  // .lp
+          expected_rs1_value += count * sizeof(T);
+        } else if (has_stride) {  // .sp
+          const uint32_t quad_scale = is_quad ? 4 : 1;
+          expected_rs1_value += rs2_value * sizeof(T) * num_ops * quad_scale;
+        } else {  // .p.xx
+          expected_rs1_value += rs2_value * sizeof(T);
+        }
+      }
+
+      // Check result
+      left = count;
+      for (int op_num = 0; op_num < num_ops; op_num++) {
+        auto vreg_num = kelvin::sim::test::kVd + op_num;
+        auto test_vreg = vreg_[vreg_num];
+        auto vreg_span = test_vreg->data_buffer()->Get<T>();
+        if (is_load) {
+          for (int element_index = 0; element_index < vd_size;
+               element_index++) {
+            auto vreg_element_index = op_num * vd_size + element_index;
+            EXPECT_EQ(memory_values[vreg_element_index],
+                      vreg_span[element_index])
+                << absl::StrCat(name, "[", vreg_element_index, "] != reg[",
+                                vreg_num, "*", element_index, "]");
+          }
+        } else {  // Store
+          const int n = std::min(vd_size, left);
+          for (int element_index = 0;
+               element_index < vd_size && element_index < n; element_index++) {
+            auto total_element_index = op_num * vd_size + element_index;
+            EXPECT_EQ(memory_values[total_element_index],
+                      vreg_span[element_index])
+                << absl::StrCat(name, " mem at ", total_element_index,
+                                " != vreg[", vreg_num, "][", element_index,
+                                "]");
+          }
+          left -= n;
+        }
+      }
+
+      if (post_increment) {
+        // Check rs1 value.
+        auto *reg = state_
+                        ->GetRegister<kelvin::sim::test::RV32Register>(
+                            kelvin::sim::test::kRs1Name)
+                        .first;
+        EXPECT_EQ(expected_rs1_value, reg->data_buffer()->Get<uint32_t>()[0])
+            << absl::StrCat(name, " post incremented rs1 is incorrect.");
+      }
+    }
+  }
+
+  template <typename T>
+  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
+    constexpr bool kNoLength = false;
+    constexpr bool kLength = true;
+    constexpr bool kNoStride = false;
+    constexpr bool kStride = true;
+    constexpr bool kPostIncrement = true;
+    constexpr bool kXVariant = true;
+    constexpr bool kNotXVariant = false;
+    constexpr bool kNotQuad = false;
+
+    const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
+
+    for (auto strip_mine : {false, true}) {
+      for (auto post_increment : {false, true}) {
+        // .x variants.
+        auto subname = absl::StrCat(name_with_type, post_increment ? "P" : "",
+                                    "X", strip_mine ? "M" : "");
+        MemoryLoadStoreOpTestHelper<T>(subname, kNoLength, kNoStride,
+                                       strip_mine, post_increment, kXVariant,
+                                       is_load, kNotQuad);
+      }
+      // .xx variants
+      for (auto len_stride_post :
+           {std::tuple(false, false, true), std::tuple(false, true, false),
+            std::tuple(false, true, true), std::tuple(true, false, false),
+            std::tuple(true, false, true)}) {
+        auto has_length = std::get<0>(len_stride_post);
+        auto has_stride = std::get<1>(len_stride_post);
+        auto post_increment = std::get<2>(len_stride_post);
+        auto subname = absl::StrCat(name_with_type,
+                                    has_length   ? "L"
+                                    : has_stride ? "S"
+                                                 : "",
+                                    post_increment ? "P" : "", "XX",
+                                    strip_mine ? "M" : "");
+        MemoryLoadStoreOpTestHelper<T>(subname, has_length, has_stride,
+                                       strip_mine, post_increment, kNotXVariant,
+                                       is_load, kNotQuad);
+      }
+
+      // .tp variants.
+      auto subname =
+          absl::StrCat(name_with_type, "TP", "XX", strip_mine ? "M" : "");
+      MemoryLoadStoreOpTestHelper<T>(subname, kLength, kStride, strip_mine,
+                                     kPostIncrement, kNotXVariant, is_load,
+                                     kNotQuad);
+    }
+  }
+
+  template <typename T>
+  void StoreQuadOpTestHelper(absl::string_view name) {
+    const auto name_with_type = absl::StrCat(name, KelvinTestTypeSuffix<T>());
+    constexpr bool kNotLength = false;
+    constexpr bool kStride = true;
+    constexpr bool kNotXVariant = false;
+    constexpr bool kNotLoad = false;
+    constexpr bool kIsQuad = true;
+    for (auto strip_mine : {false, true}) {
+      for (auto post_increment : {false, true}) {
+        auto subname =
+            absl::StrCat(name_with_type, "S", post_increment ? "P" : "", "XX",
+                         strip_mine ? "M" : "");
+        MemoryLoadStoreOpTestHelper<T>(subname, kNotLength, kStride, strip_mine,
+                                       post_increment, kNotXVariant, kNotLoad,
+                                       kIsQuad);
+      }
+    }
+  }
+
+  template <typename T1, typename TNext1, typename... TNext>
+  void MemoryLoadStoreOpTestHelper(absl::string_view name, bool is_load) {
+    MemoryLoadStoreOpTestHelper<T1>(name, is_load);
+    MemoryLoadStoreOpTestHelper<TNext1, TNext...>(name, is_load);
+  }
+
+  template <typename T1, typename TNext1, typename... TNext>
+  void StoreQuadOpTestHelper(absl::string_view name) {
+    StoreQuadOpTestHelper<T1>(name);
+    StoreQuadOpTestHelper<TNext1, TNext...>(name);
+  }
+
+ protected:
+  template <typename T>
+  T GetDefaultMemoryValue(int address) {
+    T value = 0;
+    uint8_t *ptr = reinterpret_cast<uint8_t *>(&value);
+    for (int j = 0; j < sizeof(T); j++) {
+      ptr[j] = (address + j) & 0xff;
+    }
+    return value;
+  }
+
+  template <typename T>
+  T GetSavedMemoryValue(int address) {
+    auto *db = state_->db_factory()->Allocate<T>(1);
+    memory_->Load(address, db, nullptr, nullptr);
+    T data = db->template Get<T>(0);
+    db->DecRef();
+    return data;
+  }
+};
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VLd) {
+  MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VLd",
+                                                        /*is_load=*/true);
+}
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VSt) {
+  MemoryLoadStoreOpTestHelper<int8_t, int16_t, int32_t>("VSt",
+                                                        /*is_load=*/false);
+}
+
+TEST_F(KelvinVectorMemoryInstructionsTest, VStQ) {
+  StoreQuadOpTestHelper<int8_t, int16_t, int32_t>("VStQ");
+}
+
+class KelvinGetVlInstructionTest
+    : public kelvin::sim::test::KelvinVectorInstructionsTestBase {
+ public:
+  template <typename T>
+  void GetVlTestHelper() {
+    constexpr char kRdName[] = "x8";
+    constexpr uint32_t kMaxVlenInBytes = kelvin::sim::kVectorLengthInBits / 8;
+    auto instruction = CreateInstruction();
+    AppendRegisterOperands(
+        instruction.get(),
+        {kelvin::sim::test::kRs1Name, kelvin::sim::test::kRs2Name}, {kRdName});
+    for (auto strip_mine : {false, true}) {
+      for (auto is_rs1 : {false, true}) {
+        for (auto is_rs2 : {false, true}) {
+          uint32_t rs1_value = RandomValue();
+          uint32_t rs2_value = RandomValue();
+          SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, rs1_value},
+                                       {kelvin::sim::test::kRs2Name, rs2_value},
+                                       {kRdName, UINT32_MAX}});
+          instruction->set_semantic_function(
+              absl::bind_front(&KelvinGetVl<T>, strip_mine, is_rs1, is_rs2));
+          uint32_t expected_vlen =
+              kMaxVlenInBytes / sizeof(T) * (strip_mine ? 4 : 1);
+          if (is_rs1) {
+            expected_vlen = std::min(expected_vlen, rs1_value);
+          }
+          if (is_rs2) {
+            expected_vlen = std::min(expected_vlen, rs2_value);
+          }
+          // Execute instruction.
+          instruction->Execute(nullptr);
+          EXPECT_EQ(xreg_[8]->data_buffer()->Get<uint32_t>(0), expected_vlen)
+              << "Test failed with type "
+              << (sizeof(T) == 4 ? "W" : (sizeof(T) == 2 ? "H" : "B"))
+              << ", strip_mine: " << strip_mine << ", rs1_set: " << is_rs1
+              << ", rs2_set: " << is_rs2;
+        }
+      }
+    }
+  }
+
+  template <typename T1, typename TNext1, typename... TNext>
+  void GetVlTestHelper() {
+    GetVlTestHelper<T1>();
+    GetVlTestHelper<TNext1, TNext...>();
+  }
+
+ protected:
+  // Create a random value in the valid range for the type.
+  uint32_t RandomValue() {
+    return absl::Uniform(absl::IntervalClosed, bitgen_,
+                         std::numeric_limits<uint32_t>::lowest(),
+                         std::numeric_limits<uint32_t>::max());
+  }
+};
+
+TEST_F(KelvinGetVlInstructionTest, GetVl) {
+  GetVlTestHelper<int8_t, int16_t, int32_t>();
+}
+
+}  // namespace