add test for identifying [expr] * name in parser
[openpower-isa.git] / src / openpower / decoder / pseudo / parser.py
index f7d694f7a4bcf4043c3ca681bcad47302aa08688..1e6a330ffa455be8ad4dd7a437e83a0d0f60ea7c 100644 (file)
@@ -24,11 +24,13 @@ import ast
 # Helper function
 
 regs = ['RA', 'RS', 'RB', 'RC', 'RT']
-fregs = ['FRA', 'FRS', 'FRB', 'FRC', 'FRT']
+fregs = ['FRA', 'FRS', 'FRB', 'FRC', 'FRT', 'FRS']
+SPECIAL_HELPERS = {'concat', 'MEM', 'GPR', 'FPR', 'SPR'}
+
 
 def Assign(autoassign, assignname, left, right, iea_mode):
     names = []
-    print("Assign", assignname, left, right)
+    print("Assign", autoassign, assignname, left, right)
     if isinstance(left, ast.Name):
         # Single assignment on left
         # XXX when doing IntClass, which will have an "eq" function,
@@ -39,16 +41,16 @@ def Assign(autoassign, assignname, left, right, iea_mode):
     elif isinstance(left, ast.Tuple):
         # List of things - make sure they are Name nodes
         names = []
-        for child in left.getChildren():
+        for child in left.elts:
             if not isinstance(child, ast.Name):
                 raise SyntaxError("that assignment not supported")
-            names.append(child.name)
-        ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
-        return ast.Assign([ast.AssTuple(ass_list)], right)
+            names.append(child.id)
+        ass_list = [ast.Name(name, ast.Store()) for name in names]
+        return ast.Assign([ast.Tuple(ass_list)], right)
     elif isinstance(left, ast.Subscript):
         ls = left.slice
         # XXX changing meaning of "undefined" to a function
-        #if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
+        # if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
         #        right.id == 'undefined'):
         #    # undefined needs to be copied the exact same slice
         #    right = ast.Subscript(right, ls, ast.Load())
@@ -62,10 +64,12 @@ def Assign(autoassign, assignname, left, right, iea_mode):
             # the declaration makes the slice-assignment "work"
             lower, upper, step = ls.lower, ls.upper, ls.step
             print("lower, upper, step", repr(lower), repr(upper), step)
-            if not isinstance(lower, ast.Constant) or \
-               not isinstance(upper, ast.Constant):
-                return res
-            qty = ast.Num(upper.value-lower.value)
+            # XXX relax constraint that indices on auto-assign have
+            # to be constants x[0:32]
+            # if not isinstance(lower, ast.Constant) or \
+            #   not isinstance(upper, ast.Constant):
+            #    return res
+            qty = ast.BinOp(upper, binary_ops['-'], lower)
             keywords = [ast.keyword(arg='repeat', value=qty)]
             l = [ast.Num(0)]
             right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
@@ -176,10 +180,23 @@ def check_concat(node):  # checks if the comparison is already a concat
 
 # identify SelectableInt pattern [something] * N
 # must return concat(something, repeat=N)
+# here is a TEMPORARY hack to support minimal expressions
+# such as (XLEN-16), looking for ast.BinOp
+# have to keep an eye on this
 def identify_sint_mul_pattern(p):
+    """here we are looking for patterns of this type:
+        [item] * something
+    these must specifically be returned as concat(item, repeat=something)
+    """
+    #print ("identify_sint_mul_pattern")
+    # for pat in p:
+    #    print("    ", astor.dump_tree(pat))
     if p[2] != '*':  # multiply
         return False
-    if not isinstance(p[3], ast.Constant):  # rhs = Num
+    if (not isinstance(p[3], ast.Constant) and  # rhs = Num
+        not isinstance(p[3], ast.BinOp) and     # rhs = (XLEN-something)
+            (not isinstance(p[3], ast.Name) and  # rhs = {a variable}
+            not isinstance(p[3], ast.Attribute))):   # rhs = XLEN
         return False
     if not isinstance(p[1], ast.List):  # lhs is a list
         return False
