| // 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/slot.h" |
| |
| #include <algorithm> |
| #include <cctype> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <map> |
| #include <stack> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "absl/container/btree_set.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "mpact/sim/decoder/format_name.h" |
| #include "mpact/sim/decoder/instruction.h" |
| #include "mpact/sim/decoder/instruction_set.h" |
| #include "mpact/sim/decoder/instruction_set_contexts.h" |
| #include "mpact/sim/decoder/opcode.h" |
| #include "mpact/sim/decoder/resource.h" |
| #include "mpact/sim/decoder/template_expression.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace machine_description { |
| namespace instruction_set { |
| |
| // This function translates the location specification into a set of '->' |
| // references starting with 'inst->' to get to the operand that is implied. |
| static absl::StatusOr<std::string> TranslateLocator( |
| const OperandLocator &locator) { |
| std::string code; |
| absl::StrAppend(&code, "inst->"); |
| if (locator.op_spec_number > 0) { |
| absl::StrAppend(&code, "child()->"); |
| } |
| for (int i = 1; i < locator.op_spec_number; i++) { |
| absl::StrAppend(&code, "next()->"); |
| } |
| if (locator.type == 'p') { |
| absl::StrAppend(&code, "Predicate()"); |
| } else if (locator.type == 's' || locator.type == 't') { |
| absl::StrAppend(&code, "Source(", locator.instance, ")"); |
| } else if (locator.type == 'd' || locator.type == 'e') { |
| absl::StrAppend(&code, "Destination(", locator.instance, ")"); |
| } else { |
| return absl::InternalError(absl::StrCat("Unknown locator type '", |
| std::string(locator.type, 1), "'")); |
| } |
| return code; |
| } |
| |
| // This is a helper function that generates the code snippet to extract the |
| // right sized value based on the length specifier in the print format |
| // specification. E.g., %08x, %04d, etc. |
| static std::string GetExtractor(const std::string &format) { |
| int size = 0; |
| int pos = 0; |
| int len = 1; |
| for (int i = 0; i < format.size(); i++) { |
| if (!isdigit(format[i])) continue; |
| pos = i; |
| break; |
| } |
| while (isdigit(format[pos + len])) len++; |
| if (!absl::SimpleAtoi(format.substr(pos, len), &size)) size = 0; |
| if (size == 0) return "->AsInt64(0)"; |
| if (size <= 2) return "->AsInt8(0)"; |
| if (size <= 4) return "->AsInt16(0)"; |
| if (size <= 8) return "->AsInt32(0)"; |
| return "->AsInt64(0)"; |
| } |
| |
| // Small helper function to just expand the expression specified by the |
| // FormatInfo from parsing the disassembly specifier. |
| static std::string ExpandExpression(const FormatInfo &format, |
| const std::string &locator) { |
| // Handle the case when it's just an '@' - i.e., just the address. |
| if (format.use_address && format.operation.empty()) { |
| return absl::StrCat("(inst->address())"); |
| } |
| if (format.operation.empty()) { |
| // No +/- for the @ sign, i.e., @ <</>> amount. |
| if (locator.empty()) return absl::StrCat("#error missing field locator"); |
| if (format.shift_amount == 0) { |
| return absl::StrCat(locator, GetExtractor(format.number_format)); |
| } |
| return absl::StrCat("(", locator, GetExtractor(format.number_format), |
| format.do_left_shift ? " << " : " >> ", |
| format.shift_amount, ")"); |
| } |
| // (@ +/- operand) <</>> shift amount |
| if (locator.empty()) return absl::StrCat("#error missing field locator"); |
| |
| return absl::StrCat("(", format.use_address ? "inst->address() " : "0 ", |
| format.operation, "(", locator, |
| GetExtractor(format.number_format), |
| format.shift_amount != 0 |
| ? absl::StrCat(format.do_left_shift ? " << " : " >> ", |
| format.shift_amount, "))") |
| : "))"); |
| } |
| |
| Slot::Slot(absl::string_view name, InstructionSet *instruction_set, |
| bool is_templated, SlotDeclCtx *ctx) |
| : instruction_set_(instruction_set), |
| ctx_(ctx), |
| is_templated_(is_templated), |
| name_(name), |
| pascal_name_(ToPascalCase(name)) {} |
| |
| Slot::~Slot() { |
| delete default_latency_; |
| default_latency_ = nullptr; |
| delete default_instruction_; |
| default_instruction_ = nullptr; |
| for (auto *param : template_parameters_) { |
| delete param; |
| } |
| template_parameters_.clear(); |
| for (auto &base : base_slots_) { |
| if (base.arguments != nullptr) { |
| for (auto *expr : *(base.arguments)) { |
| delete expr; |
| } |
| delete base.arguments; |
| } |
| } |
| base_slots_.clear(); |
| for (auto &[unused, inst_ptr] : instruction_map_) { |
| delete inst_ptr; |
| } |
| instruction_map_.clear(); |
| for (auto &[unused, element_ptr] : constant_map_) { |
| delete element_ptr; |
| } |
| constant_map_.clear(); |
| // The ctx objects stored in resource_spec_map_ are owned by the Antlr4 |
| // parser, so just clear the map (don't delete those objects). |
| resource_spec_map_.clear(); |
| for (auto &[ignored, expr] : attribute_map_) { |
| delete expr; |
| } |
| attribute_map_.clear(); |
| } |
| |
| absl::Status Slot::AppendInstruction(Instruction *inst) { |
| if (!is_templated()) { |
| bool valid = inst->opcode()->ValidateDestLatencies( |
| [](int l) -> bool { return l >= 0; }); |
| if (!valid) { |
| return absl::InternalError(absl::StrCat("Invalid latency for opcode '", |
| inst->opcode()->name(), "'")); |
| } |
| } |
| std::string name = inst->opcode()->name(); |
| if (!instruction_map_.contains(name)) { |
| instruction_map_.emplace(name, inst); |
| return absl::OkStatus(); |
| } |
| return absl::InternalError(absl::StrCat( |
| "Opcode '", name, "' already added to slot '", this->name(), "'")); |
| } |
| |
| absl::Status Slot::AppendInheritedInstruction(Instruction *inst, |
| TemplateInstantiationArgs *args) { |
| std::string name = inst->opcode()->name(); |
| if (!instruction_map_.contains(name)) { |
| auto derived = inst->CreateDerivedInstruction(args); |
| |
| if (derived.ok()) { |
| if (!is_templated()) { |
| bool valid = derived.value()->opcode()->ValidateDestLatencies( |
| [](int l) -> bool { return l >= 0; }); |
| if (!valid) { |
| return absl::InternalError(absl::StrCat( |
| "Invalid latency for opcode '", inst->opcode()->name(), "'")); |
| } |
| } |
| instruction_map_.emplace(name, derived.value()); |
| return absl::OkStatus(); |
| } |
| return derived.status(); |
| } |
| return absl::InternalError( |
| absl::StrCat("instruction already added: ", inst->opcode()->name())); |
| } |
| |
| bool Slot::HasInstruction(const std::string &opcode_name) const { |
| return instruction_map_.contains(opcode_name); |
| } |
| |
| static std::string indent_string(int n) { return std::string(n, ' '); } |
| |
| // Generates a string that is a unique key for the attributes to determine which |
| // instructions can share attribute setter functions. |
| std::string Slot::CreateAttributeLookupKey(const Instruction *inst) const { |
| std::string key; |
| for (auto const &[name, expr] : inst->attribute_map()) { |
| std::string value; |
| auto result = expr->GetValue(); |
| if (!result.ok()) { |
| absl::StrAppend(&key, name, "[e1]:"); |
| continue; |
| } |
| auto *value_ptr = std::get_if<int>(&result.value()); |
| if (value_ptr == nullptr) { |
| absl::StrAppend(&key, name, "[e2]:"); |
| continue; |
| } |
| absl::StrAppend(&key, name, "[", *value_ptr, "]:"); |
| } |
| return key; |
| } |
| |
| // Generate the attribute setter function that matches the "key" of the given |
| // instruction. |
| std::string Slot::GenerateAttributeSetterFcn(absl::string_view name, |
| const Instruction *inst) const { |
| std::string output; |
| absl::StrAppend(&output, "void ", name, "(Instruction *inst) {\n"); |
| if (!attribute_map_.empty()) { |
| // Allocate the array and initialize to zero. |
| absl::StrAppend(&output, " int *attrs = new int[", attribute_map_.size(), |
| "];\n" |
| " attrs = {"); |
| std::string sep = ""; |
| for (auto const &[name, expr] : inst->attribute_map()) { |
| auto result = expr->GetValue(); |
| if (!result.ok()) { |
| absl::StrAppend(&output, " #error Expression for '", name, |
| "' has no constant value\n"); |
| continue; |
| } |
| int *value = std::get_if<int>(&result.value()); |
| if (value == nullptr) { |
| absl::StrAppend(&output, " #error Expression for '", name, |
| "' does not have type int\n"); |
| continue; |
| } |
| absl::StrAppend(&output, sep, "*value"); |
| sep = ", "; |
| } |
| absl::StrAppend(&output, "};\n"); |
| absl::StrAppend(&output, |
| " inst->SetAttributes(absl::Span<int>(attrs, size));\n"); |
| } |
| absl::StrAppend(&output, "}\n"); |
| return output; |
| } |
| |
| // Return a function call string that will set the attributes for the given |
| // instruction. If no such appropriate function exists, create one. |
| std::string Slot::GenerateAttributeSetter(const Instruction *inst) { |
| auto key = CreateAttributeLookupKey(inst); |
| auto iter = attribute_setter_name_map_.find(key); |
| if (iter == attribute_setter_name_map_.end()) { |
| auto index = attribute_setter_name_map_.size(); |
| std::string func_name = |
| absl::StrCat(pascal_name(), "Slot", "SetAttributes", index); |
| iter = attribute_setter_name_map_.emplace(key, func_name).first; |
| absl::StrAppend(&setter_functions_, |
| GenerateAttributeSetterFcn(func_name, inst)); |
| } |
| return iter->second; |
| } |
| |
| namespace { |
| |
| std::string EscapeRegexCharacters(const std::string &str) { |
| std::string output; |
| if (str.empty()) return output; |
| auto pos = str.find_last_not_of(' '); |
| if (pos == std::string::npos) { |
| return "\\s+"; |
| } |
| std::string input(str.substr(pos)); |
| bool in_space = false; |
| char p; |
| for (auto c : str) { |
| if (isspace(c)) { |
| if (!in_space) { |
| if (ispunct(p)) { |
| absl::StrAppend(&output, "\\s*"); |
| } else { |
| absl::StrAppend(&output, "\\s+"); |
| } |
| } |
| in_space = true; |
| continue; |
| } |
| p = c; |
| in_space = false; |
| switch (c) { |
| case '.': |
| absl::StrAppend(&output, "\\."); |
| break; |
| case '(': |
| absl::StrAppend(&output, "\\("); |
| break; |
| case ')': |
| absl::StrAppend(&output, "\\)"); |
| break; |
| case '[': |
| absl::StrAppend(&output, "\\["); |
| break; |
| case ']': |
| absl::StrAppend(&output, "\\]"); |
| break; |
| case '*': |
| absl::StrAppend(&output, "\\*"); |
| break; |
| case '+': |
| absl::StrAppend(&output, "\\+"); |
| break; |
| case '?': |
| absl::StrAppend(&output, "\\?"); |
| break; |
| case '|': |
| absl::StrAppend(&output, "\\|"); |
| break; |
| case '{': |
| absl::StrAppend(&output, "\\{"); |
| break; |
| case '}': |
| absl::StrAppend(&output, "\\}"); |
| break; |
| case '^': |
| absl::StrAppend(&output, "\\^"); |
| break; |
| case '$': |
| absl::StrAppend(&output, "\\$"); |
| break; |
| case '!': |
| absl::StrAppend(&output, "\\!"); |
| break; |
| case '\\': |
| absl::StrAppend(&output, "\\\\"); |
| break; |
| default: |
| absl::StrAppend(&output, std::string(1, c)); |
| break; |
| } |
| } |
| return output; |
| } |
| |
| } // namespace |
| |
| std::tuple<std::string, std::vector<OperandLocator>> Slot::GenerateRegEx( |
| const Instruction *inst, std::vector<std::string> &formats) const { |
| std::string output = "R\"("; |
| std::string sep = "^"; |
| std::vector<OperandLocator> opnd_locators; |
| // Iterate over the vector of disasm formats. These will end up concatenated |
| // with \s+ separators. |
| for (auto const *disasm_fmt : inst->disasm_format_vec()) { |
| absl::StrAppend(&output, sep); |
| sep = "\\s+"; |
| // The fragments are the text part (not part of operands), that occur |
| // between the operand of the format. E.g., the commas in "r1, r2, r3". |
| auto fragment_iter = disasm_fmt->format_fragment_vec.begin(); |
| auto fragment_end = disasm_fmt->format_fragment_vec.end(); |
| // The formats are the instruction formats, E.g., the register names in |
| // "r1, r2, r3". |
| auto format_iter = disasm_fmt->format_info_vec.begin(); |
| auto format_end = disasm_fmt->format_info_vec.end(); |
| char prev = '\0'; |
| // Iterate over the format fragments. |
| while (fragment_iter != fragment_end) { |
| auto fragment = *fragment_iter; |
| if (!fragment.empty()) { |
| auto str = EscapeRegexCharacters(fragment); |
| absl::StrAppend(&output, str); |
| prev = str.back(); |
| } else { |
| prev = '\0'; |
| } |
| fragment_iter++; |
| if (format_iter != format_end) { |
| // If the trailling part of output is not '\\s*', and prev is |
| // punctuation, but not '.' or '_', add a space separator. |
| auto len = output.size(); |
| if (output.substr(len - 3) != "\\s*") { |
| if ((prev != '\0') && |
| !(isalnum(prev) || (prev == '_') || (prev == '.'))) { |
| absl::StrAppend(&output, "\\s*"); |
| } |
| } |
| std::string op_name = (*format_iter)->op_name; |
| absl::StrAppend(&output, "(\\S*?)"); |
| opnd_locators.push_back(inst->opcode()->op_locator_map().at(op_name)); |
| if ((fragment_iter != fragment_end) && (!(*fragment_iter).empty())) { |
| char c = (*fragment_iter)[0]; |
| // If the next fragment is not alnum or underscore, add a space |
| // separator. |
| if (!isalnum(c) || (c != '_')) { |
| absl::StrAppend(&output, "\\s*"); |
| } |
| } |
| format_iter++; |
| } |
| } |
| } |
| absl::StrAppend(&output, "$)\""); |
| return {output, opnd_locators}; |
| } |
| |
| std::string GenerateEncodingFunctions(const std::string &encoder, |
| InstructionSet instruction_set) { |
| std::string output; |
| absl::StrAppend(&output, "namespace {\n\n"); |
| absl::StrAppend( |
| &output, "absl::StatusOr<std::tuple<uint64_t, int>> EncodeNone(", encoder, |
| "*, SlotEnum, int, OpcodeEnum, uint64_t, const " |
| "std::vector<std::string> &) {\n" |
| " return absl::NotFoundError(\"No such opcode\");\n" |
| "}\n\n"); |
| return output; |
| } |
| // Generate a regex to match the assembly string for the instructions. |
| std::tuple<std::string, std::string> Slot::GenerateAsmRegexMatcher() const { |
| std::string h_output; |
| std::string cc_output; |
| std::string class_name = pascal_name() + "SlotMatcher"; |
| size_t max_args = 0; |
| |
| // Generate the encoder function for each instruction. |
| std::string encoder = |
| absl::StrCat(instruction_set_->pascal_name(), "EncoderInterfaceBase"); |
| |
| // Generate the matcher class. |
| absl::StrAppend( |
| &h_output, |
| "// Assembly matcher.\n" |
| "class ", |
| class_name, |
| " {\n" |
| " public:\n" |
| " ", |
| class_name, "(", instruction_set_->pascal_name(), |
| "EncoderInterfaceBase *encoder);\n" |
| " ~", |
| class_name, |
| "();\n" |
| " absl::Status Initialize();\n" |
| "absl::StatusOr<std::tuple<uint64_t, int>> " |
| " Encode(uint64_t address, absl::string_view text, int entry, " |
| "ResolverInterface *resolver, std::vector<RelocationInfo> " |
| "&relocations);\n\n" |
| " private:\n" |
| " bool Match(absl::string_view text, std::vector<int> &matches);\n" |
| " bool Extract(absl::string_view text, int index, " |
| "std::vector<std::string> &values);\n" |
| " ", |
| encoder, |
| " *encoder_;\n" |
| " std::vector<RE2 *> regex_vec_;\n" |
| " RE2::Set regex_set_;\n"); |
| absl::StrAppend(&cc_output, class_name, "::", class_name, "(", |
| instruction_set_->pascal_name(), |
| "EncoderInterfaceBase *encoder) :\n" |
| " encoder_(encoder),\n" |
| " regex_set_(RE2::Options(), RE2::ANCHOR_BOTH) {}\n" |
| "\n", |
| class_name, "::~", class_name, |
| "() {\n" |
| " for (int i = 0; i < re2_args.size(); ++i) {\n" |
| " delete re2_args[i];\n" |
| " }\n" |
| " for (auto *regex : regex_vec_) delete regex;\n" |
| " regex_vec_.clear();\n" |
| "}\n\n" |
| "absl::Status ", |
| class_name, |
| "::Initialize() {\n" |
| " int index;\n" |
| " std::string error;\n" |
| " index = regex_set_.Add(\"^$\", &error);\n" |
| " regex_vec_.push_back(new RE2(\"^$\"));\n"); |
| std::vector<std::string> formats; |
| for (auto const &[name, inst_ptr] : instruction_map_) { |
| auto [regex, opnd_locators] = GenerateRegEx(inst_ptr, formats); |
| max_args = std::max(max_args, opnd_locators.size()); |
| absl::StrAppend(&cc_output, " regex_vec_.push_back(new RE2(", regex, |
| "));\n" |
| " index = regex_set_.Add(", |
| regex, |
| ", &error);\n" |
| " if (index == -1) return absl::InternalError(error);\n"); |
| } |
| absl::StrAppend(&h_output, " std::string args[", max_args, |
| "];\n" |
| " std::array<RE2::Arg*, ", |
| max_args, "> re2_args = {"); |
| for (int i = 0; i < max_args; ++i) absl::StrAppend(&h_output, "nullptr, "); |
| absl::StrAppend(&h_output, " };\n"); |
| // Construct the RE2::Arg objects. |
| absl::StrAppend(&cc_output, |
| " auto ok = regex_set_.Compile();\n" |
| " if (!ok) return absl::InternalError(\"Failed to compile " |
| "regex set\");\n" |
| " for (int i = 0; i < ", |
| max_args, |
| "; ++i) {\n" |
| " re2_args[i] = new RE2::Arg(&args[i]);\n" |
| " }\n"); |
| absl::StrAppend( |
| &cc_output, |
| " return absl::OkStatus();\n" |
| "}\n\n" |
| "bool ", |
| class_name, |
| "::Match(absl::string_view text, std::vector<int> &matches) {\n" |
| " return regex_set_.Match(text, &matches);\n" |
| "}\n\n" |
| "bool ", |
| class_name, |
| "::Extract(absl::string_view text, int index, " |
| "std::vector<std::string> &values) {\n" |
| " auto ®ex = regex_vec_.at(index);\n" |
| " int arg_count = regex->NumberOfCapturingGroups();\n" |
| " if (!regex_vec_.at(index)->FullMatchN(text, *regex, " |
| "re2_args.data(), " |
| "arg_count))\n" |
| " return false;\n" |
| " for (int i = 0; i < arg_count; ++i) {\n" |
| " values.push_back(args[i]);\n" |
| " }\n" |
| " return true;\n" |
| "}\n\n" |
| "absl::StatusOr<std::tuple<uint64_t, int>> ", |
| pascal_name(), |
| "SlotMatcher::Encode(\n" |
| R"( |
| uint64_t address, absl::string_view text, int entry, ResolverInterface *resolver, |
| std::vector<RelocationInfo> &relocations) { |
| std::vector<int> matches; |
| std::string error_message = absl::StrCat("Failed to encode '", text, "':"); |
| if (!Match(text, matches) || (matches.size() == 0)) { |
| return absl::NotFoundError(error_message); |
| } |
| std::vector<std::tuple<uint64_t, int>> encodings; |
| for (auto index : matches) { |
| std::vector<std::string> values; |
| if (!Extract(text, index, values)) continue; |
| )", |
| " auto result = encode_fcns[index](encoder_, SlotEnum::k", |
| pascal_name(), |
| ", entry, \n" |
| " " |
| "static_cast<OpcodeEnum>(index), address, values, resolver, " |
| "relocations);\n", |
| R"( |
| if (!result.status().ok()) { |
| absl::StrAppend(&error_message, "\n ", result.status().message()); |
| continue; |
| } |
| encodings.push_back(result.value()); |
| } |
| if (encodings.empty()) return absl::NotFoundError(error_message); |
| if (encodings.size() > 1) { |
| return absl::NotFoundError( |
| absl::StrCat("Failed to encode '", text, "': ambiguous")); |
| } |
| return encodings[0]; |
| } |
| |
| )"); |
| absl::StrAppend(&h_output, "};\n\n"); |
| return {h_output, cc_output}; |
| } |
| |
| // Generate a function that will set the disassembly string for the given |
| // instruction. |
| std::string Slot::GenerateDisasmSetterFcn(absl::string_view name, |
| const Instruction *inst) const { |
| std::string output; |
| std::string class_name = pascal_name() + "Slot"; |
| absl::StrAppend(&output, "void ", name, "(Instruction *inst) {\n"); |
| absl::StrAppend(&output, " inst->SetDisassemblyString("); |
| int indent = 2; |
| int outer_paren = false; |
| // This is used to keep track of whether the current code emitted is in |
| // a call to strcat or not. It helps reduce the number of strcat calls made |
| // in the generated code. |
| std::stack<bool> in_strcat; |
| absl::StrAppend(&output, "absl::StrCat(\n"); |
| in_strcat.push(true); |
| outer_paren = true; |
| std::string outer_sep; |
| for (auto const *disasm_fmt : inst->disasm_format_vec()) { |
| int inner_paren = 0; |
| size_t index = 0; |
| std::string inner_sep; |
| // If the next string needs to be formatted within a certain width field, |
| // start out with a StrFormat call. |
| if (disasm_fmt->width != 0) { |
| absl::StrAppend(&output, outer_sep, indent_string(indent), |
| "absl::StrFormat(\"%", disasm_fmt->width, "s\",\n"); |
| indent += 2; |
| inner_paren++; |
| in_strcat.push(false); |
| } else if (!outer_sep.empty()) { |
| absl::StrAppend(&output, ", "); |
| } |
| // If multiple strings will be generated, and we're not currently in a |
| // StrCat, start a StrCat. |
| if (!((disasm_fmt->format_fragment_vec.size() == 1) && |
| disasm_fmt->format_info_vec.empty()) && |
| !in_strcat.top()) { |
| absl::StrAppend(&output, indent_string(indent), "absl::StrCat(\n"); |
| indent += 2; |
| inner_paren++; |
| in_strcat.push(true); |
| } |
| // Generate the strings from the format fragments and the format info. |
| std::string next_sep; |
| for (auto const &frag : disasm_fmt->format_fragment_vec) { |
| if (!frag.empty()) { |
| absl::StrAppend(&output, inner_sep, indent_string(indent), "\"", frag, |
| "\""); |
| next_sep = ", "; |
| } |
| if (index < disasm_fmt->format_info_vec.size()) { |
| auto *format_info = disasm_fmt->format_info_vec[index]; |
| if (format_info->op_name.empty()) { |
| if (!format_info->is_formatted) { |
| absl::StrAppend(&output, "\n#error Missing locator information"); |
| } else { |
| absl::StrAppend( |
| &output, next_sep, "absl::StrFormat(\"", |
| format_info->number_format.back() == 'x' ? "0x" : "", |
| format_info->number_format, "\", ", |
| ExpandExpression(*format_info, ""), ")"); |
| } |
| } else { |
| auto key = format_info->op_name; |
| auto iter = inst->opcode()->op_locator_map().find(key); |
| if (iter == inst->opcode()->op_locator_map().end()) { |
| absl::StrAppend(&output, "\n#error ", key, |
| " not found in instruction opcodes\n"); |
| continue; |
| } |
| auto result = TranslateLocator(iter->second); |
| if (!result.status().ok()) { |
| absl::StrAppend(&output, "\n#error ", result.status().message(), |
| "\n"); |
| continue; |
| } |
| if (!format_info->is_formatted) { |
| absl::StrAppend(&output, next_sep, result.value(), "->AsString()"); |
| } else { |
| absl::StrAppend( |
| &output, next_sep, "absl::StrFormat(\"", |
| format_info->number_format.back() == 'x' ? "0x" : "", |
| format_info->number_format, "\", ", |
| ExpandExpression(*format_info, result.value()), ")"); |
| } |
| } |
| } |
| next_sep = ", "; |
| index++; |
| if (inner_sep.empty()) inner_sep = ",\n"; |
| } |
| // Close up parentheses as required. |
| for (int i = 0; i < inner_paren; i++) { |
| absl::StrAppend(&output, ")"); |
| indent -= 2; |
| if (!in_strcat.top()) { // Finished a StrFormat. |
| absl::StrAppend(&output, "\n", indent_string(indent)); |
| } |
| in_strcat.pop(); |
| } |
| if (outer_sep.empty()) outer_sep = ",\n"; |
| } |
| if (outer_paren) { |
| in_strcat.pop(); |
| absl::StrAppend(&output, ")"); |
| } |
| |
| absl::StrAppend(&output, ");\n}\n\n"); |
| return output; |
| } |
| |
| // Generate a signature for the disassembly setter function required for the |
| // given instruction. If a matching one does not exist, call to create such a |
| // function. |
| std::string Slot::GenerateDisassemblySetter(const Instruction *inst) { |
| std::string key; |
| // First combine the disassembly fragments. |
| for (auto const *format : inst->disasm_format_vec()) { |
| for (auto const &frag : format->format_fragment_vec) { |
| absl::StrAppend(&key, frag); |
| } |
| } |
| // Then add the resources. |
| absl::StrAppend(&key, ":", CreateOperandLookupKey(inst->opcode())); |
| std::string func_name = absl::StrCat( |
| pascal_name(), "Slot", inst->opcode()->pascal_name(), "SetDisasm"); |
| auto iter = disasm_setter_name_map_.find(key); |
| if (iter == disasm_setter_name_map_.end()) { |
| iter = disasm_setter_name_map_.emplace(key, func_name).first; |
| absl::StrAppend(&setter_functions_, |
| GenerateDisasmSetterFcn(func_name, inst)); |
| } |
| return absl::StrCat(iter->second); |
| } |
| |
| // Generate the assembler function for the given instruction. |
| std::string Slot::GenerateAssemblerFcn(const Instruction *inst, |
| absl::string_view encoder_type) const { |
| std::string output; |
| int num_values = inst->opcode()->source_op_vec().size() + |
| inst->opcode()->dest_op_vec().size(); |
| absl::StrAppend( |
| &output, "absl::StatusOr<std::tuple<int, uint64_t>> ", pascal_name(), |
| "Slot", "Assemble", inst->opcode()->pascal_name(), "(", encoder_type, |
| " *enc, const std::vector<std::string> &values, SlotEnum " |
| "slot, int entry) {\n", |
| " if (values.size() != ", num_values, |
| ")\n" |
| " return absl::InvalidArgumentError(\"Wrong number of values\");\n" |
| " constexpr OpcodeEnum opcode = OpcodeEnum::k", |
| inst->opcode()->pascal_name(), |
| ";\n" |
| "auto [inst_word, num_bits] = enc->GetOpEncoding(opcode, slot, " |
| "entry);\n", |
| " absl::Status status;\n"); |
| auto const &source_op_vec = inst->opcode()->source_op_vec(); |
| for (int i = 0; i < source_op_vec.size(); ++i) { |
| std::string op_name = ToPascalCase(source_op_vec[i].name); |
| absl::StrAppend(&output, " status = enc->SetSrcEncoding(values.at(", i, |
| "), slot, entry,\n" |
| "SourceOpEnum::k", |
| op_name, ", ", i, |
| ", opcode);\n" |
| " if (!stats.ok()) return status;\n"); |
| } |
| auto const &dest_op_vec = inst->opcode()->dest_op_vec(); |
| for (int i = 0; i < dest_op_vec.size(); ++i) { |
| absl::StrAppend(&output, " status = enc->SetDestEncoding(values.at(", i, |
| "), slot, entry,\n" |
| "DestOpEnum::k", |
| dest_op_vec[i]->pascal_case_name(), ", ", i, |
| ", opcode);\n" |
| " if (!stats.ok()) return status;\n"); |
| } |
| absl::StrAppend( |
| &output, |
| " auto ok = enc->ValidateEncoding(opcode, slot, entry, inst_word);\n" |
| " if (!ok) return absl::InvalidArgumentError(\"Invalid " |
| "encoding\");\n"); |
| absl::StrAppend(&output, |
| "return std::tie(num_bits, inst_word);\n" |
| "}\n\n"); |
| return output; |
| } |
| |
| // Generate a string that is a unique identifier from the resources to |
| // determine which instructions can share resource setter functions. |
| std::string Slot::CreateResourceKey( |
| const std::vector<const ResourceReference *> &refs) const { |
| std::string key; |
| std::vector<const ResourceReference *> complex_refs; |
| std::vector<const ResourceReference *> simple_refs; |
| absl::btree_set<std::string> names; |
| // Iterate over use resources. |
| for (auto const *ref : refs) { |
| if (!ref->resource->is_simple()) { |
| complex_refs.push_back(ref); |
| } else { |
| simple_refs.push_back(ref); |
| } |
| } |
| // Simple use resources. |
| for (auto const *ref : simple_refs) { |
| std::string name = "S$"; |
| if (ref->is_array) { |
| absl::StrAppend(&name, "[", ref->resource->pascal_name(), "]"); |
| } else { |
| absl::StrAppend(&name, ref->resource->pascal_name()); |
| } |
| names.insert(name); |
| } |
| std::string sep = ""; |
| for (auto const &name : names) { |
| absl::StrAppend(&key, sep, name); |
| sep = "/"; |
| } |
| names.clear(); |
| absl::StrAppend(&key, ":"); |
| |
| // Complex use resources. |
| for (auto const *ref : complex_refs) { |
| std::string name = "C$"; |
| if (ref->is_array) { |
| absl::StrAppend(&name, "[", ref->resource->pascal_name(), "]"); |
| } else { |
| absl::StrAppend(&name, ref->resource->pascal_name()); |
| } |
| auto begin_value = ref->begin_expression->GetValue(); |
| if (!begin_value.ok()) { |
| absl::StrAppend(&name, "(?)"); |
| } else { |
| absl::StrAppend(&name, *std::get_if<int>(&begin_value.value())); |
| } |
| auto end_value = ref->end_expression->GetValue(); |
| if (!end_value.ok()) { |
| absl::StrAppend(&name, "(?)"); |
| } else { |
| absl::StrAppend(&name, *std::get_if<int>(&end_value.value())); |
| } |
| names.insert(name); |
| } |
| sep = ""; |
| for (auto const &name : names) { |
| absl::StrAppend(&key, sep, name); |
| sep = "/"; |
| } |
| return key; |
| } |
| |
| // Generate a resource setter function call for the resource "key" of the |
| // given instruction. If a matching one does not exist, call to create such a |
| // function. |
| std::string Slot::GenerateResourceSetter(const Instruction *inst, |
| absl::string_view encoding_type) { |
| std::string key = CreateResourceKey(inst->resource_use_vec()); |
| absl::StrAppend(&key, ":", CreateResourceKey(inst->resource_acquire_vec())); |
| auto iter = resource_setter_name_map_.find(key); |
| if (iter == resource_setter_name_map_.end()) { |
| auto index = resource_setter_name_map_.size(); |
| std::string func_name = |
| absl::StrCat(pascal_name(), "Slot", "SetResources", index); |
| iter = resource_setter_name_map_.emplace(key, func_name).first; |
| absl::StrAppend(&setter_functions_, |
| GenerateResourceSetterFcn(func_name, inst, encoding_type)); |
| } |
| return iter->second; |
| } |
| |
| // Create a resource setter function for the resource "key" of the given |
| // instruction. |
| std::string Slot::GenerateResourceSetterFcn( |
| absl::string_view name, const Instruction *inst, |
| absl::string_view encoding_type) const { |
| std::string output; |
| absl::StrAppend(&output, "void ", name, "(Instruction *inst, ", encoding_type, |
| " *enc, SlotEnum slot, int entry) {\n"); |
| std::string opcode_name = inst->opcode()->pascal_name(); |
| std::string opcode_enum = absl::StrCat("OpcodeEnum::k", opcode_name); |
| if (!inst->resource_use_vec().empty() || |
| !inst->resource_acquire_vec().empty()) { |
| absl::StrAppend(&output, " ResourceOperandInterface *res_op;\n"); |
| } |
| // Get all the simple resources that need to be free, then all the complex |
| // resources that need to be free in order to issue the instruction. |
| std::vector<const ResourceReference *> complex_refs; |
| std::vector<const ResourceReference *> simple_refs; |
| for (auto const *ref : inst->resource_use_vec()) { |
| // Do the complex refs last. |
| if (!ref->resource->is_simple()) { |
| complex_refs.push_back(ref); |
| } else { |
| simple_refs.push_back(ref); |
| } |
| } |
| // Simple resources. |
| if (!simple_refs.empty()) { |
| // First gather the resource references into a single vector, then request |
| // the resource operands for all the resource references in that vector. |
| absl::StrAppend(&output, " std::vector<SimpleResourceEnum> hold_vec = {"); |
| for (auto const *simple : simple_refs) { |
| std::string resource_name; |
| if (simple->is_array) { |
| resource_name = absl::StrCat("SimpleResourceEnum::k", |
| simple->resource->pascal_name()); |
| } else { |
| resource_name = absl::StrCat("SimpleResourceEnum::k", |
| simple->resource->pascal_name()); |
| } |
| absl::StrAppend(&output, "\n ", resource_name, ","); |
| } |
| absl::StrAppend(&output, |
| "};\n" |
| " res_op = enc->GetSimpleResourceOperand(slot, entry, ", |
| opcode_enum, ", hold_vec, -1);\n", |
| " if (res_op != nullptr) {\n" |
| " inst->AppendResourceHold(res_op);\n" |
| " }\n"); |
| } |
| // Complex resources. |
| for (auto const *complex : complex_refs) { |
| // Get the expression values for the begin and end expressions. |
| auto begin_value = complex->begin_expression->GetValue(); |
| auto end_value = complex->end_expression->GetValue(); |
| if (!begin_value.ok() || !end_value.ok()) { |
| absl::StrAppend(&output, |
| "#error Unable to evaluate begin or end expression\n"); |
| continue; |
| } |
| // Get the integer values from the begin and end expression values. |
| int *begin = std::get_if<int>(&begin_value.value()); |
| int *end = std::get_if<int>(&end_value.value()); |
| if ((begin == nullptr) || (end == nullptr)) { |
| absl::StrAppend( |
| &output, "#error Unable to get value of begin or end expression\n"); |
| continue; |
| } |
| if (complex->is_array) { |
| absl::StrAppend( |
| &output, |
| " auto res_op_vec = enc->GetComplexResourceOperands(slot, entry, ", |
| opcode_enum, ", ListComplexResourceEnum::k", |
| complex->resource->pascal_name(), *begin, ", ", *end, ");\n"); |
| absl::StrAppend(&output, |
| " for (auto res_op : res_op_vec) {\n" |
| " inst->AppendResourceHold(res_op);\n" |
| " }\n"); |
| } else { |
| absl::StrAppend(&output, |
| " res_op = enc->GetComplexResourceOperand(slot, entry, ", |
| opcode_enum, ", ComplexResourceEnum::k", |
| complex->resource->pascal_name(), ", "); |
| absl::StrAppend(&output, *begin, ", ", *end, ");\n"); |
| absl::StrAppend(&output, |
| " if (res_op != nullptr) {\n" |
| " inst->AppendResourceHold(res_op);\n" |
| " }\n"); |
| } |
| } |
| |
| // Get all the simple resources that need to be reserved, then all the |
| // complex resources that need to be reserved when issuing this instruction. |
| complex_refs.clear(); |
| simple_refs.clear(); |
| for (auto const *ref : inst->resource_acquire_vec()) { |
| // Do the complex refs last. |
| if (!ref->resource->is_simple()) { |
| complex_refs.push_back(ref); |
| } else { |
| simple_refs.push_back(ref); |
| } |
| } |
| // Simple resources. |
| if (!simple_refs.empty()) { |
| // Compute the set of latencies. Insert each reference into a multi-map |
| // keyed by the latency. |
| std::multimap<int, const ResourceReference *> latency_map; |
| absl::flat_hash_set<int> latencies; |
| for (auto const *simple : simple_refs) { |
| if (simple->end_expression == nullptr) { |
| continue; |
| } |
| auto end_value = simple->end_expression->GetValue(); |
| if (!end_value.ok()) { |
| absl::StrAppend(&output, "#error Unable to evaluate end expression\n"); |
| continue; |
| } |
| int *end = std::get_if<int>(&end_value.value()); |
| if (end == nullptr) { |
| absl::StrAppend(&output, |
| "#error Unable to get value of end expression\n"); |
| continue; |
| } |
| int latency = *end; |
| latencies.insert(latency); |
| latency_map.insert(std::make_pair(latency, simple)); |
| } |
| // Process the resources by latencies. |
| for (auto latency : latencies) { |
| std::string sep = ""; |
| absl::StrAppend(&output, " std::vector<SimpleResourceEnum> acquire_vec", |
| latency, " = {"); |
| for (auto iter = latency_map.lower_bound(latency); |
| iter != latency_map.upper_bound(latency); ++iter) { |
| auto *simple = iter->second; |
| std::string resource_name; |
| if (simple->is_array) { |
| resource_name = absl::StrCat("SimpleResourceEnum::k", |
| simple->resource->pascal_name()); |
| } else { |
| resource_name = absl::StrCat("SimpleResourceEnum::k", |
| simple->resource->pascal_name()); |
| } |
| absl::StrAppend(&output, sep, "\n ", resource_name, ","); |
| } |
| absl::StrAppend(&output, |
| "};\n\n" |
| " res_op = enc->GetSimpleResourceOperand(slot, entry, ", |
| opcode_enum, ", acquire_vec", latency, ", ", latency, |
| ");\n" |
| " if (res_op != nullptr) {\n" |
| " inst->AppendResourceAcquire(res_op);\n" |
| " }\n"); |
| } |
| } |
| |
| // Complex resources. |
| if (!complex_refs.empty()) { |
| for (auto const *complex : complex_refs) { |
| // Get the expression values for the begin and end expressions. |
| if (complex->begin_expression == nullptr) continue; |
| if (complex->end_expression == nullptr) continue; |
| auto begin_value = complex->begin_expression->GetValue(); |
| auto end_value = complex->end_expression->GetValue(); |
| if (!begin_value.ok() || !end_value.ok()) { |
| absl::StrAppend(&output, |
| "#error Unable to evaluate begin or end expression\n"); |
| continue; |
| } |
| // Get the integer values from the begin and end expression values. |
| int *begin = std::get_if<int>(&begin_value.value()); |
| int *end = std::get_if<int>(&end_value.value()); |
| if (complex->is_array) { |
| absl::StrAppend(&output, |
| " auto res_op_vec = " |
| "enc->GetComplexResourceOperands(slot, entry, ", |
| opcode_enum, ", ListComplexResourceEnum::k", |
| complex->resource->pascal_name(), *begin, ", ", *end, |
| ");\n"); |
| absl::StrAppend(&output, |
| " for (auto res_op : res_op_vec) {\n" |
| " inst->AppendResourceHold(res_op);\n" |
| " }\n"); |
| } else { |
| absl::StrAppend( |
| &output, |
| " res_op = enc->GetComplexResourceOperand(slot, entry, ", |
| opcode_enum, ", ComplexResourceEnum::k", |
| complex->resource->pascal_name(), ", "); |
| absl::StrAppend(&output, *begin, ", ", *end, ");\n"); |
| absl::StrAppend(&output, |
| " if (res_op != nullptr) {\n" |
| " inst->AppendResourceHold(res_op);\n" |
| " }\n"); |
| } |
| } |
| } |
| absl::StrAppend(&output, "}\n\n"); |
| return output; |
| } |
| |
| // Generates a string that is a unique identifier from the operands to |
| // determine which instructions can share operand getter functions. |
| std::string Slot::CreateOperandLookupKey(const Opcode *opcode) const { |
| std::string key; |
| // Generate identifier for the predicate operand, if the opcode has one. |
| const std::string &op_name = opcode->predicate_op_name(); |
| if (!op_name.empty()) { |
| absl::StrAppend(&key, op_name, ":"); |
| } |
| // Generate key for the source operands. |
| std::string sep = ""; |
| for (const auto &src_op : opcode->source_op_vec()) { |
| if (src_op.is_array) { |
| absl::StrAppend(&key, sep, "[", src_op.name, "]"); |
| } else { |
| absl::StrAppend(&key, sep, src_op.name); |
| } |
| sep = "/"; |
| } |
| absl::StrAppend(&key, ":"); |
| // Append identifier for destination operands. |
| sep.clear(); |
| for (auto const *dst_op : opcode->dest_op_vec()) { |
| std::string dest_op_enum; |
| if (dst_op->is_array()) { |
| absl::StrAppend(&key, sep, "[", dst_op->name(), "]"); |
| } else { |
| absl::StrAppend(&key, sep, dst_op->name()); |
| } |
| std::string latency; |
| if (dst_op->expression() == nullptr) { |
| absl::StrAppend(&latency, "(*)"); |
| } else { |
| auto result = dst_op->GetLatency(); |
| if (!result.ok()) { |
| absl::StrAppend(&latency, "(-1)"); |
| continue; |
| } |
| if (dst_op->is_array()) { |
| absl::StrAppend(&latency, "({", result.value(), "})"); |
| } else { |
| absl::StrAppend(&latency, "(", result.value(), ")"); |
| } |
| } |
| if (dst_op->is_array()) { |
| absl::StrAppend(&key, sep, "[", dst_op->name(), "]", latency); |
| } else { |
| absl::StrAppend(&key, sep, dst_op->name(), latency); |
| } |
| sep = "/"; |
| } |
| return key; |
| } |
| |
| // Generates a function that sets the operands for the operand "key" of the |
| // given opcode. |
| std::string Slot::GenerateOperandSetterFcn(absl::string_view getter_name, |
| absl::string_view encoding_type, |
| const Opcode *opcode) const { |
| std::string output; |
| absl::StrAppend(&output, "void ", getter_name, "(Instruction *inst, ", |
| encoding_type, |
| " *enc, OpcodeEnum opcode, SlotEnum slot, int entry) {\n"); |
| // Generate code to set predicate operand, if the opcode has one. |
| const std::string &op_name = opcode->predicate_op_name(); |
| if (!op_name.empty()) { |
| std::string pred_op_enum = |
| absl::StrCat("PredOpEnum::k", ToPascalCase(op_name)); |
| absl::StrAppend(&output, " inst->SetPredicate(enc->GetPredicate", |
| "(slot, entry, opcode, ", pred_op_enum, "));\n"); |
| } |
| // Generate code to set the instruction's source operands. |
| int source_no = 0; |
| for (const auto &src_op : opcode->source_op_vec()) { |
| // If the source operand is an array, then we need to iterate over the |
| // vector of operands that GetSources returns. |
| if (src_op.is_array) { |
| std::string src_op_enum = |
| absl::StrCat("ListSourceOpEnum::k", ToPascalCase(src_op.name)); |
| absl::StrAppend(&output, |
| " {\n" |
| " auto vec = enc->GetSources", |
| "(slot, entry, opcode, ", src_op_enum, ", ", source_no, |
| ");\n" |
| " for (auto *op : vec) inst->AppendSource(op);\n" |
| " }\n"); |
| } else { |
| std::string src_op_enum = |
| absl::StrCat("SourceOpEnum::k", ToPascalCase(src_op.name)); |
| absl::StrAppend(&output, " inst->AppendSource(enc->GetSource", |
| "(slot, entry, opcode, ", src_op_enum, ", ", source_no++, |
| "));\n"); |
| } |
| } |
| // Generate code to set the instruction's destination operands. |
| int dest_no = 0; |
| for (auto const *dst_op : opcode->dest_op_vec()) { |
| std::string dest_op_enum; |
| if (dst_op->is_array()) { |
| dest_op_enum = |
| absl::StrCat("ListDestOpEnum::k", dst_op->pascal_case_name()); |
| } else { |
| dest_op_enum = absl::StrCat("DestOpEnum::k", dst_op->pascal_case_name()); |
| } |
| std::string latency; |
| if (dst_op->expression() == nullptr) { |
| latency = absl::StrCat("enc->GetLatency(slot, entry, opcode, ", |
| dest_op_enum, ", ", dest_no, ")"); |
| } else { |
| auto result = dst_op->GetLatency(); |
| if (!result.ok()) { |
| absl::StrAppend(&output, "#error \"Failed to get latency for operand '", |
| dst_op->name(), "'\""); |
| dest_no++; |
| continue; |
| } |
| latency = absl::StrCat(result.value()); |
| // If the operand is an array, then the latency is a vector. |
| if (dst_op->is_array()) { |
| latency = absl::StrCat("{", latency, "}"); |
| } |
| } |
| if (dst_op->is_array()) { |
| absl::StrAppend(&output, |
| " {\n" |
| " auto vec = enc->GetDestinations", |
| "(slot, entry, opcode, ", dest_op_enum, ", ", dest_no, |
| ", ", latency, |
| ");\n" |
| " for (auto *op : vec) inst->AppendDestination(op);\n" |
| " }"); |
| } else { |
| absl::StrAppend(&output, " inst->AppendDestination(enc->GetDestination(", |
| "slot, entry, opcode, ", dest_op_enum, ", ", dest_no, |
| ", ", latency, "));\n"); |
| dest_no++; |
| } |
| dest_no++; |
| } |
| absl::StrAppend(&output, "}\n\n"); |
| return output; |
| } |
| |
| std::string Slot::ListFuncGetterInitializations( |
| absl::string_view encoding_type) { |
| std::string output; |
| if (instruction_map_.empty()) return output; |
| std::string class_name = pascal_name() + "Slot"; |
| // For each instruction create two lambda functions. One that is used to |
| // obtain the semantic function object for the instruction, the other a |
| // lambda that sets the predicate, source and target operands. Both lambdas |
| // use calls to virtual functions declared in the current class or a base |
| // class thereof. |
| std::string signature = |
| absl::StrCat("(Instruction *inst, ", encoding_type, |
| " *enc, OpcodeEnum opcode, SlotEnum slot, int entry)"); |
| absl::StrAppend(&setter_functions_, |
| GenerateOperandSetterFcn( |
| absl::StrCat(pascal_name(), "SlotSetOperandsNull"), |
| encoding_type, default_instruction_->opcode())); |
| absl::StrAppend( |
| &output, " {OperandSetter{", pascal_name(), |
| "SlotSetOperandsNull},\n" |
| " ", |
| GenerateDisassemblySetter(default_instruction_), ",\n", " ", |
| GenerateResourceSetter(default_instruction_, encoding_type), ",\n", |
| " ", GenerateAttributeSetter(default_instruction_), ",\n", |
| " SemFuncSetter{", default_instruction_->semfunc_code_string(), "}, ", |
| default_instruction_->opcode()->instruction_size(), "},\n"); |
| for (auto const &[unused, inst_ptr] : instruction_map_) { |
| auto *instruction = inst_ptr; |
| std::string opcode_name = instruction->opcode()->pascal_name(); |
| std::string opcode_enum = absl::StrCat("OpcodeEnum::k", opcode_name); |
| absl::StrAppend(&output, "\n // *** k", opcode_name, " ***\n"); |
| // For the opcode and any child opcodes, add the semantic function and |
| // operand_setter_ lambda. |
| std::string code_str; |
| std::string sep = ""; |
| std::string operands_str; |
| for (auto const *inst = instruction; inst != nullptr; |
| inst = inst->child()) { |
| // Construct operand getter lookup key. |
| std::string key = CreateOperandLookupKey(inst->opcode()); |
| auto iter = operand_setter_name_map_.find(key); |
| // If the key is not found, create a new getter function, otherwise |
| // reuse the existing one. |
| if (iter == operand_setter_name_map_.end()) { |
| auto index = operand_setter_name_map_.size(); |
| std::string setter_name = |
| absl::StrCat(class_name, "SetOperands", index); |
| absl::StrAppend(&setter_functions_, |
| GenerateOperandSetterFcn(setter_name, encoding_type, |
| inst->opcode())); |
| iter = operand_setter_name_map_.insert(std::make_pair(key, setter_name)) |
| .first; |
| } |
| absl::StrAppend(&operands_str, sep, iter->second); |
| if (inst->semfunc_code_string().empty()) { |
| // If there is no code string, use the default one. |
| absl::StrAppend(&code_str, sep, |
| default_instruction_->semfunc_code_string()); |
| } else { |
| absl::StrAppend(&code_str, sep, inst->semfunc_code_string()); |
| } |
| sep = ", "; |
| } |
| absl::StrAppend(&output, " {OperandSetter{", operands_str, "},\n", |
| " ", GenerateDisassemblySetter(instruction), ",\n", |
| " ", GenerateResourceSetter(instruction, encoding_type), |
| ",\n", " ", GenerateAttributeSetter(instruction), ",\n", |
| " SemFuncSetter{", code_str, "}, ", |
| instruction->opcode()->instruction_size(), "},\n"); |
| } |
| return output; |
| } |
| |
| // Generate the class declaration, including all getter functions for semantic |
| // functions and operand initializers. |
| std::string Slot::GenerateClassDeclaration( |
| absl::string_view encoding_type) const { |
| std::string output; |
| if (!is_referenced()) return output; |
| std::string class_name = pascal_name() + "Slot"; |
| absl::StrAppend(&output, "class ", class_name, |
| " {\n" |
| " public:\n" |
| " explicit ", |
| class_name, "(ArchState *arch_state);\n"); |
| // Emit Decode function generated that decodes the slot and creates and |
| // initializes an instruction object, as well as private data members. |
| absl::StrAppend(&output, " Instruction *Decode(uint64_t address, ", |
| encoding_type, "* isa_encoding, SlotEnum, int entry);\n", |
| "\n" |
| " private:\n" |
| " ArchState *arch_state_;\n" |
| " std::array<InstructionInfo, ", |
| instruction_map_.size() + 1, "> instruction_info_", ";\n", |
| " static constexpr SlotEnum slot_ = SlotEnum::k", |
| pascal_name(), |
| ";\n" |
| "};\n" |
| "\n"); |
| return output; |
| } |
| |
| // Write out the class definition for the Slot class including all initializer |
| // bodies. |
| std::string Slot::GenerateClassDefinition(absl::string_view encoding_type) { |
| if (!is_referenced()) return ""; |
| std::string class_name = pascal_name() + "Slot"; |
| std::string output; |
| // Constructor. |
| absl::StrAppend( |
| &output, class_name, "::", class_name, |
| "(ArchState *arch_state) :\n" |
| " arch_state_(arch_state),\n", |
| " instruction_info_{{\n", ListFuncGetterInitializations(encoding_type), |
| "}} {}\n", |
| "\n" |
| "Instruction *", |
| class_name, "::Decode(uint64_t address, ", encoding_type, |
| " *isa_encoding, SlotEnum slot, int entry) {\n" |
| " OpcodeEnum opcode = isa_encoding->GetOpcode(slot, entry);\n" |
| " int indx = static_cast<int>(opcode);\n" |
| " auto &inst_info = instruction_info_[indx];\n" |
| " Instruction *inst = new Instruction(address, arch_state_);\n" |
| " inst->set_size(inst_info.instruction_size);\n" |
| " inst->set_opcode(static_cast<int>(opcode));\n" |
| " inst->set_semantic_function(inst_info.semfunc[0]);\n" |
| " inst_info.operand_setter[0](inst, isa_encoding, opcode, slot, " |
| "entry);\n" |
| " Instruction *parent = inst;\n" |
| " for (size_t i = 1; i < inst_info.operand_setter.size(); i++) {\n" |
| " Instruction *child = new Instruction(address, arch_state_);\n" |
| " child->set_semantic_function(inst_info.semfunc[i]);\n" |
| " inst_info.operand_setter[i](child, isa_encoding, opcode, slot, " |
| "entry);\n" |
| " parent->AppendChild(child);\n" |
| " child->DecRef();\n" |
| " parent = child;\n" |
| " }\n" |
| " inst_info.resource_setter(inst, isa_encoding, slot, entry);\n" |
| " inst_info.disassembly_setter(inst);\n" |
| " inst_info.attribute_setter(inst);\n" |
| " return inst;\n" |
| "}\n"); |
| // Prepend the setter functions. |
| std::string combined_output = absl::StrCat( |
| "namespace {\n\n", setter_functions_, "} // namespace\n\n", output); |
| return combined_output; |
| } |
| |
| absl::Status Slot::CheckPredecessors(const Slot *base) const { |
| if (predecessor_set_.contains(base)) |
| return absl::AlreadyExistsError( |
| absl::StrCat("'", base->name(), |
| "' is already in the predecessor set of '", name(), "'")); |
| for (auto const *pred : predecessor_set_) { |
| auto status = pred->CheckPredecessors(base); |
| if (!status.ok()) return status; |
| } |
| for (auto const *base_pred : base->predecessor_set_) { |
| auto status = CheckPredecessors(base_pred); |
| if (!status.ok()) return status; |
| } |
| return absl::OkStatus(); |
| } |
| |
| absl::Status Slot::AddBase(const Slot *base) { |
| // First need to check if the current slot already inherits from base, or |
| // any of base's predecessors. Only tree-like inheritance is supported. |
| auto status = CheckPredecessors(base); |
| if (!status.ok()) return status; |
| predecessor_set_.insert(base); |
| base_slots_.emplace_back(base); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status Slot::AddBase(const Slot *base, |
| TemplateInstantiationArgs *arguments) { |
| auto status = CheckPredecessors(base); |
| if (!status.ok()) return status; |
| predecessor_set_.insert(base); |
| base_slots_.emplace_back(base, arguments); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status Slot::AddConstant(const std::string &ident, |
| const std::string &type, |
| TemplateExpression *expression) { |
| // Ignore the type for now - there is only int. |
| (void)type; |
| // Check if the name already exists or matches a template formal parameter. |
| if (template_parameter_map_.contains(ident)) { |
| return absl::AlreadyExistsError( |
| absl::StrCat("Slot constant '", ident, |
| "' conflicts with template formal with same name")); |
| } |
| if (constant_map_.contains(ident)) { |
| return absl::AlreadyExistsError( |
| absl::StrCat("Redefinition of slot constant '", ident, "'")); |
| } |
| constant_map_.insert(std::make_pair(ident, expression)); |
| return absl::OkStatus(); |
| } |
| |
| TemplateExpression *Slot::GetConstExpression(const std::string &ident) const { |
| auto iter = constant_map_.find(ident); |
| if (iter == constant_map_.end()) return nullptr; |
| return iter->second; |
| } |
| |
| absl::Status Slot::AddTemplateFormal(const std::string &par_name) { |
| if (template_parameter_map_.contains(par_name)) { |
| // Push it into the vector, but not the map. Have the formal name refer to |
| // the previous index. Signal error. This allows us to properly match the |
| // number of parameters in each use of the templated slot, even though |
| // there is an error in the parameter names. |
| int indx = template_parameter_map_.at(par_name); |
| template_parameters_.push_back(new TemplateFormal(par_name, indx)); |
| return absl::InternalError( |
| absl::StrCat("Duplicate parameter name '", par_name, "'")); |
| } |
| int indx = template_parameters_.size(); |
| template_parameters_.push_back(new TemplateFormal(par_name, indx)); |
| template_parameter_map_.insert(std::make_pair(par_name, indx)); |
| return absl::OkStatus(); |
| } |
| |
| TemplateFormal *Slot::GetTemplateFormal(const std::string &name) const { |
| auto iter = template_parameter_map_.find(name); |
| if (iter == template_parameter_map_.end()) return nullptr; |
| return template_parameters_[iter->second]; |
| } |
| |
| void Slot::AddInstructionAttribute(const std::string &name, |
| TemplateExpression *expr) { |
| attribute_map_.emplace(name, expr); |
| } |
| |
| } // namespace instruction_set |
| } // namespace machine_description |
| } // namespace sim |
| } // namespace mpact |