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.
# 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.
# (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
# 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
- *
- * 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 "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>]'
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):
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'
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>) {{...}};"
'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.
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,
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
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
# 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
# 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.
# 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
'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>)"
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'
# 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/simple_cpu.hh"',
+ { 'CPU_exec_context': 'SimpleCPU' })
+CpuModel('FastCPU', 'fast_cpu_exec.cc',
+ '#include "cpu/fast_cpu/fast_cpu.hh"',
+ { 'CPU_exec_context': 'FastCPU' })
+CpuModel('FullCPU', 'full_cpu_exec.cc',
+ '#include "cpu/full_cpu/dyn_inst.hh"',
+ { 'CPU_exec_context': 'DynInst' })
+
+# 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.
#
# 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.
# 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)
#####################################################################
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
(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')
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
(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'):
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' % \
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'):
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
(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'):
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')
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,' \
+ return '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)
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
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):
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')
# 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 = '',
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])