Merge zizzer:/bk/m5 into zed.eecs.umich.edu:/z/hsul/work/m5/clean
[gem5.git] / arch / isa_parser.py
index 2e3c0df3540e68920fd1a3a3c5bb5c605486db9d..d5cdd348b2a2e1be8111752589d7df19c42ce7a1 100755 (executable)
@@ -32,20 +32,10 @@ import os
 import sys
 import re
 import string
+import traceback
 # get type names
 from types import *
 
-# Check arguments.  Right now there are only two: the name of the ISA
-# description (input) file and the name of the C++ decoder (output) file.
-isa_desc_filename = sys.argv[1]
-decoder_filename = sys.argv[2]
-
-# Might as well suck the file in while we're here.  This way if it's a
-# bad filename we don't waste a lot of time building the parser :-).
-input = open(isa_desc_filename)
-isa_desc = input.read()
-input.close()
-
 # Prepend the directory where the PLY lex & yacc modules are found
 # to the search path.  Assumes we're compiling in a subdirectory
 # of 'build' in the current tree.
@@ -73,8 +63,9 @@ import yacc
 # using the same regexp as generic IDs, but distinguished in the
 # t_ID() function.  The PLY documentation suggests this approach.
 reserved = (
-    'BITFIELD', 'DECLARE', 'DECODE', 'DEFAULT', 'DEF', 'FORMAT',
-    'LET', 'NAMESPACE', 'SIGNED', 'TEMPLATE'
+    'BITFIELD', 'DECODE', 'DECODER', 'DEFAULT', 'DEF', 'EXEC', 'FORMAT',
+    'HEADER', 'LET', 'NAMESPACE', 'OPERAND_TYPES', 'OPERANDS',
+    'OUTPUT', 'SIGNED', 'TEMPLATE'
     )
 
 # List of tokens.  The lex module requires this.
@@ -205,14 +196,6 @@ lex.lex()
 # (by assigning to t[0]).
 #####################################################################
 
-# Not sure why, but we get a handful of shift/reduce conflicts on DECLARE.
-# By default these get resolved as shifts, which is correct, but
-# warnings are printed.  Explicitly marking DECLARE as right-associative
-# suppresses the warnings.
-precedence = (
-    ('right', 'DECLARE'),
-    )
-
 # The LHS of the first grammar rule is used as the start symbol
 # (in this case, 'specification').  Note that this rule enforces
 # that there will be exactly one namespace declaration, with 0 or more
@@ -220,161 +203,123 @@ precedence = (
 # the namespace decl will be outside the namespace; those after
 # will be inside.  The decoder function is always inside the namespace.
 def p_specification(t):
-    'specification : opt_defs_and_declares name_decl opt_defs_and_declares decode_block'
-    global_decls1 = t[1]
+    'specification : opt_defs_and_outputs name_decl opt_defs_and_outputs decode_block'
+    global_code = t[1]
     isa_name = t[2]
     namespace = isa_name + "Inst"
-    global_decls2 = t[3]
-    (inst_decls, code) = t[4]
-    code = indent(code)
-    # grab the last three path components of isa_desc_filename
-    filename = '/'.join(isa_desc_filename.split('/')[-3:])
-    # if the isa_desc file defines a 'rcs_id' string,
-    # echo that into the output too
-    try:
-        local_rcs_id = rcs_id
-        # strip $s out of ID so it doesn't get re-substituted
-        local_rcs_id = re.sub(r'\$', '', local_rcs_id)
-    except NameError:
-        local_rcs_id = 'Id: no RCS id found'
-    output = open(decoder_filename, 'w')
-    # split string to keep rcs from substituting this file's RCS id in
-    print >> output, '/* $Id' + '''$ */
-
-/*
- * Copyright (c) 2003
- * The Regents of The University of Michigan
- * All Rights Reserved
- *
- * This code is part of the M5 simulator, developed by Nathan Binkert,
- * Erik Hallnor, Steve Raasch, and Steve Reinhardt, with contributions
- * from Ron Dreslinski, Dave Greene, and Lisa Hsu.
- *
- * Permission is granted to use, copy, create derivative works and
- * redistribute this software and such derivative works for any
- * purpose, so long as the copyright notice above, this grant of
- * permission, and the disclaimer below appear in all copies made; and
- * so long as the name of The University of Michigan is not used in
- * any advertising or publicity pertaining to the use or distribution
- * of this software without specific, written prior authorization.
- *
- * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
- * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND
- * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
- * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT,
- * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
- * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGES.
- */
-
-/*
- * DO NOT EDIT THIS FILE!!!
- *
- * It was automatically generated from this ISA description:
- *  Filename: %(filename)s
- *    RCS %(local_rcs_id)s
- */
-
-#include "base/bitfield.hh"    // required for bitfield support
-
-
-/////////////////////////////////////
-// Global defs (outside namespace) //
-/////////////////////////////////////
-
-%(global_decls1)s
-
-/**
- * Namespace for %(isa_name)s static instruction objects.
- */
-namespace %(namespace)s
-{
-
-/////////////////////////////////////
-// Global defs  (within namespace) //
-/////////////////////////////////////
-
-%(global_decls2)s
-
-////////////////////////////////////
-// Declares from inst definitions //
-////////////////////////////////////
-
-%(inst_decls)s
-
-} // namespace %(namespace)s
-
-//////////////////////
-// Decoder function //
-//////////////////////
-
+    # wrap the decode block as a function definition
+    t[4].wrap_decode_block('''
 StaticInstPtr<%(isa_name)s>
 %(isa_name)s::decodeInst(%(isa_name)s::MachInst machInst)
 {
     using namespace %(namespace)s;
-%(code)s
-} // decodeInst
-''' % vars()
-    output.close()
+''' % vars(), '}')
+    # both the latter output blocks and the decode block are in the namespace
+    namespace_code = t[3] + t[4]
+    # pass it all back to the caller of yacc.parse()
+    t[0] = (isa_name, namespace, global_code, namespace_code)
 
 # ISA name declaration looks like "namespace <foo>;"
 def p_name_decl(t):
     'name_decl : NAMESPACE ID SEMI'
     t[0] = t[2]
 
