[pypy-commit] pypy default: (cfbolz, arigo giving the idea) to construct gigantic tuples in constant stack

cfbolz pypy.commits at gmail.com
Wed Mar 27 18:01:23 EDT 2019


Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: 
Changeset: r96369:f6a9c3de9c7d
Date: 2019-03-27 22:51 +0100
http://bitbucket.org/pypy/pypy/changeset/f6a9c3de9c7d/

Log:	(cfbolz, arigo giving the idea) to construct gigantic tuples in
	constant stack space, first build a list, then use
	().__class__(list) to turn it into a tuple. bit of a hack, but
	calling 'tuple' doesn't work if somebody overrides the global name
	'tuple'

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
@@ -912,6 +912,20 @@
         elt_count = len(tup.elts) if tup.elts is not None else 0
         if tup.ctx == ast.Store:
             self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
+        if tup.ctx == ast.Load and elt_count > MAX_STACKDEPTH_CONTAINERS:
+            # we need a complete hack to build a new tuple from the list
+            # ().__class__(l)
+            empty_index = self.add_const(self.space.newtuple([]))
+            self.emit_op_arg(ops.LOAD_CONST, empty_index)
+            self.emit_op_name(ops.LOAD_ATTR, self.names, '__class__')
+
+            self.emit_op_arg(ops.BUILD_LIST, 0)
+            for element in tup.elts:
+                element.walkabout(self)
+                self.emit_op_arg(ops.LIST_APPEND, 1)
+
+            self.emit_op_arg(ops.CALL_FUNCTION, 1)
+            return
         self.visit_sequence(tup.elts)
         if tup.ctx == ast.Load:
             self.emit_op_arg(ops.BUILD_TUPLE, elt_count)
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
@@ -1220,22 +1220,27 @@
 
 
 class TestHugeStackDepths:
-    def test_list(self):
+    def run_and_check_stacksize(self, source):
         space = self.space
-        source = "a = [" + ",".join([str(i) for i in range(200)]) + "]\n"
-        code = compile_with_astcompiler(source, 'exec', space)
+        code = compile_with_astcompiler("a = " + 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)
+        return space.getitem(w_dict, space.newtext("a"))
+
+    def test_tuple(self):
+        source = "(" + ",".join([str(i) for i in range(200)]) + ")\n"
+        w_res = self.run_and_check_stacksize(source)
+        assert self.space.unwrap(w_res) == tuple(range(200))
+
+    def test_list(self):
+        source = "a = [" + ",".join([str(i) for i in range(200)]) + "]\n"
+        w_res = self.run_and_check_stacksize(source)
+        assert self.space.unwrap(w_res) == range(200)
 
     def test_set(self):
+        source = "a = {" + ",".join([str(i) for i in range(200)]) + "}\n"
+        w_res = self.run_and_check_stacksize(source)
         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)
-
+                    for w_x in space.unpackiterable(w_res)] == range(200)


More information about the pypy-commit mailing list