[Python-checkins] [3.10] bpo-45773: Stop "optimizing" certain jump patterns (GH-29526)

brandtbucher webhook-mailer at python.org
Thu Nov 11 16:52:48 EST 2021


https://github.com/python/cpython/commit/a89bbde83fe7f8cc347341e7ec57cda3ba312530
commit: a89bbde83fe7f8cc347341e7ec57cda3ba312530
branch: 3.10
author: Brandt Bucher <brandt at python.org>
committer: brandtbucher <brandtbucher at gmail.com>
date: 2021-11-11T13:52:43-08:00
summary:

[3.10] bpo-45773: Stop "optimizing" certain jump patterns (GH-29526)

files:
A Misc/NEWS.d/next/Core and Builtins/2021-11-09-13-01-35.bpo-45773.POU8A4.rst
M Lib/test/test_peepholer.py
M Python/compile.c
M Python/importlib.h
M Python/importlib_external.h
M Python/importlib_zipimport.h

diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index 4034154e4dcfb..11a4d1740b8dc 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -514,5 +514,12 @@ def test_bpo_42057(self):
             except Exception or Exception:
                 pass
 
+    def test_bpo_45773_pop_jump_if_true(self):
+        compile("while True or spam: pass", "<test>", "exec")
+
+    def test_bpo_45773_pop_jump_if_false(self):
+        compile("while True or not spam: pass", "<test>", "exec")
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-09-13-01-35.bpo-45773.POU8A4.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-09-13-01-35.bpo-45773.POU8A4.rst
new file mode 100644
index 0000000000000..2b9ba81be233f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-11-09-13-01-35.bpo-45773.POU8A4.rst	
@@ -0,0 +1 @@
+Fix a compiler hang when attempting to optimize certain jump patterns.
diff --git a/Python/compile.c b/Python/compile.c
index 90861038e07da..3eb34d89cc55f 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -21,6 +21,8 @@
  * objects.
  */
 
+#include <stdbool.h>
+
 #include "Python.h"
 #include "pycore_ast.h"           // _PyAST_GetDocString()
 #include "pycore_compile.h"       // _PyFuture_FromAST()
@@ -7264,25 +7266,24 @@ fold_rotations(struct instr *inst, int n)
     }
 }
 