-# 'opt_defs_and_declares' is a possibly empty sequence of
-# defs and/or declares.
-def p_opt_defs_and_declares_0(t):
-    'opt_defs_and_declares : empty'
-    t[0] = ''
+# 'opt_defs_and_outputs' is a possibly empty sequence of
+# def and/or output statements.
+def p_opt_defs_and_outputs_0(t):
+    'opt_defs_and_outputs : empty'
+    t[0] = GenCode()
 
-def p_opt_defs_and_declares_1(t):
-    'opt_defs_and_declares : defs_and_declares'
+def p_opt_defs_and_outputs_1(t):
+    'opt_defs_and_outputs : defs_and_outputs'
     t[0] = t[1]
 
-def p_defs_and_declares_0(t):
-    'defs_and_declares : def_or_declare'
+def p_defs_and_outputs_0(t):
+    'defs_and_outputs : def_or_output'
     t[0] = t[1]
 
-def p_defs_and_declares_1(t):
-    'defs_and_declares : defs_and_declares def_or_declare'
+def p_defs_and_outputs_1(t):
+    'defs_and_outputs : defs_and_outputs def_or_output'
     t[0] = t[1] + t[2]
 
-# The list of possible definition/declaration statements.
-def p_def_or_declare(t):
-    '''def_or_declare : def_format
-                      | def_bitfield
-                      | def_template
-                      | global_declare
-                      | global_let
-                      | cpp_directive'''
-    t[0] = t[1]
-
-# preprocessor directives are copied directly to the output.
-def p_cpp_directive(t):
-    '''cpp_directive : CPPDIRECTIVE'''
+# The list of possible definition/output statements.
+def p_def_or_output(t):
+    '''def_or_output : def_format
+                     | def_bitfield
+                     | def_template
+                     | def_operand_types
+                     | def_operands
+                     | output_header
+                     | output_decoder
+                     | output_exec
+                     | global_let'''
     t[0] = t[1]
 
-# Global declares 'declare {{...}}' (C++ code blocks) are copied
-# directly to the output.
-def p_global_declare(t):
-    'global_declare : DECLARE CODELIT SEMI'
-    t[0] = substBitOps(t[2])
+# Output blocks 'output <foo> {{...}}' (C++ code blocks) are copied
+# directly to the appropriate output section.
+
+# Massage output block by substituting in template definitions and bit
+# operators.  We handle '%'s embedded in the string that don't
+# indicate template substitutions (or CPU-specific symbols, which get
+# handled in GenCode) by doubling them first so that the format
+# operation will reduce them back to single '%'s.
+def process_output(s):
+    # protect any non-substitution '%'s (not followed by '(')
+    s = re.sub(r'%(?!\()', '%%', s)
+    # protects cpu-specific symbols too
+    s = protect_cpu_symbols(s)
+    return substBitOps(s % templateMap)
+
+def p_output_header(t):
+    'output_header : OUTPUT HEADER CODELIT SEMI'
+    t[0] = GenCode(header_output = process_output(t[3]))
+
+def p_output_decoder(t):
+    'output_decoder : OUTPUT DECODER CODELIT SEMI'
+    t[0] = GenCode(decoder_output = process_output(t[3]))
+
+def p_output_exec(t):
+    'output_exec : OUTPUT EXEC CODELIT SEMI'
+    t[0] = GenCode(exec_output = process_output(t[3]))
 
 # global let blocks 'let {{...}}' (Python code blocks) are executed
-# directly when seen.  These are typically used to initialize global
-# Python variables used in later format definitions.
+# directly when seen.  Note that these execute in a special variable
+# context 'exportContext' to prevent the code from polluting this
+# script's namespace.
 def p_global_let(t):
     'global_let : LET CODELIT SEMI'
+    updateExportContext()
+    try:
+        exec fixPythonIndentation(t[2]) in exportContext
+    except Exception, exc:
+        error(t.lineno(1),
+              'error: %s in global let block "%s".' % (exc, t[2]))
+    t[0] = GenCode() # contributes nothing to the output C++ file
+
+# Define the mapping from operand type extensions to C++ types and bit
+# widths (stored in operandTypeMap).
+def p_def_operand_types(t):
+    'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI'
+    s = 'global operandTypeMap; operandTypeMap = {' + t[3] + '}'
     try:
-        exec(fixPythonIndentation(t[2]))
-    except:
-        error_bt(t.lineno(1), 'error in global let block "%s".' % t[2])
-    t[0] = '' # contributes nothing to the output C++ file
+        exec s
+    except Exception, exc:
+        error(t.lineno(1),
+              'error: %s in def operand_types block "%s".' % (exc, t[3]))
+    t[0] = GenCode() # contributes nothing to the output C++ file
+
+# Define the mapping from operand names to operand classes and other
+# traits.  Stored in operandTraitsMap.
+def p_def_operands(t):
+    'def_operands : DEF OPERANDS CODELIT SEMI'
+    s = 'global operandTraitsMap; operandTraitsMap = {' + t[3] + '}'
+    try:
+        exec s
+    except Exception, exc:
+        error(t.lineno(1),
+              'error: %s in def operands block "%s".' % (exc, t[3]))
+    defineDerivedOperandVars()
+    t[0] = GenCode() # contributes nothing to the output C++ file
 
 # A bitfield definition looks like:
 # 'def [signed] bitfield <ID> [<first>:<last>]'
