+# 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.
#
# Redistribution and use in source and binary forms, with or without
#
# Authors: Steve Reinhardt
+from __future__ import with_statement
import os
import sys
import re
if operands.readPC or operands.setPC:
myDict['op_decl'] += 'TheISA::PCState __parserAutoPCState;\n'
+ # In case there are predicated register reads and write, declare
+ # the variables for register indicies. It is being assumed that
+ # all the operands in the OperandList are also in the
+ # SubOperandList and in the same order. Otherwise, it is
+ # expected that predication would not be used for the operands.
+ if operands.predRead:
+ myDict['op_decl'] += 'uint8_t _sourceIndex = 0;\n'
+ if operands.predWrite:
+ myDict['op_decl'] += 'uint8_t M5_VAR_USED _destIndex = 0;\n'
+
is_src = lambda op: op.is_src
is_dest = lambda op: op.is_dest
op_wb_str = op_desc.op_wb + op_wb_str
myDict['op_wb'] = op_wb_str
- if d.operands.memOperand:
- myDict['mem_acc_type'] = d.operands.memOperand.mem_acc_type
-
elif isinstance(d, dict):
# if the argument is a dictionary, we just use it.
myDict.update(d)
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.
if hasattr(self, 'eff_ext'):
self.ctype = parser.operandTypeMap[self.eff_ext]
- # note that mem_acc_type is undefined for non-mem operands...
- # template must be careful not to use it if it doesn't apply.
- if self.isMem():
- self.mem_acc_type = self.ctype
-
# Finalize additional fields (primarily code fields). This step
# is done separately since some of these fields may depend on the
# register index enumeration that hasn't been performed yet at the
- # time of __init__().
- def finalize(self):
+ # time of __init__(). The register index enumeration is affected
+ # by predicated register reads/writes. Hence, we forward the flags
+ # that indicate whether or not predication is in use.
+ def finalize(self, predRead, predWrite):
self.flags = self.getFlags()
- self.constructor = self.makeConstructor()
+ self.constructor = self.makeConstructor(predRead, predWrite)
self.op_decl = self.makeDecl()
if self.is_src:
- self.op_rd = self.makeRead()
+ self.op_rd = self.makeRead(predRead)
self.op_src_decl = self.makeDecl()
else:
self.op_rd = ''
self.op_src_decl = ''
if self.is_dest:
- self.op_wb = self.makeWrite()
+ self.op_wb = self.makeWrite(predWrite)
self.op_dest_decl = self.makeDecl()
else:
self.op_wb = ''
def isIntReg(self):
return 0
+ def isCCReg(self):
+ return 0
+
def isControlReg(self):
return 0
def isPCPart(self):
return self.isPCState() and self.reg_spec
+ def hasReadPred(self):
+ return self.read_predicate != None
+
+ def hasWritePred(self):
+ return self.write_predicate != None
+
def getFlags(self):
# note the empty slice '[:]' gives us a copy of self.flags[0]
# instead of a reference to it
def isIntReg(self):
return 1
- def makeConstructor(self):
- c = ''
+ def makeConstructor(self, predRead, predWrite):
+ c_src = ''
+ c_dest = ''
+
if self.is_src:
- c += '\n\t_srcRegIdx[%d] = %s;' % \
- (self.src_reg_idx, self.reg_spec)
+ c_src = '\n\t_srcRegIdx[_numSrcRegs++] = %s;' % (self.reg_spec)
+ if self.hasReadPred():
+ c_src = '\n\tif (%s) {%s\n\t}' % \
+ (self.read_predicate, c_src)
+
if self.is_dest:
- c += '\n\t_destRegIdx[%d] = %s;' % \
- (self.dest_reg_idx, self.reg_spec)
- return c
+ c_dest = '\n\t_destRegIdx[_numDestRegs++] = %s;' % \
+ (self.reg_spec)
+ c_dest += '\n\t_numIntDestRegs++;'
+ if self.hasWritePred():
+ c_dest = '\n\tif (%s) {%s\n\t}' % \
+ (self.write_predicate, c_dest)
+
+ return c_src + c_dest
- def makeRead(self):
+ def makeRead(self, predRead):
if (self.ctype == 'float' or self.ctype == 'double'):
error('Attempt to read integer register as FP')
if self.read_code != None:
return self.buildReadCode('readIntRegOperand')
- int_reg_val = 'xc->readIntRegOperand(this, %d)' % self.src_reg_idx
+
+ int_reg_val = ''
+ if predRead:
+ int_reg_val = 'xc->readIntRegOperand(this, _sourceIndex++)'
+ if self.hasReadPred():
+ int_reg_val = '(%s) ? %s : 0' % \
+ (self.read_predicate, int_reg_val)
+ else:
+ int_reg_val = 'xc->readIntRegOperand(this, %d)' % self.src_reg_idx
+
return '%s = %s;\n' % (self.base_name, int_reg_val)
- def makeWrite(self):
+ def makeWrite(self, predWrite):
if (self.ctype == 'float' or self.ctype == 'double'):
error('Attempt to write integer register as FP')
if self.write_code != None:
return self.buildWriteCode('setIntRegOperand')
+
+ if predWrite:
+ wp = 'true'
+ if self.hasWritePred():
+ wp = self.write_predicate
+
+ wcond = 'if (%s)' % (wp)
+ windex = '_destIndex++'
+ else:
+ wcond = ''
+ windex = '%d' % self.dest_reg_idx
+
wb = '''
+ %s
{
%s final_val = %s;
- xc->setIntRegOperand(this, %d, final_val);\n
+ xc->setIntRegOperand(this, %s, final_val);\n
if (traceData) { traceData->setData(final_val); }
- }''' % (self.ctype, self.base_name, self.dest_reg_idx)
+ }''' % (wcond, self.ctype, self.base_name, windex)
+
return wb
class FloatRegOperand(Operand):
def isFloatReg(self):
return 1
- def makeConstructor(self):
- c = ''
+ def makeConstructor(self, predRead, predWrite):
+ c_src = ''
+ c_dest = ''
+
if self.is_src:
- c += '\n\t_srcRegIdx[%d] = %s + FP_Base_DepTag;' % \
- (self.src_reg_idx, self.reg_spec)
+ c_src = '\n\t_srcRegIdx[_numSrcRegs++] = %s + FP_Reg_Base;' % \
+ (self.reg_spec)
+
if self.is_dest:
- c += '\n\t_destRegIdx[%d] = %s + FP_Base_DepTag;' % \
- (self.dest_reg_idx, self.reg_spec)
- return c
+ c_dest = \
+ '\n\t_destRegIdx[_numDestRegs++] = %s + FP_Reg_Base;' % \
+ (self.reg_spec)
+ c_dest += '\n\t_numFPDestRegs++;'
+
+ return c_src + c_dest
- def makeRead(self):
+ def makeRead(self, predRead):
bit_select = 0
if (self.ctype == 'float' or self.ctype == 'double'):
func = 'readFloatRegOperand'
func = 'readFloatRegOperandBits'
if self.read_code != None:
return self.buildReadCode(func)
- return '%s = xc->%s(this, %d);\n' % \
- (self.base_name, func, self.src_reg_idx)
- def makeWrite(self):
+ if predRead:
+ rindex = '_sourceIndex++'
+ else:
+ rindex = '%d' % self.src_reg_idx
+
+ return '%s = xc->%s(this, %s);\n' % \
+ (self.base_name, func, rindex)
+
+ def makeWrite(self, predWrite):
if (self.ctype == 'float' or self.ctype == 'double'):
func = 'setFloatRegOperand'
else:
func = 'setFloatRegOperandBits'
if self.write_code != None:
return self.buildWriteCode(func)
+
+ if predWrite:
+ wp = '_destIndex++'
+ else:
+ wp = '%d' % self.dest_reg_idx
+ wp = 'xc->%s(this, %s, final_val);' % (func, wp)
+
+ wb = '''
+ {
+ %s final_val = %s;
+ %s\n
+ if (traceData) { traceData->setData(final_val); }
+ }''' % (self.ctype, self.base_name, wp)
+ return wb
+
+class CCRegOperand(Operand):
+ def isReg(self):
+ return 1
+
+ def isCCReg(self):
+ return 1
+
+ def makeConstructor(self, predRead, predWrite):
+ c_src = ''
+ c_dest = ''
+
+ if self.is_src:
+ c_src = '\n\t_srcRegIdx[_numSrcRegs++] = %s + CC_Reg_Base;' % \
+ (self.reg_spec)
+ if self.hasReadPred():
+ c_src = '\n\tif (%s) {%s\n\t}' % \
+ (self.read_predicate, c_src)
+
+ if self.is_dest:
+ c_dest = \
+ '\n\t_destRegIdx[_numDestRegs++] = %s + CC_Reg_Base;' % \
+ (self.reg_spec)
+ c_dest += '\n\t_numCCDestRegs++;'
+ if self.hasWritePred():
+ c_dest = '\n\tif (%s) {%s\n\t}' % \
+ (self.write_predicate, c_dest)
+
+ return c_src + c_dest
+
+ def makeRead(self, predRead):
+ if (self.ctype == 'float' or self.ctype == 'double'):
+ error('Attempt to read condition-code register as FP')
+ if self.read_code != None:
+ return self.buildReadCode('readCCRegOperand')
+
+ int_reg_val = ''
+ if predRead:
+ int_reg_val = 'xc->readCCRegOperand(this, _sourceIndex++)'
+ if self.hasReadPred():
+ int_reg_val = '(%s) ? %s : 0' % \
+ (self.read_predicate, int_reg_val)
+ else:
+ int_reg_val = 'xc->readCCRegOperand(this, %d)' % self.src_reg_idx
+
+ return '%s = %s;\n' % (self.base_name, int_reg_val)
+
+ def makeWrite(self, predWrite):
+ if (self.ctype == 'float' or self.ctype == 'double'):
+ error('Attempt to write condition-code register as FP')
+ if self.write_code != None:
+ return self.buildWriteCode('setCCRegOperand')
+
+ if predWrite:
+ wp = 'true'
+ if self.hasWritePred():
+ wp = self.write_predicate
+
+ wcond = 'if (%s)' % (wp)
+ windex = '_destIndex++'
+ else:
+ wcond = ''
+ windex = '%d' % self.dest_reg_idx
+
wb = '''
+ %s
{
%s final_val = %s;
- xc->%s(this, %d, final_val);\n
+ xc->setCCRegOperand(this, %s, final_val);\n
if (traceData) { traceData->setData(final_val); }
- }''' % (self.ctype, self.base_name, func, self.dest_reg_idx)
+ }''' % (wcond, self.ctype, self.base_name, windex)
+
return wb
class ControlRegOperand(Operand):
def isControlReg(self):
return 1
- def makeConstructor(self):
- c = ''
+ def makeConstructor(self, predRead, predWrite):
+ c_src = ''
+ c_dest = ''
+
if self.is_src:
- c += '\n\t_srcRegIdx[%d] = %s + Ctrl_Base_DepTag;' % \
- (self.src_reg_idx, self.reg_spec)
+ c_src = \
+ '\n\t_srcRegIdx[_numSrcRegs++] = %s + Misc_Reg_Base;' % \
+ (self.reg_spec)
+
if self.is_dest:
- c += '\n\t_destRegIdx[%d] = %s + Ctrl_Base_DepTag;' % \
- (self.dest_reg_idx, self.reg_spec)
- return c
+ c_dest = \
+ '\n\t_destRegIdx[_numDestRegs++] = %s + Misc_Reg_Base;' % \
+ (self.reg_spec)
+
+ return c_src + c_dest
- def makeRead(self):
+ def makeRead(self, predRead):
bit_select = 0
if (self.ctype == 'float' or self.ctype == 'double'):
error('Attempt to read control register as FP')
if self.read_code != None:
return self.buildReadCode('readMiscRegOperand')
+
+ if predRead:
+ rindex = '_sourceIndex++'
+ else:
+ rindex = '%d' % self.src_reg_idx
+
return '%s = xc->readMiscRegOperand(this, %s);\n' % \
- (self.base_name, self.src_reg_idx)
+ (self.base_name, rindex)
- def makeWrite(self):
+ def makeWrite(self, predWrite):
if (self.ctype == 'float' or self.ctype == 'double'):
error('Attempt to write control register as FP')
if self.write_code != None:
return self.buildWriteCode('setMiscRegOperand')
+
+ if predWrite:
+ windex = '_destIndex++'
+ else:
+ windex = '%d' % self.dest_reg_idx
+
wb = 'xc->setMiscRegOperand(this, %s, %s);\n' % \
- (self.dest_reg_idx, self.base_name)
+ (windex, self.base_name)
wb += 'if (traceData) { traceData->setData(%s); }' % \
self.base_name
+
return wb
class MemOperand(Operand):
def isMem(self):
return 1
- def makeConstructor(self):
+ def makeConstructor(self, predRead, predWrite):
return ''
def makeDecl(self):
# Declare memory data variable.
return '%s %s = 0;\n' % (self.ctype, self.base_name)
- def makeRead(self):
+ def makeRead(self, predRead):
if self.read_code != None:
return self.buildReadCode()
return ''
- def makeWrite(self):
+ def makeWrite(self, predWrite):
if self.write_code != None:
return self.buildWriteCode()
return ''
class PCStateOperand(Operand):
- def makeConstructor(self):
+ def makeConstructor(self, predRead, predWrite):
return ''
- def makeRead(self):
+ def makeRead(self, predRead):
if self.reg_spec:
# A component of the PC state.
return '%s = __parserAutoPCState.%s();\n' % \
# The whole PC state itself.
return '%s = xc->pcState();\n' % self.base_name
- def makeWrite(self):
+ def makeWrite(self, predWrite):
if self.reg_spec:
# A component of the PC state.
return '__parserAutoPCState.%s(%s);\n' % \
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
def __init__(self, parser, code):
self.items = []
self.bases = {}
- # delete comments so we don't match on reg specifiers inside
- code = commentRE.sub('', code)
+ # delete strings and comments so we don't match on operands inside
+ for regEx in (stringRE, commentRE):
+ code = regEx.sub('', code)
# search for operands
next_pos = 0
while 1:
self.numDestRegs = 0
self.numFPDestRegs = 0
self.numIntDestRegs = 0
+ self.numCCDestRegs = 0
+ self.numMiscDestRegs = 0
self.memOperand = None
+
+ # Flags to keep track if one or more operands are to be read/written
+ # conditionally.
+ self.predRead = False
+ self.predWrite = False
+
for op_desc in self.items:
if op_desc.isReg():
if op_desc.is_src:
self.numFPDestRegs += 1
elif op_desc.isIntReg():
self.numIntDestRegs += 1
+ elif op_desc.isCCReg():
+ self.numCCDestRegs += 1
+ elif op_desc.isControlReg():
+ self.numMiscDestRegs += 1
elif op_desc.isMem():
if self.memOperand:
error("Code block has more than one memory operand.")
self.memOperand = op_desc
+
+ # Check if this operand has read/write predication. If true, then
+ # the microop will dynamically index source/dest registers.
+ self.predRead = self.predRead or op_desc.hasReadPred()
+ self.predWrite = self.predWrite or op_desc.hasWritePred()
+
if parser.maxInstSrcRegs < self.numSrcRegs:
parser.maxInstSrcRegs = self.numSrcRegs
if parser.maxInstDestRegs < self.numDestRegs:
parser.maxInstDestRegs = self.numDestRegs
+ if parser.maxMiscDestRegs < self.numMiscDestRegs:
+ parser.maxMiscDestRegs = self.numMiscDestRegs
+
# now make a final pass to finalize op_desc fields that may depend
# on the register enumeration
for op_desc in self.items:
- op_desc.finalize()
+ op_desc.finalize(self.predRead, self.predWrite)
def __len__(self):
return len(self.items)
def __init__(self, parser, code, master_list):
self.items = []
self.bases = {}
- # delete comments so we don't match on reg specifiers inside
- code = commentRE.sub('', code)
+ # delete strings and comments so we don't match on operands inside
+ for regEx in (stringRE, commentRE):
+ code = regEx.sub('', code)
# search for operands
next_pos = 0
while 1:
# Whether this instruction manipulates the whole PC or parts of it.
# Mixing the two is a bad idea and flagged as an error.
self.pcPart = None
+
+ # Flags to keep track if one or more operands are to be read/written
+ # conditionally.
+ self.predRead = False
+ self.predWrite = False
+
for op_desc in self.items:
if op_desc.isPCPart():
self.readPC = True
if op_desc.is_dest:
self.setPC = True
+
if op_desc.isPCState():
if self.pcPart is not None:
if self.pcPart and not op_desc.isPCPart() or \
not self.pcPart and op_desc.isPCPart():
error("Mixed whole and partial PC state operands.")
self.pcPart = op_desc.isPCPart()
+
if op_desc.isMem():
if self.memOperand:
error("Code block has more than one memory operand.")
self.memOperand = op_desc
+ # Check if this operand has read/write predication. If true, then
+ # the microop will dynamically index source/dest registers.
+ self.predRead = self.predRead or op_desc.hasReadPred()
+ self.predWrite = self.predWrite or op_desc.hasWritePred()
+
+# Regular expression object to match C++ strings
+stringRE = re.compile(r'"([^"\\]|\\.)*"')
+
# Regular expression object to match C++ comments
# (used in findOperands())
-commentRE = re.compile(r'//.*\n')
+commentRE = re.compile(r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
+ re.DOTALL | re.MULTILINE)
# Regular expression object to match assignment statements
# (used in findOperands())
self.snippets = snippets
self.operands = OperandList(parser, compositeCode)
- self.constructor = self.operands.concatAttrStrings('constructor')
- self.constructor += \
- '\n\t_numSrcRegs = %d;' % self.operands.numSrcRegs
- self.constructor += \
- '\n\t_numDestRegs = %d;' % self.operands.numDestRegs
- self.constructor += \
- '\n\t_numFPDestRegs = %d;' % self.operands.numFPDestRegs
- self.constructor += \
- '\n\t_numIntDestRegs = %d;' % self.operands.numIntDestRegs
+
+ # The header of the constructor declares the variables to be used
+ # in the body of the constructor.
+ header = ''
+ header += '\n\t_numSrcRegs = 0;'
+ header += '\n\t_numDestRegs = 0;'
+ header += '\n\t_numFPDestRegs = 0;'
+ header += '\n\t_numIntDestRegs = 0;'
+ header += '\n\t_numCCDestRegs = 0;'
+
+ self.constructor = header + \
+ self.operands.concatAttrStrings('constructor')
+
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;
-
-} // 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.maxInstSrcRegs = 0
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
+
+''';
#####################################################################
#
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::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
+
+ self.write_top_level_files()
+
+ t[0] = True
- # 'opt_defs_and_outputs' is a possibly empty sequence of
- # def and/or output statements.
+ # '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_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_intlit_list_1(self, t):
- 'intlit_list : intlit_list COMMA INTLIT'
+ 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).
def buildOperandNameMap(self, user_dict, lineno):
operand_name = {}
for op_name, val in user_dict.iteritems():
- base_cls_name, dflt_ext, reg_spec, flags, sort_pri = val[:5]
- if len(val) > 5:
- read_code = val[5]
- else:
- read_code = None
- if len(val) > 6:
- write_code = val[6]
- else:
- write_code = None
- if len(val) > 7:
- error(lineno,
- 'error: too many attributes for operand "%s"' %
+
+ # Check if extra attributes have been specified.
+ if len(val) > 9:
+ error(lineno, 'error: too many attributes for operand "%s"' %
base_cls_name)
+ # Pad val with None in case optional args are missing
+ val += (None, None, None, None)
+ base_cls_name, dflt_ext, reg_spec, flags, sort_pri, \
+ read_code, write_code, read_predicate, write_predicate = val[:9]
+
# Canonical flag structure is a triple of lists, where each list
# indicates the set of flags implied by this operand always, when
# used as a source, and when used as a dest, respectively.
(uncond_flags, src_flags, dest_flags) = flags
flags = (makeList(uncond_flags),
makeList(src_flags), makeList(dest_flags))
+
# Accumulate attributes of new operand class in tmp_dict
tmp_dict = {}
attrList = ['reg_spec', 'flags', 'sort_pri',
- 'read_code', 'write_code']
+ 'read_code', 'write_code',
+ 'read_predicate', 'write_predicate']
if dflt_ext:
dflt_ctype = self.operandTypeMap[dflt_ext]
attrList.extend(['dflt_ctype', 'dflt_ext'])
for attr in attrList:
tmp_dict[attr] = eval(attr)
tmp_dict['base_name'] = op_name
+
# New class name will be e.g. "IntReg_Ra"
cls_name = base_cls_name + '_' + op_name
# Evaluate string arg to get class object. Note that the
# Define operand variables.
operands = user_dict.keys()
+ extensions = self.operandTypeMap.keys()
- operandsREString = (r'''
- (?<![\w\.]) # neg. lookbehind assertion: prevent partial matches
- ((%s)(?:\.(\w+))?) # match: operand with optional '.' then suffix
- (?![\w\.]) # neg. lookahead assertion: prevent partial matches
- '''
- % string.join(operands, '|'))
+ operandsREString = r'''
+ (?<!\w) # neg. lookbehind assertion: prevent partial matches
+ ((%s)(?:_(%s))?) # match: operand with optional '_' then suffix
+ (?!\w) # neg. lookahead assertion: prevent partial matches
+ ''' % (string.join(operands, '|'), string.join(extensions, '|'))
self.operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE)
# Same as operandsREString, but extension is mandatory, and only two
# groups are returned (base and ext, not full name as above).
# Used for subtituting '_' for '.' to make C++ identifiers.
- operandsWithExtREString = (r'(?<![\w\.])(%s)\.(\w+)(?![\w\.])'
- % string.join(operands, '|'))
+ operandsWithExtREString = r'(?<!\w)(%s)_(%s)(?!\w)' \
+ % (string.join(operands, '|'), string.join(extensions, '|'))
self.operandsWithExtRE = \
re.compile(operandsWithExtREString, re.MULTILINE)
else:
return s
- def update_if_needed(self, file, contents):
- '''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.'''
-
- file = os.path.join(self.output_dir, file)
- update = False
- if os.access(file, os.R_OK):
- f = open(file, 'r')
- old_contents = f.read()
- f.close()
- if contents != old_contents:
- os.remove(file) # in case it's write-protected
- update = True
- else:
- print 'File', file, 'is unchanged'
- else:
- update = True
- if update:
- f = open(file, 'w')
- f.write(contents)
- f.close()
+ 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 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()
# This regular expression matches '##include' directives
includeRE = re.compile(r'^\s*##include\s+"(?P<filename>[^"]*)".*$',
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_if_needed('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_if_needed('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_if_needed(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
- # max_inst_regs.hh
- self.update_if_needed('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])