No public description

PiperOrigin-RevId: 748351895
Change-Id: I13e5aad8d4a7dd5e40716e99d75cc785f13546a5
diff --git a/mpact/sim/decoder/BinFormat.g4 b/mpact/sim/decoder/BinFormat.g4
index c09440f..890ebc8 100644
--- a/mpact/sim/decoder/BinFormat.g4
+++ b/mpact/sim/decoder/BinFormat.g4
@@ -37,13 +37,23 @@
 // "inheritance" in this context only refers to the width, and helps group
 // formats in a hierarchy.
 format_def
-  : FORMAT name=IDENT width=index? inherits_from? '{' format_field_defs'}' ';'?
+  : FORMAT name=IDENT width=index? inherits_from? 
+    '{' layout_spec? format_field_defs'}' ';'?
   ;
 
 inherits_from
   : ':' IDENT
   ;
 
+layout_spec
+  : LAYOUT ':' layout_type ';'
+  ;
+
+layout_type
+  : DEFAULT
+  | PACKED_STRUCT
+  ;
+
 // Each format consists of a number of entries whose widths sum up to the
 // format widths. Each entry is either a field, or a sub-format (creating
 // a hierarchy), with the width specified with array notation. Fields and/or
@@ -259,6 +269,9 @@
 GROUP : 'group';
 DECODER : 'decoder';
 SPECIALIZES : 'specializes';
+LAYOUT : 'layout';
+DEFAULT : 'default';
+PACKED_STRUCT : 'packed_struct';
 
 // Other tokens.
 STRING_LITERAL : UNTERMINATED_STRING_LITERAL '"';
diff --git a/mpact/sim/decoder/bin_format_visitor.cc b/mpact/sim/decoder/bin_format_visitor.cc
index 97351f3..702e004 100644
--- a/mpact/sim/decoder/bin_format_visitor.cc
+++ b/mpact/sim/decoder/bin_format_visitor.cc
@@ -203,32 +203,40 @@
   std::string enc_dot_h_name = absl::StrCat(prefix, "_bin_encoder.h");
   std::string enc_dot_cc_name = absl::StrCat(prefix, "_bin_encoder.cc");
   std::string enum_dot_h_name = absl::StrCat(prefix, "_enums.h");
+  std::string types_dot_h_name = absl::StrCat(prefix, "_bin_types.h");
   std::ofstream dec_dot_h_file(absl::StrCat(directory, "/", dec_dot_h_name));
   std::ofstream dec_dot_cc_file(absl::StrCat(directory, "/", dec_dot_cc_name));
   std::ofstream enc_dot_h_file(absl::StrCat(directory, "/", enc_dot_h_name));
   std::ofstream enc_dot_cc_file(absl::StrCat(directory, "/", enc_dot_cc_name));
+  std::ofstream types_dot_h_file(
+      absl::StrCat(directory, "/", types_dot_h_name));
 
-  auto [dec_h_output, dec_cc_output] =
-      EmitDecoderFilePrefix(dec_dot_h_name, encoding_info.get());
+  auto [dec_h_output, dec_cc_output, types_h_output] = EmitDecoderFilePrefix(
+      dec_dot_h_name, types_dot_h_name, encoding_info.get());
   dec_dot_h_file << dec_h_output;
   dec_dot_cc_file << dec_cc_output;
+  types_dot_h_file << types_h_output;
   auto [enc_h_output, enc_cc_output] = EmitEncoderFilePrefix(
-      enc_dot_h_name, enum_dot_h_name, encoding_info.get());
+      enc_dot_h_name, enum_dot_h_name, types_dot_h_name, encoding_info.get());
   enc_dot_h_file << enc_h_output;
   enc_dot_cc_file << enc_cc_output;
   // Output file prefix is the input file name.
-  auto [dec_h_output2, dec_cc_output2] = EmitDecoderCode(encoding_info.get());
+  auto [dec_h_output2, dec_cc_output2, types_h_output2] =
+      EmitDecoderCode(encoding_info.get());
   dec_dot_h_file << dec_h_output2;
   dec_dot_cc_file << dec_cc_output2;
-  auto [dec_h_output3, dec_cc_output3] =
-      EmitFileSuffix(dec_dot_h_name, encoding_info.get());
+  types_dot_h_file << types_h_output2;
+  auto [dec_h_output3, dec_cc_output3, types_h_output3] =
+      EmitFileSuffix(dec_dot_h_name, types_dot_h_name, encoding_info.get());
   dec_dot_h_file << dec_h_output3;
   dec_dot_cc_file << dec_cc_output3;
+  types_dot_h_file << types_h_output3;
   auto [enc_h_output2, enc_cc_output2] = EmitEncoderCode(encoding_info.get());
   enc_dot_h_file << enc_h_output2;
   enc_dot_cc_file << enc_cc_output2;
-  auto [enc_h_output3, enc_cc_output3] =
-      EmitFileSuffix(enc_dot_h_name, encoding_info.get());
+  std::string empty;
+  auto [enc_h_output3, enc_cc_output3, not_used] =
+      EmitFileSuffix(enc_dot_h_name, empty, encoding_info.get());
   enc_dot_h_file << enc_h_output3;
   enc_dot_cc_file << enc_cc_output3;
 
@@ -243,10 +251,12 @@
   encoding->decoder()->CheckEncodings();
 }
 
