Skip to content

Commit 55c4a92

Browse files
authored
bpo-45056: Remove trailing unused constants from co_consts (GH-28109)
1 parent 19ba212 commit 55c4a92

File tree

7 files changed

+6361
-6323
lines changed

7 files changed

+6361
-6323
lines changed

Lib/test/test_compile.py

+11
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,17 @@ def test_merge_code_attrs(self):
650650
self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
651651
self.assertIs(f1.__code__.co_code, f2.__code__.co_code)
652652

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

Lib/test/test_dis.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
702702
if sys.flags.optimize:
703703
code_info_consts = "0: None"
704704
else:
705-
code_info_consts = (
706-
"""0: 'Formatted details of methods, functions, or code.'
707-
1: None"""
708-
)
705+
code_info_consts = "0: 'Formatted details of methods, functions, or code.'"
709706

710707
code_info_code_info = f"""\
711708
Name: code_info
@@ -828,7 +825,6 @@ def f(c=c):
828825
Constants:
829826
0: 0
830827
1: 1
831-
2: None
832828
Names:
833829
0: x"""
834830

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compiler now removes trailing unused constants from co_consts.

Python/compile.c

+33
Original file line numberDiff line numberDiff line change
@@ -7573,6 +7573,9 @@ normalize_basic_block(basicblock *bb);
75737573
static int
75747574
optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
75757575

7576+
static int
7577+
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts);
7578+
75767579
/* Duplicates exit BBs, so that line numbers can be propagated to them */
75777580
static int
75787581
duplicate_exits_without_lineno(struct compiler *c);
@@ -7870,6 +7873,9 @@ assemble(struct compiler *c, int addNone)
78707873
if (duplicate_exits_without_lineno(c)) {
78717874
return NULL;
78727875
}
7876+
if (trim_unused_consts(c, &a, consts)) {
7877+
goto error;
7878+
}
78737879
propagate_line_numbers(&a);
78747880
guarantee_lineno_for_exits(&a, c->u->u_firstlineno);
78757881
int maxdepth = stackdepth(c);
@@ -8599,6 +8605,33 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
85998605
return 0;
86008606
}
86018607

8608+
// Remove trailing unused constants.
8609+
static int
8610+
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts)
8611+
{
8612+
assert(PyList_CheckExact(consts));
8613+
8614+
// The first constant may be docstring; keep it always.
8615+
int max_const_index = 0;
8616+
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
8617+
for (int i = 0; i < b->b_iused; i++) {
8618+
if (b->b_instr[i].i_opcode == LOAD_CONST &&
8619+
b->b_instr[i].i_oparg > max_const_index) {
8620+
max_const_index = b->b_instr[i].i_oparg;
8621+
}
8622+
}
8623+
}
8624+
if (max_const_index+1 < PyList_GET_SIZE(consts)) {
8625+
//fprintf(stderr, "removing trailing consts: max=%d, size=%d\n",
8626+
// max_const_index, (int)PyList_GET_SIZE(consts));
8627+
if (PyList_SetSlice(consts, max_const_index+1,
8628+
PyList_GET_SIZE(consts), NULL) < 0) {
8629+
return 1;
8630+
}
8631+
}
8632+
return 0;
8633+
}
8634+
86028635
static inline int
86038636
is_exit_without_lineno(basicblock *b) {
86048637
return b->b_exit && b->b_instr[0].i_lineno < 0;

Python/frozen_modules/importlib__bootstrap.h

+2,046-2,047
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)