Adding capability to mark operands as possibly generating relocation info.

This only applies to generated assemblers.

PiperOrigin-RevId: 713424216
Change-Id: I3b9ba49354f6d64c3285f4acc28d3ff1fbd95a02
diff --git a/mpact/sim/decoder/InstructionSet.g4 b/mpact/sim/decoder/InstructionSet.g4
index f26e41e..b8e71e5 100644
--- a/mpact/sim/decoder/InstructionSet.g4
+++ b/mpact/sim/decoder/InstructionSet.g4
@@ -320,10 +320,15 @@
   ;
 
 source_operand
-  : source=IDENT
+  : operand
   | '[' array_source=IDENT ']'
   ;
 
+operand
+  : op_attribute=OP_ATTRIBUTE '(' op_name=IDENT ')'
+  | op_name=IDENT
+  ;
+
 // Destination operands may include a latency.
 
 dest_list
@@ -331,7 +336,7 @@
   ;
 
 dest_operand
-  : (dest=IDENT | '[' array_dest=IDENT ']')
+  : (operand | '[' array_dest=IDENT ']')
     ( '(' (expression | wildcard='*' ) ')' )?
   ;
 
@@ -515,6 +520,8 @@
 SLOTS : 'slots';
 TEMPLATE : 'template';
 
+OP_ATTRIBUTE : '%reloc';
+
 // Other tokens.
 STRING_LITERAL : UNTERMINATED_STRING_LITERAL '"';
 fragment UNTERMINATED_STRING_LITERAL : '"' (~["\\\r\n] | '\\' (. | EOF))*;
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc
index 6398a7c..7783807 100644
--- a/mpact/sim/decoder/instruction_set_visitor.cc
+++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -1608,25 +1608,37 @@
   if (ctx->pred != nullptr) {
     std::string name = ctx->pred->getText();
     child->opcode()->set_predicate_op_name(name);
-    parent->opcode()->op_locator_map().insert(
-        std::make_pair(name, OperandLocator(op_spec_number, 'p', 0)));
+    parent->opcode()->op_locator_map().insert(std::make_pair(
+        name, OperandLocator(op_spec_number, 'p', /*is_reloc=*/false,
+                             /*instance=*/0)));
   }
   if (ctx->source != nullptr) {
     int instance = 0;
     for (auto *source_op : ctx->source->source_operand()) {
       std::string name;
-      bool is_array;
-      if (source_op->source != nullptr) {
-        name = source_op->source->getText();
-        is_array = false;
+      bool is_array = false;
+      bool is_reloc = false;
+      if (source_op->operand() != nullptr) {
+        name = source_op->operand()->op_name->getText();
+        if (source_op->operand()->op_attribute != nullptr) {
+          auto attr = source_op->operand()->op_attribute->getText();
+          if (attr == "%reloc") {
+            is_reloc = true;
+          } else {
+            error_listener()->semanticError(
+                file_names_[context_file_map_.at(slot->ctx())],
+                source_op->operand()->op_attribute,
+                absl::StrCat("Invalid operand attribute '", attr, "'"));
+          }
+        }
       } else {
         name = source_op->array_source->getText();
         is_array = true;
       }
-      child->opcode()->AppendSourceOp(name, is_array);
+      child->opcode()->AppendSourceOp(name, is_array, is_reloc);
       parent->opcode()->op_locator_map().insert(std::make_pair(
-          name,
-          OperandLocator(op_spec_number, is_array ? 't' : 's', instance)));
+          name, OperandLocator(op_spec_number, is_array ? 't' : 's', is_reloc,
+                               instance)));
       instance++;
     }
   }
@@ -1634,10 +1646,21 @@
     int instance = 0;
     for (auto *dest_op : ctx->dest_list()->dest_operand()) {
       std::string ident;
-      bool is_array;
-      if (dest_op->dest != nullptr) {
-        ident = dest_op->dest->getText();
-        is_array = false;
+      bool is_array = false;
+      bool is_reloc = false;
+      if (dest_op->operand() != nullptr) {
+        ident = dest_op->operand()->op_name->getText();
+        if (dest_op->operand()->op_attribute != nullptr) {
+          auto attr = dest_op->operand()->op_attribute->getText();
+          if (attr == "%reloc") {
+            is_reloc = true;
+          } else {
+            error_listener()->semanticError(
+                file_names_[context_file_map_.at(slot->ctx())],
+                dest_op->operand()->op_attribute,
+                absl::StrCat("Invalid operand attribute '", attr, "'"));
+          }
+        }
       } else {
         ident = dest_op->array_dest->getText();
         is_array = true;
@@ -1649,19 +1672,20 @@
         context_file_map_.insert(
             {dest_op->expression(), context_file_map_.at(slot->ctx())});
         child->opcode()->AppendDestOp(
-            ident, is_array,
+            ident, is_array, is_reloc,
             VisitExpression(dest_op->expression(), slot, child));
       } else if (dest_op->wildcard != nullptr) {
-        child->opcode()->AppendDestOp(ident, is_array);
+        child->opcode()->AppendDestOp(ident, is_array, is_reloc);
       } else if (slot->default_latency() != nullptr) {
-        child->opcode()->AppendDestOp(ident, is_array,
+        child->opcode()->AppendDestOp(ident, is_array, is_reloc,
                                       slot->default_latency()->DeepCopy());
       } else {
-        child->opcode()->AppendDestOp(ident, is_array, new TemplateConstant(1));
+        child->opcode()->AppendDestOp(ident, is_array, is_reloc,
+                                      new TemplateConstant(1));
       }
       parent->opcode()->op_locator_map().insert(std::make_pair(
-          ident,
-          OperandLocator(op_spec_number, is_array ? 'e' : 'd', instance)));
+          ident, OperandLocator(op_spec_number, is_array ? 'e' : 'd', is_reloc,
+                                instance)));
       instance++;
     }
   }