-BinFormatVisitor::StringPair BinFormatVisitor::EmitDecoderFilePrefix(
-    const std::string &dot_h_name, BinEncodingInfo *encoding_info) const {
+BinFormatVisitor::StringTriple BinFormatVisitor::EmitDecoderFilePrefix(
+    const std::string &dot_h_name, const std::string &types_dot_h_name,
+    BinEncodingInfo *encoding_info) const {
   std::string h_string;
   std::string cc_string;
+  std::string types_string;
 
   std::string guard_name = ToHeaderGuard(dot_h_name);
   absl::StrAppend(&h_string, "#ifndef ", guard_name,
@@ -260,16 +270,33 @@
                   "\n"
                   "#include \"absl/functional/any_invocable.h\"\n"
                   "#include \"absl/log/log.h\"\n"
+                  "#include \"",
+                  types_dot_h_name,
+                  "\"\n"
                   "\n\n");
+  std::string types_guard_name = ToHeaderGuard(types_dot_h_name);
+  absl::StrAppend(&types_string, "#ifndef ", types_guard_name,
+                  "\n"
+                  "#define ",
+                  types_guard_name,
+                  "\n"
+                  "\n"
+                  "#include <iostream>\n"
+                  "#include <cstdint>\n"
+                  "\n");
   for (auto const &include_file : encoding_info->include_files()) {
     absl::StrAppend(&h_string, "#include ", include_file, "\n");
   }
   absl::StrAppend(&h_string, "\n");
-  absl::StrAppend(&cc_string, "#include \"", dot_h_name, "\"\n\n");
+  absl::StrAppend(&cc_string, "#include \"", dot_h_name,
+                  "\"\n"
+                  "#include \"",
+                  types_dot_h_name, "\"\n\n");
   for (auto &name_space : encoding_info->decoder()->namespaces()) {
     auto name_space_str = absl::StrCat("namespace ", name_space, " {\n");
     absl::StrAppend(&h_string, name_space_str);
     absl::StrAppend(&cc_string, name_space_str);
+    absl::StrAppend(&types_string, name_space_str);
   }
   absl::StrAppend(&h_string, "\n");
   absl::StrAppend(&cc_string, "\n");
@@ -285,39 +312,49 @@
     absl::StrAppend(&h_string, "  k", ToPascalCase(name), " = ", i++, ",\n");
   }
   absl::StrAppend(&h_string, "};\n\n");
-  return {h_string, cc_string};
+  return {h_string, cc_string, types_string};
 }
 
-BinFormatVisitor::StringPair BinFormatVisitor::EmitFileSuffix(
-    const std::string &dot_h_name, BinEncodingInfo *encoding_info) {
+BinFormatVisitor::StringTriple BinFormatVisitor::EmitFileSuffix(
+    const std::string &dot_h_name, const std::string &types_dot_h_name,
+    BinEncodingInfo *encoding_info) {
   std::string h_string;
   std::string cc_string;
+  std::string types_string;
 
   absl::StrAppend(&h_string, "\n");
   absl::StrAppend(&cc_string, "\n");
+  if (!types_dot_h_name.empty()) absl::StrAppend(&types_string, "\n");
   auto &namespaces = encoding_info->decoder()->namespaces();
   for (auto rptr = namespaces.rbegin(); rptr != namespaces.rend(); rptr++) {
     std::string name_space = absl::StrCat("}  // namespace ", *rptr, "\n");
     absl::StrAppend(&h_string, name_space);
     absl::StrAppend(&cc_string, name_space);
+    if (!types_dot_h_name.empty()) absl::StrAppend(&types_string, name_space);
   }
   std::string guard_name = ToHeaderGuard(dot_h_name);
   absl::StrAppend(&h_string, "\n#endif // ", guard_name);
-  return {h_string, cc_string};
+  if (!types_dot_h_name.empty()) {
+    std::string types_guard_name = ToHeaderGuard(types_dot_h_name);
+    absl::StrAppend(&types_string, "\n#endif // ", types_guard_name);
+  }
+  return {h_string, cc_string, types_string};
 }
 
-BinFormatVisitor::StringPair BinFormatVisitor::EmitDecoderCode(
+BinFormatVisitor::StringTriple BinFormatVisitor::EmitDecoderCode(
     BinEncodingInfo *encoding) {
   std::string h_string;
   std::string cc_string;
   std::string group_string;
+  std::string extractor_types;
   std::string extractor_class =
       absl::StrCat("class Extractors {\n", "public: \n");
   // Write out the inline functions for bitfield and overlay extractions.
   for (auto &[unused, format_ptr] : encoding->format_map()) {
-    auto [functions, classes] = format_ptr->GenerateExtractors();
-    absl::StrAppend(&h_string, functions);
-    absl::StrAppend(&extractor_class, classes);
+    auto extractors = format_ptr->GenerateExtractors();
+    absl::StrAppend(&h_string, extractors.h_output);
+    absl::StrAppend(&extractor_class, extractors.class_output);
+    absl::StrAppend(&extractor_types, extractors.types_output);
   }
   absl::StrAppend(&h_string, extractor_class, "};\n\n");
   auto *decoder = encoding->decoder();
@@ -331,12 +368,12 @@
     absl::StrAppend(&group_string, group->WriteGroup());
   }
   absl::StrAppend(&h_string, group_string);
-  return {h_string, cc_string};
+  return {h_string, cc_string, extractor_types};
 }
 
 std::tuple<std::string, std::string> BinFormatVisitor::EmitEncoderFilePrefix(
     const std::string &dot_h_name, const std::string &enum_h_name,
-    BinEncodingInfo *encoding_info) const {
+    const std::string &types_dot_h_name, BinEncodingInfo *encoding_info) const {
   std::string h_string;
   std::string cc_string;
 
@@ -351,16 +388,22 @@
                   "#include <cstdint>\n\n"
                   "#include \"absl/base/no_destructor.h\"\n"
                   "#include \"absl/container/flat_hash_map.h\"\n"
-                  "#include \"absl/log/log.h\"\n\n"
+                  "#include \"absl/log/log.h\"\n"
                   "#include \"",
-                  enum_h_name, "\"\n");
+                  enum_h_name,
+                  "\"\n"
+                  "#include \"",
+                  types_dot_h_name, "\"\n\n");
   absl::StrAppend(&cc_string, "#include \"", dot_h_name,
                   "\"\n\n"
                   "#include <cstdint>\n\n"
                   "#include \"absl/base/no_destructor.h\"\n"
                   "#include \"absl/container/flat_hash_map.h\"\n"
                   "#include \"",
-                  enum_h_name, "\"\n");
+                  enum_h_name,
+                  "\"\n"
+                  "#include \"",
+                  types_dot_h_name, "\"\n\n");
   for (auto &name_space : encoding_info->decoder()->namespaces()) {
     auto name_space_str = absl::StrCat("namespace ", name_space, " {\n");
     absl::StrAppend(&cc_string, name_space_str);
@@ -689,8 +732,14 @@
                                    ctx->start, format_res.status().message());
     return;
   }