@@ -384,7 +329,8 @@ def p_def_bitfield_0(t):
     expr = 'bits(machInst, %2d, %2d)' % (t[6], t[8])
     if (t[2] == 'signed'):
         expr = 'sext<%d>(%s)' % (t[6] - t[8] + 1, expr)
-    t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
+    hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
+    t[0] = GenCode(header_output = hash_define)
 
 # alternate form for single bit: 'def [signed] bitfield <ID> [<bit>]'
 def p_def_bitfield_1(t):
@@ -392,7 +338,8 @@ def p_def_bitfield_1(t):
     expr = 'bits(machInst, %2d, %2d)' % (t[6], t[6])
     if (t[2] == 'signed'):
         expr = 'sext<%d>(%s)' % (1, expr)
-    t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
+    hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
+    t[0] = GenCode(header_output = hash_define)
 
 def p_opt_signed_0(t):
     'opt_signed : SIGNED'
@@ -407,8 +354,8 @@ templateMap = {}
 
 def p_def_template(t):
     'def_template : DEF TEMPLATE ID CODELIT SEMI'
-    templateMap[t[3]] = t[4]
-    t[0] = ''
+    templateMap[t[3]] = Template(t[4])
+    t[0] = GenCode()
 
 # An instruction format definition looks like
 # "def format <fmt>(<params>) {{...}};"
@@ -416,12 +363,7 @@ def p_def_format(t):
     'def_format : DEF FORMAT ID LPAREN param_list RPAREN CODELIT SEMI'
     (id, params, code) = (t[3], t[5], t[7])
     defFormat(id, params, code, t.lineno(1))
-    # insert a comment into the output to note that the def was processed
-    t[0] = '''
-//
-// parser: format %s defined
-//
-''' % id
+    t[0] = GenCode()
 
 # The formal parameter list for an instruction format is a possibly
 # empty list of comma-separated parameters.
@@ -461,18 +403,13 @@ def p_param_1(t):
 def p_decode_block(t):
     'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE'
     default_defaults = defaultStack.pop()
-    (decls, code, has_default) = t[5]
+    codeObj = t[5]
     # use the "default defaults" only if there was no explicit
     # default statement in decode_stmt_list
-    if not has_default:
-        (default_decls, default_code) = default_defaults
-        decls += default_decls
-        code += default_code
-    t[0] = (decls,  '''
-switch (%s) {
-%s
-}
-''' % (t[2], indent(code)))
+    if not codeObj.has_decode_default:
+        codeObj += default_defaults
+    codeObj.wrap_decode_block('switch (%s) {\n' % t[2], '}\n')
+    t[0] = codeObj
 
 # The opt_default statement serves only to push the "default defaults"
 # onto defaultStack.  This value will be used by nested decode blocks,
@@ -488,8 +425,9 @@ def p_opt_default_0(t):
 def p_opt_default_1(t):
     'opt_default : DEFAULT inst'
     # push the new default
-    (decls, code) = t[2]
-    defaultStack.push((decls, '\ndefault:\n%sbreak;' % code))
+    codeObj = t[2]
+    codeObj.wrap_decode_block('\ndefault:\n', 'break;\n')
+    defaultStack.push(codeObj)
     # no meaningful value returned
     t[0] = None
 
@@ -499,12 +437,9 @@ def p_decode_stmt_list_0(t):
 
 def p_decode_stmt_list_1(t):
     'decode_stmt_list : decode_stmt decode_stmt_list'
-    (decls1, code1, has_default1) = t[1]
-    (decls2, code2, has_default2) = t[2]
-    if (has_default1 and has_default2):
+    if (t[1].has_decode_default and t[2].has_decode_default):
         error(t.lineno(1), 'Two default cases in decode block')
-    t[0] = (decls1 + '\n' + decls2, code1 + '\n' + code2,
-            has_default1 or has_default2)
+    t[0] = t[1] + t[2]
 
 #
 # Decode statement rules
@@ -517,7 +452,7 @@ def p_decode_stmt_list_1(t):
 
 
 # Preprocessor directives found in a decode statement list are passed
-# through to the output, replicated to both the declaration and decode
+# through to the output, replicated to all of the output code
 # streams.  This works well for ifdefs, so we can ifdef out both the
 # declarations and the decode cases generated by an instruction
 # definition.  Handling them as part of the grammar makes it easy to
@@ -525,7 +460,7 @@ def p_decode_stmt_list_1(t):
 # the other statements.
 def p_decode_stmt_cpp(t):
     'decode_stmt : CPPDIRECTIVE'
-    t[0] = (t[1], t[1], 0)
+    t[0] = GenCode(t[1], t[1], t[1], t[1])
 
 # A format block 'format <foo> { ... }' sets the default instruction
 # format used to handle instruction definitions inside the block.
@@ -554,27 +489,31 @@ def p_push_format_id(t):
 # specified constant, do a nested decode on some other field.
 def p_decode_stmt_decode(t):
     'decode_stmt : case_label COLON decode_block'
