Rewrote the instruction decoder code-generator. - Reduced amount of C++ code generated for instruction decoding (40-50%) - Significantly improves compile time of the generated decoders (up to 80%) PiperOrigin-RevId: 698135815 Change-Id: Ia34801dc4c9cb2412da741ce14f97d6ca2446bc3
diff --git a/mpact/sim/decoder/instruction_set.cc b/mpact/sim/decoder/instruction_set.cc index 267c80b..924a639 100644 --- a/mpact/sim/decoder/instruction_set.cc +++ b/mpact/sim/decoder/instruction_set.cc
@@ -369,7 +369,7 @@ std::string class_name = pascal_name() + "InstructionSet"; std::string factory_class_name = class_name + "Factory"; - for (auto const *slot : slot_order_) { + for (auto *slot : slot_order_) { absl::StrAppend(&output, slot->GenerateClassDefinition(encoding_type)); } // Constructor.
diff --git a/mpact/sim/decoder/instruction_set.h b/mpact/sim/decoder/instruction_set.h index 06ef836..e767325 100644 --- a/mpact/sim/decoder/instruction_set.h +++ b/mpact/sim/decoder/instruction_set.h
@@ -15,12 +15,13 @@ #ifndef MPACT_SIM_DECODER_INSTRUCTION_SET_H_ #define MPACT_SIM_DECODER_INSTRUCTION_SET_H_ -#include <deque> #include <memory> #include <string> #include <vector> +#include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" #include "mpact/sim/decoder/bundle.h" #include "mpact/sim/decoder/opcode.h"
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc index acd112a..14e9bdf 100644 --- a/mpact/sim/decoder/instruction_set_visitor.cc +++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -2225,28 +2225,25 @@ "opcode, ListDestOpEnum dest_op, int dest_no) { return {0}; }\n"); absl::StrAppend(&output, "};\n\n"); - absl::StrAppend( - &output, - "using OperandSetter = " - "std::vector<std::function<void(Instruction *, ", - encoding_base_name, - "*, SlotEnum, int)>>;\n" - "using DisassemblySetter = std::function<void(Instruction *)>;\n" - "using ResourceSetter = std::function<void(Instruction *, ", - encoding_base_name, - "*, SlotEnum, int)>;\n" - "using AttributeSetter = std::function<void(Instruction *)>;\n" - "struct InstructionInfo {\n" - " OperandSetter operand_setter;\n" - " DisassemblySetter disassembly_setter;\n" - " ResourceSetter resource_setter;\n" - " AttributeSetter attribute_setter;\n" - " std::vector<SemFunc> semfunc;\n" - " int instruction_size;\n" - "};\n" - "using InstructionInfoMap = absl::flat_hash_map<int, " - "InstructionInfo *>;\n" - "\n"); + absl::StrAppend(&output, + "using OperandSetter = " + "std::vector<void (*)(Instruction *, ", + encoding_base_name, + "*, OpcodeEnum, SlotEnum, int)>;\n" + "using DisassemblySetter = void(*)(Instruction *);\n" + "using ResourceSetter = void(*)(Instruction *, ", + encoding_base_name, + "*, SlotEnum, int);\n" + "using AttributeSetter = void(*)(Instruction *);\n" + "struct InstructionInfo {\n" + " OperandSetter operand_setter;\n" + " DisassemblySetter disassembly_setter;\n" + " ResourceSetter resource_setter;\n" + " AttributeSetter attribute_setter;\n" + " std::vector<SemFunc> semfunc;\n" + " int instruction_size;\n" + "};\n" + "\n"); return output; }
diff --git a/mpact/sim/decoder/mpact_sim_isa.bzl b/mpact/sim/decoder/mpact_sim_isa.bzl index d068a97..668817b 100644 --- a/mpact/sim/decoder/mpact_sim_isa.bzl +++ b/mpact/sim/decoder/mpact_sim_isa.bzl
@@ -106,16 +106,20 @@ ) # The rule for the lib that is built from the generated sources. + lib_deps = [] + if "@com_google_absl//absl/container:flat_hash_map" not in deps: + lib_deps.append("@com_google_absl//absl/container:flat_hash_map") + if "@com_google_absl//absl/strings:str_format" not in deps: + lib_deps.append("@com_google_absl//absl/strings:str_format") + if "@com_google_mpact-sim//mpact/sim/generic:arch_state" not in deps: + lib_deps.append("@com_google_mpact-sim//mpact/sim/generic:arch_state") + if "@com_google_mpact-sim//mpact/sim/generic:instruction" not in deps: + lib_deps.append("@com_google_mpact-sim//mpact/sim/generic:instruction") native.cc_library( name = name, srcs = [f for f in out_files if f.endswith(".cc")], hdrs = [f for f in out_files if f.endswith(".h")], - deps = [ - "@com_google_mpact-sim//mpact/sim/generic:arch_state", - "@com_google_mpact-sim//mpact/sim/generic:instruction", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/strings:str_format", - ] + deps, + deps = lib_deps + deps, ) def mpact_bin_fmt_decoder(name, includes, src = "", srcs = [], deps = [], decoder_name = "", prefix = ""): @@ -170,17 +174,22 @@ ) # The rule for the lib that is built from the generated sources. + lib_deps = [] + if "@com_google_absl//absl/container:flat_hash_map" not in deps: + lib_deps.append("@com_google_absl//absl/container:flat_hash_map") + if "@com_google_absl//absl/functional:any_invocable" not in deps: + lib_deps.append("@com_google_absl//absl/functional:any_invocable") + if "@com_google_absl//absl/strings:str_format" not in deps: + lib_deps.append("@com_google_absl//absl/strings:str_format") + if "@com_google_mpact-sim//mpact/sim/generic:arch_state" not in deps: + lib_deps.append("@com_google_mpact-sim//mpact/sim/generic:arch_state") + if "@com_google_mpact-sim//mpact/sim/generic:instruction" not in deps: + lib_deps.append("@com_google_mpact-sim//mpact/sim/generic:instruction") native.cc_library( name = name, srcs = [f for f in out_files if f.endswith(".cc")], hdrs = [f for f in out_files if f.endswith(".h")], - deps = [ - "@com_google_mpact-sim//mpact/sim/generic:arch_state", - "@com_google_mpact-sim//mpact/sim/generic:instruction", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/strings:str_format", - ] + deps, + deps = lib_deps + deps, ) def mpact_proto_fmt_decoder(name, includes, src = "", srcs = [], proto_files = [], deps = [], decoder_name = "", prefix = ""):
diff --git a/mpact/sim/decoder/slot.cc b/mpact/sim/decoder/slot.cc index 2b5edf0..9d996f9 100644 --- a/mpact/sim/decoder/slot.cc +++ b/mpact/sim/decoder/slot.cc
@@ -24,6 +24,7 @@ #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" @@ -210,50 +211,88 @@ static std::string indent_string(int n) { return std::string(n, ' '); } -std::string Slot::GenerateAttributeSetter(const Instruction *inst) const { - if (InstructionSet::attribute_names() == nullptr) { - return " info->attribute_setter = [](Instruction *inst) {};\n"; - } - std::string output; - absl::StrAppend(&output, - " info->attribute_setter = [](Instruction *inst) {\n"); - - // Allocate the array and initialize to zero. - absl::StrAppend( - &output, - " int size = static_cast<int>(AttributeEnum::kPastMaxValue);\n" - " int *attrs = new int[size];\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(&output, " #error Expression for '", name, - "' has no constant value\n"); + absl::StrAppend(&key, name, "[e1]:"); 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"); + auto *value_ptr = std::get_if<int>(&result.value()); + if (value_ptr == nullptr) { + absl::StrAppend(&key, name, "[e2]:"); continue; } - absl::StrAppend(&output, " attrs[static_cast<int>(AttributeEnum::k", - ToPascalCase(name), ")] = ", *value, ";\n"); + absl::StrAppend(&key, name, "[", *value_ptr, "]:"); } - absl::StrAppend(&output, - " inst->SetAttributes(absl::Span<int>(attrs, size));\n" - " };\n\n"); + 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; } -std::string Slot::GenerateDisassemblySetter(const Instruction *inst) const { - std::string output; - if (inst->disasm_format_vec().empty()) { - return " info->disassembly_setter = [](Instruction *) {};\n"; +// 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)); } - absl::StrAppend(&output, - " info->disassembly_setter = [](Instruction *inst) {\n" - " inst->SetDisassemblyString("); - int indent = 6; + return iter->second; +} + +// Generate a func tion 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 @@ -349,23 +388,130 @@ absl::StrAppend(&output, ")"); } - absl::StrAppend(&output, - ");\n" - " };\n\n"); + absl::StrAppend(&output, ");\n}\n\n"); return output; } -std::string Slot::GenerateResourceSetter( - const Instruction *inst, absl::string_view encoding_type) const { +// 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 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); - std::string signature = absl::StrCat("(Instruction *inst, ", encoding_type, - " *enc, SlotEnum slot, int entry)"); - absl::StrAppend(&output, " info->resource_setter = []", signature, " {\n"); if (!inst->resource_use_vec().empty() || !inst->resource_acquire_vec().empty()) { - absl::StrAppend(&output, " ResourceOperandInterface *res_op;\n"); + 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. @@ -383,8 +529,7 @@ 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 = {"); + absl::StrAppend(&output, " std::vector<SimpleResourceEnum> hold_vec = {"); for (auto const *simple : simple_refs) { std::string resource_name; if (simple->is_array) { @@ -394,15 +539,15 @@ resource_name = absl::StrCat("SimpleResourceEnum::k", simple->resource->pascal_name()); } - absl::StrAppend(&output, "\n ", resource_name, ", "); + absl::StrAppend(&output, "\n ", resource_name, ","); } absl::StrAppend(&output, - "};\n\n" - " res_op = enc->GetSimpleResourceOperand(slot, entry, ", + "};\n" + " res_op = enc->GetSimpleResourceOperand(slot, entry, ", opcode_enum, ", hold_vec, -1);\n", - " if (res_op != nullptr) {\n" - " inst->AppendResourceHold(res_op);\n" - " }\n"); + " if (res_op != nullptr) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); } // Complex resources. for (auto const *complex : complex_refs) { @@ -425,23 +570,23 @@ if (complex->is_array) { absl::StrAppend( &output, - " auto res_op_vec = enc->GetComplexResourceOperands(slot, entry, ", + " 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"); + " 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, + " 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"); + " if (res_op != nullptr) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); } } @@ -485,8 +630,7 @@ // Process the resources by latencies. for (auto latency : latencies) { std::string sep = ""; - absl::StrAppend(&output, - " std::vector<SimpleResourceEnum> acquire_vec", + absl::StrAppend(&output, " std::vector<SimpleResourceEnum> acquire_vec", latency, " = {"); for (auto iter = latency_map.lower_bound(latency); iter != latency_map.upper_bound(latency); ++iter) { @@ -499,17 +643,16 @@ resource_name = absl::StrCat("SimpleResourceEnum::k", simple->resource->pascal_name()); } - absl::StrAppend(&output, sep, "\n ", resource_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"); + 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"); } } @@ -531,15 +674,15 @@ int *end = std::get_if<int>(&end_value.value()); if (complex->is_array) { absl::StrAppend(&output, - " auto res_op_vec = " + " 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"); + " for (auto res_op : res_op_vec) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); } else { absl::StrAppend( &output, @@ -548,18 +691,161 @@ complex->resource->pascal_name(), ", "); absl::StrAppend(&output, *begin, ", ", *end, ");\n"); absl::StrAppend(&output, - " if (res_op != nullptr) {\n" - " inst->AppendResourceHold(res_op);\n" - " }\n"); + " if (res_op != nullptr) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); } } } - absl::StrAppend(&output, " };\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) const { + absl::string_view encoding_type) { std::string output; if (instruction_map_.empty()) return output; std::string class_name = pascal_name() + "Slot"; @@ -567,143 +853,67 @@ // 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, SlotEnum slot, int entry)"); - absl::StrAppend(&output, - " int index;\n" - " InstructionInfo *info;\n" - " // For kNone - unknown instruction.\n" - " index = static_cast<int>(OpcodeEnum::kNone);\n" - " info = new InstructionInfo;\n" - " info->instruction_size = ", - min_instruction_size(), - ";\n\n" - " info->operand_setter.push_back([]", - signature, - "{});\n" - " info->semfunc.push_back(", - default_instruction_->semfunc_code_string(), ");\n", - GenerateResourceSetter(default_instruction_, encoding_type), - GenerateDisassemblySetter(default_instruction_), - GenerateAttributeSetter(default_instruction_), - " instruction_info_.insert({index, info});\n"); + 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", " {", + 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 // *** ", opcode_name, " ***\n", - " index = static_cast<int>(", opcode_enum, - ");\n" - " info = new InstructionInfo;\n" - " info->instruction_size = ", - instruction->opcode()->instruction_size(), ";\n"); + 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()) { - std::string code_str; + // 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. - code_str = default_instruction_->semfunc_code_string(); + absl::StrAppend(&code_str, sep, + default_instruction_->semfunc_code_string()); } else { - code_str = inst->semfunc_code_string(); + absl::StrAppend(&code_str, sep, inst->semfunc_code_string()); } - absl::StrAppend(&output, " info->semfunc.push_back(", code_str, - ");\n" - " info->operand_setter.push_back([]", - signature, " {\n"); - // Generate code to set predicate operand, if the opcode has one. - const std::string &op_name = inst->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_enum, ", ", pred_op_enum, - "));\n"); - } - // Generate code to set the instruction's source operands. - int source_no = 0; - for (const auto &src_op : inst->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_enum, ", ", 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_enum, ", ", src_op_enum, - ", ", source_no++, "));\n"); - } - } - // Generate code to set the instruction's destination operands. - int dest_no = 0; - for (auto const *dst_op : inst->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_enum, - ", ", 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_enum, ", ", 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_enum, ", ", dest_op_enum, ", ", dest_no, - ", ", latency, "));\n"); - dest_no++; - } - dest_no++; - } - absl::StrAppend(&output, " });\n\n"); + sep = ", "; } - absl::StrAppend(&output, GenerateDisassemblySetter(instruction)); - absl::StrAppend(&output, - GenerateResourceSetter(instruction, encoding_type)); - absl::StrAppend(&output, GenerateAttributeSetter(instruction)); - absl::StrAppend(&output, " instruction_info_.insert({index, info});\n"); + absl::StrAppend(&output, " {OperandSetter{", operands_str, "},\n", + " ", GenerateDisassemblySetter(instruction), ",\n", + " ", GenerateResourceSetter(instruction, encoding_type), + ",\n", " ", GenerateAttributeSetter(instruction), ",\n", + " {", code_str, "}, ", + instruction->opcode()->instruction_size(), "},\n"); } + absl::StrAppend(&output, " }"); return output; } @@ -718,10 +928,7 @@ " {\n" " public:\n" " explicit ", - class_name, - "(ArchState *arch_state);\n" - " virtual ~", - class_name, "();\n"); + 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, ", @@ -729,7 +936,8 @@ "\n" " private:\n" " ArchState *arch_state_;\n" - " InstructionInfoMap instruction_info_;\n", + " std::array<InstructionInfo, ", + instruction_map_.size() + 1, "> instruction_info_", ";\n", " static constexpr SlotEnum slot_ = SlotEnum::k", pascal_name(), ";\n" @@ -740,8 +948,7 @@ // Write out the class definition for the Slot class including all initializer // bodies. -std::string Slot::GenerateClassDefinition( - absl::string_view encoding_type) const { +std::string Slot::GenerateClassDefinition(absl::string_view encoding_type) { if (!is_referenced()) return ""; std::string class_name = pascal_name() + "Slot"; std::string output; @@ -749,46 +956,41 @@ absl::StrAppend( &output, class_name, "::", class_name, "(ArchState *arch_state) :\n" - " arch_state_(arch_state)\n" - "{\n", - ListFuncGetterInitializations(encoding_type), - "}\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" - " if (!instruction_info_.contains(indx)) indx = 0;\n" - " auto *inst_info = instruction_info_[indx];\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_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, slot, entry);\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" + " 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, slot, entry);\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" + " 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"); - // Destructor. - absl::StrAppend(&output, class_name, "::~", class_name, - "() {\n" - " for (auto &[unused, info_ptr] : instruction_info_) {\n" - " delete info_ptr;\n" - " };\n" - " instruction_info_.clear();\n" - "}\n"); - return output; + // 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 { @@ -855,8 +1057,8 @@ 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. + // 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(
diff --git a/mpact/sim/decoder/slot.h b/mpact/sim/decoder/slot.h index 888b584..79cb6ea 100644 --- a/mpact/sim/decoder/slot.h +++ b/mpact/sim/decoder/slot.h
@@ -75,7 +75,7 @@ // Return string for the header file declarations for this class. std::string GenerateClassDeclaration(absl::string_view encoding_type) const; // Return string for the .cc file definitions for this class. - std::string GenerateClassDefinition(absl::string_view encoding_type) const; + std::string GenerateClassDefinition(absl::string_view encoding_type); // Add a non-templated slot as a base. absl::Status AddBase(const Slot *base); @@ -122,8 +122,7 @@ const std::string &name() const { return name_; } const std::string &pascal_name() const { return pascal_name_; } const std::vector<BaseSlot> &base_slots() const { return base_slots_; } - const absl::flat_hash_map<std::string, Instruction *> &instruction_map() - const { + const absl::btree_map<std::string, Instruction *> &instruction_map() const { return instruction_map_; } const std::vector<TemplateFormal *> &template_parameters() const { @@ -143,16 +142,39 @@ } private: - std::string GenerateAttributeSetter(const Instruction *inst) const; - std::string GenerateDisassemblySetter(const Instruction *inst) const; + // These functions generate the functions that are called by the decoder to + // set the instruction operands. + std::string CreateOperandLookupKey(const Opcode *opcode) const; + std::string GenerateOperandSetterFcn(absl::string_view getter_name, + absl::string_view encoding_type, + const Opcode *opcode) const; + // These functions generate the functions that are called by the decoder to + // set the instruction resources. + std::string CreateResourceKey( + const std::vector<const ResourceReference *> &refs) const; std::string GenerateResourceSetter(const Instruction *inst, - absl::string_view encoding_type) const; + absl::string_view encoding_type); + std::string GenerateResourceSetterFcn(absl::string_view name, + const Instruction *inst, + absl::string_view encoding_type) const; + // These functions generate the functions that are called by the decoder to + // set the instruction disassembly string. + std::string GenerateDisassemblySetter(const Instruction *inst); + std::string GenerateDisasmSetterFcn(absl::string_view name, + const Instruction *inst) const; + // These functions generate the functions that are called by the decoder to + // set the instruction attributes. + std::string GenerateAttributeSetter(const Instruction *inst); + std::string GenerateAttributeSetterFcn(absl::string_view name, + const Instruction *inst) const; + std::string CreateAttributeLookupKey(const Instruction *inst) const; + // Generates a string that is a unique key for the operands to determine which + // instructions can share operand getter functions. // Build up a string containing the function getter initializers that are // stored in two flat hash maps with the opcode as the key. These functions // are lambda's that call the getters for the semantic functions as well as // operand getters for each instruction opcode. - std::string ListFuncGetterInitializations( - absl::string_view encoding_type) const; + std::string ListFuncGetterInitializations(absl::string_view encoding_type); // Transitively check if base slot is in the predecessor set of the current // slot or any of its inheritance predecessors. Returns AlreadyExistsError // if the current slot or its predecessors already inherit from base or its @@ -183,12 +205,18 @@ // Pointer to slot it inherits from. std::vector<BaseSlot> base_slots_; absl::flat_hash_set<const Slot *> predecessor_set_; + // Map from operand getter key to operand getter function name. + absl::flat_hash_map<std::string, std::string> operand_setter_name_map_; + absl::flat_hash_map<std::string, std::string> disasm_setter_name_map_; + absl::flat_hash_map<std::string, std::string> resource_setter_name_map_; + absl::flat_hash_map<std::string, std::string> attribute_setter_name_map_; + std::string setter_functions_; // Used to list the unique getters for the operands. absl::flat_hash_set<std::string> pred_operand_getters_; absl::flat_hash_set<std::string> src_operand_getters_; absl::flat_hash_set<std::string> dst_operand_getters_; // Map of instructions defined in this slot or inherited. - absl::flat_hash_map<std::string, Instruction *> instruction_map_; + absl::btree_map<std::string, Instruction *> instruction_map_; absl::flat_hash_set<std::string> operand_setters_; // Template parameter names. std::vector<TemplateFormal *> template_parameters_;
diff --git a/mpact/sim/decoder/test/instruction_set_visitor_test.cc b/mpact/sim/decoder/test/instruction_set_visitor_test.cc index d5b0e74..6e0342f 100644 --- a/mpact/sim/decoder/test/instruction_set_visitor_test.cc +++ b/mpact/sim/decoder/test/instruction_set_visitor_test.cc
@@ -16,7 +16,6 @@ #include <cstdlib> #include <fstream> -#include <istream> #include <iterator> #include <string> #include <vector> @@ -24,7 +23,7 @@ #include "absl/log/check.h" #include "absl/log/log_sink_registry.h" #include "absl/strings/str_cat.h" -#include "googlemock/include/gmock/gmock.h" +#include "googlemock/include/gmock/gmock.h" // IWYU pragma: keep #include "googletest/include/gtest/gtest.h" #include "mpact/sim/decoder/test/log_sink.h" #include "re2/re2.h"