-
-static int
-eliminate_jump_to_jump(basicblock *bb, int opcode) {
-    assert (bb->b_iused > 0);
-    struct instr *inst = &bb->b_instr[bb->b_iused-1];
-    assert (is_jump(inst));
-    assert (inst->i_target->b_iused > 0);
-    struct instr *target = &inst->i_target->b_instr[0];
-    if (inst->i_target == target->i_target) {
-        /* Nothing to do */
-        return 0;
-    }
-    int lineno = target->i_lineno;
-    if (add_jump_to_block(bb, opcode, lineno, target->i_target) == 0) {
-        return -1;
+// Attempt to eliminate jumps to jumps by updating inst to jump to
+// target->i_target using the provided opcode. Return whether or not the
+// optimization was successful.
+static bool
+jump_thread(struct instr *inst, struct instr *target, int opcode)
+{
+    assert(is_jump(inst));
+    assert(is_jump(target));
+    // bpo-45773: If inst->i_target == target->i_target, then nothing actually
+    // changes (and we fall into an infinite loop):
+    if (inst->i_lineno == target->i_lineno &&
+        inst->i_target != target->i_target)
+    {
+        inst->i_target = target->i_target;
+        inst->i_opcode = opcode;
+        return true;
     }
-    assert (bb->b_iused >= 2);
-    bb->b_instr[bb->b_iused-2].i_opcode = NOP;
-    return 0;
+    return false;
 }
 
 /* Maximum size of basic block that should be copied in optimizer */
@@ -7399,25 +7400,21 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
                    where y+1 is the instruction following the second test.
                 */
             case JUMP_IF_FALSE_OR_POP:
-                switch(target->i_opcode) {
+                switch (target->i_opcode) {
                     case POP_JUMP_IF_FALSE:
-                        if (inst->i_lineno == target->i_lineno) {
-                            *inst = *target;
-                            i--;
-                        }
+                        i -= jump_thread(inst, target, POP_JUMP_IF_FALSE);
                         break;
                     case JUMP_ABSOLUTE:
                     case JUMP_FORWARD:
                     case JUMP_IF_FALSE_OR_POP:
-                        if (inst->i_lineno == target->i_lineno &&
-                            inst->i_target != target->i_target) {
-                            inst->i_target = target->i_target;
-                            i--;
-                        }
+                        i -= jump_thread(inst, target, JUMP_IF_FALSE_OR_POP);
                         break;
                     case JUMP_IF_TRUE_OR_POP:
-                        assert (inst->i_target->b_iused == 1);
+                    case POP_JUMP_IF_TRUE:
                         if (inst->i_lineno == target->i_lineno) {
+                            // We don't need to bother checking for loops here,
+                            // since a block's b_next cannot point to itself:
+                            assert(inst->i_target != inst->i_target->b_next);
                             inst->i_opcode = POP_JUMP_IF_FALSE;
                             inst->i_target = inst->i_target->b_next;
                             --i;
@@ -7425,27 +7422,22 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
                         break;
                 }
                 break;
-
             case JUMP_IF_TRUE_OR_POP:
-                switch(target->i_opcode) {
+                switch (target->i_opcode) {
                     case POP_JUMP_IF_TRUE:
-                        if (inst->i_lineno == target->i_lineno) {
-                            *inst = *target;
-                            i--;
-                        }
+                        i -= jump_thread(inst, target, POP_JUMP_IF_TRUE);
                         break;
                     case JUMP_ABSOLUTE:
                     case JUMP_FORWARD:
                     case JUMP_IF_TRUE_OR_POP:
-                        if (inst->i_lineno == target->i_lineno &&
-                            inst->i_target != target->i_target) {
-                            inst->i_target = target->i_target;
-                            i--;
-                        }
+                        i -= jump_thread(inst, target, JUMP_IF_TRUE_OR_POP);
                         break;
                     case JUMP_IF_FALSE_OR_POP:
-                        assert (inst->i_target->b_iused == 1);
+                    case POP_JUMP_IF_FALSE:
                         if (inst->i_lineno == target->i_lineno) {
+                            // We don't need to bother checking for loops here,
+                            // since a block's b_next cannot point to itself:
+                            assert(inst->i_target != inst->i_target->b_next);
                             inst->i_opcode = POP_JUMP_IF_TRUE;
                             inst->i_target = inst->i_target->b_next;
                             --i;
@@ -7453,54 +7445,33 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
                         break;
                 }
                 break;
-
             case POP_JUMP_IF_FALSE:
-                switch(target->i_opcode) {
+                switch (target->i_opcode) {
                     case JUMP_ABSOLUTE:
                     case JUMP_FORWARD:
-                        if (inst->i_lineno == target->i_lineno) {
-                            inst->i_target = target->i_target;
-                            i--;
-                        }
-                        break;
+                    case JUMP_IF_FALSE_OR_POP:
+                        i -= jump_thread(inst, target, POP_JUMP_IF_FALSE);
                 }
                 break;
-
             case POP_JUMP_IF_TRUE:
-                switch(target->i_opcode) {
+                switch (target->i_opcode) {
                     case JUMP_ABSOLUTE:
                     case JUMP_FORWARD:
-                        if (inst->i_lineno == target->i_lineno) {
-                            inst->i_target = target->i_target;
-                            i--;
-                        }
-                        break;
+                    case JUMP_IF_TRUE_OR_POP:
+                        i -= jump_thread(inst, target, POP_JUMP_IF_TRUE);
                 }
                 break;
-
             case JUMP_ABSOLUTE:
             case JUMP_FORWARD:
-                assert (i == bb->b_iused-1);
-                switch(target->i_opcode) {
-                    case JUMP_FORWARD:
-                        if (eliminate_jump_to_jump(bb, inst->i_opcode)) {
-                            goto error;
-                        }
-                        break;
-
+                switch (target->i_opcode) {
                     case JUMP_ABSOLUTE:
-                        if (eliminate_jump_to_jump(bb, JUMP_ABSOLUTE)) {
-                            goto error;
-                        }
-                        break;
+                    case JUMP_FORWARD:
+                        i -= jump_thread(inst, target, JUMP_ABSOLUTE);
                 }
                 break;
             case FOR_ITER:
-                assert (i == bb->b_iused-1);
                 if (target->i_opcode == JUMP_FORWARD) {
-                    if (eliminate_jump_to_jump(bb, inst->i_opcode)) {
-                        goto error;
-                    }
+                    i -= jump_thread(inst, target, FOR_ITER);
                 }
                 break;
             case ROT_N:
diff --git a/Python/importlib.h b/Python/importlib.h
index dd1a9f172c04f..ab3e69b22b662 100644
--- a/Python/importlib.h
+++ b/Python/importlib.h
@@ -782,8 +782,8 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     103,0,110,1,100,2,125,5,124,4,124,0,124,1,124,5,
     100,4,141,3,83,0,124,3,100,2,117,0,114,67,116,0,
     124,1,100,5,131,2,114,65,122,7,124,1,160,4,124,0,
-    161,1,125,3,87,0,110,13,4,0,116,5,121,64,1,0,
-    1,0,1,0,100,2,125,3,89,0,110,3,119,0,100,6,
+    161,1,125,3,87,0,113,67,4,0,116,5,121,64,1,0,
+    1,0,1,0,100,2,125,3,89,0,113,67,119,0,100,6,
     125,3,116,6,124,0,124,1,124,2,124,3,100,7,141,4,
     83,0,41,8,122,53,82,101,116,117,114,110,32,97,32,109,
     111,100,117,108,101,32,115,112,101,99,32,98,97,115,101,100,
@@ -812,9 +812,9 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     122,5,124,0,106,4,125,5,87,0,110,11,4,0,116,1,
     121,59,1,0,1,0,1,0,100,0,125,5,89,0,110,1,
     119,0,124,2,100,0,117,0,114,87,124,5,100,0,117,0,
-    114,85,122,5,124,1,106,5,125,2,87,0,110,13,4,0,
+    114,85,122,5,124,1,106,5,125,2,87,0,113,87,4,0,
     116,1,121,84,1,0,1,0,1,0,100,0,125,2,89,0,
-    110,3,119,0,124,5,125,2,122,5,124,0,106,6,125,6,
+    113,87,119,0,124,5,125,2,122,5,124,0,106,6,125,6,
     87,0,110,11,4,0,116,1,121,103,1,0,1,0,1,0,
     100,0,125,6,89,0,110,1,119,0,122,7,116,7,124,0,
     106,8,131,1,125,7,87,0,110,11,4,0,116,1,121,122,
diff --git a/Python/importlib_external.h b/Python/importlib_external.h
index 90c8d89d63074..bd4d95a61e00a 100644
--- a/Python/importlib_external.h
+++ b/Python/importlib_external.h
@@ -977,8 +977,8 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = {
     8,0,0,0,67,0,0,0,115,54,1,0,0,124,1,100,
     1,117,0,114,29,100,2,125,1,116,0,124,2,100,3,131,
     2,114,28,122,7,124,2,160,1,124,0,161,1,125,1,87,
-    0,110,38,4,0,116,2,121,27,1,0,1,0,1,0,89,
-    0,110,30,119,0,110,28,116,3,160,4,124,1,161,1,125,
+    0,113,57,4,0,116,2,121,27,1,0,1,0,1,0,89,
+    0,113,57,119,0,110,28,116,3,160,4,124,1,161,1,125,
     1,116,5,124,1,131,1,115,57,122,9,116,6,116,3,160,
     7,161,0,124,1,131,2,125,1,87,0,110,9,4,0,116,
     8,121,56,1,0,1,0,1,0,89,0,110,1,119,0,116,
@@ -990,7 +990,7 @@ const unsigned char _Py_M__importlib_bootstrap_external[] = {
     1,83,0,124,3,116,16,117,0,114,131,116,0,124,2,100,
     6,131,2,114,130,122,7,124,2,160,17,124,0,161,1,125,
     7,87,0,110,9,4,0,116,2,121,124,1,0,1,0,1,
-    0,89,0,110,10,119,0,124,7,114,130,103,0,124,4,95,
+    0,89,0,113,134,119,0,124,7,114,130,103,0,124,4,95,
     18,110,3,124,3,124,4,95,18,124,4,106,18,103,0,107,
     2,114,153,124,1,114,153,116,19,124,1,131,1,100,7,25,
     0,125,8,124,4,106,18,160,20,124,8,161,1,1,0,124,
diff --git a/Python/importlib_zipimport.h b/Python/importlib_zipimport.h
index bb870c3a0353a..07c9dd574040f 100644
--- a/Python/importlib_zipimport.h
+++ b/Python/importlib_zipimport.h
@@ -1004,9 +1004,9 @@ const unsigned char _Py_M__zipimport[] = {
     1,0,1,0,89,0,113,9,119,0,124,8,100,4,25,0,
     125,9,116,8,124,0,106,4,124,8,131,2,125,10,100,0,
     125,11,124,5,114,91,122,10,116,9,124,0,124,9,124,7,
-    124,1,124,10,131,5,125,11,87,0,110,25,4,0,116,10,
+    124,1,124,10,131,5,125,11,87,0,113,96,4,0,116,10,
     121,90,1,0,125,12,1,0,122,8,124,12,125,3,87,0,
-    89,0,100,0,125,12,126,12,110,10,100,0,125,12,126,12,
+    89,0,100,0,125,12,126,12,113,96,100,0,125,12,126,12,
     119,1,119,0,116,11,124,9,124,10,131,2,125,11,124,11,
     100,0,117,0,114,101,113,9,124,8,100,4,25,0,125,9,
     124,11,124,6,124,9,102,3,2,0,1,0,83,0,124,3,



More information about the Python-checkins mailing list