-    (label, is_default) = t[1]
-    (decls, code) = t[3]
+    label = t[1]
+    codeObj = t[3]
     # just wrap the decoding code from the block as a case in the
     # outer switch statement.
-    t[0] = (decls, '\n%s:\n%s' % (label, indent(code)), is_default)
+    codeObj.wrap_decode_block('\n%s:\n' % label)
+    codeObj.has_decode_default = (label == 'default')
+    t[0] = codeObj
 
 # Instruction definition (finally!).
 def p_decode_stmt_inst(t):
     'decode_stmt : case_label COLON inst SEMI'
-    (label, is_default) = t[1]
-    (decls, code) = t[3]
-    t[0] = (decls, '\n%s:%sbreak;' % (label, indent(code)), is_default)
+    label = t[1]
+    codeObj = t[3]
+    codeObj.wrap_decode_block('\n%s:' % label, 'break;\n')
+    codeObj.has_decode_default = (label == 'default')
+    t[0] = codeObj
 
 # The case label is either a list of one or more constants or 'default'
 def p_case_label_0(t):
     'case_label : intlit_list'
-    t[0] = (': '.join(map(lambda a: 'case %#x' % a, t[1])), 0)
+    t[0] = ': '.join(map(lambda a: 'case %#x' % a, t[1]))
 
 def p_case_label_1(t):
     'case_label : DEFAULT'
-    t[0] = ('default', 1)
+    t[0] = 'default'
 
 #
 # The constant list for a decode case label must be non-empty, but may have
@@ -596,12 +535,13 @@ def p_inst_0(t):
     'inst : ID LPAREN arg_list RPAREN'
     # Pass the ID and arg list to the current format class to deal with.
     currentFormat = formatStack.top()
-    (decls, code) = currentFormat.defineInst(t[1], t[3], t.lineno(1))
+    codeObj = currentFormat.defineInst(t[1], t[3], t.lineno(1))
     args = ','.join(map(str, t[3]))
     args = re.sub('(?m)^', '//', args)
     args = re.sub('^//', '', args)
-    comment = '// %s::%s(%s)\n' % (currentFormat.id, t[1], args)
-    t[0] = (comment + decls, comment + code)
+    comment = '\n// %s::%s(%s)\n' % (currentFormat.id, t[1], args)
+    codeObj.prepend_all(comment)
+    t[0] = codeObj
 
 # Define an instruction using an explicitly specified format:
 # "<fmt>::<mnemonic>(<args>)"
@@ -611,9 +551,10 @@ def p_inst_1(t):
         format = formatMap[t[1]]
     except KeyError:
         error(t.lineno(1), 'instruction format "%s" not defined.' % t[1])
-    (decls, code) = format.defineInst(t[3], t[5], t.lineno(1))
-    comment = '// %s::%s(%s)\n' % (t[1], t[3], t[5])
-    t[0] = (comment + decls, comment + code)
+    codeObj = format.defineInst(t[3], t[5], t.lineno(1))
+    comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5])
+    codeObj.prepend_all(comment)
+    t[0] = codeObj
 
 def p_arg_list_0(t):
     'arg_list : empty'
@@ -655,6 +596,139 @@ def p_error(t):
 # Now build the parser.
 yacc.yacc()
 
