move FPDIV, FPMUL (etc) to ISAFPHelpers class
[openpower-isa.git] / src / openpower / decoder / pseudo / parser.py
index dce891212626689be388a09993460dc3306650b6..b2215d38aa8e9abf81f9dd778c317a5be3f37140 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,
@@ -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,22 @@ 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)):        # rhs = XLEN
         return False
     if not isinstance(p[1], ast.List):  # lhs is a list
         return False
@@ -189,13 +205,22 @@ def identify_sint_mul_pattern(p):
     return True  # yippee!
 
 
-def apply_trailer(atom, trailer):
+def apply_trailer(atom, trailer, read_regs):
     if trailer[0] == "TLIST":
         # assume depth of one
-        atom = apply_trailer(atom, trailer[1])
+        atom = apply_trailer(atom, trailer[1], read_regs)
         trailer = trailer[2]
     if trailer[0] == "CALL":
         #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
+        for arg in trailer[1]:
+            if isinstance(arg, ast.Name):
+                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)
@@ -207,6 +232,11 @@ def apply_trailer(atom, trailer):
         subs = trailer[1]
         if len(subs) == 1:
             idx = subs[0]
+            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)
+                idx = ast.Name("_%s" % idx.id, ast.Load())
         else:
             idx = ast.Slice(subs[0], subs[1], None)
         # if isinstance(atom, ast.Name) and atom.id == 'CR':
@@ -258,13 +288,18 @@ class PowerParser:
         ("left", "INVERT"),
     )
 
-    def __init__(self, form, include_carry_in_write=False):
+    def __init__(self, form, include_carry_in_write=False, helper=False):
         self.include_ca_in_write = include_carry_in_write
+        self.helper = helper
         self.gprs = {}
-        form = self.sd.sigforms[form]
-        print(form)
-        formkeys = form._asdict().keys()
+        if form is not None:
+            form = self.sd.sigforms[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)
@@ -312,16 +347,26 @@ class PowerParser:
     def p_funcdef(self, p):
         "funcdef : DEF NAME parameters COLON suite"
         p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
+        # 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) |
@@ -331,7 +376,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]]
 
@@ -369,10 +415,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]
@@ -397,18 +443,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', 'SPR']:
+            elif isinstance(p[1], ast.Call) and p[1].func.id in \
+                            ['GPR', 'FPR', 'SPR']:
                 print(astor.dump_tree(p[1]))
                 # replace GPR(x) with GPR[x]
-                idx = p[1].args[0]
-                p[1] = ast.Subscript(p[1].func, idx, ast.Load())
+                idx = p[1].args[0].id
+                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())
+                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]))
@@ -421,7 +483,11 @@ class PowerParser:
             else:
                 print("help, help")
                 print(astor.dump_tree(p[1]))
-            print("expr assign", name, p[1])
+            print("expr assign", name, p[1], "to", p[3])
+            if isinstance(p[3], ast.Name):
+                toname = p[3].id
+                if toname in self.gprs:
+                    self.read_regs.add(toname)
             if name and name in self.gprs:
                 self.write_regs.add(name)  # add to list of regs to write
             p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
@@ -452,20 +518,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):
@@ -610,7 +668,7 @@ class PowerParser:
                       | comparison BITXOR comparison
                       | comparison BITAND comparison
                       | PLUS comparison
-                      | comparison MINUS
+                      | MINUS comparison
                       | INVERT comparison
                       | comparison APPEND comparison
                       | power"""
@@ -672,7 +730,7 @@ class PowerParser:
             print(astor.dump_tree(p[1]))
             print("power dump trailerlist")
             print(astor.dump_tree(p[2]))
-            p[0] = apply_trailer(p[1], p[2])
+            p[0] = apply_trailer(p[1], p[2], self.read_regs)
             if isinstance(p[1], ast.Name):
                 name = p[1].id
                 if name in regs + fregs:
@@ -688,7 +746,9 @@ class PowerParser:
         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())
@@ -736,8 +796,11 @@ class PowerParser:
                     p[2].left.id in self.gprs:
                 rid = p[2].left.id
                 self.read_regs.add(rid)  # add to list of regs to read
-                # create special call to GPR.getz
-                gprz = ast.Name("GPR", ast.Load())
+                # create special call to GPR.getz or FPR.getz
+                if rid in fregs:
+                    gprz = ast.Name("FPR", ast.Load())
+                else:
+                    gprz = ast.Name("GPR", ast.Load())
                 # get testzero function
                 gprz = ast.Attribute(gprz, "getz", ast.Load())
                 # *sigh* see class GPR.  we need index itself not reg value
@@ -850,9 +913,10 @@ class PowerParser:
 
 
 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, lexer=None, debug=False, form=None, incl_carry=False, helper=False):
+        if form is not None:
+            self.sd = create_pdecode()
+        PowerParser.__init__(self, form, incl_carry, helper=helper)
         self.debug = debug
         if lexer is None:
             lexer = IndentLexer(debug=0)
@@ -864,6 +928,8 @@ class GardenSnakeParser(PowerParser):
     def parse(self, code):
         # self.lexer.input(code)
         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)
 
 
@@ -876,19 +942,19 @@ _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]
+                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
+                                           incl_carry=incl_carry, helper=helper)
+                _CACHED_PARSERS[debug, form, incl_carry, helper] = parser
 
             self.parser = deepcopy(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)