[pypy-commit] pypy optresult-unroll: * reimplement structs that are immutable constants, built during traces

arigo noreply at buildbot.pypy.org
Tue Aug 25 13:23:12 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: optresult-unroll
Changeset: r79224:13fed87e2df5
Date: 2015-08-25 13:23 +0200
http://bitbucket.org/pypy/pypy/changeset/13fed87e2df5/

Log:	* reimplement structs that are immutable constants, built during
	traces

	* in llsupport/descr.py, merge SizeDescrWithVTable with SizeDescr

diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -111,8 +111,8 @@
                                                                self.S)
         return heaptracker.adr2int(llmemory.cast_ptr_to_adr(self._vtable))
 
-    def count_fields_if_immutable(self):
-        return heaptracker.count_fields_if_immutable(self.S)
+    def is_immutable(self):
+        return heaptracker.is_immutable_struct(self.S)
 
     def __repr__(self):
         return 'SizeDescr(%r)' % (self.S,)
diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -36,37 +36,32 @@
     size = 0      # help translation
     tid = llop.combine_ushort(lltype.Signed, 0, 0)
     vtable = lltype.nullptr(rclass.OBJECT_VTABLE)
+    immutable_flag = False
 
-    def __init__(self, size, count_fields_if_immut=-1,
-                 gc_fielddescrs=None, all_fielddescrs=None,
-                 vtable=lltype.nullptr(rclass.OBJECT_VTABLE)):
+    def __init__(self, size, gc_fielddescrs=None, all_fielddescrs=None,
+                 vtable=lltype.nullptr(rclass.OBJECT_VTABLE),
+                 immutable_flag=False):
         self.size = size
-        self.count_fields_if_immut = count_fields_if_immut
         self.gc_fielddescrs = gc_fielddescrs
         self.all_fielddescrs = all_fielddescrs
         self.vtable = vtable
+        self.immutable_flag = immutable_flag
 
     def get_all_fielddescrs(self):
         return self.all_fielddescrs
 
-    def count_fields_if_immutable(self):
-        return self.count_fields_if_immut
-
     def repr_of_descr(self):
         return '<SizeDescr %s>' % self.size
 
     def is_object(self):
-        return False
+        return bool(self.vtable)
 
-class SizeDescrWithVTable(SizeDescr):
-    def is_object(self):
-        return True
+    def is_immutable(self):
+        return self.immutable_flag
 
     def get_vtable(self):
         return heaptracker.adr2int(llmemory.cast_ptr_to_adr(self.vtable))
 
-BaseSizeDescr = SizeDescr
-
 def get_size_descr(gccache, STRUCT, vtable):
     cache = gccache._cache_size
     assert not isinstance(vtable, bool)
@@ -74,16 +69,14 @@
         return cache[STRUCT]
     except KeyError:
         size = symbolic.get_size(STRUCT, gccache.translate_support_code)
-        count_fields_if_immut = heaptracker.count_fields_if_immutable(STRUCT)
+        immutable_flag = heaptracker.is_immutable_struct(STRUCT)
         gc_fielddescrs = heaptracker.gc_fielddescrs(gccache, STRUCT)
         if vtable:
             assert heaptracker.has_gcstruct_a_vtable(STRUCT)
-            sizedescr = SizeDescrWithVTable(size, count_fields_if_immut,
-                                            gc_fielddescrs, None, vtable)
         else:
             assert not heaptracker.has_gcstruct_a_vtable(STRUCT)
-            sizedescr = SizeDescr(size, count_fields_if_immut,
-                                  gc_fielddescrs, None)
+        sizedescr = SizeDescr(size, gc_fielddescrs, vtable=vtable,
+                              immutable_flag=immutable_flag)
         gccache.init_size_descr(STRUCT, sizedescr)
         cache[STRUCT] = sizedescr
         all_fielddescrs = heaptracker.all_fielddescrs(gccache, STRUCT)
@@ -124,7 +117,7 @@
         return 'FieldDescr<%s>' % (self.name,)
 
     def check_correct_type(self, struct):
