summaryrefslogtreecommitdiff
path: root/src/arch/x86/isa
diff options
context:
space:
mode:
authorGabe Black <gblack@eecs.umich.edu>2007-04-03 15:01:09 +0000
committerGabe Black <gblack@eecs.umich.edu>2007-04-03 15:01:09 +0000
commit61c56ffeaf3288e2a67c479a742cde5ab2d9377a (patch)
treeb8b45bdd677e1d81241a6247b0574a3f8130e592 /src/arch/x86/isa
parent0ce6936e7d5373593f5a468529d30035db62b601 (diff)
downloadgem5-61c56ffeaf3288e2a67c479a742cde5ab2d9377a.tar.xz
A batch of changes and fixes. Macroops are now generated automatically, multiops do alot more of what they're supposed to (excluding memory operands), and microops are slightly more implemented.
--HG-- extra : convert_revision : 518059f47e11df50aa450d4a322ef2ac069c99c9
Diffstat (limited to 'src/arch/x86/isa')
-rw-r--r--src/arch/x86/isa/decoder/one_byte_opcodes.isa18
-rw-r--r--src/arch/x86/isa/formats/formats.isa3
-rw-r--r--src/arch/x86/isa/formats/macroop.isa160
-rw-r--r--src/arch/x86/isa/formats/multi.isa217
-rw-r--r--src/arch/x86/isa/includes.isa7
-rw-r--r--src/arch/x86/isa/microasm.isa71
6 files changed, 365 insertions, 111 deletions
diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
index b4aeece07..f7e6e3994 100644
--- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa
+++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
@@ -61,14 +61,15 @@
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();
@@ -125,15 +126,16 @@
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();
diff --git a/src/arch/x86/isa/formats/formats.isa b/src/arch/x86/isa/formats/formats.isa
index d763c05bc..f4e5c402f 100644
--- a/src/arch/x86/isa/formats/formats.isa
+++ b/src/arch/x86/isa/formats/formats.isa
@@ -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
index 000000000..717103df1
--- /dev/null
+++ b/src/arch/x86/isa/formats/macroop.isa
@@ -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)
+}};
diff --git a/src/arch/x86/isa/formats/multi.isa b/src/arch/x86/isa/formats/multi.isa
index c14e80095..9fceec2b0 100644
--- a/src/arch/x86/isa/formats/multi.isa
+++ b/src/arch/x86/isa/formats/multi.isa
@@ -60,95 +60,152 @@
// 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)
}};
diff --git a/src/arch/x86/isa/includes.isa b/src/arch/x86/isa/includes.isa
index 65e735b03..3440ec5da 100644
--- a/src/arch/x86/isa/includes.isa
+++ b/src/arch/x86/isa/includes.isa
@@ -83,9 +83,14 @@
////////////////////////////////////////////////////////////////////
//
-// 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>
diff --git a/src/arch/x86/isa/microasm.isa b/src/arch/x86/isa/microasm.isa
index 2abce6e7f..711ebf667 100644
--- a/src/arch/x86/isa/microasm.isa
+++ b/src/arch/x86/isa/microasm.isa
@@ -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
}};