Skip to content

Commit 27b69e6

Browse files
authored
bpo-45773: Stop "optimizing" certain jump patterns (GH-29505)
1 parent 9178f53 commit 27b69e6

File tree

3 files changed

+49
-76
lines changed

3 files changed

+49
-76
lines changed

Lib/test/test_peepholer.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,5 +600,12 @@ def test_bpo_42057(self):
600600
except Exception or Exception:
601601
pass
602602

603+
def test_bpo_45773_pop_jump_if_true(self):
604+
compile("while True or spam: pass", "<test>", "exec")
605+
606+
def test_bpo_45773_pop_jump_if_false(self):
607+
compile("while True or not spam: pass", "<test>", "exec")
608+
609+
603610
if __name__ == "__main__":
604611
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a compiler hang when attempting to optimize certain jump patterns.

Python/compile.c

Lines changed: 41 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -8052,29 +8052,24 @@ fold_rotations(struct instr *inst, int n)
80528052
}
80538053
}
80548054

8055-
8056-
static int
8057-
eliminate_jump_to_jump(basicblock *bb, int opcode) {
8058-
assert (bb->b_iused > 0);
8059-
struct instr *inst = &bb->b_instr[bb->b_iused-1];
8060-
assert (is_jump(inst));
8061-
assert (inst->i_target->b_iused > 0);
8062-
struct instr *target = &inst->i_target->b_instr[0];
8063-
if (inst->i_target == target->i_target) {
8064-
/* Nothing to do */
8065-
return 0;
8066-
}
8067-
int lineno = target->i_lineno;
8068-
int end_lineno = target->i_end_lineno;
8069-
int col_offset = target->i_col_offset;
8070-
int end_col_offset = target->i_end_col_offset;
8071-
if (add_jump_to_block(bb, opcode, lineno, end_lineno, col_offset,
8072-
end_col_offset, target->i_target) == 0) {
8073-
return -1;
8055+
// Attempt to eliminate jumps to jumps by updating inst to jump to
8056+
// target->i_target using the provided opcode. Return whether or not the
8057+
// optimization was successful.
8058+
static bool
8059+
jump_thread(struct instr *inst, struct instr *target, int opcode)
8060+
{
8061+
assert(is_jump(inst));
8062+
assert(is_jump(target));
8063+
// bpo-45773: If inst->i_target == target->i_target, then nothing actually
8064+
// changes (and we fall into an infinite loop):
8065+
if (inst->i_lineno == target->i_lineno &&
8066+
inst->i_target != target->i_target)
8067+
{
8068+
inst->i_target = target->i_target;
8069+
inst->i_opcode = opcode;
8070+
return true;
80748071
}
8075-
assert (bb->b_iused >= 2);
8076-
bb->b_instr[bb->b_iused-2].i_opcode = NOP;
8077-
return 0;
8072+
return false;
80788073
}
80798074

80808075
/* Maximum size of basic block that should be copied in optimizer */
@@ -8199,108 +8194,78 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
81998194
where y+1 is the instruction following the second test.
82008195
*/
82018196
case JUMP_IF_FALSE_OR_POP:
8202-
switch(target->i_opcode) {
8197+
switch (target->i_opcode) {
82038198
case POP_JUMP_IF_FALSE:
8204-
if (inst->i_lineno == target->i_lineno) {
8205-
*inst = *target;
8206-
i--;
8207-
}
8199+
i -= jump_thread(inst, target, POP_JUMP_IF_FALSE);
82088200
break;
82098201
case JUMP_ABSOLUTE:
82108202
case JUMP_FORWARD:
82118203
case JUMP_IF_FALSE_OR_POP:
8212-
if (inst->i_lineno == target->i_lineno &&
8213-
inst->i_target != target->i_target) {
8214-
inst->i_target = target->i_target;
8215-
i--;
8216-
}
8204+
i -= jump_thread(inst, target, JUMP_IF_FALSE_OR_POP);
82178205
break;
82188206
case JUMP_IF_TRUE_OR_POP:
8219-
assert (inst->i_target->b_iused == 1);
8207+
case POP_JUMP_IF_TRUE:
82208208
if (inst->i_lineno == target->i_lineno) {
8209+
// We don't need to bother checking for loops here,
8210+
// since a block's b_next cannot point to itself:
8211+
assert(inst->i_target != inst->i_target->b_next);
82218212
inst->i_opcode = POP_JUMP_IF_FALSE;
82228213
inst->i_target = inst->i_target->b_next;
82238214
--i;
82248215
}
82258216
break;
82268217
}
82278218
break;
8228-
82298219
case JUMP_IF_TRUE_OR_POP:
8230-
switch(target->i_opcode) {
8220+
switch (target->i_opcode) {
82318221
case POP_JUMP_IF_TRUE:
8232-
if (inst->i_lineno == target->i_lineno) {
8233-
*inst = *target;
8234-
i--;
8235-
}
8222+
i -= jump_thread(inst, target, POP_JUMP_IF_TRUE);
82368223
break;
82378224
case JUMP_ABSOLUTE:
82388225
case JUMP_FORWARD:
82398226
case JUMP_IF_TRUE_OR_POP:
8240-
if (inst->i_lineno == target->i_lineno &&
8241-
inst->i_target != target->i_target) {
8242-
inst->i_target = target->i_target;
8243-
i--;
8244-
}
8227+
i -= jump_thread(inst, target, JUMP_IF_TRUE_OR_POP);
82458228
break;
82468229
case JUMP_IF_FALSE_OR_POP:
8247-
assert (inst->i_target->b_iused == 1);
8230+
case POP_JUMP_IF_FALSE:
82488231
if (inst->i_lineno == target->i_lineno) {
8232+
// We don't need to bother checking for loops here,
8233+
// since a block's b_next cannot point to itself:
8234+
assert(inst->i_target != inst->i_target->b_next);
82498235
inst->i_opcode = POP_JUMP_IF_TRUE;
82508236
inst->i_target = inst->i_target->b_next;
82518237
--i;
82528238
}
82538239
break;
82548240
}
82558241
break;
8256-
82578242
case POP_JUMP_IF_FALSE:
8258-
switch(target->i_opcode) {
8243+
switch (target->i_opcode) {
82598244
case JUMP_ABSOLUTE:
82608245
case JUMP_FORWARD:
8261-
if (inst->i_lineno == target->i_lineno) {
8262-
inst->i_target = target->i_target;
8263-
i--;
8264-
}
8265-
break;
8246+
case JUMP_IF_FALSE_OR_POP:
8247+
i -= jump_thread(inst, target, POP_JUMP_IF_FALSE);
82668248
}
82678249
break;
8268-
82698250
case POP_JUMP_IF_TRUE:
8270-
switch(target->i_opcode) {
8251+
switch (target->i_opcode) {
82718252
case JUMP_ABSOLUTE:
82728253
case JUMP_FORWARD:
8273-
if (inst->i_lineno == target->i_lineno) {
8274-
inst->i_target = target->i_target;
8275-
i--;
8276-
}
8277-
break;
8254+
case JUMP_IF_TRUE_OR_POP:
8255+
i -= jump_thread(inst, target, POP_JUMP_IF_TRUE);
82788256
}
82798257
break;
8280-
82818258
case JUMP_ABSOLUTE:
82828259
case JUMP_FORWARD:
8283-
assert (i == bb->b_iused-1);
8284-
switch(target->i_opcode) {
8285-
case JUMP_FORWARD:
8286-
if (eliminate_jump_to_jump(bb, inst->i_opcode)) {
8287-
goto error;
8288-
}
8289-
break;
8290-
8260+
switch (target->i_opcode) {
82918261
case JUMP_ABSOLUTE:
8292-
if (eliminate_jump_to_jump(bb, JUMP_ABSOLUTE)) {
8293-
goto error;
8294-
}
8295-
break;
8262+
case JUMP_FORWARD:
8263+
i -= jump_thread(inst, target, JUMP_ABSOLUTE);
82968264
}
82978265
break;
82988266
case FOR_ITER:
8299-
assert (i == bb->b_iused-1);
83008267
if (target->i_opcode == JUMP_FORWARD) {
8301-
if (eliminate_jump_to_jump(bb, inst->i_opcode)) {
8302-
goto error;
8303-
}
8268+
i -= jump_thread(inst, target, FOR_ITER);
83048269
}
83058270
break;
83068271
case ROT_N:

0 commit comments

Comments
 (0)