[pypy-commit] pypy default: issue2980: stop using arbitrarily much stack for building constant lists and

cfbolz pypy.commits at gmail.com
Wed Mar 27 10:39:51 EDT 2019


Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: 
Changeset: r96362:c46e92cbc2f9
Date: 2019-03-27 14:41 +0100
http://bitbucket.org/pypy/pypy/changeset/c46e92cbc2f9/

Log:	issue2980: stop using arbitrarily much stack for building constant
	lists and sets. do it element by element instead. otherwise the JIT
	cannot produce any code for any of the loops in the same scope,
	because it runs into limitations.

diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -19,6 +19,7 @@
     symbols = symtable.SymtableBuilder(space, module, info)
     return TopLevelCodeGenerator(space, module, symbols, info).assemble()
 
+MAX_STACKDEPTH_CONTAINERS = 100
 
 name_ops_default = misc.dict_to_switch({
     ast.Load: ops.LOAD_NAME,
@@ -920,9 +921,17 @@
         elt_count = len(l.elts) if l.elts is not None else 0
         if l.ctx == ast.Store:
             self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
-        self.visit_sequence(l.elts)
-        if l.ctx == ast.Load:
-            self.emit_op_arg(ops.BUILD_LIST, elt_count)
+        if elt_count > MAX_STACKDEPTH_CONTAINERS:
+            # pushing all the elements would make the stack depth gigantic.
+            # build the list incrementally instead
+            self.emit_op_arg(ops.BUILD_LIST, 0)
+            for element in l.elts:
+                element.walkabout(self)
+                self.emit_op_arg(ops.LIST_APPEND, 1)
+        else:
+            self.visit_sequence(l.elts)
+            if l.ctx == ast.Load:
+                self.emit_op_arg(ops.BUILD_LIST, elt_count)
 
     def visit_Dict(self, d):
         self.update_position(d.lineno)
@@ -936,8 +945,15 @@
     def visit_Set(self, s):
         self.update_position(s.lineno)
         elt_count = len(s.elts) if s.elts is not None else 0
-        self.visit_sequence(s.elts)
-        self.emit_op_arg(ops.BUILD_SET, elt_count)
+        if elt_count > MAX_STACKDEPTH_CONTAINERS:
+            self.emit_op_arg(ops.BUILD_SET, 0)
+            for element in s.elts:
+                element.walkabout(self)
+                self.emit_op_arg(ops.SET_ADD, 1)
+        else:
+            self.visit_sequence(s.elts)
+            self.emit_op_arg(ops.BUILD_SET, elt_count)
+
 
     def visit_Name(self, name):
         self.update_position(name.lineno)
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -1218,3 +1218,24 @@
         counts = self.count_instructions(source)
         assert ops.BUILD_TUPLE not in counts
 
+
+class TestHugeStackDepths:
+    def test_list(self):
+        space = self.space
+        source = "a = [" + ",".join([str(i) for i in range(200)]) + "]\n"
+        code = compile_with_astcompiler(source, 'exec', space)
+        assert code.co_stacksize < 100
+        w_dict = space.newdict()
+        code.exec_code(space, w_dict, w_dict)
+        assert space.unwrap(space.getitem(w_dict, space.newtext("a"))) == range(200)
+
+    def test_set(self):
+        space = self.space
+        source = "a = {" + ",".join([str(i) for i in range(200)]) + "}\n"
+        code = compile_with_astcompiler(source, 'exec', space)
+        assert code.co_stacksize < 100
+        w_dict = space.newdict()
+        code.exec_code(space, w_dict, w_dict)
+        assert [space.int_w(w_x)
+                    for w_x in space.unpackiterable(space.getitem(w_dict, space.newtext("a")))] == range(200)
+


More information about the pypy-commit mailing list