| // 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_unary_instructions.h" |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <ios> |
| #include <vector> |
| |
| #include "absl/random/random.h" |
| #include "absl/types/span.h" |
| #include "cheriot/cheriot_register.h" |
| #include "cheriot/test/riscv_cheriot_vector_instructions_test_base.h" |
| #include "googlemock/include/gmock/gmock.h" |
| #include "mpact/sim/generic/instruction.h" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "riscv//riscv_register.h" |
| |
| namespace { |
| |
| using ::absl::Span; |
| using ::mpact::sim::cheriot::CheriotRegister; |
| using ::mpact::sim::generic::Instruction; |
| using ::mpact::sim::generic::SameSignedType; |
| using ::mpact::sim::riscv::RVVectorRegister; |
| |
| using ::mpact::sim::cheriot::Vcpop; |
| using ::mpact::sim::cheriot::Vfirst; |
| using ::mpact::sim::cheriot::Vid; |
| using ::mpact::sim::cheriot::Viota; |
| using ::mpact::sim::cheriot::Vmsbf; |
| using ::mpact::sim::cheriot::Vmsif; |
| using ::mpact::sim::cheriot::Vmsof; |
| using ::mpact::sim::cheriot::VmvFromScalar; |
| using ::mpact::sim::cheriot::VmvToScalar; |
| using ::mpact::sim::cheriot::Vsext2; |
| using ::mpact::sim::cheriot::Vsext4; |
| using ::mpact::sim::cheriot::Vsext8; |
| using ::mpact::sim::cheriot::Vzext2; |
| using ::mpact::sim::cheriot::Vzext4; |
| using ::mpact::sim::cheriot::Vzext8; |
| |
| using SignedXregType = |
| SameSignedType<CheriotRegister::ValueType, int64_t>::type; |
| |
| constexpr uint8_t k5AMask[kVectorLengthInBytes] = { |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, |
| }; |
| |
| constexpr uint8_t kE7Mask[kVectorLengthInBytes] = { |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, |
| }; |
| |
| constexpr uint8_t kAllOnesMask[kVectorLengthInBytes] = { |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| }; |
| |
| class RiscVCheriotVectorUnaryInstructionsTest |
| : public RiscVCheriotVectorInstructionsTestBase {}; |
| |
| // Test move vector element 0 to scalar register. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, VmvToScalar) { |
| SetSemanticFunction(&VmvToScalar); |
| AppendRegisterOperands({}, {kRs1Name}); |
| AppendVectorRegisterOperands({kVs2}, {}); |
| for (int byte_sew : {1, 2, 4, 8}) { |
| int vlen = kVectorLengthInBytes / byte_sew; |
| uint32_t vtype = |
| (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4]; |
| ConfigureVectorUnit(vtype, vlen); |
| // Test 10 different values. |
| for (int i = 0; i < 10; i++) { |
| int64_t value; |
| switch (byte_sew) { |
| case 1: { |
| auto val8 = RandomValue<int8_t>(); |
| value = static_cast<int64_t>(val8); |
| SetVectorRegisterValues<int8_t>( |
| {{kVs2Name, absl::Span<const int8_t>(&val8, 1)}}); |
| break; |
| } |
| case 2: { |
| auto val16 = RandomValue<int16_t>(); |
| value = static_cast<int64_t>(val16); |
| SetVectorRegisterValues<int16_t>( |
| {{kVs2Name, absl::Span<const int16_t>(&val16, 1)}}); |
| break; |
| } |
| case 4: { |
| auto val32 = RandomValue<int32_t>(); |
| value = static_cast<int64_t>(val32); |
| SetVectorRegisterValues<int32_t>( |
| {{kVs2Name, absl::Span<const int32_t>(&val32, 1)}}); |
| break; |
| } |
| case 8: { |
| auto val64 = RandomValue<int64_t>(); |
| value = val64; |
| SetVectorRegisterValues<int64_t>( |
| {{kVs2Name, absl::Span<const int64_t>(&val64, 1)}}); |
| break; |
| } |
| } |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRs1]->data_buffer()->Get<SignedXregType>(0), |
| static_cast<SignedXregType>(value)); |
| } |
| } |
| } |
| |
| // Test move scalar to vector element 0. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, VmvFromScalar) { |
| SetSemanticFunction(&VmvFromScalar); |
| AppendRegisterOperands({kRs1Name}, {}); |
| AppendVectorRegisterOperands({}, {kVs2}); |
| for (int byte_sew : {1, 2, 4, 8}) { |
| int vlen = kVectorLengthInBytes / byte_sew; |
| uint32_t vtype = |
| (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[4]; |
| ConfigureVectorUnit(vtype, vlen); |
| // Test 10 different values. |
| for (int i = 0; i < 10; i++) { |
| auto value = RandomValue<SignedXregType>(); |
| SetRegisterValues<SignedXregType>({{kRs1Name, value}}); |
| instruction_->Execute(); |
| switch (byte_sew) { |
| case 1: |
| EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int8_t>(0), |
| static_cast<int8_t>(value)); |
| break; |
| case 2: |
| EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int16_t>(0), |
| static_cast<int16_t>(value)); |
| break; |
| case 4: |
| EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int32_t>(0), |
| static_cast<int32_t>(value)); |
| break; |
| case 8: |
| EXPECT_EQ(vreg_[kVs2]->data_buffer()->Get<int64_t>(0), |
| static_cast<int64_t>(value)); |
| break; |
| } |
| } |
| } |
| } |
| |
| // Test vector mask population count. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vcpop) { |
| uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; |
| SetSemanticFunction(&Vcpop); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| AppendRegisterOperands({}, {kRdName}); |
| for (int vlen : {1, 8, 32, 48, 127, 200}) { |
| ConfigureVectorUnit(vtype, vlen); |
| // All 1s for mask and vector. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, kAllOnesMask}, {kVmaskName, kAllOnesMask}}); |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0), |
| vlen); |
| |
| // Mask is inverse of vector. Will result in 0. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}}); |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0), 0); |
| } |
| } |
| |
| // Test vector mask find first set. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vfirst) { |
| SetSemanticFunction(&Vfirst); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {}); |
| AppendRegisterOperands({}, {kRdName}); |
| uint8_t reg_value[kVectorLengthInBytes]; |
| // Set vtype to byte vector, and vector lmul to 8. |
| uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; |
| ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8); |
| |
| // Clear the reg_value array. |
| std::memset(reg_value, 0, kVectorLengthInBytes); |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}}); |
| // Execute the instruction. The result should be minus 1. |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<SignedXregType>(0), -1); |
| |
| // Pick a random location 20 times and set that bit to 1. Test first that |
| // the correct value is returned, then clear the mask bit that corresponds to |
| // that value, and ensure that now the result is -1. |
| for (int i = 0; i < 20; i++) { |
| // Clear the reg_value array. |
| std::memset(reg_value, 0, kVectorLengthInBytes); |
| // Get a random value for index to set. |
| uint32_t index = absl::Uniform(absl::IntervalClosed, bitgen_, 0, |
| kVectorLengthInBytes * 8 - 1); |
| // Compute the byte and bit index to be set. |
| auto byte_index = index >> 3; |
| auto bit_index = index & 0b111; |
| // Set the bit in the source register. |
| reg_value[byte_index] |= 1 << bit_index; |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}}); |
| // Execute the instruction. The result should be the index value. |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<CheriotRegister::ValueType>(0), |
| index); |
| |
| // Clear the mask bit for the bit that was set. |
| auto mask_db = vreg_[kVmask]->data_buffer()->Get<uint8_t>(); |
| mask_db[byte_index] &= ~(1 << bit_index); |
| // Execute the instruction. The result should be minus 1. |
| instruction_->Execute(); |
| EXPECT_EQ(creg_[kRd]->data_buffer()->Get<SignedXregType>(0), -1); |
| } |
| } |
| |
| // Zero extension from sew/2 to sew. Test for sew of 16, 32 and 64 bits. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_16) { |
| SetSemanticFunction(&Vzext2); |
| UnaryOpTestHelperV<uint16_t, uint8_t>( |
| "Vzext2_16", /*sew*/ 16, instruction_, |
| [](uint8_t vs2) -> uint16_t { return vs2; }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_32) { |
| SetSemanticFunction(&Vzext2); |
| UnaryOpTestHelperV<uint32_t, uint16_t>( |
| "Vzext2_32", /*sew*/ 32, instruction_, |
| [](uint16_t vs2) -> uint32_t { return vs2; }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext2_64) { |
| SetSemanticFunction(&Vzext2); |
| UnaryOpTestHelperV<uint64_t, uint32_t>( |
| "Vzext2_64", /*sew*/ 64, instruction_, |
| [](uint32_t vs2) -> uint64_t { return vs2; }); |
| } |
| |
| // Sign extension from sew/2 to sew. Testing for sew of 16, 32 and 64. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_16) { |
| SetSemanticFunction(&Vsext2); |
| UnaryOpTestHelperV<int16_t, int8_t>("Vsext2_16", /*sew*/ 16, instruction_, |
| [](int8_t vs2) -> int16_t { |
| int16_t res = static_cast<int16_t>(vs2); |
| return res; |
| }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_32) { |
| SetSemanticFunction(&Vsext2); |
| UnaryOpTestHelperV<int32_t, int16_t>( |
| "Vsext2_32", /*sew*/ 32, instruction_, [](int16_t vs2) -> int32_t { |
| int32_t res = static_cast<int32_t>(vs2); |
| return res; |
| }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext2_64) { |
| SetSemanticFunction(&Vsext2); |
| UnaryOpTestHelperV<int64_t, int32_t>( |
| "Vsext2_64", /*sew*/ 64, instruction_, [](int32_t vs2) -> int64_t { |
| int64_t res = static_cast<int64_t>(vs2); |
| return res; |
| }); |
| } |
| |
| // Zero extension from sew/4 to sew. Testing for sew of 32 and 64. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext4_32) { |
| SetSemanticFunction(&Vzext4); |
| UnaryOpTestHelperV<uint32_t, uint8_t>( |
| "Vzext4_32", /*sew*/ 32, instruction_, |
| [](uint8_t vs2) -> uint32_t { return vs2; }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext4_64) { |
| SetSemanticFunction(&Vzext4); |
| UnaryOpTestHelperV<uint64_t, uint16_t>( |
| "Vzext4_32", /*sew*/ 64, instruction_, |
| [](uint16_t vs2) -> uint64_t { return vs2; }); |
| } |
| |
| // Sign extension from sew/4 to sew. Testing for sew of 32 and 64. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext4_32) { |
| SetSemanticFunction(&Vsext4); |
| UnaryOpTestHelperV<int32_t, int8_t>("Vzext4_32", /*sew*/ 32, instruction_, |
| [](int8_t vs2) -> int32_t { |
| int32_t res = static_cast<int32_t>(vs2); |
| return res; |
| }); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext4_64) { |
| SetSemanticFunction(&Vsext4); |
| UnaryOpTestHelperV<int64_t, int16_t>( |
| "Vzext4_64", /*sew*/ 64, instruction_, [](int16_t vs2) -> int64_t { |
| int64_t res = static_cast<int64_t>(vs2); |
| return res; |
| }); |
| } |
| |
| // Zero extension from sew/8 to sew. Testing for sew of 64. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vzext8_64) { |
| SetSemanticFunction(&Vzext8); |
| UnaryOpTestHelperV<uint64_t, uint8_t>("Vsext8_64", /*sew*/ 64, instruction_, |
| [](uint8_t vs2) -> uint64_t { |
| uint64_t res = vs2; |
| return (res << 56) >> 56; |
| }); |
| } |
| |
| // Sign extension from sew/8 to sew. Testing for sew of 64. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vsext8_64) { |
| SetSemanticFunction(&Vsext8); |
| UnaryOpTestHelperV<int64_t, int8_t>("Vzext8_64", /*sew*/ 64, instruction_, |
| [](int8_t vs2) -> int64_t { |
| int64_t res = static_cast<int64_t>(vs2); |
| return res; |
| }); |
| } |
| |
| // Test "set before first mask bit". |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsbf) { |
| SetSemanticFunction(&Vmsbf); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {kVd}); |
| uint8_t reg_value[kVectorLengthInBytes]; |
| // Set vtype to byte vector, and vector lmul to 8. |
| uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; |
| ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8); |
| |
| // Clear the reg_value array. |
| std::memset(reg_value, 0, kVectorLengthInBytes); |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}}); |
| // Execute the instruction. The result should be all 1s. |
| instruction_->Execute(); |
| auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(dest_span[i], 0b1111'1111) << "Index: " << i; |
| } |
| |
| // Pick a random location 20 times and set that bit to 1. Test first that |
| // the vector mask is produced. |
| for (int i = 0; i < 20; i++) { |
| // Clear the reg_value array. |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}}); |
| // Get a random value for which bit index to set. |
| uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0, |
| kVectorLengthInBytes * 8); |
| // Compute the byte and bit index to be set. |
| auto byte_index = index >> 3; |
| auto bit_index = index & 0b111; |
| // Set the bit in the source register and mask registers. |
| auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>(); |
| auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>(); |
| mask_span[byte_index] |= 1 << bit_index; |
| src_span[byte_index] |= 1 << bit_index; |
| // Execute the instruction. The result should be the index value. |
| instruction_->Execute(); |
| // Check results. |
| dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| // First check all the flag values before the byte where the index is. |
| for (int j = 0; j < byte_index; j++) { |
| EXPECT_EQ(dest_span[j], |
| (0b1111'1111 & mask_span[j]) | (dest_span[j] & ~mask_span[j])); |
| } |
| // Check the flag values of the byte where the index is. |
| EXPECT_EQ(dest_span[byte_index], |
| (((1 << bit_index) - 1) & mask_span[byte_index]) | |
| (dest_span[byte_index] & ~mask_span[byte_index])); |
| // Check the flag values after the byte where the index is. |
| for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) { |
| EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]); |
| } |
| } |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsof) { |
| SetSemanticFunction(&Vmsof); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {kVd}); |
| uint8_t reg_value[kVectorLengthInBytes]; |
| // Set vtype to byte vector, and vector lmul to 8. |
| uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; |
| ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8); |
| |
| // Clear the reg_value array. |
| std::memset(reg_value, 0, kVectorLengthInBytes); |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}}); |
| // Execute the instruction. The result should be all 1s. |
| instruction_->Execute(); |
| auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(dest_span[i], 0) << "Index: " << i; |
| } |
| |
| // Pick a random location 20 times and set that bit to 1. Test first that |
| // the vector mask is produced. |
| for (int i = 0; i < 20; i++) { |
| // Clear the reg_value array. |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}}); |
| // Get a random value for which bit index to set. |
| uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0, |
| kVectorLengthInBytes * 8); |
| // Compute the byte and bit index to be set. |
| auto byte_index = index >> 3; |
| auto bit_index = index & 0b111; |
| // Set the bit in the source register and mask registers. |
| auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>(); |
| auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>(); |
| mask_span[byte_index] |= 1 << bit_index; |
| src_span[byte_index] |= 1 << bit_index; |
| // Execute the instruction. The result should be the index value. |
| instruction_->Execute(); |
| // Check results. |
| dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| // First check all the flag values before the byte where the index is. |
| for (int j = 0; j < byte_index; j++) { |
| EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]); |
| } |
| // Check the flag values of the byte where the index is. |
| EXPECT_EQ(dest_span[byte_index], |
| ((1 << bit_index) & mask_span[byte_index]) | |
| (dest_span[byte_index] & ~mask_span[byte_index])) |
| << " dest: " << std::hex << (int)dest_span[byte_index] |
| << " mask: " << (int)mask_span[byte_index]; |
| // Check the flag values after the byte where the index is. |
| for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) { |
| EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]); |
| } |
| } |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vmsif) { |
| SetSemanticFunction(&Vmsif); |
| AppendVectorRegisterOperands({kVs2, kVmask}, {kVd}); |
| uint8_t reg_value[kVectorLengthInBytes]; |
| // Set vtype to byte vector, and vector lmul to 8. |
| uint32_t vtype = (kSewSettingsByByteSize[1] << 3) | kLmulSettingByLogSize[7]; |
| ConfigureVectorUnit(vtype, kVectorLengthInBytes * 8); |
| |
| // Clear the reg_value array. |
| std::memset(reg_value, 0, kVectorLengthInBytes); |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, reg_value}, {kVmaskName, kAllOnesMask}}); |
| // Execute the instruction. The result should be all 1s. |
| instruction_->Execute(); |
| auto dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| for (int i = 0; i < kVectorLengthInBytes; i++) { |
| EXPECT_EQ(dest_span[i], 0b1111'1111) << "Index: " << i; |
| } |
| |
| // Pick a random location 20 times and set that bit to 1. Test first that |
| // the vector mask is produced. |
| for (int i = 0; i < 20; i++) { |
| // Clear the reg_value array. |
| // Set the register values. |
| SetVectorRegisterValues<uint8_t>( |
| {{kVs2Name, kA5Mask}, {kVmaskName, k5AMask}}); |
| // Get a random value for which bit index to set. |
| uint32_t index = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0, |
| kVectorLengthInBytes * 8); |
| // Compute the byte and bit index to be set. |
| auto byte_index = index >> 3; |
| auto bit_index = index & 0b111; |
| // Set the bit in the source register and mask registers. |
| auto mask_span = vreg_[kVmask]->data_buffer()->Get<uint8_t>(); |
| auto src_span = vreg_[kVs2]->data_buffer()->Get<uint8_t>(); |
| mask_span[byte_index] |= 1 << bit_index; |
| src_span[byte_index] |= 1 << bit_index; |
| // Execute the instruction. The result should be the index value. |
| instruction_->Execute(); |
| // Check results. |
| dest_span = vreg_[kVd]->data_buffer()->Get<uint8_t>(); |
| // First check all the flag values before the byte where the index is. |
| for (int j = 0; j < byte_index; j++) { |
| EXPECT_EQ(dest_span[j], |
| (0b1111'1111 & mask_span[j]) | (dest_span[j] & ~mask_span[j])); |
| } |
| // Check the flag values of the byte where the index is. |
| EXPECT_EQ(dest_span[byte_index], |
| (((1 << (bit_index + 1)) - 1) & mask_span[byte_index]) | |
| (dest_span[byte_index] & ~mask_span[byte_index])); |
| // Check the flag values after the byte where the index is. |
| for (int j = byte_index + 1; j < kVectorLengthInBytes; j++) { |
| EXPECT_EQ(dest_span[j], dest_span[j] & ~mask_span[j]); |
| } |
| } |
| } |
| |
| // Helper function for testing Viota instructions. |
| template <typename T> |
| void TestViota(RiscVCheriotVectorUnaryInstructionsTest *tester, |
| Instruction *inst) { |
| // Set up vector unit. |
| int byte_sew = sizeof(T); |
| uint32_t vtype = |
| (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[7]; |
| tester->ConfigureVectorUnit(vtype, 1024); |
| int vlen = tester->rv_vector()->vector_length(); |
| int num_reg = |
| (vlen * byte_sew + kVectorLengthInBytes - 1) / kVectorLengthInBytes; |
| int num_per_reg = kVectorLengthInBytes / byte_sew; |
| int num_values_per_reg = kVectorLengthInBytes / sizeof(T); |
| |
| // Set up instruction. |
| tester->SetSemanticFunction(&Viota); |
| tester->AppendVectorRegisterOperands({kVs2, kVmask}, {kVd}); |
| tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kE7Mask}}); |
| int count = vlen; |
| // Initialize vs2 to random values. |
| auto span = tester->vreg()[kVs2]->data_buffer()->Get<T>(); |
| for (int i = 0; i < num_values_per_reg; i++) { |
| span[i] = tester->RandomValue<T>(); |
| } |
| |
| for (int reg = kVd; reg < kVd + num_reg; reg++) { |
| auto reg_span = tester->vreg()[reg]->data_buffer()->Get<T>(); |
| for (int i = 0; i < num_per_reg; i++) { |
| reg_span[i] = static_cast<T>(count--); |
| } |
| } |
| |
| // Execute instruction. |
| inst->Execute(); |
| |
| // Check results. |
| const auto mask_span = tester->vreg()[kVmask]->data_buffer()->Get<uint8_t>(); |
| count = 0; |
| for (int i = 0; i < vlen; i++) { |
| int reg = kVd + i / num_per_reg; |
| int reg_index = i % num_per_reg; |
| const auto rs2_span = tester->vreg()[kVs2]->data_buffer()->Get<uint8_t>(); |
| auto value = tester->vreg()[reg]->data_buffer()->Get<T>(reg_index); |
| int mask_index = i >> 3; |
| int mask_offset = i & 0b111; |
| int mask_value = (mask_span[mask_index] >> mask_offset) & 0b1; |
| const bool rs2_bit = (rs2_span[mask_index] >> mask_offset) & 0b1; |
| if (mask_value) { |
| EXPECT_EQ(value, static_cast<T>(count)) << "active index: " << i; |
| if (rs2_bit) { |
| count++; |
| } |
| } else { |
| EXPECT_EQ(value, static_cast<T>(vlen - i)) << "inactive index: " << i; |
| } |
| } |
| } |
| |
| // Test the viota instruction. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota8) { |
| TestViota<uint8_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota16) { |
| TestViota<uint16_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota32) { |
| TestViota<uint32_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Viota64) { |
| TestViota<uint64_t>(this, instruction_); |
| } |
| |
| // Helper function for testing Vid instructions. |
| template <typename T> |
| void TestVid(RiscVCheriotVectorUnaryInstructionsTest *tester, |
| Instruction *inst) { |
| // Initialize the vector unit. |
| int byte_sew = sizeof(T); |
| uint32_t vtype = |
| (kSewSettingsByByteSize[byte_sew] << 3) | kLmulSettingByLogSize[7]; |
| int vlen = tester->rv_vector()->vector_length(); |
| int num_reg = |
| (vlen * byte_sew + kVectorLengthInBytes - 1) / kVectorLengthInBytes; |
| int num_per_reg = kVectorLengthInBytes / byte_sew; |
| tester->ConfigureVectorUnit(vtype, 1024); |
| |
| // Configure the instruction. |
| tester->SetSemanticFunction(&Vid); |
| tester->AppendVectorRegisterOperands({kVmask}, {kVd}); |
| tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kE7Mask}}); |
| int count = vlen; |
| for (int reg = kVd; reg < kVd + num_reg; reg++) { |
| auto reg_span = tester->vreg()[reg]->data_buffer()->Get<T>(); |
| for (int i = 0; i < num_per_reg; i++) { |
| reg_span[i] = static_cast<T>(count--); |
| } |
| } |
| |
| // Execute the instruction. |
| inst->Execute(); |
| |
| // Check the results. |
| auto mask_span = tester->vreg()[kVmask]->data_buffer()->Get<T>(); |
| count = 0; |
| for (int i = 0; i < vlen; i++) { |
| int reg = kVd + i / num_per_reg; |
| int reg_index = i % num_per_reg; |
| auto value = tester->vreg()[reg]->data_buffer()->Get<T>(reg_index); |
| int mask_index = i >> 3; |
| int mask_offset = i & 0b111; |
| int mask_value = (mask_span[mask_index] >> mask_offset) & 0b1; |
| if (mask_value) { |
| EXPECT_EQ(value, static_cast<T>(i)) << "active index: " << i; |
| count++; |
| } else { |
| EXPECT_EQ(value, static_cast<T>(vlen - i)) << "inactive index: " << i; |
| } |
| } |
| } |
| |
| // Vid instructions. |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid8) { |
| TestVid<uint8_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid16) { |
| TestVid<uint16_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid32) { |
| TestVid<uint32_t>(this, instruction_); |
| } |
| |
| TEST_F(RiscVCheriotVectorUnaryInstructionsTest, Vid64) { |
| TestVid<uint64_t>(this, instruction_); |
| } |
| } // namespace |