Skip to content

Commit 334589f

Browse files
authored
gh-126835: Set location for noped out instructions after constant folding in CFG. (#130109)
1 parent 0f20281 commit 334589f

File tree

2 files changed

+108
-54
lines changed

2 files changed

+108
-54
lines changed

Lib/test/test_peepholer.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,83 @@ def test_folding_subscript(self):
535535
self.assertInBytecode(code, 'BINARY_OP', argval=subscr_argval)
536536
self.check_lnotab(code)
537537

538+
def test_constant_folding_remove_nop_location(self):
539+
sources = [
540+
"""
541+
(-
542+
-
543+
-
544+
1)
545+
""",
546+
547+
"""
548+
(1
549+
+
550+
2
551+
+
552+
3)
553+
""",
554+
555+
"""
556+
(1,
557+
2,
558+
3)[0]
559+
""",
560+
561+
"""
562+
[1,
563+
2,
564+
3]
565+
""",
566+
567+
"""
568+
{1,
569+
2,
570+
3}
571+
""",
572+
573+
"""
574+
1 in [
575+
1,
576+
2,
577+
3
578+
]
579+
""",
580+
581+
"""
582+
1 in {
583+
1,
584+
2,
585+
3
586+
}
587+
""",
588+
589+
"""
590+
for _ in [1,
591+
2,
592+
3]:
593+
pass
594+
""",
595+
596+
"""
597+
for _ in [1,
598+
2,
599+
x]:
600+
pass
601+
""",
602+
603+
"""
604+
for _ in {1,
605+
2,
606+
3}:
607+
pass
608+
"""
609+
]
610+
611+
for source in sources:
612+
code = compile(textwrap.dedent(source), '', 'single')
613+
self.assertNotInBytecode(code, 'NOP')
614+
538615
def test_in_literal_list(self):
539616
def containtest():
540617
return x in [a, b]

Python/flowgraph.c

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ is_jump(cfg_instr *i)
126126
_instr__ptr_->i_oparg = 0; \
127127
} while (0);
128128

129+
#define INSTR_SET_LOC(I, LOC) \
130+
do { \
131+
cfg_instr *_instr__ptr_ = (I); \
132+
_instr__ptr_->i_loc = (LOC); \
133+
} while (0);
134+
129135
/***** Blocks *****/
130136

131137
/* Returns the offset of the next instruction in the current block's
@@ -1382,18 +1388,20 @@ get_constant_sequence(basicblock *bb, int start, int size,
13821388

13831389
/*
13841390
Walk basic block backwards starting from "start" and change "count" number of
1385-
non-NOP instructions to NOP's.
1391+
non-NOP instructions to NOP's and set their location to NO_LOCATION.
13861392
*/
13871393
static void
13881394
nop_out(basicblock *bb, int start, int count)
13891395
{
13901396
assert(start < bb->b_iused);
13911397
for (; count > 0; start--) {
13921398
assert(start >= 0);
1393-
if (bb->b_instr[start].i_opcode == NOP) {
1399+
cfg_instr *instr = &bb->b_instr[start];
1400+
if (instr->i_opcode == NOP) {
13941401
continue;
13951402
}
1396-
INSTR_SET_OP0(&bb->b_instr[start], NOP);
1403+
INSTR_SET_OP0(instr, NOP);
1404+
INSTR_SET_LOC(instr, NO_LOCATION);
13971405
count--;
13981406
}
13991407
}
@@ -1423,7 +1431,7 @@ fold_tuple_of_constants(basicblock *bb, int n, PyObject *consts, PyObject *const
14231431
int index = add_const(newconst, consts, const_cache);
14241432
RETURN_IF_ERROR(index);
14251433
nop_out(bb, n-1, seq_size);
1426-
INSTR_SET_OP1(&bb->b_instr[n], LOAD_CONST, index);
1434+
INSTR_SET_OP1(instr, LOAD_CONST, index);
14271435
return SUCCESS;
14281436
}
14291437

@@ -1479,40 +1487,16 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop,
14791487
else {
14801488
assert(i >= 2);
14811489
assert(instr->i_opcode == BUILD_LIST || instr->i_opcode == BUILD_SET);
1490+
1491+
INSTR_SET_LOC(&bb->b_instr[i-2], instr->i_loc);
1492+
14821493
INSTR_SET_OP1(&bb->b_instr[i-2], instr->i_opcode, 0);
14831494
INSTR_SET_OP1(&bb->b_instr[i-1], LOAD_CONST, index);
14841495
INSTR_SET_OP1(&bb->b_instr[i], instr->i_opcode == BUILD_LIST ? LIST_EXTEND : SET_UPDATE, 1);
14851496
}
14861497
return SUCCESS;
14871498
}
14881499

