blob: 8fd7ad52eec130aa7765ad1218882fc03d2b9f7e [file] [log] [blame]
// Copyright 2024 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_permute_instructions.h"
#include <cstdint>
#include <vector>
#include "absl/random/random.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 "riscv//riscv_register.h"
// This file contains tests for the RiscV vector permute instructions.
namespace {
using ::mpact::sim::cheriot::CheriotRegister;
using ::mpact::sim::generic::Instruction;
using ::mpact::sim::riscv::RVVectorRegister;
using ::mpact::sim::cheriot::Vcompress;
using ::mpact::sim::cheriot::Vrgather;
using ::mpact::sim::cheriot::Vrgatherei16;
using ::mpact::sim::cheriot::Vslide1down;
using ::mpact::sim::cheriot::Vslide1up;
using ::mpact::sim::cheriot::Vslidedown;
using ::mpact::sim::cheriot::Vslideup;
class RiscVCheriotVectorPermuteInstructionsTest
: public RiscVCheriotVectorInstructionsTestBase {};
// Helper function for vector-vector vrgather instructions.
template <typename T, typename I>
void VrgatherVVHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
Instruction *inst) {
auto *rv_vector = tester->rv_vector();
// Configure vector unit for sew and maximum lmul.
uint32_t vtype = 0;
int max_regs = 8;
if (sizeof(I) > sizeof(T)) {
// This happens for vrgatherei16 when sew is 8.
vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[6];
max_regs = 4;
} else {
vtype = (kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
}
tester->ConfigureVectorUnit(vtype, 2048);
int vlen = rv_vector->vector_length();
int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
int max_index = num_values_per_reg * max_regs;
int num_indices_per_reg = kVectorLengthInBytes / sizeof(I);
// Initialize vs2 and vs1 to random values.
for (int reg = kVs2; reg < kVs2 + max_regs; reg++) {
auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
span[i] = tester->RandomValue<T>();
}
}
for (int reg = kVs1; reg < kVs1 + max_regs; reg++) {
auto span = tester->vreg()[reg]->data_buffer()->Get<I>();
for (int i = 0; i < num_indices_per_reg; i++) {
span[i] =
absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
}
}
tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
inst->Execute();
for (int i = 0; i < vlen; i++) {
int value_reg_offset = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
int index_reg_offset = i / num_indices_per_reg;
int index_elem_index = i % num_indices_per_reg;
int mask_index = i >> 8;
int mask_offset = i & 0b111;
bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
// Get the destination value.
T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
if (mask_value) {
// If it's an active element, get the index value.
I indx = tester->vreg()[kVs1 + index_reg_offset]->data_buffer()->Get<I>(
index_elem_index);
if (indx >= max_index) { // If the index is out of range, compare to 0.
EXPECT_EQ(0, dst);
} else { // Else, get the src value at that index and compare.
int reg = kVs2 + indx / num_values_per_reg;
int element = indx % num_values_per_reg;
T src = tester->vreg()[reg]->data_buffer()->Get<T>(element);
EXPECT_EQ(src, dst);
}
} else { // Inactive values are unchanged.
T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst) << "index: " << i << " offset: " << value_reg_offset
<< " elem: " << value_elem_index;
}
}
}
// Helper function for vector-scalar vrgather instructions.
template <typename T>
void VrgatherVSHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
Instruction *inst) {
auto *rv_vector = tester->rv_vector();
// Configure vector unit.
uint32_t vtype =
(kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
tester->ConfigureVectorUnit(vtype, 2048);
int vlen = rv_vector->vector_length();
int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
int max_index = num_values_per_reg * 8;
// Initialize vs2 to random values.
for (int reg = kVs2; reg < kVs2 + 8; reg++) {
auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
span[i] = tester->RandomValue<T>();
}
}
tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
// Try 20 different index values randomly.
for (int num = 0; num < 20; num++) {
// Set a random index value.
CheriotRegister::ValueType index_value =
absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
tester->SetRegisterValues<CheriotRegister::ValueType>(
{{kRs1Name, index_value}});
// Execute the instruction.
inst->Execute();
for (int i = 0; i < vlen; i++) {
int value_reg_offset = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
int mask_index = i >> 8;
int mask_offset = i & 0b111;
bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
// Get the destination value.
T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
if (mask_value) { // If it's an active value.
// If the index is out of range, it's 0.
if (index_value >= max_index) {
EXPECT_EQ(0, dst) << "max: " << max_index << " indx: " << index_value;
} else { // Otherwise, get the src value from vs2 and compare.
int reg = index_value / num_values_per_reg;
int element = index_value % num_values_per_reg;
T src = tester->vreg()[kVs2 + reg]->data_buffer()->Get<T>(element);
EXPECT_EQ(src, dst);
}
} else { // Inactive values are unchanged.
T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst);
}
}
}
}
// Test vrgather instruction for 1, 2, 4, and 8 byte SEWs - vector index.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV8) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint8_t, uint8_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV16) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint16_t, uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV32) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint32_t, uint32_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVV64) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint64_t, uint64_t>(this, instruction_);
}
// Test vrgather instruction for 1, 2, 4, and 8 byte SEWs - scalar index.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS8) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
VrgatherVSHelper<uint8_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS16) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
VrgatherVSHelper<uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS32) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
VrgatherVSHelper<uint32_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, VrgatherVS64) {
SetSemanticFunction(&Vrgather);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
VrgatherVSHelper<uint64_t>(this, instruction_);
}
// Test vrgatherei16 instruction for SEW values of 1, 2, 4, and 8 bytes.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV8) {
SetSemanticFunction(&Vrgatherei16);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint8_t, uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV16) {
SetSemanticFunction(&Vrgatherei16);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint16_t, uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV32) {
SetSemanticFunction(&Vrgatherei16);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint32_t, uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vrgatherei16VV64) {
SetSemanticFunction(&Vrgatherei16);
AppendVectorRegisterOperands({kVs2, kVs1, kVmask}, {kVd});
VrgatherVVHelper<uint64_t, uint16_t>(this, instruction_);
}
// Helper function for slideup/down instructions.
template <typename T>
void SlideHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
Instruction *inst, bool is_slide_up) {
auto *rv_vector = tester->rv_vector();
uint32_t vtype =
(kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
tester->ConfigureVectorUnit(vtype, 2048);
int vlen = rv_vector->vector_length();
int max_vlen = rv_vector->max_vector_length();
int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
// Initialize vs2 to random values.
for (int reg = 0; reg < 8; reg++) {
auto src_span = tester->vreg()[kVs2 + reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
src_span[i] = tester->RandomValue<T>();
}
}
tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
// Try 20 different shift values randomly.
for (int num = 0; num < 20; num++) {
CheriotRegister::ValueType shift_value =
absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
tester->SetRegisterValues<CheriotRegister::ValueType>(
{{kRs1Name, shift_value}});
// Initialize the destination register set.
int value_indx = 0;
for (int reg = 0; reg < 8; reg++) {
auto dst_span = tester->vreg()[kVd + reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
dst_span[i] = value_indx++;
}
}
inst->Execute();
for (int i = 0; i < vlen; i++) {
int value_reg_offset = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
int mask_index = i >> 8;
int mask_offset = i & 0b111;
bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
if (is_slide_up) { // For slide up instruction.
if ((i < shift_value) || (!mask_value)) {
// If it's an inactive element, or the index is less than the shift
// amount, the element is unchanged.
T value = static_cast<T>(i);
EXPECT_EQ(value, dst) << "indx: " << i;
} else {
// Active elements are shifted up by 'shift_value'.
int src_reg_offset = (i - shift_value) / num_values_per_reg;
int src_reg_index = (i - shift_value) % num_values_per_reg;
T src = tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
src_reg_index);
EXPECT_EQ(src, dst) << "indx: " << i;
}
} else { // This is slide down.
if (mask_value) {
if (i + shift_value >= max_vlen) {
// Active elements originating beyond max_vlen are 0.
EXPECT_EQ(0, dst) << "indx: " << i;
} else {
// Active elements are shifted down by 'shift_value'.
int src_reg_offset = (i + shift_value) / num_values_per_reg;
int src_reg_index = (i + shift_value) % num_values_per_reg;
T src =
tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
src_reg_index);
EXPECT_EQ(src, dst) << "indx: " << i;
}
} else {
// All inactive elements are unchanged.
T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst) << "indx: " << i;
}
}
}
}
}
// Test vslideup instruction for SEW values of 1, 2, 4, and 8 bytes.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup8) {
SetSemanticFunction(&Vslideup);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup16) {
SetSemanticFunction(&Vslideup);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup32) {
SetSemanticFunction(&Vslideup);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslideup64) {
SetSemanticFunction(&Vslideup);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ true);
}
// Test vslidedown instruction for SEW values of 1, 2, 4, and 8 bytes.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown8) {
SetSemanticFunction(&Vslidedown);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint8_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown16) {
SetSemanticFunction(&Vslidedown);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint16_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown32) {
SetSemanticFunction(&Vslidedown);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint32_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslidedown64) {
SetSemanticFunction(&Vslidedown);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
SlideHelper<uint64_t>(this, instruction_, /*is_slide_up*/ false);
}
template <typename T>
void Slide1Helper(RiscVCheriotVectorPermuteInstructionsTest *tester,
Instruction *inst, bool is_slide_up) {
auto *rv_vector = tester->rv_vector();
uint32_t vtype =
(kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
tester->ConfigureVectorUnit(vtype, 2048);
int vlen = rv_vector->vector_length();
int max_vlen = rv_vector->max_vector_length();
int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
// Initialize vs2 to random values.
for (int reg = kVs2; reg < kVs2 + 8; reg++) {
auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
span[i] = tester->RandomValue<T>();
}
}
tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
// Try 20 different shift values randomly.
for (int num = 0; num < 20; num++) {
CheriotRegister::ValueType fill_in_value =
absl::Uniform(absl::IntervalClosed, tester->bitgen(), 0, 2 * vlen);
tester->SetRegisterValues<CheriotRegister::ValueType>(
{{kRs1Name, fill_in_value}});
fill_in_value = static_cast<T>(fill_in_value);
inst->Execute();
for (int i = 0; i < vlen; i++) {
int value_reg_offset = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
int mask_index = i >> 8;
int mask_offset = i & 0b111;
bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
T dst = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
if (is_slide_up) {
if (!mask_value) { // Inactive elements are unchanged.
T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst) << "i: " << i;
} else {
if (i == 0) { // The first value should match the fill-in.
EXPECT_EQ(fill_in_value, dst) << "i: " << i;
} else { // Other values are shifted by 1.
int src_reg_offset = (i - 1) / num_values_per_reg;
int src_reg_index = (i - 1) % num_values_per_reg;
T src =
tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
src_reg_index);
EXPECT_EQ(src, dst) << "i: " << i;
}
}
} else { // This is slide down.
if (mask_value) {
if (i + 1 >= max_vlen) { // The last value should match the fill-in.
EXPECT_EQ(fill_in_value, dst);
} else { // Other elements are shifted by 1.
int src_reg_offset = (i + 1) / num_values_per_reg;
int src_reg_index = (i + 1) % num_values_per_reg;
T src =
tester->vreg()[kVs2 + src_reg_offset]->data_buffer()->Get<T>(
src_reg_index);
EXPECT_EQ(src, dst) << "i: " << i;
}
} else { // Inactive elements are unchanged.
T src = tester->vreg()[kVd + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst) << "i: " << i;
}
}
}
}
}
// Test vslide1up instruction for SEW values of 1, 2, 4, and 8 bytes.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up8) {
SetSemanticFunction(&Vslide1up);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up16) {
SetSemanticFunction(&Vslide1up);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up32) {
SetSemanticFunction(&Vslide1up);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ true);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1up64) {
SetSemanticFunction(&Vslide1up);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ true);
}
// Test vslide1down instruction for SEW values of 1, 2, 4, and 8 bytes.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down8) {
SetSemanticFunction(&Vslide1down);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint8_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down16) {
SetSemanticFunction(&Vslide1down);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint16_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down32) {
SetSemanticFunction(&Vslide1down);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint32_t>(this, instruction_, /*is_slide_up*/ false);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vslide1down64) {
SetSemanticFunction(&Vslide1down);
AppendVectorRegisterOperands({kVs2}, {});
AppendRegisterOperands({kRs1Name}, {});
AppendVectorRegisterOperands({kVmask}, {kVd});
Slide1Helper<uint64_t>(this, instruction_, /*is_slide_up*/ false);
}
template <typename T>
void CompressHelper(RiscVCheriotVectorPermuteInstructionsTest *tester,
Instruction *inst) {
auto *rv_vector = tester->rv_vector();
uint32_t vtype =
(kSewSettingsByByteSize[sizeof(T)] << 3) | kLmulSettingByLogSize[7];
tester->ConfigureVectorUnit(vtype, 2048);
int vlen = rv_vector->vector_length();
int num_values_per_reg = kVectorLengthInBytes / sizeof(T);
auto vd_span = tester->vreg()[kVd]->data_buffer()->Get<T>();
std::vector<T> origin_vd_values(vd_span.begin(), vd_span.end());
// Initialize vs2 to random values.
for (int reg = kVs2; reg < kVs2 + 8; reg++) {
auto span = tester->vreg()[reg]->data_buffer()->Get<T>();
for (int i = 0; i < num_values_per_reg; i++) {
span[i] = tester->RandomValue<T>();
}
}
tester->SetVectorRegisterValues<uint8_t>({{kVmaskName, kA5Mask}});
inst->Execute();
// First check all the elements that were compressed (mask bit true).
int offset = 0;
for (int i = 0; i < vlen; i++) {
int value_reg_offset = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
int mask_index = i >> 8;
int mask_offset = i & 0b111;
bool mask_value = (kA5Mask[mask_index] >> mask_offset) & 0b1;
if (mask_value) {
T src = tester->vreg()[kVs2 + value_reg_offset]->data_buffer()->Get<T>(
value_elem_index);
int dst_reg_index = offset / num_values_per_reg;
int dst_element_index = offset % num_values_per_reg;
T dst = tester->vreg()[kVd + dst_reg_index]->data_buffer()->Get<T>(
dst_element_index);
EXPECT_EQ(src, dst) << "index: " << i;
offset++;
}
}
// The remaining elements are unchanged.
for (int i = offset; i < vlen; i++) {
int value_reg_index = i / num_values_per_reg;
int value_elem_index = i % num_values_per_reg;
T src = origin_vd_values[value_elem_index];
T dst = tester->vreg()[kVd + value_reg_index]->data_buffer()->Get<T>(
value_elem_index);
EXPECT_EQ(src, dst) << "index: " << i;
}
}
// Test compress instruction for SEW of 8, 16, 32, and 64.
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress8) {
SetSemanticFunction(&Vcompress);
AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
CompressHelper<uint8_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress16) {
SetSemanticFunction(&Vcompress);
AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
CompressHelper<uint16_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress32) {
SetSemanticFunction(&Vcompress);
AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
CompressHelper<uint32_t>(this, instruction_);
}
TEST_F(RiscVCheriotVectorPermuteInstructionsTest, Vcompress64) {
SetSemanticFunction(&Vcompress);
AppendVectorRegisterOperands({kVs2, kVmask}, {kVd});
CompressHelper<uint64_t>(this, instruction_);
}
} // namespace