| // 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_group.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "absl/strings/str_cat.h" |
| #include "mpact/sim/decoder/bin_encoding_info.h" |
| #include "mpact/sim/decoder/decoder_error_listener.h" |
| #include "mpact/sim/decoder/extract.h" |
| #include "mpact/sim/decoder/instruction_encoding.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace decoder { |
| namespace bin_format { |
| |
| InstructionGroup::InstructionGroup(std::string name, int width, |
| std::string format_name, |
| std::string opcode_enum, |
| BinEncodingInfo *encoding_info) |
| : name_(name), |
| width_(width), |
| format_name_(format_name), |
| opcode_enum_(opcode_enum), |
| encoding_info_(encoding_info) { |
| format_ = encoding_info->GetFormat(format_name); |
| if (format_ == nullptr) { |
| encoding_info_->error_listener()->semanticError( |
| nullptr, absl::StrCat("Undefined format '", format_name, |
| "' used by instruction group '", name, "'")); |
| } |
| } |
| |
| InstructionGroup::~InstructionGroup() { |
| for (auto *enc : encoding_vec_) { |
| delete enc; |
| } |
| encoding_map_.clear(); |
| encoding_vec_.clear(); |
| } |
| |
| // Add an instruction encoding into the current group. Check that the format |
| // has the correct width, and that the format the encoding is defined in, or |
| // derives from the format associated with the instruction group. |
| InstructionEncoding *InstructionGroup::AddInstructionEncoding(std::string name, |
| Format *format) { |
| if ((format_ == nullptr) || (!format->IsDerivedFrom(format_))) { |
| encoding_info_->error_listener()->semanticError( |
| nullptr, absl::StrCat("Format '", format->name(), |
| "' used by instruction encoding '", name, |
| "' is not derived from '", format_name_, "'")); |
| return nullptr; |
| } |
| if (format->declared_width() != width_) { |
| encoding_info_->error_listener()->semanticError( |
| nullptr, absl::StrCat("Format '", format->name(), |
| "' used by instruction encoding '", name, |
| "' has width diffrerent from encoding group '", |
| name_, "'")); |
| return nullptr; |
| } |
| if (encoding_name_set_.contains(name)) { |
| encoding_info_->error_listener()->semanticError( |
| nullptr, absl::StrCat("Duplicate encoding name '", name, "' in group '", |
| this->name(), "' - ignored.")); |
| return nullptr; |
| } |
| encoding_name_set_.insert(name); |
| auto *encoding = new InstructionEncoding(name, format); |
| encoding_vec_.push_back(encoding); |
| return encoding; |
| } |
| |
| void InstructionGroup::AddInstructionEncoding(InstructionEncoding *encoding) { |
| if (encoding_name_set_.contains(encoding->name())) { |
| encoding_info_->error_listener()->semanticError( |
| nullptr, absl::StrCat("Duplicate encoding name '", encoding->name(), |
| "' in group '", name(), "' - ignored.")); |
| return; |
| } |
| encoding_name_set_.insert(encoding->name()); |
| encoding_vec_.push_back(encoding); |
| } |
| |
| void InstructionGroup::ProcessEncodings() { |
| if (encoding_vec_.empty()) { |
| encoding_info_->error_listener()->semanticWarning( |
| nullptr, |
| absl::StrCat("No encodings in instruction group: '", name(), "'")); |
| return; |
| } |
| // Insert the encodings into a map based on the mask value - grouping |
| // instructions with the same mask. |
| for (auto *enc : encoding_vec_) { |
| encoding_map_.insert(std::make_pair(enc->GetMask(), enc)); |
| } |
| encoding_group_vec_.push_back(new EncodingGroup(this, 0)); |
| for (auto &[unused, enc_ptr] : encoding_map_) { |
| bool is_added = false; |
| for (auto *group : encoding_group_vec_) { |
| if (group->CanAddEncoding(enc_ptr)) { |
| group->AddEncoding(enc_ptr); |
| is_added = true; |
| break; |
| } |
| } |
| if (!is_added) { |
| encoding_group_vec_.push_back(new EncodingGroup(this, 0)); |
| encoding_group_vec_.back()->AddEncoding(enc_ptr); |
| is_added = true; |
| } |
| } |
| for (auto *grp : encoding_group_vec_) { |
| grp->AddSubGroups(); |
| } |
| } |
| |
| // Check for encoding errors. |
| void InstructionGroup::CheckEncodings() { |
| for (auto *enc_grp : encoding_group_vec_) { |
| enc_grp->CheckEncodings(); |
| } |
| } |
| |
| // Helper function used to sort the instruction group elements in a vector. |
| static bool InstructionGroupLess(EncodingGroup *lhs, EncodingGroup *rhs) { |
| uint64_t lhs_value = 0; |
| uint64_t rhs_value = 0; |
| if (lhs->parent() == nullptr) { |
| auto grp_recipe = GetExtractionRecipe(lhs->constant()); |
| lhs_value = ExtractValue(lhs->encoding_vec()[0]->GetValue(), grp_recipe); |
| rhs_value = ExtractValue(rhs->encoding_vec()[0]->GetValue(), grp_recipe); |
| } else { |
| auto grp_recipe = GetExtractionRecipe(lhs->parent()->discriminator()); |
| lhs_value = ExtractValue(lhs->encoding_vec()[0]->GetValue(), grp_recipe); |
| rhs_value = ExtractValue(rhs->encoding_vec()[0]->GetValue(), grp_recipe); |
| } |
| return lhs_value < rhs_value; |
| } |
| |
| // Emit the code in the form of two strings that are returned in a tuple. |
| std::tuple<std::string, std::string> InstructionGroup::EmitCode() { |
| std::string h_string; |
| std::string cc_string; |
| |
| // First sort the encoding group vector according to the value of the |
| // discriminator bits. |
| std::sort(encoding_group_vec_.begin(), encoding_group_vec_.end(), |
| &InstructionGroupLess); |
| |
| if (!encoding_group_vec_.empty()) { |
| std::string initializers; |
| // The signature for the top level decode function for this instruction |
| // group. |
| std::string signature = |
| absl::StrCat(opcode_enum_, " Decode", this->name(), "(", |
| format_->uint_type_name(), " inst_word)"); |
| // First part of the definition of the top level decoder function. |
| std::string top_level_decoder = |
| absl::StrCat(signature, " {\n", " ", opcode_enum_, " opcode;\n"); |
| std::string declarations = |
| absl::StrCat(opcode_enum_, " Decode", this->name(), "None(", |
| format_->uint_type_name(), ");\n"); |
| std::string definitions = |
| absl::StrCat(opcode_enum_, " Decode", this->name(), "None(", |
| format_->uint_type_name(), ") {\n return ", opcode_enum_, |
| "::kNone;\n}\n\n"); |
| for (size_t i = 0; i < encoding_group_vec_.size(); i++) { |
| auto *grp = encoding_group_vec_[i]; |
| std::string name = absl::StrCat(this->name(), "_", absl::Hex(i)); |
| grp->EmitInitializers(name, &initializers, opcode_enum_); |
| grp->EmitDecoders(name, &declarations, &definitions, opcode_enum_); |
| absl::StrAppend(&top_level_decoder, " opcode = Decode", name, |
| "(inst_word);\n"); |
| if (encoding_group_vec_.size() > 1) { |
| absl::StrAppend(&top_level_decoder, " if (opcode != ", opcode_enum_, |
| "::kNone) return opcode;\n"); |
| } |
| } |
| // Last part of the definition of the top level decoder function. |
| absl::StrAppend(&top_level_decoder, |
| " return opcode;\n" |
| "}\n"); |
| // String the different strings together in order and return. |
| absl::StrAppend(&cc_string, declarations, initializers, definitions, |
| top_level_decoder); |
| absl::StrAppend(&h_string, signature, ";\n"); |
| } |
| return std::make_tuple(h_string, cc_string); |
| } |
| |
| // Write out instruction group information. |
| std::string InstructionGroup::WriteGroup() { |
| std::string output; |
| absl::StrAppend(&output, "\n\n// Instruction group: ", name_, "\n"); |
| absl::PadSpec pad; |
| switch (width_ / 8) { |
| case 1: |
| pad = absl::PadSpec::kZeroPad2; |
| break; |
| case 2: |
| pad = absl::PadSpec::kZeroPad4; |
| break; |
| case 4: |
| pad = absl::PadSpec::kZeroPad8; |
| break; |
| case 8: |
| pad = absl::PadSpec::kZeroPad16; |
| break; |
| default: |
| pad = absl::PadSpec::kNoPad; |
| break; |
| } |
| uint64_t common_mask = 0xffff'ffff'ffff'ffff; |
| for (auto &[key, unused] : encoding_map_) { |
| common_mask &= key; |
| } |
| absl::StrAppend(&output, "// common bits: ", absl::Hex(common_mask, pad), |
| "\n"); |
| for (auto *grp : encoding_group_vec_) { |
| absl::StrAppend(&output, grp->DumpGroup("", " ")); |
| } |
| return output; |
| } |
| |
| } // namespace bin_format |
| } // namespace decoder |
| } // namespace sim |
| } // namespace mpact |