| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cheriot/riscv_cheriot_vector_memory_instructions.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstring> |
| #include <functional> |
| #include <ios> |
| #include <string> |
| #include <type_traits> |
| #include <vector> |
| |
| #include "absl/functional/bind_front.h" |
| #include "absl/log/log.h" |
| #include "absl/numeric/bits.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/types/span.h" |
| #include "cheriot/cheriot_register.h" |
| #include "cheriot/cheriot_state.h" |
| #include "cheriot/cheriot_vector_state.h" |
| #include "googlemock/include/gmock/gmock.h" |
| #include "mpact/sim/generic/data_buffer.h" |
| #include "mpact/sim/generic/immediate_operand.h" |
| #include "mpact/sim/generic/instruction.h" |
| #include "mpact/sim/generic/register.h" |
| #include "mpact/sim/util/memory/tagged_flat_demand_memory.h" |
| #include "riscv//riscv_register.h" |
| |
| // This file contains the test fixture and tests for testing RiscV vector |
| // memory instructions. |
| |
| namespace { |
| |
| using ::absl::Span; |
| using ::mpact::sim::cheriot::CheriotRegister; |
| using ::mpact::sim::cheriot::CheriotState; |
| using ::mpact::sim::cheriot::CheriotVectorState; |
| using ::mpact::sim::generic::ImmediateOperand; |
| using ::mpact::sim::generic::Instruction; |
| using ::mpact::sim::generic::RegisterBase; |
| using ::mpact::sim::riscv::RV32VectorDestinationOperand; |
| using ::mpact::sim::riscv::RV32VectorSourceOperand; |
| using ::mpact::sim::riscv::RVVectorRegister; |
| using ::mpact::sim::util::TaggedFlatDemandMemory; |
| using ::std::tuple; |
| |
| // Semantic functions. |
| using ::mpact::sim::cheriot::VlChild; |
| using ::mpact::sim::cheriot::VlIndexed; |
| using ::mpact::sim::cheriot::Vlm; |
| using ::mpact::sim::cheriot::VlRegister; |
| using ::mpact::sim::cheriot::VlSegment; |
| using ::mpact::sim::cheriot::VlSegmentChild; |
| using ::mpact::sim::cheriot::VlSegmentIndexed; |
| using ::mpact::sim::cheriot::VlSegmentStrided; |
| using ::mpact::sim::cheriot::VlStrided; |
| using ::mpact::sim::cheriot::Vsetvl; |
| using ::mpact::sim::cheriot::VsIndexed; |
| using ::mpact::sim::cheriot::Vsm; |
| using ::mpact::sim::cheriot::VsRegister; |
| using ::mpact::sim::cheriot::VsSegment; |
| using ::mpact::sim::cheriot::VsSegmentIndexed; |
| using ::mpact::sim::cheriot::VsSegmentStrided; |
| using ::mpact::sim::cheriot::VsStrided; |
| |
| // Constants used in the tests. |
| constexpr int kVectorLengthInBits = 512; |
| constexpr int kVectorLengthInBytes = kVectorLengthInBits / 8; |
| constexpr uint32_t kInstAddress = 0x1000; |
| constexpr uint32_t kDataLoadAddress = 0x1'0000; |
| constexpr uint32_t kDataStoreAddress = 0x8'0000; |
| constexpr char kRs1Name[] = "c1"; |
| constexpr int kRs1 = 1; |
| constexpr char kRs2Name[] = "c2"; |
| constexpr char kRs3Name[] = "c3"; |
| constexpr char kRdName[] = "c8"; |
| constexpr int kRd = 8; |
| constexpr int kVmask = 1; |
| constexpr char kVmaskName[] = "v1"; |
| constexpr int kVd = 8; |
| constexpr int kVs1 = 16; |
| constexpr int kVs2 = 24; |
| |
| // Setting bits and corresponding values for lmul and sew. |
| constexpr int kLmulSettings[7] = {0b101, 0b110, 0b111, 0b000, |
| 0b001, 0b010, 0b011}; |
| constexpr int kLmul8Values[7] = {1, 2, 4, 8, 16, 32, 64}; |
| constexpr int kLmulSettingByLogSize[] = {0, 0b101, 0b110, 0b111, |
| 0b000, 0b001, 0b010, 0b011}; |
| constexpr int kSewSettings[4] = {0b000, 0b001, 0b010, 0b011}; |
| constexpr int kSewValues[4] = {1, 2, 4, 8}; |
| constexpr int kSewSettingsByByteSize[] = {0, 0b000, 0b001, 0, 0b010, |
| 0, 0, 0, 0b011}; |
| |
| // Don't need to set every byte, as only the low bits are used for mask values. |
| constexpr uint8_t kA5Mask[kVectorLengthInBytes] = { |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, |
| }; |
| |
| // Test fixture class. This class allows for more convenient manipulations |
| // of instructions to test the semantic functions. |
| class RiscVCheriotVInstructionsTest : public testing::Test { |
| public: |
| RiscVCheriotVInstructionsTest() { |
| memory_ = new TaggedFlatDemandMemory(8); |
| state_ = new CheriotState("test", memory_); |
| rv_vector_ = new CheriotVectorState(state_, kVectorLengthInBytes); |
| instruction_ = new Instruction(kInstAddress, state_); |
| instruction_->set_size(4); |
| child_instruction_ = new Instruction(kInstAddress, state_); |
| child_instruction_->set_size(4); |
| // Initialize a portion of memory with a known pattern. |
| auto *db = state_->db_factory()->Allocate(8192); |
| auto span = db->Get<uint8_t>(); |
| for (int i = 0; i < 8192; i++) { |
| span[i] = i & 0xff; |
| } |
| memory_->Store(kDataLoadAddress - 4096, db); |
| db->DecRef(); |
| for (int i = 1; i < 32; i++) { |
| creg_[i] = |
| state_->GetRegister<CheriotRegister>(absl::StrCat("c", i)).first; |
| creg_[i]->ResetMemoryRoot(); |
| } |
| for (int i = 1; i < 32; i++) { |
| vreg_[i] = |
| state_->GetRegister<RVVectorRegister>(absl::StrCat("v", i)).first; |
| } |
| } |
| |
| ~RiscVCheriotVInstructionsTest() override { |
| delete state_; |
| delete rv_vector_; |
| instruction_->DecRef(); |
| child_instruction_->DecRef(); |
| delete memory_; |
| } |
| |
| // Creates immediate operands with the values from the vector and appends them |
| // to the given instruction. |
| template <typename T> |
| void AppendImmediateOperands(Instruction *inst, |
| const std::vector<T> &values) { |
| for (auto value : values) { |
| auto *src = new ImmediateOperand<T>(value); |
| inst->AppendSource(src); |
| } |
| } |
| |
| // Creates immediate operands with the values from the vector and appends them |
| // to the default instruction. |
| template <typename T> |
| void AppendImmediateOperands(const std::vector<T> &values) { |
| AppendImmediateOperands<T>(instruction_, values); |
| } |
| |
| // Creates source and destination scalar register operands for the registers |
| // named in the two vectors and append them to the given instruction. |
| void AppendRegisterOperands(Instruction *inst, |
| const std::vector<std::string> &sources, |
| const std::vector<std::string> &destinations) { |
| for (auto ®_name : sources) { |
| auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first; |
| inst->AppendSource(reg->CreateSourceOperand()); |
| } |
| for (auto ®_name : destinations) { |
| auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first; |
| inst->AppendDestination(reg->CreateDestinationOperand(0)); |
| } |
| } |
| |
| // Creates source and destination scalar register operands for the registers |
| // named in the two vectors and append them to the default instruction. |
| void AppendRegisterOperands(const std::vector<std::string> &sources, |
| const std::vector<std::string> &destinations) { |
| AppendRegisterOperands(instruction_, sources, destinations); |
| } |
| |
| // Returns the value of the named vector register. |
| template <typename T> |
| T GetRegisterValue(absl::string_view vreg_name) { |
| auto *reg = state_->GetRegister<CheriotRegister>(vreg_name).first; |
| return reg->data_buffer()->Get<T>(); |
| } |
| |
| // named register and sets it to the corresponding value. |
| template <typename T> |
| void SetRegisterValues( |
| const std::vector<tuple<std::string, const T>> &values) { |
| for (auto &[reg_name, value] : values) { |
| auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first; |
| auto *db = state_->db_factory()->Allocate<CheriotRegister::ValueType>(1); |
| db->Set<T>(0, value); |
| reg->SetDataBuffer(db); |
| db->DecRef(); |
| } |
| } |
| |
| // Creates source and destination scalar register operands for the registers |
| // named in the two vectors and append them to the given instruction. |
| void AppendVectorRegisterOperands(Instruction *inst, |
| const std::vector<int> &sources, |
| const std::vector<int> &destinations) { |
| for (auto ®_no : sources) { |
| std::vector<RegisterBase *> reg_vec; |
| for (int i = 0; (i < 8) && (i + reg_no < 32); i++) { |
| std::string reg_name = absl::StrCat("v", i + reg_no); |
| reg_vec.push_back( |
| state_->GetRegister<RVVectorRegister>(reg_name).first); |
| } |
| auto *op = new RV32VectorSourceOperand( |
| absl::Span<RegisterBase *>(reg_vec), absl::StrCat("v", reg_no)); |
| inst->AppendSource(op); |
| } |
| for (auto ®_no : destinations) { |
| std::vector<RegisterBase *> reg_vec; |
| for (int i = 0; (i < 8) && (i + reg_no < 32); i++) { |
| std::string reg_name = absl::StrCat("v", i + reg_no); |
| reg_vec.push_back( |
| state_->GetRegister<RVVectorRegister>(reg_name).first); |
| } |
| auto *op = new RV32VectorDestinationOperand( |
| absl::Span<RegisterBase *>(reg_vec), 0, absl::StrCat("v", reg_no)); |
| inst->AppendDestination(op); |
| } |
| } |
| // Creates source and destination scalar register operands for the registers |
| // named in the two vectors and append them to the default instruction. |
| void AppendVectorRegisterOperands(const std::vector<int> &sources, |
| const std::vector<int> &destinations) { |
| AppendVectorRegisterOperands(instruction_, sources, destinations); |
| } |
| |
| // Returns the value of the named vector register. |
| template <typename T> |
| T GetVectorRegisterValue(absl::string_view reg_name) { |
| auto *reg = state_->GetRegister<RVVectorRegister>(reg_name).first; |
| return reg->data_buffer()->Get<T>(0); |
| } |
| |
| // Set a vector register value. Takes a vector of tuples of register names and |
| // spans of values, fetches each register and sets it to the corresponding |
| // value. |
| template <typename T> |
| void SetVectorRegisterValues( |
| const std::vector<tuple<std::string, Span<const T>>> &values) { |
| for (auto &[vreg_name, span] : values) { |
| auto *vreg = state_->GetRegister<RVVectorRegister>(vreg_name).first; |
| auto *db = state_->db_factory()->MakeCopyOf(vreg->data_buffer()); |
| db->template Set<T>(span); |
| vreg->SetDataBuffer(db); |
| db->DecRef(); |
| } |
| } |
| |
| // Initializes the semantic function of the instruction object. |
| void SetSemanticFunction(Instruction *inst, |
| Instruction::SemanticFunction fcn) { |
| inst->set_semantic_function(fcn); |
| } |
| |
| // Initializes the semantic function for the default instruction. |
| void SetSemanticFunction(Instruction::SemanticFunction fcn) { |
| instruction_->set_semantic_function(fcn); |
| } |
| |
| // Sets the default child instruction as the child of the default instruction. |
| void SetChildInstruction() { instruction_->AppendChild(child_instruction_); } |
| |
| // Initializes the semantic function for the default child instruction. |
| void SetChildSemanticFunction(Instruction::SemanticFunction fcn) { |
| child_instruction_->set_semantic_function(fcn); |
| } |
| |
| // Configure the vector unit according to the vtype and vlen values. |
| void ConfigureVectorUnit(uint32_t vtype, uint32_t vlen) { |
| Instruction *inst = new Instruction(state_); |
| AppendImmediateOperands<uint32_t>(inst, {vlen, vtype}); |
| SetSemanticFunction(inst, absl::bind_front(&Vsetvl, true, false)); |
| inst->Execute(nullptr); |
| inst->DecRef(); |
| } |
| |
| template <typename T> |
| T ComputeValue(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> |
| void VectorLoadUnitStridedHelper() { |
| // Set up instructions. |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {}); |
| AppendVectorRegisterOperands({kVmask}, {}); |
| SetSemanticFunction(absl::bind_front(&VlStrided, |
| /*element_width*/ sizeof(T))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction(&VlChild); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| // Set up register values. |
| SetRegisterValues<uint32_t>( |
| {{kRs1Name, kDataLoadAddress}, {kRs2Name, sizeof(T)}}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| uint32_t vtype = |
| (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check register values. |
| int count = 0; |
| for (int reg = kVd; reg < kVd + 8; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<T>(); |
| for (int i = 0; i < kVectorLengthInBytes / sizeof(T); i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| // First compute the expected value, then compare it. |
| T value = ComputeValue<T>(4096 + count * sizeof(T)); |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(T) << " LMUL8 " << lmul8 |
| << " Count " << count << " Reg " << reg << " value " << i; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(T) << " LMUL8 " << lmul8 |
| << " Count " << count << " Reg " << reg << " value " << i; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| void VectorLoadStridedHelper() { |
| const int strides[5] = {1, 4, 0, -1, -3}; |
| // Set up instructions. |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {}); |
| AppendVectorRegisterOperands({kVmask}, {}); |
| SetSemanticFunction(absl::bind_front(&VlStrided, |
| /*element_width*/ sizeof(T))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction(&VlChild); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| // Set up register values. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Try different strides. |
| for (int s = 0; s < 5; s++) { |
| int32_t stride = strides[s] * sizeof(T); |
| SetRegisterValues<int32_t>({{kRs2Name, stride}}); |
| // Configure vector unit. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check register values. |
| int count = 0; |
| for (int reg = kVd; reg < kVd + 8; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<T>(); |
| for (int i = 0; i < kVectorLengthInBytes / sizeof(T); i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| // First compute the expected value, then compare it. |
| T value = ComputeValue<T>(4096 + count * stride); |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(T) << " stride: " << stride |
| << " LMUL8 " << lmul8 << " Count " << count << " Reg " << reg |
| << " value " << i; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(T) << " stride: " << stride |
| << " LMUL8 " << lmul8 << " Count " << count << " Reg " << reg |
| << " value " << i; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| T IndexValue(int i) { |
| T offset = ~i & 0b1111; |
| T val = (i & ~0b1111) | offset; |
| return val * sizeof(T); |
| } |
| |
| // Helper function for testing vector load indexed instructions. |
| template <typename IndexType, typename ValueType> |
| void VectorLoadIndexedHelper() { |
| // Set up instructions. |
| AppendRegisterOperands({kRs1Name}, {}); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| SetSemanticFunction(absl::bind_front(&VlIndexed, |
| /*index_width*/ sizeof(IndexType))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction(&VlChild); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| // Set up register values. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) | |
| kLmulSettings[lmul_index]; |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| int lmul8 = kLmul8Values[lmul_index]; |
| int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType); |
| |
| if ((index_emul8 == 0) || (index_emul8 > 64)) { |
| // The index vector length is illegal. |
| instruction_->Execute(nullptr); |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8); |
| // Set up index vector values. |
| int values_per_reg = kVectorLengthInBytes / sizeof(IndexType); |
| for (int i = 0; i < num_values; i++) { |
| int reg_index = kVs2 + i / values_per_reg; |
| int reg_offset = i % values_per_reg; |
| auto index_span = vreg_[reg_index]->data_buffer()->Get<IndexType>(); |
| index_span[reg_offset] = IndexValue<ValueType>(i); |
| } |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check register values. |
| int count = 0; |
| for (int reg = kVd; reg < kVd + 8; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<ValueType>(); |
| for (int i = 0; i < kVectorLengthInBytes / sizeof(ValueType); i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| // Compare expected value. |
| auto value = |
| ComputeValue<ValueType>(4096 + IndexValue<ValueType>(count)); |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(ValueType) << " index: " << index |
| << " LMUL8 " << lmul8 << " Count " << count << " reg " << reg; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(ValueType) << " index: " << index |
| << " LMUL8 " << lmul8 << " count " << count << " reg " << reg; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| |
| // Helper function to test vector load segment strided instructions. |
| template <typename T> |
| void VectorLoadSegmentHelper() { |
| // Set up instructions. |
| AppendRegisterOperands({kRs1Name}, {}); |
| AppendVectorRegisterOperands({kVmask}, {}); |
| AppendRegisterOperands({kRs3Name}, {}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| SetSemanticFunction(absl::bind_front(&VlSegment, |
| /*element_width*/ sizeof(T))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction( |
| absl::bind_front(&VlSegmentChild, /*element_width*/ sizeof(T))); |
| AppendRegisterOperands(child_instruction_, {kRs3Name}, {}); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| // Set up register values. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| // Iterate over legal values in the nf field. |
| for (int nf = 1; nf < 8; nf++) { |
| int num_fields = nf + 1; |
| // Iterate over different lmul values. |
| SetRegisterValues<int32_t>({{kRs3Name, nf}}); |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| if (lmul8 * num_fields > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| |
| // Check register values. |
| int count = 0; |
| // Fields are in consecutive (groups of) registers. First compute the |
| // number of registers for each field. |
| int regs_per_field = ::std::max(1, lmul8 / 8); |
| for (int field = 0; field < num_fields; field++) { |
| int start_reg = kVd + field * regs_per_field; |
| int max_reg = start_reg + regs_per_field; |
| count = 0; |
| for (int reg = start_reg; reg < max_reg; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<T>(); |
| int num_reg_elements = |
| std::min(kVectorLengthInBytes / sizeof(T), |
| kVectorLengthInBytes * lmul8 / (sizeof(T) * 8)); |
| for (int i = 0; i < num_reg_elements; i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| int address = |
| 4096 + count * sizeof(T) * num_fields + field * sizeof(T); |
| T value = ComputeValue<T>(address); |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(T) << " LMUL8 " << lmul8 |
| << " Count " << count << " Reg " << reg << " value " << i; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(T) << " LMUL8 " << lmul8 |
| << " Count " << count << " Reg " << reg << " value " << i; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Helper function to test vector load segment strided instructions. |
| template <typename T> |
| void VectorLoadStridedSegmentHelper() { |
| const int strides[5] = {1, 4, 0, -1, -3}; |
| // Set up instructions. |
| // Base address and stride. |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {}); |
| // Vector mask register. |
| AppendVectorRegisterOperands({kVmask}, {}); |
| // Operand to hold the number of fields. |
| AppendRegisterOperands({kRs3Name}, {}); |
| // Initialize the mask. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Bind semantic function. |
| SetSemanticFunction(absl::bind_front(&VlSegmentStrided, |
| /*element_width*/ sizeof(T))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction( |
| absl::bind_front(&VlSegmentChild, /*element_width*/ sizeof(T))); |
| // Number of fields. |
| AppendRegisterOperands(child_instruction_, {kRs3Name}, {}); |
| // Destination vector register operand. |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| |
| // Set up register values. |
| // Base address. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| // Iterate over legal values in the nf field. |
| for (int nf = 1; nf < 8; nf++) { |
| int num_fields = nf + 1; |
| // Set the number of fields. |
| SetRegisterValues<int32_t>({{kRs3Name, nf}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| |
| // Try different strides. |
| for (int s = 0; s < 5; s++) { |
| int32_t stride = strides[s] * num_fields * sizeof(T); |
| // Set the stride. |
| SetRegisterValues<int32_t>({{kRs2Name, stride}}); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| if (lmul8 * num_fields > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| |
| // Check register values. |
| // Fields are in consecutive (groups of) registers. First compute the |
| // number of registers for each field. |
| int regs_per_field = ::std::max(1, lmul8 / 8); |
| for (int field = 0; field < num_fields; field++) { |
| int start_reg = kVd + field * regs_per_field; |
| int max_reg = start_reg + regs_per_field; |
| int count = 0; |
| for (int reg = start_reg; reg < max_reg; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<T>(); |
| int num_reg_elements = |
| std::min(kVectorLengthInBytes / sizeof(T), |
| kVectorLengthInBytes * lmul8 / (sizeof(T) * 8)); |
| for (int i = 0; i < num_reg_elements; i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| // First compute the expected value, then compare it. |
| int address = 4096 + stride * count + field * sizeof(T); |
| T value = ComputeValue<T>(address); |
| |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(T) << " stride: " << stride |
| << " LMUL8 " << lmul8 << " Count " << count << " Reg " |
| << reg << " value " << i; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(T) << " stride: " << stride |
| << " LMUL8 " << lmul8 << " Count " << count << " Reg " |
| << reg << " value " << i; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Helper function to test vector load segment indexed instructions. |
| template <typename IndexType, typename ValueType> |
| void VectorLoadIndexedSegmentHelper() { |
| // Set up instructions. |
| // Base address and stride. |
| AppendRegisterOperands({kRs1Name}, {}); |
| // Vector mask register. |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| // Operand to hold the number of fields. |
| AppendRegisterOperands({kRs3Name}, {}); |
| // Initialize the mask. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Bind semantic function. |
| SetSemanticFunction(absl::bind_front(&VlSegmentIndexed, |
| /*element_width*/ sizeof(IndexType))); |
| // Add the child instruction that performs the register write-back. |
| SetChildInstruction(); |
| SetChildSemanticFunction( |
| absl::bind_front(&VlSegmentChild, |
| /*element_width*/ sizeof(ValueType))); |
| // Number of fields. |
| AppendRegisterOperands(child_instruction_, {kRs3Name}, {}); |
| // Destination vector register operand. |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| |
| // Set up register values. |
| // Base address. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| // Iterate over legal values in the nf field. |
| for (int nf = 1; nf < 8; nf++) { |
| int num_fields = nf + 1; |
| // Set the number of fields. |
| SetRegisterValues<int32_t>({{kRs3Name, nf}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| rv_vector_->clear_vector_exception(); |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType); |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8); |
| |
| if ((index_emul8 == 0) || (index_emul8 > 64)) { |
| // The index vector length is illegal. |
| instruction_->Execute(nullptr); |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| continue; |
| } |
| if (lmul8 * num_fields > 64) { |
| instruction_->Execute(nullptr); |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| continue; |
| } |
| |
| // Set up index vector values. |
| int values_per_reg = kVectorLengthInBytes / sizeof(IndexType); |
| for (int i = 0; i < num_values; i++) { |
| int reg_index = kVs2 + i / values_per_reg; |
| int reg_offset = i % values_per_reg; |
| auto index_span = vreg_[reg_index]->data_buffer()->Get<IndexType>(); |
| index_span[reg_offset] = IndexValue<ValueType>(i); |
| } |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| |
| // Check register values. |
| // Fields are in consecutive (groups of) registers. First compute the |
| // number of registers for each field. |
| int regs_per_field = ::std::max(1, lmul8 / 8); |
| for (int field = 0; field < num_fields; field++) { |
| int start_reg = kVd + field * regs_per_field; |
| int max_reg = start_reg + regs_per_field; |
| int count = 0; |
| for (int reg = start_reg; reg < max_reg; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<ValueType>(); |
| int num_reg_elements = std::min( |
| kVectorLengthInBytes / sizeof(ValueType), |
| kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8)); |
| for (int i = 0; i < num_reg_elements; i++) { |
| int mask_index = count / 8; |
| int mask_offset = count % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (count < num_values)) { |
| // First compute the expected value, then compare it. |
| int address = 4096 + IndexValue<IndexType>(count) + |
| field * sizeof(ValueType); |
| ValueType value = ComputeValue<ValueType>(address); |
| |
| EXPECT_EQ(value, span[i]) |
| << "element size " << sizeof(ValueType) << " LMUL8 " |
| << lmul8 << " Count " << count << " Reg " << reg |
| << " value " << i; |
| } else { |
| // The remainder of the values should be zero. |
| EXPECT_EQ(0, span[i]) |
| << "element size " << sizeof(ValueType) << " LMUL8 " |
| << lmul8 << " Count " << count << " Reg " << reg |
| << " value " << i; |
| } |
| count++; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| void VectorStoreStridedHelper() { |
| const int strides[5] = {1, 4, 8, -1, -3}; |
| // Set up instructions. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {}); |
| AppendVectorRegisterOperands({kVmask}, {}); |
| SetSemanticFunction(absl::bind_front(&VsStrided, |
| /*element_width*/ sizeof(T))); |
| // Set up register values. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Set the store data register elements to be consecutive integers. |
| for (int reg = 0; reg < 8; reg++) { |
| auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>(); |
| for (int i = 0; i < reg_span.size(); i++) { |
| reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1); |
| } |
| } |
| auto *clear_mem_db = state_->db_factory()->Allocate<T>(0x8000); |
| memset(clear_mem_db->raw_ptr(), 0, 0x8000); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Try different strides. |
| for (int s = 0; s < 5; s++) { |
| int32_t stride = strides[s] * sizeof(T); |
| SetRegisterValues<int32_t>({{kRs2Name, stride}}); |
| // Configure vector unit. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check memory values. |
| auto *data_db = state_->db_factory()->Allocate<T>(1); |
| uint64_t base = kDataStoreAddress; |
| T value = 1; |
| for (int i = 0; i < 8 * kVectorLengthInBytes / sizeof(T); i++) { |
| data_db->template Set<T>(0, 0); |
| state_->DbgLoadMemory(base, data_db); |
| int mask_index = i / 8; |
| int mask_offset = i % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (i < num_values)) { |
| EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value)) |
| << "index: " << i << " element_size: " << sizeof(T) |
| << " lmul8: " << lmul8 << " stride: " << stride; |
| } else { |
| EXPECT_EQ(data_db->template Get<T>(0), 0) << "index: " << i; |
| } |
| base += stride; |
| value++; |
| } |
| data_db->DecRef(); |
| // Clear memory. |
| state_->DbgStoreMemory(kDataStoreAddress - 0x4000, clear_mem_db); |
| } |
| } |
| clear_mem_db->DecRef(); |
| } |
| |
| template <typename IndexType, typename ValueType> |
| void VectorStoreIndexedHelper() { |
| // Set up instructions. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| AppendRegisterOperands({kRs1Name}, {}); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| SetSemanticFunction(absl::bind_front(&VsIndexed, |
| /*index_width*/ sizeof(IndexType))); |
| |
| // Set up register values. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| |
| int values_per_reg = kVectorLengthInBytes / sizeof(ValueType); |
| int index_values_per_reg = kVectorLengthInBytes / sizeof(IndexType); |
| for (int reg = 0; reg < 8; reg++) { |
| for (int i = 0; i < values_per_reg; i++) { |
| vreg_[kVs1 + reg]->data_buffer()->Set<ValueType>( |
| i, reg * values_per_reg + i); |
| } |
| } |
| |
| auto *data_db = state_->db_factory()->Allocate<ValueType>(1); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(ValueType)] << 3) | |
| kLmulSettings[lmul_index]; |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| int lmul8 = kLmul8Values[lmul_index]; |
| int index_emul8 = lmul8 * sizeof(IndexType) / sizeof(ValueType); |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(ValueType) * 8); |
| |
| // Skip if the number of values is greater than the offset representation. |
| // This only happens for uint8_t. |
| if (num_values > 256) continue; |
| |
| // Check the index vector length. |
| if ((index_emul8 == 0) || (index_emul8 > 64)) { |
| // The index vector length is illegal. |
| instruction_->Execute(nullptr); |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| |
| for (int i = 0; i < num_values; i++) { |
| 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)); |
| } |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check results. |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| |
| // Check register values. |
| for (int i = 0; i < 8 * kVectorLengthInBytes / sizeof(ValueType); i++) { |
| uint64_t address = kDataStoreAddress + IndexValue<IndexType>(i); |
| state_->DbgLoadMemory(address, data_db); |
| int mask_index = i / 8; |
| int mask_offset = i % 8; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (i < num_values)) { |
| EXPECT_EQ(data_db->template Get<ValueType>(0), i) |
| << "reg[" << i / values_per_reg << "][" << i % values_per_reg |
| << "]"; |
| } else { |
| EXPECT_EQ(data_db->template Get<ValueType>(0), 0) |
| << "reg[" << i / values_per_reg << "][" << i % values_per_reg |
| << "]"; |
| } |
| // Clear the memory location. |
| data_db->template Set<ValueType>(0, 0); |
| state_->DbgStoreMemory(address, data_db); |
| } |
| } |
| data_db->DecRef(); |
| } |
| |
| // Helper function to test vector load segment strided instructions. |
| template <typename T> |
| void VectorStoreSegmentHelper() { |
| // Set up instructions. |
| // Store data register. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| // Base address and stride. |
| AppendRegisterOperands({kRs1Name}, {}); |
| // Vector mask register. |
| AppendVectorRegisterOperands({kVmask}, {}); |
| // Operand to hold the number of fields. |
| AppendRegisterOperands({kRs3Name}, {}); |
| // Bind semantic function. |
| SetSemanticFunction(absl::bind_front(&VsSegment, |
| /*element_width*/ sizeof(T))); |
| |
| // Set up register values. |
| // Set the store data register elements to be consecutive integers. |
| for (int reg = 0; reg < 8; reg++) { |
| auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>(); |
| for (int i = 0; i < reg_span.size(); i++) { |
| reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1); |
| } |
| } |
| // Base address. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| // Initialize the mask. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| |
| int num_values_per_register = kVectorLengthInBytes / sizeof(T); |
| // Can load all the data in one load, so set the data_db size accordingly. |
| auto *data_db = |
| state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes * 8); |
| // Iterate over legal values in the nf field. |
| for (int nf = 1; nf < 8; nf++) { |
| int num_fields = nf + 1; |
| // Set the number of fields in the source operand. |
| SetRegisterValues<int32_t>({{kRs3Name, nf}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| // Clear the memory. |
| uint64_t base = kDataStoreAddress; |
| std::memset(data_db->raw_ptr(), 0, data_db->template size<uint8_t>()); |
| state_->DbgStoreMemory(base, data_db); |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| if (lmul8 * num_fields > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| int emul = sizeof(T) * lmul8 / rv_vector_->selected_element_width(); |
| if (emul == 0 || emul > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| // Check memory values. |
| T value = 1; |
| int vlen = rv_vector_->vector_length(); |
| int num_regs = std::max(1, lmul8 / 8); |
| // Load the store data. |
| state_->DbgLoadMemory(base, data_db); |
| // Iterate over fields. |
| for (int field = 0; field < num_fields; field++) { |
| // Iterate over the registers used for each field. |
| int segment_no = 0; |
| for (int reg = 0; reg < num_regs; reg++) { |
| // Iterate over segments within each register. |
| for (int i = 0; i < num_values_per_register; i++) { |
| // Get the mask value. |
| int mask_index = segment_no >> 3; |
| int mask_offset = segment_no & 0b111; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| T mem_value = |
| data_db->template Get<T>(segment_no * num_fields + field); |
| if (mask && (segment_no < vlen)) { |
| EXPECT_EQ(mem_value, static_cast<T>(value)) |
| << " segment_no: " << segment_no << " field: " << field |
| << " index: " << i << " element_size: " << sizeof(T) |
| << " lmul8: " << lmul8 << " mask: " << mask; |
| // Zero the memory location. |
| data_db->template Set<T>(0, 0); |
| } else { |
| EXPECT_EQ(mem_value, 0) |
| << " segment_no: " << segment_no << " field: " << field |
| << " index: " << i << " element_size: " << sizeof(T) |
| << " lmul8: " << lmul8 << " mask: " << mask; |
| } |
| value++; |
| segment_no++; |
| } |
| } |
| } |
| if (HasFailure()) { |
| data_db->DecRef(); |
| return; |
| } |
| } |
| } |
| data_db->DecRef(); |
| } |
| |
| // Helper function to test vector load segment strided instructions. |
| template <typename T> |
| void VectorStoreStridedSegmentHelper() { |
| const int strides[5] = {1, 4, 8, -1, -3}; |
| // Set up instructions. |
| // Store data register. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| // Base address and stride. |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {}); |
| // Vector mask register. |
| AppendVectorRegisterOperands({kVmask}, {}); |
| // Operand to hold the number of fields. |
| AppendRegisterOperands({kRs3Name}, {}); |
| // Bind semantic function. |
| SetSemanticFunction(absl::bind_front(&VsSegmentStrided, |
| /*element_width*/ sizeof(T))); |
| |
| // Set up register values. |
| // Set the store data register elements to be consecutive integers. |
| for (int reg = 0; reg < 8; reg++) { |
| auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>(); |
| for (int i = 0; i < reg_span.size(); i++) { |
| reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1); |
| } |
| } |
| // Base address. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| // Initialize the mask. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| |
| int num_values_per_register = kVectorLengthInBytes / sizeof(T); |
| |
| auto *data_db = state_->db_factory()->Allocate<T>(1); |
| // Iterate over legal values in the nf field. |
| for (int nf = 1; nf < 8; nf++) { |
| int num_fields = nf + 1; |
| // Set the number of fields in the source operand. |
| SetRegisterValues<int32_t>({{kRs3Name, nf}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| ConfigureVectorUnit(vtype, /*vlen*/ 1024); |
| |
| // Try different strides. |
| for (int s : strides) { |
| int32_t stride = s * num_fields * sizeof(T); |
| // Set the stride. |
| SetRegisterValues<int32_t>({{kRs2Name, stride}}); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| if (lmul8 * num_fields > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| int emul = sizeof(T) * lmul8 / rv_vector_->selected_element_width(); |
| if (emul == 0 || emul > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()); |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| // Check memory values. |
| uint64_t base = kDataStoreAddress; |
| T value = 1; |
| int vlen = rv_vector_->vector_length(); |
| int num_regs = std::max(1, lmul8 / 8); |
| // Iterate over fields. |
| for (int field = 0; field < num_fields; field++) { |
| uint64_t address = base + field * sizeof(T); |
| // Iterate over the registers used for each field. |
| int segment_no = 0; |
| for (int reg = 0; reg < num_regs; reg++) { |
| // Iterate over segments within each register. |
| for (int i = 0; i < num_values_per_register; i++) { |
| // Load the data. |
| data_db->template Set<T>(0, 0); |
| state_->DbgLoadMemory(address, data_db); |
| // Get the mask value. |
| int mask_index = segment_no >> 3; |
| int mask_offset = segment_no & 0b111; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (segment_no < vlen)) { |
| EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value)) |
| << std::hex << "address: 0x" << address << std::dec |
| << " segment_no: " << segment_no << " field: " << field |
| << " index: " << i << " element_size: " << sizeof(T) |
| << " lmul8: " << lmul8 << " stride: " << stride; |
| // Zero the memory location. |
| data_db->template Set<T>(0, 0); |
| state_->StoreMemory(instruction_, address, data_db); |
| } else { |
| EXPECT_EQ(data_db->template Get<T>(0), 0) |
| << std::hex << "address: 0x" << address << std::dec |
| << " segment_no: " << segment_no << " field: " << field |
| << " index: " << i << " element_size: " << sizeof(T) |
| << " lmul8: " << lmul8 << " stride: " << stride |
| << " mask: " << mask; |
| } |
| value++; |
| address += stride; |
| segment_no++; |
| } |
| } |
| } |
| if (HasFailure()) { |
| data_db->DecRef(); |
| return; |
| } |
| } |
| } |
| } |
| data_db->DecRef(); |
| } |
| |
| // Helper function to test vector load segment strided instructions. |
| template <typename T, typename I> |
| void VectorStoreIndexedSegmentHelper() { |
| // Ensure that the IndexType is signed. |
| using IndexType = typename std::make_signed<I>::type; |
| // Set up instructions. |
| // Store data register. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| // Base address and stride. |
| AppendRegisterOperands({kRs1Name}, {}); |
| // Vector index register and vector mask register. |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| // Operand to hold the number of fields. |
| AppendRegisterOperands({kRs3Name}, {}); |
| // Bind semantic function. |
| SetSemanticFunction( |
| absl::bind_front(&VsSegmentIndexed, /*index_width*/ sizeof(IndexType))); |
| |
| // Set up register values. |
| // Set the store data register elements to be consecutive integers. |
| for (int reg = 0; reg < 8; reg++) { |
| auto reg_span = vreg_[reg + kVs1]->data_buffer()->Get<T>(); |
| for (int i = 0; i < reg_span.size(); i++) { |
| reg_span[i] = static_cast<T>(reg * reg_span.size() + i + 1); |
| } |
| } |
| // Base address. |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| // Initialize the mask. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVmaskName, Span<const uint8_t>(kA5Mask)}}); |
| // Index values. |
| int index_values_per_reg = kVectorLengthInBytes / sizeof(IndexType); |
| int num_values_per_register = kVectorLengthInBytes / sizeof(T); |
| |
| auto *data_db = state_->db_factory()->Allocate<T>(1); |
| // Iterate over legal values in the nf field. |
| for (int num_fields = 1; num_fields < 8; num_fields++) { |
| // Set the number of fields in the source operand. |
| SetRegisterValues<int32_t>({{kRs3Name, num_fields - 1}}); |
| // Iterate over different lmul values. |
| for (int lmul_index = 0; lmul_index < 7; lmul_index++) { |
| // Configure vector unit, set the sew to the element width. |
| uint32_t vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | |
| kLmulSettings[lmul_index]; |
| int lmul8 = kLmul8Values[lmul_index]; |
| |
| // Set up Index vector. |
| // Max number of segments (for testing) is limited by the range of the |
| // index type. For byte indices, the range is only +/- 128 for each |
| // index. Since the index isn't scaled, this means that the max number |
| // of segments for byte indices is 256 / (sizeof(T) * nf) |
| int segment_size = sizeof(T) * num_fields; |
| int max_segments = |
| kVectorLengthInBytes * lmul8 / (8 * num_fields * sizeof(T)); |
| if (sizeof(IndexType) == 1) { |
| max_segments = std::min(256 / num_fields, max_segments); |
| } |
| ConfigureVectorUnit(vtype, max_segments); |
| |
| int emul8 = |
| sizeof(IndexType) * lmul8 / rv_vector_->selected_element_width(); |
| // Make sure not to write too many indices. At this point the emul |
| // value may still be "illegal", so avoid a "crash" due to writing |
| // the data_buffer out of range. |
| int max_indices = kVectorLengthInBytes * |
| std::min(8, std::max(1, emul8 / 8)) / |
| sizeof(IndexType); |
| if (max_indices > max_segments) { |
| max_indices = max_segments; |
| } |
| // Verify that the index space is large enough. That means that the |
| // index data type can contain enough index values to spread out all the |
| // stores to unique locations. |
| int num_values = kVectorLengthInBytes * lmul8 / (sizeof(T) * 8); |
| if (((num_values * sizeof(T)) >> 8) > (1 << (sizeof(IndexType) - 1))) { |
| LOG(WARNING) << "Index space is too small for the number of bytes: " |
| << num_values * sizeof(T); |
| continue; |
| } |
| for (int i = 0; i < max_indices; i++) { |
| int reg = i / index_values_per_reg; |
| int element = i % index_values_per_reg; |
| // Scale index by the segment size to avoid writing to the same |
| // location twice. |
| vreg_[kVs2 + reg]->data_buffer()->Set<IndexType>( |
| element, IndexValue<IndexType>(i) * segment_size); |
| } |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check for exceptions when they should be set, and verify no |
| // exception otherwise. |
| if (lmul8 * num_fields > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()) |
| << "emul8: " << emul8 << " lmul8: " << lmul8; |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| if (emul8 == 0 || emul8 > 64) { |
| EXPECT_TRUE(rv_vector_->vector_exception()) |
| << "emul8: " << emul8 << " lmul8: " << lmul8; |
| rv_vector_->clear_vector_exception(); |
| continue; |
| } |
| EXPECT_FALSE(rv_vector_->vector_exception()) |
| << "emul8: " << emul8 << " lmul8: " << lmul8; |
| |
| // Check memory values. |
| uint64_t base = kDataStoreAddress; |
| T value = 1; |
| int vlen = rv_vector_->vector_length(); |
| // Iterate over fields. |
| for (int field = 0; field < num_fields; field++) { |
| // Expected value starts at 1 for the first register element and |
| // increments from there. The following computes the expected value |
| // of segment 0 for each field. |
| value = field * kVectorLengthInBytes * std::max(1, lmul8 / 8) / |
| sizeof(T) + |
| 1; |
| for (int segment = 0; segment < max_segments; segment++) { |
| int index_reg = segment / index_values_per_reg; |
| int index_no = segment % index_values_per_reg; |
| // Load the data. |
| int64_t index = |
| vreg_[kVs2 + index_reg]->data_buffer()->Get<IndexType>( |
| index_no); |
| uint64_t address = base + field * sizeof(T) + index; |
| state_->DbgLoadMemory(address, data_db); |
| int element = segment % num_values_per_register; |
| // Get the mask value. |
| int mask_index = segment >> 3; |
| int mask_offset = segment & 0b111; |
| bool mask = (kA5Mask[mask_index] >> mask_offset) & 0x1; |
| if (mask && (segment < vlen)) { |
| EXPECT_EQ(data_db->template Get<T>(0), static_cast<T>(value)) |
| << std::hex << "address: 0x" << address << std::dec |
| << " index: " << index << " segment_no: " << segment |
| << " field: " << field << " i: " << element |
| << " element_size: " << sizeof(T) << " lmul8: " << lmul8 |
| << " num_fields: " << num_fields; |
| // Zero the memory location. |
| data_db->template Set<T>(0, 0); |
| state_->StoreMemory(instruction_, address, data_db); |
| } else { |
| EXPECT_EQ(data_db->template Get<T>(0), 0) |
| << std::hex << "address: 0x" << address << std::dec |
| << " index: " << index << " segment_no: " << segment |
| << " field: " << field << " i: " << element |
| << " element_size: " << sizeof(T) << " lmul8: " << lmul8 |
| << " mask: " << mask; |
| } |
| value++; |
| } |
| } |
| if (HasFailure()) { |
| data_db->DecRef(); |
| return; |
| } |
| } |
| } |
| data_db->DecRef(); |
| } |
| |
| protected: |
| CheriotRegister *creg_[32]; |
| RVVectorRegister *vreg_[32]; |
| CheriotState *state_; |
| Instruction *instruction_; |
| Instruction *child_instruction_; |
| TaggedFlatDemandMemory *memory_; |
| CheriotVectorState *rv_vector_; |
| }; |
| |
| // Test the vector configuration set instructions. There are three separate |
| // versions depending on whether Rs1 is X0 or not, of if Rd is X0. |
| // The first handles the case when Rs1 is not X0. |
| TEST_F(RiscVCheriotVInstructionsTest, VsetvlNN) { |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName}); |
| SetSemanticFunction(absl::bind_front(&Vsetvl, |
| /*rd_zero*/ false, |
| /*rs1_zero*/ false)); |
| for (int lmul = 0; lmul < 7; lmul++) { |
| for (int sew = 0; sew < 4; sew++) { |
| for (int vlen_select = 0; vlen_select < 2; vlen_select++) { |
| // Try vlen below max and above. |
| uint32_t vlen = (vlen_select == 0) ? 16 : 1024; |
| uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0; |
| uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0; |
| uint32_t vtype = |
| vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul]; |
| |
| SetRegisterValues<uint32_t>( |
| {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}}); |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check results. |
| uint32_t expected_vlen = |
| std::min<uint32_t>(vlen, kVectorLengthInBytes * kLmul8Values[lmul] / |
| (8 * kSewValues[sew])); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), expected_vlen) |
| << "LMUL: " << kLmul8Values[lmul] << " SEW: " << kSewValues[sew] |
| << " AVL: " << vlen; |
| EXPECT_EQ(rv_vector_->vector_length(), expected_vlen); |
| EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0); |
| EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0); |
| EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]); |
| EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]); |
| } |
| } |
| } |
| } |
| |
| // The case when Rd is X0, but not Rs1. |
| TEST_F(RiscVCheriotVInstructionsTest, VsetvlZN) { |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName}); |
| SetSemanticFunction(absl::bind_front(&Vsetvl, |
| /*rd_zero*/ true, /*rs1_zero*/ false)); |
| for (int lmul = 0; lmul < 7; lmul++) { |
| for (int sew = 0; sew < 4; sew++) { |
| for (int vlen_select = 0; vlen_select < 2; vlen_select++) { |
| // Try vlen below max and above. |
| uint32_t vlen = (vlen_select == 0) ? 16 : 1024; |
| uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0; |
| uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0; |
| uint32_t vtype = |
| vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul]; |
| |
| SetRegisterValues<uint32_t>( |
| {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}}); |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check results. |
| uint32_t expected_vlen = |
| std::min<uint32_t>(vlen, kVectorLengthInBytes * kLmul8Values[lmul] / |
| (8 * kSewValues[sew])); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), 0); |
| EXPECT_EQ(rv_vector_->vector_length(), expected_vlen); |
| EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0); |
| EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0); |
| EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]); |
| EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]); |
| } |
| } |
| } |
| } |
| |
| // The case when Rd is not X0, but Rs1 is X0. |
| TEST_F(RiscVCheriotVInstructionsTest, VsetvlNZ) { |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName}); |
| SetSemanticFunction(absl::bind_front(&Vsetvl, |
| /*rd_zero*/ false, /*rs1_zero*/ true)); |
| for (int lmul = 0; lmul < 7; lmul++) { |
| for (int sew = 0; sew < 4; sew++) { |
| for (int vlen_select = 0; vlen_select < 2; vlen_select++) { |
| // Try vlen below max and above. |
| uint32_t vlen = (vlen_select == 0) ? 16 : 1024; |
| uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0; |
| uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0; |
| uint32_t vtype = |
| vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul]; |
| |
| SetRegisterValues<uint32_t>( |
| {{kRs1Name, vlen}, {kRs2Name, vtype}, {kRdName, 0}}); |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check results. |
| // In this case, vlen is vlen max. |
| uint32_t expected_vlen = |
| kVectorLengthInBytes * kLmul8Values[lmul] / (8 * kSewValues[sew]); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), expected_vlen); |
| EXPECT_EQ(rv_vector_->vector_length(), expected_vlen); |
| EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0); |
| EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0); |
| EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]); |
| EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]); |
| } |
| } |
| } |
| } |
| |
| // The case when Rd and Rs1 are X0. In this case we are testing if an invalid |
| // vector operation happens, which occurs if the max vector length changes due |
| // to the new value of vtype. |
| TEST_F(RiscVCheriotVInstructionsTest, VsetvlZZ) { |
| AppendRegisterOperands({kRs1Name, kRs2Name}, {kRdName}); |
| SetSemanticFunction(absl::bind_front(&Vsetvl, |
| /*rd_zero*/ true, /*rs1_zero*/ true)); |
| // Iterate over vector lengths. |
| for (int vlen = 512; vlen > 8; vlen /= 2) { |
| // First set the appropriate vector type for sew = 1 byte. |
| uint32_t lmul8 = vlen * 8 / kVectorLengthInBytes; |
| ASSERT_LE(lmul8, 64); |
| ASSERT_GE(lmul8, 1); |
| int lmul8_log2 = absl::bit_width<uint32_t>(lmul8); |
| int lmul_setting = kLmulSettingByLogSize[lmul8_log2]; |
| // Set vtype for this vector length. |
| rv_vector_->SetVectorType(lmul_setting); |
| int max_vector_length = rv_vector_->max_vector_length(); |
| for (int lmul = 0; lmul < 7; lmul++) { |
| for (int sew = 0; sew < 4; sew++) { |
| // Clear any exception. |
| rv_vector_->clear_vector_exception(); |
| // Set up the vtype to try to set using the instruction. |
| uint32_t vma = (lmul & 1) ? 0b1'0'000'000 : 0; |
| uint32_t vta = (sew & 1) ? 0b0'1'000'000 : 0; |
| uint32_t vtype = |
| vma | vta | (kSewSettings[sew] << 3) | kLmulSettings[lmul]; |
| |
| SetRegisterValues<uint32_t>( |
| {{kRs1Name, 0xdeadbeef}, {kRs2Name, vtype}, {kRdName, 0xdeadbeef}}); |
| |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Check results. |
| uint32_t new_vlen = |
| kVectorLengthInBytes * kLmul8Values[lmul] / (8 * kSewValues[sew]); |
| // If vlen changes, then we expect an error and no change. |
| if (new_vlen != max_vector_length) { |
| EXPECT_TRUE(rv_vector_->vector_exception()) |
| << "vlen: " << max_vector_length |
| << " lmul: " << kLmul8Values[lmul] << " sew: " << kSewValues[sew]; |
| } else { |
| // Otherwise, check that the values are as expected. |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| EXPECT_EQ(rv_vector_->vector_length_multiplier(), kLmul8Values[lmul]); |
| EXPECT_EQ(rv_vector_->selected_element_width(), kSewValues[sew]); |
| EXPECT_EQ(rv_vector_->vector_mask_agnostic(), vma != 0); |
| EXPECT_EQ(rv_vector_->vector_tail_agnostic(), vta != 0); |
| } |
| // No change in registers or vector length. |
| EXPECT_EQ(creg_[kRs1]->data_buffer()->Get<uint32_t>(0), 0xdeadbeef); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<uint32_t>(0), 0xdeadbeef); |
| EXPECT_EQ(rv_vector_->max_vector_length(), max_vector_length); |
| } |
| } |
| } |
| } |
| |
| // This tests the semantic function for the VleN and VlseN instructions. VleN |
| // is just a unit stride Vlse. |
| TEST_F(RiscVCheriotVInstructionsTest, Vle8) { |
| VectorLoadUnitStridedHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vle16) { |
| VectorLoadUnitStridedHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vse32) { |
| VectorLoadUnitStridedHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vle64) { |
| VectorLoadUnitStridedHelper<uint64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlse8) { |
| VectorLoadStridedHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlse16) { |
| VectorLoadStridedHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlse32) { |
| VectorLoadStridedHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlse64) { |
| VectorLoadStridedHelper<uint64_t>(); |
| } |
| |
| // Test of vector load mask. |
| TEST_F(RiscVCheriotVInstructionsTest, Vlm) { |
| // Set up operands and register values. |
| AppendRegisterOperands({kRs1Name}, {}); |
| SetSemanticFunction(&Vlm); |
| SetChildInstruction(); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| SetChildSemanticFunction(&VlChild); |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| auto span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(i & 0xff, span[i]) << "element: " << i; |
| } |
| } |
| |
| // Test of vector load register. Loads 1, 2, 4 or 8 registers. |
| TEST_F(RiscVCheriotVInstructionsTest, VlRegister) { |
| // Set up operands and register values. |
| AppendRegisterOperands({kRs1Name}, {}); |
| SetChildInstruction(); |
| AppendVectorRegisterOperands(child_instruction_, {}, {kVd}); |
| SetChildSemanticFunction(&VlChild); |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataLoadAddress}}); |
| // Test 1, 2, 4 and 8 register versions. |
| for (int num_reg = 1; num_reg <= 8; num_reg *= 2) { |
| SetSemanticFunction( |
| absl::bind_front(&VlRegister, num_reg, /*element_width*/ 1)); |
| // Execute instruction. |
| instruction_->Execute(); |
| // Check values. |
| |
| for (int reg = kVd; reg < num_reg; reg++) { |
| auto span = vreg_[reg]->data_buffer()->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(span[i], i & 0xff) |
| << absl::StrCat("Reg: ", reg, " element ", i); |
| } |
| } |
| } |
| } |
| |
| // Indexed loads directly encode the element width of the index value. The |
| // width of the load value is determined by sew (selected element width). |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_8) { |
| VectorLoadIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_16) { |
| VectorLoadIndexedHelper<uint8_t, uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_32) { |
| VectorLoadIndexedHelper<uint8_t, uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed8_64) { |
| VectorLoadIndexedHelper<uint8_t, uint64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_8) { |
| VectorLoadIndexedHelper<uint16_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_16) { |
| VectorLoadIndexedHelper<uint16_t, uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_32) { |
| VectorLoadIndexedHelper<uint16_t, uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed16_64) { |
| VectorLoadIndexedHelper<uint16_t, uint64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_8) { |
| VectorLoadIndexedHelper<uint32_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_16) { |
| VectorLoadIndexedHelper<uint32_t, uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_32) { |
| VectorLoadIndexedHelper<uint32_t, uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed32_64) { |
| VectorLoadIndexedHelper<uint32_t, uint64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_8) { |
| VectorLoadIndexedHelper<uint64_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_16) { |
| VectorLoadIndexedHelper<uint64_t, uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_32) { |
| VectorLoadIndexedHelper<uint64_t, uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VlIndexed64_64) { |
| VectorLoadIndexedHelper<uint64_t, uint64_t>(); |
| } |
| |
| // Test vector load segment unit stride. |
| TEST_F(RiscVCheriotVInstructionsTest, Vlsege8) { |
| VectorLoadSegmentHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlsege16) { |
| VectorLoadSegmentHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlsege32) { |
| VectorLoadSegmentHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlsege64) { |
| VectorLoadSegmentHelper<uint64_t>(); |
| } |
| |
| // Test vector load segment, strided. |
| TEST_F(RiscVCheriotVInstructionsTest, Vlssege8) { |
| VectorLoadStridedSegmentHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlssege16) { |
| VectorLoadStridedSegmentHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlssege32) { |
| VectorLoadStridedSegmentHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vlssege64) { |
| VectorLoadStridedSegmentHelper<uint64_t>(); |
| } |
| |
| // Test vector load segment, indexed. |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_8) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_16) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_32) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei8_64) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_8) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_16) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_32) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei16_64) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_8) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_16) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_32) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei32_64) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_8) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_16) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_32) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vluxsegei64_64) { |
| VectorLoadIndexedSegmentHelper<uint8_t, uint8_t>(); |
| } |
| |
| // Test Vector store strided. |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vsse8) { |
| VectorStoreStridedHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vsse16) { |
| VectorStoreStridedHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vsse32) { |
| VectorStoreStridedHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vsse64) { |
| VectorStoreStridedHelper<uint64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, Vsm) { |
| ConfigureVectorUnit(0b0'0'000'000, /*vlen*/ 1024); |
| // Set up operands and register values. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| AppendRegisterOperands({kRs1Name}, {}); |
| SetSemanticFunction(&Vsm); |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| vreg_[kVs1]->data_buffer()->Set<uint8_t>(i, i); |
| } |
| // Execute instruction. |
| instruction_->Execute(nullptr); |
| |
| // Verify result. |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| auto *data_db = state_->db_factory()->Allocate<uint8_t>(kVectorLengthInBytes); |
| state_->DbgLoadMemory(kDataStoreAddress, data_db); |
| auto span = data_db->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(static_cast<int>(span[i]), i); |
| } |
| data_db->DecRef(); |
| } |
| |
| // Tests of indexed stores, cross product of index types with value types. |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_8) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_16) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_32) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed8_64) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_8) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_16) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_32) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed16_64) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_8) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_16) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_32) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed32_64) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_8) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_16) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_32) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsIndexed64_64) { |
| VectorStoreIndexedHelper<uint8_t, uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsRegister) { |
| ConfigureVectorUnit(0b0'0'000'000, /*vlen*/ 1024); |
| int num_elem = kVectorLengthInBytes / sizeof(uint64_t); |
| // Set up operands and register values. |
| AppendVectorRegisterOperands({kVs1}, {}); |
| for (int reg = 0; reg < 8; reg++) { |
| for (int i = 0; i < num_elem; i++) { |
| vreg_[kVs1 + reg]->data_buffer()->Set<uint64_t>(i, reg * num_elem + i); |
| } |
| } |
| AppendRegisterOperands({kRs1Name}, {}); |
| SetRegisterValues<uint32_t>({{kRs1Name, kDataStoreAddress}}); |
| |
| auto data_db = state_->db_factory()->Allocate(8 * kVectorLengthInBytes); |
| for (int num_regs = 1; num_regs <= 8; num_regs++) { |
| // Clear Memory. |
| memset(data_db->raw_ptr(), 0, data_db->size<uint8_t>()); |
| state_->DbgStoreMemory(kDataStoreAddress, data_db); |
| |
| SetSemanticFunction(absl::bind_front(&VsRegister, num_regs)); |
| |
| // Execute instruction. |
| instruction_->Execute(); |
| |
| // Verify results. |
| EXPECT_FALSE(rv_vector_->vector_exception()); |
| uint64_t base = kDataStoreAddress; |
| for (int reg = 0; reg < 8; reg++) { |
| state_->DbgLoadMemory(base, data_db); |
| auto span = data_db->Get<uint64_t>(); |
| for (int i = 0; i < num_elem; i++) { |
| if (reg < num_regs) { |
| EXPECT_EQ(span[i], reg * num_elem + i) |
| << "reg[" << reg << "][" << i << "]"; |
| } else { |
| EXPECT_EQ(span[i], 0) << "reg[" << reg << "][" << i << "]"; |
| } |
| } |
| base += kVectorLengthInBytes; |
| } |
| } |
| data_db->DecRef(); |
| } |
| |
| // Test vector store segment unit stride. |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegment8) { |
| VectorStoreSegmentHelper<uint8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegment16) { |
| VectorStoreSegmentHelper<uint16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegment32) { |
| VectorStoreSegmentHelper<uint32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegment64) { |
| VectorStoreSegmentHelper<uint64_t>(); |
| } |
| |
| // Test vector store segment strided. |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided8) { |
| VectorStoreStridedSegmentHelper<uint8_t>(); |
| } |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided16) { |
| VectorStoreStridedSegmentHelper<uint16_t>(); |
| } |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided32) { |
| VectorStoreStridedSegmentHelper<uint32_t>(); |
| } |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentStrided64) { |
| VectorStoreStridedSegmentHelper<uint64_t>(); |
| } |
| |
| // Test vector store segment indexed. Test each |
| // combination of element size and index size. |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_8) { |
| VectorStoreIndexedSegmentHelper<uint8_t, int8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_16) { |
| VectorStoreIndexedSegmentHelper<uint8_t, int16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_32) { |
| VectorStoreIndexedSegmentHelper<uint8_t, int32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed8_64) { |
| VectorStoreIndexedSegmentHelper<uint8_t, int64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_8) { |
| VectorStoreIndexedSegmentHelper<uint16_t, int8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_16) { |
| VectorStoreIndexedSegmentHelper<uint16_t, int16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_32) { |
| VectorStoreIndexedSegmentHelper<uint16_t, int32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed16_64) { |
| VectorStoreIndexedSegmentHelper<uint16_t, int64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_8) { |
| VectorStoreIndexedSegmentHelper<uint32_t, int8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_16) { |
| VectorStoreIndexedSegmentHelper<uint32_t, int16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_32) { |
| VectorStoreIndexedSegmentHelper<uint32_t, int32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed32_64) { |
| VectorStoreIndexedSegmentHelper<uint32_t, int64_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_8) { |
| VectorStoreIndexedSegmentHelper<uint64_t, int8_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_16) { |
| VectorStoreIndexedSegmentHelper<uint64_t, int16_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_32) { |
| VectorStoreIndexedSegmentHelper<uint64_t, int32_t>(); |
| } |
| |
| TEST_F(RiscVCheriotVInstructionsTest, VsSegmentIndexed64_64) { |
| VectorStoreIndexedSegmentHelper<uint64_t, int64_t>(); |
| } |
| } // namespace |