No public description

PiperOrigin-RevId: 975822396
Change-Id: Ic29e7d48e56192a98d10c5b807fe73b1be53b7d7
diff --git a/mpact/sim/decoder/InstructionSet.g4 b/mpact/sim/decoder/InstructionSet.g4
index cefbc9a..48e4192 100644
--- a/mpact/sim/decoder/InstructionSet.g4
+++ b/mpact/sim/decoder/InstructionSet.g4
@@ -401,7 +401,11 @@
 // An opcode attribute is either a disassembly specifier or a semfunc specifier.
 
 opcode_attribute
-  : disasm_spec | semfunc_spec | resource_spec | instruction_attribute_spec
+  : disasm_spec
+  | semfunc_spec
+  | resource_spec
+  | instruction_attribute_spec
+  | latency_spec
   ;
 
 // The disassembly specifier lists a sequence of format strings. Each formatted
@@ -413,6 +417,20 @@
   : DISASM ':' STRING_LITERAL ( ',' STRING_LITERAL )*
   ;
 
+// The latency specifier lists the destination operands and their latencies.
+latency_spec
+  : LATENCY ':' dest_operand_latency_list
+  ;
+
+dest_operand_latency_list
+  : dest_operand_with_latency (',' dest_operand_with_latency)*
+  ;
+
+dest_operand_with_latency
+  : (op_name=IDENT | '[' array_dest=IDENT ']')
+    ('(' (expression | wildcard='*' ) ')')?
+  ;
+
 // The semantic function specifier lists a sequence of strings that in C++ can
 // be assigned to a C++ callables with signature void(Instruction *). These
 // will be used when dispatching the instruction. There will be one string
