X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Farch%2Fisa_parser.py;h=ec0efe5e6a95c8ec806e9df5877b1106c286aa7c;hb=d2fd3b2ec2c265eb9ed3bdcc1db3e4c3feee3846;hp=a8f9852ea76cf6c3487cd88021e338887932a41b;hpb=40a05f04fbcd0f411b4cf2e2ac38b9f5bb42f4b6;p=gem5.git diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py index a8f9852ea..ec0efe5e6 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'(?pcState();\n' + \ + myDict['op_rd'] + + # Compose the op_wb string. If we're going to write back the + # PC state because we changed some of its elements, we'll need to + # do that as early as possible. That allows later uncoordinated + # modifications to the PC to layer appropriately. + reordered = list(operands.items) + reordered.reverse() + op_wb_str = '' + pcWbStr = 'xc->pcState(__parserAutoPCState);\n' + for op_desc in reordered: + if op_desc.isPCPart() and op_desc.is_dest: + op_wb_str = op_desc.op_wb + pcWbStr + op_wb_str + pcWbStr = '' + else: + op_wb_str = op_desc.op_wb + op_wb_str + myDict['op_wb'] = op_wb_str elif isinstance(d, dict): # if the argument is a dictionary, we just use it. @@ -198,7 +233,7 @@ class Template(object): # CPU-specific term gets interpolated into another template or into # an output block. def __str__(self): - return expand_cpu_symbols_to_string(self.template) + return self.parser.expandCpuSymbolsToString(self.template) ################ # Format object. @@ -207,21 +242,8 @@ class Template(object): # 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(parser.templateMap) - -def exportDict(*symNames): - return dict([(s, eval(s)) for s in symNames]) - - class Format(object): def __init__(self, id, params, code): - # constructor: just save away arguments self.id = id self.params = params label = 'def format ' + id @@ -235,24 +257,25 @@ class Format(object): exec c self.func = defInst - def defineInst(self, name, args, lineno): - context = {} - updateExportContext() - context.update(exportContext) + def defineInst(self, parser, name, args, lineno): + parser.updateExportContext() + context = parser.exportContext.copy() if len(name): Name = name[0].upper() if len(name) > 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 +283,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 +306,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 +341,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 +412,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,39 +420,32 @@ 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} - if self.size != self.dflt_size: - return '%s = bits(%s, %d, 0);\n' % \ - (self.base_name, code, self.size-1) - else: - return '%s = %s;\n' % \ - (self.base_name, code) + subst_dict = {"name": self.base_name, + "func": func, + "reg_idx": self.reg_spec, + "ctype": self.ctype} + if hasattr(self, 'src_reg_idx'): + subst_dict['op_idx'] = self.src_reg_idx + code = self.read_code % subst_dict + return '%s = %s;\n' % (self.base_name, code) def buildWriteCode(self, func = None): - if (self.size != self.dflt_size and self.is_signed): - 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, + "ctype": self.ctype, + "final_val": self.base_name} + 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; %s; if (traceData) { traceData->setData(final_val); } - }''' % (self.dflt_ctype, final_val, code) + }''' % (self.dflt_ctype, self.base_name, 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 @@ -515,38 +454,32 @@ class Operand(object): # extension, if one was explicitly provided, or the default. if ext: self.eff_ext = ext - else: + elif hasattr(self, 'dflt_ext'): self.eff_ext = self.dflt_ext - (self.size, self.ctype, self.is_signed) = 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. - if self.isMem(): - self.mem_acc_size = self.makeAccSize() - if self.ctype in ['Twin32_t', 'Twin64_t']: - self.mem_acc_type = 'Twin' - else: - self.mem_acc_type = 'uint' + if hasattr(self, 'eff_ext'): + self.ctype = parser.operandTypeMap[self.eff_ext] # 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 = '' @@ -567,6 +500,18 @@ class Operand(object): def isControlReg(self): return 0 + def isPCState(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 @@ -589,49 +534,68 @@ class IntRegOperand(Operand): 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) - def makeRead(self): + return c_src + c_dest + + 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') - if (self.size == self.dflt_size): - return '%s = xc->readIntRegOperand(this, %d);\n' % \ - (self.base_name, self.src_reg_idx) - elif (self.size > self.dflt_size): - int_reg_val = 'xc->readIntRegOperand(this, %d)' % \ - (self.src_reg_idx) - if (self.is_signed): - int_reg_val = 'sext<%d>(%s)' % (self.dflt_size, int_reg_val) - return '%s = %s;\n' % (self.base_name, int_reg_val) + + 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: - return '%s = bits(xc->readIntRegOperand(this, %d), %d, 0);\n' % \ - (self.base_name, self.src_reg_idx, self.size-1) + int_reg_val = 'xc->readIntRegOperand(this, %d)' % self.src_reg_idx - def makeWrite(self): + 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 integer register as FP') if self.write_code != None: return self.buildWriteCode('setIntRegOperand') - if (self.size != self.dflt_size and self.is_signed): - final_val = 'sext<%d>(%s)' % (self.size, self.base_name) + + if predWrite: + wp = 'true' + if self.hasWritePred(): + wp = self.write_predicate + + wcond = 'if (%s)' % (wp) + windex = '_destIndex++' else: - final_val = self.base_name + 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.dflt_ctype, final_val, self.dest_reg_idx) + }''' % (wcond, self.ctype, self.base_name, windex) + return wb class FloatRegOperand(Operand): @@ -641,53 +605,59 @@ 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_Base_DepTag;' % \ + (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_Base_DepTag;' % \ + (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' else: func = 'readFloatRegOperandBits' - if (self.size != self.dflt_size): - bit_select = 1 - base = 'xc->%s(this, %d)' % (func, self.src_reg_idx) if self.read_code != None: return self.buildReadCode(func) - if bit_select: - return '%s = bits(%s, %d, 0);\n' % \ - (self.base_name, base, self.size-1) + + if predRead: + rindex = '_sourceIndex++' else: - return '%s = %s;\n' % (self.base_name, base) + rindex = '%d' % self.src_reg_idx + + return '%s = xc->%s(this, %s);\n' % \ + (self.base_name, func, rindex) - def makeWrite(self): - final_val = self.base_name - final_ctype = self.ctype + def makeWrite(self, predWrite): if (self.ctype == 'float' or self.ctype == 'double'): func = 'setFloatRegOperand' - elif (self.ctype == 'uint32_t' or self.ctype == 'uint64_t'): - func = 'setFloatRegOperandBits' else: func = 'setFloatRegOperandBits' - final_ctype = 'uint%d_t' % self.dflt_size - if (self.size != self.dflt_size and self.is_signed): - final_val = 'sext<%d>(%s)' % (self.size, self.base_name) 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; - xc->%s(this, %d, final_val);\n + %s\n if (traceData) { traceData->setData(final_val); } - }''' % (final_ctype, final_val, func, self.dest_reg_idx) + }''' % (self.ctype, self.base_name, wp) return wb class ControlRegOperand(Operand): @@ -697,235 +667,122 @@ 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 + Ctrl_Base_DepTag;' % \ + (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 + Ctrl_Base_DepTag;' % \ + (self.reg_spec) - def makeRead(self): + return c_src + c_dest + + 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') - base = 'xc->readMiscRegOperand(this, %s)' % self.src_reg_idx - if self.size == self.dflt_size: - return '%s = %s;\n' % (self.base_name, base) + + if predRead: + rindex = '_sourceIndex++' else: - return '%s = bits(%s, %d, 0);\n' % \ - (self.base_name, base, self.size-1) + rindex = '%d' % self.src_reg_idx - def makeWrite(self): + return '%s = xc->readMiscRegOperand(this, %s);\n' % \ + (self.base_name, rindex) + + 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): # Note that initializations in the declarations are solely # to avoid 'uninitialized variable' errors from the compiler. # Declare memory data variable. - if self.ctype in ['Twin32_t','Twin64_t']: - return "%s %s; %s.a = 0; %s.b = 0;\n" % \ - (self.ctype, self.base_name, self.base_name, self.base_name) 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 '' - # Return the memory access size *in bits*, suitable for - # forming a type via "uint%d_t". Divide by 8 if you want bytes. - def makeAccSize(self): - return self.size - -class PCOperand(Operand): - def makeConstructor(self): - return '' - - def makeRead(self): - return '%s = xc->readPC();\n' % self.base_name - - def makeWrite(self): - return 'xc->setPC(%s);\n' % self.base_name - -class UPCOperand(Operand): - def makeConstructor(self): +class PCStateOperand(Operand): + def makeConstructor(self, predRead, predWrite): return '' - def makeRead(self): - if self.read_code != None: - return self.buildReadCode('readMicroPC') - return '%s = xc->readMicroPC();\n' % self.base_name - - def makeWrite(self): - if self.write_code != None: - return self.buildWriteCode('setMicroPC') - return 'xc->setMicroPC(%s);\n' % self.base_name - -class NUPCOperand(Operand): - def makeConstructor(self): - return '' - - def makeRead(self): - if self.read_code != None: - return self.buildReadCode('readNextMicroPC') - return '%s = xc->readNextMicroPC();\n' % self.base_name - - def makeWrite(self): - if self.write_code != None: - return self.buildWriteCode('setNextMicroPC') - return 'xc->setNextMicroPC(%s);\n' % self.base_name - -class NPCOperand(Operand): - def makeConstructor(self): - return '' - - def makeRead(self): - if self.read_code != None: - return self.buildReadCode('readNextPC') - return '%s = xc->readNextPC();\n' % self.base_name - - def makeWrite(self): - if self.write_code != None: - return self.buildWriteCode('setNextPC') - return 'xc->setNextPC(%s);\n' % self.base_name - -class NNPCOperand(Operand): - def makeConstructor(self): - return '' - - def makeRead(self): - if self.read_code != None: - return self.buildReadCode('readNextNPC') - return '%s = xc->readNextNPC();\n' % self.base_name - - def makeWrite(self): - if self.write_code != None: - 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] + def makeRead(self, predRead): + if self.reg_spec: + # A component of the PC state. + return '%s = __parserAutoPCState.%s();\n' % \ + (self.base_name, self.reg_spec) else: - read_code = None - if len(val) > 6: - write_code = val[6] + # The whole PC state itself. + return '%s = xc->pcState();\n' % self.base_name + + def makeWrite(self, predWrite): + if self.reg_spec: + # A component of the PC state. + return '__parserAutoPCState.%s(%s);\n' % \ + (self.reg_spec, self.base_name) 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''' - (?pcState(%s);\n' % self.base_name + + def makeDecl(self): + ctype = 'TheISA::PCState' + if self.isPCPart(): + ctype = self.ctype + return "%s %s;\n" % (ctype, self.base_name) + + def isPCState(self): + return 1 class OperandList(object): '''Find all the operands in the given code block. Returns an operand descriptor list (instance of class OperandList).''' - def __init__(self, code): + 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: - match = operandsRE.search(code, next_pos) + match = parser.operandsRE.search(code, next_pos) if not match: # no more matches: we're done break @@ -946,8 +803,8 @@ class OperandList(object): op_desc.is_dest = op_desc.is_dest or is_dest else: # new operand: create new descriptor - op_desc = operandNameMap[op_base](op_full, op_ext, - is_src, is_dest) + op_desc = parser.operandNameMap[op_base](parser, + op_full, op_ext, is_src, is_dest) self.append(op_desc) # start next search after end of current match next_pos = match.end() @@ -958,7 +815,14 @@ class OperandList(object): self.numDestRegs = 0 self.numFPDestRegs = 0 self.numIntDestRegs = 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: @@ -971,20 +835,29 @@ class OperandList(object): self.numFPDestRegs += 1 elif op_desc.isIntReg(): self.numIntDestRegs += 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 - global maxInstSrcRegs - global maxInstDestRegs - if maxInstSrcRegs < self.numSrcRegs: - maxInstSrcRegs = self.numSrcRegs - if maxInstDestRegs < self.numDestRegs: - maxInstDestRegs = self.numDestRegs + + # 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) @@ -1034,15 +907,16 @@ class OperandList(object): class SubOperandList(OperandList): '''Find all the operands in the given code block. Returns an operand descriptor list (instance of class OperandList).''' - def __init__(self, code, master_list): + 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: - match = operandsRE.search(code, next_pos) + match = parser.operandsRE.search(code, next_pos) if not match: # no more matches: we're done break @@ -1065,33 +939,55 @@ class SubOperandList(OperandList): next_pos = match.end() self.sort() self.memOperand = None + # Whether the whole PC needs to be read so parts of it can be accessed + self.readPC = False + # Whether the whole PC needs to be written after parts of it were + # changed + self.setPC = False + # 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()) assignRE = re.compile(r'\s*=(?!=)', re.MULTILINE) -# Munge operand names in code string to make legal C++ variable names. -# This means getting rid of the type extension if any. -# (Will match base_name attribute of Operand object.) -def substMungedOpNames(code): - return operandsWithExtRE.sub(r'\1', code) - -# Fix up code snippets for final substitution in templates. -def mungeSnippet(s): - if isinstance(s, str): - return substMungedOpNames(substBitOps(s)) - else: - return s - def makeFlagConstructor(flag_list): if len(flag_list) == 0: return '' @@ -1115,7 +1011,7 @@ instFlagRE = re.compile(r'Is.*') opClassRE = re.compile(r'.*Op|No_OpClass') class InstObjParams(object): - def __init__(self, mnem, class_name, base_class = '', + def __init__(self, parser, mnem, class_name, base_class = '', snippets = {}, opt_args = []): self.mnemonic = mnem self.class_name = class_name @@ -1125,16 +1021,19 @@ class InstObjParams(object): compositeCode = ' '.join(map(str, snippets.values())) self.snippets = snippets - self.operands = OperandList(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 + self.operands = OperandList(parser, compositeCode) + + # 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;' + + self.constructor = header + \ + self.operands.concatAttrStrings('constructor') + self.flags = self.operands.concatAttrLists('flags') # Make a basic guess on the operand class (function unit type). @@ -1189,19 +1088,6 @@ class Stack(list): 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() - - ####################### # # Output file template @@ -1238,18 +1124,44 @@ 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): + def __init__(self, output_dir, cpu_models): super(ISAParser, self).__init__() self.output_dir = output_dir + self.cpuModels = cpu_models + + # variable to hold templates self.templateMap = {} + # This dictionary maps format name strings to Format objects. + self.formatMap = {} + + # The format stack. + self.formatStack = Stack(NoFormat()) + + # The default case stack. + self.defaultStack = Stack(None) + + # 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. + self.fileNameStack = Stack() + + symbols = ('makeList', 're', 'string') + self.exportContext = dict([(s, eval(s)) for s in symbols]) + + self.maxInstSrcRegs = 0 + self.maxInstDestRegs = 0 + self.maxMiscDestRegs = 0 + ##################################################################### # # Lexer @@ -1370,13 +1282,13 @@ class ISAParser(Grammar): return t def t_NEWFILE(self, t): - r'^\#\#newfile\s+"[\w/.-]*"' - fileNameStack.push((t.value[11:-1], t.lexer.lineno)) + r'^\#\#newfile\s+"[^"]*"' + self.fileNameStack.push((t.value[11:-1], t.lexer.lineno)) t.lexer.lineno = 0 def t_ENDFILE(self, t): r'^\#\#endfile' - (old_filename, t.lexer.lineno) = fileNameStack.pop() + (old_filename, t.lexer.lineno) = self.fileNameStack.pop() # # The functions t_NEWLINE, t_ignore, and t_error are @@ -1431,7 +1343,7 @@ class ISAParser(Grammar): # wrap the decode block as a function definition t[4].wrap_decode_block(''' StaticInstPtr -%(isa_name)s::decodeInst(%(isa_name)s::ExtMachInst machInst) +%(isa_name)s::Decoder::decodeInst(%(isa_name)s::ExtMachInst machInst) { using namespace %(namespace)s; ''' % vars(), '}') @@ -1450,7 +1362,7 @@ StaticInstPtr # def and/or output statements. def p_opt_defs_and_outputs_0(self, t): 'opt_defs_and_outputs : empty' - t[0] = GenCode() + t[0] = GenCode(self) def p_opt_defs_and_outputs_1(self, t): 'opt_defs_and_outputs : defs_and_outputs' @@ -1487,22 +1399,22 @@ StaticInstPtr # get handled in GenCode) by doubling them first so that the # format operation will reduce them back to single '%'s. def process_output(self, s): - s = protect_non_subst_percents(s) + s = self.protectNonSubstPercents(s) # protects cpu-specific symbols too - s = protect_cpu_symbols(s) + s = self.protectCpuSymbols(s) return substBitOps(s % self.templateMap) def p_output_header(self, t): 'output_header : OUTPUT HEADER CODELIT SEMI' - t[0] = GenCode(header_output = self.process_output(t[3])) + 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(decoder_output = self.process_output(t[3])) + 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(exec_output = self.process_output(t[3])) + t[0] = GenCode(self, exec_output = self.process_output(t[3])) # global let blocks 'let {{...}}' (Python code blocks) are # executed directly when seen. Note that these execute in a @@ -1510,44 +1422,50 @@ StaticInstPtr # from polluting this script's namespace. def p_global_let(self, t): 'global_let : LET CODELIT SEMI' - updateExportContext() - exportContext["header_output"] = '' - exportContext["decoder_output"] = '' - exportContext["exec_output"] = '' - exportContext["decode_block"] = '' + self.updateExportContext() + self.exportContext["header_output"] = '' + self.exportContext["decoder_output"] = '' + self.exportContext["exec_output"] = '' + self.exportContext["decode_block"] = '' try: - exec fixPythonIndentation(t[2]) in exportContext + exec 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(header_output = exportContext["header_output"], - decoder_output = exportContext["decoder_output"], - exec_output = exportContext["exec_output"], - decode_block = exportContext["decode_block"]) + 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"]) # Define the mapping from operand type extensions to C++ types and # bit widths (stored in operandTypeMap). def p_def_operand_types(self, t): 'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI' try: - user_dict = eval('{' + t[3] + '}') + self.operandTypeMap = eval('{' + t[3] + '}') except Exception, exc: + if debug: + raise error(t, 'error: %s in def operand_types block "%s".' % (exc, t[3])) - buildOperandTypeMap(user_dict, t.lexer.lineno) - t[0] = GenCode() # contributes nothing to the output C++ file + 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. def p_def_operands(self, t): 'def_operands : DEF OPERANDS CODELIT SEMI' - if not globals().has_key('operandTypeMap'): + if not hasattr(self, 'operandTypeMap'): error(t, 'error: operand types must be defined before operands') try: - user_dict = eval('{' + t[3] + '}', exportContext) + user_dict = eval('{' + t[3] + '}', self.exportContext) except Exception, exc: + if debug: + raise error(t, 'error: %s in def operands block "%s".' % (exc, t[3])) - buildOperandNameMap(user_dict, t.lexer.lineno) - t[0] = GenCode() # contributes nothing to the output C++ file + 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 [:]' @@ -1558,7 +1476,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 +1485,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 +1494,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 +1514,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 +1596,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 +1613,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 +1622,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 +1655,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 +1668,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 +1677,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 +1737,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 +1751,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 +1852,162 @@ 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 buildOperandNameMap(self, user_dict, lineno): + operand_name = {} + for op_name, val in user_dict.iteritems(): + + # 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. + # 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 = {} + attrList = ['reg_spec', 'flags', 'sort_pri', + '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 + # 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() + extensions = self.operandTypeMap.keys() + + operandsREString = r''' + (?[\w/.-]*)".*$', + includeRE = re.compile(r'^\s*##include\s+"(?P[^"]*)".*$', re.MULTILINE) def replace_include(self, matchobj, dirname): @@ -1984,14 +2057,14 @@ StaticInstPtr except IOError: error('Error including file "%s"' % filename) - fileNameStack.push((filename, 0)) + self.fileNameStack.push((filename, 0)) # Find any includes and include them def replace(matchobj): return self.replace_include(matchobj, current_dir) contents = self.includeRE.sub(replace, contents) - fileNameStack.pop() + self.fileNameStack.pop() return contents def _parse_isa_desc(self, isa_desc_file): @@ -2003,11 +2076,11 @@ StaticInstPtr isa_desc = self.read_and_flatten(isa_desc_file) # Initialize filename stack with outer file. - fileNameStack.push((isa_desc_file, 0)) + self.fileNameStack.push((isa_desc_file, 0)) # Parse it. (isa_name, namespace, global_code, namespace_code) = \ - self.parse(isa_desc) + self.parse_string(isa_desc) # grab the last three path components of isa_desc_file to put in # the output @@ -2029,7 +2102,7 @@ StaticInstPtr self.update_if_needed('decoder.cc', file_template % vars()) # generate per-cpu exec files - for cpu in cpu_models: + for cpu in self.cpuModels: includes = '#include "decoder.hh"\n' includes += cpu.includes global_output = global_code.exec_output[cpu.name] @@ -2040,10 +2113,9 @@ StaticInstPtr # 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. - global maxInstSrcRegs - MaxInstSrcRegs = maxInstSrcRegs - global maxInstDestRegs - MaxInstDestRegs = maxInstDestRegs + MaxInstSrcRegs = self.maxInstSrcRegs + MaxInstDestRegs = self.maxInstDestRegs + MaxMiscDestRegs = self.maxMiscDestRegs # max_inst_regs.hh self.update_if_needed('max_inst_regs.hh', max_inst_regs_template % vars()) @@ -2052,15 +2124,11 @@ StaticInstPtr try: self._parse_isa_desc(*args, **kwargs) except ISAParserError, e: - e.exit(fileNameStack) - -# global list of CpuModel objects (see cpu_models.py) -cpu_models = [] + e.exit(self.fileNameStack) # Called as script: get args from command line. # Args are: 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])