Skip to content

Commit d41abe8

Browse files
ambvmethane
andauthored
[3.10] bpo-45056: Remove trailing unused constants from co_consts (GH-28109) (GH-28125)
(cherry picked from commit 55c4a92) Co-authored-by: Inada Naoki <[email protected]>
1 parent 7538fe3 commit d41abe8

File tree

7 files changed

+4654
-4616
lines changed

7 files changed

+4654
-4616
lines changed

Lib/test/test_compile.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,17 @@ def test_merge_code_attrs(self):
648648
self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
649649
self.assertIs(f1.__code__.co_code, f2.__code__.co_code)
650650

651+
# Stripping unused constants is not a strict requirement for the
652+
# Python semantics, it's a more an implementation detail.
653+
@support.cpython_only
654+
def test_strip_unused_consts(self):
655+
# Python 3.10rc1 appended None to co_consts when None is not used
656+
# at all. See bpo-45056.
657+
def f1():
658+
"docstring"
659+
return 42
660+
self.assertEqual(f1.__code__.co_consts, ("docstring", 42))
661+
651662
# This is a regression test for a CPython specific peephole optimizer
652663
# implementation bug present in a few releases. It's assertion verifies
653664
# that peephole optimization was actually done though that isn't an

Lib/test/test_dis.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,10 +689,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
689689
if sys.flags.optimize:
690690
code_info_consts = "0: None"
691691
else:
692-
code_info_consts = (
693-
"""0: 'Formatted details of methods, functions, or code.'
694-
1: None"""
695-
)
692+
code_info_consts = "0: 'Formatted details of methods, functions, or code.'"
696693

697694
code_info_code_info = f"""\
698695
Name: code_info
@@ -816,7 +813,6 @@ def f(c=c):
816813
Constants:
817814
0: 0
818815
1: 1
819-
2: None
820816
Names:
821817
0: x"""
822818

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compiler now removes trailing unused constants from co_consts.

Python/compile.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6986,6 +6986,9 @@ normalize_basic_block(basicblock *bb);
69866986
static int
69876987
optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
69886988

6989+
static int
6990+
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts);
6991+
69896992
/* Duplicates exit BBs, so that line numbers can be propagated to them */
69906993
static int
69916994
duplicate_exits_without_lineno(struct compiler *c);
@@ -7127,6 +7130,9 @@ assemble(struct compiler *c, int addNone)
71277130
if (duplicate_exits_without_lineno(c)) {
71287131
return NULL;
71297132
}
7133+
if (trim_unused_consts(c, &a, consts)) {
7134+
goto error;
7135+
}
71307136
propagate_line_numbers(&a);
71317137
guarantee_lineno_for_exits(&a, c->u->u_firstlineno);
71327138
/* Can't modify the bytecode after computing jump offsets. */
@@ -7809,6 +7815,33 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
78097815
return 0;
78107816
}
78117817

7818+
// Remove trailing unused constants.
7819+
static int
7820+
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts)
7821+
{
7822+
assert(PyList_CheckExact(consts));
7823+
7824+
// The first constant may be docstring; keep it always.
7825+
int max_const_index = 0;
7826+
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
7827+
for (int i = 0; i < b->b_iused; i++) {
7828+
if (b->b_instr[i].i_opcode == LOAD_CONST &&
7829+
b->b_instr[i].i_oparg > max_const_index) {
7830+
max_const_index = b->b_instr[i].i_oparg;
7831+
}
7832+
}
7833+
}
7834+
if (max_const_index+1 < PyList_GET_SIZE(consts)) {
7835+
//fprintf(stderr, "removing trailing consts: max=%d, size=%d\n",
7836+
// max_const_index, (int)PyList_GET_SIZE(consts));
7837+
if (PyList_SetSlice(consts, max_const_index+1,
7838+
PyList_GET_SIZE(consts), NULL) < 0) {
7839+
return 1;
7840+
}
7841+
}
7842+
return 0;
7843+
}
7844+
78127845
static inline int
78137846
is_exit_without_lineno(basicblock *b) {
78147847
return b->b_exit && b->b_instr[0].i_lineno < 0;

0 commit comments

Comments
 (0)