-  // Parse the fields in the format.
+  // Parse the layout.
   auto format = format_res.value();
+  if (ctx->layout_spec() != nullptr) {
+    if (ctx->layout_spec()->layout_type()->getText() == "packed_struct") {
+      format->set_layout(Format::Layout::kPackedStruct);
+    }
+  }
+  // Parse the fields in the format.
   auto file_index = context_file_map_.at(ctx);
   for (auto field : ctx->format_field_defs()->field_def()) {
     context_file_map_.insert({field, file_index});
@@ -717,6 +766,12 @@
     // If it's a field definition, add the field.
     bool is_signed = ctx->sign_spec()->SIGNED() != nullptr;
     int width = ConvertToInt(ctx->index()->number());
+    if ((format->layout() == Format::Layout::kPackedStruct) && (width > 64)) {
+      error_listener_->semanticError(
+          file_names_[context_file_map_.at(ctx)], ctx->index()->number()->start,
+          "Fields in packed struct layouts can not be > 64 bits");
+      return;
+    }
     auto status = format->AddField(field_name, is_signed, width);
     if (!status.ok()) {
       error_listener_->semanticError(file_names_[context_file_map_.at(ctx)],
@@ -730,6 +785,12 @@
   int size = 1;
   if (ctx->index() != nullptr) {
     size = ConvertToInt(ctx->index()->number());
+    if ((format->layout() == Format::Layout::kPackedStruct) && (size > 1)) {
+      error_listener_->semanticError(
+          file_names_[context_file_map_.at(ctx)], ctx->index()->number()->start,
+          "Formats in packed struct layouts can not be replicated");
+      return;
+    }
   }
   std::string format_ref_name = ctx->format_name->getText();
   // Make sure that the referred to format is fully parsed.
@@ -741,6 +802,13 @@
     }
     format_ref = encoding_info->GetFormat(format_ref_name);
   }
+  int width = format_ref->declared_width();
+  if ((format->layout() == Format::Layout::kPackedStruct) && (width > 64)) {
+    error_listener_->semanticError(
+        file_names_[context_file_map_.at(ctx)], ctx->index()->number()->start,
+        "Formats used in packed struct layouts can not be > 64 bits");
+    return;
+  }
   format->AddFormatReferenceField(field_name, format_ref_name, size,
                                   ctx->start);
 }
diff --git a/mpact/sim/decoder/bin_format_visitor.h b/mpact/sim/decoder/bin_format_visitor.h
index a5f15fc..82b49ab 100644
--- a/mpact/sim/decoder/bin_format_visitor.h
+++ b/mpact/sim/decoder/bin_format_visitor.h
@@ -77,6 +77,11 @@
     std::string h_output;
     std::string cc_output;
   };
+  struct StringTriple {
+    std::string h_output;
+    std::string cc_output;
+    std::string types_output;
+  };
 
   BinFormatVisitor();
   ~BinFormatVisitor();
@@ -94,19 +99,22 @@
   void PerformEncodingChecks(BinEncodingInfo *encoding);
   // Called to generate and emit code for the decoder according to the parsed
   // input file.
-  StringPair EmitDecoderCode(BinEncodingInfo *encoding);
-  StringPair EmitDecoderFilePrefix(const std::string &dot_h_name,
-                                   BinEncodingInfo *encoding_info) const;
+  StringTriple EmitDecoderCode(BinEncodingInfo *encoding);
+  StringTriple EmitDecoderFilePrefix(const std::string &dot_h_name,
+                                     const std::string &types_dot_h_name,
+                                     BinEncodingInfo *encoding_info) const;
   // Called to generate and emit code for the decoder according to the parsed
   // input file.
   std::tuple<std::string, std::string> EmitEncoderCode(
       BinEncodingInfo *encoding);
   std::tuple<std::string, std::string> EmitEncoderFilePrefix(
       const std::string &dot_h_name, const std::string &enum_h_name,
+      const std::string &types_dot_h_name,
       BinEncodingInfo *encoding_info) const;
   // Generate the file suffixes (namespace closing etc.)
-  StringPair EmitFileSuffix(const std::string &dot_h_name,
-                            BinEncodingInfo *encoding_info);
+  StringTriple EmitFileSuffix(const std::string &dot_h_name,
+                              const std::string &types_dot_h_name,
+                              BinEncodingInfo *encoding_info);
   // Utility methods to parse certain nodes.
   BinaryNum ParseBinaryNum(TerminalNode *node);
   BitRange GetBitIndexRange(BitIndexRangeCtx *ctx);
diff --git a/mpact/sim/decoder/format.cc b/mpact/sim/decoder/format.cc
index 0ea3491..59f2164 100644
--- a/mpact/sim/decoder/format.cc
+++ b/mpact/sim/decoder/format.cc
@@ -17,7 +17,6 @@
 #include <algorithm>
 #include <cstdint>
 #include <string>
-#include <tuple>
 #include <utility>
 
 #include "absl/numeric/bits.h"
@@ -353,6 +352,32 @@
   return false;
 }
 
+std::string Format::GeneratePackedStructFieldExtractor(
+    const Field *field) const {
+  std::string h_output;
+  int width = field->width;
+  std::string return_type = GetUIntType(width);
+  std::string signature = absl::StrCat("inline ", return_type, " Extract",
+                                       ToPascalCase(field->name), "(");
+  if (declared_width_ < 64) {
+    absl::StrAppend(&signature, GetUIntType(declared_width_), " value) {\n");
+  } else {
+    absl::StrAppend(&signature, "const uint8_t *value) {\n");
+  }
+  absl::StrAppend(&h_output, signature);
+  // Now start the body.
+  std::string union_type = absl::StrCat("const ", ToSnakeCase(name()),
+                                        "::Union", ToPascalCase(name()));
+  absl::StrAppend(&h_output, "  ", union_type,
+                  " *packed_union;\n"
+                  "  packed_union = reinterpret_cast<",
+                  union_type, "*>(",
+                  declared_width_ > 64 ? "value);\n" : "&value);\n",
+                  "  return packed_union->", ToSnakeCase(name()), ".",
+                  field->name, ";\n}\n\n");
+  return h_output;
+}
+
 // This method generates the C++ code for the field extractors for the current
 // format.
 std::string Format::GenerateFieldExtractor(const Field *field) const {
@@ -426,9 +451,41 @@
   return h_output;
 }
 
