`deepcopy` from cache instead of recreating parsers for `GardenSnakeCompiler`
[soc.git] / src / soc / decoder / pseudo / parser.py
index 9b3e8f82d89dbf237b4a07ec5fe75a4491f4537b..1466f87cf1f63a0526e91910f93534e46716675e 100644 (file)
@@ -11,6 +11,7 @@
 from pprint import pprint
 from ply import lex, yacc
 import astor
+from copy import deepcopy
 
 from soc.decoder.power_decoder import create_pdecode
 from soc.decoder.pseudo.lexer import IndentLexer
@@ -23,9 +24,9 @@ import ast
 # Helper function
 
 
-def Assign(autoassign, left, right, iea_mode):
+def Assign(autoassign, assignname, left, right, iea_mode):
     names = []
-    print("Assign", left, right)
+    print("Assign", assignname, left, right)
     if isinstance(left, ast.Name):
         # Single assignment on left
         # XXX when doing IntClass, which will have an "eq" function,
@@ -43,18 +44,29 @@ def Assign(autoassign, left, right, iea_mode):
         ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
         return ast.Assign([ast.AssTuple(ass_list)], right)
     elif isinstance(left, ast.Subscript):
-        res = ast.Assign([left], right)
         ls = left.slice
+        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())
+            return ast.Assign([left], right)
+        res = ast.Assign([left], right)
         if autoassign and isinstance(ls, ast.Slice):
-            # hack to create a variable pre-declared
+            # hack to create a variable pre-declared based on a slice.
+            # dividend[0:32] = (RA)[0:32] will create
+            #       dividend = [0] * 32
+            #       dividend[0:32] = (RA)[0:32]
+            # 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)
+            print("lower, upper, step", repr(lower), repr(upper), step)
             if not isinstance(lower, ast.Constant) or \
                not isinstance(upper, ast.Constant):
                 return res
-            right = ast.BinOp(ast.List([ast.Num(0)]),
-                    ast.Mult(), ast.Num(upper.value-lower.value))
-            declare = ast.Assign([left], right)
+            qty = ast.Num(upper.value-lower.value)
+            keywords = [ast.keyword(arg='repeat', value=qty)]
+            l = [ast.Num(0)]
+            right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
+            declare = ast.Assign([ast.Name(assignname, ast.Store())], right)
             return [declare, res]
         return res
         # XXX HMMM probably not needed...
@@ -194,6 +206,9 @@ def apply_trailer(atom, trailer):
             idx = subs[0]
         else:
             idx = ast.Slice(subs[0], subs[1], None)
+        # if isinstance(atom, ast.Name) and atom.id == 'CR':
+            # atom.id = 'CR' # bad hack
+            #print ("apply_trailer Subscript", atom.id, idx)
         return ast.Subscript(atom, idx, ast.Load())
 
 ##########   Parser (tokens -> AST) ######
@@ -240,7 +255,8 @@ class PowerParser:
         ("left", "INVERT"),
     )
 
-    def __init__(self, form):
+    def __init__(self, form, include_carry_in_write=False):
+        self.include_ca_in_write = include_carry_in_write
         self.gprs = {}
         form = self.sd.sigforms[form]
         print(form)
@@ -248,6 +264,7 @@ class PowerParser:
         self.declared_vars = set()
         for rname in ['RA', 'RB', 'RC', 'RT', 'RS']:
             self.gprs[rname] = None
+            self.declared_vars.add(rname)
         self.available_op_fields = set()
         for k in formkeys:
             if k not in self.gprs:
@@ -258,7 +275,7 @@ class PowerParser:
         self.read_regs = OrderedSet()
         self.uninit_regs = OrderedSet()
         self.write_regs = OrderedSet()
-        self.special_regs = OrderedSet() # see p_atom_name
+        self.special_regs = OrderedSet()  # see p_atom_name
 
     # The grammar comments come from Python's Grammar/Grammar file
 
@@ -382,7 +399,8 @@ class PowerParser:
                     if name in self.gprs:
                         # add to list of uninitialised
                         self.uninit_regs.add(name)
-                    autoassign = name not in self.declared_vars
+                    autoassign = (name not in self.declared_vars and
+                                  name not in self.special_regs)
             elif isinstance(p[1], ast.Call) and p[1].func.id in ['GPR', 'SPR']:
                 print(astor.dump_tree(p[1]))
                 # replace GPR(x) with GPR[x]
@@ -403,7 +421,7 @@ class PowerParser:
             print("expr assign", name, p[1])
             if name and name in self.gprs:
                 self.write_regs.add(name)  # add to list of regs to write
-            p[0] = Assign(autoassign, p[1], p[3], iea_mode)
+            p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
             if name:
                 self.declared_vars.add(name)
 
@@ -434,10 +452,17 @@ class PowerParser:
         """for_stmt : FOR atom EQ test TO test COLON suite
                     | DO atom EQ test TO test COLON suite
         """
-        # auto-add-one (sigh) due to python range
         start = p[4]
-        end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
-        it = ast.Call(ast.Name("range", ast.Load()), [start, end], [])
+        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, [])
         p[0] = ast.For(p[2], it, p[8], [])
 
     def p_while_stmt(self, p):
@@ -595,6 +620,24 @@ class PowerParser:
             elif p[2] == '||':
                 l = check_concat(p[1]) + check_concat(p[3])
                 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
+            elif p[2] in ['/', '%']:
+                # bad hack: if % or / used anywhere other than div/mod ops,
+                # do % or /.  however if the argument names are "dividend"
+                # we must call the special trunc_divs and trunc_rems functions
+                l, r = p[1], p[3]
+                # actual call will be "dividend / divisor" - just check
+                # LHS name
+                # XXX DISABLE BAD HACK (False)
+                if False and isinstance(l, ast.Name) and l.id == 'dividend':
+                    if p[2] == '/':
+                        fn = 'trunc_divs'
+                    else:
+                        fn = 'trunc_rems'
+                    # return "function trunc_xxx(l, r)"
+                    p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
+                else:
+                    # return "l {binop} r"
+                    p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
             elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
                 p[0] = binary_ops[p[2]]((p[1], p[3]))
             elif identify_sint_mul_pattern(p):
@@ -625,17 +668,24 @@ class PowerParser:
             print("power dump trailerlist")
             print(astor.dump_tree(p[2]))
             p[0] = apply_trailer(p[1], p[2])
+            if isinstance(p[1], ast.Name):
+                name = p[1].id
+                if name in ['RA', 'RS', 'RB', 'RC']:
+                    self.read_regs.add(name)
 
     def p_atom_name(self, p):
         """atom : NAME"""
         name = p[1]
         if name in self.available_op_fields:
             self.op_fields.add(name)
-        if name in ['CA', 'CA32']:
+        if name == 'overflow':
             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']:
             self.special_regs.add(name)
-            self.write_regs.add(name) # and add to list to write
+            self.write_regs.add(name)  # and add to list to write
         p[0] = ast.Name(id=name, ctx=ast.Load())
 
     def p_atom_number(self, p):
@@ -795,9 +845,9 @@ class PowerParser:
 
 
 class GardenSnakeParser(PowerParser):
-    def __init__(self, lexer=None, debug=False, form=None):
+    def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
         self.sd = create_pdecode()
-        PowerParser.__init__(self, form)
+        PowerParser.__init__(self, form, incl_carry)
         self.debug = debug
         if lexer is None:
             lexer = IndentLexer(debug=0)
@@ -816,9 +866,24 @@ class GardenSnakeParser(PowerParser):
 
 #from compiler import misc, syntax, pycodegen
 
+_CACHED_PARSERS = {}
+_CACHE_PARSERS = True
+
+
 class GardenSnakeCompiler(object):
-    def __init__(self, debug=False, form=None):
-        self.parser = GardenSnakeParser(debug=debug, form=form)
+    def __init__(self, debug=False, form=None, incl_carry=False):
+        if _CACHE_PARSERS:
+            try:
+                parser = _CACHED_PARSERS[debug, form, incl_carry]
+            except KeyError:
+                parser = GardenSnakeParser(debug=debug, form=form,
+                                           incl_carry=incl_carry)
+                _CACHED_PARSERS[debug, form, incl_carry] = parser
+
+            self.parser = deepcopy(parser)
+        else:
+            self.parser = GardenSnakeParser(debug=debug, form=form,
+                                            incl_carry=incl_carry)
 
     def compile(self, code, mode="exec", filename="<string>"):
         tree = self.parser.parse(code)