No public description

PiperOrigin-RevId: 753741219
Change-Id: I1fde3de2a94a2eebd52450c794c4dd2044d470a6
diff --git a/mpact/sim/decoder/BUILD b/mpact/sim/decoder/BUILD
index 536ba70..7f22b2d 100644
--- a/mpact/sim/decoder/BUILD
+++ b/mpact/sim/decoder/BUILD
@@ -121,6 +121,7 @@
         "@com_google_absl//absl/container:btree",
         "@com_google_absl//absl/container:flat_hash_map",
         "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/flags:flag",
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/memory",
         "@com_google_absl//absl/numeric:bits",
diff --git a/mpact/sim/decoder/BinFormat.g4 b/mpact/sim/decoder/BinFormat.g4
index 890ebc8..41473ba 100644
--- a/mpact/sim/decoder/BinFormat.g4
+++ b/mpact/sim/decoder/BinFormat.g4
@@ -22,7 +22,11 @@
 grammar BinFormat;
 
 top_level
-  : declaration_list EOF
+  : once? declaration_list EOF
+  ;
+
+once
+  : '#' ONCE
   ;
 
 declaration_list
@@ -272,6 +276,7 @@
 LAYOUT : 'layout';
 DEFAULT : 'default';
 PACKED_STRUCT : 'packed_struct';
+ONCE : 'once';
 
 // Other tokens.
 STRING_LITERAL : UNTERMINATED_STRING_LITERAL '"';
diff --git a/mpact/sim/decoder/bin_format_contexts.h b/mpact/sim/decoder/bin_format_contexts.h
index 8457626..67b4560 100644
--- a/mpact/sim/decoder/bin_format_contexts.h
+++ b/mpact/sim/decoder/bin_format_contexts.h
@@ -40,6 +40,7 @@
 using InstructionDefCtx = BinFormatParser::Instruction_defContext;
 using NamespaceCtx = BinFormatParser::Namespace_declContext;
 using NumberCtx = BinFormatParser::NumberContext;
+using OnceCtx = BinFormatParser::OnceContext;
 using OverlayDefCtx = BinFormatParser::Overlay_defContext;
 using TopLevelCtx = BinFormatParser::Top_levelContext;
 
