| // 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. |
| |
| #ifndef THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_VECTOR_H_ |
| #define THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_VECTOR_H_ |
| |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/strings/str_cat.h" |
| #include "absl/types/span.h" |
| #include "mpact/sim/generic/immediate_operand.h" |
| #include "mpact/sim/generic/literal_operand.h" |
| #include "mpact/sim/generic/operand_interface.h" |
| #include "mpact/sim/generic/register.h" |
| #include "mpact/sim/generic/resource_operand_interface.h" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "riscv/riscv_encoding_common.h" |
| #include "riscv/riscv_getter_helpers.h" |
| #include "riscv/riscv_register.h" |
| #include "riscv/riscv_state.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace riscv { |
| |
| using ::mpact::sim::generic::DestinationOperandInterface; |
| using ::mpact::sim::generic::ImmediateOperand; |
| using ::mpact::sim::generic::IntLiteralOperand; |
| using ::mpact::sim::generic::ResourceOperandInterface; |
| using ::mpact::sim::generic::SourceOperandInterface; |
| using ::mpact::sim::generic::operator*; // NOLINT: is used below. |
| |
| constexpr int kNumRegTable[8] = {8, 1, 2, 1, 4, 1, 2, 1}; |
| |
| template <typename RegType> |
| inline void GetVRegGroup(RiscVState* state, int reg_num, |
| std::vector<generic::RegisterBase*>* vreg_group) { |
| // The number of registers in a vector register group depends on the register |
| // index: 0, 8, 16, 24 each have 8 registers, 4, 12, 20, 28 each have 4, |
| // 2, 6, 10, 14, 18, 22, 26, 30 each have two, and all odd numbered register |
| // groups have only 1. |
| int num_regs = kNumRegTable[reg_num % 8]; |
| for (int i = 0; i < num_regs; i++) { |
| auto vreg_name = absl::StrCat(RiscVState::kVregPrefix, reg_num + i); |
| vreg_group->push_back(state->GetRegister<RegType>(vreg_name).first); |
| } |
| } |
| template <typename RegType> |
| inline SourceOperandInterface* GetVectorRegisterSourceOp(RiscVState* state, |
| int reg_num) { |
| std::vector<generic::RegisterBase*> vreg_group; |
| GetVRegGroup<RegType>(state, reg_num, &vreg_group); |
| auto* v_src_op = new RV32VectorSourceOperand( |
| absl::Span<generic::RegisterBase*>(vreg_group), |
| absl::StrCat(RiscVState::kVregPrefix, reg_num)); |
| return v_src_op; |
| } |
| |
| template <typename RegType> |
| inline DestinationOperandInterface* GetVectorRegisterDestinationOp( |
| RiscVState* state, int latency, int reg_num) { |
| std::vector<generic::RegisterBase*> vreg_group; |
| GetVRegGroup<RegType>(state, reg_num, &vreg_group); |
| auto* v_dst_op = new RV32VectorDestinationOperand( |
| absl::Span<generic::RegisterBase*>(vreg_group), latency, |
| absl::StrCat(RiscVState::kVregPrefix, reg_num)); |
| return v_dst_op; |
| } |
| |
| template <typename RegType> |
| inline SourceOperandInterface* GetVectorMaskRegisterSourceOp(RiscVState* state, |
| int reg_num) { |
| // Mask register groups only have a single register. |
| std::vector<generic::RegisterBase*> vreg_group; |
| vreg_group.push_back( |
| state |
| ->GetRegister<RegType>(absl::StrCat(RiscVState::kVregPrefix, reg_num)) |
| .first); |
| auto* v_src_op = new RV32VectorSourceOperand( |
| absl::Span<generic::RegisterBase*>(vreg_group), |
| absl::StrCat(RiscVState::kVregPrefix, reg_num)); |
| return v_src_op; |
| } |
| |
| template <typename RegType> |
| inline DestinationOperandInterface* GetVectorMaskRegisterDestinationOp( |
| RiscVState* state, int latency, int reg_num) { |
| // Mask register groups only have a single register. |
| std::vector<generic::RegisterBase*> vreg_group; |
| vreg_group.push_back( |
| state |
| ->GetRegister<RegType>(absl::StrCat(RiscVState::kVregPrefix, reg_num)) |
| .first); |
| auto* v_dst_op = new RV32VectorDestinationOperand( |
| absl::Span<generic::RegisterBase*>(vreg_group), latency, |
| absl::StrCat(RiscVState::kVregPrefix, reg_num)); |
| return v_dst_op; |
| } |
| |
| // 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 AddRiscVVectorSourceGetters(SourceOpGetterMap& getter_map, |
| RiscVEncodingCommon* common) { |
| // Source operand getters. |
| Insert(getter_map, *Enum::kVd, [common]() -> SourceOperandInterface* { |
| auto num = Extractors::VArith::ExtractVd(common->inst_word()); |
| return GetVectorRegisterSourceOp<VectorRegister>(common->state(), num); |
| }); |
| 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 GetVectorMaskRegisterSourceOp<VectorRegister>(common->state(), 0); |
| }); |
| Insert(getter_map, *Enum::kVmaskTrue, [common]() -> SourceOperandInterface* { |
| return new RV32VectorTrueOperand(common->state()); |
| }); |
| Insert(getter_map, *Enum::kVm, [common]() -> SourceOperandInterface* { |
| auto vm = Extractors::VArith::ExtractVm(common->inst_word()); |
| return new generic::ImmediateOperand<bool>( |
| vm, absl::StrCat("vm.", vm ? "t" : "f")); |
| }); |
| Insert(getter_map, *Enum::kVs1, [common]() -> SourceOperandInterface* { |
| auto num = Extractors::VArith::ExtractVs1(common->inst_word()); |
| return GetVectorRegisterSourceOp<VectorRegister>(common->state(), num); |
| }); |
| Insert(getter_map, *Enum::kVs2, [common]() -> SourceOperandInterface* { |
| auto num = Extractors::VArith::ExtractVs2(common->inst_word()); |
| return GetVectorRegisterSourceOp<VectorRegister>(common->state(), num); |
| }); |
| Insert(getter_map, *Enum::kVs3, [common]() -> SourceOperandInterface* { |
| auto num = Extractors::VMem::ExtractVs3(common->inst_word()); |
| return GetVectorRegisterSourceOp<VectorRegister>(common->state(), num); |
| }); |
| |
| Insert(getter_map, *Enum::kSimm5, [common]() -> SourceOperandInterface* { |
| const auto num = |
| Extractors::Inst32Format::ExtractSimm5(common->inst_word()); |
| return new generic::ImmediateOperand<int32_t>(num); |
| }); |
| |
| Insert(getter_map, *Enum::kUimm5, [common]() -> SourceOperandInterface* { |
| const auto num = |
| Extractors::Inst32Format::ExtractUimm5(common->inst_word()); |
| return new generic::ImmediateOperand<int32_t>(num); |
| }); |
| |
| Insert(getter_map, *Enum::kZimm10, [common]() -> SourceOperandInterface* { |
| const auto num = |
| Extractors::Inst32Format::ExtractZimm10(common->inst_word()); |
| return new generic::ImmediateOperand<int32_t>(num); |
| }); |
| |
| Insert(getter_map, *Enum::kZimm11, [common]() -> SourceOperandInterface* { |
| const auto num = |
| Extractors::Inst32Format::ExtractZimm11(common->inst_word()); |
| return new generic::ImmediateOperand<int32_t>(num); |
| }); |
| |
| Insert(getter_map, *Enum::kConst1, []() -> SourceOperandInterface* { |
| return new generic::ImmediateOperand<int32_t>(1); |
| }); |
| |
| Insert(getter_map, *Enum::kConst2, []() -> SourceOperandInterface* { |
| return new generic::ImmediateOperand<int32_t>(2); |
| }); |
| |
| Insert(getter_map, *Enum::kConst4, []() -> SourceOperandInterface* { |
| return new generic::ImmediateOperand<int32_t>(4); |
| }); |
| |
| Insert(getter_map, *Enum::kConst8, []() -> SourceOperandInterface* { |
| return new generic::ImmediateOperand<int32_t>(8); |
| }); |
| |
| Insert(getter_map, *Enum::kNf, [common]() -> SourceOperandInterface* { |
| auto num_fields = Extractors::VMem::ExtractNf(common->inst_word()); |
| return new generic::ImmediateOperand<uint8_t>(num_fields, |
| absl::StrCat(num_fields + 1)); |
| }); |
| Insert(getter_map, *Enum::kFs1, [common]() -> SourceOperandInterface* { |
| const int num = Extractors::VArith::ExtractRs1(common->inst_word()); |
| return GetRegisterSourceOp<RV64Register>(common->state(), |
| std::string(kFregNames[num]), |
| std::string(kFregAbiNames[num])); |
| }); |
| } |
| |
| // This function is used to add the destination operand getters to the given |
| // "getter map". The function is templated on the enum type that defines the |
| // destination operand types, the Extractors type that defines the bit |
| // extraction functions, and the IntRegister and FpRegister types that are used |
| // to construct the register operand. |
| template <typename Enum, typename Extractors, typename VectorRegister> |
| void AddRiscVVectorDestGetters(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 GetVectorRegisterDestinationOp<VectorRegister>( |
| common->state(), latency, num); |
| }); |
| Insert(getter_map, *Enum::kFd, |
| [common](int latency) -> DestinationOperandInterface* { |
| const int num = Extractors::VArith::ExtractRd(common->inst_word()); |
| return GetRegisterDestinationOp<RV64Register>( |
| common->state(), std::string(kFregNames[num]), latency, |
| std::string(kFregAbiNames[num])); |
| }); |
| } |
| |
| // This function is used to add the simple resource getters to the given |
| // "getter map". The function is templated on the enum type that defines the |
| // simple resource types, the Extractors type that defines the bit |
| // extraction functions, and the IntRegister and FpRegister types that are used |
| // to construct the register operand. |
| template <typename Enum, typename Extractors> |
| void AddRiscVVectorSimpleResourceGetters(SimpleResourceGetterMap& getter_map, |
| RiscVEncodingCommon* common) { |
| // TODO(torerik): Add resource getters when appropriate. |
| } |
| |
| } // namespace riscv |
| } // namespace sim |
| } // namespace mpact |
| |
| #endif // THIRD_PARTY_MPACT_RISCV_RISCV_GETTERS_VECTOR_H_ |