bpo-45056: Remove trailing unused constants from co_consts (GH-28109)

This commit is contained in:
Inada Naoki 2021-09-02 20:02:06 +09:00 committed by GitHub
parent 19ba2122ac
commit 55c4a92fc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 6369 additions and 6331 deletions

View file

@ -650,6 +650,17 @@ def test_merge_code_attrs(self):
self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
self.assertIs(f1.__code__.co_code, f2.__code__.co_code)
# Stripping unused constants is not a strict requirement for the
# Python semantics, it's a more an implementation detail.
@support.cpython_only
def test_strip_unused_consts(self):
# Python 3.10rc1 appended None to co_consts when None is not used
# at all. See bpo-45056.
def f1():
"docstring"
return 42
self.assertEqual(f1.__code__.co_consts, ("docstring", 42))
# This is a regression test for a CPython specific peephole optimizer
# implementation bug present in a few releases. It's assertion verifies
# that peephole optimization was actually done though that isn't an

View file

@ -702,10 +702,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
if sys.flags.optimize:
code_info_consts = "0: None"
else:
code_info_consts = (
"""0: 'Formatted details of methods, functions, or code.'
1: None"""
)
code_info_consts = "0: 'Formatted details of methods, functions, or code.'"
code_info_code_info = f"""\
Name: code_info
@ -828,7 +825,6 @@ def f(c=c):
Constants:
0: 0
1: 1
2: None
Names:
0: x"""

View file

@ -0,0 +1 @@
Compiler now removes trailing unused constants from co_consts.

View file

@ -7573,6 +7573,9 @@ normalize_basic_block(basicblock *bb);
static int
optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
static int
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts);
/* Duplicates exit BBs, so that line numbers can be propagated to them */
static int
duplicate_exits_without_lineno(struct compiler *c);
@ -7870,6 +7873,9 @@ assemble(struct compiler *c, int addNone)
if (duplicate_exits_without_lineno(c)) {
return NULL;
}
if (trim_unused_consts(c, &a, consts)) {
goto error;
}
propagate_line_numbers(&a);
guarantee_lineno_for_exits(&a, c->u->u_firstlineno);
int maxdepth = stackdepth(c);
@ -8599,6 +8605,33 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
return 0;
}
// Remove trailing unused constants.
static int
trim_unused_consts(struct compiler *c, struct assembler *a, PyObject *consts)
{
assert(PyList_CheckExact(consts));
// The first constant may be docstring; keep it always.
int max_const_index = 0;
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
for (int i = 0; i < b->b_iused; i++) {
if (b->b_instr[i].i_opcode == LOAD_CONST &&
b->b_instr[i].i_oparg > max_const_index) {
max_const_index = b->b_instr[i].i_oparg;
}
}
}
if (max_const_index+1 < PyList_GET_SIZE(consts)) {
//fprintf(stderr, "removing trailing consts: max=%d, size=%d\n",
// max_const_index, (int)PyList_GET_SIZE(consts));
if (PyList_SetSlice(consts, max_const_index+1,
PyList_GET_SIZE(consts), NULL) < 0) {
return 1;
}
}
return 0;
}
static inline int
is_exit_without_lineno(basicblock *b) {
return b->b_exit && b->b_instr[0].i_lineno < 0;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff