X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Farch%2Fisa_parser.py;h=4e06c2ded836a425e0fb6cb4781e5f428b90dc30;hb=249549f9c3cc9317b1eeca8e844287dbbaf5f4ca;hp=a8f9852ea76cf6c3487cd88021e338887932a41b;hpb=40a05f04fbcd0f411b4cf2e2ac38b9f5bb42f4b6;p=gem5.git diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py index a8f9852ea..4e06c2ded 100755 --- a/src/arch/isa_parser.py +++ b/src/arch/isa_parser.py @@ -36,6 +36,8 @@ from types import * from m5.util.grammar import Grammar +debug=False + ################### # Utility functions @@ -83,7 +85,7 @@ class ISAParserError(Exception): self.lineno = first self.string = second - def display(self, filename_stack, print_traceback=False): + def display(self, filename_stack, print_traceback=debug): # 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 @@ -104,7 +106,7 @@ class ISAParserError(Exception): return "%s%s %s" % (spaces, line_str, self.string) - def exit(self, filename_stack, print_traceback=False): + def exit(self, filename_stack, print_traceback=debug): # Just call exit. sys.exit(self.display(filename_stack, print_traceback)) @@ -121,7 +123,8 @@ def error(*args): labelRE = re.compile(r'(? 1: Name += name[1:] - context.update({ 'name': name, 'Name': Name }) + context.update({ 'name' : name, 'Name' : Name }) try: vars = self.func(self.user_code, context, *args[0], **args[1]) except Exception, exc: + if debug: + raise 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) + return GenCode(parser, **vars) # Special null format to catch an implicit-format instruction # definition outside of any format block. @@ -260,61 +251,10 @@ class NoFormat(object): def __init__(self): self.defaultInst = '' - def defineInst(self, name, args, lineno): + def defineInst(self, parser, 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 format -def 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) - -##################################################################### -# -# Support Classes -# -##################################################################### - -# 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 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) - -# Protect any non-dict-substitution '%'s in a format string -# (i.e. those not followed by '(') -def protect_non_subst_percents(s): - return re.sub(r'%(?!\()', '%%', s) - ############### # GenCode class # @@ -334,29 +274,31 @@ class GenCode(object): # 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, + def __init__(self, parser, 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) + 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 = expand_cpu_symbols_to_dict(exec_output) - self.decode_block = expand_cpu_symbols_to_string(decode_block) + self.exec_output = parser.expandCpuSymbolsToDict(exec_output) + self.decode_block = parser.expandCpuSymbolsToString(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: + for cpu in self.parser.cpuModels: n = cpu.name exec_output[n] = self.exec_output[n] + other.exec_output[n] - return GenCode(self.header_output + other.header_output, + return GenCode(self.parser, + self.header_output + other.header_output, self.decoder_output + other.decoder_output, exec_output, self.decode_block + other.decode_block, @@ -367,7 +309,7 @@ class GenCode(object): 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: + for cpu in self.parser.cpuModels: self.exec_output[cpu.name] = pre + self.exec_output[cpu.name] # Wrap the decode block in a pair of strings (e.g., 'case foo:' @@ -438,34 +380,6 @@ def makeList(arg): else: return [ arg ] -# Generate operandTypeMap from the user's 'def operand_types' -# statement. -def buildOperandTypeMap(user_dict, lineno): - global operandTypeMap - operandTypeMap = {} - for (ext, (desc, size)) in user_dict.iteritems(): - if desc == 'signed int': - ctype = 'int%d_t' % size - is_signed = 1 - elif desc == 'unsigned int': - ctype = 'uint%d_t' % size - is_signed = 0 - elif desc == 'float': - is_signed = 1 # shouldn't really matter - if size == 32: - ctype = 'float' - elif size == 64: - ctype = 'double' - elif desc == 'twin64 int': - is_signed = 0 - ctype = 'Twin64_t' - elif desc == 'twin32 int': - is_signed = 0 - ctype = 'Twin32_t' - if ctype == '': - error(lineno, 'Unrecognized type description "%s" in user_dict') - operandTypeMap[ext] = (size, ctype, is_signed) - class Operand(object): '''Base class for operand descriptors. An instance of this class (or actually a class derived from this one) represents a specific @@ -474,12 +388,14 @@ class Operand(object): type (e.g., "32-bit integer register").''' def buildReadCode(self, func = None): - code = self.read_code % {"name": self.base_name, - "func": func, - "op_idx": self.src_reg_idx, - "reg_idx": self.reg_spec, - "size": self.size, - "ctype": self.ctype} + subst_dict = {"name": self.base_name, + "func": func, + "reg_idx": self.reg_spec, + "size": self.size, + "ctype": self.ctype} + if hasattr(self, 'src_reg_idx'): + subst_dict['op_idx'] = self.src_reg_idx + code = self.read_code % subst_dict if self.size != self.dflt_size: return '%s = bits(%s, %d, 0);\n' % \ (self.base_name, code, self.size-1) @@ -492,13 +408,15 @@ class Operand(object): final_val = 'sext<%d>(%s)' % (self.size, self.base_name) else: final_val = self.base_name - code = self.write_code % {"name": self.base_name, - "func": func, - "op_idx": self.dest_reg_idx, - "reg_idx": self.reg_spec, - "size": self.size, - "ctype": self.ctype, - "final_val": final_val} + subst_dict = {"name": self.base_name, + "func": func, + "reg_idx": self.reg_spec, + "size": self.size, + "ctype": self.ctype, + "final_val": final_val} + if hasattr(self, 'dest_reg_idx'): + subst_dict['op_idx'] = self.dest_reg_idx + code = self.write_code % subst_dict return ''' { %s final_val = %s; @@ -506,7 +424,7 @@ class Operand(object): if (traceData) { traceData->setData(final_val); } }''' % (self.dflt_ctype, final_val, code) - def __init__(self, full_name, ext, is_src, is_dest): + def __init__(self, parser, full_name, ext, is_src, is_dest): self.full_name = full_name self.ext = ext self.is_src = is_src @@ -518,7 +436,8 @@ class Operand(object): else: self.eff_ext = self.dflt_ext - (self.size, self.ctype, self.is_signed) = operandTypeMap[self.eff_ext] + self.size, self.ctype, self.is_signed = \ + parser.operandTypeMap[self.eff_ext] # note that mem_acc_size is undefined for non-mem operands... # template must be careful not to use it if it doesn't apply. @@ -828,96 +747,10 @@ class NNPCOperand(Operand): return self.buildWriteCode('setNextNPC') return 'xc->setNextNPC(%s);\n' % self.base_name -def buildOperandNameMap(user_dict, lineno): - global operandNameMap - operandNameMap = {} - 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"' % - base_cls_name) - - (dflt_size, dflt_ctype, dflt_is_signed) = operandTypeMap[dflt_ext] - # 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. - # For simplicity this can be initialized using a variety of fairly - # obvious shortcuts; we convert these to canonical form here. - if not flags: - # no flags specified (e.g., 'None') - flags = ( [], [], [] ) - elif isinstance(flags, str): - # a single flag: assumed to be unconditional - flags = ( [ flags ], [], [] ) - elif isinstance(flags, list): - # a list of flags: also assumed to be unconditional - flags = ( flags, [], [] ) - elif isinstance(flags, tuple): - # it's a tuple: it should be a triple, - # but each item could be a single string or a list - (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 = {} - for attr in ('dflt_ext', 'reg_spec', 'flags', 'sort_pri', - 'dflt_size', 'dflt_ctype', 'dflt_is_signed', - 'read_code', 'write_code'): - 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 - # actual base class for "IntReg" is "IntRegOperand", i.e. we - # have to append "Operand". - try: - base_cls = eval(base_cls_name + 'Operand') - except NameError: - error(lineno, - 'error: unknown operand base class "%s"' % base_cls_name) - # The following statement creates a new class called - # as a subclass of with the attributes - # in tmp_dict, just as if we evaluated a class declaration. - operandNameMap[op_name] = type(cls_name, (base_cls,), tmp_dict) - - # Define operand variables. - operands = user_dict.keys() - - operandsREString = (r''' - (? [:]' @@ -1558,7 +1394,7 @@ StaticInstPtr 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(header_output = hash_define) + t[0] = GenCode(self, header_output=hash_define) # alternate form for single bit: 'def [signed] bitfield []' def p_def_bitfield_1(self, t): @@ -1567,7 +1403,7 @@ StaticInstPtr 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(header_output = hash_define) + t[0] = GenCode(self, header_output=hash_define) # alternate form for structure member: 'def bitfield ' def p_def_bitfield_struct(self, t): @@ -1576,7 +1412,7 @@ StaticInstPtr 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(header_output = hash_define) + t[0] = GenCode(self, header_output=hash_define) def p_id_with_dot_0(self, t): 'id_with_dot : ID' @@ -1596,16 +1432,16 @@ StaticInstPtr def p_def_template(self, t): 'def_template : DEF TEMPLATE ID CODELIT SEMI' - self.templateMap[t[3]] = Template(t[4]) - t[0] = GenCode() + self.templateMap[t[3]] = Template(self, t[4]) + t[0] = GenCode(self) # An instruction format definition looks like # "def format () {{...}};" def p_def_format(self, 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.lexer.lineno) - t[0] = GenCode() + 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 @@ -1678,7 +1514,7 @@ StaticInstPtr # def p_decode_block(self, t): 'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE' - default_defaults = defaultStack.pop() + default_defaults = self.defaultStack.pop() codeObj = t[5] # use the "default defaults" only if there was no explicit # default statement in decode_stmt_list @@ -1695,7 +1531,7 @@ StaticInstPtr 'opt_default : empty' # no default specified: reuse the one currently at the top of # the stack - defaultStack.push(defaultStack.top()) + self.defaultStack.push(self.defaultStack.top()) # no meaningful value returned t[0] = None @@ -1704,7 +1540,7 @@ StaticInstPtr # push the new default codeObj = t[2] codeObj.wrap_decode_block('\ndefault:\n', 'break;\n') - defaultStack.push(codeObj) + self.defaultStack.push(codeObj) # no meaningful value returned t[0] = None @@ -1737,7 +1573,7 @@ StaticInstPtr # the code generated by the other statements. def p_decode_stmt_cpp(self, t): 'decode_stmt : CPPDIRECTIVE' - t[0] = GenCode(t[1], t[1], t[1], t[1]) + t[0] = GenCode(self, t[1], t[1], t[1], t[1]) # A format block 'format { ... }' sets the default # instruction format used to handle instruction definitions inside @@ -1750,7 +1586,7 @@ StaticInstPtr # is processed (see below). Once the parser has recognized # the full production (though the right brace), we're done # with the format, so now we can pop it. - formatStack.pop() + self.formatStack.pop() t[0] = t[4] # This rule exists so we can set the current format (& push the @@ -1759,7 +1595,7 @@ StaticInstPtr def p_push_format_id(self, t): 'push_format_id : ID' try: - formatStack.push(formatMap[t[1]]) + self.formatStack.push(self.formatMap[t[1]]) t[0] = ('', '// format %s' % t[1]) except KeyError: error(t, 'instruction format "%s" not defined.' % t[1]) @@ -1819,8 +1655,8 @@ StaticInstPtr def p_inst_0(self, t): 'inst : ID LPAREN arg_list RPAREN' # Pass the ID and arg list to the current format class to deal with. - currentFormat = formatStack.top() - codeObj = currentFormat.defineInst(t[1], t[3], t.lexer.lineno) + currentFormat = self.formatStack.top() + codeObj = currentFormat.defineInst(self, t[1], t[3], t.lexer.lineno) args = ','.join(map(str, t[3])) args = re.sub('(?m)^', '//', args) args = re.sub('^//', '', args) @@ -1833,10 +1669,11 @@ StaticInstPtr def p_inst_1(self, t): 'inst : ID DBLCOLON ID LPAREN arg_list RPAREN' try: - format = formatMap[t[1]] + format = self.formatMap[t[1]] except KeyError: error(t, 'instruction format "%s" not defined.' % t[1]) - codeObj = format.defineInst(t[3], t[5], t.lexer.lineno) + + codeObj = format.defineInst(self, t[3], t[5], t.lexer.lineno) comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5]) codeObj.prepend_all(comment) t[0] = codeObj @@ -1933,6 +1770,191 @@ StaticInstPtr # END OF GRAMMAR RULES + def updateExportContext(self): + + # create a continuation that allows us to grab the current parser + def wrapInstObjParams(*args): + return InstObjParams(self, *args) + self.exportContext['InstObjParams'] = wrapInstObjParams + self.exportContext.update(self.templateMap) + + def defFormat(self, id, params, code, lineno): + '''Define a new format''' + + # make sure we haven't already defined this one + if id in self.formatMap: + error(lineno, 'format %s redefined.' % id) + + # create new object and store in global map + self.formatMap[id] = Format(id, params, code) + + def expandCpuSymbolsToDict(self, template): + '''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.''' + + # Protect '%'s that don't go with CPU-specific terms + t = re.sub(r'%(?!\(CPU_)', '%%', template) + result = {} + for cpu in self.cpuModels: + result[cpu.name] = t % cpu.strings + return result + + def expandCpuSymbolsToString(self, template): + '''*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.''' + + if template.find('%(CPU_') != -1: + return reduce(lambda x,y: x+y, + self.expandCpuSymbolsToDict(template).values()) + else: + return template + + def protectCpuSymbols(self, template): + '''Protect CPU-specific references by doubling the + corresponding '%'s (in preparation for substituting a different + set of references into the template).''' + + return re.sub(r'%(?=\(CPU_)', '%%', template) + + def protectNonSubstPercents(self, s): + '''Protect any non-dict-substitution '%'s in a format string + (i.e. those not followed by '(')''' + + return re.sub(r'%(?!\()', '%%', s) + + def buildOperandTypeMap(self, user_dict, lineno): + """Generate operandTypeMap from the user's 'def operand_types' + statement.""" + operand_type = {} + for (ext, (desc, size)) in user_dict.iteritems(): + if desc == 'signed int': + ctype = 'int%d_t' % size + is_signed = 1 + elif desc == 'unsigned int': + ctype = 'uint%d_t' % size + is_signed = 0 + elif desc == 'float': + is_signed = 1 # shouldn't really matter + if size == 32: + ctype = 'float' + elif size == 64: + ctype = 'double' + elif desc == 'twin64 int': + is_signed = 0 + ctype = 'Twin64_t' + elif desc == 'twin32 int': + is_signed = 0 + ctype = 'Twin32_t' + if ctype == '': + error(parser, lineno, + 'Unrecognized type description "%s" in user_dict') + operand_type[ext] = (size, ctype, is_signed) + + self.operandTypeMap = operand_type + + 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"' % + base_cls_name) + + (dflt_size, dflt_ctype, dflt_is_signed) = \ + self.operandTypeMap[dflt_ext] + # 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. + # For simplicity this can be initialized using a variety of fairly + # obvious shortcuts; we convert these to canonical form here. + if not flags: + # no flags specified (e.g., 'None') + flags = ( [], [], [] ) + elif isinstance(flags, str): + # a single flag: assumed to be unconditional + flags = ( [ flags ], [], [] ) + elif isinstance(flags, list): + # a list of flags: also assumed to be unconditional + flags = ( flags, [], [] ) + elif isinstance(flags, tuple): + # it's a tuple: it should be a triple, + # but each item could be a single string or a list + (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 = {} + for attr in ('dflt_ext', 'reg_spec', 'flags', 'sort_pri', + 'dflt_size', 'dflt_ctype', 'dflt_is_signed', + 'read_code', 'write_code'): + 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 + # actual base class for "IntReg" is "IntRegOperand", i.e. we + # have to append "Operand". + try: + base_cls = eval(base_cls_name + 'Operand') + except NameError: + error(lineno, + 'error: unknown operand base class "%s"' % base_cls_name) + # The following statement creates a new class called + # as a subclass of with the attributes + # in tmp_dict, just as if we evaluated a class declaration. + operand_name[op_name] = type(cls_name, (base_cls,), tmp_dict) + + self.operandNameMap = operand_name + + # Define operand variables. + operands = user_dict.keys() + + operandsREString = (r''' + (? if __name__ == '__main__': execfile(sys.argv[1]) # read in CpuModel definitions cpu_models = [CpuModel.dict[cpu] for cpu in sys.argv[4:]] - parser = ISAParser(sys.argv[3]) - parser.parse_isa_desc(sys.argv[2]) + ISAParser(sys.argv[3], cpu_models).parse_isa_desc(sys.argv[2])