From: Steve Reinhardt Date: Sun, 12 Feb 2006 05:31:19 +0000 (-0500) Subject: Polishing of isa_parser.py internal operand handling, resulting in X-Git-Tag: m5_2.0_beta1~87^2~107^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=79613686f0f6a8725e88e935a7c9ff4ede4cfc2b;p=gem5.git Polishing of isa_parser.py internal operand handling, resulting in minor change to syntax of 'def operands' in ISA descriptions. arch/alpha/isa/main.isa: arch/mips/isa/operands.isa: arch/sparc/isa/operands.isa: Change 'def operands' statement to work with new isa_parser changes. arch/isa_parser.py: Merge OperandTraits and OperandDescriptor objects into a unified hierarchy of Operand objects. Required a change in the syntax of the 'def operands' statement in the ISA description. --HG-- extra : convert_revision : cb43f1607311497ead88ba13953d410ab5bc6a37 --- diff --git a/arch/alpha/isa/main.isa b/arch/alpha/isa/main.isa index fa2f71a29..c082df8c8 100644 --- a/arch/alpha/isa/main.isa +++ b/arch/alpha/isa/main.isa @@ -148,20 +148,19 @@ def operands {{ # Int regs default to unsigned, but code should not count on this. # For clarity, descriptions that depend on unsigned behavior should # explicitly specify '.uq'. - 'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1), - 'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2), - 'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3), - 'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1), - 'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2), - 'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3), - 'Mem': MemOperandTraits('uq', None, - ('IsMemRef', 'IsLoad', 'IsStore'), 4), - 'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4), - 'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1), - 'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1), + 'Ra': ('IntReg', 'uq', 'RA', 'IsInteger', 1), + 'Rb': ('IntReg', 'uq', 'RB', 'IsInteger', 2), + 'Rc': ('IntReg', 'uq', 'RC', 'IsInteger', 3), + 'Fa': ('FloatReg', 'df', 'FA', 'IsFloating', 1), + 'Fb': ('FloatReg', 'df', 'FB', 'IsFloating', 2), + 'Fc': ('FloatReg', 'df', 'FC', 'IsFloating', 3), + 'Mem': ('Mem', 'uq', None, ('IsMemRef', 'IsLoad', 'IsStore'), 4), + 'NPC': ('NPC', 'uq', None, ( None, None, 'IsControl' ), 4), + 'Runiq': ('ControlReg', 'uq', 'Uniq', None, 1), + 'FPCR': (' ControlReg', 'uq', 'Fpcr', None, 1), # The next two are hacks for non-full-system call-pal emulation - 'R0': IntRegOperandTraits('uq', '0', None, 1), - 'R16': IntRegOperandTraits('uq', '16', None, 1) + 'R0': ('IntReg', 'uq', '0', None, 1), + 'R16': ('IntReg', 'uq', '16', None, 1) }}; //////////////////////////////////////////////////////////////////// diff --git a/arch/isa_parser.py b/arch/isa_parser.py index 0e33b4ff7..030bb5a7c 100755 --- a/arch/isa_parser.py +++ b/arch/isa_parser.py @@ -320,29 +320,27 @@ def p_global_let(t): # widths (stored in operandTypeMap). def p_def_operand_types(t): 'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI' - s = 'global operandTypeMap; operandTypeMap = {' + t[3] + '}' try: - exec s + userDict = eval('{' + t[3] + '}') except Exception, exc: error(t.lineno(1), 'error: %s in def operand_types block "%s".' % (exc, t[3])) - buildOperandSizeMap() + buildOperandTypeMap(userDict, t.lineno(1)) t[0] = GenCode() # contributes nothing to the output C++ file # Define the mapping from operand names to operand classes and other -# traits. Stored in operandTraitsMap. +# traits. Stored in operandNameMap. def p_def_operands(t): 'def_operands : DEF OPERANDS CODELIT SEMI' - if not globals().has_key('operandSizeMap'): + if not globals().has_key('operandTypeMap'): error(t.lineno(1), 'error: operand types must be defined before operands') - s = 'global operandTraitsMap; operandTraitsMap = {' + t[3] + '}' try: - exec s + userDict = eval('{' + t[3] + '}') except Exception, exc: error(t.lineno(1), 'error: %s in def operands block "%s".' % (exc, t[3])) - defineDerivedOperandVars() + buildOperandNameMap(userDict, t.lineno(1)) t[0] = GenCode() # contributes nothing to the output C++ file # A bitfield definition looks like: @@ -847,6 +845,19 @@ class GenCode: # a defineInst() method that generates the code for an instruction # definition. +exportContextSymbols = ('InstObjParams', 'CodeBlock', + '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 @@ -1077,13 +1088,12 @@ def makeList(arg): else: return [ arg ] -# generate operandSizeMap based on provided operandTypeMap: -# basically generate equiv. C++ type and make is_signed flag -def buildOperandSizeMap(): - global operandSizeMap - operandSizeMap = {} - for ext in operandTypeMap.keys(): - (desc, size) = operandTypeMap[ext] +# Generate operandTypeMap from the user's 'def operand_types' +# statement. +def buildOperandTypeMap(userDict, lineno): + global operandTypeMap + operandTypeMap = {} + for (ext, (desc, size)) in userDict.iteritems(): if desc == 'signed int': ctype = 'int%d_t' % size is_signed = 1 @@ -1097,41 +1107,56 @@ def buildOperandSizeMap(): elif size == 64: ctype = 'double' if ctype == '': - error(0, 'Unrecognized type description "%s" in operandTypeMap') - operandSizeMap[ext] = (size, ctype, is_signed) + error(0, 'Unrecognized type description "%s" in userDict') + operandTypeMap[ext] = (size, ctype, is_signed) # -# Base class for operand traits. An instance of this class (or actually -# a class derived from this one) encapsulates the traits of a particular -# operand type (e.g., "32-bit integer register"). # -class OperandTraits: - def __init__(self, dflt_ext, reg_spec, flags, sort_pri): - self.dflt_ext = dflt_ext - (self.dflt_size, self.dflt_type, self.dflt_is_signed) = \ - operandSizeMap[dflt_ext] - self.reg_spec = reg_spec - # 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') - self.flags = ( [], [], [] ) - elif isinstance(flags, str): - # a single flag: assumed to be unconditional - self.flags = ( [ flags ], [], [] ) - elif isinstance(flags, list): - # a list of flags: also assumed to be unconditional - self.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 - self.flags = (makeList(uncond_flags), - makeList(src_flags), makeList(dest_flags)) - self.sort_pri = sort_pri +# +# Base class for operand descriptors. An instance of this class (or +# actually a class derived from this one) represents a specific +# operand for a code block (e.g, "Rc.sq" as a dest). Intermediate +# derived classes encapsulates the traits of a particular operand type +# (e.g., "32-bit integer register"). +# +class Operand(object): + def __init__(self, full_name, ext, is_src, is_dest): + self.full_name = full_name + self.ext = ext + self.is_src = is_src + self.is_dest = is_dest + # The 'effective extension' (eff_ext) is either the actual + # extension, if one was explicitly provided, or the default. + if ext: + self.eff_ext = ext + else: + 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() + + # 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): + self.flags = self.getFlags() + self.constructor = self.makeConstructor() + self.op_decl = self.makeDecl() + + if self.is_src: + self.op_rd = self.makeRead() + else: + self.op_rd = '' + + if self.is_dest: + self.op_wb = self.makeWrite() + else: + self.op_wb = '' def isMem(self): return 0 @@ -1148,223 +1173,239 @@ class OperandTraits: def isControlReg(self): return 0 - def getFlags(self, op_desc): + def getFlags(self): # note the empty slice '[:]' gives us a copy of self.flags[0] # instead of a reference to it my_flags = self.flags[0][:] - if op_desc.is_src: + if self.is_src: my_flags += self.flags[1] - if op_desc.is_dest: + if self.is_dest: my_flags += self.flags[2] return my_flags - def makeDecl(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + def makeDecl(self): # Note that initializations in the declarations are solely # to avoid 'uninitialized variable' errors from the compiler. - return type + ' ' + op_desc.base_name + ' = 0;\n'; + return self.ctype + ' ' + self.base_name + ' = 0;\n'; -class IntRegOperandTraits(OperandTraits): +class IntRegOperand(Operand): def isReg(self): return 1 def isIntReg(self): return 1 - def makeConstructor(self, op_desc): + def makeConstructor(self): c = '' - if op_desc.is_src: + if self.is_src: c += '\n\t_srcRegIdx[%d] = %s;' % \ - (op_desc.src_reg_idx, self.reg_spec) - if op_desc.is_dest: + (self.src_reg_idx, self.reg_spec) + if self.is_dest: c += '\n\t_destRegIdx[%d] = %s;' % \ - (op_desc.dest_reg_idx, self.reg_spec) + (self.dest_reg_idx, self.reg_spec) return c - def makeRead(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] - if (type == 'float' or type == 'double'): + def makeRead(self): + if (self.ctype == 'float' or self.ctype == 'double'): error(0, 'Attempt to read integer register as FP') - if (size == self.dflt_size): + if (self.size == self.dflt_size): return '%s = xc->readIntReg(this, %d);\n' % \ - (op_desc.base_name, op_desc.src_reg_idx) + (self.base_name, self.src_reg_idx) else: return '%s = bits(xc->readIntReg(this, %d), %d, 0);\n' % \ - (op_desc.base_name, op_desc.src_reg_idx, size-1) + (self.base_name, self.src_reg_idx, self.size-1) - def makeWrite(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] - if (type == 'float' or type == 'double'): + def makeWrite(self): + if (self.ctype == 'float' or self.ctype == 'double'): error(0, 'Attempt to write integer register as FP') - if (size != self.dflt_size and is_signed): - final_val = 'sext<%d>(%s)' % (size, op_desc.base_name) + if (self.size != self.dflt_size and self.is_signed): + final_val = 'sext<%d>(%s)' % (self.size, self.base_name) else: - final_val = op_desc.base_name + final_val = self.base_name wb = ''' { %s final_val = %s; xc->setIntReg(this, %d, final_val);\n if (traceData) { traceData->setData(final_val); } - }''' % (self.dflt_type, final_val, op_desc.dest_reg_idx) + }''' % (self.dflt_ctype, final_val, self.dest_reg_idx) return wb -class FloatRegOperandTraits(OperandTraits): +class FloatRegOperand(Operand): def isReg(self): return 1 def isFloatReg(self): return 1 - def makeConstructor(self, op_desc): + def makeConstructor(self): c = '' - if op_desc.is_src: + if self.is_src: c += '\n\t_srcRegIdx[%d] = %s + FP_Base_DepTag;' % \ - (op_desc.src_reg_idx, self.reg_spec) - if op_desc.is_dest: + (self.src_reg_idx, self.reg_spec) + if self.is_dest: c += '\n\t_destRegIdx[%d] = %s + FP_Base_DepTag;' % \ - (op_desc.dest_reg_idx, self.reg_spec) + (self.dest_reg_idx, self.reg_spec) return c - def makeRead(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + def makeRead(self): bit_select = 0 - if (type == 'float'): + if (self.ctype == 'float'): func = 'readFloatRegSingle' - elif (type == 'double'): + elif (self.ctype == 'double'): func = 'readFloatRegDouble' else: func = 'readFloatRegInt' - if (size != self.dflt_size): + if (self.size != self.dflt_size): bit_select = 1 base = 'xc->%s(this, %d)' % \ - (func, op_desc.src_reg_idx) + (func, self.src_reg_idx) if bit_select: return '%s = bits(%s, %d, 0);\n' % \ - (op_desc.base_name, base, size-1) + (self.base_name, base, self.size-1) else: - return '%s = %s;\n' % (op_desc.base_name, base) + return '%s = %s;\n' % (self.base_name, base) - def makeWrite(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] - final_val = op_desc.base_name - if (type == 'float'): + def makeWrite(self): + final_val = self.base_name + final_ctype = self.ctype + if (self.ctype == 'float'): func = 'setFloatRegSingle' - elif (type == 'double'): + elif (self.ctype == 'double'): func = 'setFloatRegDouble' else: func = 'setFloatRegInt' - type = 'uint%d_t' % self.dflt_size - if (size != self.dflt_size and is_signed): - final_val = 'sext<%d>(%s)' % (size, op_desc.base_name) + 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) wb = ''' { %s final_val = %s; xc->%s(this, %d, final_val);\n if (traceData) { traceData->setData(final_val); } - }''' % (type, final_val, func, op_desc.dest_reg_idx) + }''' % (final_ctype, final_val, func, self.dest_reg_idx) return wb -class ControlRegOperandTraits(OperandTraits): +class ControlRegOperand(Operand): def isReg(self): return 1 def isControlReg(self): return 1 - def makeConstructor(self, op_desc): + def makeConstructor(self): c = '' - if op_desc.is_src: + if self.is_src: c += '\n\t_srcRegIdx[%d] = %s_DepTag;' % \ - (op_desc.src_reg_idx, self.reg_spec) - if op_desc.is_dest: + (self.src_reg_idx, self.reg_spec) + if self.is_dest: c += '\n\t_destRegIdx[%d] = %s_DepTag;' % \ - (op_desc.dest_reg_idx, self.reg_spec) + (self.dest_reg_idx, self.reg_spec) return c - def makeRead(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + def makeRead(self): bit_select = 0 - if (type == 'float' or type == 'double'): + if (self.ctype == 'float' or self.ctype == 'double'): error(0, 'Attempt to read control register as FP') base = 'xc->read%s()' % self.reg_spec - if size == self.dflt_size: - return '%s = %s;\n' % (op_desc.base_name, base) + if self.size == self.dflt_size: + return '%s = %s;\n' % (self.base_name, base) else: return '%s = bits(%s, %d, 0);\n' % \ - (op_desc.base_name, base, size-1) + (self.base_name, base, self.size-1) - def makeWrite(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] - if (type == 'float' or type == 'double'): + def makeWrite(self): + if (self.ctype == 'float' or self.ctype == 'double'): error(0, 'Attempt to write control register as FP') - wb = 'xc->set%s(%s);\n' % (self.reg_spec, op_desc.base_name) + wb = 'xc->set%s(%s);\n' % (self.reg_spec, self.base_name) wb += 'if (traceData) { traceData->setData(%s); }' % \ - op_desc.base_name + self.base_name return wb -class MemOperandTraits(OperandTraits): +class MemOperand(Operand): def isMem(self): return 1 - def makeConstructor(self, op_desc): + def makeConstructor(self): return '' - def makeDecl(self, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + def makeDecl(self): # Note that initializations in the declarations are solely # to avoid 'uninitialized variable' errors from the compiler. # Declare memory data variable. - c = '%s %s = 0;\n' % (type, op_desc.base_name) + c = '%s %s = 0;\n' % (self.ctype, self.base_name) return c - def makeRead(self, op_desc): + def makeRead(self): return '' - def makeWrite(self, op_desc): + def makeWrite(self): 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, op_desc): - (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] - return size + def makeAccSize(self): + return self.size -class NPCOperandTraits(OperandTraits): - def makeConstructor(self, op_desc): +class NPCOperand(Operand): + def makeConstructor(self): return '' - def makeRead(self, op_desc): - return '%s = xc->readPC() + 4;\n' % op_desc.base_name - - def makeWrite(self, op_desc): - return 'xc->setNextPC(%s);\n' % op_desc.base_name - - -exportContextSymbols = ('IntRegOperandTraits', 'FloatRegOperandTraits', - 'ControlRegOperandTraits', 'MemOperandTraits', - 'NPCOperandTraits', 'InstObjParams', 'CodeBlock', - 'makeList', 're', 'string') - -exportContext = {} - -def updateExportContext(): - exportContext.update(exportDict(*exportContextSymbols)) - exportContext.update(templateMap) - + def makeRead(self): + return '%s = xc->readPC() + 4;\n' % self.base_name -def exportDict(*symNames): - return dict([(s, eval(s)) for s in symNames]) + def makeWrite(self): + return 'xc->setNextPC(%s);\n' % self.base_name -# -# Define operand variables that get derived from the basic declaration -# of ISA-specific operands in operandTraitsMap. This function must be -# called by the ISA description file explicitly after defining -# operandTraitsMap (in a 'let' block). -# -def defineDerivedOperandVars(): - global operands - operands = operandTraitsMap.keys() +def buildOperandNameMap(userDict, lineno): + global operandNameMap + operandNameMap = {} + for (op_name, val) in userDict.iteritems(): + (base_cls_name, dflt_ext, reg_spec, flags, sort_pri) = val + (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'): + 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 = userDict.keys() operandsREString = (r''' (?