diff options
author | Steve Reinhardt <stever@eecs.umich.edu> | 2004-05-17 11:49:46 -0700 |
---|---|---|
committer | Steve Reinhardt <stever@eecs.umich.edu> | 2004-05-17 11:49:46 -0700 |
commit | 1d545281b96a6df358201c7b0e610bfaf9e8f213 (patch) | |
tree | 56f869110d789a8efd02a6470b0b74ce718cab68 /arch/isa_parser.py | |
parent | 32a8827b3ea49fe2c9cd25aa88d7bae2adc6d4e6 (diff) | |
download | gem5-1d545281b96a6df358201c7b0e610bfaf9e8f213.tar.xz |
Significant changes to ISA description to completely factor
out CPU model. ISA description now generates multiple
output source files to (in theory) reduce compilation time.
arch/alpha/isa_desc:
Update for parser changes. Move most constructors
out of class declarations (which are now in decoder.hh)
and into decoder.cc. Move all execute() methods into
exec output.
arch/isa_parser.py:
Significant changes to make ISA description completely
independent of CPU model, and isolate model-dependent parts
of parser into one little class (CpuModel). Also split up code
output into multiple files (a header, a main source file, and
per-cpu execute() method files).
Noticeable changes to language as a result. See updated Doxygen
documentation.
cpu/simple_cpu/simple_cpu.hh:
SimpleCPUExecContext typedef no longer needed.
Add forward declaration of Process.
cpu/static_inst.hh:
SimpleCPUExecContext and FullCPUExecContext typedefs no longer needed.
Make eaCompInst() and memAccInst() return const refs.
--HG--
extra : convert_revision : 71471f267804fafd0a881bac7445677e76334daf
Diffstat (limited to 'arch/isa_parser.py')
-rwxr-xr-x | arch/isa_parser.py | 678 |
1 files changed, 438 insertions, 240 deletions
diff --git a/arch/isa_parser.py b/arch/isa_parser.py index 0ee9e2e2d..621720709 100755 --- a/arch/isa_parser.py +++ b/arch/isa_parser.py @@ -63,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. @@ -195,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 @@ -210,163 +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, decode_code, exec_code) = t[4] - decode_code = indent(decode_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 - -%(exec_code)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; -%(decode_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''' +# 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] -# preprocessor directives are copied directly to the output. -def p_cpp_directive(t): - '''cpp_directive : CPPDIRECTIVE''' - 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>]' @@ -376,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): @@ -384,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' @@ -399,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>) {{...}};" @@ -408,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. @@ -453,19 +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, decode_code, exec_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_decode, default_exec) = default_defaults - decls += default_decls - decode_code += default_decode - exec_code += default_exec - t[0] = (decls, ''' -switch (%s) { -%s -} -''' % (t[2], indent(decode_code)), exec_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, @@ -481,8 +425,9 @@ def p_opt_default_0(t): def p_opt_default_1(t): 'opt_default : DEFAULT inst' # push the new default - (decls, decode_code, exec_code) = t[2] - defaultStack.push((decls, '\ndefault:\n%sbreak;' % decode_code, exec_code)) + codeObj = t[2] + codeObj.wrap_decode_block('\ndefault:\n', 'break;\n') + defaultStack.push(codeObj) # no meaningful value returned t[0] = None @@ -492,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, decode_code1, exec_code1, has_default1) = t[1] - (decls2, decode_code2, exec_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, decode_code1 + '\n' + decode_code2, - exec_code1 + '\n' + exec_code2, has_default1 or has_default2) + t[0] = t[1] + t[2] # # Decode statement rules @@ -510,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 @@ -518,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], 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. @@ -547,29 +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, decode_code, exec_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(decode_code)), - exec_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, decode_code, exec_code) = t[3] - t[0] = (decls, '\n%s:%sbreak;' % (label, indent(decode_code)), - exec_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 @@ -591,13 +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, decode_code, exec_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 + decode_code, comment + exec_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>)" @@ -607,10 +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, decode_code, exec_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 + decode_code, comment + exec_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' @@ -652,6 +596,133 @@ 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/simple_cpu.hh"', + { 'CPU_exec_context': 'SimpleCPU' }) +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. # @@ -664,24 +735,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 - c = compile(f, 'def format ' + id, 'exec') - exec(c) + 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. @@ -766,13 +844,13 @@ 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): traceback.print_exc() - print >> sys.stderr, "%s:%d: %s" % (isa_desc_filename, lineno, string) + print >> sys.stderr, "%s:%d: %s" % (input_filename, lineno, string) sys.exit(1) @@ -817,6 +895,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 @@ -1111,6 +1220,22 @@ class NPCOperandTraits(OperandTraits): 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 @@ -1385,10 +1510,6 @@ class InstObjParams: self.mnemonic = mnem self.class_name = class_name self.base_class = base_class - self.exec_func_declarations = ''' - Fault execute(SimpleCPUExecContext *, Trace::InstRecord *); - Fault execute(FullCPUExecContext *, Trace::InstRecord *); -''' if code_block: for code_attr in code_block.__dict__.keys(): setattr(self, code_attr, getattr(code_block, code_attr)) @@ -1419,48 +1540,125 @@ class InstObjParams: else: self.fp_enable_check = '' - def _subst(self, template): - try: - return template % self.__dict__ - except KeyError, key: - raise KeyError, 'InstObjParams.subst: no definition for %s' % key - - def subst(self, *args): - result = [] - for t in args: - try: template = templateMap[t] - except KeyError: - error(0, 'InstObjParams::subst: undefined template "%s"' % t) - if template.find('%(cpu_model)') != -1: - tmp = '' - for cpu_model in ('SimpleCPUExecContext', 'FullCPUExecContext'): - self.cpu_model = cpu_model - tmp += self._subst(template) - result.append(tmp) - else: - result.append(self._subst(template)) - 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() # # Read in and parse the ISA description. # -def parse_isa_desc(isa_desc_file, decoder_file): - # Arguments are the name of the ISA description (input) file and - # the name of the C++ decoder (output) file. - global isa_desc_filename, decoder_filename - isa_desc_filename = isa_desc_file - decoder_filename = decoder_file +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_filename) + input = open(isa_desc_file) isa_desc = input.read() input.close() # Parse it. - yacc.parse(isa_desc) + (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]) + parse_isa_desc(sys.argv[1], sys.argv[2], sys.argv[3]) |