+
+#####################################################################
+#
+#                           Support Classes
+#
+#####################################################################
+
+################
+# CpuModel class
+#
+# The CpuModel class encapsulates everything we need to know about a
+# particular CPU model.
+
+class CpuModel:
+    # List of all CPU models.  Accessible as CpuModel.list.
+    list = []
+
+    # Constructor.  Automatically adds models to CpuModel.list.
+    def __init__(self, name, filename, includes, strings):
+        self.name = name
+        self.filename = filename   # filename for output exec code
+        self.includes = includes   # include files needed in exec file
+        # The 'strings' dict holds all the per-CPU symbols we can
+        # substitute into templates etc.
+        self.strings = strings
+        # Add self to list.
+        CpuModel.list.append(self)
+
+# Define CPU models.  The following lines should contain the only
+# CPU-model-specific information in this file.  Note that the ISA
+# description itself should have *no* CPU-model-specific content.
+CpuModel('SimpleCPU', 'simple_cpu_exec.cc',
+         '#include "cpu/simple/cpu.hh"',
+         { 'CPU_exec_context': 'SimpleCPU' })
+CpuModel('FastCPU', 'fast_cpu_exec.cc',
+         '#include "cpu/fast/cpu.hh"',
+         { 'CPU_exec_context': 'FastCPU' })
+CpuModel('FullCPU', 'full_cpu_exec.cc',
+         '#include "encumbered/cpu/full/dyn_inst.hh"',
+         { 'CPU_exec_context': 'DynInst' })
+CpuModel('AlphaFullCPU', 'alpha_o3_exec.cc',
+         '#include "cpu/o3/alpha_dyn_inst.hh"',
+         { 'CPU_exec_context': 'AlphaDynInst<AlphaSimpleImpl>' })
+
+# Expand template with CPU-specific references into a dictionary with
+# an entry for each CPU model name.  The entry key is the model name
+# and the corresponding value is the template with the CPU-specific
+# refs substituted for that model.
+def expand_cpu_symbols_to_dict(template):
+    # Protect '%'s that don't go with CPU-specific terms
+    t = re.sub(r'%(?!\(CPU_)', '%%', template)
+    result = {}
+    for cpu in CpuModel.list:
+        result[cpu.name] = t % cpu.strings
+    return result
+
+# *If* the template has CPU-specific references, return a single
+# string containing a copy of the template for each CPU model with the
+# corresponding values substituted in.  If the template has no
+# CPU-specific references, it is returned unmodified.
+def expand_cpu_symbols_to_string(template):
+    if template.find('%(CPU_') != -1:
+        return reduce(lambda x,y: x+y,
+                      expand_cpu_symbols_to_dict(template).values())
+    else:
+        return template
+
+# Protect CPU-specific references by doubling the corresponding '%'s
+# (in preparation for substituting a different set of references into
+# the template).
+def protect_cpu_symbols(template):
+    return re.sub(r'%(?=\(CPU_)', '%%', template)
+
+###############
+# GenCode class
+#
+# The GenCode class encapsulates generated code destined for various
+# output files.  The header_output and decoder_output attributes are
+# strings containing code destined for decoder.hh and decoder.cc
+# respectively.  The decode_block attribute contains code to be
+# incorporated in the decode function itself (that will also end up in
+# decoder.cc).  The exec_output attribute is a dictionary with a key
+# for each CPU model name; the value associated with a particular key
+# is the string of code for that CPU model's exec.cc file.  The
+# has_decode_default attribute is used in the decode block to allow
+# explicit default clauses to override default default clauses.
+
+class GenCode:
+    # Constructor.  At this point we substitute out all CPU-specific
+    # symbols.  For the exec output, these go into the per-model
+    # dictionary.  For all other output types they get collapsed into
+    # a single string.
+    def __init__(self,
+                 header_output = '', decoder_output = '', exec_output = '',
+                 decode_block = '', has_decode_default = False):
+        self.header_output = expand_cpu_symbols_to_string(header_output)
+        self.decoder_output = expand_cpu_symbols_to_string(decoder_output)
+        if isinstance(exec_output, dict):
+            self.exec_output = exec_output
+        elif isinstance(exec_output, str):
+            # If the exec_output arg is a single string, we replicate
+            # it for each of the CPU models, substituting and
+            # %(CPU_foo)s params appropriately.
+            self.exec_output = expand_cpu_symbols_to_dict(exec_output)
+        self.decode_block = expand_cpu_symbols_to_string(decode_block)
+        self.has_decode_default = has_decode_default
+
+    # Override '+' operator: generate a new GenCode object that
+    # concatenates all the individual strings in the operands.
+    def __add__(self, other):
+        exec_output = {}
+        for cpu in CpuModel.list:
+            n = cpu.name
+            exec_output[n] = self.exec_output[n] + other.exec_output[n]
+        return GenCode(self.header_output + other.header_output,
+                       self.decoder_output + other.decoder_output,
+                       exec_output,
+                       self.decode_block + other.decode_block,
+                       self.has_decode_default or other.has_decode_default)
+
+    # Prepend a string (typically a comment) to all the strings.
+    def prepend_all(self, pre):
+        self.header_output = pre + self.header_output
+        self.decoder_output  = pre + self.decoder_output
+        self.decode_block = pre + self.decode_block
+        for cpu in CpuModel.list:
+            self.exec_output[cpu.name] = pre + self.exec_output[cpu.name]
+
+    # Wrap the decode block in a pair of strings (e.g., 'case foo:'
+    # and 'break;').  Used to build the big nested switch statement.
+    def wrap_decode_block(self, pre, post = ''):
+        self.decode_block = pre + indent(self.decode_block) + post
+
 ################
 # Format object.
 #
@@ -667,23 +741,31 @@ class Format:
         # constructor: just save away arguments
         self.id = id
         self.params = params
-        # strip blank lines from code (ones at the end are troublesome)
-        code = re.sub(r'(?m)^\s*$', '', code);
-        if code == '':
-            code = '    pass\n'
+        label = 'def format ' + id
+        self.user_code = compile(fixPythonIndentation(code), label, 'exec')
         param_list = string.join(params, ", ")
-        f = 'def defInst(name, Name, ' + param_list + '):\n' + code
-        exec(f)
+        f = '''def defInst(_code, _context, %s):
+                my_locals = vars().copy()
+                exec _code in _context, my_locals
+                return my_locals\n''' % param_list
+        c = compile(f, label + ' wrapper', 'exec')
+        exec c
         self.func = defInst
 
     def defineInst(self, name, args, lineno):
-        # automatically provide a capitalized version of mnemonic
-        Name = string.capitalize(name)
+        context = {}
+        updateExportContext()
+        context.update(exportContext)
+        context.update({ 'name': name, 'Name': string.capitalize(name) })
         try:
-            retval = self.func(name, Name, *args)
-        except:
-            error_bt(lineno, 'error defining "%s".' % name)
-        return retval
+            vars = self.func(self.user_code, context, *args)
+        except Exception, exc:
+            error(lineno, 'error defining "%s": %s.' % (name, exc))
+        for k in vars.keys():
+            if k not in ('header_output', 'decoder_output',
+                         'exec_output', 'decode_block'):
+                del vars[k]
+        return GenCode(**vars)
 
 # Special null format to catch an implicit-format instruction
 # definition outside of any format block.
@@ -768,13 +850,14 @@ def fixPythonIndentation(s):
 # Error handler.  Just call exit.  Output formatted to work under
 # Emacs compile-mode.
 def error(lineno, string):
-    sys.exit("%s:%d: %s" % (isa_desc_filename, lineno, string))
+    sys.exit("%s:%d: %s" % (input_filename, lineno, string))
 
 # Like error(), but include a Python stack backtrace (for processing
 # Python exceptions).
 def error_bt(lineno, string):
