+# Copyright (c) 2014 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
# Copyright (c) 2003-2005 The Regents of The University of Michigan
# Copyright (c) 2013 Advanced Micro Devices, Inc.
# All rights reserved.
#
# Authors: Steve Reinhardt
+from __future__ import with_statement
import os
import sys
import re
self.parser = parser
self.header_output = parser.expandCpuSymbolsToString(header_output)
self.decoder_output = parser.expandCpuSymbolsToString(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 = parser.expandCpuSymbolsToDict(exec_output)
- self.decode_block = parser.expandCpuSymbolsToString(decode_block)
+ self.exec_output = exec_output
+ self.decode_block = decode_block
self.has_decode_default = has_decode_default
+ # Write these code chunks out to the filesystem. They will be properly
+ # interwoven by the write_top_level_files().
+ def emit(self):
+ if self.header_output:
+ self.parser.get_file('header').write(self.header_output)
+ if self.decoder_output:
+ self.parser.get_file('decoder').write(self.decoder_output)
+ if self.exec_output:
+ self.parser.get_file('exec').write(self.exec_output)
+ if self.decode_block:
+ self.parser.get_file('decode_block').write(self.decode_block)
+
# 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 self.parser.cpuModels:
- n = cpu.name
- exec_output[n] = self.exec_output[n] + other.exec_output[n]
return GenCode(self.parser,
self.header_output + other.header_output,
self.decoder_output + other.decoder_output,
- exec_output,
+ self.exec_output + other.exec_output,
self.decode_block + other.decode_block,
self.has_decode_default or other.has_decode_default)
self.header_output = pre + self.header_output
self.decoder_output = pre + self.decoder_output
self.decode_block = pre + self.decode_block
- for cpu in self.parser.cpuModels:
- self.exec_output[cpu.name] = pre + self.exec_output[cpu.name]
+ self.exec_output = pre + self.exec_output
# Wrap the decode block in a pair of strings (e.g., 'case foo:'
# and 'break;'). Used to build the big nested switch statement.
ctype = 'TheISA::PCState'
if self.isPCPart():
ctype = self.ctype
- return "%s %s;\n" % (ctype, self.base_name)
+ # Note that initializations in the declarations are solely
+ # to avoid 'uninitialized variable' errors from the compiler.
+ return '%s %s = 0;\n' % (ctype, self.base_name)
def isPCState(self):
return 1
self.flags = self.operands.concatAttrLists('flags')
- # Make a basic guess on the operand class (function unit type).
- # These are good enough for most cases, and can be overridden
- # later otherwise.
- if 'IsStore' in self.flags:
- self.op_class = 'MemWriteOp'
- elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags:
- self.op_class = 'MemReadOp'
- elif 'IsFloating' in self.flags:
- self.op_class = 'FloatAddOp'
- else:
- self.op_class = 'IntAluOp'
+ self.op_class = None
# Optional arguments are assumed to be either StaticInst flags
# or an OpClass value. To avoid having to import a complete
error('InstObjParams: optional arg "%s" not recognized '
'as StaticInst::Flag or OpClass.' % oa)
+ # Make a basic guess on the operand class if not set.
+ # These are good enough for most cases.
+ if not self.op_class:
+ if 'IsStore' in self.flags:
+ self.op_class = 'MemWriteOp'
+ elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags:
+ self.op_class = 'MemReadOp'
+ elif 'IsFloating' in self.flags:
+ self.op_class = 'FloatAddOp'
+ else:
+ self.op_class = 'IntAluOp'
+
# add flag initialization to contructor here to include
# any flags added via opt_args
self.constructor += makeFlagConstructor(self.flags)
#######################
#
-# Output file template
+# ISA Parser
+# parses ISA DSL and emits C++ headers and source
#
-file_template = '''
-/*
- * 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
-
-%(decode_function)s
-'''
-
-max_inst_regs_template = '''
-/*
- * DO NOT EDIT THIS FILE!!!
- *
- * It was automatically generated from the ISA description in %(filename)s
- */
-
-namespace %(namespace)s {
-
- const int MaxInstSrcRegs = %(MaxInstSrcRegs)d;
- const int MaxInstDestRegs = %(MaxInstDestRegs)d;
- const int MaxMiscDestRegs = %(MaxMiscDestRegs)d;
-
-} // namespace %(namespace)s
-
-'''
-
class ISAParser(Grammar):
- def __init__(self, output_dir, cpu_models):
+ class CpuModel(object):
+ def __init__(self, name, filename, includes, strings):
+ self.name = name
+ self.filename = filename
+ self.includes = includes
+ self.strings = strings
+
+ def __init__(self, output_dir):
super(ISAParser, self).__init__()
self.output_dir = output_dir
- self.cpuModels = cpu_models
+ self.filename = None # for output file watermarking/scaremongering
+
+ self.cpuModels = [
+ ISAParser.CpuModel('ExecContext',
+ 'generic_cpu_exec.cc',
+ '#include "cpu/exec_context.hh"',
+ { "CPU_exec_context" : "ExecContext" }),
+ ]
# variable to hold templates
self.templateMap = {}
# This dictionary maps format name strings to Format objects.
self.formatMap = {}
+ # Track open files and, if applicable, how many chunks it has been
+ # split into so far.
+ self.files = {}
+ self.splits = {}
+
+ # isa_name / namespace identifier from namespace declaration.
+ # before the namespace declaration, None.
+ self.isa_name = None
+ self.namespace = None
+
# The format stack.
self.formatStack = Stack(NoFormat())
self.maxInstDestRegs = 0
self.maxMiscDestRegs = 0
+ def __getitem__(self, i): # Allow object (self) to be
+ return getattr(self, i) # passed to %-substitutions
+
+ # Change the file suffix of a base filename:
+ # (e.g.) decoder.cc -> decoder-g.cc.inc for 'global' outputs
+ def suffixize(self, s, sec):
+ extn = re.compile('(\.[^\.]+)$') # isolate extension
+ if self.namespace:
+ return extn.sub(r'-ns\1.inc', s) # insert some text on either side
+ else:
+ return extn.sub(r'-g\1.inc', s)
+
+ # Get the file object for emitting code into the specified section
+ # (header, decoder, exec, decode_block).
+ def get_file(self, section):
+ if section == 'decode_block':
+ filename = 'decode-method.cc.inc'
+ else:
+ if section == 'header':
+ file = 'decoder.hh'
+ else:
+ file = '%s.cc' % section
+ filename = self.suffixize(file, section)
+ try:
+ return self.files[filename]
+ except KeyError: pass
+
+ f = self.open(filename)
+ self.files[filename] = f
+
+ # The splittable files are the ones with many independent
+ # per-instruction functions - the decoder's instruction constructors
+ # and the instruction execution (execute()) methods. These both have
+ # the suffix -ns.cc.inc, meaning they are within the namespace part
+ # of the ISA, contain object-emitting C++ source, and are included
+ # into other top-level files. These are the files that need special
+ # #define's to allow parts of them to be compiled separately. Rather
+ # than splitting the emissions into separate files, the monolithic
+ # output of the ISA parser is maintained, but the value (or lack
+ # thereof) of the __SPLIT definition during C preprocessing will
+ # select the different chunks. If no 'split' directives are used,
+ # the cpp emissions have no effect.
+ if re.search('-ns.cc.inc$', filename):
+ print >>f, '#if !defined(__SPLIT) || (__SPLIT == 1)'
+ self.splits[f] = 1
+ # ensure requisite #include's
+ elif filename in ['decoder-g.cc.inc', 'exec-g.cc.inc']:
+ print >>f, '#include "decoder.hh"'
+ elif filename == 'decoder-g.hh.inc':
+ print >>f, '#include "base/bitfield.hh"'
+
+ return f
+
+ # Weave together the parts of the different output sections by
+ # #include'ing them into some very short top-level .cc/.hh files.
+ # These small files make it much clearer how this tool works, since
+ # you directly see the chunks emitted as files that are #include'd.
+ def write_top_level_files(self):
+ dep = self.open('inc.d', bare=True)
+
+ # decoder header - everything depends on this
+ file = 'decoder.hh'
+ with self.open(file) as f:
+ inc = []
+
+ fn = 'decoder-g.hh.inc'
+ assert(fn in self.files)
+ f.write('#include "%s"\n' % fn)
+ inc.append(fn)
+
+ fn = 'decoder-ns.hh.inc'
+ assert(fn in self.files)
+ f.write('namespace %s {\n#include "%s"\n}\n'
+ % (self.namespace, fn))
+ inc.append(fn)
+
+ print >>dep, file+':', ' '.join(inc)
+
+ # decoder method - cannot be split
+ file = 'decoder.cc'
+ with self.open(file) as f:
+ inc = []
+
+ fn = 'decoder-g.cc.inc'
+ assert(fn in self.files)
+ f.write('#include "%s"\n' % fn)
+ inc.append(fn)
+
+ fn = 'decode-method.cc.inc'
+ # is guaranteed to have been written for parse to complete
+ f.write('#include "%s"\n' % fn)
+ inc.append(fn)
+
+ inc.append("decoder.hh")
+ print >>dep, file+':', ' '.join(inc)
+
+ extn = re.compile('(\.[^\.]+)$')
+
+ # instruction constructors
+ splits = self.splits[self.get_file('decoder')]
+ file_ = 'inst-constrs.cc'
+ for i in range(1, splits+1):
+ if splits > 1:
+ file = extn.sub(r'-%d\1' % i, file_)
+ else:
+ file = file_
+ with self.open(file) as f:
+ inc = []
+
+ fn = 'decoder-g.cc.inc'
+ assert(fn in self.files)
+ f.write('#include "%s"\n' % fn)
+ inc.append(fn)
+
+ fn = 'decoder-ns.cc.inc'
+ assert(fn in self.files)
+ print >>f, 'namespace %s {' % self.namespace
+ if splits > 1:
+ print >>f, '#define __SPLIT %u' % i
+ print >>f, '#include "%s"' % fn
+ print >>f, '}'
+ inc.append(fn)
+
+ inc.append("decoder.hh")
+ print >>dep, file+':', ' '.join(inc)
+
+ # instruction execution per-CPU model
+ splits = self.splits[self.get_file('exec')]
+ for cpu in self.cpuModels:
+ for i in range(1, splits+1):
+ if splits > 1:
+ file = extn.sub(r'_%d\1' % i, cpu.filename)
+ else:
+ file = cpu.filename
+ with self.open(file) as f:
+ inc = []
+
+ fn = 'exec-g.cc.inc'
+ assert(fn in self.files)
+ f.write('#include "%s"\n' % fn)
+ inc.append(fn)
+
+ f.write(cpu.includes+"\n")
+
+ fn = 'exec-ns.cc.inc'
+ assert(fn in self.files)
+ print >>f, 'namespace %s {' % self.namespace
+ print >>f, '#define CPU_EXEC_CONTEXT %s' \
+ % cpu.strings['CPU_exec_context']
+ if splits > 1:
+ print >>f, '#define __SPLIT %u' % i
+ print >>f, '#include "%s"' % fn
+ print >>f, '}'
+ inc.append(fn)
+
+ inc.append("decoder.hh")
+ print >>dep, file+':', ' '.join(inc)
+
+ # max_inst_regs.hh
+ self.update('max_inst_regs.hh',
+ '''namespace %(namespace)s {
+ const int MaxInstSrcRegs = %(maxInstSrcRegs)d;
+ const int MaxInstDestRegs = %(maxInstDestRegs)d;
+ const int MaxMiscDestRegs = %(maxMiscDestRegs)d;\n}\n''' % self)
+ print >>dep, 'max_inst_regs.hh:'
+
+ dep.close()
+
+
+ scaremonger_template ='''// DO NOT EDIT
+// This file was automatically generated from an ISA description:
+// %(filename)s
+
+''';
+
#####################################################################
#
# Lexer
reserved = (
'BITFIELD', 'DECODE', 'DECODER', 'DEFAULT', 'DEF', 'EXEC', 'FORMAT',
'HEADER', 'LET', 'NAMESPACE', 'OPERAND_TYPES', 'OPERANDS',
- 'OUTPUT', 'SIGNED', 'TEMPLATE'
+ 'OUTPUT', 'SIGNED', 'SPLIT', 'TEMPLATE'
)
# List of tokens. The lex module requires this.
# after will be inside. The decoder function is always inside the
# namespace.
def p_specification(self, t):
- '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"
- # wrap the decode block as a function definition
- t[4].wrap_decode_block('''
-StaticInstPtr
-%(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst)
-{
- using namespace %(namespace)s;
-''' % 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)
+ 'specification : opt_defs_and_outputs top_level_decode_block'
- # ISA name declaration looks like "namespace <foo>;"
- def p_name_decl(self, t):
- 'name_decl : NAMESPACE ID SEMI'
- t[0] = t[2]
+ for f in self.splits.iterkeys():
+ f.write('\n#endif\n')
+
+ for f in self.files.itervalues(): # close ALL the files;
+ f.close() # not doing so can cause compilation to fail
- # 'opt_defs_and_outputs' is a possibly empty sequence of
- # def and/or output statements.
+ self.write_top_level_files()
+
+ t[0] = True
+
+ # 'opt_defs_and_outputs' is a possibly empty sequence of def and/or
+ # output statements. Its productions do the hard work of eventually
+ # instantiating a GenCode, which are generally emitted (written to disk)
+ # as soon as possible, except for the decode_block, which has to be
+ # accumulated into one large function of nested switch/case blocks.
def p_opt_defs_and_outputs_0(self, t):
'opt_defs_and_outputs : empty'
- t[0] = GenCode(self)
def p_opt_defs_and_outputs_1(self, t):
'opt_defs_and_outputs : defs_and_outputs'
- t[0] = t[1]
def p_defs_and_outputs_0(self, t):
'defs_and_outputs : def_or_output'
- t[0] = t[1]
def p_defs_and_outputs_1(self, t):
'defs_and_outputs : defs_and_outputs def_or_output'
- t[0] = t[1] + t[2]
# The list of possible definition/output statements.
+ # They are all processed as they are seen.
def p_def_or_output(self, t):
- '''def_or_output : def_format
+ '''def_or_output : name_decl
+ | def_format
| def_bitfield
| def_bitfield_struct
| def_template
| def_operand_types
| def_operands
- | output_header
- | output_decoder
- | output_exec
- | global_let'''
+ | output
+ | global_let
+ | split'''
+
+ # Utility function used by both invocations of splitting - explicit
+ # 'split' keyword and split() function inside "let {{ }};" blocks.
+ def split(self, sec, write=False):
+ assert(sec != 'header' and "header cannot be split")
+
+ f = self.get_file(sec)
+ self.splits[f] += 1
+ s = '\n#endif\n#if __SPLIT == %u\n' % self.splits[f]
+ if write:
+ f.write(s)
+ else:
+ return s
+
+ # split output file to reduce compilation time
+ def p_split(self, t):
+ 'split : SPLIT output_type SEMI'
+ assert(self.isa_name and "'split' not allowed before namespace decl")
+
+ self.split(t[2], True)
+
+ def p_output_type(self, t):
+ '''output_type : DECODER
+ | HEADER
+ | EXEC'''
t[0] = t[1]
+ # ISA name declaration looks like "namespace <foo>;"
+ def p_name_decl(self, t):
+ 'name_decl : NAMESPACE ID SEMI'
+ assert(self.isa_name == None and "Only 1 namespace decl permitted")
+ self.isa_name = t[2]
+ self.namespace = t[2] + 'Inst'
+
# Output blocks 'output <foo> {{...}}' (C++ code blocks) are copied
# directly to the appropriate output section.
s = self.protectCpuSymbols(s)
return substBitOps(s % self.templateMap)
- def p_output_header(self, t):
- 'output_header : OUTPUT HEADER CODELIT SEMI'
- t[0] = GenCode(self, header_output = self.process_output(t[3]))
-
- def p_output_decoder(self, t):
- 'output_decoder : OUTPUT DECODER CODELIT SEMI'
- t[0] = GenCode(self, decoder_output = self.process_output(t[3]))
-
- def p_output_exec(self, t):
- 'output_exec : OUTPUT EXEC CODELIT SEMI'
- t[0] = GenCode(self, exec_output = self.process_output(t[3]))
+ def p_output(self, t):
+ 'output : OUTPUT output_type CODELIT SEMI'
+ kwargs = { t[2]+'_output' : self.process_output(t[3]) }
+ GenCode(self, **kwargs).emit()
# global let blocks 'let {{...}}' (Python code blocks) are
# executed directly when seen. Note that these execute in a
# from polluting this script's namespace.
def p_global_let(self, t):
'global_let : LET CODELIT SEMI'
+ def _split(sec):
+ return self.split(sec)
self.updateExportContext()
self.exportContext["header_output"] = ''
self.exportContext["decoder_output"] = ''
self.exportContext["exec_output"] = ''
self.exportContext["decode_block"] = ''
+ self.exportContext["split"] = _split
+ split_setup = '''
+def wrap(func):
+ def split(sec):
+ globals()[sec + '_output'] += func(sec)
+ return split
+split = wrap(split)
+del wrap
+'''
+ # This tricky setup (immediately above) allows us to just write
+ # (e.g.) "split('exec')" in the Python code and the split #ifdef's
+ # will automatically be added to the exec_output variable. The inner
+ # Python execution environment doesn't know about the split points,
+ # so we carefully inject and wrap a closure that can retrieve the
+ # next split's #define from the parser and add it to the current
+ # emission-in-progress.
try:
- exec fixPythonIndentation(t[2]) in self.exportContext
+ exec split_setup+fixPythonIndentation(t[2]) in self.exportContext
except Exception, exc:
if debug:
raise
error(t, 'error: %s in global let block "%s".' % (exc, t[2]))
- t[0] = GenCode(self,
- header_output=self.exportContext["header_output"],
- decoder_output=self.exportContext["decoder_output"],
- exec_output=self.exportContext["exec_output"],
- decode_block=self.exportContext["decode_block"])
+ GenCode(self,
+ header_output=self.exportContext["header_output"],
+ decoder_output=self.exportContext["decoder_output"],
+ exec_output=self.exportContext["exec_output"],
+ decode_block=self.exportContext["decode_block"]).emit()
# Define the mapping from operand type extensions to C++ types and
# bit widths (stored in operandTypeMap).
raise
error(t,
'error: %s in def operand_types block "%s".' % (exc, t[3]))
- t[0] = GenCode(self) # contributes nothing to the output C++ file
# Define the mapping from operand names to operand classes and
# other traits. Stored in operandNameMap.
raise
error(t, 'error: %s in def operands block "%s".' % (exc, t[3]))
self.buildOperandNameMap(user_dict, t.lexer.lineno)
- t[0] = GenCode(self) # contributes nothing to the output C++ file
# A bitfield definition looks like:
# 'def [signed] bitfield <ID> [<first>:<last>]'
if (t[2] == 'signed'):
expr = 'sext<%d>(%s)' % (t[6] - t[8] + 1, expr)
hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
- t[0] = GenCode(self, header_output=hash_define)
+ GenCode(self, header_output=hash_define).emit()
# alternate form for single bit: 'def [signed] bitfield <ID> [<bit>]'
def p_def_bitfield_1(self, t):
if (t[2] == 'signed'):
expr = 'sext<%d>(%s)' % (1, expr)
hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
- t[0] = GenCode(self, header_output=hash_define)
+ GenCode(self, header_output=hash_define).emit()
# alternate form for structure member: 'def bitfield <ID> <ID>'
def p_def_bitfield_struct(self, t):
error(t, 'error: structure bitfields are always unsigned.')
expr = 'machInst.%s' % t[5]
hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr)
- t[0] = GenCode(self, header_output=hash_define)
+ GenCode(self, header_output=hash_define).emit()
def p_id_with_dot_0(self, t):
'id_with_dot : ID'
def p_def_template(self, t):
'def_template : DEF TEMPLATE ID CODELIT SEMI'
+ if t[3] in self.templateMap:
+ print "warning: template %s already defined" % t[3]
self.templateMap[t[3]] = Template(self, t[4])
- t[0] = GenCode(self)
# 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])
self.defFormat(id, params, code, t.lexer.lineno)
- t[0] = GenCode(self)
# The formal parameter list for an instruction format is a
# possibly empty list of comma-separated parameters. Positional
# A decode block looks like:
# decode <field1> [, <field2>]* [default <inst>] { ... }
#
+ def p_top_level_decode_block(self, t):
+ 'top_level_decode_block : decode_block'
+ codeObj = t[1]
+ codeObj.wrap_decode_block('''
+StaticInstPtr
+%(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst)
+{
+ using namespace %(namespace)s;
+''' % self, '}')
+
+ codeObj.emit()
+
def p_decode_block(self, t):
'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE'
default_defaults = self.defaultStack.pop()
error(t, 'instruction format "%s" not defined.' % t[1])
# Nested decode block: if the value of the current field matches
- # the specified constant, do a nested decode on some other field.
+ # the specified constant(s), do a nested decode on some other field.
def p_decode_stmt_decode(self, t):
- 'decode_stmt : case_label COLON decode_block'
- label = t[1]
+ 'decode_stmt : case_list COLON decode_block'
+ case_list = t[1]
codeObj = t[3]
# just wrap the decoding code from the block as a case in the
# outer switch statement.
- codeObj.wrap_decode_block('\n%s:\n' % label)
- codeObj.has_decode_default = (label == 'default')
+ codeObj.wrap_decode_block('\n%s\n' % ''.join(case_list))
+ codeObj.has_decode_default = (case_list == ['default:'])
t[0] = codeObj
# Instruction definition (finally!).
def p_decode_stmt_inst(self, t):
- 'decode_stmt : case_label COLON inst SEMI'
- label = t[1]
+ 'decode_stmt : case_list COLON inst SEMI'
+ case_list = t[1]
codeObj = t[3]
- codeObj.wrap_decode_block('\n%s:' % label, 'break;\n')
- codeObj.has_decode_default = (label == 'default')
+ codeObj.wrap_decode_block('\n%s' % ''.join(case_list), 'break;\n')
+ codeObj.has_decode_default = (case_list == ['default:'])
t[0] = codeObj
- # The case label is either a list of one or more constants or
- # 'default'
- def p_case_label_0(self, t):
- 'case_label : intlit_list'
- def make_case(intlit):
- if intlit >= 2**32:
- return 'case ULL(%#x)' % intlit
- else:
- return 'case %#x' % intlit
- t[0] = ': '.join(map(make_case, t[1]))
+ # The constant list for a decode case label must be non-empty, and must
+ # either be the keyword 'default', or made up of one or more
+ # comma-separated integer literals or strings which evaluate to
+ # constants when compiled as C++.
+ def p_case_list_0(self, t):
+ 'case_list : DEFAULT'
+ t[0] = ['default:']
+
+ def prep_int_lit_case_label(self, lit):
+ if lit >= 2**32:
+ return 'case ULL(%#x): ' % lit
+ else:
+ return 'case %#x: ' % lit
- def p_case_label_1(self, t):
- 'case_label : DEFAULT'
- t[0] = 'default'
+ def prep_str_lit_case_label(self, lit):
+ return 'case %s: ' % lit
- #
- # The constant list for a decode case label must be non-empty, but
- # may have one or more comma-separated integer literals in it.
- #
- def p_intlit_list_0(self, t):
- 'intlit_list : INTLIT'
- t[0] = [t[1]]
+ def p_case_list_1(self, t):
+ 'case_list : INTLIT'
+ t[0] = [self.prep_int_lit_case_label(t[1])]
- def p_intlit_list_1(self, t):
- 'intlit_list : intlit_list COMMA INTLIT'
+ def p_case_list_2(self, t):
+ 'case_list : STRLIT'
+ t[0] = [self.prep_str_lit_case_label(t[1])]
+
+ def p_case_list_3(self, t):
+ 'case_list : case_list COMMA INTLIT'
+ t[0] = t[1]
+ t[0].append(self.prep_int_lit_case_label(t[3]))
+
+ def p_case_list_4(self, t):
+ 'case_list : case_list COMMA STRLIT'
t[0] = t[1]
- t[0].append(t[3])
+ t[0].append(self.prep_str_lit_case_label(t[3]))
# Define an instruction using the current instruction format
# (specified by an enclosing format block).
else:
return s
+ def open(self, name, bare=False):
+ '''Open the output file for writing and include scary warning.'''
+ filename = os.path.join(self.output_dir, name)
+ f = open(filename, 'w')
+ if f:
+ if not bare:
+ f.write(ISAParser.scaremonger_template % self)
+ return f
+
def update(self, file, contents):
- '''Update the output file. If the contents are unchanged,
- the scons hash feature will avoid recompilation.'''
- file = os.path.join(self.output_dir, file)
- f = open(file, 'w')
+ '''Update the output file only. Scons should handle the case when
+ the new contents are unchanged using its built-in hash feature.'''
+ f = self.open(file)
f.write(contents)
f.close()
self.fileNameStack.pop()
return contents
+ AlreadyGenerated = {}
+
def _parse_isa_desc(self, isa_desc_file):
'''Read in and parse the ISA description.'''
+ # The build system can end up running the ISA parser twice: once to
+ # finalize the build dependencies, and then to actually generate
+ # the files it expects (in src/arch/$ARCH/generated). This code
+ # doesn't do anything different either time, however; the SCons
+ # invocations just expect different things. Since this code runs
+ # within SCons, we can just remember that we've already run and
+ # not perform a completely unnecessary run, since the ISA parser's
+ # effect is idempotent.
+ if isa_desc_file in ISAParser.AlreadyGenerated:
+ return
+
+ # grab the last three path components of isa_desc_file
+ self.filename = '/'.join(isa_desc_file.split('/')[-3:])
+
# Read file and (recursively) all included files into a string.
# PLY requires that the input be in a single string so we have to
# do this up front.
# Initialize filename stack with outer file.
self.fileNameStack.push((isa_desc_file, 0))
- # Parse it.
- (isa_name, namespace, global_code, namespace_code) = \
- self.parse_string(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
- decode_function = ''
- self.update('decoder.hh', file_template % vars())
-
- # generate decoder.cc
- includes = '#include "decoder.hh"'
- global_output = global_code.decoder_output
- namespace_output = namespace_code.decoder_output
- # namespace_output += namespace_code.decode_block
- decode_function = namespace_code.decode_block
- self.update('decoder.cc', file_template % vars())
-
- # generate per-cpu exec files
- for cpu in self.cpuModels:
- includes = '#include "decoder.hh"\n'
- includes += cpu.includes
- global_output = global_code.exec_output[cpu.name]
- namespace_output = namespace_code.exec_output[cpu.name]
- decode_function = ''
- self.update(cpu.filename, file_template % vars())
-
- # The variable names here are hacky, but this will creat local
- # variables which will be referenced in vars() which have the
- # value of the globals.
- MaxInstSrcRegs = self.maxInstSrcRegs
- MaxInstDestRegs = self.maxInstDestRegs
- MaxMiscDestRegs = self.maxMiscDestRegs
- # max_inst_regs.hh
- self.update('max_inst_regs.hh',
- max_inst_regs_template % vars())
+ # Parse.
+ self.parse_string(isa_desc)
+
+ ISAParser.AlreadyGenerated[isa_desc_file] = None
def parse_isa_desc(self, *args, **kwargs):
try:
e.exit(self.fileNameStack)
# Called as script: get args from command line.
-# Args are: <path to cpu_models.py> <isa desc file> <output dir> <cpu models>
+# Args are: <isa desc file> <output dir>
if __name__ == '__main__':
- execfile(sys.argv[1]) # read in CpuModel definitions
- cpu_models = [CpuModel.dict[cpu] for cpu in sys.argv[4:]]
- ISAParser(sys.argv[3], cpu_models).parse_isa_desc(sys.argv[2])
+ ISAParser(sys.argv[2]).parse_isa_desc(sys.argv[1])