@@ -201,6 +218,10 @@ def apply_trailer(atom, trailer, read_regs):
                 name = arg.id
                 if name in regs + fregs:
                     read_regs.add(name)
+        # special-case, these functions must NOT be made "self.xxxx"
+        if atom.id not in SPECIAL_HELPERS:
+            name = ast.Name("self", ast.Load())
+            atom = ast.Attribute(name, atom, ast.Load())
         return ast.Call(atom, trailer[1], [])
         # if p[1].id == 'print':
         #    p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
@@ -215,7 +236,7 @@ def apply_trailer(atom, trailer, read_regs):
             if isinstance(idx, ast.Name) and idx.id in regs + fregs:
                 read_regs.add(idx.id)
             if isinstance(idx, ast.Name) and idx.id in regs:
-                print ("single atom subscript, underscored", idx.id)
+                print("single atom subscript, underscored", idx.id)
                 idx = ast.Name("_%s" % idx.id, ast.Load())
         else:
             idx = ast.Slice(subs[0], subs[1], None)
@@ -268,13 +289,16 @@ class PowerParser:
         ("left", "INVERT"),
     )
 
-    def __init__(self, form, include_carry_in_write=False):
-        self.include_ca_in_write = include_carry_in_write
+    def reset(self):
         self.gprs = {}
-        form = self.sd.sigforms[form]
-        print(form)
-        formkeys = form._asdict().keys()
+        if self.form is not None:
+            form = self.sd.sigforms[self.form]
+            print(form)
+            formkeys = form._asdict().keys()
+        else:
+            formkeys = []
         self.declared_vars = set()
+        self.fnparm_vars = set()
         for rname in regs + fregs:
             self.gprs[rname] = None
             self.declared_vars.add(rname)
@@ -290,6 +314,12 @@ class PowerParser:
         self.write_regs = OrderedSet()
         self.special_regs = OrderedSet()  # see p_atom_name
 
+    def __init__(self, form, include_carry_in_write=False, helper=False):
+        self.include_ca_in_write = include_carry_in_write
+        self.helper = helper
+        self.form = form
+        self.reset()
+
     # The grammar comments come from Python's Grammar/Grammar file
 
     # NB: compound_stmt in single_input is followed by extra NEWLINE!
@@ -321,17 +351,27 @@ class PowerParser:
 
     def p_funcdef(self, p):
         "funcdef : DEF NAME parameters COLON suite"
-        p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
+        p[0] = ast.FunctionDef(p[2], p[3], p[5], (), lineno=p.lineno(2))
+        # reset function parameters after suite is identified
+        self.fnparm_vars = set()
 
     # parameters: '(' [varargslist] ')'
     def p_parameters(self, p):
         """parameters : LPAR RPAR
                       | LPAR varargslist RPAR"""
-        if len(p) == 3:
-            args = []
-        else:
-            args = p[2]
+        args = []
+        if self.helper:
+            args.append("self")
+        if len(p) != 3:
+            args += p[2]
         p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
+        # during the time between parameters identified and suite is not
+        # there is a window of opportunity to declare the function parameters
+        # in-scope, for use to not overwrite them with auto-assign
+        self.fnparm_vars = set()
+        for arg in args:
+            print("adding fn parm", arg)
+            self.fnparm_vars.add(arg)
 
     # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
     # '**' NAME) |
@@ -341,7 +381,8 @@ class PowerParser:
         """varargslist : varargslist COMMA NAME
                        | NAME"""
         if len(p) == 4:
-            p[0] = p[1] + p[3]
+            print(p[1], p[3])
+            p[0] = p[1] + [p[3]]
         else:
             p[0] = [p[1]]
 