-        if isinstance(self.parent_descr, SizeDescrWithVTable):
+        if self.parent_descr.is_object():
             cls = llmemory.cast_adr_to_ptr(
                 heaptracker.int2adr(self.parent_descr.get_vtable()),
                 lltype.Ptr(rclass.OBJECT_VTABLE))
diff --git a/rpython/jit/backend/llsupport/test/test_descr.py b/rpython/jit/backend/llsupport/test/test_descr.py
--- a/rpython/jit/backend/llsupport/test/test_descr.py
+++ b/rpython/jit/backend/llsupport/test/test_descr.py
@@ -13,24 +13,24 @@
     T = lltype.GcStruct('T')
     S = lltype.GcStruct('S', ('x', lltype.Char),
                              ('y', lltype.Ptr(T)))
-    descr_s = get_size_descr(c0, S, False)
-    descr_t = get_size_descr(c0, T, False)
+    descr_s = get_size_descr(c0, S, None)
+    descr_t = get_size_descr(c0, T, None)
     assert descr_s.size == symbolic.get_size(S, False)
     assert descr_t.size == symbolic.get_size(T, False)
-    assert descr_s.count_fields_if_immutable() == -1
-    assert descr_t.count_fields_if_immutable() == -1
+    assert descr_s.is_immutable() == False
+    assert descr_t.is_immutable() == False
     assert descr_t.gc_fielddescrs == []
     assert len(descr_s.gc_fielddescrs) == 1
-    assert descr_s == get_size_descr(c0, S, False)
-    assert descr_s != get_size_descr(c1, S, False)
+    assert descr_s == get_size_descr(c0, S, None)
+    assert descr_s != get_size_descr(c1, S, None)
     #
-    descr_s = get_size_descr(c1, S, False)
+    descr_s = get_size_descr(c1, S, None)
     assert isinstance(descr_s.size, Symbolic)
-    assert descr_s.count_fields_if_immutable() == -1
+    assert descr_s.is_immutable() == False
 
     PARENT = lltype.Struct('P', ('x', lltype.Ptr(T)))
     STRUCT = lltype.GcStruct('S', ('parent', PARENT), ('y', lltype.Ptr(T)))
-    descr_struct = get_size_descr(c0, STRUCT, False)
+    descr_struct = get_size_descr(c0, STRUCT, None)
     assert len(descr_struct.gc_fielddescrs) == 2
 
 def test_get_size_descr_immut():
@@ -46,11 +46,11 @@
                         ('miss1', lltype.Void),
                         ('miss2', lltype.Void),
                         hints={'immutable': True})
-    for STRUCT, expected in [(S, 0), (T, 1), (U, 3), (V, 3)]:
+    for STRUCT in [S, T, U, V]:
         for translated in [False, True]:
             c0 = GcCache(translated)
-            descr_s = get_size_descr(c0, STRUCT, False)
-            assert descr_s.count_fields_if_immutable() == expected
+            descr_s = get_size_descr(c0, STRUCT, None)
+            assert descr_s.is_immutable() == True
 
 def test_get_field_descr():
     U = lltype.Struct('U')
@@ -329,7 +329,7 @@
     S = lltype.GcStruct('S', ('x', lltype.Char),
                              ('y', lltype.Ptr(T)),
                              ('z', lltype.Ptr(T)))
-    descr1 = get_size_descr(c0, S, False)
+    descr1 = get_size_descr(c0, S, None)
     s = symbolic.get_size(S, False)
     assert repr_of_descr(descr1) == '<SizeDescr %d>' % s
     #
diff --git a/rpython/jit/backend/llsupport/test/test_pinned_object_rewrite.py b/rpython/jit/backend/llsupport/test/test_pinned_object_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_pinned_object_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_pinned_object_rewrite.py
@@ -1,7 +1,7 @@
 from test_rewrite import get_size_descr, get_array_descr, get_description, BaseFakeCPU
 from rpython.jit.backend.llsupport.descr import get_size_descr,\
      get_field_descr, get_array_descr, ArrayDescr, FieldDescr,\
