No public description

PiperOrigin-RevId: 971363808
Change-Id: Id6365febbeb3c15eba89bb4c7939d2afab208917
diff --git a/riscv/riscv_vector_memory_instructions.cc b/riscv/riscv_vector_memory_instructions.cc
index 72e8d2a..5366199 100644
--- a/riscv/riscv_vector_memory_instructions.cc
+++ b/riscv/riscv_vector_memory_instructions.cc
@@ -73,14 +73,14 @@
     auto* dest_db = dest_op->CopyDataBuffer(reg);
     auto dest_span = dest_db->Get<T>();
     // Write data into register subject to masking.
-    int count = std::min(elements_per_vector - item_index, value_count);
-    for (int i = item_index; i < count; i++) {
-      if (masks[load_data_index + i]) {
-        dest_span[i] = values[load_data_index + i];
+    int max_entry = std::min(elements_per_vector, item_index + value_count);
+    for (int i = item_index; i < max_entry; i++) {
+      if (masks[load_data_index]) {
+        dest_span[i] = values[load_data_index];
       }
+      load_data_index++;
+      value_count--;
     }
-    value_count -= count;
-    load_data_index += count;
     dest_db->Submit(0);
     item_index = 0;
   }
@@ -355,8 +355,12 @@
     return;
   }
 
-  // Compute the number of bytes and elements to be loaded.
   int num_elements = rv_vector->vector_length();
+  if (start >= num_elements) {
+    rv_vector->clear_vstart();
+    return;
+  }
+  // Compute the number of bytes and elements to be loaded.
   int num_elements_loaded = num_elements - start;
   int num_bytes_loaded = num_elements_loaded * element_width;
 
@@ -373,9 +377,12 @@
   auto src_masks = src_mask_op->GetRegister(0)->data_buffer()->Get<uint8_t>();
 
   // Allocate a byte mask data buffer for the load.
-  auto* mask_db = db_factory->Allocate<bool>(num_elements);
+  auto* mask_db = db_factory->Allocate<bool>(num_elements_loaded);
   auto masks = mask_db->Get<bool>();
 
+  auto* rv32_state = static_cast<RiscVState*>(inst->state());
+  bool is_rv32 = (rv32_state->xlen() == RiscVXlen::RV32);
+
   // Convert the bit masks to byte masks and compute the element addresses.
   // The index elements are treated as unsigned values.
   for (int i = start; i < num_elements; i++) {
@@ -401,14 +408,17 @@
         rv_vector->set_vector_exception();
         break;
     }
-    addresses[i - start] = base + offset;
+    uint64_t addr = base + offset;
+    if (is_rv32) {
+      addr = static_cast<uint32_t>(addr);
+    }
+    addresses[i - start] = addr;
     masks[i - start] = ((src_masks[mask_index] >> mask_offset) & 0b1) != 0;
   }
 
   // Set up context and submit load.
   auto* context = new VectorLoadContext(value_db, mask_db, element_width, start,
                                         rv_vector->vector_length());
-  auto* rv32_state = static_cast<RiscVState*>(inst->state());
   value_db->set_latency(0);
   rv32_state->LoadMemory(inst, address_db, mask_db, element_width, value_db,
                          inst->child(), context);
@@ -429,13 +439,20 @@
   auto* rv_vector = static_cast<RiscVState*>(inst->state())->rv_vector();
   // Get base address.
   uint64_t base = GetInstructionSource<uint64_t>(inst, 0);
+  int start = rv_vector->vstart();
   int num_elements =
       rv_vector->vector_register_byte_length() * num_regs / element_width_bytes;
+  if (start >= num_elements) {
+    rv_vector->clear_vstart();
+    return;
+  }
+  int num_elements_loaded = num_elements - start;
   // Allocate data buffers.
   auto* db_factory = inst->state()->db_factory();
