summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2014-05-23 09:46:03 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2014-05-23 09:46:03 (GMT)
commit9961405ed122c0f91b063f3237ad47278ae72f62 (patch)
treec3cc6c2ed0312469a8b5bfe526a4bc8eb7f13750
parente17de091012eb928c5734856eeaf2bb8f99e40c1 (diff)
downloadcpython-9961405ed122c0f91b063f3237ad47278ae72f62.zip
cpython-9961405ed122c0f91b063f3237ad47278ae72f62.tar.gz
cpython-9961405ed122c0f91b063f3237ad47278ae72f62.tar.bz2
Issue #21523: Fix over-pessimistic computation of the stack effect of some opcodes in the compiler.
This also fixes a quadratic compilation time issue noticeable when compiling code with a large number of "and" and "or" operators.
-rw-r--r--Lib/test/test_compile.py42
-rw-r--r--Misc/NEWS14
-rw-r--r--Python/compile.c8
3 files changed, 59 insertions, 5 deletions
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index ccd08db..b1f55bb 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1,3 +1,4 @@
+import math
import unittest
import sys
import _ast
@@ -501,8 +502,43 @@ if 1:
check_limit("a", "*a")
-def test_main():
- support.run_unittest(TestSpecifics)
+class TestStackSize(unittest.TestCase):
+ # These tests check that the computed stack size for a code object
+ # stays within reasonable bounds (see issue #21523 for an example
+ # dysfunction).
+ N = 100
+
+ def check_stack_size(self, code):
+ # To assert that the alleged stack size is not O(N), we
+ # check that it is smaller than log(N).
+ if isinstance(code, str):
+ code = compile(code, "<foo>", "single")
+ max_size = math.ceil(math.log(len(code.co_code)))
+ self.assertLessEqual(code.co_stacksize, max_size)
+
+ def test_and(self):
+ self.check_stack_size("x and " * self.N + "x")
+
+ def test_or(self):
+ self.check_stack_size("x or " * self.N + "x")
+
+ def test_and_or(self):
+ self.check_stack_size("x and x or " * self.N + "x")
+
+ def test_chained_comparison(self):
+ self.check_stack_size("x < " * self.N + "x")
+
+ def test_if_else(self):
+ self.check_stack_size("x if x else " * self.N + "x")
+
+ def test_binop(self):
+ self.check_stack_size("x + " * self.N + "x")
+
+ def test_func_and(self):
+ code = "def f(x):\n"
+ code += " x and x\n" * self.N
+ self.check_stack_size(code)
+
if __name__ == "__main__":
- test_main()
+ unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 4b74609..c103386 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,20 @@
Python News
+++++++++++
+What's New in Python 3.4.2?
+===========================
+
+Release date: XXXX-XX-XX
+
+Core and Builtins
+-----------------
+
+- Issue #21523: Fix over-pessimistic computation of the stack effect of
+ some opcodes in the compiler. This also fixes a quadratic compilation
+ time issue noticeable when compiling code with a large number of "and"
+ and "or" operators.
+
+
What's New in Python 3.4.1?
===========================
diff --git a/Python/compile.c b/Python/compile.c
index 9978eb3..69419ec 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -3856,12 +3856,16 @@ stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth)
target_depth = depth;
if (instr->i_opcode == FOR_ITER) {
target_depth = depth-2;
- } else if (instr->i_opcode == SETUP_FINALLY ||
- instr->i_opcode == SETUP_EXCEPT) {
+ }
+ else if (instr->i_opcode == SETUP_FINALLY ||
+ instr->i_opcode == SETUP_EXCEPT) {
target_depth = depth+3;
if (target_depth > maxdepth)
maxdepth = target_depth;
}
+ else if (instr->i_opcode == JUMP_IF_TRUE_OR_POP ||
+ instr->i_opcode == JUMP_IF_FALSE_OR_POP)
+ depth = depth - 1;
maxdepth = stackdepth_walk(c, instr->i_target,
target_depth, maxdepth);
if (instr->i_opcode == JUMP_ABSOLUTE ||