diff --git a/mpact/sim/decoder/bin_format_visitor.cc b/mpact/sim/decoder/bin_format_visitor.cc
index 702e004..75e5477 100644
--- a/mpact/sim/decoder/bin_format_visitor.cc
+++ b/mpact/sim/decoder/bin_format_visitor.cc
@@ -628,24 +628,29 @@
                                         std::vector<std::string> const &dirs) {
   std::fstream include_file;
   // Open include file.
-  include_file.open(file_name, std::fstream::in);
+  // Try each of the include file directories.
+  std::string include_name;
+  for (auto const &dir : dirs) {
+    include_name = absl::StrCat(dir, "/", file_name);
+    include_file.open(include_name, std::fstream::in);
+    if (include_file.is_open()) break;
+  }
   if (!include_file.is_open()) {
-    // Try each of the include file directories.
-    for (auto const &dir : dirs) {
-      std::string include_name = absl::StrCat(dir, "/", file_name);
-      include_file.open(include_name, std::fstream::in);
-      if (include_file.is_open()) break;
-    }
+    // Try a local file.
+    include_name = file_name;
+    include_file.open(include_name, std::fstream::in);
     if (!include_file.is_open()) {
-      // Try a local file.
-      include_file.open(file_name, std::fstream::in);
-      if (!include_file.is_open()) {
-        error_listener()->semanticError(
-            ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
-        return;
-      }
+      error_listener()->semanticError(
+          ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
+      return;
     }
   }
+  // See if this file has been included before it had a "#once" declaration. If
+  // so, then don't include it again.
+  if (once_include_files_.contains(include_name)) {
+    include_file.close();
+    return;
+  }
   std::string previous_file_name = error_listener()->file_name();
   int previous_file_index_ = current_file_index_;
   error_listener()->set_file_name(file_name);
@@ -658,9 +663,9 @@
   // Add the error listener.
   include_parser->parser()->removeErrorListeners();
   include_parser->parser()->addErrorListener(error_listener());
-  // Start parsing at the declaratition_list_w_eof rule.
-  DeclarationListCtx *declaration_list =
-      include_parser->parser()->top_level()->declaration_list();
+  auto *top_level = include_parser->parser()->top_level();
+  // Start parsing at the declaratition_list rule.
+  DeclarationListCtx *declaration_list = top_level->declaration_list();
   include_file.close();
   if (error_listener()->syntax_error_count() > 0) {
     error_listener()->set_file_name(previous_file_name);
@@ -669,6 +674,11 @@
   }
   include_file_stack_.push_back(file_name);
   PreProcessDeclarations(declaration_list);
+  // See if there is a once declaration in the file.
+  OnceCtx *once_ctx = top_level->once();
+  if (once_ctx != nullptr) {
+    once_include_files_.insert(include_name);
+  }
   include_file_stack_.pop_back();
   error_listener()->set_file_name(previous_file_name);
   current_file_index_ = previous_file_index_;
diff --git a/mpact/sim/decoder/bin_format_visitor.h b/mpact/sim/decoder/bin_format_visitor.h
index 82b49ab..b824b34 100644
--- a/mpact/sim/decoder/bin_format_visitor.h
+++ b/mpact/sim/decoder/bin_format_visitor.h
@@ -181,6 +181,8 @@
   std::vector<BinFmtAntlrParserWrapper *> antlr_parser_wrappers_;
   // Map from comparator string to constraint type.
   absl::flat_hash_map<std::string, ConstraintType> constraint_string_to_type_;
+  // Set of include files marked as once.
+  absl::flat_hash_set<std::string> once_include_files_;
   // Specializations to process after all instructions have been processed.
   std::vector<InstructionDefCtx *> specializations_;
 };
diff --git a/mpact/sim/decoder/format.cc b/mpact/sim/decoder/format.cc
index 59f2164..5005388 100644
--- a/mpact/sim/decoder/format.cc
+++ b/mpact/sim/decoder/format.cc
@@ -896,7 +896,7 @@
   if (declared_width_ > 128) {
     arg_type = "const uint8_t *";
   } else {
-    std::string arg_type = GetUIntType(declared_width_);
+    arg_type = GetUIntType(declared_width_);
   }
   std::string return_type = overlay->is_signed()
                                 ? GetIntType(overlay->declared_width())
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc
index 0ce2e00..ed68626 100644
--- a/mpact/sim/decoder/instruction_set_visitor.cc
+++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -30,6 +30,7 @@
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
+#include "absl/flags/flag.h"
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "absl/strings/str_cat.h"
@@ -45,6 +46,11 @@
 #include "mpact/sim/decoder/template_expression.h"
 #include "re2/re2.h"
 
+// This flag is used to set the version of the generated code. Version 1 is
+// the default version. Version 2 adds an instruction pointer to the resource
+// and operator functions in the EncodingInterface.
+ABSL_FLAG(unsigned, generator, 1, "Version of generated code");
+
 namespace mpact {
 namespace sim {
 namespace machine_description {
@@ -90,6 +96,7 @@
     const std::vector<std::string> &file_names, const std::string &prefix,
     const std::string &isa_name, const std::vector<std::string> &include_roots,
     absl::string_view directory) {
+  generator_version_ = absl::GetFlag(FLAGS_generator);
   // Create and add the error listener.
   set_error_listener(std::make_unique<decoder::DecoderErrorListener>());
   if (isa_name.empty()) {
@@ -644,8 +651,8 @@
 void InstructionSetVisitor::VisitSlotDeclaration(
     SlotDeclCtx *ctx, InstructionSet *instruction_set) {
   bool is_templated = ctx->template_decl() != nullptr;
-  Slot *slot =
-      new Slot(ctx->slot_name->getText(), instruction_set, is_templated, ctx);
+  Slot *slot = new Slot(ctx->slot_name->getText(), instruction_set,
+                        is_templated, ctx, generator_version_);
   if (is_templated) {
     for (auto const &param : ctx->template_decl()->template_parameter_decl()) {
       auto status = slot->AddTemplateFormal(param->IDENT()->getText());
@@ -2020,6 +2027,7 @@
       format_info->is_formatted = false;
       if ((pos != std::string::npos) && (format[pos] == '?')) {
         format_info->is_optional = true;
+        disasm_fmt->num_optional++;
         pos++;
         if (pos >= format.size()) {
           pos = std::string::npos;
@@ -2298,54 +2306,71 @@
   absl::StrAppend(
       &output,
       "  virtual OpcodeEnum GetOpcode(SlotEnum slot, int entry) = 0;\n");
+  std::string optional_instruction;
+  if (generator_version_ == 2) {
+    optional_instruction = "Instruction *inst, ";
+  }
   // Get resource methods.
   absl::StrAppend(
       &output, "  virtual ResourceOperandInterface *GetSimpleResourceOperand",
-      "(SlotEnum slot, int entry, OpcodeEnum opcode, SimpleResourceVector "
+      "(", optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum opcode, SimpleResourceVector "
       "&resource_vec, int end) { return nullptr;}\n");
   absl::StrAppend(
       &output,
       "  virtual ResourceOperandInterface * "
       "GetComplexResourceOperand",
-      "(SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum "
+      "(", optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum "
       "resource_op, int begin, int end) { return nullptr; }\n");
   absl::StrAppend(
       &output,
       "  virtual std::vector<ResourceOperandInterface *> "
       "GetComplexResourceOperands",
-      "(SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum "
+      "(", optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum "
       "resource_op, int begin, int end) { return {}; }\n");
   // For each operand type, declare the pure virtual method that returns the
   // given operand.
   absl::StrAppend(&output,
                   "  virtual PredicateOperandInterface *GetPredicate"
-                  "(SlotEnum slot, int entry, OpcodeEnum opcode, PredOpEnum "
+                  "(",
+                  optional_instruction,
+                  "SlotEnum slot, int entry, OpcodeEnum opcode, PredOpEnum "
                   "pred_op) { return nullptr; }\n");
   absl::StrAppend(&output,
                   "  virtual SourceOperandInterface *GetSource"
-                  "(SlotEnum slot, int entry, OpcodeEnum opcode, SourceOpEnum "
+                  "(",
+                  optional_instruction,
+                  "SlotEnum slot, int entry, OpcodeEnum opcode, SourceOpEnum "
                   "source_op, int source_no) { return nullptr;}\n");
   absl::StrAppend(
       &output,
       "  virtual std::vector<SourceOperandInterface *> GetSources"
-      "(SlotEnum slot, int entry, OpcodeEnum opcode, ListSourceOpEnum "
+      "(",
+      optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum opcode, ListSourceOpEnum "
       "list_source_op, int source_no) { return {};}\n");
   absl::StrAppend(&output,
                   "  virtual DestinationOperandInterface *GetDestination"
-                  "(SlotEnum slot, int entry, OpcodeEnum opcode, "
+                  "(",
+                  optional_instruction,
+                  "SlotEnum slot, int entry, OpcodeEnum opcode, "
                   "DestOpEnum list_dest_op, int dest_no, int latency)"
                   " { return nullptr; }\n");
   absl::StrAppend(
       &output,
       "  virtual std::vector<DestinationOperandInterface *> GetDestinations"
-      "(SlotEnum slot, int entry, OpcodeEnum opcode, "
+      "(",
+      optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum opcode, "
       "ListDestOpEnum dest_op, int dest_no, const std::vector<int> &latency)"
       " { return {}; };\n");
   // Destination operand latency getter for destination operands with '*'
   // as latency.
   absl::StrAppend(
-      &output,
-      "  virtual int GetLatency(SlotEnum slot, int entry, OpcodeEnum "
+      &output, "  virtual int GetLatency(", optional_instruction,
+      "SlotEnum slot, int entry, OpcodeEnum "
       "opcode, DestOpEnum dest_op, int dest_no) { return 0; };\n",
       "  virtual std::vector<int> GetLatency(SlotEnum slot, int entry, "
       "OpcodeEnum "
diff --git a/mpact/sim/decoder/instruction_set_visitor.h b/mpact/sim/decoder/instruction_set_visitor.h
index a07edc5..0ae5408 100644
--- a/mpact/sim/decoder/instruction_set_visitor.h
+++ b/mpact/sim/decoder/instruction_set_visitor.h
@@ -217,6 +217,7 @@
   absl::btree_set<std::string> include_files_;
 
   int current_file_index_ = 0;
+  unsigned generator_version_;
   // Vector of file names.
   std::vector<std::string> file_names_;
   // Map from context pointer to file index.
diff --git a/mpact/sim/decoder/mpact_sim_isa.bzl b/mpact/sim/decoder/mpact_sim_isa.bzl
index efcb6f0..e714257 100644
--- a/mpact/sim/decoder/mpact_sim_isa.bzl
+++ b/mpact/sim/decoder/mpact_sim_isa.bzl
@@ -51,7 +51,7 @@
         data = data,
     )
 
-def mpact_isa_decoder(name, includes, src = "", srcs = [], deps = [], isa_name = "", prefix = "", testonly = False):
+def mpact_isa_decoder(name, includes, src = "", srcs = [], deps = [], isa_name = "", prefix = "", generator = 1, testonly = False):
     """Generates the C++ source corresponding to an MPACT Isa decoder definition.
 
     Args:
@@ -62,6 +62,8 @@
       includes: Include .isa files.
       isa_name: Name of isa to generate code for.
       prefix: File prefix for the generated files (otherwise uses base name of src file
+      generator: Version of code to generate.
+      testonly: Whether the generated code is only to be used in test.
     """
 
     # if src is not empty, prepend it to the srcs list.
@@ -92,7 +94,7 @@
 
     # The command to generate the files.
     command = ";\n".join([
-        _make_isa_tool_invocation_command(len(isa_srcs), base_file_prefix, isa_name),
+        _make_isa_tool_invocation_command(len(isa_srcs), base_file_prefix, isa_name, generator),
     ])
 
     # The rule for the generated sources.
@@ -291,13 +293,13 @@
 # files, including those that will be included, the command includes creating
 # a bash array from $(SRCS), then instead of using $(SRCS) in the command, it
 # uses only the first element of that array.
-def _make_isa_tool_invocation_command(num_srcs, prefix, isa_name):
+def _make_isa_tool_invocation_command(num_srcs, prefix, isa_name, generator):
     cmd = "ARR=($(SRCS)); $(location @com_google_mpact-sim//mpact/sim/decoder:decoder_gen) "
 
     # Add the sources that are not in includes.
     for i in range(0, num_srcs):
         cmd += "$${ARR[" + str(i) + "]} "
-    cmd += "--prefix " + prefix + " --output_dir $(@D) --include $$(dirname $${ARR[0]})"
+    cmd += "--prefix " + prefix + " --generator " + str(generator) + " --output_dir $(@D) --include $$(dirname $${ARR[0]})"
     if isa_name != "":
         cmd += " --isa_name " + isa_name
 
diff --git a/mpact/sim/decoder/opcode.h b/mpact/sim/decoder/opcode.h
index 4921df6..e616a96 100644
--- a/mpact/sim/decoder/opcode.h
+++ b/mpact/sim/decoder/opcode.h
@@ -175,6 +175,7 @@
     format_info_vec.clear();
   }
   int width = 0;
+  int num_optional = 0;
   std::vector<std::string> format_fragment_vec;
   std::vector<FormatInfo *> format_info_vec;
 };
diff --git a/mpact/sim/decoder/overlay.cc b/mpact/sim/decoder/overlay.cc
index 7842bec..cdc1f0c 100644
--- a/mpact/sim/decoder/overlay.cc
+++ b/mpact/sim/decoder/overlay.cc
@@ -284,7 +284,7 @@
       absl::StrCat("const ", ToSnakeCase(format_->name()), "::Union",
                    ToPascalCase(format_->name()));
   absl::StrAppend(&output, "  ", union_type,
-                  " packed_union;\n"
+                  " *packed_union;\n"
                   "  packed_union = reinterpret_cast<",
                   union_type, "*>(",
                   format_->declared_width() > 64 ? "value);\n" : "&value);\n");
diff --git a/mpact/sim/decoder/slot.cc b/mpact/sim/decoder/slot.cc
index c4c54f8..0918d6e 100644
--- a/mpact/sim/decoder/slot.cc
+++ b/mpact/sim/decoder/slot.cc
@@ -134,9 +134,10 @@
 }
 
 Slot::Slot(absl::string_view name, InstructionSet *instruction_set,
-           bool is_templated, SlotDeclCtx *ctx)
+           bool is_templated, SlotDeclCtx *ctx, unsigned generator_version)
     : instruction_set_(instruction_set),
       ctx_(ctx),
+      generator_version_(generator_version),
       is_templated_(is_templated),
       name_(name),
       pascal_name_(ToPascalCase(name)) {}
@@ -904,6 +905,10 @@
       !inst->resource_acquire_vec().empty()) {
     absl::StrAppend(&output, "  ResourceOperandInterface *res_op;\n");
   }
+  std::string optional_inst;
+  if (generator_version_ == 2) {
+    optional_inst = "inst, ";
+  }
   // 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;
@@ -934,8 +939,9 @@
     }
     absl::StrAppend(&output,
                     "};\n"
-                    "  res_op = enc->GetSimpleResourceOperand(slot, entry, ",
-                    opcode_enum, ", hold_vec, -1);\n",
+                    "  res_op = enc->GetSimpleResourceOperand(",
+                    optional_inst, "slot, entry, ", opcode_enum,
+                    ", hold_vec, -1);\n",
                     "  if (res_op != nullptr) {\n"
                     "    inst->AppendResourceHold(res_op);\n"
                     "  }\n");
@@ -960,18 +966,18 @@
     }
     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");
+          &output, "  auto res_op_vec = enc->GetComplexResourceOperands(",
+          optional_inst, "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",
+      absl::StrAppend(&output, "  res_op = enc->GetComplexResourceOperand(",
+                      optional_inst, "slot, entry, ", opcode_enum,
+                      ", ComplexResourceEnum::k",
                       complex->resource->pascal_name(), ", ");
       absl::StrAppend(&output, *begin, ", ", *end, ");\n");
       absl::StrAppend(&output,
@@ -1038,8 +1044,9 @@
       }
       absl::StrAppend(&output,
                       "};\n\n"
-                      "  res_op = enc->GetSimpleResourceOperand(slot, entry, ",
-                      opcode_enum, ", acquire_vec", latency, ", ", latency,
+                      "  res_op = enc->GetSimpleResourceOperand(",
+                      optional_inst, "slot, entry, ", opcode_enum,
+                      ", acquire_vec", latency, ", ", latency,
                       ");\n"
                       "  if (res_op != nullptr) {\n"
                       "    inst->AppendResourceAcquire(res_op);\n"
@@ -1066,8 +1073,9 @@
       if (complex->is_array) {
         absl::StrAppend(&output,
                         "  auto res_op_vec = "
-                        "enc->GetComplexResourceOperands(slot, entry, ",
-                        opcode_enum, ", ListComplexResourceEnum::k",
+                        "enc->GetComplexResourceOperands(",
+                        optional_inst, "slot, entry, ", opcode_enum,
+                        ", ListComplexResourceEnum::k",
                         complex->resource->pascal_name(), *begin, ", ", *end,
                         ");\n");
         absl::StrAppend(&output,
@@ -1075,11 +1083,10 @@
                         "    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(",
+                        optional_inst, "slot, entry, ", opcode_enum,
+                        ", ComplexResourceEnum::k",
+                        complex->resource->pascal_name(), ", ");
         absl::StrAppend(&output, *begin, ", ", *end, ");\n");
         absl::StrAppend(&output,
                         "  if (res_op != nullptr) {\n"
@@ -1152,6 +1159,10 @@
                                            absl::string_view encoding_type,
                                            const Opcode *opcode) const {
   std::string output;
+  std::string optional_inst;
+  if (generator_version_ == 2) {
+    optional_inst = "inst, ";
+  }
   absl::StrAppend(&output, "void ", getter_name, "(Instruction *inst, ",
                   encoding_type,
                   " *enc, OpcodeEnum opcode, SlotEnum slot, int entry) {\n");
@@ -1160,8 +1171,9 @@
   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");
+    absl::StrAppend(&output, "  inst->SetPredicate(enc->GetPredicate", "(",
+                    optional_inst, "slot, entry, opcode, ", pred_op_enum,
+                    "));\n");
   }
   // Generate code to set the instruction's source operands.
   int source_no = 0;
@@ -1174,16 +1186,17 @@
       absl::StrAppend(&output,
                       "  {\n"
                       "    auto vec = enc->GetSources",
-                      "(slot, entry, opcode, ", src_op_enum, ", ", source_no,
+                      "(", optional_inst, "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");
+      absl::StrAppend(&output, "  inst->AppendSource(enc->GetSource", "(",
+                      optional_inst, "slot, entry, opcode, ", src_op_enum, ", ",
+                      source_no++, "));\n");
     }
   }
   // Generate code to set the instruction's destination operands.
@@ -1198,8 +1211,9 @@
     }
     std::string latency;
     if (dst_op->expression() == nullptr) {
-      latency = absl::StrCat("enc->GetLatency(slot, entry, opcode, ",
-                             dest_op_enum, ", ", dest_no, ")");
+      latency = absl::StrCat("enc->GetLatency(", optional_inst,
+                             "slot, entry, opcode, ", dest_op_enum, ", ",
+                             dest_no, ")");
     } else {
       auto result = dst_op->GetLatency();
       if (!result.ok()) {
@@ -1218,15 +1232,15 @@
       absl::StrAppend(&output,
                       "  {\n"
                       "    auto vec = enc->GetDestinations",
-                      "(slot, entry, opcode, ", dest_op_enum, ", ", dest_no,
-                      ", ", latency,
+                      "(", optional_inst, "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");
+                      optional_inst, "slot, entry, opcode, ", dest_op_enum,
+                      ", ", dest_no, ", ", latency, "));\n");
       dest_no++;
     }
     dest_no++;
diff --git a/mpact/sim/decoder/slot.h b/mpact/sim/decoder/slot.h
index 1f074d0..d6a9367 100644
--- a/mpact/sim/decoder/slot.h
+++ b/mpact/sim/decoder/slot.h
@@ -62,7 +62,10 @@
 
   // Constructor and destructor.
   Slot(absl::string_view name, InstructionSet *instruction_set,
-       bool is_templated, SlotDeclCtx *ctx);
+       bool is_templated, SlotDeclCtx *ctx, unsigned generator_version);
+  Slot(absl::string_view name, InstructionSet *instruction_set,
+       bool is_templated, SlotDeclCtx *ctx)
+      : Slot(name, instruction_set, is_templated, ctx, 1) {}
   ~Slot();
 
   // Add declared opcode to the current slot.
@@ -208,6 +211,7 @@
   Instruction *default_instruction_ = nullptr;
   // Number of instances of this slot in the instruction_set instruction word.
   int size_ = 1;
+  unsigned generator_version_;
   // True if the slot is a templated slot.
   bool is_templated_;
   bool is_marked_ = false;
diff --git a/mpact/sim/decoder/test/instruction_set_test.cc b/mpact/sim/decoder/test/instruction_set_test.cc
index 9797e87..1d70b48 100644
--- a/mpact/sim/decoder/test/instruction_set_test.cc
+++ b/mpact/sim/decoder/test/instruction_set_test.cc
@@ -16,7 +16,7 @@
 
 #include <memory>
 
-#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/bundle.h"
 #include "mpact/sim/decoder/slot.h"
diff --git a/mpact/sim/generic/data_buffer.h b/mpact/sim/generic/data_buffer.h
index 12f2352..a2c70ae 100644
--- a/mpact/sim/generic/data_buffer.h
+++ b/mpact/sim/generic/data_buffer.h
@@ -19,6 +19,7 @@
 #include <cstring>
 #include <vector>
 
+#include "absl/base/macros.h"
 #include "absl/container/btree_map.h"
 #include "absl/types/span.h"
 #include "mpact/sim/generic/delay_line.h"