-     SizeDescrWithVTable, get_interiorfield_descr
+     SizeDescr, get_interiorfield_descr
 from rpython.jit.backend.llsupport.gc import GcLLDescr_boehm,\
      GcLLDescr_framework, MovableObjectTracker
 from rpython.jit.backend.llsupport import jitframe, gc
@@ -115,7 +115,7 @@
         #
         class FakeCPU(BaseFakeCPU):
             def sizeof(self, STRUCT, is_object):
-                descr = SizeDescrWithVTable(104)
+                descr = SizeDescr(104)
                 descr.tid = 9315
                 descr.vtable = 12
                 return descr
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -1,6 +1,6 @@
 from rpython.jit.backend.llsupport.descr import get_size_descr,\
      get_field_descr, get_array_descr, ArrayDescr, FieldDescr,\
-     SizeDescrWithVTable, get_interiorfield_descr
+     SizeDescr, get_interiorfield_descr
 from rpython.jit.backend.llsupport.gc import GcLLDescr_boehm,\
      GcLLDescr_framework
 from rpython.jit.backend.llsupport import jitframe
@@ -163,8 +163,8 @@
         class FakeCPU(BaseFakeCPU):
             def sizeof(self, STRUCT, is_object):
                 assert is_object
-                return SizeDescrWithVTable(102, gc_fielddescrs=[],
-                                           vtable=o_vtable)
+                return SizeDescr(102, gc_fielddescrs=[],
+                                 vtable=o_vtable)
         self.cpu = FakeCPU()
         self.gc_ll_descr = GcLLDescr_boehm(None, None, None)
 
@@ -301,7 +301,7 @@
         #
         class FakeCPU(BaseFakeCPU):
             def sizeof(self, STRUCT, is_object):
-                descr = SizeDescrWithVTable(104, gc_fielddescrs=[])
+                descr = SizeDescr(104, gc_fielddescrs=[])
                 descr.tid = 9315
                 return descr
         self.cpu = FakeCPU()
diff --git a/rpython/jit/codewriter/heaptracker.py b/rpython/jit/codewriter/heaptracker.py
--- a/rpython/jit/codewriter/heaptracker.py
+++ b/rpython/jit/codewriter/heaptracker.py
@@ -20,30 +20,8 @@
     a -= r_uint(1 << (b8 - 1))     # a -= 128
     return intmask(a)
 
-def count_fields_if_immutable(STRUCT):
-    if not isinstance(STRUCT, lltype.GcStruct):
-        return -1
-    if STRUCT._hints.get('immutable', False):
-        try:
-            return _count_fields(STRUCT)
-        except ValueError:
-            pass
-    return -1
-
-def _count_fields(STRUCT):
-    if STRUCT == rclass.OBJECT:
-        return 0    # don't count 'typeptr'
-    result = 0
-    for fieldname, TYPE in STRUCT._flds.items():
-        if TYPE is lltype.Void:
-            pass       # ignore Voids
-        elif not isinstance(TYPE, lltype.ContainerType):
-            result += 1
-        elif isinstance(TYPE, lltype.GcStruct):
-            result += _count_fields(TYPE)
-        else:
-            raise ValueError(TYPE)
-    return result
+def is_immutable_struct(S):
+    return isinstance(S, lltype.GcStruct) and S._hints.get('immutable', False)
 
 # ____________________________________________________________
 
diff --git a/rpython/jit/metainterp/optimizeopt/info.py b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -5,6 +5,7 @@
 from rpython.jit.metainterp.history import ConstInt, Const
 from rpython.rtyper.lltypesystem import lltype
 from rpython.jit.metainterp.optimizeopt.rawbuffer import RawBuffer, InvalidRawOperation
+from rpython.jit.metainterp.executor import execute
 
 
 INFO_NULL = 0
@@ -22,7 +23,10 @@
     def getconst(self):
         raise Exception("not a constant")
 
-    
+    def _is_immutable_and_filled_with_constants(self, optimizer, memo=None):
+        return False
+
+
 class PtrInfo(AbstractInfo):
     _attrs_ = ()
 
