[pypy-svn] r77196 - in pypy/branch/resoperation-refactoring/pypy/jit/metainterp: . optimizeopt test

antocuni at codespeak.net antocuni at codespeak.net
Mon Sep 20 12:21:18 CEST 2010


Author: antocuni
Date: Mon Sep 20 12:21:16 2010
New Revision: 77196

Added:
   pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_resoperation.py   (contents, props changed)
Modified:
   pypy/branch/resoperation-refactoring/pypy/jit/metainterp/optimizeopt/virtualize.py
   pypy/branch/resoperation-refactoring/pypy/jit/metainterp/resoperation.py
   pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/oparser.py
   pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_optimizeopt.py
Log:
complete the refactoring, and create a class for each operation


Modified: pypy/branch/resoperation-refactoring/pypy/jit/metainterp/optimizeopt/virtualize.py
==============================================================================
--- pypy/branch/resoperation-refactoring/pypy/jit/metainterp/optimizeopt/virtualize.py	(original)
+++ pypy/branch/resoperation-refactoring/pypy/jit/metainterp/optimizeopt/virtualize.py	Mon Sep 20 12:21:16 2010
@@ -292,7 +292,7 @@
         for i in range(len(specnodes)):
             value = self.getvalue(op.getarg(i))
             specnodes[i].teardown_virtual_node(self, value, exitargs)
-        op.setarglist(exitargs[:])
+        op = op.copy_and_change(op.getopnum(), args=exitargs[:])
         self.emit_operation(op)
 
     def optimize_VIRTUAL_REF(self, op):

Modified: pypy/branch/resoperation-refactoring/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/branch/resoperation-refactoring/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/branch/resoperation-refactoring/pypy/jit/metainterp/resoperation.py	Mon Sep 20 12:21:16 2010
@@ -2,101 +2,91 @@
 from pypy.rlib.debug import make_sure_not_resized
 
 def ResOperation(opnum, args, result, descr=None):
-    return BaseResOperation(opnum, args, result, descr)
+    cls = opclasses[opnum]
+    op = cls(result)
+    op.initarglist(args)
+    if descr is not None:
+        assert isinstance(op, ResOpWithDescr)
+        op.setdescr(descr)
+    return op
 
-class BaseResOperation(object):
-    """The central ResOperation class, representing one operation."""
 
-    __slots__ = ['_fail_args', '_opnum', '_args', 'result', '_descr',
-                 'name', 'pc', '_exc_box', '__weakref__']
+class AbstractResOp(object):
+    """The central ResOperation class, representing one operation."""
 
     # debug
-    ## name = ""
-    ## pc = 0
+    name = ""
+    pc = 0
 
-    def __init__(self, opnum, args, result, descr=None):
-        make_sure_not_resized(args)
-        assert isinstance(opnum, int)
-        self._opnum = opnum
-        self._args = list(args)
-        make_sure_not_resized(self._args)
-        assert not isinstance(result, list)
+    def __init__(self, result):
         self.result = result
-        self.setdescr(descr)
-        
-        self._fail_args = None
-        self.pc = 0
-        self.name = ''
 
-    ## def __init__(self, result):
-    ##     self.result = result
+    # methods implemented by each concrete class
+    # ------------------------------------------
+    
+    def getopnum(self):
+        raise NotImplementedError
 
-    def copy_and_change(self, opnum, args=None, result=None, descr=None):
-        "shallow copy: the returned operation is meant to be used in place of self"
-        if args is None:
-            args = self.getarglist()
-        if result is None:
-            result = self.result
-        if descr is None:
-            descr = self.getdescr()
-        newop = ResOperation(opnum, args, result, descr)
-        #if isinstance(self, GuardOperation)
-        newop.setfailargs(self.getfailargs())
-        return newop
+    # methods implemented by the arity mixins
+    # ---------------------------------------
 
-    def getopnum(self):
-        return self._opnum
-        #raise NotImplementedError
+    def initarglist(self, args):
+        "This is supposed to be called only just after the ResOp has been created"
+        raise NotImplementedError
+
+    def getarglist(self):
+        raise NotImplementedError
 
     def getarg(self, i):
-        return self._args[i]
-        #raise NotImplementedError
+        raise NotImplementedError
 
     def setarg(self, i, box):
-        self._args[i] = box
-        #raise NotImplementedError
+        raise NotImplementedError
 
     def numargs(self):
-        return len(self._args)
-        #raise NotImplementedError
+        raise NotImplementedError
 
-    def setarglist(self, args):
-        # XXX: is it really needed?
-        self._args = args
-        #raise NotImplementedError
 