@@ -379,10 +420,10 @@ class PowerParser:
                       | expr_stmt"""
         if isinstance(p[1], ast.Call):
             p[0] = ast.Expr(p[1])
-        elif isinstance(p[1], ast.Name) and p[1].id == 'TRAP':
-            # TRAP needs to actually be a function
+        elif isinstance(p[1], ast.Name) and p[1].id not in SPECIAL_HELPERS:
+            fname = p[1].id
             name = ast.Name("self", ast.Load())
-            name = ast.Attribute(name, "TRAP", ast.Load())
+            name = ast.Attribute(name, fname, ast.Load())
             p[0] = ast.Call(name, [], [])
         else:
             p[0] = p[1]
@@ -407,21 +448,34 @@ class PowerParser:
             if isinstance(p[1], ast.Name):
                 name = p[1].id
             elif isinstance(p[1], ast.Subscript):
+                print("assign subscript", p[1].value,
+                      self.declared_vars,
+                      self.fnparm_vars,
+                      self.special_regs)
+                print(astor.dump_tree(p[1]))
                 if isinstance(p[1].value, ast.Name):
                     name = p[1].value.id
+                    print("assign subscript value to name", name)
                     if name in self.gprs:
                         # add to list of uninitialised
                         self.uninit_regs.add(name)
+                    # work out if this is an ininitialised variable
+                    # that should be auto-assigned simply by being "sliced"
                     autoassign = (name not in self.declared_vars and
+                                  name not in self.fnparm_vars and
                                   name not in self.special_regs)
             elif isinstance(p[1], ast.Call) and p[1].func.id in \
-                            ['GPR', 'FPR', 'SPR']:
+                    ['GPR', 'FPR', 'SPR']:
                 print(astor.dump_tree(p[1]))
                 # replace GPR(x) with GPR[x]
                 idx = p[1].args[0].id
-                ridx = ast.Name("_%s" % idx, ast.Load())
+                if idx in regs + fregs:
+                    ridx = ast.Name("_%s" % idx, ast.Load())
+                else:
+                    ridx = ast.Name(idx, ast.Load())
                 p[1] = ast.Subscript(p[1].func, ridx, ast.Load())
-                self.read_regs.add(idx)  # add to list of regs to read
+                if idx in self.gprs:
+                    self.read_regs.add(idx)  # add to list of regs to read
             elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
                 print("mem assign")
                 print(astor.dump_tree(p[1]))
@@ -469,20 +523,12 @@ class PowerParser:
         p[0] = ast.Break()
 
     def p_for_stmt(self, p):
-        """for_stmt : FOR atom EQ test TO test COLON suite
-                    | DO atom EQ test TO test COLON suite
+        """for_stmt : FOR atom EQ comparison TO comparison COLON suite
+                    | DO atom EQ comparison TO comparison COLON suite
         """
         start = p[4]
         end = p[6]
-        if start.value > end.value:  # start greater than end, must go -ve
-            # auto-subtract-one (sigh) due to python range
-            end = ast.BinOp(p[6], ast.Add(), ast.Constant(-1))
-            arange = [start, end, ast.Constant(-1)]
-        else:
-            # auto-add-one (sigh) due to python range
-            end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
-            arange = [start, end]
-        it = ast.Call(ast.Name("range", ast.Load()), arange, [])
+        it = ast.Call(ast.Name("RANGE", ast.Load()), (start, end), [])
         p[0] = ast.For(p[2], it, p[8], [])
 
     def p_while_stmt(self, p):
@@ -627,7 +673,7 @@ class PowerParser:
                       | comparison BITXOR comparison
                       | comparison BITAND comparison
                       | PLUS comparison