+std::string Format::GeneratePackedStructFieldInserter(
+    const Field *field) const {
+  std::string h_output;
+  std::string field_type_name;
+  std::string inst_word_type_name;
+  if (computed_width_ <= 64) {
+    inst_word_type_name = GetUIntType(computed_width_);
+  } else {
+    inst_word_type_name = "uint8_t *";
+  }
+  field_type_name = GetUIntType(field->width);
+  std::string union_type =
+      absl::StrCat(ToSnakeCase(name()), "::Union", ToPascalCase(name()));
+  absl::StrAppend(&h_output, "static inline ", inst_word_type_name, " Insert",
+                  ToPascalCase(field->name), "(", field_type_name, " value, ",
+                  inst_word_type_name,
+                  " inst_word) {\n"
+                  "  ",
+                  union_type,
+                  " *packed_union;\n"
+                  "  packed_union = reinterpret_cast<",
+                  union_type, "*>(",
+                  (computed_width_ <= 64 ? "&inst_word" : "inst_word"),
+                  ");\n"
+                  "  packed_union->",
+                  ToSnakeCase(name()), ".", field->name,
+                  " = value;\n"
+                  "  return inst_word;\n"
+                  "}\n\n");
+  return h_output;
+}
+
 // This method generates the C++ code for field inserters for the current
-// format. That is, the generated code will take the value of a field and insert
-// it into the right place in the instruction word.
+// format. That is, the generated code will take the value of a field and
+// insert it into the right place in the instruction word.
 std::string Format::GenerateFieldInserter(const Field *field) const {
   std::string h_output;
   std::string field_type_name;
@@ -473,11 +530,11 @@
                     "  return inst_word;\n"
                     "}\n");
   } else {
-    absl::StrAppend(
-        &h_output,
-        "  LOG(FATAL) << \" Support for fields > 128 bits not implemented - "
-        "yet.\";\n"
-        "  return 0;\n}\n");
+    absl::StrAppend(&h_output,
+                    "  LOG(FATAL) << \" Support for fields > 128 bits not "
+                    "implemented - "
+                    "yet.\";\n"
+                    "  return 0;\n}\n");
   }
   return h_output;
 }
@@ -499,11 +556,11 @@
                   " value, ", result_type_name, " inst_word) {\n");
   // Mark error if either the overlay or the format is > 64 bits.
   if (overlay->declared_width() > 128) {
-    absl::StrAppend(
-        &h_output,
-        "  LOG(FATAL) << \" Support for overlays > 128 bits not implemented - "
-        "yet.\";\n"
-        "  return 0;\n}\n");
+    absl::StrAppend(&h_output,
+                    "  LOG(FATAL) << \" Support for overlays > 128 bits "
+                    "not implemented - "
+                    "yet.\";\n"
+                    "  return 0;\n}\n");
     return h_output;
   }
   bool use_mask_variable = false;
@@ -564,6 +621,38 @@
   return h_output;
 }
 
+std::string Format::GeneratePackedStructFormatInserter(
+    std::string_view format_alias, const Format *format, int high,
+    int size) const {
+  std::string h_output;
+  std::string inst_word_type_name;
+  if (computed_width_ <= 64) {
+    inst_word_type_name = GetUIntType(computed_width_);
+  } else {
+    inst_word_type_name = "uint8_t *";
+  }
+  std::string format_type_name = GetUIntType(format->declared_width());
+  std::string union_type =
+      absl::StrCat(ToSnakeCase(name()), "::Union", ToPascalCase(name()));
+  absl::StrAppend(&h_output, "static inline ", inst_word_type_name, "Insert",
+                  ToPascalCase(format_alias), "(", format_type_name, " value, ",
+                  inst_word_type_name,
+                  " inst_word) {\n"
+                  "  ",
+                  union_type,
+                  " *packed_union;\n"
+                  "  packed_union = reinterpret_cast<",
+                  union_type, "*>(",
+                  (computed_width_ <= 64 ? "&inst_word" : "inst_word"),
+                  ");\n"
+                  "  packed_union->",
+                  ToSnakeCase(name()), ".", format_alias,
+                  " = value;\n"
+                  "  return inst_word;\n"
+                  "}\n\n");
+  return h_output;
+}
+
 // This method generates the C++ code for format inserters for the current
 // format. That is, the generated code will take the value of a format and
 // insert it into the right place in the instruction word.
@@ -593,11 +682,11 @@
                   format_type_name, " value, ", target_type_name,
                   " inst_word) {\n");
   if (format->declared_width() > 128) {
-    absl::StrAppend(
-        &h_output,
-        "  LOG(FATAL) << \" Support for formats > 128 bits not implemented - "
-        "yet.\";\n"
-        "  return 0;\n}\n");
+    absl::StrAppend(&h_output,
+                    "  LOG(FATAL) << \" Support for formats > 128 bits not "
+                    "implemented - "
+                    "yet.\";\n"
+                    "  return 0;\n}\n");
     return h_output;
   }
   int width = format->declared_width();
@@ -645,11 +734,11 @@
                   ToPascalCase(format_alias), "(", format_type_name, " value, ",
                   target_type_name, " inst_word) {\n");
   if (format->declared_width() > 128) {
-    absl::StrAppend(
-        &h_output,
-        "  LOG(FATAL) << \" Support for formats > 128 bits not implemented - "
-        "yet.\";\n"
-        "  return 0;\n}\n");
+    absl::StrAppend(&h_output,
+                    "  LOG(FATAL) << \" Support for formats > 128 bits not "
+                    "implemented - "
+                    "yet.\";\n"
+                    "  return 0;\n}\n");
     return h_output;
   }
   int width = format->declared_width();
