[3.10] bpo-45056: Remove trailing unused constants from co_consts (GH-28109) (GH-28125)

(cherry picked from commit 55c4a92fc1)

Co-authored-by: Inada Naoki <songofacandy@gmail.com>
This commit is contained in:
Łukasz Langa 2021-09-08 18:25:09 +02:00 committed by GitHub
parent 7538fe34d7
commit d41abe8970
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 4672 additions and 4634 deletions

View file

@ -648,6 +648,17 @@ if 1:
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

@ -689,10 +689,7 @@ class DisWithFileTests(DisTests):
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
@ -816,7 +813,6 @@ Flags: NOFREE
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

@ -6986,6 +6986,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);
@ -7127,6 +7130,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);
/* Can't modify the bytecode after computing jump offsets. */
@ -7809,6 +7815,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;

2941
Python/importlib.h generated

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