-  auto* data_db = db_factory->Allocate(num_elements * element_width_bytes);
-  auto* address_db = db_factory->Allocate<uint64_t>(num_elements);
-  auto* mask_db = db_factory->Allocate<bool>(num_elements);
+  auto* data_db =
+      db_factory->Allocate(num_elements_loaded * element_width_bytes);
+  auto* address_db = db_factory->Allocate<uint64_t>(num_elements_loaded);
+  auto* mask_db = db_factory->Allocate<bool>(num_elements_loaded);
   // Get spans for addresses and masks.
   auto addresses = address_db->Get<uint64_t>();
   auto masks = mask_db->Get<bool>();
@@ -446,14 +463,14 @@
   // The SEW is the width of vector element of the vector register, and the
   // element width here is the width of the data being loaded, it may differ
   // from SEW.
-  for (int i = 0; i < num_elements; i++) {
-    addresses[i] = base + i * element_width_bytes;
-    masks[i] = true;
+  for (int i = start; i < num_elements; i++) {
+    addresses[i - start] = base + i * element_width_bytes;
+    masks[i - start] = true;
   }
 
   // Set up context and submit load.
   auto* context = new VectorLoadContext(data_db, mask_db, element_width_bytes,
-                                        0, num_elements);
+                                        start, num_elements);
   auto* rv32_state = static_cast<RiscVState*>(inst->state());
   data_db->set_latency(0);
   rv32_state->LoadMemory(inst, address_db, mask_db, element_width_bytes,
@@ -870,7 +887,12 @@
   auto* rv_vector = static_cast<RiscVState*>(inst->state())->rv_vector();
   // Compute base address.
   int start = rv_vector->vstart();
-  int num_elements = rv_vector->vector_length() - start;
+  int num_elements = rv_vector->vector_length();
+  if (start >= num_elements) {
+    rv_vector->clear_vstart();
+    return;
+  }
+  int num_elements_stored = num_elements - start;
   int element_width = rv_vector->selected_element_width();
   int lmul8 = rv_vector->vector_length_multiplier();
   int index_emul = index_width * lmul8 / element_width;
@@ -886,9 +908,9 @@
 
   // Allocate data buffers.
   auto* db_factory = inst->state()->db_factory();
-  auto* address_db = db_factory->Allocate<uint64_t>(num_elements);
-  auto* value_db = db_factory->Allocate(num_elements * element_width);
-  auto* mask_db = db_factory->Allocate<bool>(num_elements);
+  auto* address_db = db_factory->Allocate<uint64_t>(num_elements_stored);
+  auto* value_db = db_factory->Allocate(num_elements_stored * element_width);
+  auto* mask_db = db_factory->Allocate<bool>(num_elements_stored);
 
   // Get the source mask (stored in a single vector register).
   auto* src_mask_op = static_cast<RV32VectorSourceOperand*>(inst->Source(3));
@@ -898,6 +920,9 @@
   auto masks = mask_db->Get<bool>();
   auto addresses = address_db->Get<uint64_t>();
 
+  auto* rv32_state = static_cast<RiscVState*>(inst->state());
+  bool is_rv32 = (rv32_state->xlen() == RiscVXlen::RV32);
+
   // Convert the bit masks to byte masks and compute the element addresses.
   for (int i = start; i < num_elements; i++) {
     int mask_index = i >> 3;
@@ -922,20 +947,28 @@
         LOG(ERROR) << "Illegal value for index type width";
         return;
     }
-    addresses[i - start] = base + offset;
+    uint64_t addr = base + offset;
+    if (is_rv32) {
+      addr = static_cast<uint32_t>(addr);
+    }
+    addresses[i - start] = addr;
     masks[i - start] = ((src_masks[mask_index] >> mask_offset) & 0b1) != 0;
     switch (element_width) {
       case 1:
-        value_db->Set<uint8_t>(i, GetInstructionSource<uint8_t>(inst, 0, i));
+        value_db->Set<uint8_t>(i - start,
+                               GetInstructionSource<uint8_t>(inst, 0, i));
         break;
       case 2:
-        value_db->Set<uint16_t>(i, GetInstructionSource<uint16_t>(inst, 0, i));
+        value_db->Set<uint16_t>(i - start,
+                                GetInstructionSource<uint16_t>(inst, 0, i));
         break;
       case 4:
-        value_db->Set<uint32_t>(i, GetInstructionSource<uint32_t>(inst, 0, i));
+        value_db->Set<uint32_t>(i - start,
+                                GetInstructionSource<uint32_t>(inst, 0, i));
         break;
       case 8:
-        value_db->Set<uint64_t>(i, GetInstructionSource<uint64_t>(inst, 0, i));
+        value_db->Set<uint64_t>(i - start,
+                                GetInstructionSource<uint64_t>(inst, 0, i));
         break;
       default:
         offset = 0;
@@ -946,7 +979,6 @@
   }
 
   // Set up context and submit store
-  auto* rv32_state = static_cast<RiscVState*>(inst->state());
   rv32_state->StoreMemory(inst, address_db, mask_db, element_width, value_db);
   address_db->DecRef();
   mask_db->DecRef();
@@ -956,25 +988,31 @@
 
 void VsRegister(int num_regs, const Instruction* inst) {
   auto* rv_vector = static_cast<RiscVState*>(inst->state())->rv_vector();
+  int start = rv_vector->vstart();
   uint64_t base = GetInstructionSource<uint64_t>(inst, 1);
   int num_elements =
-      rv_vector->vector_register_byte_length() * num_regs / sizeof(uint64_t);
+      rv_vector->vector_register_byte_length() * num_regs / sizeof(uint8_t);
+  if (start >= num_elements) {
+    rv_vector->clear_vstart();
+    return;
+  }
+  int num_elements_stored = num_elements - start;
   // Allocate data buffers.
   auto* db_factory = inst->state()->db_factory();
-  auto* data_db = db_factory->Allocate<uint64_t>(num_elements);
-  auto* address_db = db_factory->Allocate<uint64_t>(num_elements);
-  auto* mask_db = db_factory->Allocate<bool>(num_elements);
+  auto* data_db = db_factory->Allocate<uint8_t>(num_elements_stored);
+  auto* address_db = db_factory->Allocate<uint64_t>(num_elements_stored);
+  auto* mask_db = db_factory->Allocate<bool>(num_elements_stored);
   // Get the address, mask, and data spans.
   auto addresses = address_db->Get<uint64_t>();
   auto masks = mask_db->Get<bool>();
-  auto data = data_db->Get<uint64_t>();
-  for (int i = 0; i < num_elements; i++) {
-    addresses[i] = base + i * sizeof(uint64_t);
-    masks[i] = true;
-    data[i] = GetInstructionSource<uint64_t>(inst, 0, i);
+  auto data = data_db->Get<uint8_t>();
+  for (int i = start; i < num_elements; i++) {
+    addresses[i - start] = base + i * sizeof(uint8_t);
+    masks[i - start] = true;
+    data[i - start] = GetInstructionSource<uint8_t>(inst, 0, i);
   }  // Submit store.
   auto* rv32_state = static_cast<RiscVState*>(inst->state());
-  rv32_state->StoreMemory(inst, address_db, mask_db, sizeof(uint64_t), data_db);
+  rv32_state->StoreMemory(inst, address_db, mask_db, sizeof(uint8_t), data_db);
   address_db->DecRef();
   mask_db->DecRef();
   data_db->DecRef();
diff --git a/riscv/test/riscv_vector_memory_instructions_test.cc b/riscv/test/riscv_vector_memory_instructions_test.cc
index c906d3c..a583fda 100644
--- a/riscv/test/riscv_vector_memory_instructions_test.cc
+++ b/riscv/test/riscv_vector_memory_instructions_test.cc
@@ -900,7 +900,9 @@
 
       // Skip if the number of values is greater than the offset representation.
       // This only happens for uint8_t.
-      if (num_values > 256) continue;
+      if (sizeof(IndexType) == 1 && (num_values * sizeof(ValueType) > 256)) {
+        continue;
+      }
 
       // Check the index vector length.
       if ((index_emul8 == 0) || (index_emul8 > 64)) {
@@ -915,7 +917,7 @@
         int reg = i / index_values_per_reg;
         int element = i % index_values_per_reg;
         vreg_[kVs2 + reg]->data_buffer()->Set<IndexType>(
-            element, IndexValue<IndexType>(i));
+            element, IndexValue<ValueType>(i));
       }
 
       // Execute instruction.
@@ -926,7 +928,7 @@
 
       // Check register values.
       for (int i = 0; i < 8 * kVectorLengthInBytes / sizeof(ValueType); i++) {
-        uint64_t address = kDataStoreAddress + IndexValue<IndexType>(i);
+        uint64_t address = kDataStoreAddress + IndexValue<ValueType>(i);
         state_->LoadMemory(instruction_, address, data_db, nullptr, nullptr);
         int mask_index = i / 8;
         int mask_offset = i % 8;
@@ -1971,63 +1973,63 @@
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed8_16) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint8_t, uint16_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed8_32) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint8_t, uint32_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed8_64) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint8_t, uint64_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed16_8) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint16_t, uint8_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed16_16) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint16_t, uint16_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed16_32) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint16_t, uint32_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed16_64) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint16_t, uint64_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed32_8) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint32_t, uint8_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed32_16) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint32_t, uint16_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed32_32) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint32_t, uint32_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed32_64) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint32_t, uint64_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed64_8) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint64_t, uint8_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed64_16) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint64_t, uint16_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed64_32) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint64_t, uint32_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsIndexed64_64) {
-  VectorStoreIndexedHelper<uint8_t, uint8_t>();
+  VectorStoreIndexedHelper<uint64_t, uint64_t>();
 }
 
 TEST_F(RV32VInstructionsTest, VsRegister) {
@@ -2169,4 +2171,403 @@
 TEST_F(RV32VInstructionsTest, VsSegmentIndexed64_64) {
   VectorStoreIndexedSegmentHelper<uint64_t, int64_t>();
 }
+
+// Test WriteBackLoadData with non-zero vstart.
+TEST_F(RV32VInstructionsTest, WriteBackLoadData_RespectsVstart) {
+  // Configure vector unit for 32-bit elements, LMUL=1, vl=16.
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/16);
+  ASSERT_EQ(rv_vector_->vector_length(), 16);
+  rv_vector_->set_vstart(4);
+
+  AppendRegisterOperands({kRs1Name, kRs2Name}, {});
+  AppendVectorRegisterOperands({kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VlStrided, sizeof(uint32_t)));
+  SetChildInstruction();
+  SetChildSemanticFunction(&VlChild);
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+  SetRegisterValues<int32_t>({{kRs2Name, sizeof(uint32_t)}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  // Initialize destination register with sentinel 0xDEADBEEF.
+  for (int i = 0; i < kVectorLengthInBytes / sizeof(uint32_t); i++) {
+    vreg_[kVd]->data_buffer()->Set<uint32_t>(i, 0xDEADBEEF);
+  }
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint32_t>();
+  // Elements 0..3 should be untouched.
+  for (int i = 0; i < 4; i++) {
+    EXPECT_EQ(span[i], 0xDEADBEEF) << "element: " << i;
+  }
+  // Elements 4..15 should be loaded from memory.
+  for (int i = 4; i < 16; i++) {
+    uint32_t expected = ComputeValue<uint32_t>(4096 + i * sizeof(uint32_t));
+    EXPECT_EQ(span[i], expected) << "element: " << i;
+  }
+}
+
+TEST_F(RV32VInstructionsTest, VlIndexed_RespectsVstart) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/16);
+  ASSERT_EQ(rv_vector_->vector_length(), 16);
+  rv_vector_->set_vstart(4);
+
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VlIndexed, sizeof(uint32_t)));
+  SetChildInstruction();
+  SetChildSemanticFunction(&VlChild);
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  for (int i = 0; i < 16; i++) {
+    vreg_[kVs2]->data_buffer()->Set<uint32_t>(i, i * 4);
+    vreg_[kVd]->data_buffer()->Set<uint32_t>(i, 0xDEADBEEF);
+  }
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint32_t>();
+  for (int i = 0; i < 4; i++) {
+    EXPECT_EQ(span[i], 0xDEADBEEF) << "element: " << i;
+  }
+  for (int i = 4; i < 16; i++) {
+    uint32_t expected = ComputeValue<uint32_t>(4096 + i * 4);
+    EXPECT_EQ(span[i], expected) << "element: " << i;
+  }
+}
+
+TEST_F(RV32VInstructionsTest, VlIndexed_VstartGteVl) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/16);
+  ASSERT_EQ(rv_vector_->vector_length(), 16);
+  rv_vector_->set_vstart(16);
+
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VlIndexed, sizeof(uint32_t)));
+  SetChildInstruction();
+  SetChildSemanticFunction(&VlChild);
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  for (int i = 0; i < 16; i++) {
+    vreg_[kVs2]->data_buffer()->Set<uint32_t>(i, i * 4);
+    vreg_[kVd]->data_buffer()->Set<uint32_t>(i, 0xDEADBEEF);
+  }
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint32_t>();
+  for (int i = 0; i < 16; i++) {
+    EXPECT_EQ(span[i], 0xDEADBEEF) << "element: " << i;
+  }
+}
+
+TEST_F(RV32VInstructionsTest, VlIndexed_Rv32AddressWrap) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/1);
+  ASSERT_EQ(rv_vector_->vector_length(), 1);
+
+  // Set memory at wrapped address 0x00000100.
+  auto* mem_db = state_->db_factory()->Allocate<uint32_t>(1);
+  mem_db->Set<uint32_t>(0, 0x12345678);
+  memory_->Store(0x00000100, mem_db);
+  mem_db->DecRef();
+
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VlIndexed, sizeof(uint32_t)));
+  SetChildInstruction();
+  SetChildSemanticFunction(&VlChild);
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+
+  // Base = 0xFFFFFF00, Offset = 0x200 -> Sum wraps to 0x00000100 in 32-bit.
+  SetRegisterValues<uint32_t>({{kRs1Name, 0xFFFFFF00}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+  vreg_[kVs2]->data_buffer()->Set<uint32_t>(0, 0x200);
+  vreg_[kVd]->data_buffer()->Set<uint32_t>(0, 0);
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint32_t>();
+  EXPECT_EQ(span[0], 0x12345678);
+}
+
+TEST_F(RV32VInstructionsTest, VsIndexed_RespectsVstart) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/16);
+  ASSERT_EQ(rv_vector_->vector_length(), 16);
+  rv_vector_->set_vstart(4);
+
+  AppendVectorRegisterOperands({kVs1}, {});
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VsIndexed, sizeof(uint32_t)));
+
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  for (int i = 0; i < 16; i++) {
+    vreg_[kVs1]->data_buffer()->Set<uint32_t>(i, 0x100 + i);
+    vreg_[kVs2]->data_buffer()->Set<uint32_t>(i, i * sizeof(uint32_t));
+  }
+
+  // Clear memory.
+  auto* zero_db = state_->db_factory()->Allocate<uint32_t>(16);
+  std::memset(zero_db->raw_ptr(), 0, 16 * sizeof(uint32_t));
+  state_->StoreMemory(instruction_, kDataStoreAddress, zero_db);
+  zero_db->DecRef();
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto* load_db = state_->db_factory()->Allocate<uint32_t>(16);
+  state_->LoadMemory(instruction_, kDataStoreAddress, load_db, nullptr,
+                     nullptr);
+  auto span = load_db->Get<uint32_t>();
+  for (int i = 0; i < 4; i++) {
+    EXPECT_EQ(span[i], 0) << "element: " << i;
+  }
+  for (int i = 4; i < 16; i++) {
+    EXPECT_EQ(span[i], 0x100 + i) << "element: " << i;
+  }
+  load_db->DecRef();
+}
+
+TEST_F(RV32VInstructionsTest, VsIndexed_VstartGteVl) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/16);
+  ASSERT_EQ(rv_vector_->vector_length(), 16);
+  rv_vector_->set_vstart(16);
+
+  AppendVectorRegisterOperands({kVs1}, {});
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VsIndexed, sizeof(uint32_t)));
+
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  for (int i = 0; i < 16; i++) {
+    vreg_[kVs1]->data_buffer()->Set<uint32_t>(i, 0x100 + i);
+    vreg_[kVs2]->data_buffer()->Set<uint32_t>(i, i * sizeof(uint32_t));
+  }
+
+  // Clear memory.
+  auto* zero_db = state_->db_factory()->Allocate<uint32_t>(16);
+  std::memset(zero_db->raw_ptr(), 0, 16 * sizeof(uint32_t));
+  state_->StoreMemory(instruction_, kDataStoreAddress, zero_db);
+  zero_db->DecRef();
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto* load_db = state_->db_factory()->Allocate<uint32_t>(16);
+  state_->LoadMemory(instruction_, kDataStoreAddress, load_db, nullptr,
+                     nullptr);
+  auto span = load_db->Get<uint32_t>();
+  for (int i = 0; i < 16; i++) {
+    EXPECT_EQ(span[i], 0) << "element: " << i;
+  }
+  load_db->DecRef();
+}
+
+TEST_F(RV32VInstructionsTest, VsIndexed_Rv32AddressWrap) {
+  uint32_t vtype =
+      (kSewSettingsByByteSize[sizeof(uint32_t)] << 3) | kLmulSettings[3];
+  ConfigureVectorUnit(vtype, /*avl=*/1);
+  ASSERT_EQ(rv_vector_->vector_length(), 1);
+
+  AppendVectorRegisterOperands({kVs1}, {});
+  AppendRegisterOperands({kRs1Name}, {});
+  AppendVectorRegisterOperands({kVs2, kVmask}, {});
+  SetSemanticFunction(absl::bind_front(&VsIndexed, sizeof(uint32_t)));
+
+  SetRegisterValues<uint32_t>({{kRs1Name, 0xFFFFFF00}});
+  uint8_t all_ones_mask[kVectorLengthInBytes];
+  std::memset(all_ones_mask, 0xff, sizeof(all_ones_mask));
+  SetVectorRegisterValues<uint8_t>(
+      {{kVmaskName, Span<const uint8_t>(all_ones_mask)}});
+
+  vreg_[kVs1]->data_buffer()->Set<uint32_t>(0, 0xCAFEBABE);
+  vreg_[kVs2]->data_buffer()->Set<uint32_t>(0, 0x200);
+
+  // Clear memory at 0x00000100.
+  auto* zero_db = state_->db_factory()->Allocate<uint32_t>(1);
+  zero_db->Set<uint32_t>(0, 0);
+  memory_->Store(0x00000100, zero_db);
+  zero_db->DecRef();
+
+  instruction_->Execute(nullptr);
+  EXPECT_FALSE(rv_vector_->vector_exception());
+
+  auto* load_db = state_->db_factory()->Allocate<uint32_t>(1);
+  state_->LoadMemory(instruction_, 0x00000100, load_db, nullptr, nullptr);
+  EXPECT_EQ(load_db->Get<uint32_t>(0), 0xCAFEBABE);
+  load_db->DecRef();
+}
+
+TEST_F(RV32VInstructionsTest, VlRegister_RespectsVstart) {
+  rv_vector_->set_vstart(8);
+
+  AppendRegisterOperands({kRs1Name}, {});
+  SetChildInstruction();
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+  SetChildSemanticFunction(&VlChild);
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+  SetSemanticFunction(
+      absl::bind_front(&VlRegister, /*num_regs=*/1, /*element_width=*/1));
+
+  // Initialize destination register with sentinel 0xEE.
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    vreg_[kVd]->data_buffer()->Set<uint8_t>(i, 0xEE);
+  }
+
+  instruction_->Execute();
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+  for (int i = 0; i < 8; i++) {
+    EXPECT_EQ(span[i], 0xEE) << "element: " << i;
+  }
+  for (int i = 8; i < kVectorLengthInBytes; i++) {
+    EXPECT_EQ(span[i], i & 0xff) << "element: " << i;
+  }
+}
+
+TEST_F(RV32VInstructionsTest, VlRegister_VstartGteNumElements) {
+  rv_vector_->set_vstart(kVectorLengthInBytes);
+
+  AppendRegisterOperands({kRs1Name}, {});
+  SetChildInstruction();
+  AppendVectorRegisterOperands(child_instruction_, {}, {kVd});
+  SetChildSemanticFunction(&VlChild);
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}});
+  SetSemanticFunction(
+      absl::bind_front(&VlRegister, /*num_regs=*/1, /*element_width=*/1));
+
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    vreg_[kVd]->data_buffer()->Set<uint8_t>(i, 0xEE);
+  }
+
+  instruction_->Execute();
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto span = vreg_[kVd]->data_buffer()->Get<uint8_t>();
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    EXPECT_EQ(span[i], 0xEE) << "element: " << i;
+  }
+}
+
+TEST_F(RV32VInstructionsTest, VsRegister_RespectsVstart) {
+  rv_vector_->set_vstart(8);
+
+  AppendVectorRegisterOperands({kVs1}, {});
+  AppendRegisterOperands({kRs1Name}, {});
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+  SetSemanticFunction(absl::bind_front(&VsRegister, /*num_regs=*/1));
+
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    vreg_[kVs1]->data_buffer()->Set<uint8_t>(i, 0x80 + i);
+  }
+
+  // Clear memory.
+  auto* zero_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes);
+  std::memset(zero_db->raw_ptr(), 0, kVectorLengthInBytes);
+  state_->StoreMemory(instruction_, kDataStoreAddress, zero_db);
+  zero_db->DecRef();
+
+  instruction_->Execute();
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto* load_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes);
+  state_->LoadMemory(instruction_, kDataStoreAddress, load_db, nullptr,
+                     nullptr);
+  auto span = load_db->Get<uint8_t>();
+  for (int i = 0; i < 8; i++) {
+    EXPECT_EQ(span[i], 0) << "byte: " << i;
+  }
+  for (int i = 8; i < kVectorLengthInBytes; i++) {
+    EXPECT_EQ(span[i], static_cast<uint8_t>(0x80 + i)) << "byte: " << i;
+  }
+  load_db->DecRef();
+}
+
+TEST_F(RV32VInstructionsTest, VsRegister_VstartGteNumElements) {
+  rv_vector_->set_vstart(kVectorLengthInBytes);
+
+  AppendVectorRegisterOperands({kVs1}, {});
+  AppendRegisterOperands({kRs1Name}, {});
+  SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}});
+  SetSemanticFunction(absl::bind_front(&VsRegister, /*num_regs=*/1));
+
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    vreg_[kVs1]->data_buffer()->Set<uint8_t>(i, 0x80 + i);
+  }
+
+  // Clear memory.
+  auto* zero_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes);
+  std::memset(zero_db->raw_ptr(), 0, kVectorLengthInBytes);
+  state_->StoreMemory(instruction_, kDataStoreAddress, zero_db);
+  zero_db->DecRef();
+
+  instruction_->Execute();
+  EXPECT_FALSE(rv_vector_->vector_exception());
+  EXPECT_EQ(rv_vector_->vstart(), 0);
+
+  auto* load_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes);
+  state_->LoadMemory(instruction_, kDataStoreAddress, load_db, nullptr,
+                     nullptr);
+  auto span = load_db->Get<uint8_t>();
+  for (int i = 0; i < kVectorLengthInBytes; i++) {
+    EXPECT_EQ(span[i], 0) << "byte: " << i;
+  }
+  load_db->DecRef();
+}
+
 }  // namespace