isa_parser.py
来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· Python 代码 · 共 1,867 行 · 第 1/5 页
PY
1,867 行
for cpu in cpu_models: 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 cpu_models: 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 cpu_models: 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.## A format object encapsulates an instruction format. It must provide# a defineInst() method that generates the code for an instruction# definition.exportContextSymbols = ('InstObjParams', 'makeList', 're', 'string')exportContext = {}def updateExportContext(): exportContext.update(exportDict(*exportContextSymbols)) exportContext.update(templateMap)def exportDict(*symNames): return dict([(s, eval(s)) for s in symNames])class Format: def __init__(self, id, params, code): # constructor: just save away arguments self.id = id self.params = params label = 'def format ' + id self.user_code = compile(fixPythonIndentation(code), label, 'exec') param_list = string.join(params, ", ") 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): context = {} updateExportContext() context.update(exportContext) if len(name): Name = name[0].upper() if len(name) > 1: Name += name[1:] context.update({ 'name': name, 'Name': Name }) try: vars = self.func(self.user_code, context, *args[0], **args[1]) 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.class NoFormat: def __init__(self): self.defaultInst = '' def defineInst(self, name, args, lineno): error(lineno, 'instruction definition "%s" with no active format!' % name)# This dictionary maps format name strings to Format objects.formatMap = {}# Define a new formatdef defFormat(id, params, code, lineno): # make sure we haven't already defined this one if formatMap.get(id, None) != None: error(lineno, 'format %s redefined.' % id) # create new object and store in global map formatMap[id] = Format(id, params, code)############### Stack: a simple stack object. Used for both formats (formatStack)# and default cases (defaultStack). Simply wraps a list to give more# stack-like syntax and enable initialization with an argument list# (as opposed to an argument that's a list).class Stack(list): def __init__(self, *items): list.__init__(self, items) def push(self, item): self.append(item); def top(self): return self[-1]# The global format stack.formatStack = Stack(NoFormat())# The global default case stack.defaultStack = Stack( None )# Global stack that tracks current file and line number.# Each element is a tuple (filename, lineno) that records the# *current* filename and the line number in the *previous* file where# it was included.fileNameStack = Stack()#################### Utility functions## Indent every line in string 's' by two spaces# (except preprocessor directives).# Used to make nested code blocks look pretty.#def indent(s): return re.sub(r'(?m)^(?!#)', ' ', s)## Munge a somewhat arbitrarily formatted piece of Python code# (e.g. from a format 'let' block) into something whose indentation# will get by the Python parser.## The two keys here are that Python will give a syntax error if# there's any whitespace at the beginning of the first line, and that# all lines at the same lexical nesting level must have identical# indentation. Unfortunately the way code literals work, an entire# let block tends to have some initial indentation. Rather than# trying to figure out what that is and strip it off, we prepend 'if# 1:' to make the let code the nested block inside the if (and have# the parser automatically deal with the indentation for us).## We don't want to do this if (1) the code block is empty or (2) the# first line of the block doesn't have any whitespace at the front.def fixPythonIndentation(s): # get rid of blank lines first s = re.sub(r'(?m)^\s*\n', '', s); if (s != '' and re.match(r'[ \t]', s[0])): s = 'if 1:\n' + s return s# Error handler. Just call exit. Output formatted to work under# Emacs compile-mode. Optional 'print_traceback' arg, if set to True,# prints a Python stack backtrace too (can be handy when trying to# debug the parser itself).def error(lineno, string, print_traceback = False): spaces = "" for (filename, line) in fileNameStack[0:-1]: print spaces + "In file included from " + filename + ":" spaces += " " # Print a Python stack backtrace if requested. if (print_traceback): traceback.print_exc() if lineno != 0: line_str = "%d:" % lineno else: line_str = "" sys.exit(spaces + "%s:%s %s" % (fileNameStack[-1][0], line_str, string))####################################################################### Bitfield Operator Support######################################################################bitOp1ArgRE = re.compile(r'<\s*(\w+)\s*:\s*>')bitOpWordRE = re.compile(r'(?<![\w\.])([\w\.]+)<\s*(\w+)\s*:\s*(\w+)\s*>')bitOpExprRE = re.compile(r'\)<\s*(\w+)\s*:\s*(\w+)\s*>')def substBitOps(code): # first convert single-bit selectors to two-index form # i.e., <n> --> <n:n> code = bitOp1ArgRE.sub(r'<\1:\1>', code) # simple case: selector applied to ID (name) # i.e., foo<a:b> --> bits(foo, a, b) code = bitOpWordRE.sub(r'bits(\1, \2, \3)', code) # if selector is applied to expression (ending in ')'), # we need to search backward for matching '(' match = bitOpExprRE.search(code) while match: exprEnd = match.start() here = exprEnd - 1 nestLevel = 1 while nestLevel > 0: if code[here] == '(': nestLevel -= 1 elif code[here] == ')': nestLevel += 1 here -= 1 if here < 0: sys.exit("Didn't find '('!") exprStart = here+1 newExpr = r'bits(%s, %s, %s)' % (code[exprStart:exprEnd+1], match.group(1), match.group(2)) code = code[:exprStart] + newExpr + code[match.end():] match = bitOpExprRE.search(code) return code##################### Template objects.## Template objects are format strings that allow substitution from# the attribute spaces of other objects (e.g. InstObjParams instances).labelRE = re.compile(r'(?<!%)%\(([^\)]+)\)[sd]')class Template: def __init__(self, t): self.template = t def subst(self, d): myDict = None # Protect non-Python-dict substitutions (e.g. if there's a printf # in the templated C++ code) template = protect_non_subst_percents(self.template) # CPU-model-specific substitutions are handled later (in GenCode). template = protect_cpu_symbols(template) # Build a dict ('myDict') to use for the template substitution. # Start with the template namespace. Make a copy since we're # going to modify it. myDict = templateMap.copy() if isinstance(d, InstObjParams): # If we're dealing with an InstObjParams object, we need # to be a little more sophisticated. The instruction-wide # parameters are already formed, but the parameters which # are only function wide still need to be generated. compositeCode = '' myDict.update(d.__dict__) # The "operands" and "snippets" attributes of the InstObjParams # objects are for internal use and not substitution. del myDict['operands'] del myDict['snippets'] snippetLabels = [l for l in labelRE.findall(template) if d.snippets.has_key(l)] snippets = dict([(s, mungeSnippet(d.snippets[s])) for s in snippetLabels]) myDict.update(snippets) compositeCode = ' '.join(map(str, snippets.values())) # Add in template itself in case it references any # operands explicitly (like Mem) compositeCode += ' ' + template operands = SubOperandList(compositeCode, d.operands) myDict['op_decl'] = operands.concatAttrStrings('op_decl') is_src = lambda op: op.is_src is_dest = lambda op: op.is_dest myDict['op_src_decl'] = \ operands.concatSomeAttrStrings(is_src, 'op_src_decl') myDict['op_dest_decl'] = \ operands.concatSomeAttrStrings(is_dest, 'op_dest_decl') myDict['op_rd'] = operands.concatAttrStrings('op_rd') myDict['op_wb'] = operands.concatAttrStrings('op_wb') if d.operands.memOperand: myDict['mem_acc_size'] = d.operands.memOperand.mem_acc_size 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) elif hasattr(d, '__dict__'): # if the argument is an object, we use its attribute map. myDict.update(d.__dict__) else: raise TypeError, "Template.subst() arg must be or have dictionary" return 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## The remaining code is the support for automatically extracting# instruction characteristics from pseudocode.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?