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 ¶m : 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"