Add support for vector basic bit manipulation instructions. PiperOrigin-RevId: 713788510 Change-Id: I83bfe91e781828f1f463117245b4fde1fe2ff272
diff --git a/riscv/BUILD b/riscv/BUILD index cd96797..d3e2c14 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -308,6 +308,30 @@ ], ) +# TODO(julianmb): Remove this target once there is a rva23_instructions target. +cc_library( + name = "riscv_vector_basic_bit_manipulation_instructions", + srcs = [ + "riscv_vector_basic_bit_manipulation_instructions.cc", + ], + hdrs = [ + "riscv_vector_basic_bit_manipulation_instructions.h", + "riscv_vector_instruction_helpers.h", + ], + copts = [ + "-O3", + "-ffp-model=strict", + ], + deps = [ + ":riscv_state", + "@com_google_absl//absl/log", + "@com_google_absl//absl/numeric:bits", + "@com_google_absl//absl/types:span", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + ], +) + mpact_isa_decoder( name = "riscv32g_isa", src = "riscv32g.isa", @@ -606,6 +630,35 @@ ], ) +mpact_isa_decoder( + name = "zvbb_isa", + src = "riscv_zvbb.isa", + includes = [ + "riscv_vector.isa", + ], + isa_name = "ZVBB", + prefix = "zvbb", + deps = [ + ":riscv_v", + ":riscv_vector_basic_bit_manipulation_instructions", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "zvbb_bin_fmt", + src = "riscv_zvbb.bin_fmt", + decoder_name = "ZVBB", + includes = [ + "riscv32g.bin_fmt", + "riscv_vector.bin_fmt", + ], + prefix = "zvbb", + deps = [ + ":zvbb_isa", + ], +) + cc_library( name = "riscv32g_decoder", srcs = [ @@ -644,6 +697,7 @@ "riscv_getters_zba.h", "riscv_getters_zbb32.h", "riscv_getters_zbb64.h", + "riscv_getters_zvbb.h", ], deps = [ ":riscv_encoding_common", @@ -872,6 +926,41 @@ ) cc_library( + name = "zvbb_decoder", + srcs = [ + "zvbb_decoder.cc", + "zvbb_encoding.cc", + ], + hdrs = [ + "zvbb_decoder.h", + "zvbb_encoding.h", + ], + copts = ["-O3"], + deps = [ + ":riscv_encoding_common", + ":riscv_getters", + ":riscv_state", + ":riscv_vector_basic_bit_manipulation_instructions", + ":zvbb_bin_fmt", + ":zvbb_isa", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/log", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + "@com_google_mpact-sim//mpact/sim/generic:arch_state", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:program_error", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + +cc_library( name = "riscv_top", srcs = [ "riscv_top.cc",
diff --git a/riscv/riscv_getters_zvbb.h b/riscv/riscv_getters_zvbb.h new file mode 100644 index 0000000..7484890 --- /dev/null +++ b/riscv/riscv_getters_zvbb.h
@@ -0,0 +1,104 @@ +// Copyright 2025 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. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_ + +#include <cstdint> +#include <new> + +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/immediate_operand.h" +#include "mpact/sim/generic/literal_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_getters_vector.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_register_aliases.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +// The following function adds source operand getters to the given getter map. +// The function uses the template parameters to get the correct enum type +// for the instruction set being decoded. The Extractors parameter is used to +// get the correct instruction format extractor for the instruction set. +template <typename Enum, typename Extractors, typename VectorRegister> +void AddRiscVZvbbSourceVectorGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kVmask, [common]() -> SourceOperandInterface * { + auto vm = Extractors::VArith::ExtractVm(common->inst_word()); + if (vm == 1) { + // Unmasked, return the True mask. + return new RV32VectorTrueOperand(common->state()); + } + // Masked. Return the mask register. + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), 0); + }); + Insert(getter_map, *Enum::kVs1, [common]() -> SourceOperandInterface * { + auto num = Extractors::VArith::ExtractVs1(common->inst_word()); + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), num); + }); + Insert(getter_map, *Enum::kVs2, [common]() -> SourceOperandInterface * { + auto num = Extractors::VArith::ExtractVs2(common->inst_word()); + return mpact::sim::riscv::GetVectorMaskRegisterSourceOp<VectorRegister>( + common->state(), num); + }); +} + +template <typename Enum, typename Extractors, typename IntegerRegister> +void AddRiscvZvbbSourceScalarGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kRs1, [common]() -> SourceOperandInterface * { + int num = Extractors::VArith::ExtractRs1(common->inst_word()); + if (num == 0) return new generic::IntLiteralOperand<0>({1}); + return GetRegisterSourceOp<IntegerRegister>( + common->state(), absl::StrCat(RiscVState::kXregPrefix, num), + kXRegisterAliases[num]); + }); + Insert(getter_map, *Enum::kUimm5, [common]() -> SourceOperandInterface * { + const auto num = Extractors::VArith::ExtractUimm5(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); + Insert(getter_map, *Enum::kUimm6, [common]() -> SourceOperandInterface * { + const auto num = Extractors::VArith::ExtractUimm6(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); +} + +template <typename Enum, typename Extractors, typename VectorRegister> +void AddRiscVZvbbDestGetters(DestOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Destination operand getters. + Insert(getter_map, *Enum::kVd, + [common](int latency) -> DestinationOperandInterface * { + auto num = Extractors::VArith::ExtractVd(common->inst_word()); + return mpact::sim::riscv::GetVectorRegisterDestinationOp< + VectorRegister>(common->state(), latency, num); + }); +} + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_ZVBB_H_
diff --git a/riscv/riscv_vector.bin_fmt b/riscv/riscv_vector.bin_fmt index cad9aea..11a1db0 100644 --- a/riscv/riscv_vector.bin_fmt +++ b/riscv/riscv_vector.bin_fmt
@@ -43,6 +43,8 @@ unsigned opcode[7]; overlays: unsigned uimm5[5] = vs1; + unsigned uimm6[6] = func6[0], vs1; + unsigned func5[5] = func6[5..1]; signed simm5[5] = vs1; unsigned rd[5] = vd; unsigned rs1[5] = vs1;
diff --git a/riscv/riscv_vector_basic_bit_manipulation_instructions.cc b/riscv/riscv_vector_basic_bit_manipulation_instructions.cc new file mode 100644 index 0000000..d6d8273 --- /dev/null +++ b/riscv/riscv_vector_basic_bit_manipulation_instructions.cc
@@ -0,0 +1,370 @@ +// Copyright 2025 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 "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/numeric/bits.h" +#include "absl/types/span.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/riscv_vector_instruction_helpers.h" +#include "riscv/riscv_vector_state.h" + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +namespace mpact { +namespace sim { +namespace riscv { + +void RV32VUnimplementedInstruction(const Instruction *inst) { + auto *state = static_cast<RiscVState *>(inst->state()); + state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0, + *ExceptionCode::kIllegalInstruction, + /*epc*/ inst->address(), inst); +} + +namespace { +template <typename T> +T BitReverse(T input) { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result <<= 1; + result |= (input & 1); + input >>= 1; + } + return result; +} + +template <class T> +constexpr T ByteSwap(T input) { + // TODO(julianmb): Once c++23 is supported, use std::byteswap. + T result = 0; + for (int i = 0; i < sizeof(T); ++i) { + result |= ((input >> (i * 8)) & 0xFF) << ((sizeof(T) - 1 - i) * 8); + } + return result; +} +} // namespace + +void Vandn(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2, uint8_t vs1) -> uint8_t { return vs2 & ~vs1; }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2, uint16_t vs1) -> uint16_t { return vs2 & ~vs1; }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2, uint32_t vs1) -> uint32_t { return vs2 & ~vs1; }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2, uint64_t vs1) -> uint64_t { return vs2 & ~vs1; }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vbrev8(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return BitReverse(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2) -> uint16_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2) -> uint32_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2) -> uint64_t { + absl::Span<uint8_t> span = + absl::MakeSpan(reinterpret_cast<uint8_t *>(&vs2), sizeof(vs2)); + for (uint8_t &byte : span) { + byte = BitReverse(byte); + } + return vs2; + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vrev8(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2) -> uint8_t { return vs2; }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return ByteSwap(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return ByteSwap(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return ByteSwap(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vrol(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t { + uint8_t rotate_amount = vs1 & 0b0000'0111; + return absl::rotl(vs2, rotate_amount); + }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t { + uint8_t rotate_amount = vs1 & 0b0000'1111; + return absl::rotl(vs2, rotate_amount); + }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t { + uint8_t rotate_amount = vs1 & 0b0001'1111; + return absl::rotl(vs2, rotate_amount); + }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t { + uint8_t rotate_amount = vs1 & 0b0011'1111; + return absl::rotl(vs2, rotate_amount); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vror(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint8_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint8_t { + uint8_t rotate_amount = vs1 & 0b0000'0111; + return absl::rotr(vs2, rotate_amount); + }); + case 2: + return RiscVBinaryVectorOp<uint16_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint16_t { + uint8_t rotate_amount = vs1 & 0b0000'1111; + return absl::rotr(vs2, rotate_amount); + }); + case 4: + return RiscVBinaryVectorOp<uint32_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint32_t { + uint8_t rotate_amount = vs1 & 0b0001'1111; + return absl::rotr(vs2, rotate_amount); + }); + case 8: + return RiscVBinaryVectorOp<uint64_t, uint64_t, uint64_t>( + rv_vector, inst, [](uint64_t vs2, uint64_t vs1) -> uint64_t { + uint8_t rotate_amount = vs1 & 0b0011'1111; + return absl::rotr(vs2, rotate_amount); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +// Instructions that are only in Zvbb + +void Vbrev(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return BitReverse(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return BitReverse(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return BitReverse(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return BitReverse(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vclz(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::countl_zero(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::countl_zero(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::countl_zero(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::countl_zero(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vctz(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::countr_zero(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::countr_zero(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::countr_zero(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::countr_zero(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void VectorVcpop(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVUnaryVectorOp<uint8_t, uint8_t>( + rv_vector, inst, + [](uint8_t vs2) -> uint8_t { return absl::popcount(vs2); }); + case 2: + return RiscVUnaryVectorOp<uint16_t, uint16_t>( + rv_vector, inst, + [](uint16_t vs2) -> uint16_t { return absl::popcount(vs2); }); + case 4: + return RiscVUnaryVectorOp<uint32_t, uint32_t>( + rv_vector, inst, + [](uint32_t vs2) -> uint32_t { return absl::popcount(vs2); }); + case 8: + return RiscVUnaryVectorOp<uint64_t, uint64_t>( + rv_vector, inst, + [](uint64_t vs2) -> uint64_t { return absl::popcount(vs2); }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +void Vwsll(Instruction *inst) { + auto *rv_vector = static_cast<RiscVState *>(inst->state())->rv_vector(); + int sew = rv_vector->selected_element_width(); + switch (sew) { + case 1: + return RiscVBinaryVectorOp<uint16_t, uint8_t, uint8_t>( + rv_vector, inst, [](uint8_t vs2, uint8_t vs1) -> uint16_t { + return static_cast<uint16_t>(vs2) << (vs1 & 0x0F); + }); + case 2: + return RiscVBinaryVectorOp<uint32_t, uint16_t, uint16_t>( + rv_vector, inst, [](uint16_t vs2, uint16_t vs1) -> uint32_t { + return static_cast<uint32_t>(vs2) << (vs1 & 0x1F); + }); + case 4: + return RiscVBinaryVectorOp<uint64_t, uint32_t, uint32_t>( + rv_vector, inst, [](uint32_t vs2, uint32_t vs1) -> uint64_t { + return static_cast<uint64_t>(vs2) << (vs1 & 0x3F); + }); + default: + rv_vector->set_vector_exception(); + LOG(ERROR) << "Illegal SEW value"; + return; + } +} + +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv_vector_basic_bit_manipulation_instructions.h b/riscv/riscv_vector_basic_bit_manipulation_instructions.h new file mode 100644 index 0000000..9c4f1c6 --- /dev/null +++ b/riscv/riscv_vector_basic_bit_manipulation_instructions.h
@@ -0,0 +1,51 @@ +// Copyright 2025 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. + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_ + +#include "mpact/sim/generic/instruction.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using Instruction = ::mpact::sim::generic::Instruction; + +void RV32VUnimplementedInstruction(const Instruction *inst); + +// Vector bit manipulation instructions. + +// Zvkb subset of instructions +void Vandn(Instruction *); +void Vbrev8(Instruction *); +void Vrev8(Instruction *); +void Vrol(Instruction *); +void Vror(Instruction *); + +// Zvbb instructions +void Vbrev(Instruction *); +void Vclz(Instruction *); +void Vctz(Instruction *); +// There is a name collision with an existing Vcpop instruction that stores the +// result in a scalar register. This implementation stores the result in a +// vector register. +void VectorVcpop(Instruction *); +void Vwsll(Instruction *); + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_BASIC_BIT_MANIPULATION_INSTRUCTIONS_H_
diff --git a/riscv/riscv_zvbb.bin_fmt b/riscv/riscv_zvbb.bin_fmt new file mode 100644 index 0000000..a9fb0f7 --- /dev/null +++ b/riscv/riscv_zvbb.bin_fmt
@@ -0,0 +1,47 @@ +// Copyright 2025 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 "riscv/riscv32g.bin_fmt" +#include "riscv/riscv_vector.bin_fmt" + +decoder ZVBB { + namespace mpact::sim::riscv::zvbb; + opcode_enum = "OpcodeEnum"; + includes { + #include "riscv/zvbb_decoder.h" + } + RiscVZvbbInst32 = { RiscVZvkbInst32, RiscVBasicBitInst32 }; +} + +instruction group RiscVZvkbInst32[32] : VArith { + vandn_vv : VArith : func6 == 0b000'001, func3 == 0b000, opcode == 0b101'0111; + vandn_vx : VArith : func6 == 0b000'001, func3 == 0b100, opcode == 0b101'0111; + vbrev8_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'000; + vrev8_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'001; + vrol_vv : VArith : func6 == 0b010'101, func3 == 0b000, opcode == 0b101'0111; + vrol_vx : VArith : func6 == 0b010'101, func3 == 0b100, opcode == 0b101'0111; + vror_vv : VArith : func6 == 0b010'100, func3 == 0b000, opcode == 0b101'0111; + vror_vx : VArith : func6 == 0b010'100, func3 == 0b100, opcode == 0b101'0111; + vror_vi : VArith : func5 == 0b01010, func3 == 0b011, opcode == 0b101'0111; +} + +instruction group RiscVBasicBitInst32[32] : VArith { + vbrev_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'010; + vclz_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'100; + vctz_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'101; + vcpop_v : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111, vs1 == 0b01'110; + vwsll_vv : VArith : func6 == 0b110'101, func3 == 0b000, opcode == 0b101'0111; + vwsll_vx : VArith : func6 == 0b110'101, func3 == 0b100, opcode == 0b101'0111; + vwsll_vi : VArith : func6 == 0b110'101, func3 == 0b011, opcode == 0b101'0111; +}
diff --git a/riscv/riscv_zvbb.isa b/riscv/riscv_zvbb.isa new file mode 100644 index 0000000..f6f5926 --- /dev/null +++ b/riscv/riscv_zvbb.isa
@@ -0,0 +1,103 @@ +// Copyright 2025 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. + +// This file contains the ISA description of the RiscV zvbb extention +// instructions. + +isa ZVBB { + namespace mpact::sim::riscv::zvbb; + slots { + riscv_zvbb; + } +} + +// First disasm field is 18 char wide and left justified. +disasm widths = {-18}; + +#include "riscv/riscv_vector.isa" + +slot riscv_zvkb { + includes { + #include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + vandn_vv{: vs2, vs1, vmask : vd}, + disasm: "vandn.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vandn"; + vandn_vx{: vs2, rs1, vmask : vd}, + disasm: "vandn.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vandn"; + vbrev8_v{: vs2, vmask : vd}, + disasm: "vbrev8.v", "%vd, %vs2, %vmask", + semfunc: "&Vbrev8"; + vrev8_v{: vs2, vmask : vd}, + disasm: "vrev8.v", "%vd, %vs2, %vmask", + semfunc: "&Vrev8"; + vrol_vv{: vs2, vs1, vmask : vd}, + disasm: "vrol.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vrol"; + vrol_vx{: vs2, rs1, vmask : vd}, + disasm: "vrol.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vrol"; + vror_vv{: vs2, vs1, vmask : vd}, + disasm: "vror.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vror"; + vror_vx{: vs2, rs1, vmask : vd}, + disasm: "vror.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vror"; + vror_vi{: vs2, uimm6, vmask : vd}, + disasm: "vror.vi", "%vd, %vs2, %uimm6, %vmask", + semfunc: "&Vror"; + } +} + +slot riscv_zvbb : riscv_zvkb { + includes { + #include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" + } + default size = 4; + default latency = 0; + default opcode = + disasm: "Unimplemented instruction at 0x%(@:08x)", + semfunc: "&RV32VUnimplementedInstruction"; + opcodes { + vbrev_v{: vs2, vmask : vd}, + disasm: "vbrev.v", "%vd, %vs2, %vmask", + semfunc: "&Vbrev"; + vclz_v{: vs2, vmask : vd}, + disasm: "vclz.v", "%vd, %vs2, %vmask", + semfunc: "&Vclz"; + vctz_v{: vs2, vmask : vd}, + disasm: "vctz.v", "%vd, %vs2, %vmask", + semfunc: "&Vctz"; + vcpop_v{: vs2, vmask : vd}, + disasm: "vcpop.v", "%vd, %vs2, %vmask", + semfunc: "&VectorVcpop"; + vwsll_vv{: vs2, vs1, vmask : vd}, + disasm: "vwsll.vv", "%vd, %vs2, %vs1, %vmask", + semfunc: "&Vwsll"; + vwsll_vx{: vs2, rs1, vmask : vd}, + disasm: "vwsll.vx", "%vd, %vs2, %rs1, %vmask", + semfunc: "&Vwsll"; + vwsll_vi{: vs2, uimm5, vmask : vd}, + disasm: "vwsll.vi", "%vd, %vs2, %uimm5, %vmask", + semfunc: "&Vwsll"; + } +} +
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index ea7a94d..4ea63fe 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -334,6 +334,23 @@ ) cc_test( + name = "zvbb_encoding_test", + size = "small", + srcs = [ + "zvbb_encoding_test.cc", + ], + deps = [ + "//riscv:riscv32g_bitmanip_decoder", + "//riscv:riscv_state", + "//riscv:zvbb_decoder", + "//riscv:zvbb_isa", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + +cc_test( name = "riscv32_htif_semihost_test", size = "small", srcs = [ @@ -891,6 +908,24 @@ ], ) +cc_test( + name = "riscv_vector_basic_bit_manipulation_instructions_test", + size = "small", + srcs = [ + "riscv_vector_basic_bit_manipulation_test.cc", + ], + deps = [ + ":riscv_vector_instructions_test_base", + "//riscv:riscv_state", + "//riscv:riscv_v", + "//riscv:riscv_vector_basic_bit_manipulation_instructions", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + ], +) + config_setting( name = "arm_cpu", values = {"cpu": "arm"},
diff --git a/riscv/test/riscv_vector_basic_bit_manipulation_test.cc b/riscv/test/riscv_vector_basic_bit_manipulation_test.cc new file mode 100644 index 0000000..923f3cd --- /dev/null +++ b/riscv/test/riscv_vector_basic_bit_manipulation_test.cc
@@ -0,0 +1,458 @@ +// Copyright 2025 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 <sys/types.h> + +#include <cstdint> + +#include "absl/strings/str_cat.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" +#include "riscv/riscv_vector_basic_bit_manipulation_instructions.h" +#include "riscv/test/riscv_vector_instructions_test_base.h" + +// This file contains tests for the RiscV vector basic bit manipulations. + +namespace { + +using ::mpact::sim::generic::WideType; +using ::mpact::sim::riscv::RV32Register; +using ::mpact::sim::riscv::RVVectorRegister; +using ::mpact::sim::riscv::Vandn; +using ::mpact::sim::riscv::Vbrev; +using ::mpact::sim::riscv::Vbrev8; +using ::mpact::sim::riscv::Vclz; +using ::mpact::sim::riscv::Vctz; +using ::mpact::sim::riscv::VectorVcpop; +using ::mpact::sim::riscv::Vrev8; +using ::mpact::sim::riscv::Vrol; +using ::mpact::sim::riscv::Vror; +using ::mpact::sim::riscv::Vwsll; +using ::mpact::sim::riscv::test::RiscVVectorInstructionsTestBase; + +class RiscVVectorBasicBitManipulationTest + : public RiscVVectorInstructionsTestBase {}; + +// Helper function for testing the vandn_vv instruction. Generate the expected +// result using the bitwise operator. +template <typename T> +inline void VandnVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vandn); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vandn", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { return ~vs1 & vs2; }); +} + +// Helper function for testing the vandn_vx instruction. Generate the expected +// result using the bitwise operator. +template <typename T> +inline void VandnVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vandn); + tester->BinaryOpTestHelperVX<T, T, T>( + absl::StrCat("Vandn", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { return ~rs1 & vs2; }); +} + +// Helper function for testing the vbrev_v instruction. Generate the expected +// result by reversing the input bits. +template <typename T> +inline void VbrevVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vbrev); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("Vbrev", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result = (result << 1) | (vs2 & 1); + vs2 >>= 1; + } + return result; + }); +} + +// Helper function for testing the vbrev8_v instruction. Generate the expected +// result by reversing the bits in each of the input bytes. +template <typename T> +inline void Vbrev8VHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vbrev8); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("Vbrev8", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T result = 0; + for (int offset = 0; offset < sizeof(T) * 8; offset += 8) { + uint8_t byte = (vs2 >> offset) & 0xFF; + T reversed_byte = 0; + for (int j = 0; j < 8; ++j) { + reversed_byte = (reversed_byte << 1) | (byte & 1); + byte >>= 1; + } + result |= reversed_byte << offset; + } + return result; + }); +} + +// Helper function for testing the vbrev_v instruction. Generate the expected +// result by reversing the bytes of the input. +template <typename T> +inline void Vrev8VHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrev8); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("Vrev8", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T result = 0; + for (int offset = 0; offset < sizeof(T) * 8; offset += 8) { + uint8_t byte = (vs2 >> offset) & 0xff; + result = (result << 8) | byte; + } + return result; + }); +} + +// Helper function for testing the vrol_vv instruction. Generate the expected +// result by rotating the input bits left. +template <typename T> +inline void VrolVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrol); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vrol", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { + T rotate_mask = sizeof(T) * 8 - 1; + T rotate_amount = (vs1 & rotate_mask); + return (vs2 << rotate_amount) | + (vs2 >> (sizeof(T) * 8 - rotate_amount)); + }); +} + +// Helper function for testing the vrol_vx instruction. Generate the expected +// result by rotating the input bits left. +template <typename T> +inline void VrolVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vrol); + tester->BinaryOpTestHelperVX<T, T, T>( + absl::StrCat("Vrol", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { + T rotate_mask = sizeof(T) * 8 - 1; + T rotate_amount = (rs1 & rotate_mask); + return (vs2 << rotate_amount) | + (vs2 >> (sizeof(T) * 8 - rotate_amount)); + }); +} + +// Helper function for testing the vror_vv instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> T { + T rotate_mask = sizeof(T) * 8 - 1; + T rotate_amount = (vs1 & rotate_mask); + return (vs2 >> rotate_amount) | + (vs2 << (sizeof(T) * 8 - rotate_amount)); + }); +} + +// Helper function for testing the vror_vx instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> T { + T rotate_mask = sizeof(T) * 8 - 1; + T rotate_amount = (rs1 & rotate_mask); + return (vs2 >> rotate_amount) | + (vs2 << (sizeof(T) * 8 - rotate_amount)); + }); +} + +// Helper function for testing the vror_vi instruction. Generate the expected +// result by rotating the input bits right. +template <typename T> +inline void VrorVIHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vror); + tester->BinaryOpTestHelperVV<T, T, T>( + absl::StrCat("Vror", sizeof(T) * 8, "vi"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T imm) -> T { + T rotate_mask = sizeof(T) * 8 - 1; + T rotate_amount = (imm & rotate_mask); + return (vs2 >> rotate_amount) | + (vs2 << (sizeof(T) * 8 - rotate_amount)); + }); +} + +// Helper function for testing the vclz_v instruction. Generate the expected +// result by counting the number of leading zeros in the input. +template <typename T> +inline void VclzVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vclz); + tester->UnaryOpTestHelperV<T, T>( + absl::StrCat("vclz", sizeof(T) * 8, "v"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2) -> T { + T mask = static_cast<T>(1) << (sizeof(T) * 8 - 1); + for (int i = 0; i < sizeof(T) * 8; ++i) { + if ((vs2 & mask) != 0) { + return i; + } + mask >>= 1; + } + return static_cast<T>(sizeof(T) * 8); + }); +} + +// Helper function for testing the vctz_v instruction. Generate the expected +// result by counting the number of trailing zeros in the input. +template <typename T> +inline void VctzVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&Vctz); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("vctz", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T mask = static_cast<T>(1); + for (int i = 0; i < sizeof(T) * 8; ++i) { + if ((vs2 & mask) != 0) { + return i; + } + mask <<= 1; + } + return static_cast<T>(sizeof(T) * 8); + }); +} + +// Helper function for testing the vcpop_v instruction. Generate the expected +// result by counting the number of bits set in the input. +template <typename T> +inline void VcpopVHelper(RiscVVectorBasicBitManipulationTest *tester) { + tester->SetSemanticFunction(&VectorVcpop); + tester->UnaryOpTestHelperV<T, T>(absl::StrCat("vcpop", sizeof(T) * 8, "v"), + /*sew*/ sizeof(T) * 8, tester->instruction(), + [](T vs2) -> T { + T result = 0; + for (int i = 0; i < sizeof(T) * 8; ++i) { + result += (vs2 & 1) ? 1 : 0; + vs2 >>= 1; + } + return result; + }); +} + +// Helper function for testing the vwsll_vv instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVVHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vv"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T vs1) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = (vs1 & shift_mask); + return static_cast<WT>(vs2) << shift_amount; + }); +} + +// Helper function for testing the vwsll_vx instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVXHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vx"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T rs1) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = (rs1 & shift_mask); + return static_cast<WT>(vs2) << shift_amount; + }); +} + +// Helper function for testing the vwsll_vi instruction. Generate the expected +// result by shifting the widened input left. +template <typename T> +inline void VwsllVIHelper(RiscVVectorBasicBitManipulationTest *tester) { + using WT = typename WideType<T>::type; + tester->SetSemanticFunction(&Vwsll); + tester->BinaryOpTestHelperVV<WT, T, T>( + absl::StrCat("Vwsll", sizeof(T) * 8, "vi"), /*sew*/ sizeof(T) * 8, + tester->instruction(), [](T vs2, T imm) -> WT { + T shift_mask = 2 * 8 * sizeof(T) - 1; + T shift_amount = (imm & shift_mask); + return static_cast<WT>(vs2) << shift_amount; + }); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vandn) { + VandnVVHelper<uint8_t>(this); + ResetInstruction(); + VandnVVHelper<uint16_t>(this); + ResetInstruction(); + VandnVVHelper<uint32_t>(this); + ResetInstruction(); + VandnVVHelper<uint64_t>(this); + ResetInstruction(); + + VandnVXHelper<uint8_t>(this); + ResetInstruction(); + VandnVXHelper<uint16_t>(this); + ResetInstruction(); + VandnVXHelper<uint32_t>(this); + ResetInstruction(); + VandnVXHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vbrev8) { + Vbrev8VHelper<uint8_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint16_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint32_t>(this); + ResetInstruction(); + Vbrev8VHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vrev8) { + Vrev8VHelper<uint8_t>(this); + ResetInstruction(); + Vrev8VHelper<uint16_t>(this); + ResetInstruction(); + Vrev8VHelper<uint32_t>(this); + ResetInstruction(); + Vrev8VHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vrol) { + VrolVVHelper<uint8_t>(this); + ResetInstruction(); + VrolVVHelper<uint16_t>(this); + ResetInstruction(); + VrolVVHelper<uint32_t>(this); + ResetInstruction(); + VrolVVHelper<uint64_t>(this); + ResetInstruction(); + + VrolVXHelper<uint8_t>(this); + ResetInstruction(); + VrolVXHelper<uint16_t>(this); + ResetInstruction(); + VrolVXHelper<uint32_t>(this); + ResetInstruction(); + VrolVXHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vror) { + VrorVVHelper<uint8_t>(this); + ResetInstruction(); + VrorVVHelper<uint16_t>(this); + ResetInstruction(); + VrorVVHelper<uint32_t>(this); + ResetInstruction(); + VrorVVHelper<uint64_t>(this); + ResetInstruction(); + + VrorVXHelper<uint8_t>(this); + ResetInstruction(); + VrorVXHelper<uint16_t>(this); + ResetInstruction(); + VrorVXHelper<uint32_t>(this); + ResetInstruction(); + VrorVXHelper<uint64_t>(this); + ResetInstruction(); + + VrorVIHelper<uint8_t>(this); + ResetInstruction(); + VrorVIHelper<uint16_t>(this); + ResetInstruction(); + VrorVIHelper<uint32_t>(this); + ResetInstruction(); + VrorVIHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vbrev) { + VbrevVHelper<uint8_t>(this); + ResetInstruction(); + VbrevVHelper<uint16_t>(this); + ResetInstruction(); + VbrevVHelper<uint32_t>(this); + ResetInstruction(); + VbrevVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vclzv) { + VclzVHelper<uint8_t>(this); + ResetInstruction(); + VclzVHelper<uint16_t>(this); + ResetInstruction(); + VclzVHelper<uint32_t>(this); + ResetInstruction(); + VclzVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vctz) { + VctzVHelper<uint8_t>(this); + ResetInstruction(); + VctzVHelper<uint16_t>(this); + ResetInstruction(); + VctzVHelper<uint32_t>(this); + ResetInstruction(); + VctzVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vcpop) { + VcpopVHelper<uint8_t>(this); + ResetInstruction(); + VcpopVHelper<uint16_t>(this); + ResetInstruction(); + VcpopVHelper<uint32_t>(this); + ResetInstruction(); + VcpopVHelper<uint64_t>(this); + ResetInstruction(); +} + +TEST_F(RiscVVectorBasicBitManipulationTest, vwsll) { + VwsllVVHelper<uint8_t>(this); + ResetInstruction(); + VwsllVVHelper<uint16_t>(this); + ResetInstruction(); + VwsllVVHelper<uint32_t>(this); + ResetInstruction(); + + VwsllVXHelper<uint8_t>(this); + ResetInstruction(); + VwsllVXHelper<uint16_t>(this); + ResetInstruction(); + VwsllVXHelper<uint32_t>(this); + ResetInstruction(); + + VwsllVIHelper<uint8_t>(this); + ResetInstruction(); + VwsllVIHelper<uint16_t>(this); + ResetInstruction(); + VwsllVIHelper<uint32_t>(this); + ResetInstruction(); +} + +} // namespace
diff --git a/riscv/test/zvbb_encoding_test.cc b/riscv/test/zvbb_encoding_test.cc new file mode 100644 index 0000000..64f7be6 --- /dev/null +++ b/riscv/test/zvbb_encoding_test.cc
@@ -0,0 +1,203 @@ +// Copyright 2025 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 "riscv/zvbb_encoding.h" + +#include <cstdint> + +#include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/flat_demand_memory.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_enums.h" + +// This file contains tests for the RiscV32GZBEncoding class to ensure that +// the instruction decoding is correct. + +namespace { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +using mpact::sim::riscv::RiscVState; +using mpact::sim::riscv::RiscVXlen; +using mpact::sim::util::FlatDemandMemory; + +using mpact::sim::riscv::zvbb::kComplexResourceNames; +using mpact::sim::riscv::zvbb::kDestOpNames; +using mpact::sim::riscv::zvbb::kSimpleResourceNames; +using mpact::sim::riscv::zvbb::kSourceOpNames; + +using SlotEnum = mpact::sim::riscv::zvbb::SlotEnum; +using OpcodeEnum = mpact::sim::riscv::zvbb::OpcodeEnum; +using SourceOpEnum = mpact::sim::riscv::zvbb::SourceOpEnum; +using DestOpEnum = mpact::sim::riscv::zvbb::DestOpEnum; +using SimpleResourceEnum = mpact::sim::riscv::zvbb::SimpleResourceEnum; +using ComplexResourceEnum = mpact::sim::riscv::zvbb::ComplexResourceEnum; + +using mpact::sim::riscv::zvbb::ZVBBEncoding; + +// Constexpr for opcodes for vector basic bit manipulation instructions. +constexpr uint32_t kVandnVv = 0b000001'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVandnVx = 0b000001'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVbrev8V = 0b010010'0'00000'01000'010'00000'1010111; +constexpr uint32_t kVrev8V = 0b010010'0'00000'01001'010'00000'1010111; +constexpr uint32_t kVrolVv = 0b010101'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVrolVx = 0b010101'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVrorVv = 0b010100'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVrorVx = 0b010100'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVrorVi_i5_0 = 0b01010'0'0'00000'00000'011'00000'1010111; +constexpr uint32_t kVrorVi_i5_1 = 0b01010'1'0'00000'00000'011'00000'1010111; + +constexpr uint32_t kVbrevV = 0b010010'0'00000'01010'010'00000'1010111; +constexpr uint32_t kVclzV = 0b010010'0'00000'01100'010'00000'1010111; +constexpr uint32_t kVctzV = 0b010010'0'00000'01101'010'00000'1010111; +constexpr uint32_t kVcpopV = 0b010010'0'00000'01110'010'00000'1010111; +constexpr uint32_t kVwsllVv = 0b110101'0'00000'00000'000'00000'1010111; +constexpr uint32_t kVwsllVx = 0b110101'0'00000'00000'100'00000'1010111; +constexpr uint32_t kVwsllVi = 0b110101'0'00000'00000'011'00000'1010111; + +class ZVBBEncodingTest : public testing::Test { + protected: + ZVBBEncodingTest() { + state_ = new RiscVState("test", RiscVXlen::RV32, &memory_); + enc_ = new ZVBBEncoding(state_); + } + + ~ZVBBEncodingTest() override { + delete enc_; + delete state_; + } + + FlatDemandMemory memory_; + RiscVState *state_; + ZVBBEncoding *enc_; +}; + +TEST_F(ZVBBEncodingTest, SourceOperands) { + auto &getters = enc_->source_op_getters(); + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kSourceOpNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, DestOperands) { + auto &getters = enc_->dest_op_getters(); + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No dest operand for enum value " << i + << " (" << kDestOpNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, SimpleResources) { + auto &getters = enc_->simple_resource_getters(); + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kSimpleResourceNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, ComplexResources) { + auto &getters = enc_->source_op_getters(); + for (int i = *ComplexResourceEnum::kNone; + i < *ComplexResourceEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kComplexResourceNames[i] << ")"; + } +} + +TEST_F(ZVBBEncodingTest, VandnVv) { + enc_->ParseInstruction(kVandnVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVandnVv); +} + +TEST_F(ZVBBEncodingTest, VandnVx) { + enc_->ParseInstruction(kVandnVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVandnVx); +} + +TEST_F(ZVBBEncodingTest, Vbrev8V) { + enc_->ParseInstruction(kVbrev8V); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVbrev8V); +} + +TEST_F(ZVBBEncodingTest, Vrev8V) { + enc_->ParseInstruction(kVrev8V); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrev8V); +} + +TEST_F(ZVBBEncodingTest, VrolVv) { + enc_->ParseInstruction(kVrolVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrolVv); +} + +TEST_F(ZVBBEncodingTest, VrolVx) { + enc_->ParseInstruction(kVrolVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrolVx); +} + +TEST_F(ZVBBEncodingTest, VrorVv) { + enc_->ParseInstruction(kVrorVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVv); +} + +TEST_F(ZVBBEncodingTest, VrorVx) { + enc_->ParseInstruction(kVrorVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVx); +} + +TEST_F(ZVBBEncodingTest, VrorVi) { + enc_->ParseInstruction(kVrorVi_i5_0); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVi); + enc_->ParseInstruction(kVrorVi_i5_1); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVrorVi); +} + +TEST_F(ZVBBEncodingTest, VbrevV) { + enc_->ParseInstruction(kVbrevV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVbrevV); +} + +TEST_F(ZVBBEncodingTest, VclzV) { + enc_->ParseInstruction(kVclzV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVclzV); +} + +TEST_F(ZVBBEncodingTest, VctzV) { + enc_->ParseInstruction(kVctzV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVctzV); +} + +TEST_F(ZVBBEncodingTest, VcpopV) { + enc_->ParseInstruction(kVcpopV); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVcpopV); +} + +TEST_F(ZVBBEncodingTest, VwsllVv) { + enc_->ParseInstruction(kVwsllVv); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVv); +} + +TEST_F(ZVBBEncodingTest, VwsllVx) { + enc_->ParseInstruction(kVwsllVx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVx); +} + +TEST_F(ZVBBEncodingTest, VwsllVi) { + enc_->ParseInstruction(kVwsllVi); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscvZvbb, 0), OpcodeEnum::kVwsllVi); +} + +} // namespace
diff --git a/riscv/zvbb_encoding.cc b/riscv/zvbb_encoding.cc new file mode 100644 index 0000000..3ac6038 --- /dev/null +++ b/riscv/zvbb_encoding.cc
@@ -0,0 +1,130 @@ +// Copyright 2025 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 +// +// http://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 "riscv/zvbb_encoding.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_getters_zvbb.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_bin_decoder.h" +#include "riscv/zvbb_decoder.h" +#include "riscv/zvbb_enums.h" + +namespace mpact::sim::riscv::zvbb { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +ZVBBEncoding::ZVBBEncoding(RiscVState *state) + : state_(state), + inst_word_(0), + opcode_(OpcodeEnum::kNone), + format_(FormatEnum::kNone) { + resource_delay_line_ = + state_->CreateAndAddDelayLine<generic::SimpleResourceDelayLine>(8); + // Initialize getters. + source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; }); + dest_op_getters_.emplace(*DestOpEnum::kNone, + [](int latency) { return nullptr; }); + simple_resource_getters_.emplace(*SimpleResourceEnum::kNone, + []() { return nullptr; }); + complex_resource_getters_.emplace( + *ComplexResourceEnum::kNone, + [](int latency, int end) { return nullptr; }); + + AddRiscVZvbbSourceVectorGetters<SourceOpEnum, Extractors, RVVectorRegister>( + source_op_getters_, this); + AddRiscvZvbbSourceScalarGetters<SourceOpEnum, Extractors, RV32Register>( + source_op_getters_, this); + + AddRiscVZvbbDestGetters<DestOpEnum, Extractors, RVVectorRegister>( + dest_op_getters_, this); + + // Verify that there are getters for each enum value. + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + if (source_op_getters_.find(i) == source_op_getters_.end()) { + LOG(ERROR) << "No getter for source op enum value " << i; + } + } + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + if (dest_op_getters_.find(i) == dest_op_getters_.end()) { + LOG(ERROR) << "No getter for destination op enum value " << i; + } + } + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + if (simple_resource_getters_.find(i) == simple_resource_getters_.end()) { + LOG(ERROR) << "No getter for simple resource enum value " << i; + } + } +} + +ZVBBEncoding::~ZVBBEncoding() { delete resource_pool_; } + +void ZVBBEncoding::ParseInstruction(uint32_t inst_word) { + inst_word_ = inst_word; + auto [opcode, format] = DecodeRiscVZvbbInst32WithFormat(inst_word_); + opcode_ = opcode; + format_ = format; +} + +ResourceOperandInterface *ZVBBEncoding::GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) { + return nullptr; +} + +ResourceOperandInterface *ZVBBEncoding::GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, int end) { + return nullptr; +} + +DestinationOperandInterface *ZVBBEncoding::GetDestination(SlotEnum, int, + OpcodeEnum opcode, + DestOpEnum dest_op, + int dest_no, + int latency) { + int index = static_cast<int>(dest_op); + auto iter = dest_op_getters_.find(index); + if (iter == dest_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for destination op enum value ", + index, "for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(latency); +} + +SourceOperandInterface *ZVBBEncoding::GetSource(SlotEnum, int, + OpcodeEnum opcode, + SourceOpEnum source_op, + int source_no) { + int index = static_cast<int>(source_op); + auto iter = source_op_getters_.find(index); + if (iter == source_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index, + " for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(); +} + +} // namespace mpact::sim::riscv::zvbb
diff --git a/riscv/zvbb_encoding.h b/riscv/zvbb_encoding.h new file mode 100644 index 0000000..bc84053 --- /dev/null +++ b/riscv/zvbb_encoding.h
@@ -0,0 +1,103 @@ +// Copyright 2025 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 +// +// http://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. + +#ifndef THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_ +#define THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_ + +#include <cstdint> +#include <string> + +#include "mpact/sim/generic/simple_resource.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_state.h" +#include "riscv/zvbb_bin_decoder.h" +#include "riscv/zvbb_decoder.h" +#include "riscv/zvbb_enums.h" + +namespace mpact::sim::riscv::zvbb { + +// This class provides the interface between the generated instruction decoder +// framework (which is agnostic of the actual bit representation of +// instructions) and the instruction representation. This class provides methods +// to return the opcode, source operands, and destination operands for +// instructions according to the operand fields in the encoding. +class ZVBBEncoding : public ZVBBEncodingBase, public RiscVEncodingCommon { + public: + explicit ZVBBEncoding(RiscVState *state); + ~ZVBBEncoding() override; + + void ParseInstruction(uint32_t inst_word); + OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; } + FormatEnum GetFormat(SlotEnum, int) { return format_; } + + PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum, + PredOpEnum) override { + return nullptr; + } + + ResourceOperandInterface *GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, + int end) override; + + ResourceOperandInterface *GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) override; + + SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op, + int source_no) override; + + DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum, + DestOpEnum op, int dest_no, + int latency) override; + + int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override { + return 0; + } + + // Methods inherited from RiscVEncodingCommon. + RiscVState *state() const override { return state_; } + generic::SimpleResourcePool *resource_pool() override { + return resource_pool_; + } + uint32_t inst_word() const override { return inst_word_; } + + const SourceOpGetterMap &source_op_getters() { return source_op_getters_; } + const DestOpGetterMap &dest_op_getters() { return dest_op_getters_; } + const SimpleResourceGetterMap &simple_resource_getters() { + return simple_resource_getters_; + } + const ComplexResourceGetterMap &complex_resource_getters() { + return complex_resource_getters_; + } + + private: + std::string GetSimpleResourceName(SimpleResourceEnum resource_enum); + + RiscVState *state_; + uint32_t inst_word_; + OpcodeEnum opcode_; + FormatEnum format_; + SourceOpGetterMap source_op_getters_; + DestOpGetterMap dest_op_getters_; + SimpleResourceGetterMap simple_resource_getters_; + ComplexResourceGetterMap complex_resource_getters_; + generic::SimpleResourceDelayLine *resource_delay_line_ = nullptr; + generic::SimpleResourcePool *resource_pool_ = nullptr; +}; + +} // namespace mpact::sim::riscv::zvbb + +#endif // THIRD_PARTY_MPACT_RISCV_ZVBB_ENCODING_H_