1489-
/*
1490-
Walk basic block backwards starting from "start" to collect instruction pair
1491-
that loads consts skipping NOP's in between.
1492-
*/
1493-
static bool
1494-
find_load_const_pair(basicblock *bb, int start, cfg_instr **first, cfg_instr **second)
1495-
{
1496-
cfg_instr *second_load_const = NULL;
1497-
while (start >= 0) {
1498-
cfg_instr *inst = &bb->b_instr[start--];
1499-
if (inst->i_opcode == NOP) {
1500-
continue;
1501-
}
1502-
if (!loads_const(inst->i_opcode)) {
1503-
return false;
1504-
}
1505-
if (second_load_const == NULL) {
1506-
second_load_const = inst;
1507-
continue;
1508-
}
1509-
*first = inst;
1510-
*second = second_load_const;
1511-
return true;
1512-
}
1513-
return false;
1514-
}
1515-
15161500
/* Determine opcode & oparg for freshly folded constant. */
15171501
static int
15181502
newop_from_folded(PyObject *newconst, PyObject *consts,
@@ -1534,27 +1518,25 @@ newop_from_folded(PyObject *newconst, PyObject *consts,
15341518
}
15351519

15361520
static int
1537-
optimize_if_const_op(basicblock *bb, int n, PyObject *consts, PyObject *const_cache)
1521+
optimize_if_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache)
15381522
{
1539-
cfg_instr *subscr = &bb->b_instr[n];
1540-
assert(subscr->i_opcode == BINARY_OP);
1541-
if (subscr->i_oparg != NB_SUBSCR) {
1523+
cfg_instr *binop = &bb->b_instr[i];
1524+
assert(binop->i_opcode == BINARY_OP);
1525+
if (binop->i_oparg != NB_SUBSCR) {
15421526
/* TODO: support other binary ops */
15431527
return SUCCESS;
15441528
}
1545-
cfg_instr *arg, *idx;
1546-
if (!find_load_const_pair(bb, n-1, &arg, &idx)) {
1529+
PyObject *pair;
1530+
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, 2, consts, &pair));
1531+
if (pair == NULL) {
15471532
return SUCCESS;
15481533
}
1549-
PyObject *o = NULL, *key = NULL;
1550-
if ((o = get_const_value(arg->i_opcode, arg->i_oparg, consts)) == NULL
1551-
|| (key = get_const_value(idx->i_opcode, idx->i_oparg, consts)) == NULL)
1552-
{
1553-
goto error;
1554-
}
1555-
PyObject *newconst = PyObject_GetItem(o, key);
1556-
Py_DECREF(o);
1557-
Py_DECREF(key);
1534+
assert(PyTuple_CheckExact(pair) && PyTuple_Size(pair) == 2);
1535+
PyObject *left = PyTuple_GET_ITEM(pair, 0);
1536+
PyObject *right = PyTuple_GET_ITEM(pair, 1);
1537+
assert(left != NULL && right != NULL);
1538+
PyObject *newconst = PyObject_GetItem(left, right);
1539+
Py_DECREF(pair);
15581540
if (newconst == NULL) {
15591541
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
15601542
return ERROR;
@@ -1564,14 +1546,9 @@ optimize_if_const_op(basicblock *bb, int n, PyObject *consts, PyObject *const_ca
15641546
}
15651547
int newopcode, newoparg;
15661548
RETURN_IF_ERROR(newop_from_folded(newconst, consts, const_cache, &newopcode, &newoparg));
1567-
INSTR_SET_OP1(subscr, newopcode, newoparg);
1568-
INSTR_SET_OP0(arg, NOP);
1569-
INSTR_SET_OP0(idx, NOP);
1549+
nop_out(bb, i-1, 2);
1550+
INSTR_SET_OP1(binop, newopcode, newoparg);
15701551
return SUCCESS;
1571-
error:
1572-
Py_XDECREF(o);
1573-
Py_XDECREF(key);
1574-
return ERROR;
15751552
}
15761553

15771554
#define VISITED (-1)
@@ -2072,7 +2049,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
20722049
}
20732050
break;
20742051
case BINARY_OP:
2075-
RETURN_IF_ERROR(optimize_if_const_op(bb, i, consts, const_cache));
2052+
RETURN_IF_ERROR(optimize_if_const_binop(bb, i, consts, const_cache));
20762053
break;
20772054
}
20782055
}

0 commit comments

Comments
 (0)