-    def getarglist(self):
-        return self._args
-        #raise NotImplementedError
+    # methods implemented by GuardResOp
+    # ---------------------------------
 
     def getfailargs(self):
-        return self._fail_args
-        #raise NotImplementedError
+        return None
 
     def setfailargs(self, fail_args):
-        self._fail_args = fail_args
+        raise NotImplementedError
+
+    # methods implemented by ResOpWithDescr
+    # -------------------------------------
 
     def getdescr(self):
-        return self._descr
+        return None
 
-    def setdescr(self, descr):
-        # for 'call', 'new', 'getfield_gc'...: the descr is a prebuilt
-        # instance provided by the backend holding details about the type
-        # of the operation.  It must inherit from AbstractDescr.  The
-        # backend provides it with cpu.fielddescrof(), cpu.arraydescrof(),
-        # cpu.calldescrof(), and cpu.typedescrof().
-        from pypy.jit.metainterp.history import check_descr
-        check_descr(descr)
-        self._descr = descr
+    def setdescr(self):
+        raise NotImplementedError
+
+    # common methods
+    # --------------
+
+    def copy_and_change(self, opnum, args=None, result=None, descr=None):
+        "shallow copy: the returned operation is meant to be used in place of self"
+        if args is None:
+            args = self.getarglist()
+        if result is None:
+            result = self.result
+        if descr is None:
+            descr = self.getdescr()
+        newop = ResOperation(opnum, args, result, descr)
+        return newop
 
     def clone(self):
-        descr = self._descr
+        args = self.getarglist()
+        descr = self.getdescr()
         if descr is not None:
             descr = descr.clone_if_mutable()
-        op = ResOperation(self.getopnum(), self._args, self.result, descr)
-        op._fail_args = self._fail_args
-        op.name = self.name
+        op = ResOperation(self.getopnum(), args, self.result, descr)
         if not we_are_translated():
+            op.name = self.name
             op.pc = self.pc
         return op
 
@@ -113,12 +103,14 @@
             prefix = "%s:%s   " % (self.name, self.pc)
         else:
             prefix = ""
-        if self.getdescr() is None or we_are_translated():
+        args = self.getarglist()
+        descr = self.getdescr()
+        if descr is None or we_are_translated():
             return '%s%s%s(%s)' % (prefix, sres, self.getopname(),
-                                 ', '.join([str(a) for a in self._args]))
+                                 ', '.join([str(a) for a in args]))
         else:
             return '%s%s%s(%s, descr=%r)' % (prefix, sres, self.getopname(),
-                            ', '.join([str(a) for a in self._args]), self._descr)
+                                             ', '.join([str(a) for a in args]), descr)
 
     def getopname(self):
         try:
@@ -167,30 +159,215 @@
         return opboolresult[opnum]
 
 
