+# Copyright (c) 2014 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
# Copyright (c) 2003-2005 The Regents of The University of Michigan
-# Copyright (c) 2013 Advanced Micro Devices, Inc.
+# Copyright (c) 2013,2015 Advanced Micro Devices, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
return s
class ISAParserError(Exception):
- """Error handler for parser errors"""
+ """Exception class for parser errors"""
def __init__(self, first, second=None):
if second is None:
self.lineno = 0
self.string = first
else:
- if hasattr(first, 'lexer'):
- first = first.lexer.lineno
self.lineno = first
self.string = second
- 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
- # itself).
-
- spaces = ""
- for (filename, line) in filename_stack[:-1]:
- print "%sIn file included from %s:" % (spaces, filename)
- spaces += " "
-
- # Print a Python stack backtrace if requested.
- if print_traceback or not self.lineno:
- traceback.print_exc()
-
- line_str = "%s:" % (filename_stack[-1][0], )
- if self.lineno:
- line_str += "%d:" % (self.lineno, )
-
- return "%s%s %s" % (spaces, line_str, self.string)
-
- def exit(self, filename_stack, print_traceback=debug):
- # Just call exit.
-
- sys.exit(self.display(filename_stack, print_traceback))
+ def __str__(self):
+ return self.string
def error(*args):
raise ISAParserError(*args)
return ''
def makeDecl(self):
- # Note that initializations in the declarations are solely
- # to avoid 'uninitialized variable' errors from the compiler.
# Declare memory data variable.
- return '%s %s = 0;\n' % (self.ctype, self.base_name)
+ return '%s %s;\n' % (self.ctype, self.base_name)
def makeRead(self, predRead):
if self.read_code != None:
ctype = 'TheISA::PCState'
if self.isPCPart():
ctype = self.ctype
- return "%s %s;\n" % (ctype, self.base_name)
+ # Note that initializations in the declarations are solely
+ # to avoid 'uninitialized variable' errors from the compiler.
+ return '%s %s = 0;\n' % (ctype, self.base_name)
def isPCState(self):
return 1
# find this op in the master list
op_desc = master_list.find_base(op_base)
if not op_desc:
- error('Found operand %s which is not in the master list!' \
- ' This is an internal error' % op_base)
+ error('Found operand %s which is not in the master list!'
+ % op_base)
else:
# See if we've already found this operand
op_desc = self.find_base(op_base)
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)
+# Regular expression object to match assignment statements (used in
+# findOperands()). If the code immediately following the first
+# appearance of the operand matches this regex, then the operand
+# appears to be on the LHS of an assignment, and is thus a
+# destination. basically we're looking for an '=' that's not '=='.
+# The heinous tangle before that handles the case where the operand
+# has an array subscript.
+assignRE = re.compile(r'(\[[^\]]+\])?\s*=(?!=)', re.MULTILINE)
def makeFlagConstructor(flag_list):
if len(flag_list) == 0:
self.flags = self.operands.concatAttrLists('flags')
- # Make a basic guess on the operand class (function unit type).
- # These are good enough for most cases, and can be overridden
- # later otherwise.
- if 'IsStore' in self.flags:
- self.op_class = 'MemWriteOp'
- elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags:
- self.op_class = 'MemReadOp'
- elif 'IsFloating' in self.flags:
- self.op_class = 'FloatAddOp'
- else:
- self.op_class = 'IntAluOp'
+ self.op_class = None
# Optional arguments are assumed to be either StaticInst flags
# or an OpClass value. To avoid having to import a complete
error('InstObjParams: optional arg "%s" not recognized '
'as StaticInst::Flag or OpClass.' % oa)
+ # Make a basic guess on the operand class if not set.
+ # These are good enough for most cases.
+ if not self.op_class:
+ if 'IsStore' in self.flags:
+ self.op_class = 'MemWriteOp'
+ elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags:
+ self.op_class = 'MemReadOp'
+ elif 'IsFloating' in self.flags:
+ self.op_class = 'FloatAddOp'
+ else:
+ self.op_class = 'IntAluOp'
+
# add flag initialization to contructor here to include
# any flags added via opt_args
self.constructor += makeFlagConstructor(self.flags)
def top(self):
return self[-1]
+# Format a file include stack backtrace as a string
+def backtrace(filename_stack):
+ fmt = "In file included from %s:"
+ return "\n".join([fmt % f for f in filename_stack])
+
+
+#######################
+#
+# LineTracker: track filenames along with line numbers in PLY lineno fields
+# PLY explicitly doesn't do anything with 'lineno' except propagate
+# it. This class lets us tie filenames with the line numbers with a
+# minimum of disruption to existing increment code.
+#
+
+class LineTracker(object):
+ def __init__(self, filename, lineno=1):
+ self.filename = filename
+ self.lineno = lineno
+
+ # Overload '+=' for increments. We need to create a new object on
+ # each update else every token ends up referencing the same
+ # constantly incrementing instance.
+ def __iadd__(self, incr):
+ return LineTracker(self.filename, self.lineno + incr)
+
+ def __str__(self):
+ return "%s:%d" % (self.filename, self.lineno)
+
+ # In case there are places where someone really expects a number
+ def __int__(self):
+ return self.lineno
+
+
#######################
#
# ISA Parser
try:
t.value = int(t.value,0)
except ValueError:
- error(t, 'Integer value "%s" too large' % t.value)
+ error(t.lexer.lineno, 'Integer value "%s" too large' % t.value)
t.value = 0
return t
return t
def t_NEWFILE(self, t):
- r'^\#\#newfile\s+"[^"]*"'
- self.fileNameStack.push((t.value[11:-1], t.lexer.lineno))
- t.lexer.lineno = 0
+ r'^\#\#newfile\s+"[^"]*"\n'
+ self.fileNameStack.push(t.lexer.lineno)
+ t.lexer.lineno = LineTracker(t.value[11:-2])
def t_ENDFILE(self, t):
- r'^\#\#endfile'
- (old_filename, t.lexer.lineno) = self.fileNameStack.pop()
+ r'^\#\#endfile\n'
+ t.lexer.lineno = self.fileNameStack.pop()
#
# The functions t_NEWLINE, t_ignore, and t_error are
# Error handler
def t_error(self, t):
- error(t, "illegal character '%s'" % t.value[0])
+ error(t.lexer.lineno, "illegal character '%s'" % t.value[0])
t.skip(1)
#####################################################################
except Exception, exc:
if debug:
raise
- error(t, 'error: %s in global let block "%s".' % (exc, t[2]))
+ error(t.lineno(1), 'In global let block: %s' % exc)
GenCode(self,
header_output=self.exportContext["header_output"],
decoder_output=self.exportContext["decoder_output"],
except Exception, exc:
if debug:
raise
- error(t,
- 'error: %s in def operand_types block "%s".' % (exc, t[3]))
+ error(t.lineno(1),
+ 'In def operand_types: %s' % exc)
# 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 hasattr(self, 'operandTypeMap'):
- error(t, 'error: operand types must be defined before operands')
+ error(t.lineno(1),
+ 'error: operand types must be defined before operands')
try:
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]))
+ error(t.lineno(1), 'In def operands: %s' % exc)
self.buildOperandNameMap(user_dict, t.lexer.lineno)
# A bitfield definition looks like:
def p_def_bitfield_struct(self, t):
'def_bitfield_struct : DEF opt_signed BITFIELD ID id_with_dot SEMI'
if (t[2] != ''):
- error(t, 'error: structure bitfields are always unsigned.')
+ error(t.lineno(1),
+ '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)
GenCode(self, header_output=hash_define).emit()
def p_decode_stmt_list_1(self, t):
'decode_stmt_list : decode_stmt decode_stmt_list'
if (t[1].has_decode_default and t[2].has_decode_default):
- error(t, 'Two default cases in decode block')
+ error(t.lineno(1), 'Two default cases in decode block')
t[0] = t[1] + t[2]
#
self.formatStack.push(self.formatMap[t[1]])
t[0] = ('', '// format %s' % t[1])
except KeyError:
- error(t, 'instruction format "%s" not defined.' % t[1])
+ error(t.lineno(1), 'instruction format "%s" not defined.' % t[1])
# Nested decode block: if the value of the current field matches
- # the specified constant, do a nested decode on some other field.
+ # the specified constant(s), do a nested decode on some other field.
def p_decode_stmt_decode(self, t):
- 'decode_stmt : case_label COLON decode_block'
- label = t[1]
+ 'decode_stmt : case_list COLON decode_block'
+ case_list = t[1]
codeObj = t[3]
# just wrap the decoding code from the block as a case in the
# outer switch statement.
- codeObj.wrap_decode_block('\n%s:\n' % label)
- codeObj.has_decode_default = (label == 'default')
+ codeObj.wrap_decode_block('\n%s\n' % ''.join(case_list))
+ codeObj.has_decode_default = (case_list == ['default:'])
t[0] = codeObj
# Instruction definition (finally!).
def p_decode_stmt_inst(self, t):
- 'decode_stmt : case_label COLON inst SEMI'
- label = t[1]
+ 'decode_stmt : case_list COLON inst SEMI'
+ case_list = t[1]
codeObj = t[3]
- codeObj.wrap_decode_block('\n%s:' % label, 'break;\n')
- codeObj.has_decode_default = (label == 'default')
+ codeObj.wrap_decode_block('\n%s' % ''.join(case_list), 'break;\n')
+ codeObj.has_decode_default = (case_list == ['default:'])
t[0] = codeObj
- # The case label is either a list of one or more constants or
- # 'default'
- def p_case_label_0(self, t):
- 'case_label : intlit_list'
- def make_case(intlit):
- if intlit >= 2**32:
- return 'case ULL(%#x)' % intlit
- else:
- return 'case %#x' % intlit
- t[0] = ': '.join(map(make_case, t[1]))
+ # The constant list for a decode case label must be non-empty, and must
+ # either be the keyword 'default', or made up of one or more
+ # comma-separated integer literals or strings which evaluate to
+ # constants when compiled as C++.
+ def p_case_list_0(self, t):
+ 'case_list : DEFAULT'
+ t[0] = ['default:']
+
+ def prep_int_lit_case_label(self, lit):
+ if lit >= 2**32:
+ return 'case ULL(%#x): ' % lit
+ else:
+ return 'case %#x: ' % lit
- def p_case_label_1(self, t):
- 'case_label : DEFAULT'
- t[0] = 'default'
+ def prep_str_lit_case_label(self, lit):
+ return 'case %s: ' % lit
- #
- # The constant list for a decode case label must be non-empty, but
- # may have one or more comma-separated integer literals in it.
- #
- def p_intlit_list_0(self, t):
- 'intlit_list : INTLIT'
- t[0] = [t[1]]
+ def p_case_list_1(self, t):
+ 'case_list : INTLIT'
+ t[0] = [self.prep_int_lit_case_label(t[1])]
+
+ def p_case_list_2(self, t):
+ 'case_list : STRLIT'
+ t[0] = [self.prep_str_lit_case_label(t[1])]
+
+ def p_case_list_3(self, t):
+ 'case_list : case_list COMMA INTLIT'
+ t[0] = t[1]
+ t[0].append(self.prep_int_lit_case_label(t[3]))
- def p_intlit_list_1(self, t):
- 'intlit_list : intlit_list COMMA INTLIT'
+ def p_case_list_4(self, t):
+ 'case_list : case_list COMMA STRLIT'
t[0] = t[1]
- t[0].append(t[3])
+ t[0].append(self.prep_str_lit_case_label(t[3]))
# Define an instruction using the current instruction format
# (specified by an enclosing format block).
try:
format = self.formatMap[t[1]]
except KeyError:
- error(t, 'instruction format "%s" not defined.' % t[1])
+ error(t.lineno(1), 'instruction format "%s" not defined.' % t[1])
codeObj = format.defineInst(self, t[3], t[5], t.lexer.lineno)
comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5])
# t.value)
def p_error(self, t):
if t:
- error(t, "syntax error at '%s'" % t.value)
+ error(t.lexer.lineno, "syntax error at '%s'" % t.value)
else:
error("unknown syntax error")
except IOError:
error('Error including file "%s"' % filename)
- self.fileNameStack.push((filename, 0))
+ self.fileNameStack.push(LineTracker(filename))
# Find any includes and include them
def replace(matchobj):
# do this up front.
isa_desc = self.read_and_flatten(isa_desc_file)
- # Initialize filename stack with outer file.
- self.fileNameStack.push((isa_desc_file, 0))
+ # Initialize lineno tracker
+ self.lex.lineno = LineTracker(isa_desc_file)
# Parse.
self.parse_string(isa_desc)
try:
self._parse_isa_desc(*args, **kwargs)
except ISAParserError, e:
- e.exit(self.fileNameStack)
+ print backtrace(self.fileNameStack)
+ print "At %s:" % e.lineno
+ print e
+ sys.exit(1)
# Called as script: get args from command line.
# Args are: <isa desc file> <output dir>