A batch of changes and fixes. Macroops are now generated automatically, multiops...
authorGabe Black <gblack@eecs.umich.edu>
Tue, 3 Apr 2007 15:01:09 +0000 (15:01 +0000)
committerGabe Black <gblack@eecs.umich.edu>
Tue, 3 Apr 2007 15:01:09 +0000 (15:01 +0000)
--HG--
extra : convert_revision : 518059f47e11df50aa450d4a322ef2ac069c99c9

src/arch/x86/isa/decoder/one_byte_opcodes.isa
src/arch/x86/isa/formats/formats.isa
src/arch/x86/isa/formats/macroop.isa [new file with mode: 0644]
src/arch/x86/isa/formats/multi.isa
src/arch/x86/isa/includes.isa
src/arch/x86/isa/microasm.isa

index b4aeece07220c82cde1c85b851a024769c8489e2..f7e6e39948de4a560c10d0a092600b338fefa259 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]);
             0x6: push_ES();
             0x7: pop_ES();
             default: MultiOp::add(
                 {{Add %0 %0 %1}},
                 OPCODE_OP_BOTTOM3,
                 [[Eb,Gb],[Ev,Gv],
-                 [Gb,Eb],[Gv,Ev],
-                 [Al,Ib],[rAx,Iz]]);
+                 [Gb,Eb],[Gv,Ev]]);
         }
         0x01: decode OPCODE_OP_BOTTOM3 {
             0x0: or_Eb_Gb();
             0x7: das();
         }
         0x06: decode OPCODE_OP_BOTTOM3 {
-            0x0: xor_Eb_Gb();
-            0x1: xor_Ev_Gv();
-            0x2: xor_Gb_Eb();
-            0x3: xor_Gv_Ev();
-            0x4: xor_Al_Ib();
-            0x5: xor_rAX_Iz();
+            0x4: TaggedOp::xor({{XorI %0 %0}}, [rAl]);
+            0x5: TaggedOp::xor({{XorI %0 %0}}, [rAx]);
             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]]);
         }
         0x07: decode OPCODE_OP_BOTTOM3 {
             0x0: cmp_Eb_Gb();
index d763c05bcaee1902165ce41d99ada201212eb09b..f4e5c402f5da395a94da5bc9e4cab359ed181bd6 100644 (file)
@@ -95,6 +95,9 @@
 //malfunction of the decode mechanism.
 ##include "error.isa"
 
+//Include code to build up macro op instructions
+##include "macroop.isa"
+
 //Include a format which implements a batch of instructions which do the same
 //thing on a variety of inputs
 ##include "multi.isa"
diff --git a/src/arch/x86/isa/formats/macroop.isa b/src/arch/x86/isa/formats/macroop.isa
new file mode 100644 (file)
index 0000000..717103d
--- /dev/null
@@ -0,0 +1,160 @@
+// -*- mode:c++ -*-
+
+// Copyright (c) 2007 The Hewlett-Packard Development Company
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the
+// following conditions are met:
+//
+// The software must be used only for Non-Commercial Use which means any
+// use which is NOT directed to receiving any direct monetary
+// compensation for, or commercial advantage from such use.  Illustrative
+// examples of non-commercial use are academic research, personal study,
+// teaching, education and corporate research & development.
+// Illustrative examples of commercial use are distributing products for
+// commercial advantage and providing services using the software for
+// commercial advantage.
+//
+// If you wish to use this software or functionality therein that may be
+// covered by patents for commercial use, please contact:
+//     Director of Intellectual Property Licensing
+//     Office of Strategy and Technology
+//     Hewlett-Packard Company
+//     1501 Page Mill Road
+//     Palo Alto, California  94304
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.  Redistributions
+// in binary form must reproduce the above copyright notice, this list of
+// conditions and the following disclaimer in the documentation and/or
+// other materials provided with the distribution.  Neither the name of
+// the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.  No right of
+// sublicense is granted herewith.  Derivatives of the software and
+// output created using the software may be prepared, but only for
+// Non-Commercial Uses.  Derivatives of the software may be shared with
+// others provided: (i) the others agree to abide by the list of
+// conditions herein which includes the Non-Commercial Use restrictions;
+// and (ii) such Derivatives of the software include the above copyright
+// notice to acknowledge the contribution from this software where
+// applicable, this list of conditions and the disclaimer below.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Authors: Gabe Black
+
+////////////////////////////////////////////////////////////////////
+//
+// Instructions that do the same thing to multiple sets of arguments.
+//
+
+output header {{
+
+        // Base class for most macroops, except ones that need to commit as
+        // they go.
+        class X86MacroInst : public X86StaticInst
+        {
+          protected:
+            const uint32_t numMicroOps;
+
+            //Constructor.
+            X86MacroInst(const char *mnem, ExtMachInst _machInst,
+                    uint32_t _numMicroOps)
+                        : X86StaticInst(mnem, _machInst, No_OpClass),
+                        numMicroOps(_numMicroOps)
+            {
+                assert(numMicroOps);
+                microOps = new StaticInstPtr[numMicroOps];
+                flags[IsMacroOp] = true;
+            }
+
+            ~X86MacroInst()
+            {
+                delete [] microOps;
+            }
+
+            std::string generateDisassembly(Addr pc,
+                const SymbolTable *symtab) const;
+
+            StaticInstPtr * microOps;
+
+            StaticInstPtr fetchMicroOp(MicroPC microPC)
+            {
+                assert(microPC < numMicroOps);
+                return microOps[microPC];
+            }
+
+            %(BasicExecPanic)s
+        };
+
+        // Base class for macroops which commit as they go. This is for
+        // instructions which can be partially completed like those with the
+        // rep prefix. This prevents those instructions from overflowing
+        // buffers with uncommitted microops.
+        class X86RollingMacroInst : public X86MacroInst
+        {
+          protected:
+            //Constructor.
+            X86RollingMacroInst(const char *mnem, ExtMachInst _machInst,
+                    uint32_t _numMicroOps)
+                        : X86MacroInst(mnem, _machInst, numMicroOps)
+            {}
+        };
+}};
+
+// Basic instruction class constructor template.
+def template MacroConstructor {{
+        inline %(class_name)s::%(class_name)s(ExtMachInst machInst)
+            : %(base_class)s("%(mnemonic)s", machInst, %(num_micro_ops)s)
+        {
+                %(constructor)s;
+                //alloc_micro_ops is the code that sets up the microOps
+                //array in the parent class. This hook will hopefully
+                //allow all that to be automated.
+                %(alloc_micro_ops)s;
+                setMicroFlags();
+        }
+}};
+
+let {{
+    def genMacroOp(name, Name, ops, rolling = False):
+        baseClass = 'X86MacroInst'
+        if rolling:
+            baseClass = 'X86RollingMacroInst'
+        numMicroOps = len(ops)
+        allocMicroOps = ''
+        micropc = 0
+        allocMicroOps += \
+            "microOps[0] = %s;\n" % \
+            op.getAllocator(True, not rolling, True, False)
+        micropc += 1
+        if numMicroOps > 2:
+            for op in ops[1:-1]:
+                allocMicroOps += \
+                    "microOps[%d] = %s;\n" % \
+                    (micropc, op.getAllocator(True, not rolling, False, False))
+                micropc += 1
+        allocMicroOps += \
+            "microOps[%d] = %s;\n" % \
+            op.getAllocator(True, not rolling, False, True)
+        iop = InstObjParams(name, Name, baseClass,
+                {'code' : '', 'num_micro_ops' : numMicroOps,
+                'alloc_micro_ops' : allocMicroOps})
+        header_output = BasicDeclare.subst(iop)
+        decoder_output = MacroConstructor.subst(iop)
+        decode_block = BasicDecode.subst(iop)
+        exec_output = ''
+        return (header_output, decoder_output, decode_block, exec_output)
+}};
index c14e80095bb929469b2a938023228fb12d18303e..9fceec2b014a63be7c4eac3cbe18b29ccfda83a0 100644 (file)
 // Instructions that do the same thing to multiple sets of arguments.
 //
 
-output header {{
-}};
-
-output decoder {{
-}};
-
-output exec {{
+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)
 }};
 
 let {{
-    multiops = {}
+    # 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)
 }};
 
