Reworking how x86's isa description works. I'm adopting the following definitions...
authorGabe Black <gblack@eecs.umich.edu>
Wed, 4 Apr 2007 14:31:59 +0000 (14:31 +0000)
committerGabe Black <gblack@eecs.umich.edu>
Wed, 4 Apr 2007 14:31:59 +0000 (14:31 +0000)
MicroOp: A single operation actually implemented in hardware.
MacroOp: A collection of microops which are executed as a unit.
Instruction: An architected instruction which can be implemented with a macroop or a microop.

--HG--
extra : convert_revision : 1cfc8409cc686c75220767839f55a30551aa6f13

src/arch/x86/isa/decoder/one_byte_opcodes.isa
src/arch/x86/isa/formats/multi.isa
src/arch/x86/isa/main.isa
src/arch/x86/isa/microasm.isa

index f7e6e39948de4a560c10d0a092600b338fefa259..938904bc1195e7012810e1fa6b8ffcda08591cfd 100644 (file)
 0x1: decode OPCODE_OP_TOP5 {
     format WarnUnimpl {
         0x00: decode OPCODE_OP_BOTTOM3 {
-            0x4: TaggedOp::add({{AddI %0 %0}}, [rAl]);
-            0x5: TaggedOp::add({{AddI %0 %0}}, [rAx]);
+            0x4: Inst::addI(rAl,Ib);
+            0x5: Inst::addI(rAx,Iz);
             0x6: push_ES();
             0x7: pop_ES();
-            default: MultiOp::add(
-                {{Add %0 %0 %1}},
-                OPCODE_OP_BOTTOM3,
-                [[Eb,Gb],[Ev,Gv],
-                 [Gb,Eb],[Gv,Ev]]);
+            default: MultiInst::add(OPCODE_OP_BOTTOM3,
+                             [Eb,Gb],[Ev,Gv],[Gb,Eb],[Gv,Ev]);
         }
         0x01: decode OPCODE_OP_BOTTOM3 {
             0x0: or_Eb_Gb();
             0x7: das();
         }
         0x06: decode OPCODE_OP_BOTTOM3 {
-            0x4: TaggedOp::xor({{XorI %0 %0}}, [rAl]);
-            0x5: TaggedOp::xor({{XorI %0 %0}}, [rAx]);
+            0x4: Inst::xorI(rAl,Ib);
+            0x5: Inst::xorI(rAx,Iz);
             0x6: M5InternalError::error(
                 {{"Tried to execute the SS segment override prefix!"}});
             0x7: aaa();
-            default: MultiOp::xor(
-                {{Xor %0 %0 %1}},
-                OPCODE_OP_BOTTOM3,
-                [[Eb,Gb],[Ev,Gv],
-                 [Gb,Eb],[Gv,Ev]]);
+            default: MultiInst::xor(OPCODE_OP_BOTTOM3,
+                                    [Eb,Gb],[Ev,Gv],[Gb,Eb],[Gv,Ev]);
         }
         0x07: decode OPCODE_OP_BOTTOM3 {
             0x0: cmp_Eb_Gb();
index 9fceec2b014a63be7c4eac3cbe18b29ccfda83a0..7ad5ecd4817909df1e1b062e98b3a08addda4f97 100644 (file)
 //
 
 let {{
-    # This builds either a regular or macro op to implement the sequence of
-    # ops we give it.
-    def genInst(name, Name, ops):
-        # If we can implement this instruction with exactly one microop, just
-        # use that directly.
-        newStmnt = ''
-        if len(ops) == 1:
-            decode_block = "return (X86StaticInst *)(%s);" % \
-                            ops[0].getAllocator()
-            return ('', '', decode_block, '')
-        else:
-            # Build a macroop to contain the sequence of microops we've
-            # been given.
-            return genMacroOp(name, Name, ops)
+    def doInst(name, Name, opTypeSet):
+        if not instDict.has_key(Name):
+            raise Exception, "Unrecognized instruction: %s" % Name
+        inst = instDict[Name]()
+        return inst.emit(opTypeSet)
 }};
 
-let {{
-    # This code builds up a decode block which decodes based on switchval.
-    # vals is a dict which matches case values with what should be decoded to.
-    # builder is called on the exploded contents of "vals" values to generate
-    # whatever code should be used.
-    def doMultiOp(name, Name, builder, switchVal, vals, default = None):
-        header_output = ''
-        decoder_output = ''
-        decode_block = 'switch(%s) {\n' % switchVal
-        exec_output = ''
-        for (val, todo) in vals.items():
-            (new_header_output,
-             new_decoder_output,
-             new_decode_block,
-             new_exec_output) = builder(name, Name, *todo)
-            header_output += new_header_output
-            decoder_output += new_decoder_output
-            decode_block += '\tcase %s: %s\n' % (val, new_decode_block)
-            exec_output += new_exec_output
-        if default:
-            (new_header_output,
-             new_decoder_output,
-             new_decode_block,
-             new_exec_output) = builder(name, Name, *default)
-            header_output += new_header_output
-            decoder_output += new_decoder_output
-            decode_block += '\tdefault: %s\n' % new_decode_block
-            exec_output += new_exec_output
-        decode_block += '}\n'
-        return (header_output, decoder_output, decode_block, exec_output)
-}};
-
-let {{
-
-    # This function specializes the given piece of code to use a particular
-    # set of argument types described by "opTags". These are "implemented"
-    # in reverse order.
-    def doCompOps(name, Name, code, opTags, postfix):
-        opNum = len(opTags) - 1
-        while len(opTags):
-            # print "Building a composite op with tags", opTags
-            # print "And code", code
-            opNum = len(opTags) - 1
-            # A regular expression to find the operand placeholders we're
-            # interested in.
-            opRe = re.compile("%%(?P<operandNum>%d)(?=[^0-9]|$)" % opNum)
-            tag = opTags[opNum]
-            # Build up a name for this instructions class using the argument
-            # types. Each variation will get its own name this way.
-            postfix = '_' + tag + postfix
-            tagParser = re.compile(r"(?P<tagType>[A-Z][A-Z]*)(?P<tagSize>[a-z][a-z]*)|(r(?P<tagReg>[A-Za-z0-9][A-Za-z0-9]*))")
-            tagMatch = tagParser.search(tag)
-            if tagMatch == None:
-                raise Exception, "Problem parsing operand tag %s" % tag
-            reg = tagMatch.group("tagReg")
-            tagType = tagMatch.group("tagType")
-            tagSize = tagMatch.group("tagSize")
-            if reg:
-                #Figure out what to do with fixed register operands
-                if reg in ("Ax", "Bx", "Cx", "Dx"):
-                    code = opRe.sub("{INTREG_R%s}" % reg.upper(), code)
-                elif reg == "Al":
-                    # We need a way to specify register width
-                    code = opRe.sub("{INTREG_RAX}", code)
-                else:
-                    print "Didn't know how to encode fixed register %s!" % reg
-            elif tagType == None or tagSize == None:
-                raise Exception, "Problem parsing operand tag: %s" % tag
-            elif tagType == "C" or tagType == "D" or tagType == "G" or \
-                                   tagType == "P" or tagType == "S" or \
-                                   tagType == "T" or tagType == "V":
-                # Use the "reg" field of the ModRM byte to select the register
-                code = opRe.sub("{(uint8_t)MODRM_REG}", code)
-            elif tagType == "E" or tagType == "Q" or tagType == "W":
-                # This might refer to memory or to a register. We need to
-                # divide it up farther.
-                regCode = opRe.sub("{(uint8_t)MODRM_RM}", code)
-                regTags = copy.copy(opTags)
-                regTags.pop(-1)
-                # This needs to refer to memory, but we'll fill in the details
-                # later. It needs to take into account unaligned memory
-                # addresses.
-                memCode = opRe.sub("0", code)
-                memTags = copy.copy(opTags)
-                memTags.pop(-1)
-                return doMultiOp(name, Name, doCompOps, "MODRM_MOD",
-                    {"3" : (regCode, regTags, postfix)},
-                           (memCode, memTags, postfix))
-            elif tagType == "I" or tagType == "J":
-                # Substitute in an immediate
-                code = opRe.sub("{IMMEDIATE}", code)
-            elif tagType == "M":
-                # This needs to refer to memory, but we'll fill in the details
-                # later. It needs to take into account unaligned memory
-                # addresses.
-                code = opRe.sub("0", code)
-            elif tagType == "PR" or tagType == "R" or tagType == "VR":
-                # There should probably be a check here to verify that mod
-                # is equal to 11b
-                code = opRe.sub("{(uint8_t)MODRM_RM}", code)
-            else:
-                raise Exception, "Unrecognized tag %s." % tag
-            opTags.pop(-1)
-
-        # At this point, we've built up "code" to have all the necessary extra
-        # instructions needed to implement whatever types of operands were
-        # specified. Now we'll assemble it it into a microOp sequence.
-        ops = assembleMicro(code)
-
-        # Build a macroop to contain the sequence of microops we've
-        # constructed. The decode block will be used to fill in our
-        # inner decode structure, and the rest will be concatenated and
-        # passed back.
-        return genInst(name, Name + postfix, ops)
-}};
-
-def format TaggedOp(code, tagSet) {{
+def format Inst(*opTypeSet) {{
     (header_output,
      decoder_output,
      decode_block,
-     exec_output) = doCompOps(name, Name, code, tagSet, '')
+     exec_output) = doInst(name, Name, list(opTypeSet))
 }};
 
-def format MultiOp(code, switchVal, opTags, *opt_flags) {{
+def format MultiInst(switchVal, *opTypeSets) {{
     switcher = {}
-    for (count, tagSet) in zip(xrange(len(opTags) - 1), opTags):
-        switcher[count] = (code, tagSet, '')
+    for (count, opTypeSet) in zip(xrange(len(opTypeSets)), opTypeSets):
+        switcher[count] = (opTypeSet,)
     (header_output,
      decoder_output,
      decode_block,
-     exec_output) = doMultiOp(name, Name, doCompOps, switchVal, switcher)
+     exec_output) = doSplitDecode(name, Name, doInst, switchVal, switcher)
 }};
index fe1d4e5150aeddf93981b4059e4075ceb27ae7d5..cc3a9bee4df58c26b1b48a006c519399a3cc965d 100644 (file)
@@ -84,6 +84,9 @@ namespace X86ISA;
 //Include the base class for x86 instructions, and some support code
 ##include "base.isa"
 
+//Include the instruction definitions
+##include "insts/insts.isa"
+
 //Include the definitions for the instruction formats
 ##include "formats/formats.isa"
 
index 711ebf667286fc3429908d98db0e3905017e2ee0..6d428881eabaf22014b0e838c31cc1845019148b 100644 (file)
 
 ////////////////////////////////////////////////////////////////////
 //
-//  Code to "assemble" microcode sequences
+//  Code to "specialize" a microcode sequence to use a particular
+//  variety of operands
 //
 
 let {{
-    class MicroOpStatement:
+    # This builds either a regular or macro op to implement the sequence of
+    # ops we give it.
+    def genInst(name, Name, ops):
+        # If we can implement this instruction with exactly one microop, just
+        # use that directly.
+        newStmnt = ''
+        if len(ops) == 1:
+            decode_block = "return (X86StaticInst *)(%s);" % \
+                            ops[0].getAllocator()
+            return ('', '', decode_block, '')
+        else:
+            # Build a macroop to contain the sequence of microops we've
+            # been given.
+            return genMacroOp(name, Name, ops)
+}};
+
+let {{
+    # This code builds up a decode block which decodes based on switchval.
+    # vals is a dict which matches case values with what should be decoded to.
+    # builder is called on the exploded contents of "vals" values to generate
+    # whatever code should be used.
+    def doSplitDecode(name, Name, builder, switchVal, vals, default = None):
+        header_output = ''
+        decoder_output = ''
+        decode_block = 'switch(%s) {\n' % switchVal
+        exec_output = ''
+        for (val, todo) in vals.items():
+            (new_header_output,
+             new_decoder_output,
+             new_decode_block,
+             new_exec_output) = builder(name, Name, *todo)
+            header_output += new_header_output
+            decoder_output += new_decoder_output
+            decode_block += '\tcase %s: %s\n' % (val, new_decode_block)
+            exec_output += new_exec_output
+        if default:
+            (new_header_output,
+             new_decoder_output,
+             new_decode_block,
+             new_exec_output) = builder(name, Name, *default)
+            header_output += new_header_output
+            decoder_output += new_decoder_output
+            decode_block += '\tdefault: %s\n' % new_decode_block
+            exec_output += new_exec_output
+        decode_block += '}\n'
+        return (header_output, decoder_output, decode_block, exec_output)
+}};
+
+let {{
+    class OpType(object):
+        parser = re.compile(r"(?P<tag>[A-Z][A-Z]*)(?P<size>[a-z][a-z]*)|(r(?P<reg>[A-Za-z0-9][A-Za-z0-9]*))")
+        def __init__(self, opTypeString):
+            match = OpType.parser.search(opTypeString)
+            if match == None:
+                raise Exception, "Problem parsing operand type %s" % opTypeString
+            self.reg = match.group("reg")
+            self.tag = match.group("tag")
+            self.size = match.group("size")
+}};
+
+let {{
+
+    # This function specializes the given piece of code to use a particular
+    # set of argument types described by "opTypes". These are "implemented"
+    # in reverse order.
+    def specializeInst(name, Name, code, opTypes):
+        opNum = len(opTypes) - 1
+        while len(opTypes):
+            # print "Building a composite op with tags", opTypes
+            # print "And code", code
+            opNum = len(opTypes) - 1
+            # A regular expression to find the operand placeholders we're
+            # interested in.
+            opRe = re.compile("%%(?P<operandNum>%d)(?=[^0-9]|$)" % opNum)
+
+            # Parse the operand type strign we're working with
+            print "About to parse tag %s" % opTypes[opNum]
+            opType = OpType(opTypes[opNum])
+
+            if opType.reg:
+                #Figure out what to do with fixed register operands
+                if opType.reg in ("Ax", "Bx", "Cx", "Dx"):
+                    code = opRe.sub("{INTREG_R%s}" % opType.reg.upper(), code)
+                elif opType.reg == "Al":
+                    # We need a way to specify register width
+                    code = opRe.sub("{INTREG_RAX}", code)
+                else:
+                    print "Didn't know how to encode fixed register %s!" % opType.reg
+            elif opType.tag == None or opType.size == None:
+                raise Exception, "Problem parsing operand tag: %s" % opType.tag
+            elif opType.tag in ("C", "D", "G", "P", "S", "T", "V"):
+                # Use the "reg" field of the ModRM byte to select the register
+                code = opRe.sub("{(uint8_t)MODRM_REG}", code)
+            elif opType.tag in ("E", "Q", "W"):
+                # This might refer to memory or to a register. We need to
+                # divide it up farther.
+                regCode = opRe.sub("{(uint8_t)MODRM_RM}", code)
+                regTypes = copy.copy(opTypes)
+                regTypes.pop(-1)
+                # This needs to refer to memory, but we'll fill in the details
+                # later. It needs to take into account unaligned memory
+                # addresses.
+                memCode = opRe.sub("0", code)
+                memTypes = copy.copy(opTypes)
+                memTypes.pop(-1)
+                return doSplitDecode(name, Name, specializeInst, "MODRM_MOD",
+                    {"3" : (regCode, regTypes)}, (memCode, memTypes))
+            elif opType.tag in ("I", "J"):
+                # Immediates are already in the instruction, so don't leave in
+                # those parameters
+                code = opRe.sub("", code)
+            elif opType.tag == "M":
+                # This needs to refer to memory, but we'll fill in the details
+                # later. It needs to take into account unaligned memory
+                # addresses.
+                code = opRe.sub("0", code)
+            elif opType.tag in ("PR", "R", "VR"):
+                # There should probably be a check here to verify that mod
+                # is equal to 11b
+                code = opRe.sub("{(uint8_t)MODRM_RM}", code)
+            else:
+                raise Exception, "Unrecognized tag %s." % opType.tag
+            opTypes.pop(-1)
+
+        # At this point, we've built up "code" to have all the necessary extra
+        # instructions needed to implement whatever types of operands were
+        # specified. Now we'll assemble it it into a microOp sequence.
+        ops = assembleMicro(code)
+
+        # Build a macroop to contain the sequence of microops we've
+        # constructed. The decode block will be used to fill in our
+        # inner decode structure, and the rest will be concatenated and
+        # passed back.
+        return genInst(name, Name, ops)
+}};
+
+////////////////////////////////////////////////////////////////////
+//
+//  The microcode assembler
+//
+
+let {{
+    class MicroOpStatement(object):
         def __init__(self):
             self.className = ''
             self.label = ''
@@ -101,7 +244,9 @@ let {{
                 labels[op.label] = count
             micropc += 1
         return labels
+}};
 
+let{{
     def assembleMicro(code):
         # This function takes in a block of microcode assembly and returns
         # a python list of objects which describe it.