| // 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/encoding_group.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/container/btree_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/functional/bind_front.h" |
| #include "absl/log/log.h" |
| #include "absl/numeric/bits.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "mpact/sim/decoder/extract.h" |
| #include "mpact/sim/decoder/format_name.h" |
| #include "mpact/sim/decoder/instruction_encoding.h" |
| #include "mpact/sim/decoder/instruction_group.h" |
| #include "mpact/sim/decoder/overlay.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace decoder { |
| namespace bin_format { |
| |
| using machine_description::instruction_set::ToPascalCase; |
| |
| // Indexed by the members of the ConstraintType enum in instruction_encoding.h. |
| const char *kComparison[] = { |
| /* kEq */ "==", |
| /* kNe */ "!=", |
| /* kLt */ "<", |
| /* kLe */ "<=", |
| /* kGt */ ">", |
| /* kGe */ ">=", |
| }; |
| |
| // Helper function to provide a less than comparison of instruction encodings. |
| // This is used in call to std::sort. |
| static bool EncodingLess(uint64_t mask, InstructionEncoding *lhs, |
| InstructionEncoding *rhs) { |
| uint64_t lhs_val = lhs->GetValue() & mask; |
| uint64_t rhs_val = rhs->GetValue() & mask; |
| return lhs_val < rhs_val; |
| } |
| |
| EncodingGroup::EncodingGroup(InstructionGroup *inst_group, uint64_t ignore) |
| : EncodingGroup(nullptr, inst_group, ignore) {} |
| |
| EncodingGroup::EncodingGroup(EncodingGroup *parent, |
| InstructionGroup *inst_group, uint64_t ignore) |
| : inst_group_(inst_group), parent_(parent), ignore_(ignore) {} |
| |
| EncodingGroup::~EncodingGroup() { |
| for (auto *group : encoding_group_vec_) { |
| delete group; |
| } |
| encoding_group_vec_.clear(); |
| } |
| |
| // Adjust the masks and resort the encoding_vec. |
| void EncodingGroup::AdjustMask() { |
| if (parent_ != nullptr) { |
| uint64_t parent_mask = parent_->mask(); |
| constant_ &= ~parent_mask; |
| mask_ &= ~parent_mask; |
| varying_ &= ~parent_mask; |
| value_ &= ~parent_mask; |
| } |
| std::sort(encoding_vec_.begin(), encoding_vec_.end(), |
| absl::bind_front(&EncodingLess, mask_ & ~constant_ & ~ignore_)); |
| } |
| |
| // Adds an instruction encoding to the encoding group. |
| void EncodingGroup::AddEncoding(InstructionEncoding *enc) { |
| if (encoding_vec_.empty()) { |
| last_value_ = enc->GetValue(); |
| mask_ = enc->GetMask(); |
| } |
| encoding_vec_.push_back(enc); |
| mask_ &= enc->GetMask() & ~ignore_; |
| uint64_t value = enc->GetValue(); |
| varying_ |= (value ^ last_value_); |
| constant_ = ((~varying_) & mask_) & ~ignore_; |
| last_value_ = value; |
| value_ = encoding_vec_[0]->GetValue() & constant_; |
| } |
| |
| // Returns true if there is overlap between the encoding and those already |
| // in the group. Returns false if it would clear the mask of common "fixed" |
| // bits. |
| bool EncodingGroup::CanAddEncoding(InstructionEncoding *enc) { |
| if (encoding_vec_.empty()) return true; |
| uint64_t new_mask = mask_ & enc->GetMask(); |
| if (new_mask == 0) return false; |
| return true; |
| } |
| |
| // Returns true if the decode can be done by simple opcode table lookup as |
| // opposed to a function that performs comparisons. |
| bool EncodingGroup::IsSimpleDecode() { |
| if (!encoding_group_vec().empty()) return false; |
| for (auto *enc : encoding_vec_) { |
| if (!enc->other_constraints().empty()) return false; |
| if (!enc->equal_extracted_constraints().empty()) return false; |
| if (!enc->HasSpecialization()) return false; |
| } |
| return discriminator_ == varying_; |
| } |
| |
| // This method takes the current group and seeks to break the encodings into |
| // subgroups based on the bits of the instructions that vary across the members |
| // of the group. |
| void EncodingGroup::AddSubGroups() { |
| AdjustMask(); |
| discriminator_ = mask_ & ~constant_; |
| simple_decoding_ = IsSimpleDecode(); |
| discriminator_recipe_ = GetExtractionRecipe(mask_ & ~constant_); |
| discriminator_size_ = |
| discriminator_ == 0 ? 0 : 1 << absl::popcount(discriminator_); |
| unsigned shift = |
| absl::bit_width(static_cast<unsigned>(inst_group_->width())) - 1; |
| if (absl::popcount(static_cast<unsigned>(inst_group_->width())) > 1) { |
| shift++; |
| } |
| shift = std::max(shift, 3U); |
| if (shift > 6) { |
| inst_word_type_ = "\n#error instruction word wider than 64 bits\n"; |
| } else { |
| inst_word_type_ = absl::StrCat("uint", 1 << shift, "_t"); |
| } |
| // Get the recipe for extracting only the varying bits that are part of the |
| // mask into a compressed (i.e., bits are all adjacent) value. |
| auto recipe = GetExtractionRecipe(discriminator_); |
| // Iterate across the possible values of the compressed varying bits. |
| for (int i = 0; i < (1 << absl::popcount(discriminator_)); i++) { |
| // Create a new group for the current value 'i'. |
| auto *encoding_group = new EncodingGroup( |
| this, inst_group_, ignore_ | constant_ | discriminator_); |
| for (auto *enc : encoding_vec_) { |
| // Add all encodings that have value 'i' for the varying bits. |
| if (ExtractValue(enc->GetValue(), recipe) != static_cast<unsigned>(i)) |
| continue; |
| encoding_group->AddEncoding(enc); |
| } |
| // Avoid useless groups and infinite recursion by deleting any groups that |
| // are empty and where all the encodings ended up in the same subgroup. |
| if (encoding_group->encoding_vec().empty()) { |
| delete encoding_group; |
| continue; |
| } |
| if (encoding_group->encoding_vec().size() == encoding_vec_.size()) { |
| delete encoding_group; |
| return; |
| } |
| // Remove the bits used to break up the current group from the mask of the |
| // subgroup, as they have already been "used" on the path from the top to |
| // the subgroup. |
| encoding_group->AdjustMask(); |
| // If there are still bits that are part of the mask, try to recursively |
| // break down the current group. |
| if ((encoding_group->varying() | encoding_group->constant()) != |
| encoding_group->mask()) { |
| simple_decoding_ = false; |
| encoding_group->AddSubGroups(); |
| // But undo it if the max number of "varying" bits across the groups |
| // is less than 2. |
| int max = 0; |
| for (auto *group : encoding_group->encoding_group_vec_) { |
| max = std::max(max, absl::popcount(group->varying())); |
| } |
| if (max < 2) { |
| for (auto *group : encoding_group->encoding_group_vec_) { |
| delete group; |
| } |
| encoding_group->encoding_group_vec_.clear(); |
| } |
| } else { |
| encoding_group->discriminator_ = |
| encoding_group->mask_ & ~encoding_group->constant_; |
| encoding_group->discriminator_recipe_ = GetExtractionRecipe( |
| encoding_group->mask_ & ~encoding_group->constant_); |
| encoding_group->discriminator_size_ = |
| 1 << absl::popcount(encoding_group->discriminator_); |
| encoding_group->simple_decoding_ = encoding_group->IsSimpleDecode(); |
| unsigned shift = |
| absl::bit_width(static_cast<unsigned>(inst_group_->width())) - 1; |
| if (absl::popcount(static_cast<unsigned>(inst_group_->width())) > 1) { |
| shift++; |
| } |
| shift = std::max(shift, 3U); |
| if (shift > 6) { |
| encoding_group->inst_word_type_ = |
| "\n#error instruction word wider than 64 bits\n"; |
| } else { |
| encoding_group->inst_word_type_ = |
| absl::StrCat("uint", 1 << shift, "_t"); |
| } |
| } |
| encoding_group_vec_.push_back(encoding_group); |
| } |
| } |
| |
| // Check for collisions of opcodes. |
| void EncodingGroup::CheckEncodings() const { |
| if (encoding_group_vec_.empty() && simple_decoding_) { |
| // The encodings are in order of discriminator value, so checking for |
| // duplicates are easy. |
| int prev_value = -1; |
| InstructionEncoding *prev_enc = nullptr; |
| for (auto *enc : encoding_vec_) { |
| int value = ExtractValue(enc->GetValue(), discriminator_recipe_); |
| if (value == prev_value) { |
| inst_group_->encoding_info()->error_listener()->semanticError( |
| nullptr, absl::StrCat("Duplicate encodings in instruction group ", |
| inst_group_->name(), ": ", enc->name(), |
| " and ", prev_enc->name())); |
| } |
| prev_enc = enc; |
| prev_value = value; |
| } |
| } |
| for (auto const *enc_grp : encoding_group_vec_) { |
| enc_grp->CheckEncodings(); |
| } |
| } |
| |
| // Emit the initializers of the decode function tables/opcode tables used |
| // by the decoding functions. |
| void EncodingGroup::EmitInitializers(absl::string_view name, |
| std::string *initializers_ptr, |
| const std::string &opcode_enum) const { |
| if (discriminator_size_ == 0) return; |
| absl::StrAppend(initializers_ptr, "constexpr int kParseGroup", name, |
| "_Size = ", discriminator_size_, ";\n\n"); |
| absl::StrAppend(initializers_ptr, "absl::AnyInvocable<std::pair<", |
| opcode_enum, ", FormatEnum>(", inst_word_type_, |
| ")>" |
| " parse_group_", |
| name, "[kParseGroup", name, "_Size] = {\n"); |
| size_t encoding_index = 0; |
| // Compute how many function names per line accounting for ',' and white |
| // space. |
| size_t per_line = 76 / (8 + name.size() + 1 + 2 + 2); |
| for (uint64_t i = 0; i < discriminator_size_; i++) { |
| uint64_t value = 0xffff'ffff'ffff'ffff; |
| if (encoding_index < encoding_group_vec_.size()) { |
| value = ExtractValue( |
| encoding_group_vec_[encoding_index]->encoding_vec_[0]->GetValue(), |
| discriminator_recipe_); |
| } |
| // Line start indent. |
| if ((i % per_line) == 0) { |
| absl::StrAppend(initializers_ptr, " "); |
| } |
| if (i == value) { |
| absl::StrAppend(initializers_ptr, " &Decode", name, "_", absl::Hex(i), |
| ","); |
| encoding_index++; |
| } else { |
| absl::StrAppend(initializers_ptr, " &Decode", inst_group_->name(), |
| "None,"); |
| } |
| |
| // Break the line every 4 items. |
| if ((i % per_line) == per_line - 1) { |
| absl::StrAppend(initializers_ptr, "\n"); |
| } |
| } |
| absl::StrAppend(initializers_ptr, "};\n\n"); |
| for (auto const *enc_grp : encoding_group_vec_) { |
| // Don't create initializers for leaf encoding groups. They don't need them. |
| if (enc_grp->encoding_group_vec_.empty()) continue; |
| std::string grp_name = absl::StrCat( |
| name, "_", |
| absl::Hex(ExtractValue(enc_grp->encoding_vec_[0]->GetValue(), |
| discriminator_recipe_))); |
| enc_grp->EmitInitializers(grp_name, initializers_ptr, opcode_enum); |
| } |
| } |
| |
| // Generate the code for the decoders, both the declarations in the .h file as |
| // well as the definitions in the .cc file. |
| void EncodingGroup::EmitDecoders(absl::string_view name, |
| std::string *declarations_ptr, |
| std::string *definitions_ptr, |
| const std::string &opcode_enum) const { |
| // Generate the decode function signature. |
| std::string signature = |
| absl::StrCat("std::pair<", opcode_enum, ", FormatEnum> Decode", name, "(", |
| inst_word_type_, " inst_word)"); |
| absl::StrAppend(declarations_ptr, signature, ";\n"); |
| absl::StrAppend(definitions_ptr, signature, " {\n"); |
| // Generate the index extraction code if the discriminator size is > 0. |
| std::string index_extraction; |
| if (!discriminator_recipe_.empty()) { |
| index_extraction = absl::StrCat(" ", inst_word_type_, " index;\n"); |
| absl::StrAppend( |
| &index_extraction, |
| WriteExtraction(discriminator_recipe_, "inst_word", "index", " ")); |
| } |
| // If the encoding group has a constant value, generate that test. |
| std::string constant_test; |
| if (constant_) { |
| uint64_t const_value = encoding_vec_[0]->GetValue() & constant_; |
| absl::StrAppend(&constant_test, " if ((inst_word & 0x", |
| absl::Hex(constant_), ") != 0x", absl::Hex(const_value), |
| ") return std::make_pair(", opcode_enum, |
| "::kNone, FormatEnum::kNone);\n"); |
| } |
| if (!encoding_group_vec_.empty()) { |
| // Create decoder for a non-leaf encoding group. Just extract the index |
| // and call the next level decode. |
| absl::StrAppend(definitions_ptr, constant_test, index_extraction); |
| absl::StrAppend(definitions_ptr, " return parse_group_", name, |
| "[index](inst_word);\n"); |
| } else { |
| // Create decoder for a leaf encoding group. |
| if (simple_decoding_) { |
| // Simple decoding means that there are no special values in the opcodes |
| // that have to be extracted and checked. A simple table lookup is |
| // sufficient. |
| if (encoding_vec_.size() == 1) { |
| // If the table size is 1, no need to generate the table, just return |
| // the opcode. |
| absl::StrAppend(definitions_ptr, constant_test, |
| " return std::make_pair(", opcode_enum, "::k", |
| ToPascalCase(encoding_vec_[0]->name()), |
| ", FormatEnum::k", |
| ToPascalCase(encoding_vec_[0]->format_name()), ");\n"); |
| } else { |
| // First generate the table of opcodes. The opcodes are in order of the |
| // extracted discriminator value. If there are gaps, fill them in with |
| // kNone values. |
| int count = 1 << absl::popcount(discriminator_); |
| absl::StrAppend(definitions_ptr, " static constexpr std::pair<", |
| opcode_enum, ", FormatEnum> opcodes[", count, |
| "] = {\n"); |
| int entry = 0; |
| for (auto *enc : encoding_vec_) { |
| int value = ExtractValue(enc->GetValue(), discriminator_recipe_); |
| while (entry < value) { |
| absl::StrAppend(definitions_ptr, " {", opcode_enum, |
| "::kNone, FormatEnum::kNone},\n"); |
| entry++; |
| } |
| absl::StrAppend(definitions_ptr, " {", opcode_enum, "::k", |
| ToPascalCase(enc->name()), ", FormatEnum::k", |
| ToPascalCase(enc->format_name()), "},\n"); |
| entry++; |
| } |
| while (entry < count) { |
| absl::StrAppend(definitions_ptr, " {", opcode_enum, |
| "::kNone, FormatEnum::kNone},\n"); |
| entry++; |
| } |
| absl::StrAppend(definitions_ptr, " };\n"); |
| // Return the appropriate opcode. |
| absl::StrAppend(definitions_ptr, constant_test, index_extraction); |
| absl::StrAppend(definitions_ptr, " return opcodes[index];\n"); |
| } |
| } else { // if (simple_decoding_) |
| // Non simple decoding requires a sequence of if statements, as some |
| // of the opcodes may have additional constraints == or != on bitfields |
| // or overlays. |
| absl::StrAppend(definitions_ptr, constant_test, index_extraction); |
| EmitComplexDecoderBody(definitions_ptr, index_extraction, opcode_enum); |
| } |
| } |
| |
| absl::StrAppend(definitions_ptr, "}\n\n"); |
| |
| if (encoding_group_vec_.empty()) return; |
| |
| for (auto const *enc_grp : encoding_group_vec_) { |
| uint64_t value = ExtractValue(enc_grp->encoding_vec_[0]->GetValue(), |
| discriminator_recipe_); |
| std::string grp_name = absl::StrCat(name, "_", absl::Hex(value)); |
| enc_grp->EmitDecoders(grp_name, declarations_ptr, definitions_ptr, |
| opcode_enum); |
| } |
| } |
| |
| void EncodingGroup::EmitComplexDecoderBody( |
| std::string *definitions_ptr, absl::string_view index_extraction, |
| absl::string_view opcode_enum) const { |
| // If the index_extraction is empty, use a series of if statements. |
| if (index_extraction.empty()) { |
| EmitComplexDecoderBodyIfSequence(definitions_ptr, opcode_enum); |
| return; |
| } |
| // Group the encodings by index value. |
| absl::btree_map<uint64_t, std::vector<InstructionEncoding *>> encoding_map; |
| for (auto *encoding : encoding_vec_) { |
| // Get the discriminator value. |
| uint64_t index_value = |
| ExtractValue(encoding->GetValue(), discriminator_recipe_); |
| encoding_map[index_value].push_back(encoding); |
| } |
| absl::StrAppend(definitions_ptr, " switch (index) {\n"); |
| // For each index value, generate the 'case' statement. |
| for (auto &[index_value, encodings] : encoding_map) { |
| absl::flat_hash_set<std::string> extracted; |
| absl::StrAppend(definitions_ptr, " case 0x", absl::Hex(index_value), |
| ": {\n"); |
| int if_count = 0; |
| for (auto *encoding : encodings) { |
| for (auto *constraint : encoding->equal_constraints()) { |
| ProcessConstraint(extracted, constraint, definitions_ptr); |
| } |
| if_count += EmitEncodingIfStatement(/*indent*/ 4, encoding, opcode_enum, |
| extracted, definitions_ptr); |
| } |
| if (if_count > 0) absl::StrAppend(definitions_ptr, " break;\n"); |
| absl::StrAppend(definitions_ptr, " }\n"); |
| } |
| absl::StrAppend(definitions_ptr, " default: break;\n", " }\n", |
| " return std::make_pair(", opcode_enum, |
| "::kNone, FormatEnum::kNone);\n"); |
| } |
| |
| void EncodingGroup::EmitComplexDecoderBodyIfSequence( |
| std::string *definitions_ptr, absl::string_view opcode_enum) const { |
| // For each instruction in the encoding vec, generate the if statement |
| // to see if the instruction is matched. |
| absl::flat_hash_set<std::string> extracted; |
| int count = 0; |
| // For equal constraints, some can be ignored because those bits are |
| // wholly considered by the parent groups or the discriminator. |
| for (auto *encoding : encoding_vec_) { |
| for (auto *constraint : encoding->equal_constraints()) { |
| ProcessConstraint(extracted, constraint, definitions_ptr); |
| } |
| count += EmitEncodingIfStatement(/*indent*/ 0, encoding, opcode_enum, |
| extracted, definitions_ptr); |
| } |
| if (count > 0) { |
| absl::StrAppend(definitions_ptr, " return std::make_pair(", opcode_enum, |
| "::kNone, FormatEnum::kNone);\n"); |
| } |
| } |
| |
| void EncodingGroup::ProcessConstraint( |
| const absl::flat_hash_set<std::string> &extracted, Constraint *constraint, |
| std::string *definitions_ptr) const { |
| if (constraint->field != nullptr) { |
| // Field constraint. |
| Field *field = constraint->field; |
| std::string name = absl::StrCat(field->name, "_value"); |
| if (extracted.contains(name)) return; |
| uint64_t mask = ((1ULL << field->width) - 1); |
| // If the bits in the field are already handled by a parent or |
| // the discriminator, ignore the field. There is no need to emit |
| // a check for it. |
| if (((mask << field->low) & ~(ignore_ | discriminator_)) == 0) { |
| constraint->can_ignore = true; |
| } |
| return; |
| } |
| |
| // It's an overlay constraint. |
| Overlay *overlay = constraint->overlay; |
| std::string name = absl::StrCat(overlay->name(), "_value"); |
| uint64_t mask = 0; |
| // Get the bits that correspond to the overlay. |
| auto result = overlay->GetBitField((1 << overlay->declared_width()) - 1); |
| if (result.ok()) { |
| mask = result.value(); |
| } else { |
| absl::StrAppend(definitions_ptr, |
| "#error Internal error: cannot extract value from ", |
| overlay->name()); |
| return; |
| } |
| // If the bits in the overlay are already handled by a parent or |
| // the discriminator, ignore the field. There is no need to emit |
| // a check for it. |
| if ((mask & ~(ignore_ | discriminator_)) == 0) { |
| constraint->can_ignore = true; |
| } |
| } |
| |
| int EncodingGroup::EmitEncodingIfStatement( |
| int indent, const InstructionEncoding *encoding, |
| absl::string_view opcode_enum, absl::flat_hash_set<std::string> &extracted, |
| std::string *definitions_ptr) const { |
| std::string indent_str(indent + 2, ' '); |
| // Write any field/overlay extractions needed for the encoding (that |
| // haven't already been extracted). |
| EmitExtractions(indent, encoding->equal_constraints(), extracted, |
| definitions_ptr); |
| EmitExtractions(indent, encoding->equal_extracted_constraints(), extracted, |
| definitions_ptr); |
| EmitExtractions(indent, encoding->other_constraints(), extracted, |
| definitions_ptr); |
| // Write any field/overlay extractions needed for the specializations (that |
| // haven't already been extracted). |
| for (auto const &[unused, encoding] : encoding->specializations()) { |
| EmitExtractions(indent, encoding->equal_constraints(), extracted, |
| definitions_ptr); |
| EmitExtractions(indent, encoding->equal_extracted_constraints(), extracted, |
| definitions_ptr); |
| EmitExtractions(indent, encoding->other_constraints(), extracted, |
| definitions_ptr); |
| } |
| // Construct the if statement. |
| std::string condition; |
| std::string connector; |
| int count = 0; |
| // Equal constraints. |
| count += EmitConstraintConditions(encoding->equal_constraints(), |
| "==", connector, &condition); |
| count += EmitConstraintConditions(encoding->equal_extracted_constraints(), |
| "==", connector, &condition); |
| count += EmitOtherConstraintConditions(encoding->other_constraints(), |
| connector, &condition); |
| |
| bool specialization = encoding->HasSpecialization(); |
| // Ensure the number of parentheses are appropriate to the number of |
| // conjunctions in the if statement. |
| if (count > 1) { |
| absl::StrAppend(definitions_ptr, indent_str, "if (", condition, |
| specialization ? ") {\n" : ")\n"); |
| indent_str.append(" "); |
| } else if (count == 1) { |
| absl::StrAppend(definitions_ptr, indent_str, "if ", condition, |
| specialization ? " {\n" : "\n"); |
| indent_str.append(" "); |
| } |
| // If the instruction has specializations, emit the if statement for each |
| // specialization. |
| std::string if_str = "if "; |
| if (specialization) { |
| for (auto const &[name, encoding] : encoding->specializations()) { |
| int spec_count = 0; |
| std::string spec_condition; |
| std::string spec_connector; |
| // Construct the condition for the specialization instruction. |
| spec_count += EmitConstraintConditions( |
| encoding->equal_constraints(), "==", spec_connector, &spec_condition); |
| spec_count += |
| EmitConstraintConditions(encoding->equal_extracted_constraints(), |
| "==", spec_connector, &spec_condition); |
| spec_count += EmitOtherConstraintConditions( |
| encoding->other_constraints(), spec_connector, &spec_condition); |
| if (spec_count > 1) { |
| absl::StrAppend(definitions_ptr, indent_str, if_str, "(", |
| spec_condition, ") {\n"); |
| indent_str.append(" "); |
| } else if (spec_count == 1) { |
| absl::StrAppend(definitions_ptr, indent_str, if_str, spec_condition, |
| " {\n"); |
| } |
| absl::StrAppend(definitions_ptr, indent_str, " return std::make_pair(", |
| opcode_enum, "::k", ToPascalCase(encoding->name()), |
| ", FormatEnum::k", ToPascalCase(encoding->format_name()), |
| ");\n"); |
| if_str = "} else if "; |
| } |
| absl::StrAppend(definitions_ptr, indent_str, "} else {\n"); |
| indent_str.append(" "); |
| } |
| absl::StrAppend(definitions_ptr, indent_str, "return std::make_pair(", |
| opcode_enum, "::k", ToPascalCase(encoding->name()), |
| ", FormatEnum::k", ToPascalCase(encoding->format_name()), |
| ");\n"); |
| if (specialization) { |
| // Close the if statements. |
| indent_str = indent_str.substr(2); |
| absl::StrAppend(definitions_ptr, indent_str, "}\n"); |
| indent_str = indent_str.substr(2); |
| absl::StrAppend(definitions_ptr, indent_str, "}\n"); |
| } |
| return count != 0 ? 1 : 0; |
| } |
| |
| void EncodingGroup::EmitFieldExtraction( |
| const Field *field, const std::string &indent_str, |
| absl::flat_hash_set<std::string> &extracted, |
| std::string *definitions_ptr) const { |
| std::string name = absl::StrCat(field->name, "_value"); |
| if (!extracted.contains(name)) { |
| std::string data_type; |
| if (field->width > inst_group_->width()) { |
| auto shift = absl::bit_width(static_cast<unsigned>(field->width)) - 1; |
| if (absl::popcount(static_cast<unsigned>(field->width)) > 1) shift++; |
| shift = std::max(shift, 3); |
| if (shift > 6) { |
| LOG(ERROR) << "Field '" << field->name << "' width: " << field->width |
| << " > 64 bits"; |
| data_type = |
| absl::StrCat("#error field width ", field->width, " > 64 bits"); |
| } else { |
| data_type = absl::StrCat("uint", 1 << shift, "_t"); |
| } |
| } else { |
| data_type = inst_word_type_; |
| } |
| uint64_t mask = ((1ULL << field->width) - 1); |
| absl::StrAppend(definitions_ptr, indent_str, data_type, " ", name, |
| " = (inst_word >> ", field->low, ") & 0x", absl::Hex(mask), |
| ";\n"); |
| extracted.insert(name); |
| } |
| } |
| |
| void EncodingGroup::EmitOverlayExtraction( |
| const Overlay *overlay, const std::string &indent_str, |
| absl::flat_hash_set<std::string> &extracted, |
| std::string *definitions_ptr) const { |
| std::string name = absl::StrCat(overlay->name(), "_value"); |
| if (!extracted.contains(name)) { |
| auto ovl_width = overlay->declared_width(); |
| std::string data_type; |
| if (ovl_width > inst_group_->width()) { |
| auto shift = absl::bit_width(static_cast<unsigned>(ovl_width)) - 1; |
| if (absl::popcount(static_cast<unsigned>(ovl_width)) > 1) shift++; |
| shift = std::max(shift, 3); |
| if (shift > 6) { |
| LOG(ERROR) << "Field '" << overlay->name() << "' width: " << ovl_width |
| << " > 64 bits"; |
| data_type = |
| absl::StrCat("#error overlay width ", ovl_width, " > 64 bits"); |
| } else { |
| data_type = absl::StrCat("uint", 1 << shift, "_t"); |
| } |
| } else { |
| data_type = inst_word_type_; |
| } |
| absl::StrAppend(definitions_ptr, indent_str, data_type, " ", name, ";\n"); |
| absl::StrAppend(definitions_ptr, indent_str, |
| overlay->WriteSimpleValueExtractor("inst_word", name)); |
| extracted.insert(name); |
| } |
| } |
| |
| void EncodingGroup::EmitExtractions( |
| int indent, const std::vector<Constraint *> &constraints, |
| absl::flat_hash_set<std::string> &extracted, |
| std::string *definitions_ptr) const { |
| std::string indent_str(indent + 2, ' '); |
| // Write any field/overlay extractions needed for the constraints. |
| // Note, the extractions may be wider than the instruction word width, due |
| // to constant bits being added, so make sure to use appropriate type for |
| // each extraction. |
| for (auto const *constraint : constraints) { |
| if (constraint->can_ignore) continue; |
| if (constraint->field != nullptr) { |
| EmitFieldExtraction(constraint->field, indent_str, extracted, |
| definitions_ptr); |
| } else { |
| EmitOverlayExtraction(constraint->overlay, indent_str, extracted, |
| definitions_ptr); |
| } |
| if (constraint->rhs_field != nullptr) { |
| EmitFieldExtraction(constraint->rhs_field, indent_str, extracted, |
| definitions_ptr); |
| } else if (constraint->rhs_overlay != nullptr) { |
| EmitOverlayExtraction(constraint->rhs_overlay, indent_str, extracted, |
| definitions_ptr); |
| } |
| } |
| } |
| |
| int EncodingGroup::EmitOtherConstraintConditions( |
| const std::vector<Constraint *> &constraints, std::string &connector, |
| std::string *condition) const { |
| int count = 0; |
| for (auto const *constraint : constraints) { |
| if (constraint->can_ignore) continue; |
| |
| std::string comparison(kComparison[static_cast<int>(constraint->type)]); |
| std::string lhs_name = absl::StrCat((constraint->field != nullptr) |
| ? constraint->field->name |
| : constraint->overlay->name(), |
| "_value"); |
| std::string rhs; |
| if ((constraint->rhs_field != nullptr) || |
| (constraint->rhs_overlay != nullptr)) { |
| rhs = absl::StrCat((constraint->rhs_field != nullptr) |
| ? constraint->rhs_field->name |
| : constraint->rhs_overlay->name(), |
| "_value"); |
| } else { |
| rhs = absl::StrCat("0x", absl::Hex(constraint->value)); |
| } |
| |
| absl::StrAppend(condition, connector, "(", lhs_name, " ", comparison, " ", |
| rhs, ")"); |
| connector = " &&\n "; |
| count++; |
| } |
| return count; |
| } |
| |
| int EncodingGroup::EmitConstraintConditions( |
| const std::vector<Constraint *> &constraints, absl::string_view comparison, |
| std::string &connector, std::string *condition) const { |
| int count = 0; |
| for (auto const *constraint : constraints) { |
| std::string comparison(kComparison[static_cast<int>(constraint->type)]); |
| if (constraint->can_ignore) continue; |
| std::string name = absl::StrCat((constraint->field != nullptr) |
| ? constraint->field->name |
| : constraint->overlay->name(), |
| "_value"); |
| absl::StrAppend(condition, connector, "(", name, " ", comparison, " 0x", |
| absl::Hex(constraint->value), ")"); |
| connector = " &&\n "; |
| count++; |
| } |
| return count; |
| } |
| |
| // This method dumps statistics about the group useful for development. |
| // TODO(torerik): remove when no longer needed. |
| std::string EncodingGroup::DumpGroup(std::string prefix, std::string indent) { |
| std::string output; |
| auto pad = absl::PadSpec::kZeroPad8; |
| uint64_t grp_value; |
| if (parent_ == nullptr) { |
| auto grp_recipe = GetExtractionRecipe(constant_); |
| grp_value = ExtractValue(encoding_vec_[0]->GetValue(), grp_recipe); |
| } else { |
| auto grp_recipe = GetExtractionRecipe(parent_->mask() & parent_->varying()); |
| grp_value = ExtractValue(encoding_vec_[0]->GetValue(), grp_recipe); |
| } |
| uint64_t const_value = encoding_vec_[0]->GetValue() & constant_; |
| uint64_t discriminator = mask_ & ~constant_; |
| absl::StrAppend( |
| &output, "//", indent, prefix, "GROUP:\n//", indent, |
| " mask: ", absl::Hex(mask_, pad), "\n//", indent, |
| " ignore: ", absl::Hex(ignore_, pad), "\n//", indent, |
| " constant: ", absl::Hex(constant_, pad), " : ", |
| absl::Hex(const_value, pad), "\n//", indent, indent, |
| " varying: ", absl::Hex(varying_, pad), "\n//", indent, |
| " value: ", absl::Hex(grp_value, pad), "\n//", indent, |
| " discriminator: ", absl::Hex(discriminator, pad), "\n//", indent, |
| " simple: ", simple_decoding_ ? "true\n//" : "false\n//", indent, |
| " leaf: ", |
| encoding_group_vec_.empty() ? "true\n//" : "false\n//", |
| " encodings: ", encoding_vec_.size(), "\n"); |
| if (encoding_group_vec_.empty()) { |
| auto recipe = GetExtractionRecipe(varying_ & mask_ & ~ignore_); |
| for (auto *enc : encoding_vec_) { |
| uint64_t value = ExtractValue(enc->GetValue(), recipe); |
| absl::StrAppend(&output, "//", indent, " ", enc->name(), ": ", |
| absl::Hex(enc->GetValue() & varying_ & mask_, pad), " : ", |
| absl::Hex(value, pad), ": "); |
| uint64_t mask = enc->GetCombinedMask(); // ^ mask_; |
| if (parent_ != nullptr) { |
| mask &= ~(parent_->mask()); |
| } |
| if (mask != 0) { |
| mask &= ~ignore_; |
| absl::StrAppend(&output, absl::Hex(mask, pad), ": "); |
| } |
| for (auto *constraint : enc->equal_extracted_constraints()) { |
| std::string name = constraint->field == nullptr |
| ? constraint->overlay->name() |
| : constraint->field->name; |
| absl::StrAppend(&output, " ", name, " == ", |
| absl::Hex(constraint->value, absl::PadSpec::kZeroPad8), |
| " "); |
| } |
| for (auto *constraint : enc->other_constraints()) { |
| std::string name = constraint->field == nullptr |
| ? constraint->overlay->name() |
| : constraint->field->name; |
| std::string rhs_value; |
| if (constraint->rhs_field != nullptr) { |
| rhs_value = constraint->rhs_field->name; |
| } else if (constraint->rhs_overlay != nullptr) { |
| rhs_value = constraint->rhs_overlay->name(); |
| } else { |
| rhs_value = absl::StrCat( |
| absl::Hex(constraint->value, absl::PadSpec::kZeroPad8)); |
| } |
| absl::StrAppend(&output, " ", name, " ", constraint->type, " ", |
| rhs_value, " "); |
| } |
| absl::StrAppend(&output, "\n"); |
| } |
| } else { |
| for (auto *group : encoding_group_vec_) { |
| absl::StrAppend(&output, group->DumpGroup("SUB" + prefix, indent + " ")); |
| } |
| } |
| return output; |
| } |
| |
| } // namespace bin_format |
| } // namespace decoder |
| } // namespace sim |
| } // namespace mpact |