-def format MultiOp(code, switchVal, opTags, *opt_flags) {{
-    # These are C++ statements to create each type of static int. Since we
-    # don't know what will be microcoded and what won't, we can't assume a
-    # particular set of arguments for the constructor.
-    instNew = []
-    orig_code = code
-    opRe = re.compile(r"%(?P<operandNum>[0-9]*)")
-    # Get all the labels out of the code and make a dict for them. We'll do
-    # this once since the position of labels shouldn't need to change at all.
-    ops = assembleMicro(code)
-    labels = buildLabelDict(ops)
-    for tagSet in opTags:
-        # A list of strings which either have the register number to use, or
-        # a piece of code for calculating it.
-        regNums = []
-        code = orig_code
-        # Build up a name for this instructions class using the argument
-        # types. Each variation will get its own name this way.
-        postfix = ''
-        for tag in tagSet:
-            postfix += '_' + tag
-
-        # Figure out what register indexes to use for each operand. This
-        # is where loads/stores could be set up. I need to distinguish
-        # between inputs and outputs.
-        # For right now, the indexes are just an increasing sequence
-        counter = 0
-        for tag in tagSet:
-            regNums.append("%d" % counter)
-            counter += 1
+let {{
 
-        # Replace the placeholders %0, %1, etc., with the right register
-        # indexes.
-        opMatch = opRe.search(code)
-        while opMatch:
-            opNum = opMatch.group("operandNum")
-            opNum = int(opNum)
-            if opNum > len(regNums):
-                print "No operand type specified for operand %d!" % opNum
-                print "I should bail out here too!"
-            regNum = regNums[opNum]
-            code = opRe.sub(regNum, code, 1)
-            opMatch = opRe.search(code)
+    # 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)
 
-        # All the loads which feed this instruction
-        loads = []
-        # All the ops that make up the instruction proper.
+        # 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)
-        # Get all the labels out and make a dict for them
-        # All the stores for this instruction's results
-        stores = []
-
-        # Various counts
-        numLoads = len(loads)
-        numOps = len(ops)
-        numStores = len(stores)
-        totalOps = numLoads + numOps + numStores
-        print "There are %d total ops" % totalOps
 
-        # If we can implement this instruction with exactly one microop, just
-        # use that directly.
-        newStmnt = ''
-        if totalOps == 1:
-            newStmnt = ops[0].getAllocator(labels)
-        else:
-            # Build up a macro op. We'll punt on this for now
-            pass
+        # 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)
+}};
 
-        instNew.append(newStmnt)
+def format TaggedOp(code, tagSet) {{
+    (header_output,
+     decoder_output,
+     decode_block,
+     exec_output) = doCompOps(name, Name, code, tagSet, '')
+}};
 
-    decodeBlob = 'switch(%s) {\n' % switchVal
-    counter = 0
-    for newStmnt in instNew:
-        decodeBlob += 'case %d: return (X86StaticInst *)(%s);\n' % \
-                      (counter, newStmnt)
-        counter += 1
-    decodeBlob += '}\n'
-    decode_block = decodeBlob
+def format MultiOp(code, switchVal, opTags, *opt_flags) {{
+    switcher = {}
+    for (count, tagSet) in zip(xrange(len(opTags) - 1), opTags):
+        switcher[count] = (code, tagSet, '')
+    (header_output,
+     decoder_output,
+     decode_block,
+     exec_output) = doMultiOp(name, Name, doCompOps, switchVal, switcher)
 }};
index 65e735b0396df3434af5ff7d8ecfbe5efdbb0f61..3440ec5da4ddf894c877027c0a2966d6241d06d4 100644 (file)
 
 ////////////////////////////////////////////////////////////////////
 //
-// Output include file directives.
+// Output include file directives. Also import the python modules we
+// need for all the x86 custom decoder stuff
 //
 
+let {{
+    import copy
+}};
+
 output header {{
 #include <cstring>
 #include <sstream>
index 2abce6e7fbf6058e6613957dfabdd6dc23164304..711ebf667286fc3429908d98db0e3905017e2ee0 100644 (file)
@@ -67,7 +67,18 @@ let {{
             self.label = ''
             self.args = []
 
-        def getAllocator(self, labelDict = {}):
+        # This converts a list of python bools into
+        # a comma seperated list of C++ bools.
+        def microFlagsText(self, vals):
+            text = ""
+            for val in vals:
+                if val:
+                    text += ", true"
+                else:
+                    text += ", false"
+            return text
+
+        def getAllocator(self, *microFlags):
             args = ''
             for arg in self.args:
                 if arg.has_key("operandConst"):
@@ -75,13 +86,21 @@ let {{
                 elif arg.has_key("operandCode"):
                     args += ", %s" % arg["operandCode"]
                 elif arg.has_key("operandLabel"):
-                    if not labelDict.has_key(arg["operandLabel"]):
-                        print "Unrecognized label %s!" % arg["operandLabel"]
-                    args += ", %s" % labelDict[arg["operandLabel"]]
+                    raise Exception, "Found a label while creating allocator string."
                 else:
-                    print "Unrecognized operand type!"
-            return 'new %s(machInst %s)' % (self.className, args)
+                    raise Exception, "Unrecognized operand type."
+            return 'new %s(machInst%s%s)' % (self.className, self.microFlagsText(microFlags), args)
+}};
 
+let {{
+    def buildLabelDict(ops):
+        labels = {}
+        micropc = 0
+        for op in ops:
+            if op.label:
+                labels[op.label] = count
+            micropc += 1
+        return labels
 
     def assembleMicro(code):
         # This function takes in a block of microcode assembly and returns
@@ -113,25 +132,26 @@ let {{
             statement = MicroOpStatement()
             # Get a line and seperate it from the rest of the code
             line = lineMatch.group("line")
-            print "Parsing line %s" % line
+            orig_line = line
+            # print "Parsing line %s" % line
             code = lineRe.sub('', code, 1)
 
             # Find the label, if any
             labelMatch = labelRe.search(line)
             if labelMatch != None:
                 statement.label = labelMatch.group("label")
-                print "Found label %s." % statement.label
+                print "Found label %s." % statement.label
             # Clear the label from the statement
             line = labelRe.sub('', line, 1)
 
             # Find the class name which is roughly equivalent to the op name
             classMatch = classRe.search(line)
             if classMatch == None:
-                print "Oh no! I can't find what instruction you want!"
-                print "I should really bail out here, but I don't know how!"
+                raise Exception, "Couldn't find class name in statement: %s" \
+                        % orig_line
             else:
                 statement.className = classMatch.group("className")
-                print "Found class name %s." % statement.className
+                print "Found class name %s." % statement.className
 
             # Clear the class name from the statement
             line = classRe.sub('', line, 1)
@@ -149,24 +169,31 @@ let {{
                     if opMatch.group(opType):
                         statement.args[-1][opType] = opMatch.group(opType)
                 if len(statement.args[-1]) == 0:
-                    print "I had a problem parsing an operand!"
+                    print "Problem parsing operand in statement: %s" \
+                            % orig_line
                 line = opRe.sub('', line, 1)
-                print "Found operand %s." % statement.args[-1]
+                print "Found operand %s." % statement.args[-1]
                 opMatch = opRe.search(line)
-            print "Found operands", statement.args
+            print "Found operands", statement.args
 
             # Add this statement to our collection
             statements.append(statement)
 
             # Get the next line
             lineMatch = lineRe.search(code)
-        return statements
 
-    def buildLabelDict(ops):
-        labels = {}
-        count = 0
-        for op in ops:
-            if op.label:
-                labels[op.label] = count
-            count += 1
+        # Decode the labels into displacements
+        labels = buildLabelDict(statements)
+        micropc = 0
+        for statement in statements:
+            for arg in statement.args:
+                if arg.has_key("operandLabel"):
+                    if not labels.has_key(arg["operandLabel"]):
+                        raise Exception, "Unrecognized label: %s." % arg["operandLabel"]
+                    # This is assuming that intra microcode branches go to
+                    # the next micropc + displacement, or
+                    # micropc + 1 + displacement.
+                    arg["operandConst"] = labels[arg["operandLabel"]] - micropc - 1
+            micropc += 1
+        return statements
 }};