Internal change PiperOrigin-RevId: 528888255 Change-Id: I91889da5039ffabe4249a6883132ef06e5dddee4
diff --git a/mpact/sim/decoder/bin_decoder.cc b/mpact/sim/decoder/bin_decoder.cc index 04baa27..3eb1786 100644 --- a/mpact/sim/decoder/bin_decoder.cc +++ b/mpact/sim/decoder/bin_decoder.cc
@@ -17,7 +17,6 @@ #include <string> #include "mpact/sim/decoder/bin_encoding_info.h" -#include "mpact/sim/decoder/instruction_encoding.h" #include "mpact/sim/decoder/instruction_group.h" namespace mpact { @@ -38,7 +37,7 @@ instruction_group_vec_.clear(); } -void BinDecoder::SelectInstructionGroupForDecoder(InstructionGroup *group) { +void BinDecoder::AddInstructionGroup(InstructionGroup *group) { instruction_group_vec_.push_back(group); }
diff --git a/mpact/sim/decoder/bin_decoder.h b/mpact/sim/decoder/bin_decoder.h index 285b8ff..e096ab8 100644 --- a/mpact/sim/decoder/bin_decoder.h +++ b/mpact/sim/decoder/bin_decoder.h
@@ -46,7 +46,7 @@ // Checks for invalid encodings, such as some duplicates. void CheckEncodings(); // Select instruction group for decoder generation. - void SelectInstructionGroupForDecoder(InstructionGroup *group); + void AddInstructionGroup(InstructionGroup *group); // Accessors. const std::string &name() const { return name_; }
diff --git a/mpact/sim/decoder/bin_encoding_info.cc b/mpact/sim/decoder/bin_encoding_info.cc index 435f447..ab700f1 100644 --- a/mpact/sim/decoder/bin_encoding_info.cc +++ b/mpact/sim/decoder/bin_encoding_info.cc
@@ -33,7 +33,13 @@ DecoderErrorListener *error_listener) : opcode_enum_(opcode_enum), error_listener_(error_listener) {} -BinEncodingInfo::~BinEncodingInfo() { delete decoder_; } +BinEncodingInfo::~BinEncodingInfo() { + delete decoder_; + for (auto &[unused, format_ptr] : format_map_) { + delete format_ptr; + } + format_map_.clear(); +} // Add name of an include file to be included in the generated code. void BinEncodingInfo::AddIncludeFile(std::string include_file) { @@ -45,7 +51,7 @@ int width) { // Verify that the format name hasn't been used. if (format_map_.contains(name)) { - return absl::InternalError( + return absl::AlreadyExistsError( absl::StrCat("Error: format '", name, "' already defined")); } auto format = new Format(name, width, this); @@ -58,7 +64,7 @@ std::string parent_name) { // Verify that the format name hasn't been used. if (format_map_.contains(name)) { - return absl::InternalError( + return absl::AlreadyExistsError( absl::StrCat("Error: format '", name, "' already defined")); } auto format = new Format(name, width, parent_name, this); @@ -78,7 +84,7 @@ absl::StatusOr<InstructionGroup *> BinEncodingInfo::AddInstructionGroup( std::string name, int width, std::string format_name) { if (instruction_group_map_.contains(name)) { - return absl::InternalError( + return absl::AlreadyExistsError( absl::StrCat("Error: instruction group '", name, "' already defined")); } auto group = @@ -89,7 +95,7 @@ // Top level method that calls the checking method of each format. This is // called after all the formats have been added. -absl::Status BinEncodingInfo::CheckFormats() { +void BinEncodingInfo::PropagateExtractors() { for (auto &[unused, format] : format_map_) { // For the base formats (those who do not inherit from another format). if (format->base_format() == nullptr) { @@ -102,7 +108,6 @@ format->PropagateExtractorsDown(); } } - return absl::OkStatus(); } BinDecoder *BinEncodingInfo::AddBinDecoder(std::string name) {
diff --git a/mpact/sim/decoder/bin_encoding_info.h b/mpact/sim/decoder/bin_encoding_info.h index ee3cdff..6e3cee2 100644 --- a/mpact/sim/decoder/bin_encoding_info.h +++ b/mpact/sim/decoder/bin_encoding_info.h
@@ -16,7 +16,6 @@ #define MPACT_SIM_DECODER_BIN_ENCODING_INFO_H_ #include <string> -#include <vector> #include "absl/container/btree_map.h" #include "absl/container/btree_set.h" @@ -59,10 +58,8 @@ absl::StatusOr<InstructionGroup *> AddInstructionGroup( std::string name, int width, std::string format_name); - // Performs a consistency check on the formats and handles any out of order - // declarations - there is no rule that a format has to be declared before it - // is used. - absl::Status CheckFormats(); + // Propagates bitfield extractors where possible. + void PropagateExtractors(); // Create and add binary decoder descriptor. BinDecoder *AddBinDecoder(std::string name);
diff --git a/mpact/sim/decoder/bin_format_gen_main.cc b/mpact/sim/decoder/bin_format_gen_main.cc index 3333d2e..30494fc 100644 --- a/mpact/sim/decoder/bin_format_gen_main.cc +++ b/mpact/sim/decoder/bin_format_gen_main.cc
@@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <fstream> #include <iostream> #include <ostream> #include <string> @@ -39,11 +38,10 @@ int main(int argc, char **argv) { auto arg_vec = absl::ParseCommandLine(argc, argv); - std::string file_name; + std::vector<std::string> file_names; - // Open input file as stream if specified. - if (arg_vec.size() > 1) { - file_name = arg_vec[1]; + for (int i = 1; i < arg_vec.size(); ++i) { + file_names.push_back(arg_vec[i]); } ::mpact::sim::decoder::bin_format::BinFormatVisitor visitor; @@ -61,7 +59,7 @@ exit(-1); } - auto status = visitor.Process(file_name, decoder_name, prefix, include_roots, + auto status = visitor.Process(file_names, decoder_name, prefix, include_roots, output_dir); if (!status.ok()) { LOG(ERROR) << status.message();
diff --git a/mpact/sim/decoder/bin_format_visitor.cc b/mpact/sim/decoder/bin_format_visitor.cc index c3fff36..468542c 100644 --- a/mpact/sim/decoder/bin_format_visitor.cc +++ b/mpact/sim/decoder/bin_format_visitor.cc
@@ -27,7 +27,7 @@ #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" +#include "antlr4-runtime/ParserRuleContext.h" #include "mpact/sim/decoder/bin_decoder.h" #include "mpact/sim/decoder/bin_encoding_info.h" #include "mpact/sim/decoder/bin_format_contexts.h" @@ -93,7 +93,7 @@ using ::mpact::sim::machine_description::instruction_set::ToHeaderGuard; absl::Status BinFormatVisitor::Process( - const std::string &file_name, const std::string &decoder_name, + const std::vector<std::string> &file_names, const std::string &decoder_name, absl::string_view prefix, const std::vector<std::string> &include_roots, absl::string_view directory) { decoder_name_ = decoder_name; @@ -105,22 +105,22 @@ std::istream *source_stream = &std::cin; - if (!file_name.empty()) { - source_stream = new std::fstream(file_name, std::fstream::in); + if (!file_names.empty()) { + source_stream = new std::fstream(file_names[0], std::fstream::in); } // Create an antlr4 stream from the input stream. BinFmtAntlrParserWrapper parser_wrapper(source_stream); // Create and add the error listener. set_error_listener(std::make_unique<decoder::DecoderErrorListener>()); - error_listener_->set_file_name(file_name); + error_listener_->set_file_name(file_names[0]); parser_wrapper.parser()->removeErrorListeners(); parser_wrapper.parser()->addErrorListener(error_listener()); // Parse the file and then create the data structures. TopLevelCtx *top_level = parser_wrapper.parser()->top_level(); (void)top_level; - if (!file_name.empty()) { + if (!file_names.empty()) { delete source_stream; source_stream = nullptr; } @@ -129,7 +129,13 @@ return absl::InternalError("Errors encountered - terminating."); } // Visit the parse tree starting at the namespaces declaration. - auto encoding_info = VisitTopLevel(top_level, decoder_name); + VisitTopLevel(top_level); + // Process additional source files. + for (int i = 1; i < file_names.size(); ++i) { + ParseIncludeFile(top_level, file_names[i], {}); + } + // Process the parse tree. + auto encoding_info = ProcessTopLevel(decoder_name); // Include files may generate additional syntax errors. if (encoding_info == nullptr) { LOG(ERROR) << "No encoding specified"; @@ -295,11 +301,14 @@ return ret_val; } -std::unique_ptr<BinEncodingInfo> BinFormatVisitor::VisitTopLevel( - TopLevelCtx *ctx, const std::string &decoder_name) { +void BinFormatVisitor::VisitTopLevel(TopLevelCtx *ctx) { PreProcessDeclarations(ctx->declaration_list()); - // At this point we have all the formats, instruction groups and decoders. - // First make sure that the named decoder exists. +} + +std::unique_ptr<BinEncodingInfo> BinFormatVisitor::ProcessTopLevel( + const std::string &decoder_name) { + // At this point we have the contexts for all slots, bundles and isas. + // First make sure the named isa (decoder) has been defined. auto decoder_iter = decoder_decl_map_.find(decoder_name); if (decoder_iter == decoder_decl_map_.end()) { error_listener()->semanticError( @@ -308,11 +317,7 @@ } // Visit the decoder. auto bin_encoding_info = VisitDecoderDef(decoder_iter->second); - auto status = bin_encoding_info->CheckFormats(); - if (!status.ok()) { - error_listener_->semanticError(nullptr, status.message()); - return nullptr; - } + bin_encoding_info->PropagateExtractors(); return bin_encoding_info; } @@ -386,20 +391,30 @@ return; } } + ParseIncludeFile(ctx, file_name, include_dir_vec_); +} + +void BinFormatVisitor::ParseIncludeFile(antlr4::ParserRuleContext *ctx, + const std::string &file_name, + std::vector<std::string> const &dirs) { std::fstream include_file; // Open include file. - for (auto const &dir : include_dir_vec_) { - std::string include_name = absl::StrCat(dir, "/", file_name); - include_file.open(include_name, std::fstream::in); - if (include_file.is_open()) break; - } + include_file.open(file_name, std::fstream::in); if (!include_file.is_open()) { - // Try a local file. - include_file.open(file_name, std::fstream::in); + // Try each of the include file directories. + for (auto const &dir : dirs) { + std::string include_name = absl::StrCat(dir, "/", file_name); + include_file.open(include_name, std::fstream::in); + if (include_file.is_open()) break; + } if (!include_file.is_open()) { - error_listener()->semanticError( - ctx->start, absl::StrCat("Failed to open '", file_name, "'")); - return; + // Try a local file. + include_file.open(file_name, std::fstream::in); + if (!include_file.is_open()) { + error_listener()->semanticError( + ctx->start, absl::StrCat("Failed to open '", file_name, "'")); + return; + } } } std::string previous_file_name = error_listener()->file_name(); @@ -411,6 +426,7 @@ // Add the error listener. include_parser->parser()->removeErrorListeners(); include_parser->parser()->addErrorListener(error_listener()); + // Start parsing at the declaratition_list_w_eof rule. DeclarationListCtx *declaration_list = include_parser->parser()->declaration_list_w_eof()->declaration_list(); include_file.close(); @@ -788,7 +804,7 @@ continue; } group_name_set.insert(group_name); - decoder->SelectInstructionGroupForDecoder(inst_group); + decoder->AddInstructionGroup(inst_group); continue; } @@ -868,7 +884,7 @@ } } group_name_set.insert(parent_group->name()); - decoder->SelectInstructionGroupForDecoder(parent_group); + decoder->AddInstructionGroup(parent_group); continue; } }
diff --git a/mpact/sim/decoder/bin_format_visitor.h b/mpact/sim/decoder/bin_format_visitor.h index 87d4bff..b2638bc 100644 --- a/mpact/sim/decoder/bin_format_visitor.h +++ b/mpact/sim/decoder/bin_format_visitor.h
@@ -16,7 +16,6 @@ #define MPACT_SIM_DECODER_BIN_FORMAT_VISITOR_H_ #include <deque> -#include <list> #include <memory> #include <string> #include <utility> @@ -24,6 +23,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" +#include "antlr4-runtime/ParserRuleContext.h" #include "mpact/sim/decoder/BinFormatLexer.h" #include "mpact/sim/decoder/antlr_parser_wrapper.h" #include "mpact/sim/decoder/bin_encoding_info.h" @@ -67,7 +67,7 @@ // Entry point for processing a source_stream input, generating any output // files in the given directory. Returns OK if no errors were encountered. - absl::Status Process(const std::string &file_name, + absl::Status Process(const std::vector<std::string> &file_names, const std::string &decoder_name, absl::string_view prefix, const std::vector<std::string> &include_roots, @@ -88,14 +88,18 @@ BitRange GetBitIndexRange(BitIndexRangeCtx *ctx); int ConvertToInt(NumberCtx *ctx); // Methods that visit the nodes of the parse tree. - std::unique_ptr<BinEncodingInfo> VisitTopLevel( - TopLevelCtx *ctx, const std::string &decoder_name); + void VisitTopLevel(TopLevelCtx *ctx); + std::unique_ptr<BinEncodingInfo> ProcessTopLevel( + const std::string &decoder_name); void PreProcessDeclarations(DeclarationListCtx *ctx); void VisitDeclarations(DeclarationListCtx *ctx, BinEncodingInfo *encoding_info); void VisitFormatDef(FormatDefCtx *ctx, BinEncodingInfo *encoding_info); void VisitFieldDef(FieldDefCtx *ctx, Format *format); void VisitIncludeFile(IncludeFileCtx *ctx); + void ParseIncludeFile(antlr4::ParserRuleContext *ctx, + const std::string &file_name, + const std::vector<std::string> &dirs); void VisitOverlayDef(OverlayDefCtx *ctx, Format *format); void VisitOverlayBitField(BitFieldCtx *ctx, Overlay *overlay); InstructionGroup *VisitInstructionGroupDef(InstructionGroupDefCtx *ctx,
diff --git a/mpact/sim/decoder/decoder_error_listener.cc b/mpact/sim/decoder/decoder_error_listener.cc index 0bf5c2b..9a6e4c1 100644 --- a/mpact/sim/decoder/decoder_error_listener.cc +++ b/mpact/sim/decoder/decoder_error_listener.cc
@@ -67,16 +67,21 @@ bool exact, const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs) { + // Empty. } void DecoderErrorListener::reportAttemptingFullContext( antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, - antlr4::atn::ATNConfigSet *configs) {} + antlr4::atn::ATNConfigSet *configs) { + // Empty. +} void DecoderErrorListener::reportContextSensitivity( antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, - size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) {} + size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) { + // Empty. +} } // namespace decoder } // namespace sim
diff --git a/mpact/sim/decoder/decoder_gen_main.cc b/mpact/sim/decoder/decoder_gen_main.cc index cdc7842..fe4588f 100644 --- a/mpact/sim/decoder/decoder_gen_main.cc +++ b/mpact/sim/decoder/decoder_gen_main.cc
@@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <fstream> #include <iostream> #include <ostream> #include <string> @@ -57,11 +56,10 @@ int main(int argc, char **argv) { auto arg_vec = absl::ParseCommandLine(argc, argv); - std::string file_name; + std::vector<std::string> file_names; - // Open input file as stream if specified. - if (arg_vec.size() > 1) { - file_name = arg_vec[1]; + for (int i = 1; i < arg_vec.size(); ++i) { + file_names.push_back(arg_vec[i]); } mpact::sim::machine_description::instruction_set::InstructionSetVisitor @@ -80,6 +78,11 @@ exit(-1); } auto status = - visitor.Process(file_name, prefix, isa_name, include_roots, output_dir); - return status.ok() ? 0 : -1; + visitor.Process(file_names, prefix, isa_name, include_roots, output_dir); + + if (!status.ok()) { + LOG(ERROR) << status.message(); + return -1; + } + return 0; }
diff --git a/mpact/sim/decoder/encoding_group.cc b/mpact/sim/decoder/encoding_group.cc index 9bdab50..1261de9 100644 --- a/mpact/sim/decoder/encoding_group.cc +++ b/mpact/sim/decoder/encoding_group.cc
@@ -272,7 +272,7 @@ "None,"); } - // Break the line every 4 iterms. + // Break the line every 4 items. if ((i % per_line) == per_line - 1) { absl::StrAppend(initializers_ptr, "\n"); }
diff --git a/mpact/sim/decoder/format.cc b/mpact/sim/decoder/format.cc index 3563c38..604a8f5 100644 --- a/mpact/sim/decoder/format.cc +++ b/mpact/sim/decoder/format.cc
@@ -31,6 +31,16 @@ using ::mpact::sim::machine_description::instruction_set::ToSnakeCase; +FieldOrFormat::~FieldOrFormat() { + if (is_field_) { + delete field_; + } else { + delete format_; + } + field_ = nullptr; + format_ = nullptr; +} + // The equality operator compares to verify that the field/format definitions // are equivalent, i.e., refers to the same bits. bool FieldOrFormat::operator==(const FieldOrFormat &rhs) const { @@ -61,9 +71,9 @@ encoding_info_(encoding_info) {} Format::~Format() { - for (auto &[unused, field_ptr] : field_map_) { - delete field_ptr; - } + // for (auto &[unused, field_ptr] : field_map_) { + // delete field_ptr; + // } field_map_.clear(); for (auto &[unused, overlay_ptr] : overlay_map_) { delete overlay_ptr; @@ -247,7 +257,7 @@ // promoted. if (overlay_ptr == nullptr) continue; auto iter = base_format_->overlay_extractors_.find(name); - // If it isn't in the partent, add it. + // If it isn't in the parent, add it. if (iter == base_format_->overlay_extractors_.end()) { base_format_->overlay_extractors_.insert( std::make_pair(name, overlay_ptr));
diff --git a/mpact/sim/decoder/format.h b/mpact/sim/decoder/format.h index 9d15d09..4cb1765 100644 --- a/mpact/sim/decoder/format.h +++ b/mpact/sim/decoder/format.h
@@ -72,6 +72,7 @@ explicit FieldOrFormat(Field *field) : is_field_(true), field_(field) {} FieldOrFormat(std::string fmt_name, int size, antlr4::Token *ctx) : is_field_(false), format_name_(fmt_name), size_(size), ctx_(ctx) {} + ~FieldOrFormat(); bool is_field() const { return is_field_; } Field *field() const { return field_; }
diff --git a/mpact/sim/decoder/instruction_encoding.cc b/mpact/sim/decoder/instruction_encoding.cc index 6db1035..f951c40 100644 --- a/mpact/sim/decoder/instruction_encoding.cc +++ b/mpact/sim/decoder/instruction_encoding.cc
@@ -53,11 +53,37 @@ // Check if the field name is indeed a field. auto *field = format_->GetField(field_name); if (field != nullptr) { - if (value >= (1 << field->width)) { - return absl::InternalError( - absl::StrCat("Constraint value (", value, - ") exceeds field width for field '", field_name, "'")); + bool is_signed = field->is_signed; + if (!is_signed) { + if (value >= (1 << field->width) || (value < 0)) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value (", value, ") out of range for unsigned field '", + field_name, "'")); + } + } else { // Field is signed. + // Only eq and ne constraints are allowed on signed fields. + if (type != ConstraintType::kEq && type != ConstraintType::kNe) { + return absl::InvalidArgumentError( + absl::StrCat("Only eq and ne constraints allowed on signed field: ", + field_name)); + } + // Check that the value is in range. + if (value < 0) { + int64_t min_value = -(1 << (field->width - 1)); + if (value < min_value) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value (", value, ") out of range for signed field '", + field_name, "'")); + } + } else { + if (value >= (1 << (field->width - 1))) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value (", value, ") out of range for signed field '", + field_name, "'")); + } + } } + value &= (1 << field->width) - 1; auto *constraint = new Constraint(); constraint->type = type; constraint->field = field; @@ -68,14 +94,40 @@ auto *overlay = format_->GetOverlay(field_name); if (overlay == nullptr) { // If neither, it's an error. - return absl::InternalError(absl::StrCat( + return absl::NotFoundError(absl::StrCat( "Format '", format_->name(), "' does not contain a field or overlay named ", field_name)); } - if (value >= (1 << overlay->computed_width())) { - return absl::InternalError(absl::StrCat( - "Constraint value exceeds field width for field '", field_name, "'")); + int width = overlay->computed_width(); + bool is_signed = overlay->is_signed(); + if (!is_signed) { + if ((value >= (1 << width)) || (value < 0)) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value exceeds field width for field '", field_name, "'")); + } + } else { + if (type != ConstraintType::kEq && type != ConstraintType::kNe) { + return absl::InvalidArgumentError( + absl::StrCat("Only eq and ne constraints allowed on signed overlay: ", + field_name)); + } // Check that the value is in range. + if (value < 0) { + int64_t min_value = -(1 << (width - 1)); + if (value < min_value) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value (", value, ") out of range for signed overlay '", + field_name, "'")); + } + value = value & ((1 << width) - 1); + } else { + if (value >= (1 << (width - 1))) { + return absl::OutOfRangeError(absl::StrCat( + "Constraint value (", value, ") out of range for signed overlay '", + field_name, "'")); + } + } } + value &= (1 << width) - 1; auto *constraint = new Constraint(); constraint->type = type; constraint->overlay = overlay;
diff --git a/mpact/sim/decoder/instruction_encoding.h b/mpact/sim/decoder/instruction_encoding.h index 4c9d7b0..47fcc17 100644 --- a/mpact/sim/decoder/instruction_encoding.h +++ b/mpact/sim/decoder/instruction_encoding.h
@@ -85,8 +85,8 @@ const std::vector<Constraint *> &equal_extracted_constraints() const { return equal_extracted_constraints_; } - // The vector of not-equal constraints that have to be satisfied for an - // instruction to match this encoding. + // The vector of not-equal, greater, less, etc., constraints that have to be + // satisfied for an instruction to match this encoding. const std::vector<Constraint *> &other_constraints() const { return other_constraints_; }
diff --git a/mpact/sim/decoder/instruction_group.cc b/mpact/sim/decoder/instruction_group.cc index 33b99a9..7971fc8 100644 --- a/mpact/sim/decoder/instruction_group.cc +++ b/mpact/sim/decoder/instruction_group.cc
@@ -53,6 +53,10 @@ } encoding_map_.clear(); encoding_vec_.clear(); + for (auto *group : encoding_group_vec_) { + delete group; + } + encoding_group_vec_.clear(); } // Add an instruction encoding into the current group. Check that the format
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc index eed28fc..5f8ddeb 100644 --- a/mpact/sim/decoder/instruction_set_visitor.cc +++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -15,7 +15,6 @@ #include "mpact/sim/decoder/instruction_set_visitor.h" #include <cctype> -#include <filesystem> #include <fstream> #include <iostream> #include <istream> @@ -27,10 +26,8 @@ #include <vector> #include "absl/container/flat_hash_set.h" -#include "absl/log/log.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "antlr4-runtime/antlr4-runtime.h" #include "mpact/sim/decoder/decoder_error_listener.h" #include "mpact/sim/decoder/format_name.h" #include "mpact/sim/decoder/template_expression.h" @@ -75,7 +72,7 @@ // Main entry point for processing the file. absl::Status InstructionSetVisitor::Process( - const std::string &file_name, const std::string &prefix, + const std::vector<std::string> &file_names, const std::string &prefix, const std::string &isa_name, const std::vector<std::string> &include_roots, absl::string_view directory) { if (isa_name.empty()) { @@ -83,7 +80,6 @@ return absl::InvalidArgumentError("Isa name cannot be empty"); } - include_dir_vec_.push_back("."); for (auto &include_root : include_roots) { include_dir_vec_.push_back(include_root); } @@ -91,21 +87,21 @@ std::string isa_prefix = prefix; std::istream *source_stream = &std::cin; - if (!file_name.empty()) { - source_stream = new std::fstream(file_name, std::fstream::in); + if (!file_names.empty()) { + source_stream = new std::fstream(file_names[0], std::fstream::in); } // Create an antlr4 stream from the input stream. IsaAntlrParserWrapper parser_wrapper(source_stream); // Create and add the error listener. set_error_listener(std::make_unique<decoder::DecoderErrorListener>()); - error_listener()->set_file_name(file_name); + error_listener()->set_file_name(file_names[0]); parser_wrapper.parser()->removeErrorListeners(); parser_wrapper.parser()->addErrorListener(error_listener()); // Parse the file and then create the data structures. TopLevelCtx *top_level = parser_wrapper.parser()->top_level(); - if (!file_name.empty()) { + if (!file_names.empty()) { delete source_stream; source_stream = nullptr; } @@ -115,7 +111,13 @@ } // Visit the parse tree starting at the namespaces declaration. - auto instruction_set = VisitTopLevel(top_level, isa_name); + VisitTopLevel(top_level); + // Process additional source files. + for (int i = 1; i < file_names.size(); ++i) { + ParseIncludeFile(top_level, file_names[i], {}); + } + // Now process the parse tree. + auto instruction_set = ProcessTopLevel(isa_name); // Include files may generate additional syntax errors. if (error_listener()->HasError() > 0) { return absl::InternalError("Errors encountered - terminating."); @@ -135,8 +137,16 @@ } // If the prefix is empty, use the source file name. - if (isa_prefix.empty()) { - isa_prefix = ToSnakeCase(std::filesystem::path(file_name).stem().string()); + if (isa_prefix.empty() && file_names.empty()) { + error_listener()->semanticError(nullptr, + "No prefix or file name specified"); + } else if (isa_prefix.empty()) { + isa_prefix = + ToSnakeCase(std::filesystem::path(file_names[0]).stem().string()); + } + // Check for additional errors. + if (error_listener()->HasError() > 0) { + return absl::InternalError("Errors encountered - terminating."); } std::string encoding_type_name = absl::StrCat(ToPascalCase(isa_name), "EncodingBase"); @@ -220,8 +230,7 @@ return absl::OkStatus(); } -std::unique_ptr<InstructionSet> InstructionSetVisitor::VisitTopLevel( - TopLevelCtx *ctx, const std::string &isa_name) { +void InstructionSetVisitor::VisitTopLevel(TopLevelCtx *ctx) { auto declarations = ctx->declaration(); // Process disasm widths. Only the one in the top level file is used if there @@ -243,7 +252,10 @@ // Parse, but don't process all the slots, bundles, isas and include files. PreProcessDeclarations(declarations); +} +std::unique_ptr<InstructionSet> InstructionSetVisitor::ProcessTopLevel( + absl::string_view isa_name) { // At this point we have the contexts for all isas, bundles, and slots. // First make sure that the named isa has been defined. auto isa_ptr = isa_decl_map_.find(isa_name); @@ -455,17 +467,32 @@ return; } } + ParseIncludeFile(ctx, file_name, include_dir_vec_); +} + +void InstructionSetVisitor::ParseIncludeFile( + antlr4::ParserRuleContext *ctx, const std::string &file_name, + const std::vector<std::string> &dirs) { std::fstream include_file; // Open include file. - for (auto const &dir : include_dir_vec_) { - std::string include_name = absl::StrCat(dir, "/", file_name); - include_file.open(include_name, std::fstream::in); - if (include_file.is_open()) break; - } + include_file.open(file_name, std::fstream::in); if (!include_file.is_open()) { - error_listener()->semanticError( - ctx->start, absl::StrCat("Failed to open '", file_name, "'")); - return; + // Try each of the include file directories. + for (auto const &dir : dirs) { + std::string include_name = dir + "/" + file_name; + include_file.open(include_name, std::fstream::in); + if (include_file.is_open()) break; + } + if (!include_file.is_open()) { + if (ctx != nullptr) { + error_listener()->semanticError( + ctx->start, absl::StrCat("Failed to open '", file_name, "'")); + } else { + error_listener()->semanticError( + nullptr, absl::StrCat("Failed to open '", file_name, "'")); + } + return; + } } std::string previous_file_name = error_listener()->file_name(); error_listener()->set_file_name(file_name); @@ -477,9 +504,7 @@ // Add the error listener. include_parser->parser()->removeErrorListeners(); include_parser->parser()->addErrorListener(error_listener()); - // Note, since include statements can only occur after the isa_def, don't - // parse starting at the top level rule, instead start at the - // declaration() rule. + // Start parsing at the include_top_level rule. auto declaration_vec = include_parser->parser()->include_top_level()->declaration(); include_file.close();
diff --git a/mpact/sim/decoder/instruction_set_visitor.h b/mpact/sim/decoder/instruction_set_visitor.h index a29d178..5c529f7 100644 --- a/mpact/sim/decoder/instruction_set_visitor.h +++ b/mpact/sim/decoder/instruction_set_visitor.h
@@ -16,8 +16,6 @@ #define MPACT_SIM_DECODER_INSTRUCTION_SET_VISITOR_H_ #include <deque> -#include <iostream> -#include <istream> #include <memory> #include <optional> #include <string> @@ -29,6 +27,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "antlr4-runtime/ParserRuleContext.h" #include "mpact/sim/decoder/InstructionSetLexer.h" #include "mpact/sim/decoder/InstructionSetParser.h" #include "mpact/sim/decoder/antlr_parser_wrapper.h" @@ -67,8 +66,8 @@ // Entry point for processing a source_stream input, generating any output // files in the given directory. Returns OK if no errors were encountered. - absl::Status Process(const std::string &file_name, const std::string &prefix, - const std::string &isa_name, + absl::Status Process(const std::vector<std::string> &file_names, + const std::string &prefix, const std::string &isa_name, const std::vector<std::string> &include_roots, absl::string_view directory); @@ -123,8 +122,7 @@ // The following methods visits the parts of the parse tree indicated by // the method name and builds up the internal representation used for // decoder generation. - std::unique_ptr<InstructionSet> VisitTopLevel(TopLevelCtx *ctx, - const std::string &isa_name); + void VisitTopLevel(TopLevelCtx *ctx); std::unique_ptr<InstructionSet> VisitIsaDeclaration(IsaDeclCtx *ctx); void VisitConstantDef(ConstantDefCtx *ctx); @@ -154,6 +152,10 @@ Slot *slot, Instruction *inst, ResourceItemCtx *resource_item); void VisitResourceDetailsLists(ResourceDetailsCtx *ctx, Slot *slot, Instruction *inst, ResourceSpec *spec); + std::unique_ptr<InstructionSet> ProcessTopLevel(absl::string_view isa_name); + void ParseIncludeFile(antlr4::ParserRuleContext *ctx, + const std::string &file_name, + const std::vector<std::string> &dirs); DestinationOperand *FindDestinationOpInExpression( ExpressionCtx *ctx, const Slot *slot, const Instruction *inst) const; void PerformOpcodeOverrides(
diff --git a/mpact/sim/decoder/mpact_sim_isa.bzl b/mpact/sim/decoder/mpact_sim_isa.bzl index e1b684e..579e822 100644 --- a/mpact/sim/decoder/mpact_sim_isa.bzl +++ b/mpact/sim/decoder/mpact_sim_isa.bzl
@@ -51,21 +51,31 @@ data = data, ) -def mpact_isa_decoder(name, src, includes, deps = [], isa_name = "", prefix = ""): +def mpact_isa_decoder(name, includes, src = "", srcs = [], deps = [], isa_name = "", prefix = ""): """Generates the C++ source corresponding to an MPACT Isa decoder definition. Args: name: The name of the package to use for the cc_library. - src: The .isa file containing the Isa rules. + src: The .isa file containing the Isa rules (or use srcs). + srcs: The .isa files containing the Isa rules (if src is not specified). deps: Dependencies for the cc_library. includes: Include .isa files. isa_name: Name of isa to generate code for. prefix: File prefix for the generated files (otherwise uses base name of src file """ - if not src.endswith(".isa"): - fail("Grammar must end with .isa", "src") + + # if src is not empty, prepend it to the srcs list. + if src != "": + isa_srcs = [src] + srcs + else: + isa_srcs = srcs + + for f in isa_srcs: + if not f.endswith(".isa"): + fail("Grammar file '" + f + "' must end with .isa", "src") + if prefix == "": - file_prefix = src[:-4] + file_prefix = isa_srcs[0][:-4] base_file_prefix = _strip_path(file_prefix) else: base_file_prefix = prefix @@ -80,15 +90,15 @@ # The command to generate the files. command = ";\n".join([ - _make_isa_tool_invocation_command(base_file_prefix, isa_name), + _make_isa_tool_invocation_command(len(isa_srcs), base_file_prefix, isa_name), ]) # The rule for the generated sources. native.genrule( name = name + "_source", - # Add includes to srcs to ensure they are added to the blaze build sandbox where + # Add includes to isa_srcs to ensure they are added to the blaze build sandbox where # they can be found. - srcs = [src] + includes, + srcs = isa_srcs + includes, outs = out_files, cmd = command, heuristic_label_expansion = 0, @@ -108,19 +118,28 @@ ] + deps, ) -def mpact_bin_fmt_decoder(name, src, includes, deps = [], decoder_name = "", prefix = ""): +def mpact_bin_fmt_decoder(name, includes, src = "", srcs = [], deps = [], decoder_name = "", prefix = ""): """Generates the C++ source corresponding to an MPACT Bin Format decoder definition. Args: name: The name of the package to use for the cc_library. - src: The .bin_fmt file containing the Bin Format rules. includes: Include .bin_fmt files. + src: The .bin_fmt file containing the Bin Format rules. + srcs: List of .bin_fmt files containing the Bin Format rules. deps: Dependencies for the cc_library decoder_name: Name of decoder from .bin_fmt file to generate prefix: File prefix for the generated files (otherwise uses base name of src file """ - if not src.endswith(".bin_fmt"): - fail("Grammar must end with .bin_fmt", "src") + + if src != "": + bin_srcs = [src] + srcs + else: + bin_srcs = srcs + + for f in bin_srcs: + if not f.endswith(".bin_fmt"): + fail("Grammar file '" + f + "' must end with .bin_fmt", "srcs") + if prefix == "": file_prefix = src[:-8] base_file_prefix = _strip_path(file_prefix) @@ -135,7 +154,7 @@ # The command to generate the files. command = ";\n".join([ - _make_bin_tool_invocation_command(base_file_prefix, decoder_name), + _make_bin_tool_invocation_command(len(bin_srcs), base_file_prefix, decoder_name), ]) # The rule for the generated sources. @@ -171,26 +190,32 @@ return text return text[pos + 1:] -# Create the decoder_gen command with arguments, Since the srcs had all the files, including -# those that will be included, the command includes creating a bash array from $(SRCS), then -# instead of using $(SRCS) in the command, it uses only the first element of that array. -def _make_isa_tool_invocation_command(prefix, isa_name): - cmd = "ARR=($(SRCS)); $(location //external:decoder_gen) " + \ - "$${ARR[0]}" + \ - " --prefix " + prefix + \ - " --output_dir $(@D)" +# Create the decoder_gen command with arguments, Since the srcs had all the +# files, including those that will be included, the command includes creating +# a bash array from $(SRCS), then instead of using $(SRCS) in the command, it +# uses only the first element of that array. +def _make_isa_tool_invocation_command(num_srcs, prefix, isa_name): + cmd = "ARR=($(SRCS)); $(location //external:decoder_gen) " + + # Add the sources that are not in includes. + for i in range(0, num_srcs): + cmd += "$${ARR[" + str(i) + "]} " + cmd += "--prefix " + prefix + " --output_dir $(@D)" if isa_name != "": cmd += " --isa_name " + isa_name + return cmd # Create the bin_format_gen command with arguments, Since the srcs had all the files, including # those that will be included, the command includes creating a bash array from $(SRCS), then # instead of using $(SRCS) in the command, it uses only the first element of that array. -def _make_bin_tool_invocation_command(prefix, decoder_name): - cmd = "ARR=($(SRCS)); $(location //external:bin_format_gen) " + \ - "$${ARR[0]}" + \ - " --prefix " + prefix + \ - " --output_dir $(@D)" +def _make_bin_tool_invocation_command(num_srcs, prefix, decoder_name): + cmd = "ARR=($(SRCS)); $(location //external:bin_format_gen) " + + # Add the sources that are not in includes. + for i in range(0, num_srcs): + cmd += "$${ARR[" + str(i) + "]} " + cmd += " --prefix " + prefix + " --output_dir $(@D)" if decoder_name != "": cmd += " --decoder_name " + decoder_name return cmd
diff --git a/mpact/sim/decoder/test/BUILD b/mpact/sim/decoder/test/BUILD index ba3adad..91222dd 100644 --- a/mpact/sim/decoder/test/BUILD +++ b/mpact/sim/decoder/test/BUILD
@@ -82,6 +82,21 @@ ) mpact_cc_test( + name = "resource_test", + size = "small", + srcs = [ + "resource_test.cc", + ], + deps = [ + "//mpact/sim/decoder:isa_parser", + "@com_google_absl//absl/container:btree", + "@com_google_absl//absl/status:statusor", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + +mpact_cc_test( name = "template_expression_test", size = "small", srcs = [ @@ -137,6 +152,19 @@ isa_name = "Example", ) +mpact_isa_decoder( + name = "combined_isa", + srcs = [ + "testfiles/bundle_b.isa", + "testfiles/part1.isa", + ], + includes = [ + "//mpact/sim/decoder/test/testfiles/include:bundle_a.isa", + "//mpact/sim/decoder/test/testfiles/include:empty_include.isa", + ], + isa_name = "Example", +) + mpact_cc_library( name = "riscv32i", hdrs = ["testfiles/riscv32i.h"], @@ -155,10 +183,10 @@ deps = ["riscv32i"], ) -# The point of this test is to actually run the generator on the testfiles/example.isa -# grammar file, generate source files, and then compile the generated files and -# link into this test case. Provided that succeeds, this test case succeeds. See the -# :example_isa build rule for build options. +# The point of this test is to actually run the generator on the +# testfiles/example.isa grammar file, generate source files, and then compile +# the generated files and link into this test case. Provided that succeeds, +# this test case succeeds. See the :example_isa build rule for build options. mpact_cc_test( name = "example_decoder_test", size = "small", @@ -175,6 +203,26 @@ ], ) +# The point of this test is to actually run the generator on the +# testfiles/part1.isa grammar file, generate source files, and then compile +# the generated files and link into this test case. Provided that succeeds, +# this test case succeeds. See the :example_isa build rule for build options. +mpact_cc_test( + name = "combined_decoder_test", + size = "small", + srcs = [ + "combined_decoder_test.cc", + ], + deps = [ + ":combined_isa", + "//mpact/sim/generic:arch_state", + "//mpact/sim/generic:instruction", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + mpact_cc_test( name = "extract_bits_test", size = "small", @@ -233,3 +281,61 @@ "@com_google_googletest//:gtest_main", ], ) + +mpact_cc_test( + name = "bin_encoding_info_test", + size = "small", + srcs = [ + "bin_encoding_info_test.cc", + ], + deps = [ + "//mpact/sim/decoder:bin_format_visitor", + "//mpact/sim/decoder:decoder_error_listener", + "@com_google_absl//absl/container:btree", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + +mpact_cc_test( + name = "instruction_encoding_test", + size = "small", + srcs = [ + "instruction_encoding_test.cc", + ], + deps = [ + "//mpact/sim/decoder:bin_format_visitor", + "//mpact/sim/decoder:decoder_error_listener", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + +mpact_cc_test( + name = "bin_format_visitor_test", + size = "small", + srcs = [ + "bin_format_visitor_test.cc", + ], + data = [ + # Local include files. + "testfiles/empty_file.bin_fmt", + "testfiles/riscv32_top.bin_fmt", + "testfiles/riscv32g.bin_fmt", + "testfiles/riscv32c.bin_fmt", + ], + deps = [ + "//mpact/sim/decoder:bin_format_visitor", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/mpact/sim/decoder/test/bin_encoding_info_test.cc b/mpact/sim/decoder/test/bin_encoding_info_test.cc new file mode 100644 index 0000000..b555daa --- /dev/null +++ b/mpact/sim/decoder/test/bin_encoding_info_test.cc
@@ -0,0 +1,148 @@ +#include "mpact/sim/decoder/bin_encoding_info.h" + +#include "absl/log/check.h" +#include "absl/status/status.h" +#include "googletest/include/gtest/gtest.h" +#include "mpact/sim/decoder/decoder_error_listener.h" +#include "mpact/sim/decoder/format.h" +#include "mpact/sim/decoder/instruction_group.h" + +// This file provides for testing of the BinEncodingInfo class interfaces, with +// the exception of PropagateExtractors, as that cannot be tested in isolation. + +namespace { + +using ::mpact::sim::decoder::DecoderErrorListener; +using ::mpact::sim::decoder::bin_format::BinEncodingInfo; + +constexpr char kOpcodeEnumName[] = "OpcodeEnumName"; +constexpr char kIncludeFile0[] = "IncludeFile0"; +constexpr char kIncludeFile1[] = "IncludeFile1"; +constexpr char kIncludeFile2[] = "IncludeFile2"; +constexpr const char *kIncludeFiles[] = {kIncludeFile0, kIncludeFile1, + kIncludeFile2}; +constexpr char kFormat0[] = "Format0"; +constexpr char kFormat1[] = "Format1"; +constexpr char kFormat2[] = "Format2"; +constexpr char kGroup0[] = "Group0"; +constexpr char kBinDecoder[] = "BinDecoder"; +constexpr int kFormatWidth32 = 32; +constexpr int kFormatWidth16 = 16; + +class BinEncodingInfoTest : public ::testing::Test { + protected: + BinEncodingInfoTest() { + error_listener_ = new DecoderErrorListener(); + bin_encoding_info_ = new BinEncodingInfo(kOpcodeEnumName, error_listener_); + } + ~BinEncodingInfoTest() override { + for (auto &[name, instruction_group_ptr] : + bin_encoding_info_->instruction_group_map()) { + delete instruction_group_ptr; + } + delete bin_encoding_info_; + delete error_listener_; + } + + DecoderErrorListener *error_listener_ = nullptr; + BinEncodingInfo *bin_encoding_info_ = nullptr; +}; + +// Test for proper initialization of object. +TEST_F(BinEncodingInfoTest, Constructed) { + EXPECT_FALSE(error_listener_->HasError()); + EXPECT_TRUE(bin_encoding_info_->format_map().empty()); + EXPECT_TRUE(bin_encoding_info_->include_files().empty()); + EXPECT_TRUE(bin_encoding_info_->instruction_group_map().empty()); + EXPECT_EQ(bin_encoding_info_->decoder(), nullptr); + EXPECT_EQ(bin_encoding_info_->error_listener(), error_listener_); +} + +// Test that include files are properly added/kept. +TEST_F(BinEncodingInfoTest, AddIncludeFile) { + EXPECT_TRUE(bin_encoding_info_->include_files().empty()); + for (auto const &include_file : kIncludeFiles) { + bin_encoding_info_->AddIncludeFile(include_file); + } + EXPECT_FALSE(bin_encoding_info_->include_files().empty()); + for (auto const &include_file : kIncludeFiles) { + EXPECT_TRUE(bin_encoding_info_->include_files().contains(include_file)); + } + EXPECT_FALSE(bin_encoding_info_->include_files().contains("NoIncludeFile")); +} + +// Test that formats are properly added/kept. +TEST_F(BinEncodingInfoTest, AddFormat) { + // Adding a new format should work. + auto res0 = bin_encoding_info_->AddFormat(kFormat0, kFormatWidth32); + EXPECT_TRUE(res0.status().ok()); + auto *format = res0.value(); + EXPECT_EQ(format->name(), kFormat0); + EXPECT_EQ(format->declared_width(), kFormatWidth32); + + // Make sure we get the format back when calling GetFormat. + auto *get_format = bin_encoding_info_->GetFormat(kFormat0); + EXPECT_EQ(get_format, format); + // Adding the same format again should fail. + res0 = bin_encoding_info_->AddFormat(kFormat0, kFormatWidth32); + EXPECT_EQ(absl::StatusCode::kAlreadyExists, res0.status().code()); + + // Add a different format should work. + auto res1 = bin_encoding_info_->AddFormat(kFormat1, kFormatWidth16); + EXPECT_TRUE(res1.status().ok()); + format = res1.value(); + EXPECT_EQ(format->name(), kFormat1); + EXPECT_EQ(format->declared_width(), kFormatWidth16); + + // Add format with parent. + auto res2 = bin_encoding_info_->AddFormat(kFormat2, kFormatWidth32, kFormat0); + EXPECT_TRUE(res2.status().ok()); + format = res2.value(); + EXPECT_EQ(format->name(), kFormat2); + EXPECT_EQ(format->declared_width(), kFormatWidth32); + + // Can't add the same format twice. + res2 = bin_encoding_info_->AddFormat(kFormat2, kFormatWidth32, kFormat0); + EXPECT_EQ(absl::StatusCode::kAlreadyExists, res2.status().code()); + + // Format map. Verify that the formats are in the map. + auto &format_map = bin_encoding_info_->format_map(); + EXPECT_EQ(format_map.size(), 3); + EXPECT_NE(format_map.find(kFormat0), format_map.end()); + EXPECT_NE(format_map.find(kFormat1), format_map.end()); + EXPECT_NE(format_map.find(kFormat2), format_map.end()); +} + +// Instruction groups. +TEST_F(BinEncodingInfoTest, AddInstructionGroup) { + EXPECT_TRUE(bin_encoding_info_->instruction_group_map().empty()); + + // Add an instruction group. + auto res = bin_encoding_info_->AddInstructionGroup(kGroup0, kFormatWidth32, + kFormat0); + EXPECT_TRUE(res.status().ok()); + auto *instruction_group = res.value(); + EXPECT_EQ(instruction_group->name(), kGroup0); + EXPECT_EQ(instruction_group->width(), kFormatWidth32); + EXPECT_EQ(instruction_group->format_name(), kFormat0); + EXPECT_EQ(instruction_group->opcode_enum(), kOpcodeEnumName); + + // Adding it a second time doesn't work. + res = bin_encoding_info_->AddInstructionGroup(kGroup0, kFormatWidth32, + kFormat0); + EXPECT_EQ(absl::StatusCode::kAlreadyExists, res.status().code()); +} + +// Bin decoder. +TEST_F(BinEncodingInfoTest, AddDecoder) { + // Add BinDecoder. + auto *bin_dec = bin_encoding_info_->AddBinDecoder(kBinDecoder); + EXPECT_NE(bin_dec, nullptr); + EXPECT_FALSE(error_listener_->HasError()); + // Try adding it again. + bin_dec = bin_encoding_info_->AddBinDecoder(kBinDecoder); + EXPECT_EQ(bin_dec, nullptr); + EXPECT_TRUE(error_listener_->HasError()); +} + +} // namespace
diff --git a/mpact/sim/decoder/test/bin_format_visitor_test.cc b/mpact/sim/decoder/test/bin_format_visitor_test.cc new file mode 100644 index 0000000..7cee644 --- /dev/null +++ b/mpact/sim/decoder/test/bin_format_visitor_test.cc
@@ -0,0 +1,95 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mpact/sim/decoder/bin_format_visitor.h" + +#include <string> + +#include "absl/strings/str_cat.h" +#include "googletest/include/gtest/gtest.h" + +namespace { + +constexpr char kTestUndeclaredOutputsDir[] = "TEST_UNDECLARED_OUTPUTS_DIR"; + +constexpr char kEmptyDecoderName[] = "Empty"; +constexpr char kEmptyBaseName[] = "empty_file"; +constexpr char kRiscVDecoderName[] = "RiscV32G"; +constexpr char kRiscVBaseName[] = "riscv32"; +constexpr char kRiscVTopName[] = "riscv32_top.bin_fmt"; +constexpr char kRiscV32GName[] = "riscv32g.bin_fmt"; +constexpr char kRiscV32CName[] = "riscv32c.bin_fmt"; + +using ::mpact::sim::decoder::bin_format::BinFormatVisitor; + +// The depot path to the test directory. +constexpr char kDepotPath[] = "mpact/sim/decoder/test/"; + +std::string OutputDir() { return "./"; } + +static bool FileExists(const std::string &name) { + std::ifstream file(name); + return file.good(); +} + +class BinFormatParserTest : public testing::Test { + protected: + BinFormatParserTest() {} + + std::vector<std::string> paths_; +}; + +TEST_F(BinFormatParserTest, NullFileParsing) { + // Set up input and output file paths. + std::vector<std::string> input_files = { + absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".bin_fmt")}; + ASSERT_TRUE(FileExists(input_files[0])); + std::string output_dir = OutputDir(); + + BinFormatVisitor visitor; + // Parse and process the input file. + EXPECT_FALSE(visitor + .Process(input_files, kEmptyBaseName, kEmptyDecoderName, + paths_, output_dir) + .ok()); +} + +TEST_F(BinFormatParserTest, BasicParsing) { + // Make sure the visitor can read and parse the input file. + // Set up input and output file paths. + std::vector<std::string> input_files = { + absl::StrCat(kDepotPath, "testfiles/", kRiscVTopName), + absl::StrCat(kDepotPath, "testfiles/", kRiscV32GName), + absl::StrCat(kDepotPath, "testfiles/", kRiscV32CName), + }; + ASSERT_TRUE(FileExists(input_files[0])); + ASSERT_TRUE(FileExists(input_files[1])); + ASSERT_TRUE(FileExists(input_files[2])); + std::string output_dir = getenv(kTestUndeclaredOutputsDir); + + BinFormatVisitor visitor; + // Parse and process the input file. + EXPECT_TRUE(visitor + .Process(input_files, kRiscVDecoderName, kRiscVBaseName, + paths_, output_dir) + .ok()); + // Verify that the decoder files _decoder.{.h,.cc} files were generated. + EXPECT_TRUE(FileExists( + absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.h"))); + + EXPECT_TRUE(FileExists( + absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.cc"))); +} + +} // namespace
diff --git a/mpact/sim/decoder/test/combined_decoder_test.cc b/mpact/sim/decoder/test/combined_decoder_test.cc new file mode 100644 index 0000000..6c20fd8 --- /dev/null +++ b/mpact/sim/decoder/test/combined_decoder_test.cc
@@ -0,0 +1,25 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "googlemock/include/gmock/gmock.h" +#include "googletest/include/gtest/gtest.h" + +namespace { + +// Trivial case that succeeds as long as the generated code compiles, including +// base classes, as this test file build dependency includes the .isa grammar +// file. See the BUILD file for details. +TEST(CombinedDecoderTest, Trivial) { EXPECT_TRUE(true); } + +} // namespace
diff --git a/mpact/sim/decoder/test/instruction_encoding_test.cc b/mpact/sim/decoder/test/instruction_encoding_test.cc new file mode 100644 index 0000000..228b68a --- /dev/null +++ b/mpact/sim/decoder/test/instruction_encoding_test.cc
@@ -0,0 +1,220 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mpact/sim/decoder/instruction_encoding.h" + +#include <string> + +#include "absl/status/status.h" +#include "googlemock/include/gmock/gmock.h" +#include "googletest/include/gtest/gtest.h" +#include "mpact/sim/decoder/bin_encoding_info.h" +#include "mpact/sim/decoder/bin_format_visitor.h" +#include "mpact/sim/decoder/decoder_error_listener.h" +#include "mpact/sim/decoder/format.h" + +namespace { + +// This file contains the unit tests for the InstructionEncoding class. + +using ::mpact::sim::decoder::DecoderErrorListener; +using ::mpact::sim::decoder::bin_format::BinEncodingInfo; +using ::mpact::sim::decoder::bin_format::ConstraintType; +using ::mpact::sim::decoder::bin_format::Format; +using ::mpact::sim::decoder::bin_format::InstructionEncoding; + +constexpr char kITypeEncodingName[] = "i_test_encoding"; + +constexpr int kFunc3Value = 0b101; +constexpr int kFunc3Mask = 0b111'00000'000'0000; + +class InstructionEncodingTest : public ::testing::Test { + protected: + InstructionEncodingTest() { + error_listener_ = new DecoderErrorListener(); + encoding_info_ = new BinEncodingInfo("OpcodeEnumName", error_listener_); + i_type_fmt_ = new Format("IType", 32, encoding_info_); + (void)i_type_fmt_->AddField("imm12", /*is_signed*/ true, 12); + (void)i_type_fmt_->AddField("rs1", /*is_signed*/ false, 5); + (void)i_type_fmt_->AddField("func3", /*is_signed*/ false, 3); + (void)i_type_fmt_->AddField("rd", /*is_signed*/ false, 5); + (void)i_type_fmt_->AddField("opcode", /*is_signed*/ false, 7); + (void)i_type_fmt_->AddFieldOverlay("uspecial", /*is_signed*/ false, 10); + auto *overlay = i_type_fmt_->GetOverlay("uspecial"); + (void)overlay->AddFieldReference("rs1"); + (void)overlay->AddFieldReference("rd"); + (void)i_type_fmt_->AddFieldOverlay("sspecial", /*is_signed*/ true, 10); + overlay = i_type_fmt_->GetOverlay("sspecial"); + (void)overlay->AddFieldReference("rs1"); + (void)overlay->AddFieldReference("rd"); + (void)i_type_fmt_->ComputeAndCheckFormatWidth(); + i_type_ = new InstructionEncoding(kITypeEncodingName, i_type_fmt_); + } + + ~InstructionEncodingTest() override { + delete error_listener_; + delete i_type_fmt_; + delete encoding_info_; + delete i_type_; + } + + DecoderErrorListener *error_listener_; + BinEncodingInfo *encoding_info_; + Format *i_type_fmt_; + InstructionEncoding *i_type_; +}; + +TEST_F(InstructionEncodingTest, Basic) { + EXPECT_EQ(i_type_->name(), kITypeEncodingName); + EXPECT_EQ(i_type_->GetValue(), 0); + EXPECT_EQ(i_type_->GetMask(), 0); + EXPECT_EQ(i_type_->GetCombinedMask(), 0); + EXPECT_TRUE(i_type_->equal_constraints().empty()); + EXPECT_TRUE(i_type_->equal_extracted_constraints().empty()); + EXPECT_TRUE(i_type_->other_constraints().empty()); +} + +TEST_F(InstructionEncodingTest, BadConstraintName) { + // Wrong field names. + auto status = i_type_->AddEqualConstraint("NotAName", 0); + // Verify error message, and that nothing has changed. + EXPECT_TRUE(absl::IsNotFound(status)); + EXPECT_EQ(i_type_->GetValue(), 0); + EXPECT_EQ(i_type_->GetMask(), 0); + EXPECT_EQ(i_type_->GetCombinedMask(), 0); + EXPECT_TRUE(i_type_->equal_constraints().empty()); + EXPECT_TRUE(i_type_->equal_extracted_constraints().empty()); + EXPECT_TRUE(i_type_->other_constraints().empty()); + + // Check for other constraint type. + status = i_type_->AddOtherConstraint(ConstraintType::kNe, "NotAName", 0); + EXPECT_TRUE(absl::IsNotFound(status)); + EXPECT_EQ(i_type_->GetValue(), 0); + EXPECT_EQ(i_type_->GetMask(), 0); + EXPECT_EQ(i_type_->GetCombinedMask(), 0); + EXPECT_TRUE(i_type_->equal_constraints().empty()); + EXPECT_TRUE(i_type_->equal_extracted_constraints().empty()); + EXPECT_TRUE(i_type_->other_constraints().empty()); +} + +TEST_F(InstructionEncodingTest, OutOfRangeUnsignedField) { + // Correct field name, but value out of range. + auto status = i_type_->AddEqualConstraint("func3", 8); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddEqualConstraint("func3", -5); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kLt, "func3", 8); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kLe, "func3", -5); + EXPECT_TRUE(absl::IsOutOfRange(status)); +} + +TEST_F(InstructionEncodingTest, OutOfRangeSignedField) { + auto status = i_type_->AddEqualConstraint("imm12", 1 << 11); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddEqualConstraint("imm12", -(1 << 11) - 1); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kNe, "imm12", 1 << 11); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = + i_type_->AddOtherConstraint(ConstraintType::kNe, "imm12", -(1 << 11) - 1); + EXPECT_TRUE(absl::IsOutOfRange(status)); +} + +TEST_F(InstructionEncodingTest, OutOfRangeUnsignedOverlay) { + // Correct field name, but value out of range. + auto status = i_type_->AddEqualConstraint("uspecial", 1024); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddEqualConstraint("uspecial", -5); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kNe, "uspecial", 1024); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kNe, "uspecial", -5); + EXPECT_TRUE(absl::IsOutOfRange(status)); +} + +TEST_F(InstructionEncodingTest, OutOfRangeSignedOverlay) { + auto status = i_type_->AddEqualConstraint("sspecial", 1 << 10); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddEqualConstraint("sspecial", -(1 << 10) - 1); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = + i_type_->AddOtherConstraint(ConstraintType::kNe, "sspecial", 1 << 10); + EXPECT_TRUE(absl::IsOutOfRange(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kNe, "sspecial", + -(1 << 10) - 1); + EXPECT_TRUE(absl::IsOutOfRange(status)); +} + +TEST_F(InstructionEncodingTest, IllegalSignedConstraints) { + // Field. + auto status = i_type_->AddOtherConstraint(ConstraintType::kLt, "imm12", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kLe, "imm12", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kGt, "imm12", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kGe, "imm12", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + // Overlay. + status = i_type_->AddOtherConstraint(ConstraintType::kLt, "sspecial", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kLe, "sspecial", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kGt, "sspecial", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); + status = i_type_->AddOtherConstraint(ConstraintType::kGe, "sspecial", 5); + EXPECT_TRUE(absl::IsInvalidArgument(status)); +} + +TEST_F(InstructionEncodingTest, AddEqualUnsignedConstraint) { + auto status = i_type_->AddEqualConstraint("func3", kFunc3Value); + EXPECT_OK(status); + EXPECT_EQ(i_type_->GetValue(), kFunc3Value << 12); + EXPECT_EQ(i_type_->GetMask(), kFunc3Mask); + EXPECT_EQ(i_type_->GetCombinedMask(), kFunc3Mask); + EXPECT_EQ(i_type_->equal_constraints().size(), 1); + auto *constraint = i_type_->equal_constraints()[0]; + EXPECT_EQ(constraint->type, ConstraintType::kEq); + EXPECT_EQ(constraint->field, i_type_fmt_->GetField("func3")); + EXPECT_EQ(constraint->value, kFunc3Value); + EXPECT_EQ(constraint->overlay, nullptr); + EXPECT_EQ(constraint->can_ignore, false); + + // Other constraints are unaffected. + EXPECT_TRUE(i_type_->equal_extracted_constraints().empty()); + EXPECT_TRUE(i_type_->other_constraints().empty()); +} + +TEST_F(InstructionEncodingTest, AddOtherConstraints) { + auto status = + i_type_->AddOtherConstraint(ConstraintType::kGe, "func3", kFunc3Value); + EXPECT_OK(status); + EXPECT_EQ(i_type_->GetValue(), 0); + EXPECT_EQ(i_type_->GetMask(), 0); + EXPECT_EQ(i_type_->GetCombinedMask(), kFunc3Mask); + EXPECT_EQ(i_type_->other_constraints().size(), 1); + auto *constraint = i_type_->other_constraints()[0]; + EXPECT_EQ(constraint->type, ConstraintType::kGe); + EXPECT_EQ(constraint->field, i_type_fmt_->GetField("func3")); + EXPECT_EQ(constraint->value, kFunc3Value); + EXPECT_EQ(constraint->overlay, nullptr); + EXPECT_EQ(constraint->can_ignore, false); + + // Other constraints are unaffected. + EXPECT_TRUE(i_type_->equal_extracted_constraints().empty()); + EXPECT_TRUE(i_type_->equal_constraints().empty()); +} + +} // namespace
diff --git a/mpact/sim/decoder/test/instruction_set_visitor_test.cc b/mpact/sim/decoder/test/instruction_set_visitor_test.cc index f7fe3d4..65a32f3 100644 --- a/mpact/sim/decoder/test/instruction_set_visitor_test.cc +++ b/mpact/sim/decoder/test/instruction_set_visitor_test.cc
@@ -59,15 +59,15 @@ // An empty file should fail. TEST_F(InstructionSetParserTest, NullFileParsing) { // Set up input and output file paths. - const std::string input_file = - absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".isa"); - ASSERT_TRUE(FileExists(input_file)); + std::vector<std::string> input_files = { + absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".isa")}; + ASSERT_TRUE(FileExists(input_files[0])); std::string output_dir = OutputDir(); InstructionSetVisitor visitor; // Parse and process the input file. EXPECT_FALSE(visitor - .Process(input_file, kEmptyBaseName, kEmptyIsaName, paths_, + .Process(input_files, kEmptyBaseName, kEmptyIsaName, paths_, output_dir) .ok()); } @@ -75,30 +75,30 @@ // Make sure recursive includes cause a failure. TEST_F(InstructionSetParserTest, RecursiveInclude) { // Set up input and output file paths. - const std::string input_file = - absl::StrCat(kDepotPath, "testfiles/", kRecursiveExampleBaseName, ".isa"); - ASSERT_TRUE(FileExists(input_file)); + std::vector<std::string> input_files = {absl::StrCat( + kDepotPath, "testfiles/", kRecursiveExampleBaseName, ".isa")}; + ASSERT_TRUE(FileExists(input_files[0])); std::string output_dir = OutputDir(); InstructionSetVisitor visitor; EXPECT_FALSE(visitor - .Process(input_file, kRecursiveExampleBaseName, + .Process(input_files, kRecursiveExampleBaseName, kExampleIsaName, paths_, output_dir) .ok()); -} +} // namespace // Make sure the visitor can read and parse the input file. TEST_F(InstructionSetParserTest, BasicParsing) { // Set up input and output file paths. - const std::string input_file = - absl::StrCat(kDepotPath, "testfiles/", kExampleBaseName, ".isa"); - ASSERT_TRUE(FileExists(input_file)); + std::vector<std::string> input_files = { + absl::StrCat(kDepotPath, "testfiles/", kExampleBaseName, ".isa")}; + ASSERT_TRUE(FileExists(input_files[0])); std::string output_dir = getenv(kTestUndeclaredOutputsDir); InstructionSetVisitor visitor; // Parse and process the input file. EXPECT_TRUE(visitor - .Process(input_file, kExampleBaseName, kExampleIsaName, + .Process(input_files, kExampleBaseName, kExampleIsaName, paths_, output_dir) .ok()); // Verify that the decoder files _decoder.{.h,.cc} files were generated.
diff --git a/mpact/sim/decoder/test/resource_test.cc b/mpact/sim/decoder/test/resource_test.cc new file mode 100644 index 0000000..a8c8159 --- /dev/null +++ b/mpact/sim/decoder/test/resource_test.cc
@@ -0,0 +1,65 @@ +#include "mpact/sim/decoder/resource.h" + +#include "absl/status/statusor.h" +#include "googlemock/include/gmock/gmock.h" +#include "googletest/include/gtest/gtest.h" + +namespace { + +// This file contains tests for Resource and ResourceFactory. + +using ::mpact::sim::machine_description::instruction_set::ResourceFactory; + +constexpr char kResource1PascalName[] = "Resource1Name"; +constexpr char kResource1Name[] = "resource_1_name"; +constexpr char kResource2Name[] = "resource_2_name"; + +class ResourceTest : public ::testing::Test { + protected: + ResourceTest() {} + ~ResourceTest() override {} + + ResourceFactory factory_; +}; + +// Test that ResourceFactory works as expected. +TEST_F(ResourceTest, Factory) { + auto result = factory_.CreateResource(kResource1Name); + EXPECT_TRUE(result.status().ok()); + auto result2 = factory_.CreateResource(kResource1Name); + EXPECT_TRUE(absl::IsAlreadyExists(result2.status())); + auto *resource1 = factory_.GetOrInsertResource(kResource1Name); + EXPECT_EQ(result.value(), resource1); + auto *resource2 = factory_.GetOrInsertResource(kResource2Name); + EXPECT_NE(resource2, nullptr); + EXPECT_NE(resource2, resource1); + auto result3 = factory_.CreateResource(kResource2Name); + EXPECT_TRUE(absl::IsAlreadyExists(result3.status())); + + EXPECT_EQ(factory_.resource_map().find(kResource1Name)->second, resource1); + EXPECT_EQ(factory_.resource_map().find(kResource2Name)->second, resource2); +} + +// Test initial state of a new resource. +TEST_F(ResourceTest, ResourceInitial) { + auto *resource = factory_.GetOrInsertResource(kResource1Name); + EXPECT_TRUE(resource->is_simple()); + EXPECT_FALSE(resource->is_multi_valued()); + EXPECT_EQ(resource->name(), kResource1Name); + EXPECT_EQ(resource->pascal_name(), kResource1PascalName); +} + +// Test resource setters. +TEST_F(ResourceTest, ResourceSetters) { + auto *resource = factory_.GetOrInsertResource(kResource1Name); + resource->set_is_simple(false); + resource->set_is_multi_valued(true); + EXPECT_FALSE(resource->is_simple()); + EXPECT_TRUE(resource->is_multi_valued()); + resource->set_is_simple(true); + resource->set_is_multi_valued(false); + EXPECT_TRUE(resource->is_simple()); + EXPECT_FALSE(resource->is_multi_valued()); +} + +} // namespace
diff --git a/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt b/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt
diff --git a/mpact/sim/decoder/test/testfiles/part1.isa b/mpact/sim/decoder/test/testfiles/part1.isa new file mode 100644 index 0000000..3bfc3f0 --- /dev/null +++ b/mpact/sim/decoder/test/testfiles/part1.isa
@@ -0,0 +1,109 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Define an isa with two bundles. +isa Example { + namespace sim::example::isa; + bundles { + bundle_a; + bundle_b; + } +} + +int const1 = 1; +int my_const = const1 + 2; + +// Try some include files. +#include "mpact/sim/decoder/test/testfiles/include/empty_include.isa" +#include "mpact/sim/decoder/test/testfiles/include/bundle_a.isa" + +template<int base, int mult> +slot a_side_ops { + int base_plus_1 = base + 1; + int mult_plus_2 = mult + my_const; + default attributes = { one, two = 3, three = base, four = base_plus_1, five = 0}; + opcodes { + vctsf{(pred : sy : dest(base_plus_1))}; + cvtfs{(pred : sy : dest(base + 1))}; + adds{(pred : sy, sx : dest(base))}, + attributes: {five = 1, six}; + addf{(pred : sy, sx : dest(base))}; + subf{(pred : sy, sx : dest(base))}; + mulf{(pred : sy, sx : dest(mult_plus_2))}; + mulu{(pred : sy, sx : dest(mult + 1))}; + } +} + +slot a_side { + opcodes { + nop{()}; + delay{(pred : const)}; + settag{()}; + fence{()}; + } +} + +slot a_side_0 : a_side, a_side_ops<2, 2> { + default opcode = + semfunc: "[](Instruction *) {}"; + opcodes { + settag = delete; + addf = delete; + subf = delete; + br_abs{()}; + br_rel{()}; + br_ind{()}; + call_abs{()}; + call_rel{()}; + call_ind{()}; + } +} + +slot a_side_1 : a_side, a_side_ops<1, 4> { + default opcode = + semfunc: "[](Instruction *) {}"; + opcodes { + ld{(pred : yop : ),(: : dest)}, + disasm:"%dest = sld %pred [smem:%yop]"; + ld_offset{(pred : xop, yop : dest(abs(-2)))}; + st{(pred)}; + mulf = delete; + mulu = delete; + } +} + +slot other { + default opcode = + semfunc: "[](Instruction *) {}"; +} + +slot b_side_alu[4] { + default opcode = + semfunc: "[](Instruction *) {}"; +} + +slot b_side_store { + default opcode = + semfunc: "[](Instruction *) {}"; +} + +slot b_side_load[2] { + default opcode = + semfunc: "[](Instruction *) {}"; +} + +slot b_side_load_2 { + default opcode = + semfunc: "[](Instruction *) {}"; +}
diff --git a/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt new file mode 100644 index 0000000..6eb694d --- /dev/null +++ b/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt
@@ -0,0 +1,24 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// RiscV 32 bit G instruction decoder. +decoder RiscV32G { + namespace mpact::sim::riscv::encoding; + opcode_enum = "isa32::OpcodeEnum"; + includes { + #include "third_party/mpact_riscv/riscv32g_decoder.h" + } + RiscVGInst32; + RiscVCInst16; +};
diff --git a/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt new file mode 100644 index 0000000..d7aef71 --- /dev/null +++ b/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt
@@ -0,0 +1,176 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Compact instruction formats. + +format Inst16Format[16] { + fields: + unsigned func3[3]; + unsigned bits[11]; + unsigned op[2]; +}; + +format CSS[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm6[6]; + unsigned rs2[5]; + unsigned op[2]; + overlays: + unsigned css_imm_w[8] = imm6[1..0], imm6[5..2], 0b00; + unsigned css_imm_d[9] = imm6[2..0], imm6[5..3], 0b000; +}; + +format CL[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm3[3]; + unsigned rs1p[3]; + unsigned imm2[2]; + unsigned rdp[3]; + unsigned op[2]; + overlays: + unsigned cl_rs1[5] = 0b01, rs1p; + unsigned cl_rd[5] = 0b01, rdp; + unsigned cl_imm_w[7] = imm2[0], imm3, imm2[1], 0b00; + unsigned cl_imm_d[8] = imm2, imm3, 0b000; +}; + +format CS[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm3[3]; + unsigned rs1p[3]; + unsigned imm2[2]; + unsigned rs2p[3]; + unsigned op[2]; + overlays: + unsigned cs_rs1[5] = 0b01, rs1p; + unsigned cs_rs2[5] = 0b01, rs2p; + unsigned cs_imm_w[7] = imm2[0], imm3, imm2[1], 0b00; + unsigned cs_imm_d[8] = imm2, imm3, 0b000; +}; + +format CJ[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm11[11]; + unsigned op[2]; + overlays: + signed jimm[12] = imm11[10, 6, 8..7, 4, 5, 0, 9, 3..1], 0b0; +}; + +format CR[16] : Inst16Format { + fields: + unsigned func4[4]; + unsigned rs1[5]; + unsigned rs2[5]; + unsigned op[2]; +}; + +format CB[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm3[3]; + unsigned rs1p[3]; + unsigned imm5[5]; + unsigned op[2]; + overlays: + unsigned func2[2] = [11, 10]; + unsigned func5[5] = [12..10, 6..5]; + unsigned shamt[6] = [12, 6..2]; + unsigned rs2p[3] = [4..2]; + unsigned rs2[5] = 0b10, [4..2]; + signed bimm[9] = imm3[2], imm5[4..3, 0], imm3[1..0], imm5[2..1], 0b0; +}; + +format CI[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm1[1]; + unsigned rs1[5]; + unsigned imm5[5]; + unsigned op[2]; + overlays: + unsigned rd[5] = rs1; + signed imm6[6] = imm1, imm5; + unsigned uimm6[6] = imm1, imm5; + signed imm18[18] = imm1, imm5, 0b0000'0000'0000; + signed ci_imm10[10] = imm1, imm5[2..1, 3, 0, 4], 0b0000; + unsigned ci_imm_w[8] = imm5[1..0], imm1, imm5[4..2], 0b00; + unsigned ci_imm_d[9] = imm5[2..0], imm1, imm5[4..3], 0b000; +}; + +format CIW[16] : Inst16Format { + fields: + unsigned func3[3]; + unsigned imm8[8]; + unsigned rdp[3]; + unsigned op[2]; + overlays: + unsigned rd[5] = 0b01, rdp; + unsigned ciw_imm10[10] = imm8[5..2, 7..6, 0, 1], 0b00; +}; + +format CA[16] : Inst16Format { + fields: + unsigned func6[6]; + unsigned rs1p[3]; + unsigned func2[2]; + unsigned fs2p[3]; + unsigned op[2]; + overlays: + unsigned rs1[5] = 0b01, rs1p; + unsigned rs2[5] = 0b01, fs2p; + unsigned rd[5] = 0b01, rs1p; +}; + +// Compact instruction encodings. +instruction group RiscVCInst16[16] : Inst16Format { + caddi4spn : CIW: func3 == 0b000, op == 0b00, imm8 != 0; + cfld : CL : func3 == 0b001, op == 0b00; + clw : CL : func3 == 0b010, op == 0b00; + cflw : CL : func3 == 0b011, op == 0b00; + cfsd : CS : func3 == 0b101, op == 0b00; + csw : CS : func3 == 0b110, op == 0b00; + cfsw : CS : func3 == 0b111, op == 0b00; + cnop : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 == 0, op == 0b01; + caddi : CI : func3 == 0b000, imm6 != 0, rd != 0, op == 0b01; + cjal : CJ : func3 == 0b001, op == 0b01; + cli : CI : func3 == 0b010, rd != 0, op == 0b01; + caddi16sp : CI : func3 == 0b011, rd == 2, op == 0b01; + clui : CI : func3 == 0b011, rd != 0, rd != 2, op == 0b01; + csrli : CB : func3 == 0b100, imm3 == 0b000, op == 0b01; + csrai : CB : func3 == 0b100, imm3 == 0b001, op == 0b01; + candi : CB : func3 == 0b100, func2 == 0b10, op == 0b01; + csub : CA : func6 == 0b100'011, func2 == 0b00, op == 0b01; + cxor : CA : func6 == 0b100'011, func2 == 0b01, op == 0b01; + cor : CA : func6 == 0b100'011, func2 == 0b10, op == 0b01; + cand : CA : func6 == 0b100'011, func2 == 0b11, op == 0b01; + cj : CJ : func3 == 0b101, op == 0b01; + cbeqz : CB : func3 == 0b110, op == 0b01; + cbnez : CB : func3 == 0b111, op == 0b01; + cslli : CI : func3 == 0b000, imm1 == 0, rs1 != 0, op == 0b10; + cfldsp : CI : func3 == 0b001, op == 0b10; + clwsp : CI : func3 == 0b010, rd != 0, op == 0b10; + cflwsp : CI : func3 == 0b011, op == 0b10; + cjr : CR : func4 == 0b1000, rs1 != 0, rs2 == 0, op == 0b10; + cmv : CR : func4 == 0b1000, rs1 != 0, rs2 != 0, op == 0b10; + cebreak : CR : func4 == 0b1001, rs1 == 0, rs2 == 0, op == 0b10; + cjalr : CR : func4 == 0b1001, rs1 != 0, rs2 == 0, op == 0b10; + cadd : CR : func4 == 0b1001, rs1 != 0, rs2 != 0, op == 0b10; + cfsdsp : CSS: func3 == 0b101, op == 0b10; + cswsp : CSS: func3 == 0b110, op == 0b10; + cfswsp : CSS: func3 == 0b111, op == 0b10; +};
diff --git a/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt new file mode 100644 index 0000000..b05875a --- /dev/null +++ b/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt
@@ -0,0 +1,268 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// RiscV 32 bit instructions. +format Inst32Format[32] { + fields: + unsigned bits[25]; + unsigned opcode[7]; +}; + +format RType[32] : Inst32Format { + fields: + unsigned func7[7]; + unsigned rs2[5]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned rd[5]; + unsigned opcode[7]; + overlays: + unsigned r_uimm5[5] = rs2; +}; + +format R4Type[32] : Inst32Format { + fields: + unsigned rs3[5]; + unsigned func2[2]; + unsigned rs2[5]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned rd[5]; + unsigned opcode[7]; +}; + +format IType[32] : Inst32Format { + fields: + signed imm12[12]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned rd[5]; + unsigned opcode[7]; + overlays: + unsigned u_imm12[12] = imm12; + unsigned i_uimm5[5] = rs1; +}; + +format SType[32] : Inst32Format { + fields: + unsigned imm7[7]; + unsigned rs2[5]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned imm5[5]; + unsigned opcode[7]; + overlays: + signed s_imm[12] = imm7, imm5; +}; + +format BType[32] : Inst32Format { + fields: + unsigned imm7[7]; + unsigned rs2[5]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned imm5[5]; + unsigned opcode[7]; + overlays: + signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0; +}; + +format UType[32] : Inst32Format { + fields: + unsigned imm20[20]; + unsigned rd[5]; + unsigned opcode[7]; + overlays: + unsigned u_imm[32] = imm20, 0b0000'0000'0000; +}; + +format JType[32] : Inst32Format { + fields: + unsigned imm20[20]; + unsigned rd[5]; + unsigned opcode[7]; + overlays: + signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0; +}; + +format Fence[32] : Inst32Format { + fields: + unsigned fm[4]; + unsigned pred[4]; + unsigned succ[4]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned rd[5]; + unsigned opcode[7]; +}; + +format AType[32] : Inst32Format { + fields: + unsigned func5[5]; + unsigned aq[1]; + unsigned rl[1]; + unsigned rs2[5]; + unsigned rs1[5]; + unsigned func3[3]; + unsigned rd[5]; + unsigned opcode[7]; +}; + +instruction group RiscVGInst32[32] : Inst32Format { + lui : UType : opcode == 0b011'0111; + auipc : UType : opcode == 0b001'0111; + jal : JType : rd != 0, opcode == 0b110'1111; + j : JType : rd == 0, opcode == 0b110'1111; + jalr : IType : rd != 0, func3 == 0b000, opcode == 0b110'0111; + jr : IType : rd == 0, func3 == 0b000, opcode == 0b110'0111; + beq : BType : func3 == 0b000, opcode == 0b110'0011; + bne : BType : func3 == 0b001, opcode == 0b110'0011; + blt : BType : func3 == 0b100, opcode == 0b110'0011; + bge : BType : func3 == 0b101, opcode == 0b110'0011; + bltu : BType : func3 == 0b110, opcode == 0b110'0011; + bgeu : BType : func3 == 0b111, opcode == 0b110'0011; + lb : BType : func3 == 0b000, opcode == 0b000'0011; + lh : BType : func3 == 0b001, opcode == 0b000'0011; + lw : BType : func3 == 0b010, opcode == 0b000'0011; + lbu : BType : func3 == 0b100, opcode == 0b000'0011; + lhu : BType : func3 == 0b101, opcode == 0b000'0011; + sb : SType : func3 == 0b000, opcode == 0b010'0011; + sh : SType : func3 == 0b001, opcode == 0b010'0011; + sw : SType : func3 == 0b010, opcode == 0b010'0011; + addi : IType : func3 == 0b000, opcode == 0b001'0011; + slti : IType : func3 == 0b010, opcode == 0b001'0011; + sltiu : IType : func3 == 0b011, opcode == 0b001'0011; + xori : IType : func3 == 0b100, opcode == 0b001'0011; + ori : IType : func3 == 0b110, opcode == 0b001'0011; + andi : IType : func3 == 0b111, opcode == 0b001'0011; + slli : RType : func7 == 0b000'0000, func3==0b001, opcode == 0b001'0011; + srli : RType : func7 == 0b000'0000, func3==0b101, opcode == 0b001'0011; + srai : RType : func7 == 0b010'0000, func3==0b101, opcode == 0b001'0011; + add : RType : func7 == 0b000'0000, func3==0b000, opcode == 0b011'0011; + sub : RType : func7 == 0b010'0000, func3==0b000, opcode == 0b011'0011; + sll : RType : func7 == 0b000'0000, func3==0b001, opcode == 0b011'0011; + slt : RType : func7 == 0b000'0000, func3==0b010, opcode == 0b011'0011; + sltu : RType : func7 == 0b000'0000, func3==0b011, opcode == 0b011'0011; + xor : RType : func7 == 0b000'0000, func3==0b100, opcode == 0b011'0011; + srl : RType : func7 == 0b000'0000, func3==0b101, opcode == 0b011'0011; + sra : RType : func7 == 0b010'0000, func3==0b101, opcode == 0b011'0011; + or : RType : func7 == 0b000'0000, func3==0b110, opcode == 0b011'0011; + and : RType : func7 == 0b000'0000, func3==0b111, opcode == 0b011'0011; + fence : Fence : func3 == 0b000, opcode == 0b000'1111; + ecall : Inst32Format : bits == 0b0000'0000'0000'00000'000'00000, opcode == 0b111'0011; + ebreak : Inst32Format : bits == 0b0000'0000'0001'00000'000'00000, opcode == 0b111'0011; + // RiscV32 Instruction fence. + fencei : IType : func3 == 001, opcode == 0b000'1111; + // RiscV32 multiply divide. + mul : RType : func7 == 0b000'0001, func3 == 0b000, opcode == 0b011'0011; + mulh : RType : func7 == 0b000'0001, func3 == 0b001, opcode == 0b011'0011; + mulhsu : RType : func7 == 0b000'0001, func3 == 0b010, opcode == 0b011'0011; + mulhu : RType : func7 == 0b000'0001, func3 == 0b011, opcode == 0b011'0011; + div : RType : func7 == 0b000'0001, func3 == 0b100, opcode == 0b011'0011; + divu : RType : func7 == 0b000'0001, func3 == 0b101, opcode == 0b011'0011; + rem : RType : func7 == 0b000'0001, func3 == 0b110, opcode == 0b011'0011; + remu : RType : func7 == 0b000'0001, func3 == 0b111, opcode == 0b011'0011; + // RiscV32 atomic instructions. + lrw : AType : func5 == 0b0'0010, rs2 == 0, func3 == 0b010, opcode == 0b010'1111; + scw : AType : func5 == 0b0'0011, func3 == 0b010, opcode == 0b010'1111; + amoswapw : AType : func5 == 0b0'0001, func3 == 0b010, opcode == 0b010'1111; + amoaddw : AType : func5 == 0b0'0000, func3 == 0b010, opcode == 0b010'1111; + amoxorw : AType : func5 == 0b0'0100, func3 == 0b010, opcode == 0b010'1111; + amoandw : AType : func5 == 0b0'1100, func3 == 0b010, opcode == 0b010'1111; + amoorw : AType : func5 == 0b0'1000, func3 == 0b010, opcode == 0b010'1111; + amominw : AType : func5 == 0b1'0000, func3 == 0b010, opcode == 0b010'1111; + amomaxw : AType : func5 == 0b1'0100, func3 == 0b010, opcode == 0b010'1111; + amominuw : AType : func5 == 0b1'1000, func3 == 0b010, opcode == 0b010'1111; + amomaxuw : AType : func5 == 0b1'1100, func3 == 0b010, opcode == 0b010'1111; + // RiscV32 single precision floating point instructions. + flw : IType : func3 == 0b010, opcode == 0b000'0111; + fsw : SType : func3 == 0b010, opcode == 0b010'0111; + fmadd_s : R4Type : func2 == 0b00, opcode == 0b100'0011; + fmsub_s : R4Type : func2 == 0b00, opcode == 0b100'0111; + fnmsub_s : R4Type : func2 == 0b00, opcode == 0b100'1011; + fnmadd_s : R4Type : func2 == 0b00, opcode == 0b100'1111; + fadd_s : RType : func7 == 0b000'0000, opcode == 0b101'0011; + fsub_s : RType : func7 == 0b000'0100, opcode == 0b101'0011; + fmul_s : RType : func7 == 0b000'1000, opcode == 0b101'0011; + fdiv_s : RType : func7 == 0b000'1100, opcode == 0b101'0011; + fsqrt_s : RType : func7 == 0b010'1100, rs2 == 0, opcode == 0b101'0011; + fsgnj_s : RType : func7 == 0b001'0000, func3 == 0b000, opcode == 0b101'0011; + fsgnjn_s : RType : func7 == 0b001'0000, func3 == 0b001, opcode == 0b101'0011; + fsgnjx_s : RType : func7 == 0b001'0000, func3 == 0b010, opcode == 0b101'0011; + fmin_s : RType : func7 == 0b001'0100, func3 == 0b000, opcode == 0b101'0011; + fmax_s : RType : func7 == 0b001'0100, func3 == 0b001, opcode == 0b101'0011; + fcvt_ws : RType : func7 == 0b110'0000, rs2 == 0, opcode == 0b101'0011; + fcvt_wus : RType : func7 == 0b110'0000, rs2 == 1, opcode == 0b101'0011; + fmv_xw : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011; + fcmpeq_s : RType : func7 == 0b101'0000, func3 == 0b010, opcode == 0b101'0011; + fcmplt_s : RType : func7 == 0b101'0000, func3 == 0b001, opcode == 0b101'0011; + fcmple_s : RType : func7 == 0b101'0000, func3 == 0b000, opcode == 0b101'0011; + fclass_s : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b001, opcode == 0b101'0011; + fcvt_sw : RType : func7 == 0b110'1000, rs2 == 0, opcode == 0b101'0011; + fcvt_swu : RType : func7 == 0b110'1000, rs2 == 1, opcode == 0b101'0011; + fmv_wx : RType : func7 == 0b111'1000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011; + // RiscV32 double precision floating point instructions. + fld : IType : func3 == 0b011, opcode == 0b000'0111; + fsd : SType : func3 == 0b011, opcode == 0b010'0111; + fmadd_d : R4Type : func2 == 0b01, opcode == 0b100'0011; + fmsub_d : R4Type : func2 == 0b01, opcode == 0b100'0111; + fnmsub_d : R4Type : func2 == 0b01, opcode == 0b100'1011; + fnmadd_d : R4Type : func2 == 0b01, opcode == 0b100'1111; + fadd_d : RType : func7 == 0b000'0001, opcode == 0b101'0011; + fsub_d : RType : func7 == 0b000'0101, opcode == 0b101'0011; + fmul_d : RType : func7 == 0b000'1001, opcode == 0b101'0011; + fdiv_d : RType : func7 == 0b000'1101, opcode == 0b101'0011; + fsqrt_d : RType : func7 == 0b010'1101, rs2 == 0, opcode == 0b101'0011; + fsgnj_d : RType : func7 == 0b001'0001, func3 == 0b000, opcode == 0b101'0011; + fsgnjn_d : RType : func7 == 0b001'0001, func3 == 0b001, opcode == 0b101'0011; + fsgnjx_d : RType : func7 == 0b001'0001, func3 == 0b010, opcode == 0b101'0011; + fmin_d : RType : func7 == 0b001'0101, func3 == 0b000, opcode == 0b101'0011; + fmax_d : RType : func7 == 0b001'0101, func3 == 0b001, opcode == 0b101'0011; + fcvt_sd : RType : func7 == 0b010'0000, rs2 == 1, opcode == 0b101'0011; + fcvt_ds : RType : func7 == 0b010'0001, rs2 == 0, opcode == 0b101'0011; + fcmpeq_d : RType : func7 == 0b101'0001, func3 == 0b010, opcode == 0b101'0011; + fcmplt_d : RType : func7 == 0b101'0001, func3 == 0b001, opcode == 0b101'0011; + fcmple_d : RType : func7 == 0b101'0001, func3 == 0b000, opcode == 0b101'0011; + fclass_d : RType : func7 == 0b111'0001, rs2 == 0, func3 == 0b001, opcode == 0b101'0011; + fcvt_wd : RType : func7 == 0b110'0001, rs2 == 0, opcode == 0b101'0011; + fcvt_wud : RType : func7 == 0b110'0001, rs2 == 1, opcode == 0b101'0011; + fcvt_dw : RType : func7 == 0b110'1001, rs2 == 0, opcode == 0b101'0011; + fcvt_dwu : RType : func7 == 0b110'1001, rs2 == 1, opcode == 0b101'0011; + // RiscV32 CSR manipulation instructions. + csrrw : IType : func3 == 0b001, rd != 0, opcode == 0b111'0011; + csrrs : IType : func3 == 0b010, rs1 != 0, rd != 0, opcode == 0b111'0011; + csrrc : IType : func3 == 0b011, rs1 != 0, rd != 0, opcode == 0b111'0011; + csrrs_nr : IType : func3 == 0b010, rs1 != 0, rd == 0, opcode == 0b111'0011; + csrrc_nr : IType : func3 == 0b011, rs1 != 0, rd == 0, opcode == 0b111'0011; + csrrw_nr : IType : func3 == 0b001, rd == 0, opcode == 0b111'0011; + csrrs_nw : IType : func3 == 0b010, rs1 == 0, opcode == 0b111'0011; + csrrc_nw : IType : func3 == 0b011, rs1 == 0, opcode == 0b111'0011; + csrrwi : IType : func3 == 0b101, rd != 0, opcode == 0b111'0011; + csrrsi : IType : func3 == 0b110, rs1 != 0, rd != 0, opcode == 0b111'0011; + csrrci : IType : func3 == 0b111, rs1 != 0, rd != 0, opcode == 0b111'0011; + csrrsi_nr: IType : func3 == 0b110, rs1 != 0, rd == 0, opcode == 0b111'0011; + csrrci_nr: IType : func3 == 0b111, rs1 != 0, rd == 0, opcode == 0b111'0011; + csrrwi_nr: IType : func3 == 0b101, rd == 0, opcode == 0b111'0011; + csrrsi_nw: IType : func3 == 0b110, rs1 == 0, opcode == 0b111'0011; + csrrci_nw: IType : func3 == 0b111, rs1 == 0, opcode == 0b111'0011; + // RiscV32 Privileged instructions. + uret : Inst32Format : bits == 0b000'0000'00010'00000'000'00000, opcode == 0b111'0011; + sret : Inst32Format : bits == 0b000'1000'00010'00000'000'00000, opcode == 0b111'0011; + mret : Inst32Format : bits == 0b001'1000'00010'00000'000'00000, opcode == 0b111'0011; + wfi : Inst32Format : bits == 0b000'1000'00101'00000'000'00000, opcode == 0b111'0011; + sfence_vma_zz : RType : func7 == 0b000'1001, rs2 == 0, rs1 == 0, func3 == 0, rd == 0, opcode == 0b111'0011; + sfence_vma_zn : RType : func7 == 0b000'1001, rs2 != 0, rs1 == 0, func3 == 0, rd == 0, opcode == 0b111'0011; + sfence_vma_nz : RType : func7 == 0b000'1001, rs2 == 0, rs1 != 0, func3 == 0, rd == 0, opcode == 0b111'0011; + sfence_vma_nn : RType : func7 == 0b000'1001, rs2 != 0, rs1 != 0, func3 == 0, rd == 0, opcode == 0b111'0011; +};