-    print >> sys.stderr, "%s:%d: %s" % (isa_desc_filename, lineno, string)
-    raise
+    traceback.print_exc()
+    print >> sys.stderr, "%s:%d: %s" % (input_filename, lineno, string)
+    sys.exit(1)
 
 
 #####################################################################
@@ -818,6 +901,37 @@ def substBitOps(code):
     return code
 
 
+####################
+# Template objects.
+#
+# Template objects are format strings that allow substitution from
+# the attribute spaces of other objects (e.g. InstObjParams instances).
+
+class Template:
+    def __init__(self, t):
+        self.template = t
+
+    def subst(self, d):
+        # Start with the template namespace.  Make a copy since we're
+        # going to modify it.
+        myDict = templateMap.copy()
+        # if the argument is a dictionary, we just use it.
+        if isinstance(d, dict):
+            myDict.update(d)
+        # if the argument is an object, we use its attribute map.
+        elif hasattr(d, '__dict__'):
+            myDict.update(d.__dict__)
+        else:
+            raise TypeError, "Template.subst() arg must be or have dictionary"
+        # CPU-model-specific substitutions are handled later (in GenCode).
+        return protect_cpu_symbols(self.template) % myDict
+
+    # Convert to string.  This handles the case when a template with a
+    # CPU-specific term gets interpolated into another template or into
+    # an output block.
+    def __str__(self):
+        return expand_cpu_symbols_to_string(self.template)
+
 #####################################################################
 #
 #                             Code Parser
@@ -944,18 +1058,18 @@ class IntRegOperandTraits(OperandTraits):
                  (op_desc.dest_reg_idx, self.reg_spec)
         return c
 