-                      | comparison MINUS
+                      | MINUS comparison
                       | INVERT comparison
                       | comparison APPEND comparison
                       | power"""
@@ -666,6 +712,11 @@ class PowerParser:
                 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
             else:
                 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
+                # HORRENDOUS hack, add brackets around the bin-op by
+                # creating a function call with a *blank* function name!
+                # XXX argh doesn't work because of analysis of
+                # identify_sint_pattern
+                #p[0] = ast.Call(ast.Name("", ast.Load()), [p[0]], [])
         elif len(p) == 3:
             if isinstance(p[2], str) and p[2] == '-':
                 p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
@@ -700,15 +751,21 @@ class PowerParser:
         name = p[1]
         if name in self.available_op_fields:
             self.op_fields.add(name)
-        if name == 'overflow':
+        if name in ['overflow', 'CR0']:
             self.write_regs.add(name)
         if self.include_ca_in_write:
             if name in ['CA', 'CA32']:
                 self.write_regs.add(name)
-        if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR', 'SVSTATE']:
+        if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR',
+                    'SVSTATE', 'SVREMAP',
+                    'SVSHAPE0', 'SVSHAPE1', 'SVSHAPE2', 'SVSHAPE3']:
             self.special_regs.add(name)
             self.write_regs.add(name)  # and add to list to write
-        p[0] = ast.Name(id=name, ctx=ast.Load())
+        if name in {'XLEN'}:
+            attr = ast.Name("self", ast.Load())
+            p[0] = ast.Attribute(attr, name, ast.Load(), lineno=p.lineno(1))
+        else:
+            p[0] = ast.Name(id=name, ctx=ast.Load(), lineno=p.lineno(1))
 
     def p_atom_number(self, p):
         """atom : BINARY
@@ -869,21 +926,36 @@ class PowerParser:
         raise SyntaxError(p)
 
 
+_CACHE_DECODER = True
+_CACHED_DECODER = None
+
+
+def _create_cached_decoder():
+    global _CACHED_DECODER
+    if _CACHE_DECODER:
+        if _CACHED_DECODER is None:
+            _CACHED_DECODER = create_pdecode()
+        return _CACHED_DECODER
+    return create_pdecode()
+
+
 class GardenSnakeParser(PowerParser):
-    def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
-        self.sd = create_pdecode()
-        PowerParser.__init__(self, form, incl_carry)
+    def __init__(self, debug=False, form=None, incl_carry=False, helper=False):
+        if form is not None:
+            self.sd = _create_cached_decoder()
+        PowerParser.__init__(self, form, incl_carry, helper=helper)
         self.debug = debug
-        if lexer is None:
-            lexer = IndentLexer(debug=0)
-        self.lexer = lexer
-        self.tokens = lexer.tokens
+        self.lexer = IndentLexer(debug=0)
+        self.tokens = self.lexer.tokens
         self.parser = yacc.yacc(module=self, start="file_input_end",
                                 debug=debug, write_tables=False)
 
     def parse(self, code):
-        # self.lexer.input(code)
+        self.reset()
         result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
+        if self.helper:
+            result = [ast.ClassDef("ISACallerFnHelper", [
+                                   "ISACallerHelper"], [], result, decorator_list=[])]
         return ast.Module(result)
 
 
@@ -896,19 +968,18 @@ _CACHE_PARSERS = True
 
 
 class GardenSnakeCompiler(object):
-    def __init__(self, debug=False, form=None, incl_carry=False):
+    def __init__(self, debug=False, form=None, incl_carry=False, helper=False):
         if _CACHE_PARSERS:
             try:
-                parser = _CACHED_PARSERS[debug, form, incl_carry]
+                self.parser = _CACHED_PARSERS[debug, form, incl_carry, helper]
             except KeyError:
-                parser = GardenSnakeParser(debug=debug, form=form,
-                                           incl_carry=incl_carry)
-                _CACHED_PARSERS[debug, form, incl_carry] = parser
-
-            self.parser = deepcopy(parser)
+                self.parser = GardenSnakeParser(
+                    debug=debug, form=form, incl_carry=incl_carry,
+                    helper=helper)
+                _CACHED_PARSERS[debug, form, incl_carry, helper] = self.parser
         else:
             self.parser = GardenSnakeParser(debug=debug, form=form,
-                                            incl_carry=incl_carry)
+                                            incl_carry=incl_carry, helper=helper)
 
     def compile(self, code, mode="exec", filename="<string>"):
         tree = self.parser.parse(code)