+# ===================
+# Top of the hierachy
+# ===================
+
+class PlainResOp(AbstractResOp):
+    pass
+
+class ResOpWithDescr(AbstractResOp):
+
+    _descr = None
+
+    def getdescr(self):
+        return self._descr
+
+    def setdescr(self, descr):
+        # for 'call', 'new', 'getfield_gc'...: the descr is a prebuilt
+        # instance provided by the backend holding details about the type
+        # of the operation.  It must inherit from AbstractDescr.  The
+        # backend provides it with cpu.fielddescrof(), cpu.arraydescrof(),
+        # cpu.calldescrof(), and cpu.typedescrof().
+        from pypy.jit.metainterp.history import check_descr
+        check_descr(descr)
+        self._descr = descr
+
+class GuardResOp(ResOpWithDescr):
+
+    _fail_args = None
+
+    def getfailargs(self):
+        return self._fail_args
+
+    def setfailargs(self, fail_args):
+        self._fail_args = fail_args
+
+    def copy_and_change(self, opnum, args=None, result=None, descr=None):
+        newop = AbstractResOp.copy_and_change(self, opnum, args, result, descr)
+        newop.setfailargs(self.getfailargs())
+        return newop
+
+    def clone(self):
+        newop = AbstractResOp.clone(self)
+        newop.setfailargs(self.getfailargs())
+        return newop
+
+
+# ============
+# arity mixins
+# ============
+
+class NullaryOp(object):
+    _mixin_ = True
+
+    def initarglist(self, args):
+        assert len(args) == 0
+
+    def getarglist(self):
+        return []
+
+    def numargs(self):
+        return 0
+
+    def getarg(self, i):
+        raise IndexError
+    
+    def setarg(self, i, box):
+        raise IndexError
+
+
+class UnaryOp(object):
+    _mixin_ = True
+    _arg0 = None
+
+    def initarglist(self, args):
+        assert len(args) == 1
+        self._arg0, = args
+
+    def getarglist(self):
+        return [self._arg0]
+
+    def numargs(self):
+        return 1
+
+    def getarg(self, i):
+        if i == 0:
+            return self._arg0
+        else:
+            raise IndexError
+    
+    def setarg(self, i, box):
+        if i == 0:
+            self._arg0 = box
+        else:
+            raise IndexError
+
+
+class BinaryOp(object):
+    _mixin_ = True
+    _arg0 = None
+    _arg1 = None
+
+    def initarglist(self, args):
+        assert len(args) == 2
+        self._arg0, self._arg1 = args
+
+    def getarglist(self):
+        return [self._arg0, self._arg1, self._arg2]
+
+    def numargs(self):
+        return 2
+
+    def getarg(self, i):
+        if i == 0:
+            return self._arg0
+        elif i == 1:
+            return self._arg1
+        else:
+            raise IndexError
+    
+    def setarg(self, i, box):
+        if i == 0:
+            self._arg0 = box
+        elif i == 1:
+            self._arg1 = box
+        else:
+            raise IndexError
+
+    def getarglist(self):
+        return [self._arg0, self._arg1]
+
+
+class TernaryOp(object):
+    _mixin_ = True
+    _arg0 = None
+    _arg1 = None
+    _arg2 = None
+
+    def initarglist(self, args):
+        assert len(args) == 3
+        self._arg0, self._arg1, self._arg2 = args
+
+    def getarglist(self):
+        return [self._arg0, self._arg1, self._arg2]
+
+    def numargs(self):
+        return 3
+
+    def getarg(self, i):
+        if i == 0:
+            return self._arg0
+        elif i == 1:
+            return self._arg1
+        elif i == 2:
+            return self._arg2
+        else:
+            raise IndexError
+    
+    def setarg(self, i, box):
+        if i == 0:
+            self._arg0 = box
+        elif i == 1:
+            self._arg1 = box
+        elif i == 2:
+            self._arg2 = box
+        else:
+            raise IndexError
+
+class N_aryOp(object):
+    _mixin_ = True
+    _args = None
+
+    def initarglist(self, args):
+        self._args = args
+
+    def getarglist(self):
+        return self._args
+
+    def numargs(self):
+        return len(self._args)
+
+    def getarg(self, i):
+        return self._args[i]
+    
+    def setarg(self, i, box):
+        self._args[i] = box
+
 
 # ____________________________________________________________
 
 _oplist = [
     '_FINAL_FIRST',
-    'JUMP',
-    'FINISH',
+    'JUMP/*d',
+    'FINISH/*d',
     '_FINAL_LAST',
 
     '_GUARD_FIRST',
     '_GUARD_FOLDABLE_FIRST',
-    'GUARD_TRUE',
-    'GUARD_FALSE',
-    'GUARD_VALUE',
-    'GUARD_CLASS',
-    'GUARD_NONNULL',
-    'GUARD_ISNULL',
-    'GUARD_NONNULL_CLASS',
+    'GUARD_TRUE/1d',
+    'GUARD_FALSE/1d',
+    'GUARD_VALUE/2d',
+    'GUARD_CLASS/2d',
+    'GUARD_NONNULL/1d',
+    'GUARD_ISNULL/1d',
+    'GUARD_NONNULL_CLASS/2d',
     '_GUARD_FOLDABLE_LAST',
-    'GUARD_NO_EXCEPTION',
-    'GUARD_EXCEPTION',
-    'GUARD_NO_OVERFLOW',
-    'GUARD_OVERFLOW',
-    'GUARD_NOT_FORCED',
+    'GUARD_NO_EXCEPTION/0d',
+    'GUARD_EXCEPTION/1d',
+    'GUARD_NO_OVERFLOW/0d',
+    'GUARD_OVERFLOW/0d',
+    'GUARD_NOT_FORCED/0d',
     '_GUARD_LAST', # ----- end of guard operations -----
 
     '_NOSIDEEFFECT_FIRST', # ----- start of no_side_effect operations -----
@@ -279,18 +456,18 @@
     'UNICODESETITEM/3',
     'NEWUNICODE/1',
     #'RUNTIMENEW/1',     # ootype operation
-    'COND_CALL_GC_WB',  # [objptr, newvalue]   (for the write barrier)
+    'COND_CALL_GC_WB/2d', # [objptr, newvalue]   (for the write barrier)
     'DEBUG_MERGE_POINT/1',      # debugging only
     'VIRTUAL_REF_FINISH/2',   # removed before it's passed to the backend
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
-    'CALL',
-    'CALL_ASSEMBLER',
-    'CALL_MAY_FORCE',
-    'CALL_LOOPINVARIANT',
+    'CALL/*d',
+    'CALL_ASSEMBLER/*d',
+    'CALL_MAY_FORCE/*d',
+    'CALL_LOOPINVARIANT/*d',
     #'OOSEND',                     # ootype operation
     #'OOSEND_PURE',                # ootype operation
-    'CALL_PURE',             # removed before it's passed to the backend
+    'CALL_PURE/*d',             # removed before it's passed to the backend
                              # CALL_PURE(result, func, arg_1,..,arg_n)
     '_CANRAISE_LAST', # ----- end of can_raise operations -----
 
@@ -307,6 +484,7 @@
 class rop(object):
     pass
 
+opclasses = []   # mapping numbers to the concrete ResOp class
 opname = {}      # mapping numbers to the original names, for debugging
 oparity = []     # mapping numbers to the arity of the operation or -1
 opwithdescr = [] # mapping numbers to a flag "takes a descr"
@@ -321,16 +499,50 @@
             name, arity = name.split('/')
             withdescr = 'd' in arity
             boolresult = 'b' in arity
-            arity = int(arity.rstrip('db'))
+            arity = arity.rstrip('db')
+            if arity == '*':
+                arity = -1
+            else:
+                arity = int(arity)
         else:
             arity, withdescr, boolresult = -1, True, False       # default
         setattr(rop, name, i)
         if not name.startswith('_'):
             opname[i] = name
+            cls = create_class_for_op(name, i, arity, withdescr)
+        else:
+            cls = None
+        opclasses.append(cls)
         oparity.append(arity)
         opwithdescr.append(withdescr)
         opboolresult.append(boolresult)
-    assert len(oparity)==len(opwithdescr)==len(opboolresult)==len(_oplist)
+    assert len(opclasses)==len(oparity)==len(opwithdescr)==len(opboolresult)==len(_oplist)
+
+def create_class_for_op(name, opnum, arity, withdescr):
+    arity2mixin = {
+        0: NullaryOp,
+        1: UnaryOp,
+        2: BinaryOp,
+        3: TernaryOp
+        }
+    
+    is_guard = name.startswith('GUARD')
+    if is_guard:
+        assert withdescr
+        baseclass = GuardResOp
+    elif withdescr:
+        baseclass = ResOpWithDescr
+    else:
+        baseclass = PlainResOp
+    mixin = arity2mixin.get(arity, N_aryOp)
+
+    def getopnum(self):
+        return opnum
+
+    cls_name = '%s_OP' % name
+    bases = (mixin, baseclass)
+    dic = {'getopnum': getopnum}
+    return type(cls_name, bases, dic)
 
 setup(__name__ == '__main__')   # print out the table when run directly
 del _oplist

Modified: pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/oparser.py
==============================================================================
--- pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/oparser.py	(original)
+++ pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/oparser.py	Mon Sep 20 12:21:16 2010
@@ -6,7 +6,7 @@
 from pypy.jit.metainterp.history import TreeLoop, BoxInt, ConstInt,\
      ConstObj, ConstPtr, Box, BasicFailDescr, BoxFloat, ConstFloat,\
      LoopToken
-from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.resoperation import rop, ResOperation, ResOpWithDescr, N_aryOp
 from pypy.jit.metainterp.typesystem import llhelper
 from pypy.jit.codewriter.heaptracker import adr2int
 from pypy.rpython.lltypesystem import lltype, llmemory
@@ -16,10 +16,22 @@
 class ParseError(Exception):
     pass
 
-
 class Boxes(object):
     pass
 
+class ESCAPE_OP(N_aryOp, ResOpWithDescr):
+
+    OPNUM = -123
+
+    def __init__(self, opnum, args, result, descr=None):
+        assert opnum == self.OPNUM
+        self.result = result
+        self.initarglist(args)
+        self.setdescr(descr)
+
+    def getopnum(self):
+        return self.OPNUM
+
 class ExtendedTreeLoop(TreeLoop):
 
     def getboxes(self):
@@ -171,7 +183,7 @@
             opnum = getattr(rop, opname.upper())
         except AttributeError:
             if opname == 'escape':
-                opnum = -123
+                opnum = ESCAPE_OP.OPNUM
             else:
                 raise ParseError("unknown op: %s" % opname)
         endnum = line.rfind(')')
@@ -228,6 +240,12 @@
                     descr = self.looptoken
         return opnum, args, descr, fail_args
 
+    def create_op(self, opnum, args, result, descr):
+        if opnum == ESCAPE_OP.OPNUM:
+            return ESCAPE_OP(opnum, args, result, descr)
+        else:
+            return ResOperation(opnum, args, result, descr)
+
     def parse_result_op(self, line):
         res, op = line.split("=", 1)
         res = res.strip()
@@ -237,14 +255,16 @@
             raise ParseError("Double assign to var %s in line: %s" % (res, line))
         rvar = self.box_for_var(res)
         self.vars[res] = rvar
-        res = ResOperation(opnum, args, rvar, descr)
-        res.setfailargs(fail_args)
+        res = self.create_op(opnum, args, rvar, descr)
+        if fail_args is not None:
+            res.setfailargs(fail_args)
         return res
 
     def parse_op_no_result(self, line):
         opnum, args, descr, fail_args = self.parse_op(line)
-        res = ResOperation(opnum, args, None, descr)
-        res.setfailargs(fail_args)
+        res = self.create_op(opnum, args, None, descr)
+        if fail_args is not None:
+            res.setfailargs(fail_args)
         return res
 
     def parse_next_op(self, line):

Modified: pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_optimizeopt.py	Mon Sep 20 12:21:16 2010
@@ -42,7 +42,7 @@
     opt = optimizeopt.Optimizer(FakeMetaInterpStaticData(LLtypeMixin.cpu),
                                 None)
     fdescr = ResumeGuardDescr(None, None)
-    op = ResOperation(rop.GUARD_TRUE, [], None, descr=fdescr)
+    op = ResOperation(rop.GUARD_TRUE, ['dummy'], None, descr=fdescr)
     # setup rd data
     fi0 = resume.FrameInfo(None, "code0", 11)
     fdescr.rd_frame_info_list = resume.FrameInfo(fi0, "code1", 33)

Added: pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_resoperation.py
==============================================================================
--- (empty file)
+++ pypy/branch/resoperation-refactoring/pypy/jit/metainterp/test/test_resoperation.py	Mon Sep 20 12:21:16 2010
@@ -0,0 +1,54 @@
+import py
+from pypy.jit.metainterp import resoperation as rop
+from pypy.jit.metainterp.history import AbstractDescr
+
+def test_arity_mixins():
+    cases = [
+        (0, rop.NullaryOp),
+        (1, rop.UnaryOp),
+        (2, rop.BinaryOp),
+        (3, rop.TernaryOp),
+        (9, rop.N_aryOp)
+        ]
+
+    def test_case(n, cls):
+        obj = cls()
+        obj.initarglist(range(n))
+        assert obj.getarglist() == range(n)
+        for i in range(n):
+            obj.setarg(i, i*2)
+        assert obj.numargs() == n
+        for i in range(n):
+            assert obj.getarg(i) == i*2
+        py.test.raises(IndexError, obj.getarg, n+1)
+        py.test.raises(IndexError, obj.setarg, n+1, 0)
+
+    for n, cls in cases:
+        test_case(n, cls)
+
+def test_concrete_classes():
+    cls = rop.opclasses[rop.rop.INT_ADD]
+    assert issubclass(cls, rop.PlainResOp)
+    assert issubclass(cls, rop.BinaryOp)
+    assert cls.getopnum.im_func(None) == rop.rop.INT_ADD
+
+    cls = rop.opclasses[rop.rop.CALL]
+    assert issubclass(cls, rop.ResOpWithDescr)
+    assert issubclass(cls, rop.N_aryOp)
+    assert cls.getopnum.im_func(None) == rop.rop.CALL
+
+    cls = rop.opclasses[rop.rop.GUARD_TRUE]
+    assert issubclass(cls, rop.GuardResOp)
+    assert issubclass(cls, rop.UnaryOp)
+    assert cls.getopnum.im_func(None) == rop.rop.GUARD_TRUE
+
+def test_instantiate():
+    op = rop.ResOperation(rop.rop.INT_ADD, ['a', 'b'], 'c')
+    assert op.getarglist() == ['a', 'b']
+    assert op.result == 'c'
+
+    mydescr = AbstractDescr()
+    op = rop.ResOperation(rop.rop.CALL, ['a', 'b'], 'c', descr=mydescr)
+    assert op.getarglist() == ['a', 'b']
+    assert op.result == 'c'
+    assert op.getdescr() is mydescr



More information about the Pypy-commit mailing list