diff --git a/mpact/sim/decoder/opcode.cc b/mpact/sim/decoder/opcode.cc
index aaed476..a4a3d30 100644
--- a/mpact/sim/decoder/opcode.cc
+++ b/mpact/sim/decoder/opcode.cc
@@ -44,19 +44,22 @@
   dest_op_map_.clear();
 }
 
-void Opcode::AppendSourceOp(absl::string_view op_name, bool is_array) {
-  source_op_vec_.emplace_back(std::string(op_name), is_array);
+void Opcode::AppendSourceOp(absl::string_view op_name, bool is_array,
+                            bool is_reloc) {
+  source_op_vec_.emplace_back(std::string(op_name), is_array, is_reloc);
 }
 
-void Opcode::AppendDestOp(absl::string_view op_name, bool is_array) {
-  auto *op = new DestinationOperand(std::string(op_name), is_array);
+void Opcode::AppendDestOp(absl::string_view op_name, bool is_array,
+                          bool is_reloc) {
+  auto *op = new DestinationOperand(std::string(op_name), is_array, is_reloc);
   dest_op_vec_.push_back(op);
   dest_op_map_.insert(std::make_pair(std::string(op_name), op));
 }
 
 void Opcode::AppendDestOp(absl::string_view op_name, bool is_array,
-                          TemplateExpression *expression) {
-  auto *op = new DestinationOperand(std::string(op_name), is_array, expression);
+                          bool is_reloc, TemplateExpression *expression) {
+  auto *op = new DestinationOperand(std::string(op_name), is_array, is_reloc,
+                                    expression);
   dest_op_vec_.push_back(op);
   dest_op_map_.insert(std::make_pair(std::string(op_name), op));
 }
@@ -108,14 +111,15 @@
   new_opcode->predicate_op_name_ = opcode->predicate_op_name();
   new_opcode->op_locator_map_ = opcode->op_locator_map();
   for (auto const &src_op : opcode->source_op_vec()) {
-    new_opcode->AppendSourceOp(src_op.name, src_op.is_array);
+    new_opcode->AppendSourceOp(src_op.name, src_op.is_array, src_op.is_reloc);
   }
 
   // Copy destination operands, but evaluate any latencies using the template
   // instantiation arguments, in case those expressions use them.
   for (auto const *dest_op : opcode->dest_op_vec()) {
     if (dest_op->expression() == nullptr) {
-      new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array());
+      new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array(),
+                               dest_op->is_reloc());
     } else {
       // For each destination operand that has an expression, evaluate it in the
       // context of the passed in TemplateInstantiationArgs. This creates a copy
@@ -124,7 +128,7 @@
       auto result = dest_op->expression()->Evaluate(args);
       if (result.ok()) {
         new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array(),
-                                 result.value());
+                                 dest_op->is_reloc(), result.value());
       } else {
         delete new_opcode;
         return absl::InternalError(absl::StrCat(
diff --git a/mpact/sim/decoder/opcode.h b/mpact/sim/decoder/opcode.h
index 4b77fc3..1cacd87 100644
--- a/mpact/sim/decoder/opcode.h
+++ b/mpact/sim/decoder/opcode.h
@@ -15,6 +15,8 @@
 #ifndef MPACT_SIM_DECODER_OPCODE_H_
 #define MPACT_SIM_DECODER_OPCODE_H_
 
+#include <stdbool.h>
+
 #include <functional>
 #include <string>
 #include <utility>
@@ -50,31 +52,39 @@
 class DestinationOperand {
  public:
   // Operand latency is defined by the expression.
-  DestinationOperand(std::string name, bool is_array,
+  DestinationOperand(std::string name, bool is_array, bool is_reloc,
                      TemplateExpression *expression)
       : name_(std::move(name)),
         pascal_case_name_(ToPascalCase(name_)),
         expression_(expression),
-        is_array_(is_array) {}
+        is_array_(is_array),
+        is_reloc_(is_reloc) {}
   // Operand latency is a constant.
-  DestinationOperand(std::string name, bool is_array, int latency)
+  DestinationOperand(std::string name, bool is_array, bool is_reloc,
+                     int latency)
       : name_(std::move(name)),
         pascal_case_name_(ToPascalCase(name_)),
         expression_(new TemplateConstant(latency)),
-        is_array_(is_array) {}
+        is_array_(is_array),
+        is_reloc_(is_reloc) {}
   // This constructor is used when the destination operand latency is specified
   // as '*' - meaning that it will be computed at the time of decode.
-  explicit DestinationOperand(std::string name, bool is_array)
+  DestinationOperand(std::string name, bool is_array, bool is_reloc)
       : name_(std::move(name)),
         pascal_case_name_(ToPascalCase(name_)),
         expression_(nullptr),
-        is_array_(is_array) {}
-  ~DestinationOperand() { delete expression_; }
+        is_array_(is_array),
+        is_reloc_(is_reloc) {}
+  ~DestinationOperand() {
+    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_; }
   bool is_array() const { return is_array_; }
+  bool is_reloc() const { return is_reloc_; }
   bool HasLatency() const { return expression_ != nullptr; }
   absl::StatusOr<int> GetLatency() const {
     if (expression_ == nullptr) return -1;
@@ -96,13 +106,15 @@
   std::string pascal_case_name_;
   TemplateExpression *expression_;
   bool is_array_ = false;
+  bool is_reloc_ = false;
 };
 
 struct SourceOperand {
   std::string name;
   bool is_array;
-  SourceOperand(std::string name_, bool is_array_)
-      : name(std::move(name_)), is_array(is_array_) {}
+  bool is_reloc;
+  SourceOperand(std::string name_, bool is_array_, bool is_reloc_)
+      : name(std::move(name_)), is_array(is_array_), is_reloc(is_reloc_) {}
 };
 
 // This struct is used to specify the location of an operand within an
@@ -120,9 +132,13 @@
   static constexpr char kDestinationArray = 'e';
   int op_spec_number;
   char type;
+  bool is_reloc;
   int instance;
-  OperandLocator(int op_spec_number_, char type_, int instance_)
-      : op_spec_number(op_spec_number_), type(type_), instance(instance_) {}
+  OperandLocator(int op_spec_number_, char type_, bool is_reloc_, int instance_)
+      : op_spec_number(op_spec_number_),
+        type(type_),
+        is_reloc(is_reloc_),
+        instance(instance_) {}
 };
 
 struct FormatInfo {
@@ -210,10 +226,10 @@
   // to get the Predicate, Source and Destination operand interfaces (defined
   // in .../sim/generic/operand_interfaces.h. The implementation of these
   // methods will be left to the user of this generator tool.
-  void AppendSourceOp(absl::string_view op_name, bool is_array);
-  void AppendDestOp(absl::string_view op_name, bool is_array,
+  void AppendSourceOp(absl::string_view op_name, bool is_array, bool is_reloc);
+  void AppendDestOp(absl::string_view op_name, bool is_array, bool is_reloc,
                     TemplateExpression *expression);
-  void AppendDestOp(absl::string_view op_name, bool is_array);
+  void AppendDestOp(absl::string_view op_name, bool is_array, bool is_reloc);
   DestinationOperand *GetDestOp(absl::string_view op_name);
   // Append child opcode specification.
   void AppendChild(Opcode *op) { child_ = op; }
diff --git a/mpact/sim/decoder/test/opcode_test.cc b/mpact/sim/decoder/test/opcode_test.cc
index fba903c..8dba3f0 100644
--- a/mpact/sim/decoder/test/opcode_test.cc
+++ b/mpact/sim/decoder/test/opcode_test.cc
@@ -90,7 +90,8 @@
 TEST_F(OpcodeTest, SourceOperandNames) {
   for (int indx = 0; indx < 3; indx++) {
     std::string source_op_name = absl::StrCat("SourceOp", indx);
-    opcode_->AppendSourceOp(source_op_name, /*is_array=*/false);
+    opcode_->AppendSourceOp(source_op_name, /*is_array=*/false,
+                            /*is_reloc=*/false);
     EXPECT_EQ(opcode_->source_op_vec().size(), indx + 1);
     EXPECT_STREQ(opcode_->source_op_vec()[indx].name.c_str(),
                  source_op_name.c_str());
@@ -102,10 +103,12 @@
   for (int indx = 0; indx < 2; indx++) {
     std::string dest_op_name = absl::StrCat("DestOp", indx);
     if (indx == 0) {
-      opcode_->AppendDestOp(dest_op_name, /*is_array=*/false);
+      opcode_->AppendDestOp(dest_op_name, /*is_array=*/false,
+                            /*is_reloc=*/false);
     } else if (indx == 1) {
       // Using nullptr - the value isn't checked upon append.
-      opcode_->AppendDestOp(dest_op_name, /*is_array=*/false, nullptr);
+      opcode_->AppendDestOp(dest_op_name, /*is_array=*/false,
+                            /*is_reloc=*/false, nullptr);
     }
     EXPECT_EQ(opcode_->dest_op_vec().size(), indx + 1);
     EXPECT_STREQ(opcode_->dest_op_vec()[indx]->name().c_str(),
diff --git a/mpact/sim/decoder/test/testfiles/example.isa b/mpact/sim/decoder/test/testfiles/example.isa
index 3a6cc47..2038b83 100644
--- a/mpact/sim/decoder/test/testfiles/example.isa
+++ b/mpact/sim/decoder/test/testfiles/example.isa
@@ -38,7 +38,7 @@
   int mult_plus_2 = mult + my_const;
   default attributes = { one, two = 3, three = base, four = base_plus_1, five = 0};
   opcodes {
-    vctsf{(pred : sy : dest(base_plus_1))};
+    vctsf{(pred : sy : %reloc(dest)(base_plus_1))};
     cvtfs{(pred : sy : dest(base + 1))};
     adds{(pred : sy, sx : dest(base))},
       attributes: {five = 1, six};
@@ -80,7 +80,7 @@
   default opcode =
     semfunc: "[](Instruction *) {}";
   opcodes {
-    ld{(pred : yop : ),(: : dest)},
+    ld{(pred : %reloc(yop) : ),(: : dest)},
       disasm:"%dest = sld %pred [smem:%yop]";
     ld_offset{(pred : xop, yop : dest(abs(-2)))};
     st{(pred)};