-    def makeRead(self, op_desc, cpu_model):
+    def makeRead(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         if (type == 'float' or type == 'double'):
             error(0, 'Attempt to read integer register as FP')
         if (size == self.dflt_size):
-            return '%s = xc->readIntReg(_srcRegIdx[%d]);\n' % \
+            return '%s = xc->readIntReg(this, %d);\n' % \
                    (op_desc.munged_name, op_desc.src_reg_idx)
         else:
-            return '%s = bits(xc->readIntReg(_srcRegIdx[%d]), %d, 0);\n' % \
+            return '%s = bits(xc->readIntReg(this, %d), %d, 0);\n' % \
                    (op_desc.munged_name, op_desc.src_reg_idx, size-1)
 
-    def makeWrite(self, op_desc, cpu_model):
+    def makeWrite(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         if (type == 'float' or type == 'double'):
             error(0, 'Attempt to write integer register as FP')
@@ -966,7 +1080,7 @@ class IntRegOperandTraits(OperandTraits):
         wb = '''
         {
             %s final_val = %s;
-            xc->setIntReg(_destRegIdx[%d], final_val);\n
+            xc->setIntReg(this, %d, final_val);\n
             if (traceData) { traceData->setData(final_val); }
         }''' % (self.dflt_type, final_val, op_desc.dest_reg_idx)
         return wb
@@ -988,7 +1102,7 @@ class FloatRegOperandTraits(OperandTraits):
                  (op_desc.dest_reg_idx, self.reg_spec)
         return c
 
-    def makeRead(self, op_desc, cpu_model):
+    def makeRead(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         bit_select = 0
         if (type == 'float'):
@@ -999,7 +1113,7 @@ class FloatRegOperandTraits(OperandTraits):
             func = 'readFloatRegInt'
             if (size != self.dflt_size):
                 bit_select = 1
-        base = 'xc->%s(_srcRegIdx[%d] - FP_Base_DepTag)' % \
+        base = 'xc->%s(this, %d)' % \
                (func, op_desc.src_reg_idx)
         if bit_select:
             return '%s = bits(%s, %d, 0);\n' % \
@@ -1007,7 +1121,7 @@ class FloatRegOperandTraits(OperandTraits):
         else:
             return '%s = %s;\n' % (op_desc.munged_name, base)
 
-    def makeWrite(self, op_desc, cpu_model):
+    def makeWrite(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         final_val = op_desc.munged_name
         if (type == 'float'):
@@ -1022,7 +1136,7 @@ class FloatRegOperandTraits(OperandTraits):
         wb = '''
         {
             %s final_val = %s;
-            xc->%s(_destRegIdx[%d] - FP_Base_DepTag, final_val);\n
+            xc->%s(this, %d, final_val);\n
             if (traceData) { traceData->setData(final_val); }
         }''' % (type, final_val, func, op_desc.dest_reg_idx)
         return wb
@@ -1044,7 +1158,7 @@ class ControlRegOperandTraits(OperandTraits):
                  (op_desc.dest_reg_idx, self.reg_spec)
         return c
 
-    def makeRead(self, op_desc, cpu_model):
+    def makeRead(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         bit_select = 0
         if (type == 'float' or type == 'double'):
@@ -1056,7 +1170,7 @@ class ControlRegOperandTraits(OperandTraits):
             return '%s = bits(%s, %d, 0);\n' % \
                    (op_desc.munged_name, base, size-1)
 
-    def makeWrite(self, op_desc, cpu_model):
+    def makeWrite(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         if (type == 'float' or type == 'double'):
             error(0, 'Attempt to write control register as FP')
@@ -1087,31 +1201,49 @@ class MemOperandTraits(OperandTraits):
             c += 'uint64_t %s_write_result = 0;\n' % op_desc.base_name
         return c
 
-    def makeRead(self, op_desc, cpu_model):
+    def makeRead(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         eff_type = 'uint%d_t' % size
-        return 'fault = memAccessObj->read(EA, (%s&)%s, %s_flags);\n' \
+        return 'fault = xc->read(EA, (%s&)%s, %s_flags);\n' \
                % (eff_type, op_desc.munged_name, op_desc.base_name)
 
-    def makeWrite(self, op_desc, cpu_model):
+    def makeWrite(self, op_desc):
         (size, type, is_signed) = operandSizeMap[op_desc.eff_ext]
         eff_type = 'uint%d_t' % size
-        return 'fault = memAccessObj->write((%s&)%s, EA, %s_flags,' \
-               ' &%s_write_result);\n' \
+        wb = 'fault = xc->write((%s&)%s, EA, %s_flags, &%s_write_result);\n' \
                % (eff_type, op_desc.munged_name, op_desc.base_name,
                   op_desc.base_name)
+        wb += 'if (traceData) { traceData->setData(%s); }' % \
+              op_desc.munged_name
+        return wb
 
 class NPCOperandTraits(OperandTraits):
     def makeConstructor(self, op_desc):
         return ''
 
-    def makeRead(self, op_desc, cpu_model):
+    def makeRead(self, op_desc):
         return '%s = xc->readPC() + 4;\n' % op_desc.munged_name
 
-    def makeWrite(self, op_desc, cpu_model):
+    def makeWrite(self, op_desc):
         return 'xc->setNextPC(%s);\n' % op_desc.munged_name
 
 
+exportContextSymbols = ('IntRegOperandTraits', 'FloatRegOperandTraits',
+                        'ControlRegOperandTraits', 'MemOperandTraits',
+                        'NPCOperandTraits', 'InstObjParams', 'CodeBlock',
+                        're', 'string')
+
+exportContext = {}
+
+def updateExportContext():
+    exportContext.update(exportDict(*exportContextSymbols))
+    exportContext.update(templateMap)
+
+
+def exportDict(*symNames):
+    return dict([(s, eval(s)) for s in symNames])
+
+
 #
 # Define operand variables that get derived from the basic declaration
 # of ISA-specific operands in operandTraitsMap.  This function must be
@@ -1172,21 +1304,17 @@ class OperandDescriptor:
     def finalize(self):
         self.flags = self.traits.getFlags(self)
         self.constructor = self.traits.makeConstructor(self)
-        self.exec_decl = self.traits.makeDecl(self)
+        self.op_decl = self.traits.makeDecl(self)
 
         if self.is_src:
-            self.simple_rd = self.traits.makeRead(self, 'simple')
-            self.dtld_rd = self.traits.makeRead(self, 'dtld')
+            self.op_rd = self.traits.makeRead(self)
         else:
-            self.simple_rd = ''
-            self.dtld_rd = ''
+            self.op_rd = ''
 
         if self.is_dest:
-            self.simple_wb = self.traits.makeWrite(self, 'simple')
-            self.dtld_wb = self.traits.makeWrite(self, 'dtld')
+            self.op_wb = self.traits.makeWrite(self)
         else:
-            self.simple_wb = ''
-            self.dtld_wb = ''
+            self.op_wb = ''
 
 class OperandDescriptorList:
     def __init__(self):
@@ -1348,32 +1476,21 @@ class CodeBlock:
         self.constructor += \
                  '\n\t_numIntDestRegs = %d;' % self.operands.numIntDestRegs
 
-        self.exec_decl = self.operands.concatAttrStrings('exec_decl')
+        self.op_decl = self.operands.concatAttrStrings('op_decl')
 
         is_mem = lambda op: op.traits.isMem()
         not_mem = lambda op: not op.traits.isMem()
 
-        self.simple_rd = self.operands.concatAttrStrings('simple_rd')
-        self.simple_wb = self.operands.concatAttrStrings('simple_wb')
-        self.simple_mem_rd = \
-                 self.operands.concatSomeAttrStrings(is_mem, 'simple_rd')
-        self.simple_mem_wb = \
-                 self.operands.concatSomeAttrStrings(is_mem, 'simple_wb')
-        self.simple_nonmem_rd = \
-                 self.operands.concatSomeAttrStrings(not_mem, 'simple_rd')
-        self.simple_nonmem_wb = \
-                 self.operands.concatSomeAttrStrings(not_mem, 'simple_wb')
-
-        self.dtld_rd = self.operands.concatAttrStrings('dtld_rd')
-        self.dtld_wb = self.operands.concatAttrStrings('dtld_wb')
-        self.dtld_mem_rd = \
-                 self.operands.concatSomeAttrStrings(is_mem, 'dtld_rd')
-        self.dtld_mem_wb = \
-                 self.operands.concatSomeAttrStrings(is_mem, 'dtld_wb')
-        self.dtld_nonmem_rd = \
-                 self.operands.concatSomeAttrStrings(not_mem, 'dtld_rd')
-        self.dtld_nonmem_wb = \
-                 self.operands.concatSomeAttrStrings(not_mem, 'dtld_wb')
+        self.op_rd = self.operands.concatAttrStrings('op_rd')
+        self.op_wb = self.operands.concatAttrStrings('op_wb')
+        self.op_mem_rd = \
+                 self.operands.concatSomeAttrStrings(is_mem, 'op_rd')
+        self.op_mem_wb = \
+                 self.operands.concatSomeAttrStrings(is_mem, 'op_wb')
+        self.op_nonmem_rd = \
+                 self.operands.concatSomeAttrStrings(not_mem, 'op_rd')
+        self.op_nonmem_wb = \
+                 self.operands.concatSomeAttrStrings(not_mem, 'op_wb')
 
         self.flags = self.operands.concatAttrLists('flags')
 
@@ -1381,19 +1498,19 @@ class CodeBlock:
         # These are good enough for most cases, and will be overridden
         # later otherwise.
         if 'IsStore' in self.flags:
-            self.op_class = 'WrPort'
+            self.op_class = 'MemWriteOp'
         elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags:
-            self.op_class = 'RdPort'
+            self.op_class = 'MemReadOp'
         elif 'IsFloating' in self.flags:
-            self.op_class = 'FloatADD'
+            self.op_class = 'FloatAddOp'
         else:
-            self.op_class = 'IntALU'
+            self.op_class = 'IntAluOp'
 
 # Assume all instruction flags are of the form 'IsFoo'
 instFlagRE = re.compile(r'Is.*')
 
-# OpClass constants are just a little more complicated
-opClassRE = re.compile(r'Int.*|Float.*|.*Port|No_OpClass')
+# OpClass constants end in 'Op' except No_OpClass
+opClassRE = re.compile(r'.*Op|No_OpClass')
 
 class InstObjParams:
     def __init__(self, mnem, class_name, base_class = '',
@@ -1431,20 +1548,125 @@ class InstObjParams:
         else:
             self.fp_enable_check = ''
 
-    def subst(self, *args):
-        result = []
-        for t in args:
-            if not templateMap.has_key(t):
-                error(0, 'InstObjParams::subst: undefined template "%s"' % t)
-            try:
-                result.append(templateMap[t] % self.__dict__)
-            except KeyError, key:
-                error(0, 'InstObjParams::subst: no definition for "%s"' % key)
-        if len(args) == 1:
-            result = result[0]
-        return result
+#######################
+#
+# Output file template
+#
+
+file_template = '''
+/*
+ * Copyright (c) 2003
+ * The Regents of The University of Michigan
+ * All Rights Reserved
+ *
+ * This code is part of the M5 simulator, developed by Nathan Binkert,
+ * Erik Hallnor, Steve Raasch, and Steve Reinhardt, with contributions
+ * from Ron Dreslinski, Dave Greene, and Lisa Hsu.
+ *
+ * Permission is granted to use, copy, create derivative works and
+ * redistribute this software and such derivative works for any
+ * purpose, so long as the copyright notice above, this grant of
+ * permission, and the disclaimer below appear in all copies made; and
+ * so long as the name of The University of Michigan is not used in
+ * any advertising or publicity pertaining to the use or distribution
+ * of this software without specific, written prior authorization.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
+ * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND
+ * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT,
+ * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGES.
+ */
+
+/*
+ * DO NOT EDIT THIS FILE!!!
+ *
+ * It was automatically generated from the ISA description in %(filename)s
+ */
+
+%(includes)s
+
+%(global_output)s
+
+namespace %(namespace)s {
+
+%(namespace_output)s
+
+} // namespace %(namespace)s
+'''
+
+
+# Update the output file only if the new contents are different from
+# the current contents.  Minimizes the files that need to be rebuilt
+# after minor changes.
+def update_if_needed(file, contents):
+    update = False
+    if os.access(file, os.R_OK):
+        f = open(file, 'r')
+        old_contents = f.read()
+        f.close()
+        if contents != old_contents:
+            print 'Updating', file
+            os.remove(file) # in case it's write-protected
+            update = True
+        else:
+            print 'File', file, 'is unchanged'
+    else:
+        print 'Generating', file
+        update = True
+    if update:
+        f = open(file, 'w')
+        f.write(contents)
+        f.close()
 
 #
-# All set... read in and parse the ISA description.
+# Read in and parse the ISA description.
 #
-yacc.parse(isa_desc)
+def parse_isa_desc(isa_desc_file, output_dir, include_path):
+    # set a global var for the input filename... used in error messages
+    global input_filename
+    input_filename = isa_desc_file
+
+    # Suck the ISA description file in.
+    input = open(isa_desc_file)
+    isa_desc = input.read()
+    input.close()
+
+    # Parse it.
+    (isa_name, namespace, global_code, namespace_code) = yacc.parse(isa_desc)
+
+    # grab the last three path components of isa_desc_file to put in
+    # the output
+    filename = '/'.join(isa_desc_file.split('/')[-3:])
+
+    # generate decoder.hh
+    includes = '#include "base/bitfield.hh" // for bitfield support'
+    global_output = global_code.header_output
+    namespace_output = namespace_code.header_output
+    update_if_needed(output_dir + '/decoder.hh', file_template % vars())
+
+    # generate decoder.cc
+    includes = '#include "%s/decoder.hh"' % include_path
+    global_output = global_code.decoder_output
+    namespace_output = namespace_code.decoder_output
+    namespace_output += namespace_code.decode_block
+    update_if_needed(output_dir + '/decoder.cc', file_template % vars())
+
+    # generate per-cpu exec files
+    for cpu in CpuModel.list:
+        includes = '#include "%s/decoder.hh"\n' % include_path
+        includes += cpu.includes
+        global_output = global_code.exec_output[cpu.name]
+        namespace_output = namespace_code.exec_output[cpu.name]
+        update_if_needed(output_dir + '/' + cpu.filename,
+                          file_template % vars())
+
+# Called as script: get args from command line.
+if __name__ == '__main__':
+    parse_isa_desc(sys.argv[1], sys.argv[2], sys.argv[3])