@@ -109,6 +113,15 @@
     def force_box(self, op, optforce):
         if self.is_virtual():
             optforce.forget_numberings()
+            #
+            if self._is_immutable_and_filled_with_constants(optforce.optimizer):
+                constptr = optforce.optimizer.constant_fold(op)
+                op.set_forwarded(constptr)
+                descr = self.vdescr
+                self.vdescr = None
+                self._force_elements_immutable(descr, constptr, optforce)
+                return constptr
+            #
             op.set_forwarded(None)
             optforce._emit_operation(op)
             newop = optforce.getlastop()
@@ -214,6 +227,46 @@
         getfield_op = ResOperation(opnum, [structbox], descr=descr)
         shortboxes.add_heap_op(op, getfield_op)
 
+    def _is_immutable_and_filled_with_constants(self, optimizer, memo=None):
+        # check if it is possible to force the given structure into a
+        # compile-time constant: this is allowed only if it is declared
+        # immutable, if all fields are already filled, and if each field
+        # is either a compile-time constant or (recursively) a structure
+        # which also answers True to the same question.
+        #
+        assert self.is_virtual()
+        if not self.vdescr.is_immutable():
+            return False
+        if memo is not None and self in memo:
+            return True       # recursive case: assume yes
+        #
+        for op in self._fields:
+            if op is None:
+                return False     # there is an uninitialized field
+            op = op.get_box_replacement()
+            if op.is_constant():
+                pass            # it is a constant value: ok
+            else:
+                fieldinfo = optimizer.getptrinfo(op)
+                if fieldinfo and fieldinfo.is_virtual():
+                    # recursive check
+                    if memo is None:
+                        memo = {self: None}
+                    if not fieldinfo._is_immutable_and_filled_with_constants(
+                            optimizer, memo):
+                        return False
+                else:
+                    return False    # not a constant at all
+        return True
+
+    def _force_elements_immutable(self, descr, constptr, optforce):
+        for i, flddescr in enumerate(descr.get_all_fielddescrs()):
+            fld = self._fields[i]
+            subbox = optforce.force_box(fld)
+            assert isinstance(subbox, Const)
+            execute(optforce.optimizer.cpu, None, rop.SETFIELD_GC,
+                    flddescr, constptr, subbox)
+
 class InstancePtrInfo(AbstractStructPtrInfo):
     _attrs_ = ('_known_class',)
     _fields = None
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -6091,7 +6091,7 @@
         # ----------
         ops = """
         [p1]
-        p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+        p0 = new_with_vtable(descr=ptrobj_immut_descr)
         setfield_gc(p0, p1, descr=immut_ptrval)
         escape_n(p0)
         jump(p1)
@@ -6100,8 +6100,8 @@
         # ----------
         ops = """
         []
-        p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
-        p1 = new_with_vtable(ConstClass(intobj_immut_vtable))
+        p0 = new_with_vtable(descr=ptrobj_immut_descr)
+        p1 = new_with_vtable(descr=immut_descr)
         setfield_gc(p1, 1242, descr=immut_intval)
         setfield_gc(p0, p1, descr=immut_ptrval)
         escape_n(p0)
@@ -6115,6 +6115,8 @@
                 p1 = other.container.ptrval
                 p1cast = lltype.cast_pointer(lltype.Ptr(self.INTOBJ_IMMUT), p1)
                 return p1cast.intval == 1242
+            def _normalizedcontainer(self):
+                return self
         self.namespace['ptrobj1242'] = lltype._ptr(llmemory.GCREF,
                                                    PtrObj1242())
         expected = """
@@ -6191,6 +6193,8 @@
                 assert p2 != p1
                 p2cast = lltype.cast_pointer(lltype.Ptr(self.PTROBJ_IMMUT), p2)
                 return p2cast.ptrval == p1
+            def _normalizedcontainer(self):
+                return self
         self.namespace['ptrobjself2'] = lltype._ptr(llmemory.GCREF,
                                                     PtrObjSelf2())
         expected = """


More information about the pypy-commit mailing list