arch: Build the operand REs in the isa_parser on demand.

These regular expressions search code snippets to find places where
operands are used. Rather than build them explicitly at the end of
processing the operands{{}} construct, wait until they're first going to
be used. That way, we'll be able to define operands in as many places as
we want, as long as we've done all we're going to do before the first
instructions are defined.

This will pave the way to defining operands in regular python in let
blocks, and then possibly outside of the parser altogether, perhaps into
scons where having lots of output files for individual instructions will
be easier to manage. For now, this just lets you define multiple
operands blocks which is not all that exciting on its own :)

Change-Id: I1179092316c1c0ac2613810bfd236a32235502fb
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35237
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Richard Cooper <richard.cooper@arm.com>
Reviewed-by: Steve Reinhardt <stever@gmail.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py
index 86f5089..955d4e2 100755
--- a/src/arch/isa_parser.py
+++ b/src/arch/isa_parser.py
@@ -1133,7 +1133,7 @@
         # search for operands
         next_pos = 0
         while 1:
-            match = parser.operandsRE.search(code, next_pos)
+            match = parser.operandsRE().search(code, next_pos)
             if not match:
                 # no more matches: we're done
                 break
@@ -1303,7 +1303,7 @@
         # search for operands
         next_pos = 0
         while 1:
-            match = parser.operandsRE.search(code, next_pos)
+            match = parser.operandsRE().search(code, next_pos)
             if not match:
                 # no more matches: we're done
                 break
@@ -1558,6 +1558,13 @@
         # variable to hold templates
         self.templateMap = {}
 
+        # variable to hold operands
+        self.operandNameMap = {}
+
+        # Regular expressions for working with operands
+        self._operandsRE = None
+        self._operandsWithExtRE = None
+
         # This dictionary maps format name strings to Format objects.
         self.formatMap = {}
 
@@ -1590,6 +1597,16 @@
         self.maxInstDestRegs = 0
         self.maxMiscDestRegs = 0
 
+    def operandsRE(self):
+        if not self._operandsRE:
+            self.buildOperandREs()
+        return self._operandsRE
+
+    def operandsWithExtRE(self):
+        if not self._operandsWithExtRE:
+            self.buildOperandREs()
+        return self._operandsWithExtRE
+
     def __getitem__(self, i):    # Allow object (self) to be
         return getattr(self, i)  # passed to %-substitutions
 
@@ -2581,18 +2598,19 @@
             # in tmp_dict, just as if we evaluated a class declaration.
             operand_name[op_name] = type(cls_name, (base_cls,), tmp_dict)
 
-        self.operandNameMap = operand_name
+        self.operandNameMap.update(operand_name)
 
+    def buildOperandREs(self):
         # Define operand variables.
-        operands = list(user_dict.keys())
+        operands = list(self.operandNameMap.keys())
         # Add the elems defined in the vector operands and
         # build a map elem -> vector (used in OperandList)
         elem_to_vec = {}
-        for op in user_dict.keys():
-            if hasattr(self.operandNameMap[op], 'elems'):
-                for elem in self.operandNameMap[op].elems.keys():
+        for op_name, op in self.operandNameMap.items():
+            if hasattr(op, 'elems'):
+                for elem in op.elems.keys():
                     operands.append(elem)
-                    elem_to_vec[elem] = op
+                    elem_to_vec[elem] = op_name
         self.elemToVector = elem_to_vec
         extensions = self.operandTypeMap.keys()
 
@@ -2602,7 +2620,8 @@
         (?!\w)       # neg. lookahead assertion: prevent partial matches
         ''' % ('|'.join(operands), '|'.join(extensions))
 
-        self.operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE)
+        self._operandsRE = re.compile(operandsREString,
+                                      re.MULTILINE | re.VERBOSE)
 
         # Same as operandsREString, but extension is mandatory, and only two
         # groups are returned (base and ext, not full name as above).
@@ -2610,14 +2629,14 @@
         operandsWithExtREString = r'(?<!\w)(%s)_(%s)(?!\w)' \
             % ('|'.join(operands), '|'.join(extensions))
 
-        self.operandsWithExtRE = \
+        self._operandsWithExtRE = \
             re.compile(operandsWithExtREString, re.MULTILINE)
 
     def substMungedOpNames(self, code):
         '''Munge operand names in code string to make legal C++
         variable names.  This means getting rid of the type extension
         if any.  Will match base_name attribute of Operand object.)'''
-        return self.operandsWithExtRE.sub(r'\1', code)
+        return self.operandsWithExtRE().sub(r'\1', code)
 
     def mungeSnippet(self, s):
         '''Fix up code snippets for final substitution in templates.'''