Adds exceptions on instruction decoding when the C bit is not set in MISA. Also refactors the instruction decoders to reduce code duplication. PiperOrigin-RevId: 827555953 Change-Id: I4f5da71e4d84c4c5866291f43d6fb6406f10a84d
diff --git a/riscv/BUILD b/riscv/BUILD index 8d7eab3..8429369 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -761,6 +761,23 @@ ) cc_library( + name = "riscv_generic_decoder", + hdrs = [ + "riscv_generic_decoder.h", + ], + deps = [ + ":riscv_state", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@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 = "riscv32g_decoder", srcs = [ "riscv32_decoder.cc", @@ -774,6 +791,7 @@ deps = [ ":riscv32g_bin_fmt", ":riscv32g_isa", + ":riscv_generic_decoder", ":riscv_state", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", @@ -835,6 +853,7 @@ ":riscv32gzb_bin_fmt", ":riscv32gzb_isa", ":riscv_encoding_common", + ":riscv_generic_decoder", ":riscv_getters", ":riscv_state", "@com_google_absl//absl/log", @@ -861,6 +880,7 @@ ":riscv64gzb_bin_fmt", ":riscv64gzb_isa", ":riscv_encoding_common", + ":riscv_generic_decoder", ":riscv_getters", ":riscv_state", "@com_google_absl//absl/log", @@ -886,6 +906,7 @@ deps = [ ":riscv32gv_bin_fmt", ":riscv32gv_isa", + ":riscv_generic_decoder", ":riscv_state", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", @@ -916,6 +937,7 @@ ":riscv32gvzb_bin_fmt", ":riscv32gvzb_isa", ":riscv_encoding_common", + ":riscv_generic_decoder", ":riscv_getters", ":riscv_state", "@com_google_absl//absl/log", @@ -942,6 +964,7 @@ deps = [ ":riscv64g_bin_fmt", ":riscv64g_isa", + ":riscv_generic_decoder", ":riscv_state", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", @@ -1015,6 +1038,7 @@ deps = [ ":riscv64gv_bin_fmt", ":riscv64gv_isa", + ":riscv_generic_decoder", ":riscv_state", "@com_google_absl//absl/base", "@com_google_absl//absl/container:flat_hash_map", @@ -1046,6 +1070,7 @@ ":riscv64gvzb_bin_fmt", ":riscv64gvzb_isa", ":riscv_encoding_common", + ":riscv_generic_decoder", ":riscv_getters", ":riscv_state", "@com_google_absl//absl/log",
diff --git a/riscv/riscv32_decoder.cc b/riscv/riscv32_decoder.cc index c63a7c3..847c03e 100644 --- a/riscv/riscv32_decoder.cc +++ b/riscv/riscv32_decoder.cc
@@ -15,90 +15,36 @@ #include "riscv/riscv32_decoder.h" #include <cstdint> -#include <string> +#include <memory> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32g_decoder.h" #include "riscv/riscv32g_encoding.h" #include "riscv/riscv32g_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV32Decoder::RiscV32Decoder(RiscVState* state, util::MemoryInterface* memory) : state_(state), memory_(memory) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - - // Need a data buffer to load instructions from memory. Allocate a single - // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. - riscv_isa_factory_ = new RV32IsaFactory(); - riscv_isa_ = new isa32::RiscV32GInstructionSet(state, riscv_isa_factory_); - riscv_encoding_ = new isa32::RiscV32GEncoding(state); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); -} - -RiscV32Decoder::~RiscV32Decoder() { - delete riscv_isa_; - delete riscv_isa_factory_; - delete riscv_encoding_; - inst_db_->DecRef(); + riscv_isa_factory_ = std::make_unique<RV32IsaFactory>(); + riscv_isa_ = std::make_unique<isa32::RiscV32GInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = std::make_unique<isa32::RiscV32GEncoding>(state); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa32::OpcodeEnum, isa32::RiscV32GEncoding, + isa32::RiscV32GInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } generic::Instruction* RiscV32Decoder::DecodeInstruction(uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa32::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAddressMisaligned, - inst->address() ^ 0x1, inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa32::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAccessFault, inst->address(), - nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv32_decoder.h b/riscv/riscv32_decoder.h index e557ef8..4926a79 100644 --- a/riscv/riscv32_decoder.h +++ b/riscv/riscv32_decoder.h
@@ -18,23 +18,19 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32g_decoder.h" #include "riscv/riscv32g_encoding.h" #include "riscv/riscv32g_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. - // This is the factory class needed by the generated decoder. It is responsible // for creating the decoder for each slot instance. Since the riscv architecture // only has a single slot, it's a pretty simple class. @@ -55,7 +51,6 @@ RiscV32Decoder(RiscVState* state, util::MemoryInterface* memory); RiscV32Decoder() = delete; - ~RiscV32Decoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -69,16 +64,20 @@ } // Getter. - isa32::RiscV32GEncoding* riscv_encoding() const { return riscv_encoding_; } + isa32::RiscV32GEncoding* riscv_encoding() const { + return riscv_encoding_.get(); + } private: RiscVState* state_; util::MemoryInterface* memory_; - std::unique_ptr<generic::ProgramError> decode_error_; - generic::DataBuffer* inst_db_; - isa32::RiscV32GEncoding* riscv_encoding_; - RV32IsaFactory* riscv_isa_factory_; - isa32::RiscV32GInstructionSet* riscv_isa_; + std::unique_ptr< + RiscVGenericDecoder<isa32::OpcodeEnum, isa32::RiscV32GEncoding, + isa32::RiscV32GInstructionSet>> + decoder_; + std::unique_ptr<isa32::RiscV32GEncoding> riscv_encoding_; + std::unique_ptr<RV32IsaFactory> riscv_isa_factory_; + std::unique_ptr<isa32::RiscV32GInstructionSet> riscv_isa_; }; } // namespace riscv
diff --git a/riscv/riscv32g_bitmanip_decoder.cc b/riscv/riscv32g_bitmanip_decoder.cc index 3c2de3f..220a466 100644 --- a/riscv/riscv32g_bitmanip_decoder.cc +++ b/riscv/riscv32g_bitmanip_decoder.cc
@@ -15,87 +15,38 @@ #include "riscv/riscv32g_bitmanip_decoder.h" #include <cstdint> -#include <new> -#include <string> +#include <memory> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32gzb_decoder.h" #include "riscv/riscv32gzb_encoding.h" #include "riscv/riscv32gzb_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV32GBitmanipDecoder::RiscV32GBitmanipDecoder(RiscVState* state, util::MemoryInterface* memory) : state_(state), memory_(memory) { - // Need a data buffer to load instructions from memory. Allocate a single - // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. - riscv_isa_factory_ = new RV32GZBIsaFactory(); - riscv_isa_ = - new isa32gzb::RiscV32GZBInstructionSet(state, riscv_isa_factory_); - riscv_encoding_ = new isa32gzb::RiscV32GZBEncoding(state); -} - -RiscV32GBitmanipDecoder::~RiscV32GBitmanipDecoder() { - delete riscv_isa_; - delete riscv_isa_factory_; - delete riscv_encoding_; - inst_db_->DecRef(); + riscv_isa_factory_ = std::make_unique<RV32GZBIsaFactory>(); + riscv_isa_ = std::make_unique<isa32gzb::RiscV32GZBInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = std::make_unique<isa32gzb::RiscV32GZBEncoding>(state); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa32gzb::OpcodeEnum, isa32gzb::RiscV32GZBEncoding, + isa32gzb::RiscV32GZBInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } generic::Instruction* RiscV32GBitmanipDecoder::DecodeInstruction( uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa32gzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAddressMisaligned, - inst->address() ^ 0x1, inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa32gzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAccessFault, inst->address(), - nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv32g_bitmanip_decoder.h b/riscv/riscv32g_bitmanip_decoder.h index 7c66ab1..8a54958 100644 --- a/riscv/riscv32g_bitmanip_decoder.h +++ b/riscv/riscv32g_bitmanip_decoder.h
@@ -26,6 +26,7 @@ #include "riscv/riscv32gzb_decoder.h" #include "riscv/riscv32gzb_encoding.h" #include "riscv/riscv32gzb_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -55,7 +56,6 @@ RiscV32GBitmanipDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV32GBitmanipDecoder() = delete; - ~RiscV32GBitmanipDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -70,16 +70,20 @@ // Getter. isa32gzb::RiscV32GZBEncoding* riscv_encoding() const { - return riscv_encoding_; + return riscv_encoding_.get(); } private: RiscVState* state_; + std::unique_ptr< + RiscVGenericDecoder<isa32gzb::OpcodeEnum, isa32gzb::RiscV32GZBEncoding, + isa32gzb::RiscV32GZBInstructionSet>> + decoder_; util::MemoryInterface* memory_; generic::DataBuffer* inst_db_; - isa32gzb::RiscV32GZBEncoding* riscv_encoding_; - RV32GZBIsaFactory* riscv_isa_factory_; - isa32gzb::RiscV32GZBInstructionSet* riscv_isa_; + std::unique_ptr<isa32gzb::RiscV32GZBEncoding> riscv_encoding_; + std::unique_ptr<RV32GZBIsaFactory> riscv_isa_factory_; + std::unique_ptr<isa32gzb::RiscV32GZBInstructionSet> riscv_isa_; }; } // namespace riscv
diff --git a/riscv/riscv32g_vec_decoder.cc b/riscv/riscv32g_vec_decoder.cc index 49b0da8..f9f2939 100644 --- a/riscv/riscv32g_vec_decoder.cc +++ b/riscv/riscv32g_vec_decoder.cc
@@ -14,10 +14,16 @@ #include "riscv/riscv32g_vec_decoder.h" -#include <string> +#include <cstdint> +#include <memory> -#include "absl/strings/str_cat.h" -#include "mpact/sim/generic/program_error.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/util/memory/memory_interface.h" +#include "riscv/riscv32g_vec_encoding.h" +#include "riscv/riscv32gv_decoder.h" +#include "riscv/riscv32gv_enums.h" +#include "riscv/riscv_generic_decoder.h" +#include "riscv/riscv_state.h" namespace mpact { namespace sim { @@ -26,53 +32,20 @@ RiscV32GVecDecoder::RiscV32GVecDecoder(RiscVState* state, util::MemoryInterface* memory) : state_(state), memory_(memory) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - - // Need a data buffer to load instructions from memory. Allocate a single - // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. - riscv_isa_factory_ = new RV32GVIsaFactory(); - riscv_isa_ = new isa32v::RiscV32GVInstructionSet(state, riscv_isa_factory_); - riscv_encoding_ = new isa32v::RiscV32GVecEncoding(state); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); -} - -RiscV32GVecDecoder::~RiscV32GVecDecoder() { - inst_db_->DecRef(); - delete riscv_isa_; - delete riscv_isa_factory_; - delete riscv_encoding_; + riscv_isa_factory_ = std::make_unique<RV32GVIsaFactory>(); + riscv_isa_ = std::make_unique<isa32v::RiscV32GVInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = std::make_unique<isa32v::RiscV32GVecEncoding>(state); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa32v::OpcodeEnum, isa32v::RiscV32GVecEncoding, + isa32v::RiscV32GVInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } generic::Instruction* RiscV32GVecDecoder::DecodeInstruction(uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that generates an internal simulator error when - // executed. - if (address & 0x1) { - auto* inst = new generic::Instruction(address, state_); - std::string error = - absl::StrCat(absl::Hex(address), ": unaligned instruction address"); - inst->set_semantic_function([error, this](generic::Instruction* inst) { - decode_error_->Raise(error); - }); - inst->set_size(1); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv32g_vec_decoder.h b/riscv/riscv32g_vec_decoder.h index f75ede6..a6c38e9 100644 --- a/riscv/riscv32g_vec_decoder.h +++ b/riscv/riscv32g_vec_decoder.h
@@ -18,15 +18,14 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" #include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32g_vec_encoding.h" #include "riscv/riscv32gv_decoder.h" #include "riscv/riscv32gv_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -55,7 +54,6 @@ RiscV32GVecDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV32GVecDecoder() = delete; - ~RiscV32GVecDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -71,17 +69,19 @@ // Getter. isa32v::RiscV32GVecEncoding* riscv_encoding() const { - return riscv_encoding_; + return riscv_encoding_.get(); } private: RiscVState* state_; util::MemoryInterface* memory_; - std::unique_ptr<generic::ProgramError> decode_error_; - generic::DataBuffer* inst_db_; - isa32v::RiscV32GVecEncoding* riscv_encoding_; - RV32GVIsaFactory* riscv_isa_factory_; - isa32v::RiscV32GVInstructionSet* riscv_isa_; + std::unique_ptr< + RiscVGenericDecoder<isa32v::OpcodeEnum, isa32v::RiscV32GVecEncoding, + isa32v::RiscV32GVInstructionSet>> + decoder_; + std::unique_ptr<isa32v::RiscV32GVecEncoding> riscv_encoding_; + std::unique_ptr<RV32GVIsaFactory> riscv_isa_factory_; + std::unique_ptr<isa32v::RiscV32GVInstructionSet> riscv_isa_; }; } // namespace riscv
diff --git a/riscv/riscv32gzb_vec_decoder.cc b/riscv/riscv32gzb_vec_decoder.cc index 3318637..c4e2498 100644 --- a/riscv/riscv32gzb_vec_decoder.cc +++ b/riscv/riscv32gzb_vec_decoder.cc
@@ -16,16 +16,14 @@ #include <cstdint> #include <memory> -#include <new> -#include <string> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" #include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32gvzb_decoder.h" #include "riscv/riscv32gvzb_enums.h" #include "riscv/riscv32gzb_vec_encoding.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -36,67 +34,22 @@ RiscV32GZBVecDecoder::RiscV32GZBVecDecoder(RiscVState* state, util::MemoryInterface* memory) - : state_(state), - memory_(memory), - inst_db_(state_->db_factory()->Allocate<uint32_t>(1)) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - + : state_(state), memory_(memory) { // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. riscv_isa_factory_ = std::make_unique<RV32GVZBIsaFactory>(); riscv_isa_ = std::make_unique<isa32gvzb::RiscV32GVZBInstructionSet>( state, riscv_isa_factory_.get()); riscv_encoding_ = std::make_unique<isa32gvzb::RiscV32GZBVecEncoding>(state); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); + decoder_ = std::make_unique<RiscVGenericDecoder< + isa32gvzb::OpcodeEnum, isa32gvzb::RiscV32GZBVecEncoding, + isa32gvzb::RiscV32GVZBInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } -RiscV32GZBVecDecoder::~RiscV32GZBVecDecoder() { inst_db_->DecRef(); } - generic::Instruction* RiscV32GZBVecDecoder::DecodeInstruction( uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa32gvzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAddressMisaligned, address ^ 0x1, - inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // object that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa32gvzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAccessFault, address, nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - const uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_.get()); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv32gzb_vec_decoder.h b/riscv/riscv32gzb_vec_decoder.h index b5cc9e9..8039cea 100644 --- a/riscv/riscv32gzb_vec_decoder.h +++ b/riscv/riscv32gzb_vec_decoder.h
@@ -18,15 +18,14 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" #include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv32gvzb_decoder.h" #include "riscv/riscv32gvzb_enums.h" #include "riscv/riscv32gzb_vec_encoding.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -56,7 +55,6 @@ RiscV32GZBVecDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV32GZBVecDecoder() = delete; - ~RiscV32GZBVecDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -72,12 +70,10 @@ private: RiscVState* const state_; util::MemoryInterface* const memory_; - - // Buffer used to load instructions from memory. Re-used for each instruction - // word. - generic::DataBuffer* const inst_db_; - - std::unique_ptr<generic::ProgramError> decode_error_; + std::unique_ptr<RiscVGenericDecoder<isa32gvzb::OpcodeEnum, + isa32gvzb::RiscV32GZBVecEncoding, + isa32gvzb::RiscV32GVZBInstructionSet>> + decoder_; std::unique_ptr<isa32gvzb::RiscV32GZBVecEncoding> riscv_encoding_; std::unique_ptr<RV32GVZBIsaFactory> riscv_isa_factory_; std::unique_ptr<isa32gvzb::RiscV32GVZBInstructionSet> riscv_isa_;
diff --git a/riscv/riscv64_decoder.cc b/riscv/riscv64_decoder.cc index 39da434..3bb579c 100644 --- a/riscv/riscv64_decoder.cc +++ b/riscv/riscv64_decoder.cc
@@ -15,94 +15,41 @@ #include "riscv/riscv64_decoder.h" #include <cstdint> -#include <string> +#include <memory> -#include "absl/strings/str_cat.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64g_decoder.h" #include "riscv/riscv64g_encoding.h" #include "riscv/riscv64g_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV64Decoder::RiscV64Decoder(RiscVState* state, util::MemoryInterface* memory) : RiscV64Decoder(state, memory, /*use_abi_names*/ true) {} RiscV64Decoder::RiscV64Decoder(RiscVState* state, util::MemoryInterface* memory, bool use_abi_names) : state_(state), memory_(memory) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - - // Need a data buffer to load instructions from memory. Allocate a single - // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. - riscv_isa_factory_ = new RV64IsaFactory(); - riscv_isa_ = new isa64::RiscV64GInstructionSet(state, riscv_isa_factory_); - riscv_encoding_ = new isa64::RiscV64GEncoding(state, use_abi_names); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); -} - -RiscV64Decoder::~RiscV64Decoder() { - inst_db_->DecRef(); - delete riscv_isa_; - delete riscv_isa_factory_; - delete riscv_encoding_; + riscv_isa_factory_ = std::make_unique<RV64IsaFactory>(); + riscv_isa_ = std::make_unique<isa64::RiscV64GInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = + std::make_unique<isa64::RiscV64GEncoding>(state, use_abi_names); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa64::OpcodeEnum, isa64::RiscV64GEncoding, + isa64::RiscV64GInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } generic::Instruction* RiscV64Decoder::DecodeInstruction(uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa64::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAddressMisaligned, address ^ 0x1, - inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // object that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa64::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAccessFault, address, nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv64_decoder.h b/riscv/riscv64_decoder.h index 6d5422f..a109899 100644 --- a/riscv/riscv64_decoder.h +++ b/riscv/riscv64_decoder.h
@@ -18,15 +18,14 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" #include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64g_decoder.h" #include "riscv/riscv64g_encoding.h" #include "riscv/riscv64g_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -57,7 +56,6 @@ RiscV64Decoder(RiscVState* state, util::MemoryInterface* memory, bool use_abi_names); RiscV64Decoder() = delete; - ~RiscV64Decoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -71,16 +69,20 @@ } // Getter. - isa64::RiscV64GEncoding* riscv_encoding() const { return riscv_encoding_; } + isa64::RiscV64GEncoding* riscv_encoding() const { + return riscv_encoding_.get(); + } private: RiscVState* state_; util::MemoryInterface* memory_; - std::unique_ptr<generic::ProgramError> decode_error_; - generic::DataBuffer* inst_db_; - isa64::RiscV64GEncoding* riscv_encoding_; - RV64IsaFactory* riscv_isa_factory_; - isa64::RiscV64GInstructionSet* riscv_isa_; + std::unique_ptr< + RiscVGenericDecoder<isa64::OpcodeEnum, isa64::RiscV64GEncoding, + isa64::RiscV64GInstructionSet>> + decoder_; + std::unique_ptr<isa64::RiscV64GEncoding> riscv_encoding_; + std::unique_ptr<RV64IsaFactory> riscv_isa_factory_; + std::unique_ptr<isa64::RiscV64GInstructionSet> riscv_isa_; }; } // namespace riscv
diff --git a/riscv/riscv64g_bitmanip_decoder.cc b/riscv/riscv64g_bitmanip_decoder.cc index a69b537..c6adf3f 100644 --- a/riscv/riscv64g_bitmanip_decoder.cc +++ b/riscv/riscv64g_bitmanip_decoder.cc
@@ -15,87 +15,38 @@ #include "riscv/riscv64g_bitmanip_decoder.h" #include <cstdint> -#include <new> -#include <string> +#include <memory> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64gzb_decoder.h" #include "riscv/riscv64gzb_encoding.h" #include "riscv/riscv64gzb_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV64GBitmanipDecoder::RiscV64GBitmanipDecoder(RiscVState* state, util::MemoryInterface* memory) : state_(state), memory_(memory) { - // Need a data buffer to load instructions from memory. Allocate a single - // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. - riscv_isa_factory_ = new RV64GZBIsaFactory(); - riscv_isa_ = - new isa64gzb::RiscV64GZBInstructionSet(state, riscv_isa_factory_); - riscv_encoding_ = new isa64gzb::RiscV64GZBEncoding(state); -} - -RiscV64GBitmanipDecoder::~RiscV64GBitmanipDecoder() { - delete riscv_isa_; - delete riscv_isa_factory_; - delete riscv_encoding_; - inst_db_->DecRef(); + riscv_isa_factory_ = std::make_unique<RV64GZBIsaFactory>(); + riscv_isa_ = std::make_unique<isa64gzb::RiscV64GZBInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = std::make_unique<isa64gzb::RiscV64GZBEncoding>(state); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa64gzb::OpcodeEnum, isa64gzb::RiscV64GZBEncoding, + isa64gzb::RiscV64GZBInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } generic::Instruction* RiscV64GBitmanipDecoder::DecodeInstruction( uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa64gzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAddressMisaligned, - inst->address() ^ 0x1, inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(0, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa64gzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, inst->address(), - *ExceptionCode::kInstructionAccessFault, inst->address(), - nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv64g_bitmanip_decoder.h b/riscv/riscv64g_bitmanip_decoder.h index 683dbde..847243a 100644 --- a/riscv/riscv64g_bitmanip_decoder.h +++ b/riscv/riscv64g_bitmanip_decoder.h
@@ -18,14 +18,13 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64gzb_decoder.h" #include "riscv/riscv64gzb_encoding.h" #include "riscv/riscv64gzb_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { @@ -33,7 +32,6 @@ namespace riscv { using ::mpact::sim::generic::Instruction; -using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. // This is the factory class needed by the generated decoder. It is responsible // for creating the decoder for each slot instance. Since the riscv architecture @@ -55,7 +53,6 @@ RiscV64GBitmanipDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV64GBitmanipDecoder() = delete; - ~RiscV64GBitmanipDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -70,16 +67,19 @@ // Getter. isa64gzb::RiscV64GZBEncoding* riscv_encoding() const { - return riscv_encoding_; + return riscv_encoding_.get(); } private: RiscVState* state_; util::MemoryInterface* memory_; - generic::DataBuffer* inst_db_; - isa64gzb::RiscV64GZBEncoding* riscv_encoding_; - RV64GZBIsaFactory* riscv_isa_factory_; - isa64gzb::RiscV64GZBInstructionSet* riscv_isa_; + std::unique_ptr< + RiscVGenericDecoder<isa64gzb::OpcodeEnum, isa64gzb::RiscV64GZBEncoding, + isa64gzb::RiscV64GZBInstructionSet>> + decoder_; + std::unique_ptr<isa64gzb::RiscV64GZBEncoding> riscv_encoding_; + std::unique_ptr<RV64GZBIsaFactory> riscv_isa_factory_; + std::unique_ptr<isa64gzb::RiscV64GZBInstructionSet> riscv_isa_; }; } // namespace riscv
diff --git a/riscv/riscv64g_vec_decoder.cc b/riscv/riscv64g_vec_decoder.cc index f3fde6e..9707e09 100644 --- a/riscv/riscv64g_vec_decoder.cc +++ b/riscv/riscv64g_vec_decoder.cc
@@ -16,86 +16,36 @@ #include <cstdint> #include <memory> -#include <new> -#include <string> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64g_vec_encoding.h" #include "riscv/riscv64gv_decoder.h" #include "riscv/riscv64gv_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV64GVecDecoder::RiscV64GVecDecoder(RiscVState* state, util::MemoryInterface* memory) - : state_(state), - memory_(memory), - inst_db_(state_->db_factory()->Allocate<uint32_t>(1)) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - + : state_(state), memory_(memory) { // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. riscv_isa_factory_ = std::make_unique<RV64GVIsaFactory>(); riscv_isa_ = std::make_unique<isa64v::RiscV64GVInstructionSet>( state, riscv_isa_factory_.get()); riscv_encoding_ = std::make_unique<isa64v::RiscV64GVecEncoding>(state); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); + decoder_ = std::make_unique< + RiscVGenericDecoder<isa64v::OpcodeEnum, isa64v::RiscV64GVecEncoding, + isa64v::RiscV64GVInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } -RiscV64GVecDecoder::~RiscV64GVecDecoder() { inst_db_->DecRef(); } - generic::Instruction* RiscV64GVecDecoder::DecodeInstruction(uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa64v::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAddressMisaligned, address ^ 0x1, - inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // object that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa64v::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAccessFault, address, nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - const uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_.get()); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv64g_vec_decoder.h b/riscv/riscv64g_vec_decoder.h index 9a651cf..0b584f1 100644 --- a/riscv/riscv64g_vec_decoder.h +++ b/riscv/riscv64g_vec_decoder.h
@@ -18,23 +18,19 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64g_vec_encoding.h" #include "riscv/riscv64gv_decoder.h" #include "riscv/riscv64gv_enums.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. - // This is the factory class needed by the generated decoder. It is responsible // for creating the decoder for each slot instance. Since the riscv architecture // only has a single slot, it's a pretty simple class. @@ -56,7 +52,6 @@ RiscV64GVecDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV64GVecDecoder() = delete; - ~RiscV64GVecDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -69,15 +64,18 @@ return isa64v::kOpcodeNames[index]; } + // Getter. + isa64v::RiscV64GVecEncoding* riscv_encoding() const { + return riscv_encoding_.get(); + } + private: RiscVState* const state_; util::MemoryInterface* const memory_; - - // Buffer used to load instructions from memory. Re-used for each instruction - // word. - generic::DataBuffer* const inst_db_; - - std::unique_ptr<generic::ProgramError> decode_error_; + std::unique_ptr< + RiscVGenericDecoder<isa64v::OpcodeEnum, isa64v::RiscV64GVecEncoding, + isa64v::RiscV64GVInstructionSet>> + decoder_; std::unique_ptr<isa64v::RiscV64GVecEncoding> riscv_encoding_; std::unique_ptr<RV64GVIsaFactory> riscv_isa_factory_; std::unique_ptr<isa64v::RiscV64GVInstructionSet> riscv_isa_;
diff --git a/riscv/riscv64gzb_vec_decoder.cc b/riscv/riscv64gzb_vec_decoder.cc index a578f89..bc5bbbe 100644 --- a/riscv/riscv64gzb_vec_decoder.cc +++ b/riscv/riscv64gzb_vec_decoder.cc
@@ -16,87 +16,37 @@ #include <cstdint> #include <memory> -#include <new> -#include <string> #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64gvzb_decoder.h" #include "riscv/riscv64gvzb_enums.h" #include "riscv/riscv64gzb_vec_encoding.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). - RiscV64GZBVecDecoder::RiscV64GZBVecDecoder(RiscVState* state, util::MemoryInterface* memory) - : state_(state), - memory_(memory), - inst_db_(state_->db_factory()->Allocate<uint32_t>(1)) { - // Get a handle to the internal error in the program error controller. - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); - + : state_(state), memory_(memory) { // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. riscv_isa_factory_ = std::make_unique<RV64GVZBIsaFactory>(); riscv_isa_ = std::make_unique<isa64gvzb::RiscV64GVZBInstructionSet>( state, riscv_isa_factory_.get()); riscv_encoding_ = std::make_unique<isa64gvzb::RiscV64GZBVecEncoding>(state); - decode_error_ = state->program_error_controller()->GetProgramError( - generic::ProgramErrorController::kInternalErrorName); + decoder_ = std::make_unique<RiscVGenericDecoder< + isa64gvzb::OpcodeEnum, isa64gvzb::RiscV64GZBVecEncoding, + isa64gvzb::RiscV64GVZBInstructionSet>>( + state, memory, riscv_encoding_.get(), riscv_isa_.get()); } -RiscV64GZBVecDecoder::~RiscV64GZBVecDecoder() { inst_db_->DecRef(); } - generic::Instruction* RiscV64GZBVecDecoder::DecodeInstruction( uint64_t address) { - // First check that the address is aligned properly. If not, create and return - // an instruction object that will raise an exception. - if (address & 0x1) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(1); - inst->SetDisassemblyString("Misaligned instruction address"); - inst->set_opcode(*isa64gvzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAddressMisaligned, address ^ 0x1, - inst); - }); - return inst; - } - - // If the address is greater than the max address, return an instruction - // object that will raise an exception. - if (address > state_->max_physical_address()) { - auto* inst = new generic::Instruction(address, state_); - inst->set_size(0); - inst->SetDisassemblyString("Instruction access fault"); - inst->set_opcode(*isa64gvzb::OpcodeEnum::kNone); - inst->set_address(address); - inst->set_semantic_function([this, address](generic::Instruction* inst) { - state_->Trap(/*is_interrupt*/ false, address, - *ExceptionCode::kInstructionAccessFault, address, nullptr); - }); - return inst; - } - - // Read the instruction word from memory and parse it in the encoding parser. - memory_->Load(address, inst_db_, nullptr, nullptr); - const uint32_t iword = inst_db_->Get<uint32_t>(0); - riscv_encoding_->ParseInstruction(iword); - - // Call the isa decoder to obtain a new instruction object for the instruction - // word that was parsed above. - auto* instruction = riscv_isa_->Decode(address, riscv_encoding_.get()); - return instruction; + return decoder_->DecodeInstruction(address); } } // namespace riscv
diff --git a/riscv/riscv64gzb_vec_decoder.h b/riscv/riscv64gzb_vec_decoder.h index 723defb..954e56b 100644 --- a/riscv/riscv64gzb_vec_decoder.h +++ b/riscv/riscv64gzb_vec_decoder.h
@@ -18,23 +18,19 @@ #include <cstdint> #include <memory> -#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" -#include "mpact/sim/generic/program_error.h" -#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/memory_interface.h" #include "riscv/riscv64gvzb_decoder.h" #include "riscv/riscv64gvzb_enums.h" #include "riscv/riscv64gzb_vec_encoding.h" +#include "riscv/riscv_generic_decoder.h" #include "riscv/riscv_state.h" namespace mpact { namespace sim { namespace riscv { -using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. - // This is the factory class needed by the generated decoder. It is responsible // for creating the decoder for each slot instance. Since the riscv architecture // only has a single slot, it's a pretty simple class. @@ -56,7 +52,6 @@ RiscV64GZBVecDecoder(RiscVState* state, util::MemoryInterface* memory); RiscV64GZBVecDecoder() = delete; - ~RiscV64GZBVecDecoder() override; // This will always return a valid instruction that can be executed. In the // case of a decode error, the semantic function in the instruction object @@ -69,15 +64,18 @@ return isa64gvzb::kOpcodeNames[index]; } + // Getter. + isa64gvzb::RiscV64GZBVecEncoding* riscv_encoding() const { + return riscv_encoding_.get(); + } + private: RiscVState* const state_; util::MemoryInterface* const memory_; - - // Buffer used to load instructions from memory. Re-used for each instruction - // word. - generic::DataBuffer* const inst_db_; - - std::unique_ptr<generic::ProgramError> decode_error_; + std::unique_ptr<RiscVGenericDecoder<isa64gvzb::OpcodeEnum, + isa64gvzb::RiscV64GZBVecEncoding, + isa64gvzb::RiscV64GVZBInstructionSet>> + decoder_; std::unique_ptr<isa64gvzb::RiscV64GZBVecEncoding> riscv_encoding_; std::unique_ptr<RV64GVZBIsaFactory> riscv_isa_factory_; std::unique_ptr<isa64gvzb::RiscV64GVZBInstructionSet> riscv_isa_;
diff --git a/riscv/riscv_generic_decoder.h b/riscv/riscv_generic_decoder.h new file mode 100644 index 0000000..9e24d9b --- /dev/null +++ b/riscv/riscv_generic_decoder.h
@@ -0,0 +1,152 @@ +// 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 <cstdint> + +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/decoder_interface.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/memory_interface.h" +#include "riscv/riscv_csr.h" +#include "riscv/riscv_state.h" + +#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_ + +namespace mpact::sim::riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +using ::mpact::sim::generic::Instruction; + +template <typename OpcodeEnum, typename Encoding, typename Isa> +class RiscVGenericDecoder { + public: + RiscVGenericDecoder(RiscVState* state, util::MemoryInterface* memory, + Encoding* encoding, Isa* isa) + : state_(state), memory_(memory), encoding_(encoding), isa_(isa) { + auto res = + state_->csr_set()->GetCsr(static_cast<uint64_t>(RiscVCsrEnum::kMIsa)); + if (res.ok()) { + isa_csr_ = res.value(); + } else { + isa_csr_ = nullptr; + LOG(FATAL) << "Failed to get misa CSR: " << res.status(); + } + + // Need a data buffer to load instructions from memory. Allocate a single + // buffer that can be reused for each instruction word. + inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); + } + + ~RiscVGenericDecoder() { inst_db_->DecRef(); } + + Instruction* DecodeInstruction(uint64_t address) { + Instruction* instruction = nullptr; + auto status = CheckAddress(address, instruction); + if (!status.ok()) { + LOG(FATAL) << "Decoder error check failed."; + return nullptr; + } + if (instruction != nullptr) return instruction; + + // Read the instruction word from memory and parse it in the encoding + // parser. + memory_->Load(address, inst_db_, nullptr, nullptr); + uint32_t iword = inst_db_->Get<uint32_t>(0); + encoding_->ParseInstruction(iword); + + // Call the isa decoder to obtain a new instruction object for the + // instruction word that was parsed above. + instruction = isa_->Decode(address, encoding_); + return instruction; + } + + private: + // Check that the address is valid. If not, return an instruction object that + // will raise an exception. + absl::Status CheckAddress(uint64_t address, Instruction*& inst) { + if (isa_csr_ == nullptr) { + return absl::InvalidArgumentError("MISA CSR is null"); + } + if (inst != nullptr) { + return absl::InvalidArgumentError("Instruction is not null"); + } + // First check that the address is aligned properly. If not, create and + // return an instruction object that will raise an exception. + if (address & 0x1) { + inst = new generic::Instruction(0, state_); + inst->set_size(1); + inst->SetDisassemblyString("Misaligned instruction address"); + inst->set_opcode(*OpcodeEnum::kNone); + inst->set_address(address); + inst->set_semantic_function([this](generic::Instruction* inst) { + state_->Trap(/*is_interrupt*/ false, inst->address(), + *ExceptionCode::kInstructionAddressMisaligned, + inst->address() ^ 0x1, inst); + }); + return absl::OkStatus(); + } + + // If the C extension is not supported and the address is not 4 byte + // aligned, return an instruction that will raise an exception. + if (((isa_csr_->GetUint64() & + static_cast<uint64_t>(IsaExtensions::kCompressed)) == 0) && + (address & 0x3)) { + inst = new generic::Instruction(0, state_); + inst->set_size(1); + inst->SetDisassemblyString("Misaligned instruction address"); + inst->set_opcode(*OpcodeEnum::kNone); + inst->set_address(address); + inst->set_semantic_function([this](generic::Instruction* inst) { + state_->Trap(/*is_interrupt*/ false, inst->address(), + *ExceptionCode::kInstructionAddressMisaligned, + inst->address() & ~0x3, inst); + }); + return absl::OkStatus(); + } + + // If the address is greater than the max address, return an instruction + // that will raise an exception. + if (address > state_->max_physical_address()) { + inst = new generic::Instruction(0, state_); + inst->set_size(0); + inst->SetDisassemblyString("Instruction access fault"); + inst->set_opcode(*OpcodeEnum::kNone); + inst->set_address(address); + inst->set_semantic_function([this](generic::Instruction* inst) { + state_->Trap(/*is_interrupt*/ false, inst->address(), + *ExceptionCode::kInstructionAccessFault, inst->address(), + nullptr); + }); + return absl::OkStatus(); + } + + return absl::OkStatus(); + } + + RiscVState* state_; + util::MemoryInterface* memory_; + Encoding* encoding_; + Isa* isa_; + generic::DataBuffer* inst_db_; + RiscVCsrInterface* isa_csr_; +}; + +} // namespace mpact::sim::riscv + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_
diff --git a/riscv/riscv_state.h b/riscv/riscv_state.h index 63c5aaa..f04d56d 100644 --- a/riscv/riscv_state.h +++ b/riscv/riscv_state.h
@@ -48,6 +48,36 @@ using Instruction = ::mpact::sim::generic::Instruction; using ReferenceCount = ::mpact::sim::generic::ReferenceCount; +enum class IsaExtensions : uint64_t { + kAtomic = 1 << 0, + kBitManipulation = 1 << 1, + kCompressed = 1 << 2, + kDoublePrecisionFp = 1 << 3, + kEBaseIsa = 1 << 4, + kSinglePrecisionFp = 1 << 5, + kGReserved = 1 << 6, + kHypervisor = 1 << 7, + kIBaseIsa = 1 << 8, + kJReserved = 1 << 9, + kKReserved = 1 << 10, + kLReserved = 1 << 11, + kIntegerMulDiv = 1 << 12, + kUserLevelInterrupts = 1 << 13, + kOReserved = 1 << 14, + kPReserved = 1 << 15, + kQuadPrecisionFp = 1 << 16, + kRReserved = 1 << 17, + kSupervisorMode = 1 << 18, + kTReserved = 1 << 19, + kUserMode = 1 << 20, + kVector = 1 << 21, + kWReserved = 1 << 22, + kNonStandardExtension = 1 << 23, + kYReserved = 1 << 24, + kZReserved = 1 << 25, +}; + +constexpr uint64_t kCompressedExtension = 1 << 2; // Exception codes. enum class ExceptionCode : uint64_t { kInstructionAddressMisaligned = 0,
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index a41ea0c..3d0b397 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -475,13 +475,16 @@ ], deps = [ "//riscv:riscv32g_decoder", + "//riscv:riscv32g_isa", "//riscv:riscv_state", "@com_github_serge1_elfio//:elfio", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", "@com_google_mpact-sim//mpact/sim/util/memory", "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader", ], @@ -498,6 +501,7 @@ ], deps = [ "//riscv:riscv64g_decoder", + "//riscv:riscv64g_isa", "//riscv:riscv_state", "@com_github_serge1_elfio//:elfio", "@com_google_absl//absl/log", @@ -505,7 +509,9 @@ "@com_google_absl//absl/log:log_sink_registry", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", "@com_google_mpact-sim//mpact/sim/util/memory", "@com_google_mpact-sim//mpact/sim/util/other:log_sink", "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader",
diff --git a/riscv/test/riscv32_bitmanip_instructions_test.cc b/riscv/test/riscv32_bitmanip_instructions_test.cc index a679ba3..0b526d6 100644 --- a/riscv/test/riscv32_bitmanip_instructions_test.cc +++ b/riscv/test/riscv32_bitmanip_instructions_test.cc
@@ -16,7 +16,6 @@ #include <cstdint> #include <ios> #include <limits> -#include <new> #include <string> #include <tuple> #include <vector>
diff --git a/riscv/test/riscv32_decoder_test.cc b/riscv/test/riscv32_decoder_test.cc index a0d2d91..282238d 100644 --- a/riscv/test/riscv32_decoder_test.cc +++ b/riscv/test/riscv32_decoder_test.cc
@@ -25,14 +25,24 @@ #include "elfio/elfio_section.hpp" #include "elfio/elfio_symbols.hpp" #include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/flat_demand_memory.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" +#include "riscv/riscv32g_enums.h" +#include "riscv/riscv_csr.h" #include "riscv/riscv_state.h" namespace { +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. +using ::mpact::sim::riscv::IsaExtensions; +using ::mpact::sim::riscv::RiscVCsrEnum; using ::mpact::sim::riscv::RiscVXlen; +using ::mpact::sim::riscv::isa32::OpcodeEnum; + +constexpr uint16_t kCNopOpcode = 0b000'0'00000'00000'01; constexpr char kFileName[] = "hello_world.elf"; @@ -64,6 +74,7 @@ mpact::sim::riscv::RiscVState state_; mpact::sim::util::ElfProgramLoader loader_; mpact::sim::riscv::RiscV32Decoder decoder_; + mpact::sim::generic::DataBufferFactory db_factory_; SymbolAccessor* symbol_accessor_; }; @@ -113,4 +124,29 @@ inst->DecRef(); } +TEST_F(RiscV32DecoderTest, BadAddressNoCompressedInstructions) { + // Write opcode to memory. + auto* db = db_factory_.Allocate<uint16_t>(1); + db->Set<uint16_t>(0, kCNopOpcode); + memory_.Store(0x4322, db); + db->DecRef(); + // Get a handle to the MISA CSR. + auto res = state_.csr_set()->GetCsr(*RiscVCsrEnum::kMIsa); + CHECK_OK(res.status()); + auto* misa_csr = res.value(); + misa_csr->Set(misa_csr->GetUint64() | *IsaExtensions::kCompressed); + // If the C bit is set, then the decoder should succeed. + auto* inst = decoder_.DecodeInstruction(0x4322); + ASSERT_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), static_cast<uint32_t>(OpcodeEnum::kCnop)); + inst->DecRef(); + misa_csr->Set(misa_csr->GetUint64() & ~*IsaExtensions::kCompressed); + // If the C bit is not set, then the decoder should generate an invalid + // address exception. + inst = decoder_.DecodeInstruction(0x4322); + ASSERT_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), static_cast<uint32_t>(OpcodeEnum::kNone)); + inst->DecRef(); +} + } // namespace
diff --git a/riscv/test/riscv64_decoder_test.cc b/riscv/test/riscv64_decoder_test.cc index e4461a5..41f3911 100644 --- a/riscv/test/riscv64_decoder_test.cc +++ b/riscv/test/riscv64_decoder_test.cc
@@ -26,19 +26,29 @@ #include "elfio/elfio_section.hpp" #include "elfio/elfio_symbols.hpp" #include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/type_helpers.h" #include "mpact/sim/util/memory/flat_demand_memory.h" #include "mpact/sim/util/other/log_sink.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" +#include "riscv/riscv64g_enums.h" +#include "riscv/riscv_csr.h" #include "riscv/riscv_state.h" namespace { +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. +using ::mpact::sim::riscv::IsaExtensions; using ::mpact::sim::riscv::RiscV64Decoder; +using ::mpact::sim::riscv::RiscVCsrEnum; using ::mpact::sim::riscv::RiscVState; using ::mpact::sim::riscv::RiscVXlen; +using ::mpact::sim::riscv::isa64::OpcodeEnum; using ::mpact::sim::util::LogSink; +constexpr uint16_t kCNopOpcode = 0b000'0'00000'00000'01; + constexpr char kFileName[] = "hello_world_64.elf"; // The depot path to the test directory. @@ -69,6 +79,7 @@ mpact::sim::util::FlatDemandMemory memory_; mpact::sim::util::ElfProgramLoader loader_; RiscV64Decoder decoder_; + mpact::sim::generic::DataBufferFactory db_factory_; SymbolAccessor* symbol_accessor_; }; @@ -128,4 +139,29 @@ inst->DecRef(); } +TEST_F(RiscV64DecoderTest, BadAddressNoCompressedInstructions) { + // Write opcode to memory. + auto* db = db_factory_.Allocate<uint16_t>(1); + db->Set<uint16_t>(0, kCNopOpcode); + memory_.Store(0x4322, db); + db->DecRef(); + // Get a handle to the MISA CSR. + auto res = state_.csr_set()->GetCsr(*RiscVCsrEnum::kMIsa); + CHECK_OK(res.status()); + auto* misa_csr = res.value(); + misa_csr->Set(misa_csr->GetUint64() | *IsaExtensions::kCompressed); + // If the C bit is set, then the decoder should succeed. + auto* inst = decoder_.DecodeInstruction(0x4322); + ASSERT_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), static_cast<uint32_t>(OpcodeEnum::kCnop)); + inst->DecRef(); + misa_csr->Set(misa_csr->GetUint64() & ~*IsaExtensions::kCompressed); + // If the C bit is not set, then the decoder should generate an invalid + // address exception. + inst = decoder_.DecodeInstruction(0x4322); + ASSERT_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), static_cast<uint32_t>(OpcodeEnum::kNone)); + inst->DecRef(); +} + } // namespace