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