Skip to content

Commit b156578

Browse files
authored
gh-92031: Deoptimize Static Code at Finalization (GH-92039)
1 parent 04dc4b0 commit b156578

File tree

5 files changed

+225
-10
lines changed

5 files changed

+225
-10
lines changed

Include/internal/pycore_opcode.h

Lines changed: 186 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_embed.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,20 @@ def test_finalize_structseq(self):
343343
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
344344
self.assertEqual(out, 'Tests passed\n' * INIT_LOOPS)
345345

346+
@support.skip_if_pgo_task
347+
def test_quickened_static_code_gets_unquickened_at_Py_FINALIZE(self):
348+
# https://github.com/python/cpython/issues/92031
349+
code = """if 1:
350+
from importlib._bootstrap import _handle_fromlist
351+
import dis
352+
for name in dis.opmap:
353+
# quicken this frozen code object.
354+
_handle_fromlist(dis, [name], lambda *args: None)
355+
"""
356+
run = self.run_embedded_interpreter
357+
for i in range(50):
358+
out, err = run("test_repeated_init_exec", code, timeout=60)
359+
346360
def test_ucnhash_capi_reset(self):
347361
# bpo-47182: unicodeobject.c:ucnhash_capi was not reset on shutdown.
348362
code = "print('\\N{digit nine}')"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deoptimize statically-allocated code objects during ``Py_FINALIZE()`` so that future ``_PyCode_Quicken`` calls always start with unquickened code.

Objects/codeobject.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,23 +1350,29 @@ _PyCode_GetFreevars(PyCodeObject *co)
13501350
return get_localsplus_names(co, CO_FAST_FREE, co->co_nfreevars);
13511351
}
13521352

1353-
PyObject *
1354-
_PyCode_GetCode(PyCodeObject *co)
1353+
static void
1354+
deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len)
13551355
{
1356-
PyObject *code = PyBytes_FromStringAndSize(NULL, _PyCode_NBYTES(co));
1357-
if (code == NULL) {
1358-
return NULL;
1359-
}
1360-
_Py_CODEUNIT *instructions = (_Py_CODEUNIT *)PyBytes_AS_STRING(code);
1361-
for (int i = 0; i < Py_SIZE(co); i++) {
1362-
_Py_CODEUNIT instruction = _PyCode_CODE(co)[i];
1363-
int opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)];
1356+
for (int i = 0; i < len; i++) {
1357+
_Py_CODEUNIT instruction = instructions[i];
1358+
int opcode = _PyOpcode_Original[_Py_OPCODE(instruction)];
13641359
int caches = _PyOpcode_Caches[opcode];
13651360
instructions[i] = _Py_MAKECODEUNIT(opcode, _Py_OPARG(instruction));
13661361
while (caches--) {
13671362
instructions[++i] = _Py_MAKECODEUNIT(CACHE, 0);
13681363
}
13691364
}
1365+
}
1366+
1367+
PyObject *
1368+
_PyCode_GetCode(PyCodeObject *co)
1369+
{
1370+
PyObject *code = PyBytes_FromStringAndSize((const char *)_PyCode_CODE(co),
1371+
_PyCode_NBYTES(co));
1372+
if (code == NULL) {
1373+
return NULL;
1374+
}
1375+
deopt_code((_Py_CODEUNIT *)PyBytes_AS_STRING(code), Py_SIZE(co));
13701376
return code;
13711377
}
13721378

@@ -2076,6 +2082,7 @@ _PyStaticCode_Dealloc(PyCodeObject *co)
20762082
if (co->co_warmup == 0) {
20772083
_Py_QuickenedCount--;
20782084
}
2085+
deopt_code(_PyCode_CODE(co), Py_SIZE(co));
20792086
co->co_warmup = QUICKENING_INITIAL_WARMUP_VALUE;
20802087
PyMem_Free(co->co_extra);
20812088
co->co_extra = NULL;

Tools/scripts/generate_opcode_h.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
117117

118118
iobj.write("\nextern const uint8_t _PyOpcode_Caches[256];\n")
119119
iobj.write("\nextern const uint8_t _PyOpcode_Deopt[256];\n")
120+
iobj.write("\nextern const uint8_t _PyOpcode_Original[256];\n")
120121
iobj.write("\n#ifdef NEED_OPCODE_TABLES\n")
121122
write_int_array_from_ops("_PyOpcode_RelativeJump", opcode['hasjrel'], iobj)
122123
write_int_array_from_ops("_PyOpcode_Jump", opcode['hasjrel'] + opcode['hasjabs'], iobj)
@@ -137,6 +138,12 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
137138
for opt, deopt in sorted(deoptcodes.items()):
138139
iobj.write(f" [{opt}] = {deopt},\n")
139140
iobj.write("};\n")
141+
iobj.write("\nconst uint8_t _PyOpcode_Original[256] = {\n")
142+
for opt, deopt in sorted(deoptcodes.items()):
143+
if opt.startswith("EXTENDED_ARG"):
144+
deopt = "EXTENDED_ARG_QUICK"
145+
iobj.write(f" [{opt}] = {deopt},\n")
146+
iobj.write("};\n")
140147
iobj.write("#endif // NEED_OPCODE_TABLES\n")
141148

142149
fobj.write("\n")

0 commit comments

Comments
 (0)