@@ -683,14 +772,41 @@
   return h_output;
 }
 
-// This method generates the format extractors for the current format (for when
-// a format contains other formats).
+std::string Format::GeneratePackedStructFormatExtractor(
+    absl::string_view format_alias, const Format *format, int high,
+    int size) const {
+  std::string h_output;
+  int width = format->declared_width();
+  std::string return_type = GetUIntType(width);
+  std::string signature = absl::StrCat("inline ", return_type, " Extract",
+                                       ToPascalCase(format_alias), "(");
+  if (declared_width_ < 64) {
+    absl::StrAppend(&signature, GetUIntType(declared_width_), " value) {\n");
+  } else {
+    absl::StrAppend(&signature, "const uint8_t *value) {\n");
+  }
+  absl::StrAppend(&h_output, signature);
+  // Now start the body.
+  std::string union_type = absl::StrCat("const ", ToSnakeCase(name()),
+                                        "::Union", ToPascalCase(name()));
+  absl::StrAppend(&h_output, "  ", union_type,
+                  " *packed_union;\n"
+                  "  packed_union = reinterpret_cast<",
+                  union_type, " *>(",
+                  declared_width_ > 64 ? "value);\n" : "&value);\n",
+                  "  return packed_union->", ToSnakeCase(name()), ".",
+                  format_alias, ";\n}\n\n");
+  return h_output;
+}
+
+// This method generates the format extractors for the current format (for
+// when a format contains other formats).
 std::string Format::GenerateFormatExtractor(absl::string_view format_alias,
                                             const Format *format, int high,
                                             int size) const {
-  std::string h_output;  // For each format generate am extractor.
+  std::string h_output;  // For each format generate an extractor.
   int width = format->declared_width();
-  // An extraction can only be for 64 bits or less.
+  // An extraction can only be for 128 bits or less.
   if (width > 128) {
     encoding_info_->error_listener()->semanticError(
         nullptr,
@@ -755,8 +871,8 @@
     absl::StrAppend(&expr, ", ", width, ")");
     absl::StrAppend(&h_output, "  return ", expr, ";\n}\n\n");
   }
-  // If the parent format size is not a power of two, also create an extractor
-  // that takes a uint8_t * parameter.
+  // If the parent format size is not a power of two, also create an
+  // extractor that takes a uint8_t * parameter.
   if ((declared_width_ <= 128) &&
       (absl::popcount(static_cast<unsigned>(declared_width_)) > 1)) {
     absl::StrAppend(&h_output, "inline ", return_type, " Extract",
@@ -773,6 +889,37 @@
   return h_output;
 }
 
+std::string Format::GeneratePackedStructOverlayExtractor(
+    Overlay *overlay) const {
+  std::string h_output;
+  std::string arg_type;
+  if (declared_width_ > 128) {
+    arg_type = "const uint8_t *";
+  } else {
+    std::string arg_type = GetUIntType(declared_width_);
+  }
+  std::string return_type = overlay->is_signed()
+                                ? GetIntType(overlay->declared_width())
+                                : GetUIntType(overlay->declared_width());
+  std::string signature =
+      absl::StrCat("inline ", return_type, " Extract",
+                   ToPascalCase(overlay->name()), "(", arg_type, " value)");
+  absl::StrAppend(&h_output, signature, " {\n  ", return_type, " result;\n",
+                  overlay->WritePackedStructValueExtractor("value", "result"));
+  if (overlay->is_signed()) {
+    int shift = GetIntTypeBitWidth(overlay->declared_width()) -
+                overlay->declared_width();
+    absl::StrAppend(&h_output, "  result = result << ", shift,
+                    ";\n"
+                    "  result = result >> ",
+                    shift, ";\n");
+  }
+  absl::StrAppend(&h_output,
+                  "  return result;\n"
+                  "}\n\n");
+  return h_output;
+}
+
 // Generates the C++ code for the overlay extractors in the current format.
 std::string Format::GenerateOverlayExtractor(Overlay *overlay) const {
   std::string h_output;
@@ -827,14 +974,26 @@
   }
   absl::StrAppend(&h_output, "struct ", ToPascalCase(name()), " {\n\n");
   // First fields and formats.
+  std::string inserter;
   for (auto &[unused, field_or_format_ptr] : extractors_) {
     if (field_or_format_ptr->is_field()) {
-      auto inserter = GenerateFieldInserter(field_or_format_ptr->field());
+      if (layout() == Layout::kPackedStruct) {
+        inserter =
+            GeneratePackedStructFieldInserter(field_or_format_ptr->field());
+      } else {
+        inserter = GenerateFieldInserter(field_or_format_ptr->field());
+      }
       absl::StrAppend(&h_output, inserter);
     } else {
-      auto inserter = GenerateFormatInserter(
-          field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
-          field_or_format_ptr->high(), field_or_format_ptr->size());
+      if (layout() == Layout::kPackedStruct) {
+        inserter = GeneratePackedStructFormatInserter(
+            field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
+            field_or_format_ptr->high(), field_or_format_ptr->size());
+      } else {
+        inserter = GenerateFormatInserter(
+            field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
+            field_or_format_ptr->high(), field_or_format_ptr->size());
+      }
       absl::StrAppend(&h_output, inserter);
     }
   }
@@ -847,50 +1006,118 @@
   return h_output;
 }
 
-// Top level function called to generate all the extractors for this format.
-std::tuple<std::string, std::string> Format::GenerateExtractors() const {
-  std::string class_output;
+std::string Format::GeneratePackedStructTypes() const {
   std::string h_output;
+  // First the struct.
+  absl::StrAppend(&h_output, "struct Packed", ToPascalCase(name()), " {\n");
+  for (auto it = field_vec_.rbegin(); it != field_vec_.rend(); ++it) {
+    auto *component = *it;
+    if (component->is_field()) {
+      int width = component->field()->width;
+      std::string field_type = component->field()->is_signed
+                                   ? GetIntType(width)
+                                   : GetUIntType(width);
+      absl::StrAppend(&h_output, "  ", field_type, " ",
+                      component->field()->name, " : ",
+                      component->field()->width, ";\n");
+    } else {
+      absl::StrAppend(&h_output, "  ",
+                      GetUIntType(component->format()->declared_width()), " ",
+                      component->format_alias(), " : ",
+                      component->format()->declared_width(), ";\n");
+    }
+  }
+  absl::StrAppend(&h_output, "} ABSL_ATTRIBUTE_PACKED;\n\n");
+  // Next the union.
+  int num_bytes = (declared_width_ + 7) / 8;
+  absl::StrAppend(&h_output, "union Union", ToPascalCase(name()),
+                  " {\n"
+                  "  Packed",
+                  ToPascalCase(name()), " ", ToSnakeCase(name()),
+                  ";\n"
+                  "  uint8_t bytes[",
+                  num_bytes, "];\n");
+  // If it is 64 bits or less, add an unsigned integer value type.
+  if (declared_width_ <= 64) {
+    absl::StrAppend(&h_output, "  ", GetUIntType(declared_width_), " value;\n");
+  }
+  absl::StrAppend(&h_output, "};\n\n");
+  return h_output;
+}
+
+// Top level function called to generate all the extractors for this format.
+Extractors Format::GenerateExtractors() const {
+  Extractors extractors;
   if (extractors_.empty() && overlay_extractors_.empty()) {
-    return std::tie(h_output, class_output);
+    return extractors;
   }
 
-  class_output = absl::StrCat("class ", ToPascalCase(name()), " {\n public:\n",
-                              "  ", ToPascalCase(name()), "() = default;\n\n");
+  extractors.class_output =
+      absl::StrCat("class ", ToPascalCase(name()), " {\n public:\n", "  ",
+                   ToPascalCase(name()), "() = default;\n\n");
 
   // Use a separate namespace for each format.
-  h_output = absl::StrCat("namespace ", ToSnakeCase(name()), " {\n\n");
+  extractors.h_output =
+      absl::StrCat("namespace ", ToSnakeCase(name()), " {\n\n");
+  extractors.types_output =
+      absl::StrCat("namespace ", ToSnakeCase(name()), " {\n\n");
 
   std::string get_size = absl::StrCat("constexpr int k", ToPascalCase(name()),
                                       "Size = ", declared_width(), ";\n\n");
-  absl::StrAppend(&h_output, get_size);
-  absl::StrAppend(&class_output, "static ", get_size);
+  absl::StrAppend(&extractors.h_output, get_size);
+  absl::StrAppend(&extractors.class_output, "static ", get_size);
+
+  // If this format has a packed struct layout, generate the types required.
+  if (layout() == Layout::kPackedStruct) {
+    absl::StrAppend(&extractors.types_output, GeneratePackedStructTypes());
+  }
 
   // First fields and formats.
   for (auto &[unused, field_or_format_ptr] : extractors_) {
     if (field_or_format_ptr->is_field()) {
-      auto extractor = GenerateFieldExtractor(field_or_format_ptr->field());
-      absl::StrAppend(&h_output, extractor);
-      absl::StrAppend(&class_output, "static ", extractor);
+      std::string extractor;
+      if (layout() == Layout::kPackedStruct) {
+        extractor =
+            GeneratePackedStructFieldExtractor(field_or_format_ptr->field());
+      } else {
+        extractor = GenerateFieldExtractor(field_or_format_ptr->field());
+      }
+      absl::StrAppend(&extractors.h_output, extractor);
+      absl::StrAppend(&extractors.class_output, "static ", extractor);
     } else {
-      auto extractor = GenerateFormatExtractor(
-          field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
-          field_or_format_ptr->high(), field_or_format_ptr->size());
-      absl::StrAppend(&h_output, extractor);
-      absl::StrAppend(&class_output, "static ", extractor);
+      std::string extractor;
+      if (layout() == Layout::kPackedStruct) {
+        extractor = GeneratePackedStructFormatExtractor(
+            field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
+            field_or_format_ptr->high(), field_or_format_ptr->size());
+      } else {
+        extractor = GenerateFormatExtractor(
+            field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
+            field_or_format_ptr->high(), field_or_format_ptr->size());
+      }
+      absl::StrAppend(&extractors.h_output, extractor);
+      absl::StrAppend(&extractors.class_output, "static ", extractor);
     }
   }
 
   // Then the overlays.
   for (auto &[unused, overlay_ptr] : overlay_extractors_) {
-    auto extractor = GenerateOverlayExtractor(overlay_ptr);
-    absl::StrAppend(&h_output, extractor);
-    absl::StrAppend(&class_output, "static ", extractor);
+    std::string extractor;
+    if (layout() == Layout::kPackedStruct) {
+      extractor = GeneratePackedStructOverlayExtractor(overlay_ptr);
+    } else {
+      extractor = GenerateOverlayExtractor(overlay_ptr);
+    }
+    absl::StrAppend(&extractors.h_output, extractor);
+    absl::StrAppend(&extractors.class_output, "static ", extractor);
   }
 
-  absl::StrAppend(&h_output, "}  // namespace ", ToSnakeCase(name()), "\n\n");
-  absl::StrAppend(&class_output, "};\n\n");
-  return std::tie(h_output, class_output);
+  absl::StrAppend(&extractors.h_output, "}  // namespace ", ToSnakeCase(name()),
+                  "\n\n");
+  absl::StrAppend(&extractors.types_output, "}  // namespace ",
+                  ToSnakeCase(name()), "\n\n");
+  absl::StrAppend(&extractors.class_output, "};\n\n");
+  return extractors;
 }
 
 bool Format::IsDerivedFrom(const Format *format) {
diff --git a/mpact/sim/decoder/format.h b/mpact/sim/decoder/format.h
index c986510..deb4a55 100644
--- a/mpact/sim/decoder/format.h
+++ b/mpact/sim/decoder/format.h
@@ -17,7 +17,6 @@
 
 #include <map>
 #include <string>
-#include <tuple>
 #include <vector>
 
 #include "absl/container/btree_map.h"
@@ -106,8 +105,20 @@
   Format *format_ = nullptr;
 };
 
+struct Extractors {
+  std::string h_output;
+  std::string class_output;
+  std::string types_output;
+};
+
 class Format {
  public:
+  // Layout type of the format.
+  enum class Layout {
+    kDefault,
+    kPackedStruct,
+  };
+
   Format() = delete;
   Format(std::string name, int width, BinEncodingInfo *encoding_info);
   Format(std::string name, int width, std::string base_format_name,
@@ -140,7 +151,7 @@
   void PropagateExtractorsUp();
   void PropagateExtractorsDown();
   // Generates definitions of the field and overlay extractors in the format.
-  std::tuple<std::string, std::string> GenerateExtractors() const;
+  Extractors GenerateExtractors() const;
   // Generates definitions of the field and overlay inserters in the format.
   std::string GenerateInserters() const;
 
@@ -157,11 +168,21 @@
   Format *base_format() const { return base_format_; }
   // Return pointer to the parent encoding info class.
   BinEncodingInfo *encoding_info() const { return encoding_info_; }
+  // Field layout.
+  Layout layout() const { return layout_; }
+  void set_layout(Layout layout) { layout_ = layout; }
 
  private:
   bool HasExtract(const std::string &name) const;
   bool HasOverlayExtract(const std::string &name) const;
 
+  // Extractor generators.
+  std::string GeneratePackedStructTypes() const;
+  std::string GeneratePackedStructFieldExtractor(const Field *field) const;
+  std::string GeneratePackedStructFormatExtractor(std::string_view format_alias,
+                                                  const Format *format,
+                                                  int high, int size) const;
+  std::string GeneratePackedStructOverlayExtractor(Overlay *overlay) const;
   std::string GenerateFieldExtractor(const Field *field) const;
   std::string GenerateFormatExtractor(std::string_view format_alias,
                                       const Format *format, int high,
@@ -169,9 +190,13 @@
   std::string GenerateOverlayExtractor(Overlay *overlay) const;
   // Inserters.
   std::string GenerateFieldInserter(const Field *field) const;
+  std::string GeneratePackedStructFieldInserter(const Field *field) const;
   std::string GenerateFormatInserter(std::string_view format_alias,
                                      const Format *format, int high,
                                      int size) const;
+  std::string GeneratePackedStructFormatInserter(std::string_view format_alias,
+                                                 const Format *format, int high,
+                                                 int size) const;
   std::string GenerateReplicatedFormatInserter(std::string_view format_alias,
                                                const Format *format, int high,
                                                int size) const;
@@ -190,6 +215,7 @@
   std::string int_type_name_;
   int declared_width_;
   int computed_width_ = 0;
+  Layout layout_ = Layout::kDefault;
   Format *base_format_ = nullptr;
   std::vector<Format *> derived_formats_;
   BinEncodingInfo *encoding_info_;
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc
index 406c6dd..0ce2e00 100644
--- a/mpact/sim/decoder/instruction_set_visitor.cc
+++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -1992,6 +1992,12 @@
       pos++;
       if (pos >= format.size()) {
         pos = std::string::npos;
+      } else if (format[pos] == '?') {
+        format_info->is_optional = true;
+        pos++;
+        if (pos >= format.size()) {
+          pos = std::string::npos;
+        }
       }
       format_info->is_formatted = true;
       disasm_fmt->format_info_vec.push_back(format_info);
@@ -2012,6 +2018,13 @@
       auto *format_info = new FormatInfo();
       format_info->op_name = op_name;
       format_info->is_formatted = false;
+      if ((pos != std::string::npos) && (format[pos] == '?')) {
+        format_info->is_optional = true;
+        pos++;
+        if (pos >= format.size()) {
+          pos = std::string::npos;
+        }
+      }
       disasm_fmt->format_info_vec.push_back(format_info);
     }
 
diff --git a/mpact/sim/decoder/mpact_sim_isa.bzl b/mpact/sim/decoder/mpact_sim_isa.bzl
index db01738..efcb6f0 100644
--- a/mpact/sim/decoder/mpact_sim_isa.bzl
+++ b/mpact/sim/decoder/mpact_sim_isa.bzl
@@ -170,6 +170,7 @@
         "%s_bin_decoder.cc" % base_file_prefix,
         "%s_bin_encoder.h" % base_file_prefix,
         "%s_bin_encoder.cc" % base_file_prefix,
+        "%s_bin_types.h" % base_file_prefix,
     ]
 
     # The command to generate the files.
diff --git a/mpact/sim/decoder/opcode.h b/mpact/sim/decoder/opcode.h
index 1cacd87..4921df6 100644
--- a/mpact/sim/decoder/opcode.h
+++ b/mpact/sim/decoder/opcode.h
@@ -147,6 +147,7 @@
   FormatInfo(const FormatInfo &) = default;
   std::string op_name;
   bool is_formatted = true;
+  bool is_optional = false;
   std::string number_format;
   bool use_address = false;
   std::string operation;
diff --git a/mpact/sim/decoder/overlay.cc b/mpact/sim/decoder/overlay.cc
index b742651..7842bec 100644
--- a/mpact/sim/decoder/overlay.cc
+++ b/mpact/sim/decoder/overlay.cc
@@ -14,22 +14,28 @@
 
 #include "mpact/sim/decoder/overlay.h"
 
+#include <algorithm>
 #include <cstdint>
 #include <string>
 #include <vector>
 
+#include "absl/numeric/bits.h"
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "mpact/sim/decoder/bin_format_visitor.h"
 #include "mpact/sim/decoder/format.h"
+#include "mpact/sim/decoder/format_name.h"
 
 namespace mpact {
 namespace sim {
 namespace decoder {
 namespace bin_format {
 
+using ::mpact::sim::machine_description::instruction_set::ToPascalCase;
+using ::mpact::sim::machine_description::instruction_set::ToSnakeCase;
+
 BitsOrField::BitsOrField(Field *field, int high, int low, int width)
     : field_(field), high_(high), low_(low), width_(width), position_(-1) {}
 
@@ -250,6 +256,64 @@
   return output;
 }
 
+namespace {
+
+// Return the int type byte width (1, 2, 4, 8, 16) or (-1 if it's bigger), of
+// the integer type that would fit this format.
+int GetIntTypeBitWidth(int bitwidth) {
+  auto shift = absl::bit_width(static_cast<unsigned>(bitwidth)) - 1;
+  if (absl::popcount(static_cast<unsigned>(bitwidth)) > 1) shift++;
+  shift = std::max(shift, 3);
+  if (shift > 7) return -1;
+  return 1 << shift;
+}
+
+std::string GetUIntType(int bitwidth) {
+  if (bitwidth > 128) return "uint8_t *";
+  if (bitwidth > 64) return "absl::uint128";
+  return absl::StrCat("uint", GetIntTypeBitWidth(bitwidth), "_t");
+}
+
+}  // namespace
+
+std::string Overlay::WritePackedStructValueExtractor(
+    absl::string_view value, absl::string_view result) const {
+  std::string output;
+  std::string assign = " = ";
+  std::string union_type =
+      absl::StrCat("const ", ToSnakeCase(format_->name()), "::Union",
+                   ToPascalCase(format_->name()));
+  absl::StrAppend(&output, "  ", union_type,
+                  " packed_union;\n"
+                  "  packed_union = reinterpret_cast<",
+                  union_type, "*>(",
+                  format_->declared_width() > 64 ? "value);\n" : "&value);\n");
+  std::string result_type = GetUIntType(declared_width_);
+  for (auto *component : component_vec_) {
+    if (component->high() < 0) {
+      // Binary literals are added.
+      BinaryNum bin_num = component->bin_num();
+      // If the value is 0, no need to 'or' it in.
+      if (bin_num.value == 0) continue;
+
+      int shift = component->position() - bin_num.width + 1;
+      absl::StrAppend(&output, "  ", result, assign, bin_num.value);
+      if (shift > 0) {
+        absl::StrAppend(&output, " << ", shift);
+      }
+      absl::StrAppend(&output, ";\n");
+    } else {
+      // Field or format references are added.
+      absl::StrAppend(
+          &output, "  ", result, assign, "static_cast<", result_type,
+          ">(packed_union->", ToSnakeCase(format_->name()), ".",
+          component->field()->name, ") << ", component->position(), ";\n");
+    }
+    assign = " |= ";
+  }
+  return output;
+}
+
 // Return a string with the code (not counting function definition, variable
 // definition or return statement) for extracting the value of the overlay from
 // a variable 'value' and storing it into the variable 'result'. This extractor
diff --git a/mpact/sim/decoder/overlay.h b/mpact/sim/decoder/overlay.h
index 7917988..cd05ed7 100644
--- a/mpact/sim/decoder/overlay.h
+++ b/mpact/sim/decoder/overlay.h
@@ -107,6 +107,8 @@
   absl::StatusOr<uint64_t> GetValue(uint64_t input) const;
   std::string WriteSimpleValueExtractor(absl::string_view value,
                                         absl::string_view result) const;
+  std::string WritePackedStructValueExtractor(absl::string_view value,
+                                              absl::string_view result) const;
   absl::StatusOr<uint64_t> GetValue(uint8_t *input);
   std::string WriteComplexValueExtractor(absl::string_view value,
                                          absl::string_view result,
diff --git a/mpact/sim/decoder/slot.cc b/mpact/sim/decoder/slot.cc
index 06b48a8..c4c54f8 100644
--- a/mpact/sim/decoder/slot.cc
+++ b/mpact/sim/decoder/slot.cc
@@ -308,7 +308,7 @@
   }
   std::string input(str.substr(pos));
   bool in_space = false;
-  char p;
+  char p = '\0';
   for (auto c : str) {
     if (isspace(c)) {
       if (!in_space) {
@@ -399,6 +399,7 @@
     auto format_end = disasm_fmt->format_info_vec.end();
     char prev = '\0';
     // Iterate over the format fragments.
+    bool optional = false;
     while (fragment_iter != fragment_end) {
       auto fragment = *fragment_iter;
       if (!fragment.empty()) {
@@ -408,6 +409,10 @@
       } else {
         prev = '\0';
       }
+      if (optional) {
+        absl::StrAppend(&output, ")?");
+      }
+      optional = false;
       fragment_iter++;
       if (format_iter != format_end) {
         // If the trailling part of output is not '\\s*', and prev is
@@ -419,6 +424,10 @@
             absl::StrAppend(&output, "\\s*");
           }
         }
+        if ((*format_iter)->is_optional) {
+          optional = true;
+          absl::StrAppend(&output, "(?:");
+        }
         std::string op_name = (*format_iter)->op_name;
         absl::StrAppend(&output, "(\\S*?)");
         opnd_locators.push_back(inst->opcode()->op_locator_map().at(op_name));
@@ -433,6 +442,9 @@
         format_iter++;
       }
     }
+    if (optional) {
+      absl::StrAppend(&output, ")?");
+    }
   }
   absl::StrAppend(&output, "\\s*$)\"");
   return {output, opnd_locators};
diff --git a/mpact/sim/generic/BUILD b/mpact/sim/generic/BUILD
index e198eeb..5e6e266 100644
--- a/mpact/sim/generic/BUILD
+++ b/mpact/sim/generic/BUILD
@@ -127,6 +127,8 @@
         "core_debug_interface.h",
     ],
     deps = [
+        ":core",
+        ":instruction",
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
     ],
diff --git a/mpact/sim/generic/core_debug_interface.h b/mpact/sim/generic/core_debug_interface.h
index 75fd5f6..6402696 100644
--- a/mpact/sim/generic/core_debug_interface.h
+++ b/mpact/sim/generic/core_debug_interface.h
@@ -22,14 +22,13 @@
 
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/instruction.h"
 
 namespace mpact {
 namespace sim {
 namespace generic {
 
-class DataBuffer;
-class Instruction;
-
 enum class AccessType {
   kLoad = 1,
   kStore = 2,