diff --git a/mpact/sim/decoder/instruction.cc b/mpact/sim/decoder/instruction.cc
index 2d9ed19..4991775 100644
--- a/mpact/sim/decoder/instruction.cc
+++ b/mpact/sim/decoder/instruction.cc
@@ -137,7 +137,9 @@
 
   // Instruction attributes.
   for (auto const& [attr_name, expr_ptr] : attribute_map_) {
-    auto result = expr_ptr->Evaluate(args);
+    auto result = (expr_ptr != nullptr)
+                      ? expr_ptr->Evaluate(args)
+                      : absl::InternalError("Null expression");
     if (result.ok()) {
       new_slot->AddAttributeName(attr_name);
       new_inst->AddInstructionAttribute(attr_name, result.value());
diff --git a/mpact/sim/decoder/instruction_set_contexts.h b/mpact/sim/decoder/instruction_set_contexts.h
index ca8d67d..9df06b6 100644
--- a/mpact/sim/decoder/instruction_set_contexts.h
+++ b/mpact/sim/decoder/instruction_set_contexts.h
@@ -49,6 +49,7 @@
     InstructionSetParser::Opcode_attribute_listContext;
 using InstructionAttributeListCtx =
     InstructionSetParser::Instruction_attribute_listContext;
+using LatencySpecCtx = InstructionSetParser::Latency_specContext;
 using SemfuncSpecCtx = InstructionSetParser::Semfunc_specContext;
 using ResourceItemCtx = InstructionSetParser::Resource_itemContext;
 using ResourceDetailsCtx = InstructionSetParser::Resource_detailsContext;
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc
index d6a0b21..8cd97f2 100644
--- a/mpact/sim/decoder/instruction_set_visitor.cc
+++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -1214,12 +1214,11 @@
   bool has_semfunc = false;
   bool has_resources = false;
   bool has_attributes = false;
+  bool has_latency = false;
   // Visit the opcode attributes.
   for (auto* attribute_ctx : ctx->opcode_attribute()) {
     // Process any disassembly specifications.
     if (attribute_ctx->disasm_spec() != nullptr) {
-      // In case of override, need to clear any disasm info in instruction.
-      inst->ClearDisasmFormat();
       // Signal error if there is more than one disassembly spec.
       if (has_disasm) {
         error_listener()->semanticError(
@@ -1228,6 +1227,8 @@
         continue;
       }
       has_disasm = true;
+      // In case of override, need to clear any disasm info in instruction.
+      inst->ClearDisasmFormat();
       for (auto* disasm_fmt : attribute_ctx->disasm_spec()->STRING_LITERAL()) {
         std::string format = disasm_fmt->getText();
         // Trim the double quotes.
@@ -1247,8 +1248,6 @@
 
     // Process the semantic function specification.
     if (attribute_ctx->semfunc_spec() != nullptr) {
-      // In case of override, need to clear the semantic function string.
-      inst->ClearSemfuncCodeString();
       // Signal error if there is more than one semantic function spec.
       if (has_semfunc) {
         error_listener()->semanticError(
@@ -1257,14 +1256,14 @@
         continue;
       }
       has_semfunc = true;
+      // In case of override, need to clear the semantic function string.
+      inst->ClearSemfuncCodeString();
       VisitSemfuncSpec(attribute_ctx->semfunc_spec(), inst);
       continue;
     }
 
     // Process resource specification.
     if (attribute_ctx->resource_spec() != nullptr) {
-      // In case of override, need to clear the resource specifications.
-      inst->ClearResourceSpecs();
       // Signal error if there is more than one resource specification.
       if (has_resources) {
         error_listener()->semanticError(
@@ -1273,6 +1272,8 @@
         continue;
       }
       has_resources = true;
+      // In case of override, need to clear the resource specifications.
+      inst->ClearResourceSpecs();
       VisitResourceDetails(attribute_ctx->resource_spec()->resource_details(),
                            inst, slot);
       continue;
@@ -1280,8 +1281,6 @@
 
     // Process instruction attribute specification.
     if (attribute_ctx->instruction_attribute_spec() != nullptr) {
-      // In case of override, need to clear the attribute specification.
-      inst->ClearAttributeSpecs();
       // Signal error if there is more than one attribute specification.
       if (has_attributes) {
         error_listener()->semanticError(
@@ -1289,6 +1288,8 @@
             attribute_ctx->start, "Multiple attribute specifications");
         continue;
       }
+      // In case of override, need to clear the attribute specification.
+      inst->ClearAttributeSpecs();
       has_attributes = true;
       auto attr_list_ctx = attribute_ctx->instruction_attribute_spec()
                                ->instruction_attribute_list();
@@ -1296,6 +1297,19 @@
       continue;
     }
 
+    if (attribute_ctx->latency_spec() != nullptr) {
+      // Signal error if there is more than one latency specification.
+      if (has_latency) {
+        error_listener()->semanticError(
+            file_names_[context_file_map_.at(slot->ctx())],
+            attribute_ctx->start, "Multiple latency specifications");
+        continue;
+      }
+      has_latency = true;
+      VisitLatencySpec(attribute_ctx->latency_spec(), slot, inst);
+      continue;
+    }
+
     // Unknown attribute type.
     error_listener()->semanticError(
         file_names_[context_file_map_.at(slot->ctx())], attribute_ctx->start,
@@ -1350,6 +1364,42 @@
   attributes.clear();
 }
 
+void InstructionSetVisitor::VisitLatencySpec(LatencySpecCtx* latency_spec,
+                                             Slot* slot, Instruction* inst) {
+  for (auto* dest_op_with_latency :
+       latency_spec->dest_operand_latency_list()->dest_operand_with_latency()) {
+    std::string name;
+    if (dest_op_with_latency->op_name != nullptr) {
+      name = dest_op_with_latency->op_name->getText();
+    } else {
+      name = dest_op_with_latency->array_dest->getText();
+    }
+    // Make sure the destination operand exists.
+    DestinationOperand* dest_op = inst->GetDestOp(name);
+    if (dest_op == nullptr) {
+      error_listener()->semanticError(
+          file_names_[context_file_map_.at(slot->ctx())],
+          dest_op_with_latency->start,
+          absl::StrCat("Unknown destination operand '", name, "'"));
+      continue;
+    }
+    dest_op->ClearExpression();
+    if (dest_op_with_latency->expression() != nullptr) {
+      context_file_map_.insert({dest_op_with_latency->expression(),
+                                context_file_map_.at(slot->ctx())});
+      auto* latency_expression =
+          VisitExpression(dest_op_with_latency->expression(), slot, inst);
+      dest_op->SetExpression(latency_expression);
+    } else if (dest_op_with_latency->wildcard == nullptr) {
+      if (slot->default_latency() != nullptr) {
+        dest_op->SetExpression(slot->default_latency()->DeepCopy());
+      } else {
+        dest_op->SetExpression(new TemplateConstant(1));
+      }
+    }
+  }
+}
+
 void InstructionSetVisitor::VisitSemfuncSpec(SemfuncSpecCtx* semfunc_spec,
                                              Instruction* inst) {
   auto* child = inst;
diff --git a/mpact/sim/decoder/instruction_set_visitor.h b/mpact/sim/decoder/instruction_set_visitor.h
index 83fa3ba..0bd300d 100644
--- a/mpact/sim/decoder/instruction_set_visitor.h
+++ b/mpact/sim/decoder/instruction_set_visitor.h
@@ -132,6 +132,8 @@
   void VisitOpcodeAttributes(OpcodeAttributeListCtx* ctx, Instruction* inst,
                              Slot* slot);
   void VisitSemfuncSpec(SemfuncSpecCtx* semfunc_spec, Instruction* inst);
+  void VisitLatencySpec(LatencySpecCtx* latency_spec, Slot* slot,
+                        Instruction* inst);
   void VisitResourceDetails(ResourceDetailsCtx* ctx, Instruction* inst,
                             Slot* slot);
   std::optional<ResourceReference*> ProcessResourceReference(
diff --git a/mpact/sim/decoder/opcode.cc b/mpact/sim/decoder/opcode.cc
index a5e7e2e..c931bb5 100644
--- a/mpact/sim/decoder/opcode.cc
+++ b/mpact/sim/decoder/opcode.cc
@@ -71,6 +71,12 @@
   return nullptr;
 }
 
+void Opcode::ClearDestLatencies() {
+  for (auto* dest_op : dest_op_vec_) {
+    dest_op->ClearExpression();
+  }
+}
+
 bool Opcode::ValidateDestLatencies(
     const std::function<bool(int)>& validator) const {
   for (auto const* dest_op : dest_op_vec_) {
diff --git a/mpact/sim/decoder/opcode.h b/mpact/sim/decoder/opcode.h
index ca04563..6bee33c 100644
--- a/mpact/sim/decoder/opcode.h
+++ b/mpact/sim/decoder/opcode.h
@@ -80,6 +80,16 @@
     expression_ = nullptr;
   }
 
+  void SetExpression(TemplateExpression* expression) {
+    if (expression_ != nullptr) delete expression_;
+    expression_ = expression;
+  }
+
+  void ClearExpression() {
+    delete expression_;
+    expression_ = nullptr;
+  }
+
   const std::string& name() const { return name_; }
   const std::string& pascal_case_name() const { return pascal_case_name_; }
   TemplateExpression* expression() const { return expression_; }
@@ -238,6 +248,8 @@
   // Checks destination latencies with the given function. Returns true if all
   // comply.
   bool ValidateDestLatencies(const std::function<bool(int)>& validator) const;
+  // Clear destination latencies.
+  void ClearDestLatencies();
   int instruction_size() const { return instruction_size_; }
   void set_instruction_size(int val) { instruction_size_ = val; }
   Opcode* child() const { return child_; }
diff --git a/mpact/sim/generic/simple_resource_operand.h b/mpact/sim/generic/simple_resource_operand.h
index 9b6f9b9..6484d0d 100644
--- a/mpact/sim/generic/simple_resource_operand.h
+++ b/mpact/sim/generic/simple_resource_operand.h
@@ -65,7 +65,7 @@
 
   // If the latency is 0 release immediately, otherwise, add the resource set
   // to the resource set delay line for release after 'latency' cycles.
-  inline void Release(int latency) {
+  void Release(int latency) {
     if (latency == 0) {
       resource_set_->Release();
       return;
@@ -74,7 +74,7 @@
   }
 
   // Uses latency_.
-  inline void Release() { Release(latency_); }
+  void Release() { Release(latency_); }
 
   bool IsFree() override { return resource_set_->IsFree(); }