Commits

Armin Rigo  committed 0646aa0

(fijal, arigo)

Improve on the previous check-in to detect fresh mallocs as long
as they are in the same graph. That's probably good enough.

  • Participants
  • Parent commits 84f0946

Comments (0)

Files changed (2)

File pypy/translator/backendopt/test/test_writeanalyze.py

         result = wa.analyze_direct_call(fgraph)
         assert not result
 
+    def test_write_to_new_struct_2(self):
+        class A(object):
+            pass
+        def f(x):
+            a = A()
+            # a few extra blocks
+            i = 10
+            while i > 0:
+                i -= 1
+            # done
+            a.baz = x   # writes to a fresh new struct are ignored
+            return a
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        result = wa.analyze_direct_call(fgraph)
+        assert not result
+
+    def test_write_to_new_struct_3(self):
+        class A(object):
+            pass
+        prebuilt = A()
+        def f(x):
+            if x > 5:
+                a = A()
+            else:
+                a = A()
+            a.baz = x
+            return a
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        result = wa.analyze_direct_call(fgraph)
+        assert not result
+
+    def test_write_to_new_struct_4(self):
+        class A(object):
+            pass
+        prebuilt = A()
+        def f(x):
+            if x > 5:
+                a = A()
+            else:
+                a = prebuilt
+            a.baz = x
+            return a
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        result = wa.analyze_direct_call(fgraph)
+        assert len(result) == 1 and 'baz' in list(result)[0][-1]
+
+    def test_write_to_new_struct_5(self):
+        class A(object):
+            baz = 123
+        def f(x):
+            if x:
+                a = A()
+            else:
+                a = A()
+            a.baz += 1
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        result = wa.analyze_direct_call(fgraph)
+        assert not result
+
     def test_method(self):
         class A(object):
             def f(self):

File pypy/translator/backendopt/writeanalyze.py

+from pypy.objspace.flow.model import Variable
 from pypy.translator.backendopt import graphanalyze
 from pypy.rpython.ootypesystem import ootype
 
         return result is top_set
 
     def analyze_simple_operation(self, op, graphinfo):
-        if graphinfo and op.args[0] in graphinfo:
-            return empty_set
         if op.opname in ("setfield", "oosetfield"):
-            return frozenset([
-                ("struct", op.args[0].concretetype, op.args[1].value)])
+            if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
+                return frozenset([
+                    ("struct", op.args[0].concretetype, op.args[1].value)])
         elif op.opname == "setarrayitem":
-            return self._array_result(op.args[0].concretetype)
+            if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
+                return self._array_result(op.args[0].concretetype)
         return empty_set
 
     def _array_result(self, TYPE):
         return graphanalyze.GraphAnalyzer.analyze_external_method(self, op, TYPE, meth)
 
     def compute_graph_info(self, graph):
-        newstructs = set()
-        for block in graph.iterblocks():
+        return FreshMallocs(graph)
+
+
+class FreshMallocs(object):
+    def __init__(self, graph):
+        self.nonfresh = set(graph.getargs())
+        pendingblocks = list(graph.iterblocks())
+        self.allvariables = set()
+        for block in pendingblocks:
+            self.allvariables.update(block.inputargs)
+        pendingblocks.reverse()
+        while pendingblocks:
+            block = pendingblocks.pop()
             for op in block.operations:
+                self.allvariables.add(op.result)
                 if (op.opname == 'malloc' or op.opname == 'malloc_varsize'
                     or op.opname == 'new'):
-                    newstructs.add(op.result)
+                    continue
                 elif op.opname in ('cast_pointer', 'same_as'):
-                    if op.args[0] in newstructs:
-                        newstructs.add(op.result)
-        return newstructs
+                    if self.is_fresh_malloc(op.args[0]):
+                        continue
+                self.nonfresh.add(op.result)
+            for link in block.exits:
+                self.nonfresh.update(link.getextravars())
+                self.allvariables.update(link.getextravars())
+                prevlen = len(self.nonfresh)
+                for v1, v2 in zip(link.args, link.target.inputargs):
+                    if not self.is_fresh_malloc(v1):
+                        self.nonfresh.add(v2)
+                if len(self.nonfresh) > prevlen:
+                    pendingblocks.append(link.target)
+
+    def is_fresh_malloc(self, v):
+        if not isinstance(v, Variable):
+            return False
+        assert v in self.allvariables
+        return v not in self.nonfresh
 
 
 class ReadWriteAnalyzer(WriteAnalyzer):