From bcb25d60b1baf9348e73cbd2359342cea6009c36 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 30 Jan 2025 08:11:12 +0000 Subject: [PATCH 001/311] gh-129403: Fix `ValueError` messages in `asyncio.Barrier` and `threading.Barrier` (#129419) Co-authored-by: Peter Bierma --- Lib/asyncio/locks.py | 2 +- Lib/threading.py | 2 +- .../next/Library/2025-01-29-17-10-00.gh-issue-129403.314159.rst | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-29-17-10-00.gh-issue-129403.314159.rst diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py index f2f8b7ec858096..fa3a94764b507a 100644 --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -485,7 +485,7 @@ class Barrier(mixins._LoopBoundMixin): def __init__(self, parties): """Create a barrier, initialised to 'parties' tasks.""" if parties < 1: - raise ValueError('parties must be > 0') + raise ValueError('parties must be >= 1') self._cond = Condition() # notify all tasks when state changes diff --git a/Lib/threading.py b/Lib/threading.py index d7cc3ddc44516b..da9cdf0b09d83c 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -694,7 +694,7 @@ def __init__(self, parties, action=None, timeout=None): """ if parties < 1: - raise ValueError("parties must be > 0") + raise ValueError("parties must be >= 1") self._cond = Condition(Lock()) self._action = action self._timeout = timeout diff --git a/Misc/NEWS.d/next/Library/2025-01-29-17-10-00.gh-issue-129403.314159.rst b/Misc/NEWS.d/next/Library/2025-01-29-17-10-00.gh-issue-129403.314159.rst new file mode 100644 index 00000000000000..0c2bdd3136e3a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-29-17-10-00.gh-issue-129403.314159.rst @@ -0,0 +1 @@ +Corrected :exc:`ValueError` message for :class:`asyncio.Barrier` and :class:`threading.Barrier`. From 71aecc284efdf997939568a4167dbffe1a65b9bf Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 30 Jan 2025 00:31:54 -0800 Subject: [PATCH 002/311] remove type annotations from multiprocessing. (#129381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove type annotations from multiprocessing. One of them was technically invalid per typing specs... but since we're not checking these in the stdlib today lets elide them. https://discuss.python.org/t/static-type-annotations-in-cpython/65068/13 * use the actual comment style annotation format Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --------- Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --- Lib/multiprocessing/connection.py | 2 +- Lib/multiprocessing/synchronize.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index 710aba9685efda..d429212d447380 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -853,7 +853,7 @@ def PipeClient(address): _LEGACY_LENGTHS = (_MD5ONLY_MESSAGE_LENGTH, _MD5_DIGEST_LEN) -def _get_digest_name_and_payload(message: bytes) -> (str, bytes): +def _get_digest_name_and_payload(message): # type: (bytes) -> tuple[str, bytes] """Returns a digest name and the payload for a response hash. If a legacy protocol is detected based on the message length diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 4f72373c951abc..edd6c2543a7435 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -359,7 +359,7 @@ def wait(self, timeout=None): return True return False - def __repr__(self) -> str: + def __repr__(self): set_status = 'set' if self.is_set() else 'unset' return f"<{type(self).__qualname__} at {id(self):#x} {set_status}>" # From 9bc8c5fc0c8557b831bf47e6fe6ff85678cc8c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:11:37 +0100 Subject: [PATCH 003/311] gh-111178: fix UBSan failures in `Modules/_ssl/cert.c` (GH-129088) fix UBSan failures for `PySSLCertificate` --- Modules/_ssl/cert.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Modules/_ssl/cert.c b/Modules/_ssl/cert.c index bda66dc4d94ae6..c11ed8e3a282e6 100644 --- a/Modules/_ssl/cert.c +++ b/Modules/_ssl/cert.c @@ -153,10 +153,13 @@ _x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned lo * PySSLCertificate_Type */ +#define _PySSLCertificate_CAST(op) ((PySSLCertificate *)(op)) + static PyObject * -certificate_repr(PySSLCertificate *self) +certificate_repr(PyObject *op) { PyObject *osubject, *result; + PySSLCertificate *self = _PySSLCertificate_CAST(op); /* subject string is ASCII encoded, UTF-8 chars are quoted */ osubject = _x509name_print( @@ -176,8 +179,9 @@ certificate_repr(PySSLCertificate *self) } static Py_hash_t -certificate_hash(PySSLCertificate *self) +certificate_hash(PyObject *op) { + PySSLCertificate *self = _PySSLCertificate_CAST(op); if (self->hash == (Py_hash_t)-1) { unsigned long hash; hash = X509_subject_name_hash(self->cert); @@ -191,19 +195,20 @@ certificate_hash(PySSLCertificate *self) } static PyObject * -certificate_richcompare(PySSLCertificate *self, PyObject *other, int op) +certificate_richcompare(PyObject *lhs, PyObject *rhs, int op) { int cmp; + PySSLCertificate *self = _PySSLCertificate_CAST(lhs); _sslmodulestate *state = get_state_cert(self); - if (Py_TYPE(other) != state->PySSLCertificate_Type) { + if (Py_TYPE(rhs) != state->PySSLCertificate_Type) { Py_RETURN_NOTIMPLEMENTED; } /* only support == and != */ if ((op != Py_EQ) && (op != Py_NE)) { Py_RETURN_NOTIMPLEMENTED; } - cmp = X509_cmp(self->cert, ((PySSLCertificate*)other)->cert); + cmp = X509_cmp(self->cert, ((PySSLCertificate*)rhs)->cert); if (((op == Py_EQ) && (cmp == 0)) || ((op == Py_NE) && (cmp != 0))) { Py_RETURN_TRUE; } else { @@ -212,11 +217,12 @@ certificate_richcompare(PySSLCertificate *self, PyObject *other, int op) } static void -certificate_dealloc(PySSLCertificate *self) +certificate_dealloc(PyObject *op) { + PySSLCertificate *self = _PySSLCertificate_CAST(op); PyTypeObject *tp = Py_TYPE(self); X509_free(self->cert); - Py_TYPE(self)->tp_free(self); + (void)Py_TYPE(self)->tp_free(self); Py_DECREF(tp); } From 652f66ac386dad5992c6f7c494871907272503f8 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:16:05 +0000 Subject: [PATCH 004/311] gh-129438: Update ``--enable-experimental-jit`` section with install requirements (#129450) Add a note to the `JIT` docs that building CPython with `JIT` requires Python 3.11 or newer. Co-authored-by: Brandt Bucher --- Doc/using/configure.rst | 4 ++++ Tools/jit/README.md | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index b58532acc4b322..629859e36cb654 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -311,6 +311,10 @@ General Options By convention, ``--enable-experimental-jit`` is a shorthand for ``--enable-experimental-jit=yes``. + .. note:: + + When building CPython with JIT enabled, ensure that your system has Python 3.11 or later installed. + .. versionadded:: 3.13 .. option:: PKG_CONFIG diff --git a/Tools/jit/README.md b/Tools/jit/README.md index 801c64e4059ccc..4107265754f6ec 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -3,6 +3,8 @@ The JIT Compiler This version of CPython can be built with an experimental just-in-time compiler[^pep-744]. While most everything you already know about building and using CPython is unchanged, you will probably need to install a compatible version of LLVM first. +Python 3.11 or newer is required to build the JIT. + ## Installing LLVM The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon). @@ -54,7 +56,7 @@ For `PCbuild`-based builds, pass the new `--experimental-jit` option to `build.b For all other builds, pass the new `--enable-experimental-jit` option to `configure`. -Otherwise, just configure and build as you normally would. Cross-compiling "just works", since the JIT is built for the host platform. +Otherwise, just configure and build as you normally would. Cross-compiling "just works", since the JIT is built for the host platform. The JIT can also be enabled or disabled using the `PYTHON_JIT` environment variable, even on builds where it is enabled or disabled by default. More details about configuring CPython with the JIT and optional values for `--enable-experimental-jit` can be found [here](https://docs.python.org/dev/whatsnew/3.13.html#experimental-jit-compiler). From 6c63afc3be9dd612f587fe7869c1f317d5ea37cc Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 30 Jan 2025 13:57:55 +0300 Subject: [PATCH 005/311] gh-129467: Fix compiler warning in mpdecimal word_to_string() (#116346) Turn off false-positive -Wstringop-overflow in word_to_string(). --- Misc/sbom.spdx.json | 4 ++-- Modules/_decimal/libmpdec/io.c | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index b4d785f65639a5..316c266b7e4fd6 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -1280,11 +1280,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9dcb50e3f9c3245972731be5da0b28e7583198d9" + "checksumValue": "5d6fdd98730584f74f7b731da6e488fe234504b3" }, { "algorithm": "SHA256", - "checksumValue": "7cac49fef5e9d952ec9390bf81c54d83f1b5da32fdf76091c2f0770ed943b7fe" + "checksumValue": "d74f365463166891f62e1326d22b2d39d865776b7ea5e0df2aea5eede4d85b0f" } ], "fileName": "Modules/_decimal/libmpdec/io.c" diff --git a/Modules/_decimal/libmpdec/io.c b/Modules/_decimal/libmpdec/io.c index 4e95b8964c8e5d..bdcca001659bc0 100644 --- a/Modules/_decimal/libmpdec/io.c +++ b/Modules/_decimal/libmpdec/io.c @@ -347,6 +347,10 @@ mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status) or the location of a decimal point. */ #define EXTRACT_DIGIT(s, x, d, dot) \ if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 12 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif static inline char * word_to_string(char *s, mpd_uint_t x, int n, char *dot) { @@ -378,6 +382,9 @@ word_to_string(char *s, mpd_uint_t x, int n, char *dot) *s = '\0'; return s; } +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 12 + #pragma GCC diagnostic pop +#endif /* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */ static inline char * From f927204f64b3f8dbecec784e05bc8e25d2a78b2e Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 30 Jan 2025 03:14:23 -0800 Subject: [PATCH 006/311] gh-129005: Align FileIO.readall() allocation (#129458) Both now use a pre-allocated buffer of length `bufsize`, fill it using a readinto(), and have matching "expand buffer" logic. On my machine this takes: `./python -m test -M8g -uall test_largefile -m test_large_read -v` from ~3.7 seconds to ~3.4 seconds. --- Lib/_pyio.py | 27 ++++++++++++------- ...-01-28-21-22-44.gh-issue-129005.h57i9j.rst | 2 ++ 2 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 023478aa78c6a0..76a27910da4d5f 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1674,22 +1674,31 @@ def readall(self): except OSError: pass - result = bytearray() + result = bytearray(bufsize) + bytes_read = 0 while True: - if len(result) >= bufsize: - bufsize = len(result) - bufsize += max(bufsize, DEFAULT_BUFFER_SIZE) - n = bufsize - len(result) + if bytes_read >= bufsize: + # Parallels _io/fileio.c new_buffersize + if bufsize > 65536: + addend = bufsize >> 3 + else: + addend = bufsize + 256 + if addend < DEFAULT_BUFFER_SIZE: + addend = DEFAULT_BUFFER_SIZE + bufsize += addend + result[bytes_read:bufsize] = b'\0' + assert bufsize - bytes_read > 0, "Should always try and read at least one byte" try: - chunk = os.read(self._fd, n) + n = os.readinto(self._fd, memoryview(result)[bytes_read:]) except BlockingIOError: - if result: + if bytes_read > 0: break return None - if not chunk: # reached the end of the file + if n == 0: # reached the end of the file break - result += chunk + bytes_read += n + del result[bytes_read:] return bytes(result) def readinto(self, buffer): diff --git a/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst b/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst new file mode 100644 index 00000000000000..c76fb05e196f87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst @@ -0,0 +1,2 @@ +``_pyio.FileIO.readall()`` now allocates, resizes, and fills a data buffer using +the same algorithm ``_io.FileIO.readall()`` uses. From 3bebe46d3413195ee18c5c9ada83a35d4fd1d0e7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jan 2025 12:17:29 +0100 Subject: [PATCH 007/311] gh-128911: Add PyImport_ImportModuleAttr() function (#128912) Add PyImport_ImportModuleAttr() and PyImport_ImportModuleAttrString() functions. * Add unit tests. * Replace _PyImport_GetModuleAttr() with PyImport_ImportModuleAttr(). * Replace _PyImport_GetModuleAttrString() with PyImport_ImportModuleAttrString(). * Remove "pycore_import.h" includes, no longer needed. --- Doc/c-api/import.rst | 21 +++++++ Doc/data/refcounts.dat | 8 +++ Doc/whatsnew/3.14.rst | 5 ++ Include/cpython/import.h | 7 +++ Include/internal/pycore_import.h | 6 -- Lib/test/test_capi/test_import.py | 56 ++++++++++++++++++- ...-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst | 3 + Modules/Setup.stdlib.in | 2 +- Modules/_ctypes/callbacks.c | 2 +- Modules/_ctypes/stgdict.c | 2 +- Modules/_cursesmodule.c | 2 +- Modules/_datetimemodule.c | 6 +- Modules/_decimal/_decimal.c | 2 +- Modules/_elementtree.c | 5 +- Modules/_json.c | 2 +- Modules/_lsprof.c | 6 +- Modules/_operator.c | 2 +- Modules/_pickle.c | 4 +- Modules/_sqlite/connection.c | 3 +- Modules/_sqlite/module.c | 4 +- Modules/_sre/sre.c | 2 +- Modules/_testcapi/import.c | 44 +++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 3 + Modules/_zoneinfo.c | 8 +-- Modules/arraymodule.c | 4 +- Modules/cjkcodecs/cjkcodecs.h | 3 +- Modules/faulthandler.c | 2 +- Modules/posixmodule.c | 2 +- Modules/selectmodule.c | 3 +- Modules/timemodule.c | 2 +- Objects/abstract.c | 2 +- Objects/fileobject.c | 4 +- Objects/memoryobject.c | 2 +- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + Parser/pegen.c | 2 +- Parser/tokenizer/file_tokenizer.c | 2 +- Python/import.c | 8 +-- Python/pylifecycle.c | 4 +- 40 files changed, 194 insertions(+), 56 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst create mode 100644 Modules/_testcapi/import.c diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 6e48644c8fef8b..1cab3ce3061ec9 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -325,3 +325,24 @@ Importing Modules If Python is initialized multiple times, :c:func:`PyImport_AppendInittab` or :c:func:`PyImport_ExtendInittab` must be called before each Python initialization. + + +.. c:function:: PyObject* PyImport_ImportModuleAttr(PyObject *mod_name, PyObject *attr_name) + + Import the module *mod_name* and get its attribute *attr_name*. + + Names must be Python :class:`str` objects. + + Helper function combining :c:func:`PyImport_Import` and + :c:func:`PyObject_GetAttr`. For example, it can raise :exc:`ImportError` if + the module is not found, and :exc:`AttributeError` if the attribute doesn't + exist. + + .. versionadded:: 3.14 + +.. c:function:: PyObject* PyImport_ImportModuleAttrString(const char *mod_name, const char *attr_name) + + Similar to :c:func:`PyImport_ImportModuleAttr`, but names are UTF-8 encoded + strings instead of Python :class:`str` objects. + + .. versionadded:: 3.14 diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index e78754e24e23d8..d709d2d91b0eb0 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -3052,3 +3052,11 @@ _Py_c_quot:Py_complex:divisor:: _Py_c_sum:Py_complex::: _Py_c_sum:Py_complex:left:: _Py_c_sum:Py_complex:right:: + +PyImport_ImportModuleAttr:PyObject*::+1: +PyImport_ImportModuleAttr:PyObject*:mod_name:0: +PyImport_ImportModuleAttr:PyObject*:attr_name:0: + +PyImport_ImportModuleAttrString:PyObject*::+1: +PyImport_ImportModuleAttrString:const char *:mod_name:: +PyImport_ImportModuleAttrString:const char *:attr_name:: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2c10d7fefd44ab..8d4cb94ae2d805 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1322,6 +1322,11 @@ New features * Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is :term:`immortal`, for debugging purposes. +* Add :c:func:`PyImport_ImportModuleAttr` and + :c:func:`PyImport_ImportModuleAttrString` helper functions to import a module + and get an attribute of the module. + (Contributed by Victor Stinner in :gh:`128911`.) + Limited C API changes --------------------- diff --git a/Include/cpython/import.h b/Include/cpython/import.h index 0fd61c28cafa0e..0ce0b1ee6cce2a 100644 --- a/Include/cpython/import.h +++ b/Include/cpython/import.h @@ -21,3 +21,10 @@ struct _frozen { collection of frozen modules: */ PyAPI_DATA(const struct _frozen *) PyImport_FrozenModules; + +PyAPI_FUNC(PyObject*) PyImport_ImportModuleAttr( + PyObject *mod_name, + PyObject *attr_name); +PyAPI_FUNC(PyObject*) PyImport_ImportModuleAttrString( + const char *mod_name, + const char *attr_name); diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 318c712bdfa174..5fe60df0a92fbc 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -31,12 +31,6 @@ extern int _PyImport_FixupBuiltin( PyObject *modules ); -// Export for many shared extensions, like '_json' -PyAPI_FUNC(PyObject*) _PyImport_GetModuleAttr(PyObject *, PyObject *); - -// Export for many shared extensions, like '_datetime' -PyAPI_FUNC(PyObject*) _PyImport_GetModuleAttrString(const char *, const char *); - struct _import_runtime_state { /* The builtin modules (defined in config.c). */ diff --git a/Lib/test/test_capi/test_import.py b/Lib/test/test_capi/test_import.py index 94f96728d9174b..25136624ca4ed9 100644 --- a/Lib/test/test_capi/test_import.py +++ b/Lib/test/test_capi/test_import.py @@ -7,6 +7,7 @@ from test.support import import_helper from test.support.warnings_helper import check_warnings +_testcapi = import_helper.import_module('_testcapi') _testlimitedcapi = import_helper.import_module('_testlimitedcapi') NULL = None @@ -148,7 +149,7 @@ def check_frozen_import(self, import_frozen_module): try: self.assertEqual(import_frozen_module('zipimport'), 1) - # import zipimport again + # import zipimport again self.assertEqual(import_frozen_module('zipimport'), 1) finally: sys.modules['zipimport'] = old_zipimport @@ -317,6 +318,59 @@ def test_executecodemoduleobject(self): # CRASHES execute_code_func(NULL, code, NULL, NULL) # CRASHES execute_code_func(name, NULL, NULL, NULL) + def check_importmoduleattr(self, importmoduleattr): + self.assertIs(importmoduleattr('sys', 'argv'), sys.argv) + self.assertIs(importmoduleattr('types', 'ModuleType'), types.ModuleType) + + # module name containing a dot + attr = importmoduleattr('email.message', 'Message') + from email.message import Message + self.assertIs(attr, Message) + + with self.assertRaises(ImportError): + # nonexistent module + importmoduleattr('nonexistentmodule', 'attr') + with self.assertRaises(AttributeError): + # nonexistent attribute + importmoduleattr('sys', 'nonexistentattr') + with self.assertRaises(AttributeError): + # attribute name containing a dot + importmoduleattr('sys', 'implementation.name') + + def test_importmoduleattr(self): + # Test PyImport_ImportModuleAttr() + importmoduleattr = _testcapi.PyImport_ImportModuleAttr + self.check_importmoduleattr(importmoduleattr) + + # Invalid module name type + for mod_name in (object(), 123, b'bytes'): + with self.subTest(mod_name=mod_name): + with self.assertRaises(TypeError): + importmoduleattr(mod_name, "attr") + + # Invalid attribute name type + for attr_name in (object(), 123, b'bytes'): + with self.subTest(attr_name=attr_name): + with self.assertRaises(TypeError): + importmoduleattr("sys", attr_name) + + with self.assertRaises(SystemError): + importmoduleattr(NULL, "argv") + # CRASHES importmoduleattr("sys", NULL) + + def test_importmoduleattrstring(self): + # Test PyImport_ImportModuleAttrString() + importmoduleattr = _testcapi.PyImport_ImportModuleAttrString + self.check_importmoduleattr(importmoduleattr) + + with self.assertRaises(UnicodeDecodeError): + importmoduleattr(b"sys\xff", "argv") + with self.assertRaises(UnicodeDecodeError): + importmoduleattr("sys", b"argv\xff") + + # CRASHES importmoduleattr(NULL, "argv") + # CRASHES importmoduleattr("sys", NULL) + # TODO: test PyImport_GetImporter() # TODO: test PyImport_ReloadModule() # TODO: test PyImport_ExtendInittab() diff --git a/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst b/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst new file mode 100644 index 00000000000000..d32cd00cd5d605 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-16-12-47-01.gh-issue-128911.mHVJ4x.rst @@ -0,0 +1,3 @@ +Add :c:func:`PyImport_ImportModuleAttr` and :c:func:`PyImport_ImportModuleAttrString` +helper functions to import a module and get an attribute of the module. Patch +by Victor Stinner. diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 6b6a8ae57a5119..563bbc1bda6223 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 89c0749a093765..591206a78035c5 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -492,7 +492,7 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) if (context == NULL) context = PyUnicode_InternFromString("_ctypes.DllGetClassObject"); - func = _PyImport_GetModuleAttrString("ctypes", "DllGetClassObject"); + func = PyImport_ImportModuleAttrString("ctypes", "DllGetClassObject"); if (!func) { PyErr_WriteUnraisable(context ? context : Py_None); /* There has been a warning before about this already */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 5ca5b62427600d..d63a46a3bc23d2 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -257,7 +257,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct goto error; } - PyObject *layout_func = _PyImport_GetModuleAttrString("ctypes._layout", + PyObject *layout_func = PyImport_ImportModuleAttrString("ctypes._layout", "get_layout"); if (!layout_func) { goto error; diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index c6835738348ff9..7213a5be07de4b 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -226,7 +226,7 @@ _PyCursesCheckFunction(int called, const char *funcname) if (called == TRUE) { return 1; } - PyObject *exc = _PyImport_GetModuleAttrString("_curses", "error"); + PyObject *exc = PyImport_ImportModuleAttrString("_curses", "error"); if (exc != NULL) { PyErr_Format(exc, "must call %s() first", funcname); Py_DECREF(exc); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index ff2e6d6a098ad9..a486af7833c2c6 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1839,7 +1839,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, assert(object && format && timetuple); assert(PyUnicode_Check(format)); - PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); + PyObject *strftime = PyImport_ImportModuleAttrString("time", "strftime"); if (strftime == NULL) { return NULL; } @@ -2022,7 +2022,7 @@ static PyObject * time_time(void) { PyObject *result = NULL; - PyObject *time = _PyImport_GetModuleAttrString("time", "time"); + PyObject *time = PyImport_ImportModuleAttrString("time", "time"); if (time != NULL) { result = PyObject_CallNoArgs(time); @@ -2040,7 +2040,7 @@ build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag) PyObject *struct_time; PyObject *result; - struct_time = _PyImport_GetModuleAttrString("time", "struct_time"); + struct_time = PyImport_ImportModuleAttrString("time", "struct_time"); if (struct_time == NULL) { return NULL; } diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 78cf6b1426493b..3dcb3e9870c8a4 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -3474,7 +3474,7 @@ pydec_format(PyObject *dec, PyObject *context, PyObject *fmt, decimal_state *sta PyObject *u; if (state->PyDecimal == NULL) { - state->PyDecimal = _PyImport_GetModuleAttrString("_pydecimal", "Decimal"); + state->PyDecimal = PyImport_ImportModuleAttrString("_pydecimal", "Decimal"); if (state->PyDecimal == NULL) { return NULL; } diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 355f322d304c2f..b5b0b82571f882 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -16,7 +16,6 @@ #endif #include "Python.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_pyhash.h" // _Py_HashSecret #include // offsetof() @@ -4393,7 +4392,7 @@ module_exec(PyObject *m) CREATE_TYPE(m, st->Element_Type, &element_spec); CREATE_TYPE(m, st->XMLParser_Type, &xmlparser_spec); - st->deepcopy_obj = _PyImport_GetModuleAttrString("copy", "deepcopy"); + st->deepcopy_obj = PyImport_ImportModuleAttrString("copy", "deepcopy"); if (st->deepcopy_obj == NULL) { goto error; } @@ -4403,7 +4402,7 @@ module_exec(PyObject *m) goto error; /* link against pyexpat */ - if (!(st->expat_capsule = _PyImport_GetModuleAttrString("pyexpat", "expat_CAPI"))) + if (!(st->expat_capsule = PyImport_ImportModuleAttrString("pyexpat", "expat_CAPI"))) goto error; if (!(st->expat_capi = PyCapsule_GetPointer(st->expat_capsule, PyExpat_CAPSULE_NAME))) goto error; diff --git a/Modules/_json.c b/Modules/_json.c index 31a5e935e13ad9..5532e252819bbd 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -302,7 +302,7 @@ raise_errmsg(const char *msg, PyObject *s, Py_ssize_t end) /* Use JSONDecodeError exception to raise a nice looking ValueError subclass */ _Py_DECLARE_STR(json_decoder, "json.decoder"); PyObject *JSONDecodeError = - _PyImport_GetModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError)); + PyImport_ImportModuleAttr(&_Py_STR(json_decoder), &_Py_ID(JSONDecodeError)); if (JSONDecodeError == NULL) { return; } diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 51ad9fc7da8492..29d9e5b6ef2cbe 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -775,7 +775,7 @@ _lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, return NULL; } - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return NULL; } @@ -857,7 +857,7 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self) } if (self->flags & POF_ENABLED) { PyObject* result = NULL; - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return NULL; @@ -973,7 +973,7 @@ profiler_init_impl(ProfilerObject *self, PyObject *timer, double timeunit, Py_XSETREF(self->externalTimer, Py_XNewRef(timer)); self->tool_id = PY_MONITORING_PROFILER_ID; - PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring"); + PyObject* monitoring = PyImport_ImportModuleAttrString("sys", "monitoring"); if (!monitoring) { return -1; } diff --git a/Modules/_operator.c b/Modules/_operator.c index ce3ef015710223..59987b8f143da2 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1868,7 +1868,7 @@ methodcaller_reduce(methodcallerobject *mc, PyObject *Py_UNUSED(ignored)) PyObject *constructor; PyObject *newargs[2]; - partial = _PyImport_GetModuleAttrString("functools", "partial"); + partial = PyImport_ImportModuleAttrString("functools", "partial"); if (!partial) return NULL; diff --git a/Modules/_pickle.c b/Modules/_pickle.c index a6cfb2deeb23c1..5641f93391c551 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -362,7 +362,7 @@ _Pickle_InitState(PickleState *st) } Py_CLEAR(compat_pickle); - st->codecs_encode = _PyImport_GetModuleAttrString("codecs", "encode"); + st->codecs_encode = PyImport_ImportModuleAttrString("codecs", "encode"); if (st->codecs_encode == NULL) { goto error; } @@ -373,7 +373,7 @@ _Pickle_InitState(PickleState *st) goto error; } - st->partial = _PyImport_GetModuleAttrString("functools", "partial"); + st->partial = PyImport_ImportModuleAttrString("functools", "partial"); if (!st->partial) goto error; diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 62598ecc864120..0c98f5065ee303 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -34,7 +34,6 @@ #include "prepare_protocol.h" #include "util.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() @@ -2000,7 +1999,7 @@ pysqlite_connection_iterdump_impl(pysqlite_Connection *self, return NULL; } - PyObject *iterdump = _PyImport_GetModuleAttrString(MODULE_NAME ".dump", "_iterdump"); + PyObject *iterdump = PyImport_ImportModuleAttrString(MODULE_NAME ".dump", "_iterdump"); if (!iterdump) { if (!PyErr_Occurred()) { PyErr_SetString(self->OperationalError, diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 698e81d9b897d0..73d55fb44e2e15 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -33,8 +33,6 @@ #include "row.h" #include "blob.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() - #if SQLITE_VERSION_NUMBER < 3015002 #error "SQLite 3.15.2 or higher required" #endif @@ -234,7 +232,7 @@ static int load_functools_lru_cache(PyObject *module) { pysqlite_state *state = pysqlite_get_state(module); - state->lru_cache = _PyImport_GetModuleAttrString("functools", "lru_cache"); + state->lru_cache = PyImport_ImportModuleAttrString("functools", "lru_cache"); if (state->lru_cache == NULL) { return -1; } diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index d0025dd21e045b..0d8d4843d33c1b 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -1169,7 +1169,7 @@ compile_template(_sremodulestate *module_state, /* delegate to Python code */ PyObject *func = module_state->compile_template; if (func == NULL) { - func = _PyImport_GetModuleAttrString("re", "_compile_template"); + func = PyImport_ImportModuleAttrString("re", "_compile_template"); if (func == NULL) { return NULL; } diff --git a/Modules/_testcapi/import.c b/Modules/_testcapi/import.c new file mode 100644 index 00000000000000..27d37498f3cd83 --- /dev/null +++ b/Modules/_testcapi/import.c @@ -0,0 +1,44 @@ +#include "parts.h" +#include "util.h" + +// Test PyImport_ImportModuleAttr() +static PyObject * +pyimport_importmoduleattr(PyObject *self, PyObject *args) +{ + PyObject *mod_name, *attr_name; + if (!PyArg_ParseTuple(args, "OO", &mod_name, &attr_name)) { + return NULL; + } + NULLABLE(mod_name); + NULLABLE(attr_name); + + return PyImport_ImportModuleAttr(mod_name, attr_name); +} + + +// Test PyImport_ImportModuleAttrString() +static PyObject * +pyimport_importmoduleattrstring(PyObject *self, PyObject *args) +{ + const char *mod_name, *attr_name; + Py_ssize_t len; + if (!PyArg_ParseTuple(args, "z#z#", &mod_name, &len, &attr_name, &len)) { + return NULL; + } + + return PyImport_ImportModuleAttrString(mod_name, attr_name); +} + + +static PyMethodDef test_methods[] = { + {"PyImport_ImportModuleAttr", pyimport_importmoduleattr, METH_VARARGS}, + {"PyImport_ImportModuleAttrString", pyimport_importmoduleattrstring, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Import(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} + diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 65ba77596c760e..792552d8097312 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -61,5 +61,6 @@ int _PyTestCapi_Init_Time(PyObject *module); int _PyTestCapi_Init_Monitoring(PyObject *module); int _PyTestCapi_Init_Object(PyObject *module); int _PyTestCapi_Init_Config(PyObject *mod); +int _PyTestCapi_Init_Import(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c405a352ed74a1..4da23ba82d5756 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4401,6 +4401,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Config(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Import(m) < 0) { + return NULL; + } PyState_AddModule(m, &_testcapimodule); return m; diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index c5292575c22f23..1fcea9ce8b1261 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -782,7 +782,7 @@ zoneinfo_reduce(PyObject *obj_self, PyObject *unused) if (self->source == SOURCE_FILE) { // Objects constructed from files cannot be pickled. PyObject *pickle_error = - _PyImport_GetModuleAttrString("pickle", "PicklingError"); + PyImport_ImportModuleAttrString("pickle", "PicklingError"); if (pickle_error == NULL) { return NULL; } @@ -2554,7 +2554,7 @@ static PyObject * new_weak_cache(void) { PyObject *WeakValueDictionary = - _PyImport_GetModuleAttrString("weakref", "WeakValueDictionary"); + PyImport_ImportModuleAttrString("weakref", "WeakValueDictionary"); if (WeakValueDictionary == NULL) { return NULL; } @@ -2732,12 +2732,12 @@ zoneinfomodule_exec(PyObject *m) /* Populate imports */ state->_tzpath_find_tzfile = - _PyImport_GetModuleAttrString("zoneinfo._tzpath", "find_tzfile"); + PyImport_ImportModuleAttrString("zoneinfo._tzpath", "find_tzfile"); if (state->_tzpath_find_tzfile == NULL) { goto error; } - state->io_open = _PyImport_GetModuleAttrString("io", "open"); + state->io_open = PyImport_ImportModuleAttrString("io", "open"); if (state->io_open == NULL) { goto error; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 28c6a0ae05c598..dc1729a7a3a558 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2285,7 +2285,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, assert(state != NULL); if (state->array_reconstructor == NULL) { - state->array_reconstructor = _PyImport_GetModuleAttrString( + state->array_reconstructor = PyImport_ImportModuleAttrString( "array", "_array_reconstructor"); if (state->array_reconstructor == NULL) { return NULL; @@ -3206,7 +3206,7 @@ array_modexec(PyObject *m) return -1; } - PyObject *mutablesequence = _PyImport_GetModuleAttrString( + PyObject *mutablesequence = PyImport_ImportModuleAttrString( "collections.abc", "MutableSequence"); if (!mutablesequence) { Py_DECREF((PyObject *)state->ArrayType); diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index 2b446ba5226ac0..737a7a042753a9 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -13,7 +13,6 @@ #include "Python.h" #include "multibytecodec.h" -#include "pycore_import.h" // _PyImport_GetModuleAttrString() /* a unicode "undefined" code point */ @@ -299,7 +298,7 @@ add_codecs(cjkcodecs_module_state *st) \ static PyObject * getmultibytecodec(void) { - return _PyImport_GetModuleAttrString("_multibytecodec", "__create_codec"); + return PyImport_ImportModuleAttrString("_multibytecodec", "__create_codec"); } static void diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index b44b964b29484b..a15ced22677ab7 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1346,7 +1346,7 @@ PyInit_faulthandler(void) static int faulthandler_init_enable(void) { - PyObject *enable = _PyImport_GetModuleAttrString("faulthandler", "enable"); + PyObject *enable = PyImport_ImportModuleAttrString("faulthandler", "enable"); if (enable == NULL) { return -1; } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a35a848a7ca4b8..6d9b365ea9ceb2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9877,7 +9877,7 @@ wait_helper(PyObject *module, pid_t pid, int status, struct rusage *ru) memset(ru, 0, sizeof(*ru)); } - struct_rusage = _PyImport_GetModuleAttrString("resource", "struct_rusage"); + struct_rusage = PyImport_ImportModuleAttrString("resource", "struct_rusage"); if (struct_rusage == NULL) return NULL; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index e14e114a6dafd0..c75e2ba28c5b4e 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -14,7 +14,6 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_set_inheritable() -#include "pycore_import.h" // _PyImport_GetModuleAttrString() #include "pycore_time.h" // _PyTime_FromSecondsObject() #include @@ -1996,7 +1995,7 @@ kqueue_tracking_init(PyObject *module) { // Register a callback to invalidate kqueues with open fds after fork. PyObject *register_at_fork = NULL, *cb = NULL, *args = NULL, *kwargs = NULL, *result = NULL; - register_at_fork = _PyImport_GetModuleAttrString("posix", + register_at_fork = PyImport_ImportModuleAttrString("posix", "register_at_fork"); if (register_at_fork == NULL) { goto finally; diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 5d0cd52a93a2d3..8d2cbff662b9a3 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -979,7 +979,7 @@ time_strptime(PyObject *self, PyObject *args) { PyObject *func, *result; - func = _PyImport_GetModuleAttrString("_strptime", "_strptime_time"); + func = PyImport_ImportModuleAttrString("_strptime", "_strptime_time"); if (!func) { return NULL; } diff --git a/Objects/abstract.c b/Objects/abstract.c index c92ef10aa79648..db7b9263711f68 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -583,7 +583,7 @@ PyBuffer_SizeFromFormat(const char *format) PyObject *fmt = NULL; Py_ssize_t itemsize = -1; - calcsize = _PyImport_GetModuleAttrString("struct", "calcsize"); + calcsize = PyImport_ImportModuleAttrString("struct", "calcsize"); if (calcsize == NULL) { goto done; } diff --git a/Objects/fileobject.c b/Objects/fileobject.c index c377d1bb28b56f..7025b5bcffc1c8 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -34,7 +34,7 @@ PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const c PyObject *open, *stream; /* import _io in case we are being used to open io.py */ - open = _PyImport_GetModuleAttrString("_io", "open"); + open = PyImport_ImportModuleAttrString("_io", "open"); if (open == NULL) return NULL; stream = PyObject_CallFunction(open, "isisssO", fd, mode, @@ -506,7 +506,7 @@ PyFile_OpenCodeObject(PyObject *path) if (hook) { f = hook(path, _PyRuntime.open_code_userdata); } else { - PyObject *open = _PyImport_GetModuleAttrString("_io", "open"); + PyObject *open = PyImport_ImportModuleAttrString("_io", "open"); if (open) { f = PyObject_CallFunction(open, "Os", path, "rb"); Py_DECREF(open); diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index ea4d24dc690768..331363b2babbef 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2083,7 +2083,7 @@ struct_get_unpacker(const char *fmt, Py_ssize_t itemsize) PyObject *format = NULL; struct unpacker *x = NULL; - Struct = _PyImport_GetModuleAttrString("struct", "Struct"); + Struct = PyImport_ImportModuleAttrString("struct", "Struct"); if (Struct == NULL) return NULL; diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index c41235eac356af..733bb69acb16e2 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -127,6 +127,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 0a00df655deefc..e8ddd537674a9c 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -114,6 +114,9 @@ Source Files + + Source Files + diff --git a/Parser/pegen.c b/Parser/pegen.c index bb98e7b184a4dc..83b0022e47d619 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -111,7 +111,7 @@ init_normalization(Parser *p) if (p->normalize) { return 1; } - p->normalize = _PyImport_GetModuleAttrString("unicodedata", "normalize"); + p->normalize = PyImport_ImportModuleAttrString("unicodedata", "normalize"); if (!p->normalize) { return 0; diff --git a/Parser/tokenizer/file_tokenizer.c b/Parser/tokenizer/file_tokenizer.c index 2750527da484aa..efe9fb9b56abaf 100644 --- a/Parser/tokenizer/file_tokenizer.c +++ b/Parser/tokenizer/file_tokenizer.c @@ -158,7 +158,7 @@ fp_setreadl(struct tok_state *tok, const char* enc) return 0; } - open = _PyImport_GetModuleAttrString("io", "open"); + open = PyImport_ImportModuleAttrString("io", "open"); if (open == NULL) { return 0; } diff --git a/Python/import.c b/Python/import.c index b3648e24d0e064..dd7a0b4b1ed8de 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4111,7 +4111,7 @@ init_zipimport(PyThreadState *tstate, int verbose) PySys_WriteStderr("# installing zipimport hook\n"); } - PyObject *zipimporter = _PyImport_GetModuleAttrString("zipimport", "zipimporter"); + PyObject *zipimporter = PyImport_ImportModuleAttrString("zipimport", "zipimporter"); if (zipimporter == NULL) { _PyErr_Clear(tstate); /* No zipimporter object -- okay */ if (verbose) { @@ -4174,7 +4174,7 @@ _PyImport_FiniExternal(PyInterpreterState *interp) /******************/ PyObject * -_PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) +PyImport_ImportModuleAttr(PyObject *modname, PyObject *attrname) { PyObject *mod = PyImport_Import(modname); if (mod == NULL) { @@ -4186,7 +4186,7 @@ _PyImport_GetModuleAttr(PyObject *modname, PyObject *attrname) } PyObject * -_PyImport_GetModuleAttrString(const char *modname, const char *attrname) +PyImport_ImportModuleAttrString(const char *modname, const char *attrname) { PyObject *pmodname = PyUnicode_FromString(modname); if (pmodname == NULL) { @@ -4197,7 +4197,7 @@ _PyImport_GetModuleAttrString(const char *modname, const char *attrname) Py_DECREF(pmodname); return NULL; } - PyObject *result = _PyImport_GetModuleAttr(pmodname, pattrname); + PyObject *result = PyImport_ImportModuleAttr(pmodname, pattrname); Py_DECREF(pattrname); Py_DECREF(pmodname); return result; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 00a98af998cfce..7031d743174650 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2609,7 +2609,7 @@ create_stdio(const PyConfig *config, PyObject* io, #ifdef HAVE_WINDOWS_CONSOLE_IO /* Windows console IO is always UTF-8 encoded */ - PyTypeObject *winconsoleio_type = (PyTypeObject *)_PyImport_GetModuleAttr( + PyTypeObject *winconsoleio_type = (PyTypeObject *)PyImport_ImportModuleAttr( &_Py_ID(_io), &_Py_ID(_WindowsConsoleIO)); if (winconsoleio_type == NULL) { goto error; @@ -2714,7 +2714,7 @@ init_set_builtins_open(void) goto error; } - if (!(wrapper = _PyImport_GetModuleAttrString("io", "open"))) { + if (!(wrapper = PyImport_ImportModuleAttrString("io", "open"))) { goto error; } From e1c4ba928852eac0b0e0bded1c314e3e36975286 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 30 Jan 2025 03:23:25 -0800 Subject: [PATCH 008/311] gh-129005: _pyio.BufferedIO remove copy on readall (#129454) Slicing buf and appending chunk would always result in a copy. Commonly in a readall() there is no already read data in buf, and the amount of data read may be large, so the copy is expensive. --- Lib/_pyio.py | 3 +++ .../Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 76a27910da4d5f..755e0258770891 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1062,6 +1062,9 @@ def _read_unlocked(self, n=None): if chunk is None: return buf[pos:] or None else: + # Avoid slice + copy if there is no data in buf + if not buf: + return chunk return buf[pos:] + chunk chunks = [buf[pos:]] # Strip the consumed bytes. current_size = 0 diff --git a/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst b/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst new file mode 100644 index 00000000000000..48ee57109be2ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst @@ -0,0 +1,2 @@ +:mod:`!_pyio`: Remove an unnecessary copy when ``_pyio.BufferedReader.read()`` +is called to read all data from a file and has no data already in buffer. From a810cb89f108f026d70c5b20141b709493051d96 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jan 2025 12:27:27 +0100 Subject: [PATCH 009/311] gh-89188: Implement PyUnicode_KIND() as a function (#129412) Implement PyUnicode_KIND() and PyUnicode_DATA() as function, in addition to the macros with the same names. The macros rely on C bit fields which have compiler-specific layout. --- Include/cpython/unicodeobject.h | 8 +++++-- ...5-01-29-11-58-38.gh-issue-89188.BsfLr3.rst | 3 +++ Objects/unicodeobject.c | 21 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-29-11-58-38.gh-issue-89188.BsfLr3.rst diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index 287de52b96202c..cea69dd1280999 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -240,6 +240,8 @@ enum PyUnicode_Kind { PyUnicode_4BYTE_KIND = 4 }; +PyAPI_FUNC(int) PyUnicode_KIND(PyObject *op); + // PyUnicode_KIND(): Return one of the PyUnicode_*_KIND values defined above. // // gh-89653: Converting this macro to a static inline function would introduce @@ -264,13 +266,15 @@ static inline void* _PyUnicode_NONCOMPACT_DATA(PyObject *op) { return data; } -static inline void* PyUnicode_DATA(PyObject *op) { +PyAPI_FUNC(void*) PyUnicode_DATA(PyObject *op); + +static inline void* _PyUnicode_DATA(PyObject *op) { if (PyUnicode_IS_COMPACT(op)) { return _PyUnicode_COMPACT_DATA(op); } return _PyUnicode_NONCOMPACT_DATA(op); } -#define PyUnicode_DATA(op) PyUnicode_DATA(_PyObject_CAST(op)) +#define PyUnicode_DATA(op) _PyUnicode_DATA(_PyObject_CAST(op)) /* Return pointers to the canonical representation cast to unsigned char, Py_UCS2, or Py_UCS4 for direct character access. diff --git a/Misc/NEWS.d/next/C_API/2025-01-29-11-58-38.gh-issue-89188.BsfLr3.rst b/Misc/NEWS.d/next/C_API/2025-01-29-11-58-38.gh-issue-89188.BsfLr3.rst new file mode 100644 index 00000000000000..7ff225a7dc60c7 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-29-11-58-38.gh-issue-89188.BsfLr3.rst @@ -0,0 +1,3 @@ +Implement :c:func:`PyUnicode_KIND` and :c:func:`PyUnicode_DATA` as function, +in addition to the macros with the same names. The macros rely on C bit +fields which have compiler-specific layout. Patch by Victor Stinner. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index d9952f764bb178..c6f13f60ad741f 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -16486,3 +16486,24 @@ PyInit__string(void) { return PyModuleDef_Init(&_string_module); } + + +#undef PyUnicode_KIND +int PyUnicode_KIND(PyObject *op) +{ + if (!PyUnicode_Check(op)) { + PyErr_Format(PyExc_TypeError, "expect str, got %T", op); + return -1; + } + return _PyASCIIObject_CAST(op)->state.kind; +} + +#undef PyUnicode_DATA +void* PyUnicode_DATA(PyObject *op) +{ + if (!PyUnicode_Check(op)) { + PyErr_Format(PyExc_TypeError, "expect str, got %T", op); + return NULL; + } + return _PyUnicode_DATA(op); +} From 5ab9604683a58e613c6eba309ed89ed2092e3e2d Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:38:52 +0000 Subject: [PATCH 010/311] clearer error and suggestion when c-analyzer cannot read ignored.tsv (#129423) --- Tools/c-analyzer/c_analyzer/datafiles.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tools/c-analyzer/c_analyzer/datafiles.py b/Tools/c-analyzer/c_analyzer/datafiles.py index d5db3bd3ed74ac..79c201a5d3b92c 100644 --- a/Tools/c-analyzer/c_analyzer/datafiles.py +++ b/Tools/c-analyzer/c_analyzer/datafiles.py @@ -104,7 +104,12 @@ def _iter_ignored(infile, relroot): for v in varidinfo) if reason in bogus: reason = None - varid = _info.DeclID.from_row(varidinfo) + try: + varid = _info.DeclID.from_row(varidinfo) + except BaseException as e: + e.add_note(f"Error occurred when processing row {varidinfo} in {infile}.") + e.add_note(f"Could it be that you added a row which is not tab-delimited?") + raise e varid = varid.fix_filename(relroot, formatted=False, fixroot=False) yield varid, reason From 4e47e05045b7b05c7e166bda2afd60191314e326 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jan 2025 16:09:38 +0100 Subject: [PATCH 011/311] gh-129354: Use PyErr_FormatUnraisable() function (#129435) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). --- Modules/_datetimemodule.c | 2 +- Modules/_testcapi/watchers.c | 9 ++++++--- Modules/_winapi.c | 9 ++++----- Modules/atexitmodule.c | 3 ++- Modules/overlapped.c | 3 ++- Modules/signalmodule.c | 3 ++- Objects/dictobject.c | 3 ++- Objects/weakrefobject.c | 2 +- Python/jit.c | 2 +- 9 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index a486af7833c2c6..9d2abce203be7d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -226,7 +226,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) goto finally; error: - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when clearing _datetime module"); finally: PyErr_SetRaisedException(exc); diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index 321d3aeffb6ad1..17a30355a266f0 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -428,7 +428,8 @@ allocate_too_many_code_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyCode_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_WriteUnraisable(Py_None); + PyErr_FormatUnraisable("Exception ignored when " + "clearing code watcher"); break; } } @@ -609,7 +610,8 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_WriteUnraisable(Py_None); + PyErr_FormatUnraisable("Exception ignored when " + "clearing function watcher"); break; } } @@ -755,7 +757,8 @@ allocate_too_many_context_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyContext_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_WriteUnraisable(Py_None); + PyErr_FormatUnraisable("Exception ignored when " + "clearing context watcher"); break; } } diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 260cab48091c16..56dd38401cb273 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -171,17 +171,16 @@ overlapped_dealloc(OverlappedObject *self) { /* The operation is no longer pending -- nothing to do. */ } - else if (_Py_IsInterpreterFinalizing(_PyInterpreterState_GET())) - { + else if (_Py_IsInterpreterFinalizing(_PyInterpreterState_GET())) { /* The operation is still pending -- give a warning. This will probably only happen on Windows XP. */ PyErr_SetString(PyExc_PythonFinalizationError, "I/O operations still in flight while destroying " "Overlapped object, the process may crash"); - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when deallocating " + "overlapped operation %R", self); } - else - { + else { /* The operation is still pending, but the process is probably about to exit, so we need not worry too much about memory leaks. Leaking self prevents a potential diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 1b89b32ba907d7..1dbcc1089c32ab 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -110,7 +110,8 @@ atexit_callfuncs(struct atexit_state *state) PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks)); if (copy == NULL) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when " + "copying atexit callbacks"); return; } diff --git a/Modules/overlapped.c b/Modules/overlapped.c index 308a0dab7fab1a..525b288f3ff54d 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -759,7 +759,8 @@ Overlapped_dealloc(OverlappedObject *self) PyExc_RuntimeError, "%R still has pending operation at " "deallocation, the process may crash", self); - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when deallocating " + "overlapped operation %R", self); } } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 0e53a36bca55f0..0cc9b35300dcca 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -1837,7 +1837,8 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_Format(PyExc_OSError, "Signal %i ignored due to race condition", i); - PyErr_WriteUnraisable(Py_None); + PyErr_FormatUnraisable("Exception ignored when " + "calling signal handler"); continue; } PyObject *arglist = NULL; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 733a10a2e80b18..a05359ca0b16ef 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -7351,7 +7351,8 @@ PyObject_ClearManagedDict(PyObject *obj) if (set_or_clear_managed_dict(obj, NULL, true) < 0) { /* Must be out of memory */ assert(PyErr_Occurred() == PyExc_MemoryError); - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when " + "clearing an object managed dict"); /* Clear the dict */ PyDictObject *dict = _PyObject_GetManagedDict(obj); Py_BEGIN_CRITICAL_SECTION2(dict, obj); diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 0ee64ed70a63cd..8ced82ef36e30d 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1042,7 +1042,7 @@ PyObject_ClearWeakRefs(PyObject *object) PyObject *tuple = PyTuple_New(num_weakrefs * 2); if (tuple == NULL) { _PyWeakref_ClearWeakRefsNoCallbacks(object); - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when clearing object weakrefs"); PyErr_SetRaisedException(exc); return; } diff --git a/Python/jit.c b/Python/jit.c index 7dd0da7a45055a..4b01112f9b0a5a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -563,7 +563,7 @@ _PyJIT_Free(_PyExecutorObject *executor) executor->jit_side_entry = NULL; executor->jit_size = 0; if (jit_free(memory, size)) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored when freeing JIT memory"); } } } From 4ca9fc08f89bf7172d41e523d9e520eb1729ee8c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jan 2025 18:05:32 +0100 Subject: [PATCH 012/311] gh-111495: Add PyFile tests (#129449) Add tests for the following functions in test_capi.test_file: * PyFile_FromFd() * PyFile_GetLine() * PyFile_NewStdPrinter() * PyFile_WriteObject() * PyFile_WriteString() * PyObject_AsFileDescriptor() Add Modules/_testlimitedcapi/file.c file. Remove test_embed.StdPrinterTests which became redundant. --- Lib/test/test_capi/test_file.py | 249 +++++++++++++++++++++-- Lib/test/test_embed.py | 51 ----- Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/clinic/file.c.h | 29 ++- Modules/_testcapi/file.c | 26 ++- Modules/_testlimitedcapi.c | 3 + Modules/_testlimitedcapi/clinic/file.c.h | 81 ++++++++ Modules/_testlimitedcapi/file.c | 128 ++++++++++++ Modules/_testlimitedcapi/parts.h | 1 + PCbuild/_testlimitedcapi.vcxproj | 1 + PCbuild/_testlimitedcapi.vcxproj.filters | 1 + 11 files changed, 500 insertions(+), 72 deletions(-) create mode 100644 Modules/_testlimitedcapi/clinic/file.c.h create mode 100644 Modules/_testlimitedcapi/file.c diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py index a67a5121c4588b..b5767756992861 100644 --- a/Lib/test/test_capi/test_file.py +++ b/Lib/test/test_capi/test_file.py @@ -1,26 +1,242 @@ +import io import os import unittest +import warnings from test import support -from test.support import import_helper, os_helper +from test.support import import_helper, os_helper, warnings_helper -_testcapi = import_helper.import_module('_testcapi') +_testcapi = import_helper.import_module('_testcapi') +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') +_io = import_helper.import_module('_io') NULL = None +STDOUT_FD = 1 + +with open(__file__, 'rb') as fp: + FIRST_LINE = next(fp).decode() +FIRST_LINE_NORM = FIRST_LINE.rstrip() + '\n' class CAPIFileTest(unittest.TestCase): + def test_pyfile_fromfd(self): + # Test PyFile_FromFd() which is a thin wrapper to _io.open() + pyfile_fromfd = _testlimitedcapi.pyfile_fromfd + filename = __file__ + with open(filename, "rb") as fp: + fd = fp.fileno() + + # FileIO + fp.seek(0) + obj = pyfile_fromfd(fd, filename, "rb", 0, NULL, NULL, NULL, 0) + try: + self.assertIsInstance(obj, _io.FileIO) + self.assertEqual(obj.readline(), FIRST_LINE.encode()) + finally: + obj.close() + + # BufferedReader + fp.seek(0) + obj = pyfile_fromfd(fd, filename, "rb", 1024, NULL, NULL, NULL, 0) + try: + self.assertIsInstance(obj, _io.BufferedReader) + self.assertEqual(obj.readline(), FIRST_LINE.encode()) + finally: + obj.close() + + # TextIOWrapper + fp.seek(0) + obj = pyfile_fromfd(fd, filename, "r", 1, + "utf-8", "replace", NULL, 0) + try: + self.assertIsInstance(obj, _io.TextIOWrapper) + self.assertEqual(obj.encoding, "utf-8") + self.assertEqual(obj.errors, "replace") + self.assertEqual(obj.readline(), FIRST_LINE_NORM) + finally: + obj.close() + + def test_pyfile_getline(self): + # Test PyFile_GetLine(file, n): call file.readline() + # and strip "\n" suffix if n < 0. + pyfile_getline = _testlimitedcapi.pyfile_getline + + # Test Unicode + with open(__file__, "r") as fp: + fp.seek(0) + self.assertEqual(pyfile_getline(fp, -1), + FIRST_LINE_NORM.rstrip('\n')) + fp.seek(0) + self.assertEqual(pyfile_getline(fp, 0), + FIRST_LINE_NORM) + fp.seek(0) + self.assertEqual(pyfile_getline(fp, 6), + FIRST_LINE_NORM[:6]) + + # Test bytes + with open(__file__, "rb") as fp: + fp.seek(0) + self.assertEqual(pyfile_getline(fp, -1), + FIRST_LINE.rstrip('\n').encode()) + fp.seek(0) + self.assertEqual(pyfile_getline(fp, 0), + FIRST_LINE.encode()) + fp.seek(0) + self.assertEqual(pyfile_getline(fp, 6), + FIRST_LINE.encode()[:6]) + + def test_pyfile_writestring(self): + # Test PyFile_WriteString(str, file): call file.write(str) + writestr = _testlimitedcapi.pyfile_writestring + + with io.StringIO() as fp: + self.assertEqual(writestr("a\xe9\u20ac\U0010FFFF".encode(), fp), 0) + with self.assertRaises(UnicodeDecodeError): + writestr(b"\xff", fp) + with self.assertRaises(UnicodeDecodeError): + writestr("\udc80".encode("utf-8", "surrogatepass"), fp) + + text = fp.getvalue() + self.assertEqual(text, "a\xe9\u20ac\U0010FFFF") + + with self.assertRaises(SystemError): + writestr(b"abc", NULL) + + def test_pyfile_writeobject(self): + # Test PyFile_WriteObject(obj, file, flags): + # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW. + # - Call file.write(repr(obj)) otherwise. + writeobject = _testlimitedcapi.pyfile_writeobject + Py_PRINT_RAW = 1 + + with io.StringIO() as fp: + # Test flags=Py_PRINT_RAW + self.assertEqual(writeobject("raw", fp, Py_PRINT_RAW), 0) + writeobject(NULL, fp, Py_PRINT_RAW) + + # Test flags=0 + self.assertEqual(writeobject("repr", fp, 0), 0) + writeobject(NULL, fp, 0) + + text = fp.getvalue() + self.assertEqual(text, "raw'repr'") + + # invalid file type + for invalid_file in (123, "abc", object()): + with self.subTest(file=invalid_file): + with self.assertRaises(AttributeError): + writeobject("abc", invalid_file, Py_PRINT_RAW) + + with self.assertRaises(TypeError): + writeobject("abc", NULL, 0) + + def test_pyobject_asfiledescriptor(self): + # Test PyObject_AsFileDescriptor(obj): + # - Return obj if obj is an integer. + # - Return obj.fileno() otherwise. + # File descriptor must be >= 0. + asfd = _testlimitedcapi.pyobject_asfiledescriptor + + self.assertEqual(asfd(123), 123) + self.assertEqual(asfd(0), 0) + + with open(__file__, "rb") as fp: + self.assertEqual(asfd(fp), fp.fileno()) + + # bool emits RuntimeWarning + msg = r"bool is used as a file descriptor" + with warnings_helper.check_warnings((msg, RuntimeWarning)): + self.assertEqual(asfd(True), 1) + + class FakeFile: + def __init__(self, fd): + self.fd = fd + def fileno(self): + return self.fd + + # file descriptor must be positive + with self.assertRaises(ValueError): + asfd(-1) + with self.assertRaises(ValueError): + asfd(FakeFile(-1)) + + # fileno() result must be an integer + with self.assertRaises(TypeError): + asfd(FakeFile("text")) + + # unsupported types + for obj in ("string", ["list"], object()): + with self.subTest(obj=obj): + with self.assertRaises(TypeError): + asfd(obj) + + # CRASHES asfd(NULL) + + def test_pyfile_newstdprinter(self): + # Test PyFile_NewStdPrinter() + pyfile_newstdprinter = _testcapi.pyfile_newstdprinter + + file = pyfile_newstdprinter(STDOUT_FD) + self.assertEqual(file.closed, False) + self.assertIsNone(file.encoding) + self.assertEqual(file.mode, "w") + + self.assertEqual(file.fileno(), STDOUT_FD) + self.assertEqual(file.isatty(), os.isatty(STDOUT_FD)) + + # flush() is a no-op + self.assertIsNone(file.flush()) + + # close() is a no-op + self.assertIsNone(file.close()) + self.assertEqual(file.closed, False) + + support.check_disallow_instantiation(self, type(file)) + + def test_pyfile_newstdprinter_write(self): + # Test the write() method of PyFile_NewStdPrinter() + pyfile_newstdprinter = _testcapi.pyfile_newstdprinter + + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + + try: + old_stdout = os.dup(STDOUT_FD) + except OSError as exc: + # os.dup(STDOUT_FD) is not supported on WASI + self.skipTest(f"os.dup() failed with {exc!r}") + + try: + with open(filename, "wb") as fp: + # PyFile_NewStdPrinter() only accepts fileno(stdout) + # or fileno(stderr) file descriptor. + fd = fp.fileno() + os.dup2(fd, STDOUT_FD) + + file = pyfile_newstdprinter(STDOUT_FD) + self.assertEqual(file.write("text"), 4) + # The surrogate character is encoded with + # the "surrogateescape" error handler + self.assertEqual(file.write("[\udc80]"), 8) + finally: + os.dup2(old_stdout, STDOUT_FD) + os.close(old_stdout) + + with open(filename, "r") as fp: + self.assertEqual(fp.read(), "text[\\udc80]") + def test_py_fopen(self): # Test Py_fopen() and Py_fclose() + py_fopen = _testcapi.py_fopen with open(__file__, "rb") as fp: source = fp.read() for filename in (__file__, os.fsencode(__file__)): with self.subTest(filename=filename): - data = _testcapi.py_fopen(filename, "rb") + data = py_fopen(filename, "rb") self.assertEqual(data, source[:256]) - data = _testcapi.py_fopen(os_helper.FakePath(filename), "rb") + data = py_fopen(os_helper.FakePath(filename), "rb") self.assertEqual(data, source[:256]) filenames = [ @@ -43,41 +259,46 @@ def test_py_fopen(self): filename = None continue try: - data = _testcapi.py_fopen(filename, "rb") + data = py_fopen(filename, "rb") self.assertEqual(data, source[:256]) finally: os_helper.unlink(filename) # embedded null character/byte in the filename with self.assertRaises(ValueError): - _testcapi.py_fopen("a\x00b", "rb") + py_fopen("a\x00b", "rb") with self.assertRaises(ValueError): - _testcapi.py_fopen(b"a\x00b", "rb") + py_fopen(b"a\x00b", "rb") # non-ASCII mode failing with "Invalid argument" with self.assertRaises(OSError): - _testcapi.py_fopen(__file__, b"\xc2\x80") + py_fopen(__file__, b"\xc2\x80") with self.assertRaises(OSError): # \x98 is invalid in cp1250, cp1251, cp1257 # \x9d is invalid in cp1252-cp1255, cp1258 - _testcapi.py_fopen(__file__, b"\xc2\x98\xc2\x9d") + py_fopen(__file__, b"\xc2\x98\xc2\x9d") # UnicodeDecodeError can come from the audit hook code with self.assertRaises((UnicodeDecodeError, OSError)): - _testcapi.py_fopen(__file__, b"\x98\x9d") + py_fopen(__file__, b"\x98\x9d") # invalid filename type for invalid_type in (123, object()): with self.subTest(filename=invalid_type): with self.assertRaises(TypeError): - _testcapi.py_fopen(invalid_type, "rb") + py_fopen(invalid_type, "rb") if support.MS_WINDOWS: with self.assertRaises(OSError): # On Windows, the file mode is limited to 10 characters - _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8") + py_fopen(__file__, "rt+, ccs=UTF-8") + + # CRASHES py_fopen(NULL, 'rb') + # CRASHES py_fopen(__file__, NULL) + + # TODO: Test Py_UniversalNewlineFgets() - # CRASHES _testcapi.py_fopen(NULL, 'rb') - # CRASHES _testcapi.py_fopen(__file__, NULL) + # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by + # test_embed.test_open_code_hook() if __name__ == "__main__": diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index f41c45ec4d9cdd..72221379a00051 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1985,56 +1985,5 @@ def test_presite(self): self.assertIn("unique-python-message", out) -class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase): - # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr(): - # "Set up a preliminary stderr printer until we have enough - # infrastructure for the io module in place." - - STDOUT_FD = 1 - - def create_printer(self, fd): - ctypes = import_helper.import_module('ctypes') - PyFile_NewStdPrinter = ctypes.pythonapi.PyFile_NewStdPrinter - PyFile_NewStdPrinter.argtypes = (ctypes.c_int,) - PyFile_NewStdPrinter.restype = ctypes.py_object - return PyFile_NewStdPrinter(fd) - - def test_write(self): - message = "unicode:\xe9-\u20ac-\udc80!\n" - - stdout_fd = self.STDOUT_FD - stdout_fd_copy = os.dup(stdout_fd) - self.addCleanup(os.close, stdout_fd_copy) - - rfd, wfd = os.pipe() - self.addCleanup(os.close, rfd) - self.addCleanup(os.close, wfd) - try: - # PyFile_NewStdPrinter() only accepts fileno(stdout) - # or fileno(stderr) file descriptor. - os.dup2(wfd, stdout_fd) - - printer = self.create_printer(stdout_fd) - printer.write(message) - finally: - os.dup2(stdout_fd_copy, stdout_fd) - - data = os.read(rfd, 100) - self.assertEqual(data, message.encode('utf8', 'backslashreplace')) - - def test_methods(self): - fd = self.STDOUT_FD - printer = self.create_printer(fd) - self.assertEqual(printer.fileno(), fd) - self.assertEqual(printer.isatty(), os.isatty(fd)) - printer.flush() # noop - printer.close() # noop - - def test_disallow_instantiation(self): - fd = self.STDOUT_FD - printer = self.create_printer(fd) - support.check_disallow_instantiation(self, type(printer)) - - if __name__ == "__main__": unittest.main() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 563bbc1bda6223..b31bf2734e4fd1 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -163,7 +163,7 @@ @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h index fddbf48071bd3b..6efb6b47353443 100644 --- a/Modules/_testcapi/clinic/file.c.h +++ b/Modules/_testcapi/clinic/file.c.h @@ -4,6 +4,33 @@ preserve #include "pycore_modsupport.h" // _PyArg_CheckPositional() +PyDoc_STRVAR(_testcapi_pyfile_newstdprinter__doc__, +"pyfile_newstdprinter($module, fd, /)\n" +"--\n" +"\n"); + +#define _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF \ + {"pyfile_newstdprinter", (PyCFunction)_testcapi_pyfile_newstdprinter, METH_O, _testcapi_pyfile_newstdprinter__doc__}, + +static PyObject * +_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd); + +static PyObject * +_testcapi_pyfile_newstdprinter(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int fd; + + fd = PyLong_AsInt(arg); + if (fd == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testcapi_pyfile_newstdprinter_impl(module, fd); + +exit: + return return_value; +} + PyDoc_STRVAR(_testcapi_py_fopen__doc__, "py_fopen($module, path, mode, /)\n" "--\n" @@ -34,4 +61,4 @@ _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=c4dc92400306c3eb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e943bbd7f181d079 input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c index d15173fc7959e5..060e0f50598d7e 100644 --- a/Modules/_testcapi/file.c +++ b/Modules/_testcapi/file.c @@ -5,11 +5,29 @@ #include "util.h" #include "clinic/file.c.h" + /*[clinic input] module _testcapi [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ + +/*[clinic input] +_testcapi.pyfile_newstdprinter + + fd: int + / + +[clinic start generated code]*/ + +static PyObject * +_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd) +/*[clinic end generated code: output=8a2d1c57b6892db3 input=442f1824142262ea]*/ +{ + return PyFile_NewStdPrinter(fd); +} + + /*[clinic input] _testcapi.py_fopen @@ -38,7 +56,9 @@ _testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode, return PyBytes_FromStringAndSize(buffer, size); } + static PyMethodDef test_methods[] = { + _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF _TESTCAPI_PY_FOPEN_METHODDEF {NULL}, }; @@ -46,9 +66,5 @@ static PyMethodDef test_methods[] = { int _PyTestCapi_Init_File(PyObject *m) { - if (PyModule_AddFunctions(m, test_methods) < 0){ - return -1; - } - - return 0; + return PyModule_AddFunctions(m, test_methods); } diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index 82dac1c999470f..4dae99ec92a085 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -89,5 +89,8 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_Version(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_File(mod) < 0) { + return NULL; + } return mod; } diff --git a/Modules/_testlimitedcapi/clinic/file.c.h b/Modules/_testlimitedcapi/clinic/file.c.h new file mode 100644 index 00000000000000..663619eead2a3a --- /dev/null +++ b/Modules/_testlimitedcapi/clinic/file.c.h @@ -0,0 +1,81 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(_testcapi_pyfile_getline__doc__, +"pyfile_getline($module, file, n, /)\n" +"--\n" +"\n"); + +#define _TESTCAPI_PYFILE_GETLINE_METHODDEF \ + {"pyfile_getline", (PyCFunction)(void(*)(void))_testcapi_pyfile_getline, METH_FASTCALL, _testcapi_pyfile_getline__doc__}, + +static PyObject * +_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n); + +static PyObject * +_testcapi_pyfile_getline(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *file; + int n; + + if (nargs != 2) { + PyErr_Format(PyExc_TypeError, "pyfile_getline expected 2 arguments, got %zd", nargs); + goto exit; + } + file = args[0]; + n = PyLong_AsInt(args[1]); + if (n == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testcapi_pyfile_getline_impl(module, file, n); + +exit: + return return_value; +} + +PyDoc_STRVAR(_testcapi_pyfile_writeobject__doc__, +"pyfile_writeobject($module, obj, file, flags, /)\n" +"--\n" +"\n"); + +#define _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF \ + {"pyfile_writeobject", (PyCFunction)(void(*)(void))_testcapi_pyfile_writeobject, METH_FASTCALL, _testcapi_pyfile_writeobject__doc__}, + +static PyObject * +_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj, + PyObject *file, int flags); + +static PyObject * +_testcapi_pyfile_writeobject(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *obj; + PyObject *file; + int flags; + + if (nargs != 3) { + PyErr_Format(PyExc_TypeError, "pyfile_writeobject expected 3 arguments, got %zd", nargs); + goto exit; + } + obj = args[0]; + file = args[1]; + flags = PyLong_AsInt(args[2]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testcapi_pyfile_writeobject_impl(module, obj, file, flags); + +exit: + return return_value; +} + +PyDoc_STRVAR(_testcapi_pyobject_asfiledescriptor__doc__, +"pyobject_asfiledescriptor($module, obj, /)\n" +"--\n" +"\n"); + +#define _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF \ + {"pyobject_asfiledescriptor", (PyCFunction)_testcapi_pyobject_asfiledescriptor, METH_O, _testcapi_pyobject_asfiledescriptor__doc__}, +/*[clinic end generated code: output=ea572aaaa01aec7b input=a9049054013a1b77]*/ diff --git a/Modules/_testlimitedcapi/file.c b/Modules/_testlimitedcapi/file.c new file mode 100644 index 00000000000000..e082e3c6700ee7 --- /dev/null +++ b/Modules/_testlimitedcapi/file.c @@ -0,0 +1,128 @@ +#include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED + // Need limited C API 3.13 for PyLong_AsInt() +# define Py_LIMITED_API 0x030d0000 +#endif + +#include "parts.h" +#include "util.h" +#include "clinic/file.c.h" + + +/*[clinic input] +module _testcapi +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/ + + +static PyObject * +pyfile_fromfd(PyObject *module, PyObject *args) +{ + int fd; + const char *name; + Py_ssize_t size; + const char *mode; + int buffering; + const char *encoding; + const char *errors; + const char *newline; + int closefd; + if (!PyArg_ParseTuple(args, + "iz#z#" + "iz#z#" + "z#i", + &fd, &name, &size, &mode, &size, + &buffering, &encoding, &size, &errors, &size, + &newline, &size, &closefd)) { + return NULL; + } + + return PyFile_FromFd(fd, name, mode, buffering, + encoding, errors, newline, closefd); +} + + +/*[clinic input] +_testcapi.pyfile_getline + + file: object + n: int + / + +[clinic start generated code]*/ + +static PyObject * +_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n) +/*[clinic end generated code: output=137fde2774563266 input=df26686148b3657e]*/ +{ + return PyFile_GetLine(file, n); +} + + +/*[clinic input] +_testcapi.pyfile_writeobject + + obj: object + file: object + flags: int + / + +[clinic start generated code]*/ + +static PyObject * +_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj, + PyObject *file, int flags) +/*[clinic end generated code: output=ebb4d802e3db489c input=64a34a3e75b9935a]*/ +{ + NULLABLE(obj); + NULLABLE(file); + RETURN_INT(PyFile_WriteObject(obj, file, flags)); +} + + +static PyObject * +pyfile_writestring(PyObject *module, PyObject *args) +{ + const char *str; + Py_ssize_t size; + PyObject *file; + if (!PyArg_ParseTuple(args, "z#O", &str, &size, &file)) { + return NULL; + } + NULLABLE(file); + + RETURN_INT(PyFile_WriteString(str, file)); +} + + +/*[clinic input] +_testcapi.pyobject_asfiledescriptor + + obj: object + / + +[clinic start generated code]*/ + +static PyObject * +_testcapi_pyobject_asfiledescriptor(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=2d640c6a1970c721 input=45fa1171d62b18d7]*/ +{ + NULLABLE(obj); + RETURN_INT(PyObject_AsFileDescriptor(obj)); +} + + +static PyMethodDef test_methods[] = { + {"pyfile_fromfd", pyfile_fromfd, METH_VARARGS}, + _TESTCAPI_PYFILE_GETLINE_METHODDEF + _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF + {"pyfile_writestring", pyfile_writestring, METH_VARARGS}, + _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_File(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 9efcd8dcb71e5b..60f6f03011a65c 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -42,5 +42,6 @@ int _PyTestLimitedCAPI_Init_Tuple(PyObject *module); int _PyTestLimitedCAPI_Init_Unicode(PyObject *module); int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module); int _PyTestLimitedCAPI_Init_Version(PyObject *module); +int _PyTestLimitedCAPI_Init_File(PyObject *module); #endif // Py_TESTLIMITEDCAPI_PARTS_H diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 87abff52493098..36c41fc9824fda 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -114,6 +114,7 @@ + diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index a975a508506905..62ecb2f70ffa2d 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -30,6 +30,7 @@ + From 510fefdc625dd2ed2b6b3975314a59e291b94ae8 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Thu, 30 Jan 2025 19:34:09 +0000 Subject: [PATCH 013/311] gh-127349: Add check for correct resizing in REPL (#127387) --- Lib/_pyrepl/reader.py | 5 +++-- Lib/test/test_pyrepl/test_reader.py | 9 ++++++++- .../2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 4b0700d069c621..1252847e02b2ea 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -587,10 +587,11 @@ def setpos_from_xy(self, x: int, y: int) -> None: def pos2xy(self) -> tuple[int, int]: """Return the x, y coordinates of position 'pos'.""" # this *is* incomprehensible, yes. - y = 0 + p, y = 0, 0 + l2: list[int] = [] pos = self.pos assert 0 <= pos <= len(self.buffer) - if pos == len(self.buffer): + if pos == len(self.buffer) and len(self.screeninfo) > 0: y = len(self.screeninfo) - 1 p, l2 = self.screeninfo[y] return p + sum(l2) + l2.count(0), y diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index 863ecc61ddd432..27c6d6664eda9e 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -4,7 +4,7 @@ from unittest import TestCase from unittest.mock import MagicMock -from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader +from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader, prepare_console from _pyrepl.console import Event from _pyrepl.reader import Reader @@ -312,3 +312,10 @@ def test_key_press_on_tab_press_once(self): reader, _ = handle_all_events(events, prepare_reader=completing_reader) self.assert_screen_equals(reader, f"{code}a") + + def test_pos2xy_with_no_columns(self): + console = prepare_console([]) + reader = prepare_reader(console) + # Simulate a resize to 0 columns + reader.screeninfo = [] + self.assertEqual(reader.pos2xy(), (0, 0)) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst new file mode 100644 index 00000000000000..3c1586b6cbb8e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst @@ -0,0 +1,2 @@ +Fixed the error when resizing terminal in Python REPL. Patch by Semyon +Moroz. From 10ee2d9d3bcde27c75f179214ad41c00e4852a7a Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 30 Jan 2025 14:24:52 -0800 Subject: [PATCH 014/311] gh-129205: Update multiprocessing.forkserver to use os.readinto() (#129425) --- Lib/multiprocessing/forkserver.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index df9b9be9d1898b..681af2610e9b37 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -382,13 +382,14 @@ def _serve_one(child_r, fds, unused_fds, handlers): # def read_signed(fd): - data = b'' - length = SIGNED_STRUCT.size - while len(data) < length: - s = os.read(fd, length - len(data)) - if not s: + data = bytearray(SIGNED_STRUCT.size) + unread = memoryview(data) + while unread: + count = os.readinto(fd, unread) + if count == 0: raise EOFError('unexpected EOF') - data += s + unread = unread[count:] + return SIGNED_STRUCT.unpack(data)[0] def write_signed(fd, n): From c07ac3c86a8f5021e27bf2c27c6bf0a25229d846 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 31 Jan 2025 13:41:11 +0530 Subject: [PATCH 015/311] gh-128452: fix warning in socketmodule.c (#129478) --- Modules/socketmodule.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 01811afa578119..b178eb42ac8e6a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -2952,6 +2952,8 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored)) ctx.addrlen = &addrlen; ctx.addrbuf = &addrbuf; + ctx.result = INVALID_SOCKET; + if (sock_call(s, 0, sock_accept_impl, &ctx) < 0) return NULL; newfd = ctx.result; From 3ebe3d7688475e98a34d691e1ba50d8b7178a575 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 31 Jan 2025 00:40:44 -0800 Subject: [PATCH 016/311] Revert "gh-129005: _pyio.BufferedIO remove copy on readall (#129454)" (#129500) This reverts commit e1c4ba928852eac0b0e0bded1c314e3e36975286. --- Lib/_pyio.py | 3 --- .../Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst | 2 -- 2 files changed, 5 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 755e0258770891..76a27910da4d5f 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1062,9 +1062,6 @@ def _read_unlocked(self, n=None): if chunk is None: return buf[pos:] or None else: - # Avoid slice + copy if there is no data in buf - if not buf: - return chunk return buf[pos:] + chunk chunks = [buf[pos:]] # Strip the consumed bytes. current_size = 0 diff --git a/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst b/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst deleted file mode 100644 index 48ee57109be2ff..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`!_pyio`: Remove an unnecessary copy when ``_pyio.BufferedReader.read()`` -is called to read all data from a file and has no data already in buffer. From 95504f429eec04010d0b815345ebcc3af2402af0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 09:45:35 +0100 Subject: [PATCH 017/311] gh-129354: Fix grammar in PyErr_FormatUnraisable() (#129475) Replace "on verb+ing" with "while verb+ing". --- Lib/test/test_cmd_line.py | 2 +- Lib/test/test_ctypes/test_callbacks.py | 2 +- Lib/test/test_ctypes/test_random_things.py | 2 +- Lib/test/test_signal.py | 6 ++-- Modules/_ctypes/_ctypes.c | 3 +- Modules/_ctypes/callbacks.c | 10 +++---- Modules/_datetimemodule.c | 2 +- Modules/_lsprof.c | 3 +- Modules/_testcapi/watchers.c | 6 ++-- Modules/_winapi.c | 2 +- Modules/atexitmodule.c | 2 +- Modules/getpath.c | 6 ++-- Modules/overlapped.c | 2 +- Modules/signalmodule.c | 8 +++-- Objects/dictobject.c | 2 +- Objects/moduleobject.c | 6 ++-- Objects/weakrefobject.c | 3 +- Python/compile.c | 4 +-- Python/crossinterp.c | 3 +- Python/errors.c | 2 +- Python/gc.c | 4 +-- Python/gc_free_threading.c | 6 ++-- Python/import.c | 15 ++++++---- Python/jit.c | 3 +- Python/pylifecycle.c | 34 ++++++++++++---------- 25 files changed, 79 insertions(+), 59 deletions(-) diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 24cf357c581096..b949b310ac0f5f 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -491,7 +491,7 @@ def test_stdout_flush_at_shutdown(self): rc, out, err = assert_python_failure('-c', code) self.assertEqual(b'', out) self.assertEqual(120, rc) - self.assertIn(b'Exception ignored on flushing sys.stdout:\n' + self.assertIn(b'Exception ignored while flushing sys.stdout:\n' b'OSError: '.replace(b'\n', os.linesep.encode()), err) diff --git a/Lib/test/test_ctypes/test_callbacks.py b/Lib/test/test_ctypes/test_callbacks.py index 8f483dfe1db801..6c7c2e5270736e 100644 --- a/Lib/test/test_ctypes/test_callbacks.py +++ b/Lib/test/test_ctypes/test_callbacks.py @@ -324,7 +324,7 @@ def func(): self.assertIsInstance(cm.unraisable.exc_value, TypeError) self.assertEqual(cm.unraisable.err_msg, - f"Exception ignored on converting result " + f"Exception ignored while converting result " f"of ctypes callback function {func!r}") self.assertIsNone(cm.unraisable.object) diff --git a/Lib/test/test_ctypes/test_random_things.py b/Lib/test/test_ctypes/test_random_things.py index 630f6ed9489eba..73ff57d925e2ea 100644 --- a/Lib/test/test_ctypes/test_random_things.py +++ b/Lib/test/test_ctypes/test_random_things.py @@ -51,7 +51,7 @@ def expect_unraisable(self, exc_type, exc_msg=None): if exc_msg is not None: self.assertEqual(str(cm.unraisable.exc_value), exc_msg) self.assertEqual(cm.unraisable.err_msg, - f"Exception ignored on calling ctypes " + f"Exception ignored while calling ctypes " f"callback function {callback_func!r}") self.assertIsNone(cm.unraisable.object) diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 96ae79b0eb18b3..72a01cd1e451f4 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -383,7 +383,7 @@ def handler(signum, frame): except ZeroDivisionError: # An ignored exception should have been printed out on stderr err = err.getvalue() - if ('Exception ignored when trying to write to the signal wakeup fd' + if ('Exception ignored while trying to write to the signal wakeup fd' not in err): raise AssertionError(err) if ('OSError: [Errno %d]' % errno.EBADF) not in err: @@ -572,7 +572,7 @@ def handler(signum, frame): signal.raise_signal(signum) err = err.getvalue() - if ('Exception ignored when trying to {action} to the signal wakeup fd' + if ('Exception ignored while trying to {action} to the signal wakeup fd' not in err): raise AssertionError(err) """.format(action=action) @@ -642,7 +642,7 @@ def handler(signum, frame): "buffer" % written) # By default, we get a warning when a signal arrives - msg = ('Exception ignored when trying to {action} ' + msg = ('Exception ignored while trying to {action} ' 'to the signal wakeup fd') signal.set_wakeup_fd(write.fileno()) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index d86adec0aeca58..606b4636380f11 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -183,7 +183,8 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw) DictRemoverObject *self = _DictRemoverObject_CAST(myself); if (self->key && self->dict) { if (-1 == PyDict_DelItem(self->dict, self->key)) { - PyErr_FormatUnraisable("Exception ignored on calling _ctypes.DictRemover"); + PyErr_FormatUnraisable("Exception ignored while " + "calling _ctypes.DictRemover"); } Py_CLEAR(self->key); Py_CLEAR(self->dict); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 591206a78035c5..f2df7052d84fb1 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -225,9 +225,9 @@ static void _CallPythonObject(ctypes_state *st, result = PyObject_Vectorcall(callable, args, nargs, NULL); if (result == NULL) { - PyErr_FormatUnraisable( - "Exception ignored on calling ctypes callback function %R", - callable); + PyErr_FormatUnraisable("Exception ignored while " + "calling ctypes callback function %R", + callable); } #ifdef MS_WIN32 @@ -269,7 +269,7 @@ static void _CallPythonObject(ctypes_state *st, if (keep == NULL) { /* Could not convert callback result. */ PyErr_FormatUnraisable( - "Exception ignored on converting result " + "Exception ignored while converting result " "of ctypes callback function %R", callable); } @@ -282,7 +282,7 @@ static void _CallPythonObject(ctypes_state *st, "memory leak in callback function.", 1) == -1) { PyErr_FormatUnraisable( - "Exception ignored on converting result " + "Exception ignored while converting result " "of ctypes callback function %R", callable); } diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9d2abce203be7d..bcbf4217d41a9b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -226,7 +226,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) goto finally; error: - PyErr_FormatUnraisable("Exception ignored when clearing _datetime module"); + PyErr_FormatUnraisable("Exception ignored while clearing _datetime module"); finally: PyErr_SetRaisedException(exc); diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 29d9e5b6ef2cbe..fa1d333ecf3829 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -933,7 +933,8 @@ profiler_dealloc(ProfilerObject *op) if (op->flags & POF_ENABLED) { PyThreadState *tstate = _PyThreadState_GET(); if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) { - PyErr_FormatUnraisable("Exception ignored when destroying _lsprof profiler"); + PyErr_FormatUnraisable("Exception ignored while " + "destroying _lsprof profiler"); } } diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index 17a30355a266f0..f7440769b9594e 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -428,7 +428,7 @@ allocate_too_many_code_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyCode_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "clearing code watcher"); break; } @@ -610,7 +610,7 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "clearing function watcher"); break; } @@ -757,7 +757,7 @@ allocate_too_many_context_watchers(PyObject *self, PyObject *args) PyObject *exc = PyErr_GetRaisedException(); for (int i = 0; i < num_watchers; i++) { if (PyContext_ClearWatcher(watcher_ids[i]) < 0) { - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "clearing context watcher"); break; } diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 56dd38401cb273..786a828f00908c 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -177,7 +177,7 @@ overlapped_dealloc(OverlappedObject *self) PyErr_SetString(PyExc_PythonFinalizationError, "I/O operations still in flight while destroying " "Overlapped object, the process may crash"); - PyErr_FormatUnraisable("Exception ignored when deallocating " + PyErr_FormatUnraisable("Exception ignored while deallocating " "overlapped operation %R", self); } else { diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 1dbcc1089c32ab..2bfdda53af8cb2 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -110,7 +110,7 @@ atexit_callfuncs(struct atexit_state *state) PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks)); if (copy == NULL) { - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "copying atexit callbacks"); return; } diff --git a/Modules/getpath.c b/Modules/getpath.c index 2d3c9757298d16..e2478da021f511 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -955,7 +955,7 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config) ) { Py_DECREF(co); Py_DECREF(dict); - PyErr_FormatUnraisable("Exception ignored in preparing getpath"); + PyErr_FormatUnraisable("Exception ignored while preparing getpath"); return PyStatus_Error("error evaluating initial values"); } @@ -964,13 +964,13 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config) if (!r) { Py_DECREF(dict); - PyErr_FormatUnraisable("Exception ignored in running getpath"); + PyErr_FormatUnraisable("Exception ignored while running getpath"); return PyStatus_Error("error evaluating path"); } Py_DECREF(r); if (_PyConfig_FromDict(config, configDict) < 0) { - PyErr_FormatUnraisable("Exception ignored in reading getpath results"); + PyErr_FormatUnraisable("Exception ignored while reading getpath results"); Py_DECREF(dict); return PyStatus_Error("error getting getpath results"); } diff --git a/Modules/overlapped.c b/Modules/overlapped.c index 525b288f3ff54d..806ebee7a70ff1 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -759,7 +759,7 @@ Overlapped_dealloc(OverlappedObject *self) PyExc_RuntimeError, "%R still has pending operation at " "deallocation, the process may crash", self); - PyErr_FormatUnraisable("Exception ignored when deallocating " + PyErr_FormatUnraisable("Exception ignored while deallocating " "overlapped operation %R", self); } } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 0cc9b35300dcca..b679b83bed5365 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -245,7 +245,8 @@ report_wakeup_write_error(void *data) errno = (int) (intptr_t) data; PyObject *exc = PyErr_GetRaisedException(); PyErr_SetFromErrno(PyExc_OSError); - PyErr_FormatUnraisable("Exception ignored when trying to write to the signal wakeup fd"); + PyErr_FormatUnraisable("Exception ignored while " + "trying to write to the signal wakeup fd"); PyErr_SetRaisedException(exc); errno = save_errno; return 0; @@ -262,7 +263,8 @@ report_wakeup_send_error(void* data) recognizes the error codes used by both GetLastError() and WSAGetLastError */ PyErr_SetExcFromWindowsErr(PyExc_OSError, send_errno); - PyErr_FormatUnraisable("Exception ignored when trying to send to the signal wakeup fd"); + PyErr_FormatUnraisable("Exception ignored while " + "trying to send to the signal wakeup fd"); PyErr_SetRaisedException(exc); return 0; } @@ -1837,7 +1839,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate) PyErr_Format(PyExc_OSError, "Signal %i ignored due to race condition", i); - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "calling signal handler"); continue; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index a05359ca0b16ef..91cf013a1dc24b 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -7351,7 +7351,7 @@ PyObject_ClearManagedDict(PyObject *obj) if (set_or_clear_managed_dict(obj, NULL, true) < 0) { /* Must be out of memory */ assert(PyErr_Occurred() == PyExc_MemoryError); - PyErr_FormatUnraisable("Exception ignored when " + PyErr_FormatUnraisable("Exception ignored while " "clearing an object managed dict"); /* Clear the dict */ PyDictObject *dict = _PyObject_GetManagedDict(obj); diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index a8d64c9aefae6b..740392b061ba9a 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -703,7 +703,8 @@ _PyModule_ClearDict(PyObject *d) PyErr_Clear(); } if (PyDict_SetItem(d, key, Py_None) != 0) { - PyErr_FormatUnraisable("Exception ignored on clearing module dict"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing module dict"); } } } @@ -724,7 +725,8 @@ _PyModule_ClearDict(PyObject *d) PyErr_Clear(); } if (PyDict_SetItem(d, key, Py_None) != 0) { - PyErr_FormatUnraisable("Exception ignored on clearing module dict"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing module dict"); } } } diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 8ced82ef36e30d..05ae43d1df475b 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1042,7 +1042,8 @@ PyObject_ClearWeakRefs(PyObject *object) PyObject *tuple = PyTuple_New(num_weakrefs * 2); if (tuple == NULL) { _PyWeakref_ClearWeakRefsNoCallbacks(object); - PyErr_FormatUnraisable("Exception ignored when clearing object weakrefs"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing object weakrefs"); PyErr_SetRaisedException(exc); return; } diff --git a/Python/compile.c b/Python/compile.c index ef470830336dde..b58c12d4b881ac 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -704,12 +704,12 @@ _PyCompile_ExitScope(compiler *c) assert(c->u); /* we are deleting from a list so this really shouldn't fail */ if (PySequence_DelItem(c->c_stack, n) < 0) { - PyErr_FormatUnraisable("Exception ignored on removing " + PyErr_FormatUnraisable("Exception ignored while removing " "the last compiler stack item"); } if (nested_seq != NULL) { if (_PyInstructionSequence_AddNested(c->u->u_instr_sequence, nested_seq) < 0) { - PyErr_FormatUnraisable("Exception ignored on appending " + PyErr_FormatUnraisable("Exception ignored while appending " "nested instruction sequence"); } } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 0a106ad636bfe8..7eb5bc267487d1 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -784,7 +784,8 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) PyObject *exc = PyErr_GetRaisedException(); if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) { #ifdef Py_DEBUG - PyErr_FormatUnraisable("Exception ignored when setting _errdisplay"); + PyErr_FormatUnraisable("Exception ignored while " + "setting _errdisplay"); #endif PyErr_Clear(); } diff --git a/Python/errors.c b/Python/errors.c index 9c7b771133dcf4..0a19d898da75d7 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1633,7 +1633,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) PyObject *hook_args = make_unraisable_hook_args( tstate, exc_type, exc_value, exc_tb, err_msg, obj); if (hook_args == NULL) { - err_msg_str = ("Exception ignored on building " + err_msg_str = ("Exception ignored while building " "sys.unraisablehook arguments"); goto error; } diff --git a/Python/gc.c b/Python/gc.c index 3fe0b7f814544d..420240fc3020be 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -1779,7 +1779,7 @@ do_gc_callback(GCState *gcstate, const char *phase, "collected", stats->collected, "uncollectable", stats->uncollectable); if (info == NULL) { - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks"); return; } } @@ -1787,7 +1787,7 @@ do_gc_callback(GCState *gcstate, const char *phase, PyObject *phase_obj = PyUnicode_FromString(phase); if (phase_obj == NULL) { Py_XDECREF(info); - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks"); return; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 211d52d6cc7164..905a14f660ec6c 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1427,7 +1427,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, "collected", collected, "uncollectable", uncollectable); if (info == NULL) { - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + PyErr_FormatUnraisable("Exception ignored while " + "invoking gc callbacks"); return; } } @@ -1435,7 +1436,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, PyObject *phase_obj = PyUnicode_FromString(phase); if (phase_obj == NULL) { Py_XDECREF(info); - PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + PyErr_FormatUnraisable("Exception ignored while " + "invoking gc callbacks"); return; } diff --git a/Python/import.c b/Python/import.c index dd7a0b4b1ed8de..8cc8d3a503bffa 100644 --- a/Python/import.c +++ b/Python/import.c @@ -594,7 +594,8 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) if (PyList_SetSlice(MODULES_BY_INDEX(interp), 0, PyList_GET_SIZE(MODULES_BY_INDEX(interp)), NULL)) { - PyErr_FormatUnraisable("Exception ignored on clearing interpreters module list"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing interpreters module list"); } } @@ -4080,13 +4081,15 @@ _PyImport_FiniCore(PyInterpreterState *interp) int verbose = _PyInterpreterState_GetConfig(interp)->verbose; if (_PySys_ClearAttrString(interp, "meta_path", verbose) < 0) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.meta_path"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing sys.meta_path"); } // XXX Pull in most of finalize_modules() in pylifecycle.c. if (_PySys_ClearAttrString(interp, "modules", verbose) < 0) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.modules"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing sys.modules"); } _PyImport_ClearCore(interp); @@ -4161,10 +4164,12 @@ _PyImport_FiniExternal(PyInterpreterState *interp) // XXX Uninstall importlib metapath importers here? if (_PySys_ClearAttrString(interp, "path_importer_cache", verbose) < 0) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.path_importer_cache"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing sys.path_importer_cache"); } if (_PySys_ClearAttrString(interp, "path_hooks", verbose) < 0) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.path_hooks"); + PyErr_FormatUnraisable("Exception ignored while " + "clearing sys.path_hooks"); } } diff --git a/Python/jit.c b/Python/jit.c index 4b01112f9b0a5a..33c2418084b1fd 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -563,7 +563,8 @@ _PyJIT_Free(_PyExecutorObject *executor) executor->jit_side_entry = NULL; executor->jit_size = 0; if (jit_free(memory, size)) { - PyErr_FormatUnraisable("Exception ignored when freeing JIT memory"); + PyErr_FormatUnraisable("Exception ignored while " + "freeing JIT memory"); } } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 7031d743174650..300a871d2cc4bf 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1475,13 +1475,15 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) PySys_WriteStderr("# clear builtins._\n"); } if (PyDict_SetItemString(interp->builtins, "_", Py_None) < 0) { - PyErr_FormatUnraisable("Exception ignored on setting builtin variable _"); + PyErr_FormatUnraisable("Exception ignored while " + "setting builtin variable _"); } const char * const *p; for (p = sys_deletes; *p != NULL; p++) { if (_PySys_ClearAttrString(interp, *p, verbose) < 0) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.%s", *p); + PyErr_FormatUnraisable("Exception ignored while " + "clearing sys.%s", *p); } } for (p = sys_files; *p != NULL; p+=2) { @@ -1492,13 +1494,15 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose) } PyObject *value; if (PyDict_GetItemStringRef(interp->sysdict, orig_name, &value) < 0) { - PyErr_FormatUnraisable("Exception ignored on restoring sys.%s", name); + PyErr_FormatUnraisable("Exception ignored while " + "restoring sys.%s", name); } if (value == NULL) { value = Py_NewRef(Py_None); } if (PyDict_SetItemString(interp->sysdict, name, value) < 0) { - PyErr_FormatUnraisable("Exception ignored on restoring sys.%s", name); + PyErr_FormatUnraisable("Exception ignored while " + "restoring sys.%s", name); } Py_DECREF(value); } @@ -1510,7 +1514,7 @@ finalize_remove_modules(PyObject *modules, int verbose) { PyObject *weaklist = PyList_New(0); if (weaklist == NULL) { - PyErr_FormatUnraisable("Exception ignored on removing modules"); + PyErr_FormatUnraisable("Exception ignored while removing modules"); } #define STORE_MODULE_WEAKREF(name, mod) \ @@ -1519,13 +1523,13 @@ finalize_remove_modules(PyObject *modules, int verbose) if (wr) { \ PyObject *tup = PyTuple_Pack(2, name, wr); \ if (!tup || PyList_Append(weaklist, tup) < 0) { \ - PyErr_FormatUnraisable("Exception ignored on removing modules"); \ + PyErr_FormatUnraisable("Exception ignored while removing modules"); \ } \ Py_XDECREF(tup); \ Py_DECREF(wr); \ } \ else { \ - PyErr_FormatUnraisable("Exception ignored on removing modules"); \ + PyErr_FormatUnraisable("Exception ignored while removing modules"); \ } \ } @@ -1536,7 +1540,7 @@ finalize_remove_modules(PyObject *modules, int verbose) } \ STORE_MODULE_WEAKREF(name, mod); \ if (PyObject_SetItem(modules, name, Py_None) < 0) { \ - PyErr_FormatUnraisable("Exception ignored on removing modules"); \ + PyErr_FormatUnraisable("Exception ignored while removing modules"); \ } \ } @@ -1550,14 +1554,14 @@ finalize_remove_modules(PyObject *modules, int verbose) else { PyObject *iterator = PyObject_GetIter(modules); if (iterator == NULL) { - PyErr_FormatUnraisable("Exception ignored on removing modules"); + PyErr_FormatUnraisable("Exception ignored while removing modules"); } else { PyObject *key; while ((key = PyIter_Next(iterator))) { PyObject *value = PyObject_GetItem(modules, key); if (value == NULL) { - PyErr_FormatUnraisable("Exception ignored on removing modules"); + PyErr_FormatUnraisable("Exception ignored while removing modules"); continue; } CLEAR_MODULE(key, value); @@ -1565,7 +1569,7 @@ finalize_remove_modules(PyObject *modules, int verbose) Py_DECREF(key); } if (PyErr_Occurred()) { - PyErr_FormatUnraisable("Exception ignored on removing modules"); + PyErr_FormatUnraisable("Exception ignored while removing modules"); } Py_DECREF(iterator); } @@ -1585,7 +1589,7 @@ finalize_clear_modules_dict(PyObject *modules) } else { if (PyObject_CallMethodNoArgs(modules, &_Py_ID(clear)) == NULL) { - PyErr_FormatUnraisable("Exception ignored on clearing sys.modules"); + PyErr_FormatUnraisable("Exception ignored while clearing sys.modules"); } } } @@ -1597,11 +1601,11 @@ finalize_restore_builtins(PyThreadState *tstate) PyInterpreterState *interp = tstate->interp; PyObject *dict = PyDict_Copy(interp->builtins); if (dict == NULL) { - PyErr_FormatUnraisable("Exception ignored on restoring builtins"); + PyErr_FormatUnraisable("Exception ignored while restoring builtins"); } PyDict_Clear(interp->builtins); if (PyDict_Update(interp->builtins, interp->builtins_copy)) { - PyErr_FormatUnraisable("Exception ignored on restoring builtins"); + PyErr_FormatUnraisable("Exception ignored while restoring builtins"); } Py_XDECREF(dict); } @@ -1773,7 +1777,7 @@ flush_std_files(void) if (fout != NULL && fout != Py_None && !file_is_closed(fout)) { if (_PyFile_Flush(fout) < 0) { - PyErr_FormatUnraisable("Exception ignored on flushing sys.stdout"); + PyErr_FormatUnraisable("Exception ignored while flushing sys.stdout"); status = -1; } } From 8df5193d37f70a1478642c4b456dcc7d6df6c117 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:49:06 -0800 Subject: [PATCH 018/311] gh-127975: Avoid reusing quote types in ast.unparse if not needed (#127980) --- Lib/ast.py | 11 ++++++++--- Lib/test/test_unparse.py | 10 ++++++---- .../2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst diff --git a/Lib/ast.py b/Lib/ast.py index 154d2c8c1f9ebb..0937c27bdf8a11 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1196,9 +1196,14 @@ def visit_JoinedStr(self, node): fallback_to_repr = True break quote_types = new_quote_types - elif "\n" in value: - quote_types = [q for q in quote_types if q in _MULTI_QUOTES] - assert quote_types + else: + if "\n" in value: + quote_types = [q for q in quote_types if q in _MULTI_QUOTES] + assert quote_types + + new_quote_types = [q for q in quote_types if q not in value] + if new_quote_types: + quote_types = new_quote_types new_fstring_parts.append(value) if fallback_to_repr: diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 332919540da4d6..f6c4f1f3f6476a 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -513,11 +513,13 @@ def test_class_bases_and_keywords(self): self.check_src_roundtrip("class X(*args, **kwargs):\n pass") def test_fstrings(self): - self.check_src_roundtrip("f'-{f'*{f'+{f'.{x}.'}+'}*'}-'") - self.check_src_roundtrip("f'\\u2028{'x'}'") + self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''') + self.check_src_roundtrip('''f\'-{f\'\'\'*{f"""+{f".{f'{x}'}."}+"""}*\'\'\'}-\'''') + self.check_src_roundtrip('''f\'-{f\'*{f\'\'\'+{f""".{f"{f'{x}'}"}."""}+\'\'\'}*\'}-\'''') + self.check_src_roundtrip('''f"\\u2028{'x'}"''') self.check_src_roundtrip(r"f'{x}\n'") - self.check_src_roundtrip("f'{'\\n'}\\n'") - self.check_src_roundtrip("f'{f'{x}\\n'}\\n'") + self.check_src_roundtrip('''f"{'\\n'}\\n"''') + self.check_src_roundtrip('''f"{f'{x}\\n'}\\n"''') def test_docstrings(self): docstrings = ( diff --git a/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst b/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst new file mode 100644 index 00000000000000..597fa41deb811c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst @@ -0,0 +1 @@ +Avoid reusing quote types in :func:`ast.unparse` if not needed. From e3eba8ce266f90d9f8faeb5b2b4b64e56110bd2a Mon Sep 17 00:00:00 2001 From: Michael Osipov Date: Fri, 31 Jan 2025 10:02:45 +0100 Subject: [PATCH 019/311] gh-129393: Make 'sys.platform' return "freebsd" only on FreeBSD (#129394) Make 'sys.platform' return "freebsd" only on FreeBSD without major version. --- Doc/library/sys.rst | 15 ++++++++++----- Doc/whatsnew/3.14.rst | 3 +++ ...2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst | 2 ++ configure | 1 + configure.ac | 1 + 5 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 151fd60532048a..5a096235713319 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1422,6 +1422,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only AIX ``'aix'`` Android ``'android'`` Emscripten ``'emscripten'`` + FreeBSD ``'freebsd'`` iOS ``'ios'`` Linux ``'linux'`` macOS ``'darwin'`` @@ -1432,12 +1433,12 @@ always available. Unless explicitly noted otherwise, all variables are read-only On Unix systems not listed in the table, the value is the lowercased OS name as returned by ``uname -s``, with the first part of the version as returned by - ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, *at the time - when Python was built*. Unless you want to test for a specific system - version, it is therefore recommended to use the following idiom:: + ``uname -r`` appended, e.g. ``'sunos5'``, *at the time when Python was built*. + Unless you want to test for a specific system version, it is therefore + recommended to use the following idiom:: - if sys.platform.startswith('freebsd'): - # FreeBSD-specific code here... + if sys.platform.startswith('sunos'): + # SunOS-specific code here... .. versionchanged:: 3.3 On Linux, :data:`sys.platform` doesn't contain the major version anymore. @@ -1451,6 +1452,10 @@ always available. Unless explicitly noted otherwise, all variables are read-only On Android, :data:`sys.platform` now returns ``'android'`` rather than ``'linux'``. + .. versionchanged:: 3.14 + On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore. + It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. + .. seealso:: :data:`os.name` has a coarser granularity. :func:`os.uname` gives diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 8d4cb94ae2d805..484e306335829a 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -649,6 +649,9 @@ sys which only exists in specialized builds of Python, may now return objects from other interpreters than the one it's called in. +* On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore. + It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. + sys.monitoring -------------- diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst new file mode 100644 index 00000000000000..e36e6f565efd81 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst @@ -0,0 +1,2 @@ +On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore. +It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. diff --git a/configure b/configure index 885c2cf7828d6c..3eb787f788bfb9 100755 --- a/configure +++ b/configure @@ -4132,6 +4132,7 @@ then case $MACHDEP in aix*) MACHDEP="aix";; + freebsd*) MACHDEP="freebsd";; linux-android*) MACHDEP="android";; linux*) MACHDEP="linux";; cygwin*) MACHDEP="cygwin";; diff --git a/configure.ac b/configure.ac index f89a0801948ca5..c0130b8082cd8a 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,7 @@ then case $MACHDEP in aix*) MACHDEP="aix";; + freebsd*) MACHDEP="freebsd";; linux-android*) MACHDEP="android";; linux*) MACHDEP="linux";; cygwin*) MACHDEP="cygwin";; From 674befbd7be3bffee66772ff9fdef168feda47ef Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 31 Jan 2025 02:50:54 -0800 Subject: [PATCH 020/311] GH-129386: Add `test.support.reset_code` (GH-129486) --- Lib/test/support/__init__.py | 7 +++++++ Lib/test/test_capi/test_opt.py | 7 ++++--- Lib/test/test_dis.py | 4 ++-- Lib/test/test_embed.py | 3 ++- Lib/test/test_opcache.py | 8 +++++--- .../Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst | 2 ++ 6 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 6436753f998a16..230bb240c89f77 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -66,6 +66,7 @@ "BrokenIter", "in_systemd_nspawn_sync_suppressed", "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", + "reset_code", ] @@ -1286,6 +1287,12 @@ def requires_specialization_ft(test): _opcode.ENABLE_SPECIALIZATION_FT, "requires specialization")(test) +def reset_code(f: types.FunctionType) -> types.FunctionType: + """Clear all specializations, local instrumentation, and JIT code for the given function.""" + f.__code__ = f.__code__.replace() + return f + + #======================================================================= # Check for the presence of docstrings. diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index d3aea37e094e61..02e534caec1162 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -9,7 +9,8 @@ import _opcode from test.support import (script_helper, requires_specialization, - import_helper, Py_GIL_DISABLED, requires_jit_enabled) + import_helper, Py_GIL_DISABLED, requires_jit_enabled, + reset_code) _testinternalcapi = import_helper.import_module("_testinternalcapi") @@ -19,11 +20,11 @@ @contextlib.contextmanager def clear_executors(func): # Clear executors in func before and after running a block - func.__code__ = func.__code__.replace() + reset_code(func) try: yield finally: - func.__code__ = func.__code__.replace() + reset_code(func) def get_first_executor(func): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index bba2ac80aa6769..e99289cf66af67 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -15,7 +15,7 @@ import unittest from test.support import (captured_stdout, requires_debug_ranges, requires_specialization, cpython_only, - os_helper, import_helper) + os_helper, import_helper, reset_code) from test.support.bytecode_helper import BytecodeTestCase @@ -1356,7 +1356,7 @@ def f(): self.code_quicken(f) else: # "copy" the code to un-quicken it: - f.__code__ = f.__code__.replace() + reset_code(f) for instruction in _unroll_caches_as_Instructions(dis.get_instructions( f, show_caches=True, adaptive=adaptive ), show_caches=True): diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 72221379a00051..cd65496cafb04d 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -391,6 +391,7 @@ def test_specialized_static_code_gets_unspecialized_at_Py_FINALIZE(self): import importlib._bootstrap import opcode import test.test_dis + import test.support def is_specialized(f): for instruction in dis.get_instructions(f, adaptive=True): @@ -409,7 +410,7 @@ def is_specialized(f): func = importlib._bootstrap._handle_fromlist # "copy" the code to un-specialize it: - func.__code__ = func.__code__.replace() + test.support.reset_code(func) assert not is_specialized(func), "specialized instructions found" diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 2defe74892786d..87de4c94ba26fb 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -6,7 +6,7 @@ import unittest from test.support import (threading_helper, check_impl_detail, requires_specialization, requires_specialization_ft, - cpython_only, requires_jit_disabled) + cpython_only, requires_jit_disabled, reset_code) from test.support.import_helper import import_module # Skip this module on other interpreters, it is cpython specific: @@ -579,9 +579,9 @@ def assert_races_do_not_crash( # Reset: if check_items: for item in items: - item.__code__ = item.__code__.replace() + reset_code(item) else: - read.__code__ = read.__code__.replace() + reset_code(read) # Specialize: for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): read(items) @@ -1552,6 +1552,7 @@ def test_store_attr_instance_value(self): class C: pass + @reset_code def set_value(n): c = C() for i in range(n): @@ -1577,6 +1578,7 @@ class C: for i in range(_testinternalcapi.SHARED_KEYS_MAX_SIZE - 1): setattr(c, f"_{i}", None) + @reset_code def set_value(n): for i in range(n): c.x = i diff --git a/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst b/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst new file mode 100644 index 00000000000000..a03f596bc46c30 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst @@ -0,0 +1,2 @@ +Add ``test.support.reset_code``, which can be used to reset various +bytecode-level optimizations and local instrumentation for a function. From 31c82c28f927b7e55c7dfdd548322c6c36760278 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 Jan 2025 12:17:37 +0100 Subject: [PATCH 021/311] GH-128469: Revert "warn when libpython was loaded from outside the build directory (#128645)" (#129506) --- Lib/test/test_getpath.py | 37 ------------------------------------- Modules/getpath.py | 13 ------------- 2 files changed, 50 deletions(-) diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index ca7cee0c39872a..f86df9d0d03485 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -1,13 +1,7 @@ import copy import ntpath -import os import pathlib import posixpath -import shutil -import subprocess -import sys -import sysconfig -import tempfile import unittest from test.support import verbose @@ -870,37 +864,6 @@ def test_PYTHONHOME_in_venv(self): actual = getpath(ns, expected) self.assertEqual(expected, actual) - -class RealGetPathTests(unittest.TestCase): - @unittest.skipUnless( - sysconfig.is_python_build(), - 'Test only available when running from the buildir', - ) - @unittest.skipUnless( - any(sys.platform.startswith(p) for p in ('linux', 'freebsd', 'centos')), - 'Test only support on Linux-like OS-es (support LD_LIBRARY_PATH)', - ) - @unittest.skipUnless( - sysconfig.get_config_var('LDLIBRARY') != sysconfig.get_config_var('LIBRARY'), - 'Test only available when using a dynamic libpython', - ) - def test_builddir_wrong_library_warning(self): - library_name = sysconfig.get_config_var('INSTSONAME') - with tempfile.TemporaryDirectory() as tmpdir: - shutil.copy2( - os.path.join(sysconfig.get_config_var('srcdir'), library_name), - os.path.join(tmpdir, library_name) - ) - env = os.environ.copy() - env['LD_LIBRARY_PATH'] = tmpdir - process = subprocess.run( - [sys.executable, '-c', ''], - env=env, check=True, capture_output=True, text=True, - ) - error_msg = 'The runtime library has been loaded from outside the build directory' - self.assertTrue(process.stderr.startswith(error_msg), process.stderr) - - # ****************************************************************************** DEFAULT_NAMESPACE = dict( diff --git a/Modules/getpath.py b/Modules/getpath.py index 9d531e29becbc8..be2210345afbda 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -785,19 +785,6 @@ def search_up(prefix, *landmarks, test=isfile): base_exec_prefix = config.get('base_exec_prefix') or EXEC_PREFIX or base_prefix -# ****************************************************************************** -# MISC. RUNTIME WARNINGS -# ****************************************************************************** - -# When running Python from the build directory, if libpython is dynamically -# linked, the wrong library might be loaded. -if build_prefix and library and not dirname(abspath(library)).startswith(build_prefix): - msg = f'The runtime library has been loaded from outside the build directory ({library})!' - if os_name == 'posix': - msg += ' Consider setting LD_LIBRARY_PATH=. to force it to be loaded from the build directory.' - warn(msg) - - # ****************************************************************************** # SET pythonpath FROM _PTH FILE # ****************************************************************************** From c3ae5c9e4ad121f8ba60ffe81ca4e2a9c52dc659 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 31 Jan 2025 12:12:24 +0000 Subject: [PATCH 022/311] GH-128563: Simplify recursion check in `_PyEval_EvalFrameDefault` (GH-129481) Simplify recursion check in _PyEval_EvalFrameDefault --- Python/ceval.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 10c20faf852479..e3b87441f8088d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -786,7 +786,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyInterpreterFrame entry_frame; - + if (_Py_EnterRecursiveCallTstate(tstate, "")) { + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyEval_FrameClearAndPop(tstate, frame); + return NULL; + } #if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG) /* Set these to invalid but identifiable values for debugging. */ @@ -811,11 +815,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int tstate->current_frame = frame; tstate->c_recursion_remaining -= (PY_EVAL_C_STACK_UNITS - 1); - if (_Py_EnterRecursiveCallTstate(tstate, "")) { - tstate->c_recursion_remaining--; - tstate->py_recursion_remaining--; - goto exit_unwind; - } /* support for generator.throw() */ if (throwflag) { From 0373926260b2b96b025019b5ddcdd4423858e41f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 13:16:08 +0100 Subject: [PATCH 023/311] gh-129354: Use PyErr_FormatUnraisable() function (#129511) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). --- Modules/_ctypes/_ctypes.c | 9 ++++--- Modules/_ctypes/callbacks.c | 47 +++++++++++++++++-------------------- Modules/_lsprof.c | 6 +++-- Objects/unicodeobject.c | 4 +++- Objects/weakrefobject.c | 9 ++++--- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 606b4636380f11..7c0ac1a57f534c 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -464,7 +464,8 @@ CType_Type_traverse(PyObject *self, visitproc visit, void *arg) { StgInfo *info = _PyStgInfo_FromType_NoState(self); if (!info) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored while " + "calling ctypes traverse function %R", self); } if (info) { Py_VISIT(info->proto); @@ -495,7 +496,8 @@ CType_Type_clear(PyObject *self) { StgInfo *info = _PyStgInfo_FromType_NoState(self); if (!info) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored while " + "clearing ctypes %R", self); } if (info) { ctype_clear_stginfo(info); @@ -508,7 +510,8 @@ CType_Type_dealloc(PyObject *self) { StgInfo *info = _PyStgInfo_FromType_NoState(self); if (!info) { - PyErr_WriteUnraisable(NULL); // NULL avoids segfault here + PyErr_FormatUnraisable("Exception ignored while " + "deallocating ctypes %R", self); } if (info) { PyMem_Free(info->ffi_type_pointer.elements); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index f2df7052d84fb1..11f49963a54c54 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -487,39 +487,31 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { PyObject *func, *result; long retval; - static PyObject *context; - - if (context == NULL) - context = PyUnicode_InternFromString("_ctypes.DllGetClassObject"); func = PyImport_ImportModuleAttrString("ctypes", "DllGetClassObject"); if (!func) { - PyErr_WriteUnraisable(context ? context : Py_None); /* There has been a warning before about this already */ - return E_FAIL; + goto error; } { PyObject *py_rclsid = PyLong_FromVoidPtr((void *)rclsid); if (py_rclsid == NULL) { Py_DECREF(func); - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } PyObject *py_riid = PyLong_FromVoidPtr((void *)riid); if (py_riid == NULL) { Py_DECREF(func); Py_DECREF(py_rclsid); - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } PyObject *py_ppv = PyLong_FromVoidPtr(ppv); if (py_ppv == NULL) { Py_DECREF(py_rclsid); Py_DECREF(py_riid); Py_DECREF(func); - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } result = PyObject_CallFunctionObjArgs(func, py_rclsid, @@ -532,17 +524,21 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) } Py_DECREF(func); if (!result) { - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } retval = PyLong_AsLong(result); if (PyErr_Occurred()) { - PyErr_WriteUnraisable(context ? context : Py_None); - retval = E_FAIL; + Py_DECREF(result); + goto error; } Py_DECREF(result); return retval; + +error: + PyErr_FormatUnraisable("Exception ignored while calling " + "ctypes.DllGetClassObject"); + return E_FAIL; } STDAPI DllGetClassObject(REFCLSID rclsid, @@ -563,10 +559,6 @@ long Call_CanUnloadNow(void) { PyObject *mod, *func, *result; long retval; - static PyObject *context; - - if (context == NULL) - context = PyUnicode_InternFromString("_ctypes.DllCanUnloadNow"); mod = PyImport_ImportModule("ctypes"); if (!mod) { @@ -580,24 +572,27 @@ long Call_CanUnloadNow(void) func = PyObject_GetAttrString(mod, "DllCanUnloadNow"); Py_DECREF(mod); if (!func) { - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } result = _PyObject_CallNoArgs(func); Py_DECREF(func); if (!result) { - PyErr_WriteUnraisable(context ? context : Py_None); - return E_FAIL; + goto error; } retval = PyLong_AsLong(result); if (PyErr_Occurred()) { - PyErr_WriteUnraisable(context ? context : Py_None); - retval = E_FAIL; + Py_DECREF(result); + goto error; } Py_DECREF(result); return retval; + +error: + PyErr_FormatUnraisable("Exception ignored while calling " + "ctypes.DllCanUnloadNow"); + return E_FAIL; } /* diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index fa1d333ecf3829..eab26b39be14ef 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -97,7 +97,8 @@ static PyTime_t CallExternalTimer(ProfilerObject *pObj) pObj->flags &= ~POF_EXT_TIMER; if (o == NULL) { - PyErr_WriteUnraisable(pObj->externalTimer); + PyErr_FormatUnraisable("Exception ignored while calling " + "_lsprof timer %R", pObj->externalTimer); return 0; } @@ -116,7 +117,8 @@ static PyTime_t CallExternalTimer(ProfilerObject *pObj) } Py_DECREF(o); if (err < 0) { - PyErr_WriteUnraisable(pObj->externalTimer); + PyErr_FormatUnraisable("Exception ignored while calling " + "_lsprof timer %R", pObj->externalTimer); return 0; } return result; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c6f13f60ad741f..75967d69ed374d 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1735,7 +1735,9 @@ unicode_dealloc(PyObject *unicode) PyObject *popped; int r = PyDict_Pop(interned, unicode, &popped); if (r == -1) { - PyErr_WriteUnraisable(unicode); + PyErr_FormatUnraisable("Exception ignored while " + "removing an interned string %R", + unicode); // We don't know what happened to the string. It's probably // best to leak it: // - if it was popped, there are no more references to it diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 05ae43d1df475b..bd4c4ac9b3475a 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -987,10 +987,13 @@ handle_callback(PyWeakReference *ref, PyObject *callback) { PyObject *cbresult = PyObject_CallOneArg(callback, (PyObject *)ref); - if (cbresult == NULL) - PyErr_WriteUnraisable(callback); - else + if (cbresult == NULL) { + PyErr_FormatUnraisable("Exception ignored while " + "calling weakref callback %R", callback); + } + else { Py_DECREF(cbresult); + } } /* This function is called by the tp_dealloc handler to clear weak references. From 22b2d37f4211f51a3c90680edeb4c10261f58158 Mon Sep 17 00:00:00 2001 From: qm2k Date: Fri, 31 Jan 2025 12:16:24 +0000 Subject: [PATCH 024/311] gh-71494: string.Formatter: support keys/attributes in unnumbered fields (GH-21767) --- Lib/string.py | 15 ++++++++------- Lib/test/test_string.py | 19 +++++++++++++++++++ .../2020-08-07-16-55-57.bpo-27307.Xqzzda.rst | 1 + 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst diff --git a/Lib/string.py b/Lib/string.py index 2eab6d4f595c4e..c4f05c7223ce8a 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -212,19 +212,20 @@ def _vformat(self, format_string, args, kwargs, used_args, recursion_depth, # this is some markup, find the object and do # the formatting - # handle arg indexing when empty field_names are given. - if field_name == '': + # handle arg indexing when empty field first parts are given. + field_first, _ = _string.formatter_field_name_split(field_name) + if field_first == '': if auto_arg_index is False: raise ValueError('cannot switch from manual field ' 'specification to automatic field ' 'numbering') - field_name = str(auto_arg_index) + field_name = str(auto_arg_index) + field_name auto_arg_index += 1 - elif field_name.isdigit(): + elif isinstance(field_first, int): if auto_arg_index: - raise ValueError('cannot switch from manual field ' - 'specification to automatic field ' - 'numbering') + raise ValueError('cannot switch from automatic field ' + 'numbering to manual field ' + 'specification') # disable auto arg incrementing, if it gets # used later on, then an exception will be raised auto_arg_index = False diff --git a/Lib/test/test_string.py b/Lib/test/test_string.py index 824b89ad517c12..f6d112d8a93ec4 100644 --- a/Lib/test/test_string.py +++ b/Lib/test/test_string.py @@ -1,6 +1,7 @@ import unittest import string from string import Template +import types class ModuleTest(unittest.TestCase): @@ -101,6 +102,24 @@ def test_index_lookup(self): with self.assertRaises(KeyError): fmt.format("{0[2]}{0[0]}", {}) + def test_auto_numbering_lookup(self): + fmt = string.Formatter() + namespace = types.SimpleNamespace(foo=types.SimpleNamespace(bar='baz')) + widths = [None, types.SimpleNamespace(qux=4)] + self.assertEqual( + fmt.format("{.foo.bar:{[1].qux}}", namespace, widths), 'baz ') + + def test_auto_numbering_reenterability(self): + class ReenteringFormatter(string.Formatter): + def format_field(self, value, format_spec): + if format_spec.isdigit() and int(format_spec) > 0: + return self.format('{:{}}!', value, int(format_spec) - 1) + else: + return super().format_field(value, format_spec) + fmt = ReenteringFormatter() + x = types.SimpleNamespace(a='X') + self.assertEqual(fmt.format('{.a:{}}', x, 3), 'X!!!') + def test_override_get_value(self): class NamespaceFormatter(string.Formatter): def __init__(self, namespace={}): diff --git a/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst new file mode 100644 index 00000000000000..6e7a856d994cb6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst @@ -0,0 +1 @@ +Add attribute and item access support to :class:`string.Formatter` in auto-numbering mode, which allows format strings like '{.name}' and '{[1]}'. From 8b70ff587220ebaefae9b6283b4993c78a726324 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 13:51:58 +0100 Subject: [PATCH 025/311] gh-93649: Move PyFrame C API tests to test_capi (#129512) * Add Lib/test/test_capi/test_frame.py file. * Move C API tests from test_frame to test_capi.test_frame. * Add Modules/_testcapi/frame.c file. * Move C API tests from _testcapimodule.c to frame.c --- Lib/test/test_capi/test_frame.py | 56 +++++++++++++ Lib/test/test_frame.py | 45 ---------- Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/frame.c | 134 ++++++++++++++++++++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 114 +------------------------ PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + 8 files changed, 199 insertions(+), 157 deletions(-) create mode 100644 Lib/test/test_capi/test_frame.py create mode 100644 Modules/_testcapi/frame.c diff --git a/Lib/test/test_capi/test_frame.py b/Lib/test/test_capi/test_frame.py new file mode 100644 index 00000000000000..23cb8e3dada9d4 --- /dev/null +++ b/Lib/test/test_capi/test_frame.py @@ -0,0 +1,56 @@ +import sys +import unittest +from test.support import import_helper + + +_testcapi = import_helper.import_module('_testcapi') + + +class FrameTest(unittest.TestCase): + def getframe(self): + return sys._getframe() + + def test_frame_getters(self): + frame = self.getframe() + self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame)) + self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame)) + self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame)) + self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame)) + + def test_getvar(self): + current_frame = sys._getframe() + x = 1 + self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1) + self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1) + with self.assertRaises(NameError): + _testcapi.frame_getvar(current_frame, "y") + with self.assertRaises(NameError): + _testcapi.frame_getvarstring(current_frame, b"y") + + # wrong name type + with self.assertRaises(TypeError): + _testcapi.frame_getvar(current_frame, b'x') + with self.assertRaises(TypeError): + _testcapi.frame_getvar(current_frame, 123) + + def getgenframe(self): + yield sys._getframe() + + def test_frame_get_generator(self): + gen = self.getgenframe() + frame = next(gen) + self.assertIs(gen, _testcapi.frame_getgenerator(frame)) + + def test_frame_fback_api(self): + """Test that accessing `f_back` does not cause a segmentation fault on + a frame created with `PyFrame_New` (GH-99110).""" + def dummy(): + pass + + frame = _testcapi.frame_new(dummy.__code__, globals(), locals()) + # The following line should not cause a segmentation fault. + self.assertIsNone(frame.f_back) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py index 7bd13eada8fedf..4d086064023488 100644 --- a/Lib/test/test_frame.py +++ b/Lib/test/test_frame.py @@ -773,51 +773,6 @@ def f(): self.assertIs(catcher.unraisable.exc_type, TypeError) self.assertIsNone(weak()) -@unittest.skipIf(_testcapi is None, 'need _testcapi') -class TestCAPI(unittest.TestCase): - def getframe(self): - return sys._getframe() - - def test_frame_getters(self): - frame = self.getframe() - self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame)) - self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame)) - self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame)) - self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame)) - - def test_getvar(self): - current_frame = sys._getframe() - x = 1 - self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1) - self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1) - with self.assertRaises(NameError): - _testcapi.frame_getvar(current_frame, "y") - with self.assertRaises(NameError): - _testcapi.frame_getvarstring(current_frame, b"y") - - # wrong name type - with self.assertRaises(TypeError): - _testcapi.frame_getvar(current_frame, b'x') - with self.assertRaises(TypeError): - _testcapi.frame_getvar(current_frame, 123) - - def getgenframe(self): - yield sys._getframe() - - def test_frame_get_generator(self): - gen = self.getgenframe() - frame = next(gen) - self.assertIs(gen, _testcapi.frame_getgenerator(frame)) - - def test_frame_fback_api(self): - """Test that accessing `f_back` does not cause a segmentation fault on - a frame created with `PyFrame_New` (GH-99110).""" - def dummy(): - pass - - frame = _testcapi.frame_new(dummy.__code__, globals(), locals()) - # The following line should not cause a segmentation fault. - self.assertIsNone(frame.f_back) if __name__ == "__main__": unittest.main() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index b31bf2734e4fd1..1fd7c8bd2e2d73 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/frame.c b/Modules/_testcapi/frame.c new file mode 100644 index 00000000000000..5748dca948ea94 --- /dev/null +++ b/Modules/_testcapi/frame.c @@ -0,0 +1,134 @@ +#include "parts.h" +#include "util.h" + +#include "frameobject.h" // PyFrame_New() + + +static PyObject * +frame_getlocals(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetLocals((PyFrameObject *)frame); +} + + +static PyObject * +frame_getglobals(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetGlobals((PyFrameObject *)frame); +} + + +static PyObject * +frame_getgenerator(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetGenerator((PyFrameObject *)frame); +} + + +static PyObject * +frame_getbuiltins(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetBuiltins((PyFrameObject *)frame); +} + + +static PyObject * +frame_getlasti(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + int lasti = PyFrame_GetLasti((PyFrameObject *)frame); + if (lasti < 0) { + assert(lasti == -1); + Py_RETURN_NONE; + } + return PyLong_FromLong(lasti); +} + + +static PyObject * +frame_new(PyObject *self, PyObject *args) +{ + PyObject *code, *globals, *locals; + if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) { + return NULL; + } + if (!PyCode_Check(code)) { + PyErr_SetString(PyExc_TypeError, "argument must be a code object"); + return NULL; + } + PyThreadState *tstate = PyThreadState_Get(); + + return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals); +} + + +static PyObject * +frame_getvar(PyObject *self, PyObject *args) +{ + PyObject *frame, *name; + if (!PyArg_ParseTuple(args, "OO", &frame, &name)) { + return NULL; + } + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + + return PyFrame_GetVar((PyFrameObject *)frame, name); +} + + +static PyObject * +frame_getvarstring(PyObject *self, PyObject *args) +{ + PyObject *frame; + const char *name; + if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) { + return NULL; + } + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + + return PyFrame_GetVarString((PyFrameObject *)frame, name); +} + + +static PyMethodDef test_methods[] = { + {"frame_getlocals", frame_getlocals, METH_O, NULL}, + {"frame_getglobals", frame_getglobals, METH_O, NULL}, + {"frame_getgenerator", frame_getgenerator, METH_O, NULL}, + {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL}, + {"frame_getlasti", frame_getlasti, METH_O, NULL}, + {"frame_new", frame_new, METH_VARARGS, NULL}, + {"frame_getvar", frame_getvar, METH_VARARGS, NULL}, + {"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL}, + {NULL}, +}; + +int +_PyTestCapi_Init_Frame(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} + diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index 792552d8097312..e5d2502edf5362 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -62,5 +62,6 @@ int _PyTestCapi_Init_Monitoring(PyObject *module); int _PyTestCapi_Init_Object(PyObject *module); int _PyTestCapi_Init_Config(PyObject *mod); int _PyTestCapi_Init_Import(PyObject *mod); +int _PyTestCapi_Init_Frame(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4da23ba82d5756..d25e61dbc3d588 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2533,109 +2533,6 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } -static PyObject * -frame_getlocals(PyObject *self, PyObject *frame) -{ - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - return PyFrame_GetLocals((PyFrameObject *)frame); -} - -static PyObject * -frame_getglobals(PyObject *self, PyObject *frame) -{ - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - return PyFrame_GetGlobals((PyFrameObject *)frame); -} - -static PyObject * -frame_getgenerator(PyObject *self, PyObject *frame) -{ - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - return PyFrame_GetGenerator((PyFrameObject *)frame); -} - -static PyObject * -frame_getbuiltins(PyObject *self, PyObject *frame) -{ - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - return PyFrame_GetBuiltins((PyFrameObject *)frame); -} - -static PyObject * -frame_getlasti(PyObject *self, PyObject *frame) -{ - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - int lasti = PyFrame_GetLasti((PyFrameObject *)frame); - if (lasti < 0) { - assert(lasti == -1); - Py_RETURN_NONE; - } - return PyLong_FromLong(lasti); -} - -static PyObject * -frame_new(PyObject *self, PyObject *args) -{ - PyObject *code, *globals, *locals; - if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) { - return NULL; - } - if (!PyCode_Check(code)) { - PyErr_SetString(PyExc_TypeError, "argument must be a code object"); - return NULL; - } - PyThreadState *tstate = PyThreadState_Get(); - - return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals); -} - -static PyObject * -test_frame_getvar(PyObject *self, PyObject *args) -{ - PyObject *frame, *name; - if (!PyArg_ParseTuple(args, "OO", &frame, &name)) { - return NULL; - } - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - - return PyFrame_GetVar((PyFrameObject *)frame, name); -} - -static PyObject * -test_frame_getvarstring(PyObject *self, PyObject *args) -{ - PyObject *frame; - const char *name; - if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) { - return NULL; - } - if (!PyFrame_Check(frame)) { - PyErr_SetString(PyExc_TypeError, "argument must be a frame"); - return NULL; - } - - return PyFrame_GetVarString((PyFrameObject *)frame, name); -} - - static PyObject * gen_get_code(PyObject *self, PyObject *gen) { @@ -3599,14 +3496,6 @@ static PyMethodDef TestMethods[] = { {"type_get_tp_mro", type_get_tp_mro, METH_O}, {"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, - {"frame_getlocals", frame_getlocals, METH_O, NULL}, - {"frame_getglobals", frame_getglobals, METH_O, NULL}, - {"frame_getgenerator", frame_getgenerator, METH_O, NULL}, - {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL}, - {"frame_getlasti", frame_getlasti, METH_O, NULL}, - {"frame_new", frame_new, METH_VARARGS, NULL}, - {"frame_getvar", test_frame_getvar, METH_VARARGS, NULL}, - {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL}, {"gen_get_code", gen_get_code, METH_O, NULL}, {"get_feature_macros", get_feature_macros, METH_NOARGS, NULL}, {"test_code_api", test_code_api, METH_NOARGS, NULL}, @@ -4404,6 +4293,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Import(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Frame(m) < 0) { + return NULL; + } PyState_AddModule(m, &_testcapimodule); return m; diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 733bb69acb16e2..e94b4c46e6dbb8 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -128,6 +128,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index e8ddd537674a9c..782f91afb1996f 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -117,6 +117,9 @@ Source Files + + Source Files + From 3447f4a56a71a4017e55d8f46160a63f111ec373 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 14:20:35 +0100 Subject: [PATCH 026/311] gh-129354: Use PyErr_FormatUnraisable() function (#129514) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). --- Modules/_asynciomodule.c | 6 ++++-- Modules/_testcapi/gc.c | 11 +++++++---- Modules/posixmodule.c | 18 ++++++++++++------ Modules/pyexpat.c | 3 ++- Python/gc.c | 20 +++++++++++++------- Python/gc_free_threading.c | 20 +++++++++++++------- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index d5d49658555f1a..b488fd92aa6817 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1715,7 +1715,8 @@ FutureObj_finalize(FutureObj *fut) if (func != NULL) { PyObject *res = PyObject_CallOneArg(func, context); if (res == NULL) { - PyErr_WriteUnraisable(func); + PyErr_FormatUnraisable("Exception ignored while calling asyncio " + "function %R", func); } else { Py_DECREF(res); @@ -2978,7 +2979,8 @@ TaskObj_finalize(TaskObj *task) if (func != NULL) { PyObject *res = PyObject_CallOneArg(func, context); if (res == NULL) { - PyErr_WriteUnraisable(func); + PyErr_FormatUnraisable("Exception ignored while calling asyncio " + "function %R", func); } else { Py_DECREF(res); diff --git a/Modules/_testcapi/gc.c b/Modules/_testcapi/gc.c index 7e33e0d4861e84..3691796302e500 100644 --- a/Modules/_testcapi/gc.c +++ b/Modules/_testcapi/gc.c @@ -94,7 +94,7 @@ slot_tp_del(PyObject *self) PyObject *tp_del = PyUnicode_InternFromString("__tp_del__"); if (tp_del == NULL) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored while deallocating"); PyErr_SetRaisedException(exc); return; } @@ -104,10 +104,13 @@ slot_tp_del(PyObject *self) if (del != NULL) { res = PyObject_CallOneArg(del, self); Py_DECREF(del); - if (res == NULL) - PyErr_WriteUnraisable(del); - else + if (res == NULL) { + PyErr_FormatUnraisable("Exception ignored while calling " + "deallocator %R", del); + } + else { Py_DECREF(res); + } } /* Restore the saved exception. */ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6d9b365ea9ceb2..b3b5572e1cfa30 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -606,8 +606,10 @@ run_at_forkers(PyObject *lst, int reverse) * one of the callbacks. */ cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst)); - if (cpy == NULL) - PyErr_WriteUnraisable(lst); + if (cpy == NULL) { + PyErr_FormatUnraisable("Exception ignored in atfork callback " + "while copying list %R", lst); + } else { if (reverse) PyList_Reverse(cpy); @@ -615,10 +617,13 @@ run_at_forkers(PyObject *lst, int reverse) PyObject *func, *res; func = PyList_GET_ITEM(cpy, i); res = _PyObject_CallNoArgs(func); - if (res == NULL) - PyErr_WriteUnraisable(func); - else + if (res == NULL) { + PyErr_FormatUnraisable("Exception ignored " + "in atfork callback %R", func); + } + else { Py_DECREF(res); + } } Py_DECREF(cpy); } @@ -16330,7 +16335,8 @@ ScandirIterator_finalize(ScandirIterator *iterator) "unclosed scandir iterator %R", iterator)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) { - PyErr_WriteUnraisable((PyObject *) iterator); + PyErr_FormatUnraisable("Exception ignored while finalizing " + "scandir iterator %R", iterator); } } } diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 9931ca2a8d4749..3290706f143b9a 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -1948,7 +1948,8 @@ pyexpat_capsule_destructor(PyObject *capsule) { void *p = PyCapsule_GetPointer(capsule, PyExpat_CAPSULE_NAME); if (p == NULL) { - PyErr_WriteUnraisable(capsule); + PyErr_FormatUnraisable("Exception ignored while destroying " + "pyexact capsule"); return; } PyMem_Free(p); diff --git a/Python/gc.c b/Python/gc.c index 420240fc3020be..0fb2f03b0406ad 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -994,7 +994,8 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old) /* copy-paste of weakrefobject.c's handle_callback() */ temp = PyObject_CallOneArg(callback, (PyObject *)wr); if (temp == NULL) { - PyErr_WriteUnraisable(callback); + PyErr_FormatUnraisable("Exception ignored on " + "calling weakref callback %R", callback); } else { Py_DECREF(temp); @@ -1797,7 +1798,8 @@ do_gc_callback(GCState *gcstate, const char *phase, Py_INCREF(cb); /* make sure cb doesn't go away */ r = PyObject_Vectorcall(cb, stack, 2, NULL); if (r == NULL) { - PyErr_WriteUnraisable(cb); + PyErr_FormatUnraisable("Exception ignored while " + "calling GC callback %R", cb); } else { Py_DECREF(r); @@ -2086,13 +2088,14 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp) "gc", NULL, message, PyList_GET_SIZE(gcstate->garbage))) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored in GC shutdown"); } if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) { PyObject *repr = NULL, *bytes = NULL; repr = PyObject_Repr(gcstate->garbage); if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) { - PyErr_WriteUnraisable(gcstate->garbage); + PyErr_FormatUnraisable("Exception ignored in GC shutdown " + "while formatting garbage"); } else { PySys_WriteStderr( @@ -2344,9 +2347,12 @@ PyObject_GC_Del(void *op) #ifdef Py_DEBUG PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, - "gc", NULL, "Object of type %s is not untracked before destruction", - Py_TYPE(op)->tp_name)) { - PyErr_WriteUnraisable(NULL); + "gc", NULL, + "Object of type %s is not untracked " + "before destruction", + Py_TYPE(op)->tp_name)) + { + PyErr_FormatUnraisable("Exception ignored on object deallocation"); } PyErr_SetRaisedException(exc); #endif diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 905a14f660ec6c..5d264e407e1cc8 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1128,7 +1128,8 @@ call_weakref_callbacks(struct collection_state *state) /* copy-paste of weakrefobject.c's handle_callback() */ PyObject *temp = PyObject_CallOneArg(callback, (PyObject *)wr); if (temp == NULL) { - PyErr_WriteUnraisable(callback); + PyErr_FormatUnraisable("Exception ignored while " + "calling weakref callback %R", callback); } else { Py_DECREF(temp); @@ -1447,7 +1448,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, Py_INCREF(cb); /* make sure cb doesn't go away */ r = PyObject_Vectorcall(cb, stack, 2, NULL); if (r == NULL) { - PyErr_WriteUnraisable(cb); + PyErr_FormatUnraisable("Exception ignored while " + "calling GC callback %R", cb); } else { Py_DECREF(r); @@ -2029,13 +2031,14 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp) "gc", NULL, message, PyList_GET_SIZE(gcstate->garbage))) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored in GC shutdown"); } if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) { PyObject *repr = NULL, *bytes = NULL; repr = PyObject_Repr(gcstate->garbage); if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) { - PyErr_WriteUnraisable(gcstate->garbage); + PyErr_FormatUnraisable("Exception ignored in GC shutdown " + "while formatting garbage"); } else { PySys_WriteStderr( @@ -2243,9 +2246,12 @@ PyObject_GC_Del(void *op) #ifdef Py_DEBUG PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, - "gc", NULL, "Object of type %s is not untracked before destruction", - ((PyObject*)op)->ob_type->tp_name)) { - PyErr_WriteUnraisable(NULL); + "gc", NULL, + "Object of type %s is not untracked " + "before destruction", + Py_TYPE(op)->tp_name)) + { + PyErr_FormatUnraisable("Exception ignored on object deallocation"); } PyErr_SetRaisedException(exc); #endif From 9d63ae5fe52d95059ab1bcd4cbb1f9e17033c897 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 31 Jan 2025 15:30:19 +0200 Subject: [PATCH 027/311] gh-129502: Fix handling errors in ctypes callbacks (GH-129504) Unlikely errors in preparing arguments for ctypes callback are now handled in the same way as errors raised in the callback of in converting the result of the callback -- using sys.unraisablehook() instead of sys.excepthook() and not setting sys.last_exc and other variables. --- ...-01-31-11-14-05.gh-issue-129502.j_ArNo.rst | 5 ++ Modules/_ctypes/callbacks.c | 66 +++++++++---------- 2 files changed, 35 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst diff --git a/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst new file mode 100644 index 00000000000000..e9e9d12c11d0ac --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst @@ -0,0 +1,5 @@ +Unlikely errors in preparing arguments for :mod:`ctypes` callback are now +handled in the same way as errors raised in the callback of in converting +the result of the callback -- using :func:`sys.unraisablehook` instead of +:func:`sys.excepthook` and not setting :data:`sys.last_exc` and other +variables. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 11f49963a54c54..b84bd25af8ec2c 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -81,22 +81,6 @@ PyType_Spec cthunk_spec = { /**************************************************************/ -static void -PrintError(const char *msg, ...) -{ - char buf[512]; - PyObject *f = PySys_GetObject("stderr"); - va_list marker; - - va_start(marker, msg); - PyOS_vsnprintf(buf, sizeof(buf), msg, marker); - va_end(marker); - if (f != NULL && f != Py_None) - PyFile_WriteString(buf, f); - PyErr_Print(); -} - - #ifdef MS_WIN32 /* * We must call AddRef() on non-NULL COM pointers we receive as arguments @@ -108,26 +92,23 @@ PrintError(const char *msg, ...) * after checking for PyObject_IsTrue(), but this would probably be somewhat * slower. */ -static void +static int TryAddRef(PyObject *cnv, CDataObject *obj) { IUnknown *punk; PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv); if (!attrdict) { - return; + return 0; } int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_)); if (r <= 0) { - if (r < 0) { - PrintError("getting _needs_com_addref_"); - } - return; + return r; } punk = *(IUnknown **)obj->b_ptr; if (punk) punk->lpVtbl->AddRef(punk); - return; + return 0; } #endif @@ -162,14 +143,13 @@ static void _CallPythonObject(ctypes_state *st, StgInfo *info; if (PyStgInfo_FromType(st, cnv, &info) < 0) { - goto Done; + goto Error; } if (info && info->getfunc && !_ctypes_simple_instance(st, cnv)) { PyObject *v = info->getfunc(*pArgs, info->size); if (!v) { - PrintError("create argument %zd:\n", i); - goto Done; + goto Error; } args[i] = v; /* XXX XXX XX @@ -182,24 +162,25 @@ static void _CallPythonObject(ctypes_state *st, /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */ CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv); if (!obj) { - PrintError("create argument %zd:\n", i); - goto Done; + goto Error; } if (!CDataObject_Check(st, obj)) { + PyErr_Format(PyExc_TypeError, + "%R returned unexpected result of type %T", cnv, obj); Py_DECREF(obj); - PrintError("unexpected result of create argument %zd:\n", i); - goto Done; + goto Error; } memcpy(obj->b_ptr, *pArgs, info->size); args[i] = (PyObject *)obj; #ifdef MS_WIN32 - TryAddRef(cnv, obj); + if (TryAddRef(cnv, obj) < 0) { + goto Error; + } #endif } else { - PyErr_SetString(PyExc_TypeError, - "cannot build parameter"); - PrintError("Parsing argument %zd\n", i); - goto Done; + PyErr_Format(PyExc_TypeError, + "cannot build parameter of type %R", cnv); + goto Error; } /* XXX error handling! */ pArgs++; @@ -207,8 +188,13 @@ static void _CallPythonObject(ctypes_state *st, if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { error_object = _ctypes_get_errobj(st, &space); - if (error_object == NULL) + if (error_object == NULL) { + PyErr_FormatUnraisable( + "Exception ignored while setting error for " + "ctypes callback function %R", + callable); goto Done; + } if (flags & FUNCFLAG_USE_ERRNO) { int temp = space[0]; space[0] = errno; @@ -295,6 +281,14 @@ static void _CallPythonObject(ctypes_state *st, for (j = 0; j < i; j++) { Py_DECREF(args[j]); } + return; + + Error: + PyErr_FormatUnraisable( + "Exception ignored while creating argument %zd for " + "ctypes callback function %R", + i, callable); + goto Done; } static void closure_fcn(ffi_cif *cif, From 881984b41a103b4c7fac262a00cc240ae8fbdba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:33:30 +0100 Subject: [PATCH 028/311] gh-111178: fix UBSan failures in `Modules/_sqlite` (GH-129087) * fix UBSan failures for `pysqlite_Blob` * fix UBSan failures for `pysqlite_Connection` * fix UBSan failures for `pysqlite_Cursor` * fix UBSan failures for `pysqlite_PrepareProtocol` * fix UBSan failures for `pysqlite_Row` * fix UBSan failures for `pysqlite_Statement` * suppress unused return values --- Modules/_sqlite/blob.c | 24 +++++++++----- Modules/_sqlite/connection.c | 50 ++++++++++++++++++++---------- Modules/_sqlite/cursor.c | 26 ++++++++++------ Modules/_sqlite/module.c | 2 +- Modules/_sqlite/prepare_protocol.c | 5 ++- Modules/_sqlite/row.c | 40 ++++++++++++++++-------- Modules/_sqlite/statement.c | 9 ++++-- 7 files changed, 101 insertions(+), 55 deletions(-) diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index d1a549a971c24a..390375628bfb4f 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -9,6 +9,8 @@ #include "clinic/blob.c.h" #undef clinic_state +#define _pysqlite_Blob_CAST(op) ((pysqlite_Blob *)(op)) + /*[clinic input] module _sqlite3 class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType" @@ -29,32 +31,35 @@ close_blob(pysqlite_Blob *self) } static int -blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg) +blob_traverse(PyObject *op, visitproc visit, void *arg) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->connection); return 0; } static int -blob_clear(pysqlite_Blob *self) +blob_clear(PyObject *op) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); Py_CLEAR(self->connection); return 0; } static void -blob_dealloc(pysqlite_Blob *self) +blob_dealloc(PyObject *op) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); close_blob(self); if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)self); + PyObject_ClearWeakRefs(op); } - tp->tp_clear((PyObject *)self); + (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); } @@ -373,8 +378,9 @@ blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val, } static Py_ssize_t -blob_length(pysqlite_Blob *self) +blob_length(PyObject *op) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); if (!check_blob(self)) { return -1; } @@ -449,8 +455,9 @@ subscript_slice(pysqlite_Blob *self, PyObject *item) } static PyObject * -blob_subscript(pysqlite_Blob *self, PyObject *item) +blob_subscript(PyObject *op, PyObject *item) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); if (!check_blob(self)) { return NULL; } @@ -546,8 +553,9 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value) } static int -blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value) +blob_ass_subscript(PyObject *op, PyObject *item, PyObject *value) { + pysqlite_Blob *self = _pysqlite_Blob_CAST(op); if (!check_blob(self)) { return -1; } diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 0c98f5065ee303..80021ccad4629e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -135,6 +135,8 @@ sqlite3_int64_converter(PyObject *obj, sqlite3_int64 *result) #include "clinic/connection.c.h" #undef clinic_state +#define _pysqlite_Connection_CAST(op) ((pysqlite_Connection *)(op)) + /*[clinic input] module _sqlite3 class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionType" @@ -384,8 +386,9 @@ do { \ } while (0) static int -connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg) +connection_traverse(PyObject *op, visitproc visit, void *arg) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->statement_cache); Py_VISIT(self->cursors); @@ -409,8 +412,9 @@ clear_callback_context(callback_context *ctx) } static int -connection_clear(pysqlite_Connection *self) +connection_clear(PyObject *op) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); Py_CLEAR(self->statement_cache); Py_CLEAR(self->cursors); Py_CLEAR(self->blobs); @@ -517,7 +521,7 @@ connection_dealloc(PyObject *self) } PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - tp->tp_clear(self); + (void)tp->tp_clear(self); tp->tp_free(self); Py_DECREF(tp); } @@ -1715,8 +1719,10 @@ int pysqlite_check_thread(pysqlite_Connection* self) return 1; } -static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused) +static PyObject * +pysqlite_connection_get_isolation_level(PyObject *op, void *Py_UNUSED(closure)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_connection(self)) { return NULL; } @@ -1726,16 +1732,20 @@ static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* se Py_RETURN_NONE; } -static PyObject* pysqlite_connection_get_total_changes(pysqlite_Connection* self, void* unused) +static PyObject * +pysqlite_connection_get_total_changes(PyObject *op, void *Py_UNUSED(closure)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_connection(self)) { return NULL; } return PyLong_FromLong(sqlite3_total_changes(self->db)); } -static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* self, void* unused) +static PyObject * +pysqlite_connection_get_in_transaction(PyObject *op, void *Py_UNUSED(closure)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_connection(self)) { return NULL; } @@ -1746,8 +1756,11 @@ static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* sel } static int -pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored)) +pysqlite_connection_set_isolation_level(PyObject *op, + PyObject *isolation_level, + void *Py_UNUSED(ignored)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (isolation_level == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; @@ -1770,11 +1783,11 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso } static PyObject * -pysqlite_connection_call(pysqlite_Connection *self, PyObject *args, - PyObject *kwargs) +pysqlite_connection_call(PyObject *op, PyObject *args, PyObject *kwargs) { PyObject* sql; pysqlite_Statement* statement; + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -2525,8 +2538,9 @@ getconfig_impl(pysqlite_Connection *self, int op) } static PyObject * -get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx)) +get_autocommit(PyObject *op, void *Py_UNUSED(closure)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; } @@ -2540,8 +2554,9 @@ get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx)) } static int -set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx)) +set_autocommit(PyObject *op, PyObject *val, void *Py_UNUSED(closure)) { + pysqlite_Connection *self = _pysqlite_Connection_CAST(op); if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return -1; } @@ -2566,7 +2581,7 @@ set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx)) } static PyObject * -get_sig(PyObject *self, void *Py_UNUSED(ctx)) +get_sig(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure)) { return PyUnicode_FromString("(sql, /)"); } @@ -2576,11 +2591,12 @@ static const char connection_doc[] = PyDoc_STR("SQLite database connection object."); static PyGetSetDef connection_getset[] = { - {"isolation_level", (getter)pysqlite_connection_get_isolation_level, (setter)pysqlite_connection_set_isolation_level}, - {"total_changes", (getter)pysqlite_connection_get_total_changes, (setter)0}, - {"in_transaction", (getter)pysqlite_connection_get_in_transaction, (setter)0}, - {"autocommit", (getter)get_autocommit, (setter)set_autocommit}, - {"__text_signature__", get_sig, (setter)0}, + {"isolation_level", pysqlite_connection_get_isolation_level, + pysqlite_connection_set_isolation_level}, + {"total_changes", pysqlite_connection_get_total_changes, NULL}, + {"in_transaction", pysqlite_connection_get_in_transaction, NULL}, + {"autocommit", get_autocommit, set_autocommit}, + {"__text_signature__", get_sig, NULL}, {NULL} }; diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 24e97fcf1897e9..02d598040775b0 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -44,6 +44,8 @@ typedef enum { #include "clinic/cursor.c.h" #undef clinic_state +#define _pysqlite_Cursor_CAST(op) ((pysqlite_Cursor *)(op)) + static inline int check_cursor_locked(pysqlite_Cursor *cur) { @@ -146,8 +148,9 @@ stmt_reset(pysqlite_Statement *self) } static int -cursor_traverse(pysqlite_Cursor *self, visitproc visit, void *arg) +cursor_traverse(PyObject *op, visitproc visit, void *arg) { + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->connection); Py_VISIT(self->description); @@ -159,8 +162,9 @@ cursor_traverse(pysqlite_Cursor *self, visitproc visit, void *arg) } static int -cursor_clear(pysqlite_Cursor *self) +cursor_clear(PyObject *op) { + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); Py_CLEAR(self->connection); Py_CLEAR(self->description); Py_CLEAR(self->row_cast_map); @@ -176,14 +180,15 @@ cursor_clear(pysqlite_Cursor *self) } static void -cursor_dealloc(pysqlite_Cursor *self) +cursor_dealloc(PyObject *op) { + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)self); + PyObject_ClearWeakRefs(op); } - tp->tp_clear((PyObject *)self); + (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); } @@ -1087,8 +1092,9 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self, } static PyObject * -pysqlite_cursor_iternext(pysqlite_Cursor *self) +pysqlite_cursor_iternext(PyObject *op) { + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); if (!check_cursor(self)) { return NULL; } @@ -1125,7 +1131,7 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self) } if (!Py_IsNone(self->row_factory)) { PyObject *factory = self->row_factory; - PyObject *args[] = { (PyObject *)self, row, }; + PyObject *args[] = { op, row, }; PyObject *new_row = PyObject_Vectorcall(factory, args, 2, NULL); Py_SETREF(row, new_row); } @@ -1144,7 +1150,7 @@ pysqlite_cursor_fetchone_impl(pysqlite_Cursor *self) { PyObject* row; - row = pysqlite_cursor_iternext(self); + row = pysqlite_cursor_iternext((PyObject *)self); if (!row && !PyErr_Occurred()) { Py_RETURN_NONE; } @@ -1174,7 +1180,7 @@ pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows) return NULL; } - while ((row = pysqlite_cursor_iternext(self))) { + while ((row = pysqlite_cursor_iternext((PyObject *)self))) { if (PyList_Append(list, row) < 0) { Py_DECREF(row); break; @@ -1212,7 +1218,7 @@ pysqlite_cursor_fetchall_impl(pysqlite_Cursor *self) return NULL; } - while ((row = pysqlite_cursor_iternext(self))) { + while ((row = pysqlite_cursor_iternext((PyObject *)self))) { if (PyList_Append(list, row) < 0) { Py_DECREF(row); break; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 73d55fb44e2e15..27e8dab92e0e67 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -617,7 +617,7 @@ module_clear(PyObject *module) static void module_free(void *module) { - module_clear((PyObject *)module); + (void)module_clear((PyObject *)module); } #define ADD_TYPE(module, type) \ diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index 44533225665dab..31092417cb480d 100644 --- a/Modules/_sqlite/prepare_protocol.c +++ b/Modules/_sqlite/prepare_protocol.c @@ -24,8 +24,7 @@ #include "prepare_protocol.h" static int -pysqlite_prepare_protocol_init(pysqlite_PrepareProtocol *self, PyObject *args, - PyObject *kwargs) +pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs) { return 0; } @@ -38,7 +37,7 @@ pysqlite_prepare_protocol_traverse(PyObject *self, visitproc visit, void *arg) } static void -pysqlite_prepare_protocol_dealloc(pysqlite_PrepareProtocol *self) +pysqlite_prepare_protocol_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c index 14555076a7e79a..79660008b180dc 100644 --- a/Modules/_sqlite/row.c +++ b/Modules/_sqlite/row.c @@ -32,6 +32,8 @@ #include "clinic/row.c.h" #undef clinic_state +#define _pysqlite_Row_CAST(op) ((pysqlite_Row *)(op)) + /*[clinic input] module _sqlite3 class _sqlite3.Row "pysqlite_Row *" "clinic_state()->RowType" @@ -39,16 +41,18 @@ class _sqlite3.Row "pysqlite_Row *" "clinic_state()->RowType" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=966c53403d7f3a40]*/ static int -row_clear(pysqlite_Row *self) +row_clear(PyObject *op) { + pysqlite_Row *self = _pysqlite_Row_CAST(op); Py_CLEAR(self->data); Py_CLEAR(self->description); return 0; } static int -row_traverse(pysqlite_Row *self, visitproc visit, void *arg) +row_traverse(PyObject *op, visitproc visit, void *arg) { + pysqlite_Row *self = _pysqlite_Row_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->data); Py_VISIT(self->description); @@ -60,7 +64,7 @@ pysqlite_row_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - tp->tp_clear(self); + (void)tp->tp_clear(self); tp->tp_free(self); Py_DECREF(tp); } @@ -94,10 +98,12 @@ pysqlite_row_new_impl(PyTypeObject *type, pysqlite_Cursor *cursor, return (PyObject *) self; } -PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx) +static PyObject * +pysqlite_row_item(PyObject *op, Py_ssize_t idx) { - PyObject *item = PyTuple_GetItem(self->data, idx); - return Py_XNewRef(item); + pysqlite_Row *self = _pysqlite_Row_CAST(op); + PyObject *item = PyTuple_GetItem(self->data, idx); + return Py_XNewRef(item); } static int @@ -129,10 +135,11 @@ equal_ignore_case(PyObject *left, PyObject *right) } static PyObject * -pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx) +pysqlite_row_subscript(PyObject *op, PyObject *idx) { Py_ssize_t _idx; Py_ssize_t nitems, i; + pysqlite_Row *self = _pysqlite_Row_CAST(op); if (PyLong_Check(idx)) { _idx = PyNumber_AsSsize_t(idx, PyExc_IndexError); @@ -174,8 +181,9 @@ pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx) } static Py_ssize_t -pysqlite_row_length(pysqlite_Row* self) +pysqlite_row_length(PyObject *op) { + pysqlite_Row *self = _pysqlite_Row_CAST(op); return PyTuple_GET_SIZE(self->data); } @@ -208,24 +216,30 @@ pysqlite_row_keys_impl(pysqlite_Row *self) return list; } -static PyObject* pysqlite_iter(pysqlite_Row* self) +static PyObject * +pysqlite_iter(PyObject *op) { + pysqlite_Row *self = _pysqlite_Row_CAST(op); return PyObject_GetIter(self->data); } -static Py_hash_t pysqlite_row_hash(pysqlite_Row *self) +static Py_hash_t +pysqlite_row_hash(PyObject *op) { + pysqlite_Row *self = _pysqlite_Row_CAST(op); return PyObject_Hash(self->description) ^ PyObject_Hash(self->data); } -static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid) +static PyObject * +pysqlite_row_richcompare(PyObject *op, PyObject *opother, int opid) { if (opid != Py_EQ && opid != Py_NE) Py_RETURN_NOTIMPLEMENTED; + pysqlite_Row *self = _pysqlite_Row_CAST(op); pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self)); - if (PyObject_TypeCheck(_other, state->RowType)) { - pysqlite_Row *other = (pysqlite_Row *)_other; + if (PyObject_TypeCheck(opother, state->RowType)) { + pysqlite_Row *other = (pysqlite_Row *)opother; int eq = PyObject_RichCompareBool(self->description, other->description, Py_EQ); if (eq < 0) { return NULL; diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 229bfc3b504165..facced0dfbfafd 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -25,6 +25,8 @@ #include "statement.h" #include "util.h" +#define _pysqlite_Statement_CAST(op) ((pysqlite_Statement *)(op)) + /* prototypes */ static const char *lstrip_sql(const char *sql); @@ -99,10 +101,11 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) } static void -stmt_dealloc(pysqlite_Statement *self) +stmt_dealloc(PyObject *op) { + pysqlite_Statement *self = _pysqlite_Statement_CAST(op); PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); + PyObject_GC_UnTrack(op); if (self->st) { Py_BEGIN_ALLOW_THREADS sqlite3_finalize(self->st); @@ -114,7 +117,7 @@ stmt_dealloc(pysqlite_Statement *self) } static int -stmt_traverse(pysqlite_Statement *self, visitproc visit, void *arg) +stmt_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); return 0; From 5424e3b0348c6029e96d4cb106cbcd5cca86d980 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 15:03:54 +0100 Subject: [PATCH 029/311] gh-93649: Add Modules/_testcapi/type.c file (#129516) Move PyType C API tests to a new file. Move following tests from test_capi.test_misc to test_capi.test_type: * BuiltinStaticTypesTests * test_get_type_name() * test_get_base_by_token() --- Lib/test/test_capi/test_misc.py | 174 --------------------- Lib/test/test_capi/test_type.py | 174 +++++++++++++++++++++ Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/parts.h | 1 + Modules/_testcapi/type.c | 251 ++++++++++++++++++++++++++++++ Modules/_testcapimodule.c | 221 +------------------------- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + 8 files changed, 434 insertions(+), 393 deletions(-) create mode 100644 Modules/_testcapi/type.c diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 1087b38c225085..efa01a84167002 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1111,147 +1111,6 @@ class Data(_testcapi.ObjExtraData): del d.extra self.assertIsNone(d.extra) - def test_get_type_name(self): - class MyType: - pass - - from _testcapi import ( - get_type_name, get_type_qualname, - get_type_fullyqualname, get_type_module_name) - - from collections import OrderedDict - ht = _testcapi.get_heaptype_for_name() - for cls, fullname, modname, qualname, name in ( - (int, - 'int', - 'builtins', - 'int', - 'int'), - (OrderedDict, - 'collections.OrderedDict', - 'collections', - 'OrderedDict', - 'OrderedDict'), - (ht, - '_testcapi.HeapTypeNameType', - '_testcapi', - 'HeapTypeNameType', - 'HeapTypeNameType'), - (MyType, - f'{__name__}.CAPITest.test_get_type_name..MyType', - __name__, - 'CAPITest.test_get_type_name..MyType', - 'MyType'), - ): - with self.subTest(cls=repr(cls)): - self.assertEqual(get_type_fullyqualname(cls), fullname) - self.assertEqual(get_type_module_name(cls), modname) - self.assertEqual(get_type_qualname(cls), qualname) - self.assertEqual(get_type_name(cls), name) - - # override __module__ - ht.__module__ = 'test_module' - self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType') - self.assertEqual(get_type_module_name(ht), 'test_module') - self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType') - self.assertEqual(get_type_name(ht), 'HeapTypeNameType') - - # override __name__ and __qualname__ - MyType.__name__ = 'my_name' - MyType.__qualname__ = 'my_qualname' - self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname') - self.assertEqual(get_type_module_name(MyType), __name__) - self.assertEqual(get_type_qualname(MyType), 'my_qualname') - self.assertEqual(get_type_name(MyType), 'my_name') - - # override also __module__ - MyType.__module__ = 'my_module' - self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname') - self.assertEqual(get_type_module_name(MyType), 'my_module') - self.assertEqual(get_type_qualname(MyType), 'my_qualname') - self.assertEqual(get_type_name(MyType), 'my_name') - - # PyType_GetFullyQualifiedName() ignores the module if it's "builtins" - # or "__main__" of it is not a string - MyType.__module__ = 'builtins' - self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') - MyType.__module__ = '__main__' - self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') - MyType.__module__ = 123 - self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') - - def test_get_base_by_token(self): - def get_base_by_token(src, key, comparable=True): - def run(use_mro): - find_first = _testcapi.pytype_getbasebytoken - ret1, result = find_first(src, key, use_mro, True) - ret2, no_result = find_first(src, key, use_mro, False) - self.assertIn(ret1, (0, 1)) - self.assertEqual(ret1, result is not None) - self.assertEqual(ret1, ret2) - self.assertIsNone(no_result) - return result - - found_in_mro = run(True) - found_in_bases = run(False) - if comparable: - self.assertIs(found_in_mro, found_in_bases) - return found_in_mro - return found_in_mro, found_in_bases - - create_type = _testcapi.create_type_with_token - get_token = _testcapi.get_tp_token - - Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC - self.assertEqual(Py_TP_USE_SPEC, 0) - - A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC) - self.assertTrue(get_token(A1) != Py_TP_USE_SPEC) - - B1 = create_type('_testcapi.B1', id(self)) - self.assertTrue(get_token(B1) == id(self)) - - tokenA1 = get_token(A1) - # find A1 from A1 - found = get_base_by_token(A1, tokenA1) - self.assertIs(found, A1) - - # no token in static types - STATIC = type(1) - self.assertEqual(get_token(STATIC), 0) - found = get_base_by_token(STATIC, tokenA1) - self.assertIs(found, None) - - # no token in pure subtypes - class A2(A1): pass - self.assertEqual(get_token(A2), 0) - # find A1 - class Z(STATIC, B1, A2): pass - found = get_base_by_token(Z, tokenA1) - self.assertIs(found, A1) - - # searching for NULL token is an error - with self.assertRaises(SystemError): - get_base_by_token(Z, 0) - with self.assertRaises(SystemError): - get_base_by_token(STATIC, 0) - - # share the token with A1 - C1 = create_type('_testcapi.C1', tokenA1) - self.assertTrue(get_token(C1) == tokenA1) - - # find C1 first by shared token - class Z(C1, A2): pass - found = get_base_by_token(Z, tokenA1) - self.assertIs(found, C1) - # B1 not found - found = get_base_by_token(Z, get_token(B1)) - self.assertIs(found, None) - - with self.assertRaises(TypeError): - _testcapi.pytype_getbasebytoken( - 'not a type', id(self), True, False) - def test_gen_get_code(self): def genf(): yield gen = genf() @@ -2922,39 +2781,6 @@ def test_linked_lifecycle_link_incref_unlink_decref(self): 0, get_refcount(interpid)) -class BuiltinStaticTypesTests(unittest.TestCase): - - TYPES = [ - object, - type, - int, - str, - dict, - type(None), - bool, - BaseException, - Exception, - Warning, - DeprecationWarning, # Warning subclass - ] - - def test_tp_bases_is_set(self): - # PyTypeObject.tp_bases is documented as public API. - # See https://github.com/python/cpython/issues/105020. - for typeobj in self.TYPES: - with self.subTest(typeobj): - bases = _testcapi.type_get_tp_bases(typeobj) - self.assertIsNot(bases, None) - - def test_tp_mro_is_set(self): - # PyTypeObject.tp_bases is documented as public API. - # See https://github.com/python/cpython/issues/105020. - for typeobj in self.TYPES: - with self.subTest(typeobj): - mro = _testcapi.type_get_tp_mro(typeobj) - self.assertIsNot(mro, None) - - class TestStaticTypes(unittest.TestCase): _has_run = False diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index ffcaae73bca236..7e5d013d737ab0 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -4,7 +4,181 @@ _testcapi = import_helper.import_module('_testcapi') +class BuiltinStaticTypesTests(unittest.TestCase): + + TYPES = [ + object, + type, + int, + str, + dict, + type(None), + bool, + BaseException, + Exception, + Warning, + DeprecationWarning, # Warning subclass + ] + + def test_tp_bases_is_set(self): + # PyTypeObject.tp_bases is documented as public API. + # See https://github.com/python/cpython/issues/105020. + for typeobj in self.TYPES: + with self.subTest(typeobj): + bases = _testcapi.type_get_tp_bases(typeobj) + self.assertIsNot(bases, None) + + def test_tp_mro_is_set(self): + # PyTypeObject.tp_bases is documented as public API. + # See https://github.com/python/cpython/issues/105020. + for typeobj in self.TYPES: + with self.subTest(typeobj): + mro = _testcapi.type_get_tp_mro(typeobj) + self.assertIsNot(mro, None) + + class TypeTests(unittest.TestCase): + def test_get_type_name(self): + class MyType: + pass + + from _testcapi import ( + get_type_name, get_type_qualname, + get_type_fullyqualname, get_type_module_name) + + from collections import OrderedDict + ht = _testcapi.get_heaptype_for_name() + for cls, fullname, modname, qualname, name in ( + (int, + 'int', + 'builtins', + 'int', + 'int'), + (OrderedDict, + 'collections.OrderedDict', + 'collections', + 'OrderedDict', + 'OrderedDict'), + (ht, + '_testcapi.HeapTypeNameType', + '_testcapi', + 'HeapTypeNameType', + 'HeapTypeNameType'), + (MyType, + f'{__name__}.TypeTests.test_get_type_name..MyType', + __name__, + 'TypeTests.test_get_type_name..MyType', + 'MyType'), + ): + with self.subTest(cls=repr(cls)): + self.assertEqual(get_type_fullyqualname(cls), fullname) + self.assertEqual(get_type_module_name(cls), modname) + self.assertEqual(get_type_qualname(cls), qualname) + self.assertEqual(get_type_name(cls), name) + + # override __module__ + ht.__module__ = 'test_module' + self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType') + self.assertEqual(get_type_module_name(ht), 'test_module') + self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType') + self.assertEqual(get_type_name(ht), 'HeapTypeNameType') + + # override __name__ and __qualname__ + MyType.__name__ = 'my_name' + MyType.__qualname__ = 'my_qualname' + self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname') + self.assertEqual(get_type_module_name(MyType), __name__) + self.assertEqual(get_type_qualname(MyType), 'my_qualname') + self.assertEqual(get_type_name(MyType), 'my_name') + + # override also __module__ + MyType.__module__ = 'my_module' + self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname') + self.assertEqual(get_type_module_name(MyType), 'my_module') + self.assertEqual(get_type_qualname(MyType), 'my_qualname') + self.assertEqual(get_type_name(MyType), 'my_name') + + # PyType_GetFullyQualifiedName() ignores the module if it's "builtins" + # or "__main__" of it is not a string + MyType.__module__ = 'builtins' + self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') + MyType.__module__ = '__main__' + self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') + MyType.__module__ = 123 + self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') + + def test_get_base_by_token(self): + def get_base_by_token(src, key, comparable=True): + def run(use_mro): + find_first = _testcapi.pytype_getbasebytoken + ret1, result = find_first(src, key, use_mro, True) + ret2, no_result = find_first(src, key, use_mro, False) + self.assertIn(ret1, (0, 1)) + self.assertEqual(ret1, result is not None) + self.assertEqual(ret1, ret2) + self.assertIsNone(no_result) + return result + + found_in_mro = run(True) + found_in_bases = run(False) + if comparable: + self.assertIs(found_in_mro, found_in_bases) + return found_in_mro + return found_in_mro, found_in_bases + + create_type = _testcapi.create_type_with_token + get_token = _testcapi.get_tp_token + + Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC + self.assertEqual(Py_TP_USE_SPEC, 0) + + A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC) + self.assertTrue(get_token(A1) != Py_TP_USE_SPEC) + + B1 = create_type('_testcapi.B1', id(self)) + self.assertTrue(get_token(B1) == id(self)) + + tokenA1 = get_token(A1) + # find A1 from A1 + found = get_base_by_token(A1, tokenA1) + self.assertIs(found, A1) + + # no token in static types + STATIC = type(1) + self.assertEqual(get_token(STATIC), 0) + found = get_base_by_token(STATIC, tokenA1) + self.assertIs(found, None) + + # no token in pure subtypes + class A2(A1): pass + self.assertEqual(get_token(A2), 0) + # find A1 + class Z(STATIC, B1, A2): pass + found = get_base_by_token(Z, tokenA1) + self.assertIs(found, A1) + + # searching for NULL token is an error + with self.assertRaises(SystemError): + get_base_by_token(Z, 0) + with self.assertRaises(SystemError): + get_base_by_token(STATIC, 0) + + # share the token with A1 + C1 = create_type('_testcapi.C1', tokenA1) + self.assertTrue(get_token(C1) == tokenA1) + + # find C1 first by shared token + class Z(C1, A2): pass + found = get_base_by_token(Z, tokenA1) + self.assertIs(found, C1) + # B1 not found + found = get_base_by_token(Z, get_token(B1)) + self.assertIs(found, None) + + with self.assertRaises(TypeError): + _testcapi.pytype_getbasebytoken( + 'not a type', id(self), True, False) + def test_freeze(self): # test PyType_Freeze() type_freeze = _testcapi.type_freeze diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 1fd7c8bd2e2d73..a276fa642de42e 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index e5d2502edf5362..c4b73459b3712b 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -63,5 +63,6 @@ int _PyTestCapi_Init_Object(PyObject *module); int _PyTestCapi_Init_Config(PyObject *mod); int _PyTestCapi_Init_Import(PyObject *mod); int _PyTestCapi_Init_Frame(PyObject *mod); +int _PyTestCapi_Init_Type(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapi/type.c b/Modules/_testcapi/type.c new file mode 100644 index 00000000000000..9bef58d1f83668 --- /dev/null +++ b/Modules/_testcapi/type.c @@ -0,0 +1,251 @@ +#include "parts.h" +#include "util.h" + + +static PyType_Slot HeapTypeNameType_slots[] = { + {0}, +}; + +static PyType_Spec HeapTypeNameType_Spec = { + .name = "_testcapi.HeapTypeNameType", + .basicsize = sizeof(PyObject), + .flags = Py_TPFLAGS_DEFAULT, + .slots = HeapTypeNameType_slots, +}; + +static PyObject * +get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyType_FromSpec(&HeapTypeNameType_Spec); +} + + +static PyObject * +get_type_name(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return PyType_GetName((PyTypeObject *)type); +} + + +static PyObject * +get_type_qualname(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return PyType_GetQualName((PyTypeObject *)type); +} + + +static PyObject * +get_type_fullyqualname(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return PyType_GetFullyQualifiedName((PyTypeObject *)type); +} + + +static PyObject * +get_type_module_name(PyObject *self, PyObject *type) +{ + assert(PyType_Check(type)); + return PyType_GetModuleName((PyTypeObject *)type); +} + + +static PyObject * +test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + /* Test for PyType_GetDict */ + + // Assert ints have a `to_bytes` method + PyObject *long_dict = PyType_GetDict(&PyLong_Type); + assert(long_dict); + assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref + Py_DECREF(long_dict); + + // Make a new type, add an attribute to it and assert it's there + PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec); + assert(HeapTypeNameType); + assert(PyObject_SetAttrString( + HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0); + PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType); + assert(type_dict); + assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref + Py_DECREF(HeapTypeNameType); + Py_DECREF(type_dict); + Py_RETURN_NONE; +} + + +static PyObject * +test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new); + if (PyLong_Type.tp_new != tp_new) { + PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long"); + return NULL; + } + + reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr); + if (PyLong_Type.tp_repr != tp_repr) { + PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long"); + return NULL; + } + + ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call); + if (tp_call != NULL) { + PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long"); + return NULL; + } + + binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add); + if (PyLong_Type.tp_as_number->nb_add != nb_add) { + PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long"); + return NULL; + } + + lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length); + if (mp_length != NULL) { + PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long"); + return NULL; + } + + void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1); + if (over_value != NULL) { + PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long"); + return NULL; + } + + tp_new = PyType_GetSlot(&PyLong_Type, 0); + if (tp_new != NULL) { + PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long"); + return NULL; + } + if (PyErr_ExceptionMatches(PyExc_SystemError)) { + // This is the right exception + PyErr_Clear(); + } + else { + return NULL; + } + + Py_RETURN_NONE; +} + + +// Get type->tp_version_tag +static PyObject * +type_get_version(PyObject *self, PyObject *type) +{ + if (!PyType_Check(type)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyObject *res = PyLong_FromUnsignedLong( + ((PyTypeObject *)type)->tp_version_tag); + if (res == NULL) { + assert(PyErr_Occurred()); + return NULL; + } + return res; +} + +static PyObject * +type_modified(PyObject *self, PyObject *arg) +{ + if (!PyType_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyTypeObject *type = (PyTypeObject*)arg; + + PyType_Modified(type); + Py_RETURN_NONE; +} + + +static PyObject * +type_assign_version(PyObject *self, PyObject *arg) +{ + if (!PyType_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyTypeObject *type = (PyTypeObject*)arg; + + int res = PyUnstable_Type_AssignVersionTag(type); + return PyLong_FromLong(res); +} + + +static PyObject * +type_get_tp_bases(PyObject *self, PyObject *arg) +{ + if (!PyType_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyTypeObject *type = (PyTypeObject*)arg; + + PyObject *bases = type->tp_bases; + if (bases == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(bases); +} + +static PyObject * +type_get_tp_mro(PyObject *self, PyObject *arg) +{ + if (!PyType_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyTypeObject *type = (PyTypeObject*)arg; + + PyObject *mro = ((PyTypeObject *)type)->tp_mro; + if (mro == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(mro); +} + + +static PyObject * +type_freeze(PyObject *module, PyObject *arg) +{ + if (!PyType_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be a type"); + return NULL; + } + PyTypeObject *type = (PyTypeObject*)arg; + + if (PyType_Freeze(type) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +static PyMethodDef test_methods[] = { + {"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS}, + {"get_type_name", get_type_name, METH_O}, + {"get_type_qualname", get_type_qualname, METH_O}, + {"get_type_fullyqualname", get_type_fullyqualname, METH_O}, + {"get_type_module_name", get_type_module_name, METH_O}, + {"test_get_type_dict", test_get_type_dict, METH_NOARGS}, + {"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS}, + {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, + {"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")}, + {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")}, + {"type_get_tp_bases", type_get_tp_bases, METH_O}, + {"type_get_tp_mro", type_get_tp_mro, METH_O}, + {"type_freeze", type_freeze, METH_O}, + {NULL}, +}; + +int +_PyTestCapi_Init_Type(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index d25e61dbc3d588..ad9d836120b506 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -530,136 +530,6 @@ test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored)) } -static PyObject * -test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new); - if (PyLong_Type.tp_new != tp_new) { - PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long"); - return NULL; - } - - reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr); - if (PyLong_Type.tp_repr != tp_repr) { - PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long"); - return NULL; - } - - ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call); - if (tp_call != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long"); - return NULL; - } - - binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add); - if (PyLong_Type.tp_as_number->nb_add != nb_add) { - PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long"); - return NULL; - } - - lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length); - if (mp_length != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long"); - return NULL; - } - - void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1); - if (over_value != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long"); - return NULL; - } - - tp_new = PyType_GetSlot(&PyLong_Type, 0); - if (tp_new != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long"); - return NULL; - } - if (PyErr_ExceptionMatches(PyExc_SystemError)) { - // This is the right exception - PyErr_Clear(); - } - else { - return NULL; - } - - Py_RETURN_NONE; -} - - -static PyType_Slot HeapTypeNameType_slots[] = { - {0}, -}; - -static PyType_Spec HeapTypeNameType_Spec = { - .name = "_testcapi.HeapTypeNameType", - .basicsize = sizeof(PyObject), - .flags = Py_TPFLAGS_DEFAULT, - .slots = HeapTypeNameType_slots, -}; - -static PyObject * -get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return PyType_FromSpec(&HeapTypeNameType_Spec); -} - - -static PyObject * -get_type_name(PyObject *self, PyObject *type) -{ - assert(PyType_Check(type)); - return PyType_GetName((PyTypeObject *)type); -} - - -static PyObject * -get_type_qualname(PyObject *self, PyObject *type) -{ - assert(PyType_Check(type)); - return PyType_GetQualName((PyTypeObject *)type); -} - - -static PyObject * -get_type_fullyqualname(PyObject *self, PyObject *type) -{ - assert(PyType_Check(type)); - return PyType_GetFullyQualifiedName((PyTypeObject *)type); -} - - -static PyObject * -get_type_module_name(PyObject *self, PyObject *type) -{ - assert(PyType_Check(type)); - return PyType_GetModuleName((PyTypeObject *)type); -} - - -static PyObject * -test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - /* Test for PyType_GetDict */ - - // Assert ints have a `to_bytes` method - PyObject *long_dict = PyType_GetDict(&PyLong_Type); - assert(long_dict); - assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref - Py_DECREF(long_dict); - - // Make a new type, add an attribute to it and assert it's there - PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec); - assert(HeapTypeNameType); - assert(PyObject_SetAttrString( - HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0); - PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType); - assert(type_dict); - assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref - Py_DECREF(HeapTypeNameType); - Py_DECREF(type_dict); - Py_RETURN_NONE; -} - static PyObject * pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored)) { @@ -2380,68 +2250,6 @@ test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) } -// type->tp_version_tag -static PyObject * -type_get_version(PyObject *self, PyObject *type) -{ - if (!PyType_Check(type)) { - PyErr_SetString(PyExc_TypeError, "argument must be a type"); - return NULL; - } - PyObject *res = PyLong_FromUnsignedLong( - ((PyTypeObject *)type)->tp_version_tag); - if (res == NULL) { - assert(PyErr_Occurred()); - return NULL; - } - return res; -} - -static PyObject * -type_modified(PyObject *self, PyObject *type) -{ - if (!PyType_Check(type)) { - PyErr_SetString(PyExc_TypeError, "argument must be a type"); - return NULL; - } - PyType_Modified((PyTypeObject *)type); - Py_RETURN_NONE; -} - - -static PyObject * -type_assign_version(PyObject *self, PyObject *type) -{ - if (!PyType_Check(type)) { - PyErr_SetString(PyExc_TypeError, "argument must be a type"); - return NULL; - } - int res = PyUnstable_Type_AssignVersionTag((PyTypeObject *)type); - return PyLong_FromLong(res); -} - - -static PyObject * -type_get_tp_bases(PyObject *self, PyObject *type) -{ - PyObject *bases = ((PyTypeObject *)type)->tp_bases; - if (bases == NULL) { - Py_RETURN_NONE; - } - return Py_NewRef(bases); -} - -static PyObject * -type_get_tp_mro(PyObject *self, PyObject *type) -{ - PyObject *mro = ((PyTypeObject *)type)->tp_mro; - if (mro == NULL) { - Py_RETURN_NONE; - } - return Py_NewRef(mro); -} - - /* We only use 2 in test_capi/test_misc.py. */ #define NUM_BASIC_STATIC_TYPES 2 static PyTypeObject BasicStaticTypes[NUM_BASIC_STATIC_TYPES] = { @@ -3205,19 +3013,6 @@ finalize_thread_hang(PyObject *self, PyObject *callback) } -static PyObject * -type_freeze(PyObject *module, PyObject *args) -{ - PyTypeObject *type; - if (!PyArg_ParseTuple(args, "O!", &PyType_Type, &type)) { - return NULL; - } - if (PyType_Freeze(type) < 0) { - return NULL; - } - Py_RETURN_NONE; -} - struct atexit_data { int called; PyThreadState *tstate; @@ -3415,13 +3210,6 @@ static PyMethodDef TestMethods[] = { {"py_buildvalue", py_buildvalue, METH_VARARGS}, {"py_buildvalue_ints", py_buildvalue_ints, METH_VARARGS}, {"test_buildvalue_N", test_buildvalue_N, METH_NOARGS}, - {"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS}, - {"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS}, - {"get_type_name", get_type_name, METH_O}, - {"get_type_qualname", get_type_qualname, METH_O}, - {"get_type_fullyqualname", get_type_fullyqualname, METH_O}, - {"get_type_module_name", get_type_module_name, METH_O}, - {"test_get_type_dict", test_get_type_dict, METH_NOARGS}, {"test_reftracer", test_reftracer, METH_NOARGS}, {"_test_thread_state", test_thread_state, METH_VARARGS}, {"gilstate_ensure_release", gilstate_ensure_release, METH_NOARGS}, @@ -3489,11 +3277,6 @@ static PyMethodDef TestMethods[] = { {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, - {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")}, - {"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")}, - {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")}, - {"type_get_tp_bases", type_get_tp_bases, METH_O}, - {"type_get_tp_mro", type_get_tp_mro, METH_O}, {"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"gen_get_code", gen_get_code, METH_O, NULL}, @@ -3516,7 +3299,6 @@ static PyMethodDef TestMethods[] = { {"function_set_warning", function_set_warning, METH_NOARGS}, {"test_critical_sections", test_critical_sections, METH_NOARGS}, {"finalize_thread_hang", finalize_thread_hang, METH_O, NULL}, - {"type_freeze", type_freeze, METH_VARARGS}, {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, @@ -4296,6 +4078,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Frame(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Type(m) < 0) { + return NULL; + } PyState_AddModule(m, &_testcapimodule); return m; diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index e94b4c46e6dbb8..09969331c6edd4 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -129,6 +129,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 782f91afb1996f..52491643ad842f 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -120,6 +120,9 @@ Source Files + + Source Files + From 79f85a0bc1c3b3c30b2f979033eef9840e21db31 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 15:06:14 +0100 Subject: [PATCH 030/311] gh-129354: Use PyErr_FormatUnraisable() function (#129518) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). --- Modules/_ssl.c | 25 ++++++++++++++++++------ Modules/_threadmodule.c | 43 ++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index c15a582a92aa4a..85e917fbbb7093 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -4666,7 +4666,8 @@ _servername_callback(SSL *s, int *al, void *args) servername_bytes = PyBytes_FromString(servername); if (servername_bytes == NULL) { - PyErr_WriteUnraisable((PyObject *) sslctx); + PyErr_FormatUnraisable("Exception ignored " + "in ssl servername callback"); goto error; } /* server_hostname was encoded to an A-label by our caller; put it @@ -4674,7 +4675,10 @@ _servername_callback(SSL *s, int *al, void *args) */ servername_str = PyUnicode_FromEncodedObject(servername_bytes, "ascii", NULL); if (servername_str == NULL) { - PyErr_WriteUnraisable(servername_bytes); + PyErr_FormatUnraisable("Exception ignored " + "in ssl servername callback " + "while decoding name %R", + servername_bytes); Py_DECREF(servername_bytes); goto error; } @@ -4687,7 +4691,10 @@ _servername_callback(SSL *s, int *al, void *args) Py_DECREF(ssl_socket); if (result == NULL) { - PyErr_WriteUnraisable(sslctx->set_sni_cb); + PyErr_FormatUnraisable("Exception ignored " + "in ssl servername callback " + "while calling set SNI callback %R", + sslctx->set_sni_cb); *al = SSL_AD_HANDSHAKE_FAILURE; ret = SSL_TLSEXT_ERR_ALERT_FATAL; } @@ -4700,7 +4707,11 @@ _servername_callback(SSL *s, int *al, void *args) } else { *al = (int) PyLong_AsLong(result); if (PyErr_Occurred()) { - PyErr_WriteUnraisable(result); + PyErr_FormatUnraisable("Exception ignored " + "in ssl servername callback " + "while calling set SNI callback " + "(result=%R)", + result); *al = SSL_AD_INTERNAL_ERROR; } ret = SSL_TLSEXT_ERR_ALERT_FATAL; @@ -5007,7 +5018,8 @@ static unsigned int psk_client_callback(SSL *s, error: if (PyErr_Occurred()) { - PyErr_WriteUnraisable(callback); + PyErr_FormatUnraisable("Exception ignored in ssl PSK client callback " + "while calling callback %R", callback); } PyGILState_Release(gstate); return 0; @@ -5116,7 +5128,8 @@ static unsigned int psk_server_callback(SSL *s, error: if (PyErr_Occurred()) { - PyErr_WriteUnraisable(callback); + PyErr_FormatUnraisable("Exception ignored in ssl PSK server callback " + "while calling callback %R", callback); } PyGILState_Release(gstate); return 0; diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index dbc574f7816b85..e251736fb36aa9 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1539,17 +1539,20 @@ create_localsdict(localobject *self, thread_module_state *state, goto err; } - if (PyDict_SetItem(self->localdicts, tstate->threading_local_key, ldict) < - 0) { + if (PyDict_SetItem(self->localdicts, tstate->threading_local_key, + ldict) < 0) + { goto err; } wr = create_sentinel_wr(self); if (wr == NULL) { PyObject *exc = PyErr_GetRaisedException(); - if (PyDict_DelItem(self->localdicts, tstate->threading_local_key) < - 0) { - PyErr_WriteUnraisable((PyObject *)self); + if (PyDict_DelItem(self->localdicts, + tstate->threading_local_key) < 0) + { + PyErr_FormatUnraisable("Exception ignored while deleting " + "thread local of %R", self); } PyErr_SetRaisedException(exc); goto err; @@ -1557,9 +1560,11 @@ create_localsdict(localobject *self, thread_module_state *state, if (PySet_Add(self->thread_watchdogs, wr) < 0) { PyObject *exc = PyErr_GetRaisedException(); - if (PyDict_DelItem(self->localdicts, tstate->threading_local_key) < - 0) { - PyErr_WriteUnraisable((PyObject *)self); + if (PyDict_DelItem(self->localdicts, + tstate->threading_local_key) < 0) + { + PyErr_FormatUnraisable("Exception ignored while deleting " + "thread local of %R", self); } PyErr_SetRaisedException(exc); goto err; @@ -1609,13 +1614,16 @@ _ldict(localobject *self, thread_module_state *state) we create a new one the next time we do an attr access */ PyObject *exc = PyErr_GetRaisedException(); - if (PyDict_DelItem(self->localdicts, tstate->threading_local_key) < - 0) { - PyErr_WriteUnraisable((PyObject *)self); - PyErr_Clear(); + if (PyDict_DelItem(self->localdicts, + tstate->threading_local_key) < 0) + { + PyErr_FormatUnraisable("Exception ignored while deleting " + "thread local of %R", self); + assert(!PyErr_Occurred()); } if (PySet_Discard(self->thread_watchdogs, wr) < 0) { - PyErr_WriteUnraisable((PyObject *)self); + PyErr_FormatUnraisable("Exception ignored while discarding " + "thread watchdog of %R", self); } PyErr_SetRaisedException(exc); Py_DECREF(ldict); @@ -1746,12 +1754,14 @@ clear_locals(PyObject *locals_and_key, PyObject *dummyweakref) if (self->localdicts != NULL) { PyObject *key = PyTuple_GetItem(locals_and_key, 1); if (PyDict_Pop(self->localdicts, key, NULL) < 0) { - PyErr_WriteUnraisable((PyObject*)self); + PyErr_FormatUnraisable("Exception ignored while clearing " + "thread local %R", (PyObject *)self); } } if (self->thread_watchdogs != NULL) { if (PySet_Discard(self->thread_watchdogs, dummyweakref) < 0) { - PyErr_WriteUnraisable((PyObject *)self); + PyErr_FormatUnraisable("Exception ignored while clearing " + "thread local %R", (PyObject *)self); } } @@ -2314,7 +2324,8 @@ thread_shutdown(PyObject *self, PyObject *args) // Wait for the thread to finish. If we're interrupted, such // as by a ctrl-c we print the error and exit early. if (ThreadHandle_join(handle, -1) < 0) { - PyErr_WriteUnraisable(NULL); + PyErr_FormatUnraisable("Exception ignored while joining a thread " + "in _thread._shutdown()"); ThreadHandle_decref(handle); Py_RETURN_NONE; } From 7eaef74561c27865496505913d19eec7bb3fbcf5 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Fri, 31 Jan 2025 14:08:48 +0000 Subject: [PATCH 031/311] gh-129430: Make walking vm regions more efficient in MacOS (#129494) --- Modules/_testexternalinspection.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/_testexternalinspection.c b/Modules/_testexternalinspection.c index 5a28c0e266226b..22074c81b7405f 100644 --- a/Modules/_testexternalinspection.c +++ b/Modules/_testexternalinspection.c @@ -232,15 +232,15 @@ search_map_for_section(pid_t pid, const char* secname, const char* substr) { &count, &object_name) == KERN_SUCCESS) { - int path_len = proc_regionfilename( - pid, address, map_filename, MAXPATHLEN); - if (path_len == 0) { + if ((region_info.protection & VM_PROT_READ) == 0 + || (region_info.protection & VM_PROT_EXECUTE) == 0) { address += size; continue; } - if ((region_info.protection & VM_PROT_READ) == 0 - || (region_info.protection & VM_PROT_EXECUTE) == 0) { + int path_len = proc_regionfilename( + pid, address, map_filename, MAXPATHLEN); + if (path_len == 0) { address += size; continue; } From fad36bf38248130bc48b81a5e7c31a7649a6456e Mon Sep 17 00:00:00 2001 From: Valery Fedorenko Date: Fri, 31 Jan 2025 22:36:30 +0800 Subject: [PATCH 032/311] gh-126108: Fix potential null pointer dereference in `PySys_AddWarnOptionUnicode` (#126118) --- .../2024-10-29-09-15-10.gh-issue-126108.eTIjHY.rst | 1 + Python/sysmodule.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2024-10-29-09-15-10.gh-issue-126108.eTIjHY.rst diff --git a/Misc/NEWS.d/next/Security/2024-10-29-09-15-10.gh-issue-126108.eTIjHY.rst b/Misc/NEWS.d/next/Security/2024-10-29-09-15-10.gh-issue-126108.eTIjHY.rst new file mode 100644 index 00000000000000..9f2c7e84d4dff0 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2024-10-29-09-15-10.gh-issue-126108.eTIjHY.rst @@ -0,0 +1 @@ +Fix a possible ``NULL`` pointer dereference in :c:func:`!PySys_AddWarnOptionUnicode`. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 7e4cb45af05672..11b96c8455de14 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2847,6 +2847,7 @@ PySys_ResetWarnOptions(void) static int _PySys_AddWarnOptionWithError(PyThreadState *tstate, PyObject *option) { + assert(tstate != NULL); PyObject *warnoptions = get_warnoptions(tstate); if (warnoptions == NULL) { return -1; @@ -2862,11 +2863,11 @@ PyAPI_FUNC(void) PySys_AddWarnOptionUnicode(PyObject *option) { PyThreadState *tstate = _PyThreadState_GET(); + _Py_EnsureTstateNotNULL(tstate); + assert(!_PyErr_Occurred(tstate)); if (_PySys_AddWarnOptionWithError(tstate, option) < 0) { /* No return value, therefore clear error state if possible */ - if (tstate) { - _PyErr_Clear(tstate); - } + _PyErr_Clear(tstate); } } From 60a85415aeb5a8be54b3c412d19a7444bf5ac757 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 31 Jan 2025 16:02:50 +0100 Subject: [PATCH 033/311] gh-93649: Add Modules/_testcapi/function.c file (#129521) * Move PyFunction C API tests to a new file. * Add Lib/test/test_capi/test_function.py. * Move tests from test_capi.test_misc to test_capi.test_function. --- Lib/test/test_capi/test_function.py | 323 ++++++++++++++++++++++++++++ Lib/test/test_capi/test_misc.py | 291 +------------------------ Modules/Setup.stdlib.in | 2 +- Modules/_testcapi/function.c | 143 ++++++++++++ Modules/_testcapi/parts.h | 1 + Modules/_testcapimodule.c | 125 +---------- PCbuild/_testcapi.vcxproj | 1 + PCbuild/_testcapi.vcxproj.filters | 3 + 8 files changed, 476 insertions(+), 413 deletions(-) create mode 100644 Lib/test/test_capi/test_function.py create mode 100644 Modules/_testcapi/function.c diff --git a/Lib/test/test_capi/test_function.py b/Lib/test/test_capi/test_function.py new file mode 100644 index 00000000000000..9dca377e28ba42 --- /dev/null +++ b/Lib/test/test_capi/test_function.py @@ -0,0 +1,323 @@ +import unittest +from test.support import import_helper + + +_testcapi = import_helper.import_module('_testcapi') + + +class FunctionTest(unittest.TestCase): + def test_function_get_code(self): + # Test PyFunction_GetCode() + import types + + def some(): + pass + + code = _testcapi.function_get_code(some) + self.assertIsInstance(code, types.CodeType) + self.assertEqual(code, some.__code__) + + with self.assertRaises(SystemError): + _testcapi.function_get_code(None) # not a function + + def test_function_get_globals(self): + # Test PyFunction_GetGlobals() + def some(): + pass + + globals_ = _testcapi.function_get_globals(some) + self.assertIsInstance(globals_, dict) + self.assertEqual(globals_, some.__globals__) + + with self.assertRaises(SystemError): + _testcapi.function_get_globals(None) # not a function + + def test_function_get_module(self): + # Test PyFunction_GetModule() + def some(): + pass + + module = _testcapi.function_get_module(some) + self.assertIsInstance(module, str) + self.assertEqual(module, some.__module__) + + with self.assertRaises(SystemError): + _testcapi.function_get_module(None) # not a function + + def test_function_get_defaults(self): + # Test PyFunction_GetDefaults() + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + defaults = _testcapi.function_get_defaults(some) + self.assertEqual(defaults, ('p', 0, None)) + self.assertEqual(defaults, some.__defaults__) + + with self.assertRaises(SystemError): + _testcapi.function_get_defaults(None) # not a function + + def test_function_set_defaults(self): + # Test PyFunction_SetDefaults() + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + old_defaults = ('p', 0, None) + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_defaults(some, 1) # not tuple or None + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_defaults(1, ()) # not a function + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + new_defaults = ('q', 1, None) + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + # Empty tuple is fine: + new_defaults = () + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + class tuplesub(tuple): ... # tuple subclasses must work + + new_defaults = tuplesub(((1, 2), ['a', 'b'], None)) + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + # `None` is special, it sets `defaults` to `NULL`, + # it needs special handling in `_testcapi`: + _testcapi.function_set_defaults(some, None) + self.assertEqual(_testcapi.function_get_defaults(some), None) + self.assertEqual(some.__defaults__, None) + + def test_function_get_kw_defaults(self): + # Test PyFunction_GetKwDefaults() + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + defaults = _testcapi.function_get_kw_defaults(some) + self.assertEqual(defaults, {'kw2': True}) + self.assertEqual(defaults, some.__kwdefaults__) + + with self.assertRaises(SystemError): + _testcapi.function_get_kw_defaults(None) # not a function + + def test_function_set_kw_defaults(self): + # Test PyFunction_SetKwDefaults() + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + old_defaults = {'kw2': True} + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_kw_defaults(some, 1) # not dict or None + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_kw_defaults(1, {}) # not a function + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + new_defaults = {'kw2': (1, 2, 3)} + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + # Empty dict is fine: + new_defaults = {} + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + class dictsub(dict): ... # dict subclasses must work + + new_defaults = dictsub({'kw2': None}) + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + # `None` is special, it sets `kwdefaults` to `NULL`, + # it needs special handling in `_testcapi`: + _testcapi.function_set_kw_defaults(some, None) + self.assertEqual(_testcapi.function_get_kw_defaults(some), None) + self.assertEqual(some.__kwdefaults__, None) + + def test_function_get_closure(self): + # Test PyFunction_GetClosure() + from types import CellType + + def regular_function(): ... + def unused_one_level(arg1): + def inner(arg2, arg3): ... + return inner + def unused_two_levels(arg1, arg2): + def decorator(arg3, arg4): + def inner(arg5, arg6): ... + return inner + return decorator + def with_one_level(arg1): + def inner(arg2, arg3): + return arg1 + arg2 + arg3 + return inner + def with_two_levels(arg1, arg2): + def decorator(arg3, arg4): + def inner(arg5, arg6): + return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + return inner + return decorator + + # Functions without closures: + self.assertIsNone(_testcapi.function_get_closure(regular_function)) + self.assertIsNone(regular_function.__closure__) + + func = unused_one_level(1) + closure = _testcapi.function_get_closure(func) + self.assertIsNone(closure) + self.assertIsNone(func.__closure__) + + func = unused_two_levels(1, 2)(3, 4) + closure = _testcapi.function_get_closure(func) + self.assertIsNone(closure) + self.assertIsNone(func.__closure__) + + # Functions with closures: + func = with_one_level(5) + closure = _testcapi.function_get_closure(func) + self.assertEqual(closure, func.__closure__) + self.assertIsInstance(closure, tuple) + self.assertEqual(len(closure), 1) + self.assertEqual(len(closure), len(func.__code__.co_freevars)) + for cell in closure: + self.assertIsInstance(cell, CellType) + self.assertTrue(closure[0].cell_contents, 5) + + func = with_two_levels(1, 2)(3, 4) + closure = _testcapi.function_get_closure(func) + self.assertEqual(closure, func.__closure__) + self.assertIsInstance(closure, tuple) + self.assertEqual(len(closure), 4) + self.assertEqual(len(closure), len(func.__code__.co_freevars)) + for cell in closure: + self.assertIsInstance(cell, CellType) + self.assertEqual([cell.cell_contents for cell in closure], + [1, 2, 3, 4]) + + def test_function_get_closure_error(self): + # Test PyFunction_GetClosure() + with self.assertRaises(SystemError): + _testcapi.function_get_closure(1) + with self.assertRaises(SystemError): + _testcapi.function_get_closure(None) + + def test_function_set_closure(self): + # Test PyFunction_SetClosure() + from types import CellType + + def function_without_closure(): ... + def function_with_closure(arg): + def inner(): + return arg + return inner + + func = function_without_closure + _testcapi.function_set_closure(func, (CellType(1), CellType(1))) + closure = _testcapi.function_get_closure(func) + self.assertEqual([c.cell_contents for c in closure], [1, 1]) + self.assertEqual([c.cell_contents for c in func.__closure__], [1, 1]) + + func = function_with_closure(1) + _testcapi.function_set_closure(func, + (CellType(1), CellType(2), CellType(3))) + closure = _testcapi.function_get_closure(func) + self.assertEqual([c.cell_contents for c in closure], [1, 2, 3]) + self.assertEqual([c.cell_contents for c in func.__closure__], [1, 2, 3]) + + def test_function_set_closure_none(self): + # Test PyFunction_SetClosure() + def function_without_closure(): ... + def function_with_closure(arg): + def inner(): + return arg + return inner + + _testcapi.function_set_closure(function_without_closure, None) + self.assertIsNone( + _testcapi.function_get_closure(function_without_closure)) + self.assertIsNone(function_without_closure.__closure__) + + _testcapi.function_set_closure(function_with_closure, None) + self.assertIsNone( + _testcapi.function_get_closure(function_with_closure)) + self.assertIsNone(function_with_closure.__closure__) + + def test_function_set_closure_errors(self): + # Test PyFunction_SetClosure() + def function_without_closure(): ... + + with self.assertRaises(SystemError): + _testcapi.function_set_closure(None, ()) # not a function + + with self.assertRaises(SystemError): + _testcapi.function_set_closure(function_without_closure, 1) + self.assertIsNone(function_without_closure.__closure__) # no change + + # NOTE: this works, but goes against the docs: + _testcapi.function_set_closure(function_without_closure, (1, 2)) + self.assertEqual( + _testcapi.function_get_closure(function_without_closure), (1, 2)) + self.assertEqual(function_without_closure.__closure__, (1, 2)) + + # TODO: test PyFunction_New() + # TODO: test PyFunction_NewWithQualName() + # TODO: test PyFunction_SetVectorcall() + # TODO: test PyFunction_GetAnnotations() + # TODO: test PyFunction_SetAnnotations() + # TODO: test PyClassMethod_New() + # TODO: test PyStaticMethod_New() + # + # PyFunction_AddWatcher() and PyFunction_ClearWatcher() are tested by + # test_capi.test_watchers. + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index efa01a84167002..acc4803d785366 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -928,175 +928,6 @@ def __init__(self): _testcapi.clear_managed_dict(c) self.assertEqual(c.__dict__, {}) - def test_function_get_code(self): - import types - - def some(): - pass - - code = _testcapi.function_get_code(some) - self.assertIsInstance(code, types.CodeType) - self.assertEqual(code, some.__code__) - - with self.assertRaises(SystemError): - _testcapi.function_get_code(None) # not a function - - def test_function_get_globals(self): - def some(): - pass - - globals_ = _testcapi.function_get_globals(some) - self.assertIsInstance(globals_, dict) - self.assertEqual(globals_, some.__globals__) - - with self.assertRaises(SystemError): - _testcapi.function_get_globals(None) # not a function - - def test_function_get_module(self): - def some(): - pass - - module = _testcapi.function_get_module(some) - self.assertIsInstance(module, str) - self.assertEqual(module, some.__module__) - - with self.assertRaises(SystemError): - _testcapi.function_get_module(None) # not a function - - def test_function_get_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - defaults = _testcapi.function_get_defaults(some) - self.assertEqual(defaults, ('p', 0, None)) - self.assertEqual(defaults, some.__defaults__) - - with self.assertRaises(SystemError): - _testcapi.function_get_defaults(None) # not a function - - def test_function_set_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - old_defaults = ('p', 0, None) - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_defaults(some, 1) # not tuple or None - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_defaults(1, ()) # not a function - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - new_defaults = ('q', 1, None) - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - # Empty tuple is fine: - new_defaults = () - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - class tuplesub(tuple): ... # tuple subclasses must work - - new_defaults = tuplesub(((1, 2), ['a', 'b'], None)) - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - # `None` is special, it sets `defaults` to `NULL`, - # it needs special handling in `_testcapi`: - _testcapi.function_set_defaults(some, None) - self.assertEqual(_testcapi.function_get_defaults(some), None) - self.assertEqual(some.__defaults__, None) - - def test_function_get_kw_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - defaults = _testcapi.function_get_kw_defaults(some) - self.assertEqual(defaults, {'kw2': True}) - self.assertEqual(defaults, some.__kwdefaults__) - - with self.assertRaises(SystemError): - _testcapi.function_get_kw_defaults(None) # not a function - - def test_function_set_kw_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - old_defaults = {'kw2': True} - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_kw_defaults(some, 1) # not dict or None - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_kw_defaults(1, {}) # not a function - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - new_defaults = {'kw2': (1, 2, 3)} - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - # Empty dict is fine: - new_defaults = {} - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - class dictsub(dict): ... # dict subclasses must work - - new_defaults = dictsub({'kw2': None}) - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - # `None` is special, it sets `kwdefaults` to `NULL`, - # it needs special handling in `_testcapi`: - _testcapi.function_set_kw_defaults(some, None) - self.assertEqual(_testcapi.function_get_kw_defaults(some), None) - self.assertEqual(some.__kwdefaults__, None) - def test_unstable_gc_new_with_extra_data(self): class Data(_testcapi.ObjExtraData): __slots__ = ('x', 'y') @@ -1319,127 +1150,6 @@ def test_pyobject_getitemdata_error(self): _testcapi.pyobject_getitemdata(0) - def test_function_get_closure(self): - from types import CellType - - def regular_function(): ... - def unused_one_level(arg1): - def inner(arg2, arg3): ... - return inner - def unused_two_levels(arg1, arg2): - def decorator(arg3, arg4): - def inner(arg5, arg6): ... - return inner - return decorator - def with_one_level(arg1): - def inner(arg2, arg3): - return arg1 + arg2 + arg3 - return inner - def with_two_levels(arg1, arg2): - def decorator(arg3, arg4): - def inner(arg5, arg6): - return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 - return inner - return decorator - - # Functions without closures: - self.assertIsNone(_testcapi.function_get_closure(regular_function)) - self.assertIsNone(regular_function.__closure__) - - func = unused_one_level(1) - closure = _testcapi.function_get_closure(func) - self.assertIsNone(closure) - self.assertIsNone(func.__closure__) - - func = unused_two_levels(1, 2)(3, 4) - closure = _testcapi.function_get_closure(func) - self.assertIsNone(closure) - self.assertIsNone(func.__closure__) - - # Functions with closures: - func = with_one_level(5) - closure = _testcapi.function_get_closure(func) - self.assertEqual(closure, func.__closure__) - self.assertIsInstance(closure, tuple) - self.assertEqual(len(closure), 1) - self.assertEqual(len(closure), len(func.__code__.co_freevars)) - for cell in closure: - self.assertIsInstance(cell, CellType) - self.assertTrue(closure[0].cell_contents, 5) - - func = with_two_levels(1, 2)(3, 4) - closure = _testcapi.function_get_closure(func) - self.assertEqual(closure, func.__closure__) - self.assertIsInstance(closure, tuple) - self.assertEqual(len(closure), 4) - self.assertEqual(len(closure), len(func.__code__.co_freevars)) - for cell in closure: - self.assertIsInstance(cell, CellType) - self.assertEqual([cell.cell_contents for cell in closure], - [1, 2, 3, 4]) - - def test_function_get_closure_error(self): - with self.assertRaises(SystemError): - _testcapi.function_get_closure(1) - with self.assertRaises(SystemError): - _testcapi.function_get_closure(None) - - def test_function_set_closure(self): - from types import CellType - - def function_without_closure(): ... - def function_with_closure(arg): - def inner(): - return arg - return inner - - func = function_without_closure - _testcapi.function_set_closure(func, (CellType(1), CellType(1))) - closure = _testcapi.function_get_closure(func) - self.assertEqual([c.cell_contents for c in closure], [1, 1]) - self.assertEqual([c.cell_contents for c in func.__closure__], [1, 1]) - - func = function_with_closure(1) - _testcapi.function_set_closure(func, - (CellType(1), CellType(2), CellType(3))) - closure = _testcapi.function_get_closure(func) - self.assertEqual([c.cell_contents for c in closure], [1, 2, 3]) - self.assertEqual([c.cell_contents for c in func.__closure__], [1, 2, 3]) - - def test_function_set_closure_none(self): - def function_without_closure(): ... - def function_with_closure(arg): - def inner(): - return arg - return inner - - _testcapi.function_set_closure(function_without_closure, None) - self.assertIsNone( - _testcapi.function_get_closure(function_without_closure)) - self.assertIsNone(function_without_closure.__closure__) - - _testcapi.function_set_closure(function_with_closure, None) - self.assertIsNone( - _testcapi.function_get_closure(function_with_closure)) - self.assertIsNone(function_with_closure.__closure__) - - def test_function_set_closure_errors(self): - def function_without_closure(): ... - - with self.assertRaises(SystemError): - _testcapi.function_set_closure(None, ()) # not a function - - with self.assertRaises(SystemError): - _testcapi.function_set_closure(function_without_closure, 1) - self.assertIsNone(function_without_closure.__closure__) # no change - - # NOTE: this works, but goes against the docs: - _testcapi.function_set_closure(function_without_closure, (1, 2)) - self.assertEqual( - _testcapi.function_get_closure(function_without_closure), (1, 2)) - self.assertEqual(function_without_closure.__closure__, (1, 2)) - - class TestPendingCalls(unittest.TestCase): # See the comment in ceval.c (at the "handle_eval_breaker" label) @@ -3209,5 +2919,6 @@ def test_pack_version_ctypes(self): result = ctypes_func(*args) self.assertEqual(result, expected) + if __name__ == "__main__": unittest.main() diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index a276fa642de42e..6bb05a06a3465d 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -162,7 +162,7 @@ @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c @MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c diff --git a/Modules/_testcapi/function.c b/Modules/_testcapi/function.c new file mode 100644 index 00000000000000..ec1ba508df2ce9 --- /dev/null +++ b/Modules/_testcapi/function.c @@ -0,0 +1,143 @@ +#include "parts.h" +#include "util.h" + + +static PyObject * +function_get_code(PyObject *self, PyObject *func) +{ + PyObject *code = PyFunction_GetCode(func); + if (code != NULL) { + return Py_NewRef(code); + } else { + return NULL; + } +} + + +static PyObject * +function_get_globals(PyObject *self, PyObject *func) +{ + PyObject *globals = PyFunction_GetGlobals(func); + if (globals != NULL) { + return Py_NewRef(globals); + } else { + return NULL; + } +} + + +static PyObject * +function_get_module(PyObject *self, PyObject *func) +{ + PyObject *module = PyFunction_GetModule(func); + if (module != NULL) { + return Py_NewRef(module); + } else { + return NULL; + } +} + + +static PyObject * +function_get_defaults(PyObject *self, PyObject *func) +{ + PyObject *defaults = PyFunction_GetDefaults(func); + if (defaults != NULL) { + return Py_NewRef(defaults); + } else if (PyErr_Occurred()) { + return NULL; + } else { + Py_RETURN_NONE; // This can happen when `defaults` are set to `None` + } +} + + +static PyObject * +function_set_defaults(PyObject *self, PyObject *args) +{ + PyObject *func = NULL, *defaults = NULL; + if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) { + return NULL; + } + int result = PyFunction_SetDefaults(func, defaults); + if (result == -1) + return NULL; + Py_RETURN_NONE; +} + + +static PyObject * +function_get_kw_defaults(PyObject *self, PyObject *func) +{ + PyObject *defaults = PyFunction_GetKwDefaults(func); + if (defaults != NULL) { + return Py_NewRef(defaults); + } else if (PyErr_Occurred()) { + return NULL; + } else { + Py_RETURN_NONE; // This can happen when `kwdefaults` are set to `None` + } +} + + +static PyObject * +function_set_kw_defaults(PyObject *self, PyObject *args) +{ + PyObject *func = NULL, *defaults = NULL; + if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) { + return NULL; + } + int result = PyFunction_SetKwDefaults(func, defaults); + if (result == -1) + return NULL; + Py_RETURN_NONE; +} + + +static PyObject * +function_get_closure(PyObject *self, PyObject *func) +{ + PyObject *closure = PyFunction_GetClosure(func); + if (closure != NULL) { + return Py_NewRef(closure); + } else if (PyErr_Occurred()) { + return NULL; + } else { + Py_RETURN_NONE; // This can happen when `closure` is set to `None` + } +} + + +static PyObject * +function_set_closure(PyObject *self, PyObject *args) +{ + PyObject *func = NULL, *closure = NULL; + if (!PyArg_ParseTuple(args, "OO", &func, &closure)) { + return NULL; + } + int result = PyFunction_SetClosure(func, closure); + if (result == -1) { + return NULL; + } + Py_RETURN_NONE; +} + + +static PyMethodDef test_methods[] = { + {"function_get_code", function_get_code, METH_O, NULL}, + {"function_get_globals", function_get_globals, METH_O, NULL}, + {"function_get_module", function_get_module, METH_O, NULL}, + {"function_get_defaults", function_get_defaults, METH_O, NULL}, + {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL}, + {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL}, + {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, + {"function_get_closure", function_get_closure, METH_O, NULL}, + {"function_set_closure", function_set_closure, METH_VARARGS, NULL}, + {NULL}, +}; + +int +_PyTestCapi_Init_Function(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index c4b73459b3712b..af6400162daf2b 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -64,5 +64,6 @@ int _PyTestCapi_Init_Config(PyObject *mod); int _PyTestCapi_Init_Import(PyObject *mod); int _PyTestCapi_Init_Frame(PyObject *mod); int _PyTestCapi_Init_Type(PyObject *mod); +int _PyTestCapi_Init_Function(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index ad9d836120b506..5b5b630e7e7581 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2652,119 +2652,6 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } -static PyObject * -function_get_code(PyObject *self, PyObject *func) -{ - PyObject *code = PyFunction_GetCode(func); - if (code != NULL) { - return Py_NewRef(code); - } else { - return NULL; - } -} - -static PyObject * -function_get_globals(PyObject *self, PyObject *func) -{ - PyObject *globals = PyFunction_GetGlobals(func); - if (globals != NULL) { - return Py_NewRef(globals); - } else { - return NULL; - } -} - -static PyObject * -function_get_module(PyObject *self, PyObject *func) -{ - PyObject *module = PyFunction_GetModule(func); - if (module != NULL) { - return Py_NewRef(module); - } else { - return NULL; - } -} - -static PyObject * -function_get_defaults(PyObject *self, PyObject *func) -{ - PyObject *defaults = PyFunction_GetDefaults(func); - if (defaults != NULL) { - return Py_NewRef(defaults); - } else if (PyErr_Occurred()) { - return NULL; - } else { - Py_RETURN_NONE; // This can happen when `defaults` are set to `None` - } -} - -static PyObject * -function_set_defaults(PyObject *self, PyObject *args) -{ - PyObject *func = NULL, *defaults = NULL; - if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) { - return NULL; - } - int result = PyFunction_SetDefaults(func, defaults); - if (result == -1) - return NULL; - Py_RETURN_NONE; -} - -static PyObject * -function_get_kw_defaults(PyObject *self, PyObject *func) -{ - PyObject *defaults = PyFunction_GetKwDefaults(func); - if (defaults != NULL) { - return Py_NewRef(defaults); - } else if (PyErr_Occurred()) { - return NULL; - } else { - Py_RETURN_NONE; // This can happen when `kwdefaults` are set to `None` - } -} - -static PyObject * -function_set_kw_defaults(PyObject *self, PyObject *args) -{ - PyObject *func = NULL, *defaults = NULL; - if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) { - return NULL; - } - int result = PyFunction_SetKwDefaults(func, defaults); - if (result == -1) - return NULL; - Py_RETURN_NONE; -} - -static PyObject * -function_get_closure(PyObject *self, PyObject *func) -{ - PyObject *closure = PyFunction_GetClosure(func); - if (closure != NULL) { - return Py_NewRef(closure); - } else if (PyErr_Occurred()) { - return NULL; - } else { - Py_RETURN_NONE; // This can happen when `closure` is set to `None` - } -} - -static PyObject * -function_set_closure(PyObject *self, PyObject *args) -{ - PyObject *func = NULL, *closure = NULL; - if (!PyArg_ParseTuple(args, "OO", &func, &closure)) { - return NULL; - } - int result = PyFunction_SetClosure(func, closure); - if (result == -1) { - return NULL; - } - Py_RETURN_NONE; -} - - static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { @@ -3286,15 +3173,6 @@ static PyMethodDef TestMethods[] = { {"settrace_to_record", settrace_to_record, METH_O, NULL}, {"test_macros", test_macros, METH_NOARGS, NULL}, {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, - {"function_get_code", function_get_code, METH_O, NULL}, - {"function_get_globals", function_get_globals, METH_O, NULL}, - {"function_get_module", function_get_module, METH_O, NULL}, - {"function_get_defaults", function_get_defaults, METH_O, NULL}, - {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL}, - {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL}, - {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, - {"function_get_closure", function_get_closure, METH_O, NULL}, - {"function_set_closure", function_set_closure, METH_VARARGS, NULL}, {"test_weakref_capi", test_weakref_capi, METH_NOARGS}, {"function_set_warning", function_set_warning, METH_NOARGS}, {"test_critical_sections", test_critical_sections, METH_NOARGS}, @@ -4081,6 +3959,9 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Type(m) < 0) { return NULL; } + if (_PyTestCapi_Init_Function(m) < 0) { + return NULL; + } PyState_AddModule(m, &_testcapimodule); return m; diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 09969331c6edd4..a68f15d25aabb7 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -130,6 +130,7 @@ + diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 52491643ad842f..21091e9dc1aa16 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -123,6 +123,9 @@ Source Files + + Source Files + From 9ba281d871c4df3a3ac4cb7896d24ba0d42751a3 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 31 Jan 2025 10:27:08 -0500 Subject: [PATCH 034/311] gh-128509: Add `sys._is_immortal` for identifying immortal objects (#128510) Co-authored-by: Kumar Aditya --- Doc/glossary.rst | 3 ++ Doc/library/sys.rst | 23 +++++++++++++ Doc/whatsnew/3.14.rst | 4 +++ ...-01-04-20-51-48.gh-issue-128509.3gr_-O.rst | 2 ++ Python/clinic/sysmodule.c.h | 32 ++++++++++++++++++- Python/sysmodule.c | 18 +++++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-04-20-51-48.gh-issue-128509.3gr_-O.rst diff --git a/Doc/glossary.rst b/Doc/glossary.rst index e3a14601398e89..d933ca6b467cf3 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -658,6 +658,9 @@ Glossary and therefore it is never deallocated while the interpreter is running. For example, :const:`True` and :const:`None` are immortal in CPython. + Immortal objects can be identified via :func:`sys._is_immortal`, or + via :c:func:`PyUnstable_IsImmortal` in the C API. + immutable An object with a fixed value. Immutable objects include numbers, strings and tuples. Such an object cannot be altered. A new object has to diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 5a096235713319..855237e0984972 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -855,6 +855,11 @@ always available. Unless explicitly noted otherwise, all variables are read-only reflect the actual number of references. Consequently, do not rely on the returned value to be accurate, other than a value of 0 or 1. + .. impl-detail:: + + :term:`Immortal ` objects with a large reference count can be + identified via :func:`_is_immortal`. + .. versionchanged:: 3.12 Immortal objects have very large refcounts that do not match the actual number of references to the object. @@ -1264,6 +1269,24 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. versionadded:: 3.12 +.. function:: _is_immortal(op) + + Return :const:`True` if the given object is :term:`immortal`, :const:`False` + otherwise. + + .. note:: + + Objects that are immortal (and thus return ``True`` upon being passed + to this function) are not guaranteed to be immortal in future versions, + and vice versa for mortal objects. + + .. versionadded:: next + + .. impl-detail:: + + This function should be used for specialized purposes only. + It is not guaranteed to exist in all implementations of Python. + .. function:: _is_interned(string) Return :const:`True` if the given string is "interned", :const:`False` diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 484e306335829a..daed0e8aa509a1 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -649,9 +649,13 @@ sys which only exists in specialized builds of Python, may now return objects from other interpreters than the one it's called in. +* Add :func:`sys._is_immortal` for determining if an object is :term:`immortal`. + (Contributed by Peter Bierma in :gh:`128509`.) + * On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore. It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. + sys.monitoring -------------- diff --git a/Misc/NEWS.d/next/Library/2025-01-04-20-51-48.gh-issue-128509.3gr_-O.rst b/Misc/NEWS.d/next/Library/2025-01-04-20-51-48.gh-issue-128509.3gr_-O.rst new file mode 100644 index 00000000000000..ba45884304f662 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-04-20-51-48.gh-issue-128509.3gr_-O.rst @@ -0,0 +1,2 @@ +Add :func:`sys._is_immortal` for identifying :term:`immortal` objects at +runtime. diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index cfcbd55388efa0..1e53624d4d45d7 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -373,6 +373,36 @@ sys__is_interned(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(sys__is_immortal__doc__, +"_is_immortal($module, op, /)\n" +"--\n" +"\n" +"Return True if the given object is \"immortal\" per PEP 683.\n" +"\n" +"This function should be used for specialized purposes only."); + +#define SYS__IS_IMMORTAL_METHODDEF \ + {"_is_immortal", (PyCFunction)sys__is_immortal, METH_O, sys__is_immortal__doc__}, + +static int +sys__is_immortal_impl(PyObject *module, PyObject *op); + +static PyObject * +sys__is_immortal(PyObject *module, PyObject *op) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = sys__is_immortal_impl(module, op); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(sys_settrace__doc__, "settrace($module, function, /)\n" "--\n" @@ -1724,4 +1754,4 @@ sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=568b0a0069dc43e8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1e5f608092c12636 input=a9049054013a1b77]*/ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 11b96c8455de14..d5cb448eb618e8 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -972,6 +972,23 @@ sys__is_interned_impl(PyObject *module, PyObject *string) return PyUnicode_CHECK_INTERNED(string); } +/*[clinic input] +sys._is_immortal -> bool + + op: object + / + +Return True if the given object is "immortal" per PEP 683. + +This function should be used for specialized purposes only. +[clinic start generated code]*/ + +static int +sys__is_immortal_impl(PyObject *module, PyObject *op) +/*[clinic end generated code: output=c2f5d6a80efb8d1a input=4609c9bf5481db76]*/ +{ + return PyUnstable_IsImmortal(op); +} /* * Cached interned string objects used for calling the profile and @@ -2588,6 +2605,7 @@ static PyMethodDef sys_methods[] = { SYS__GETFRAMEMODULENAME_METHODDEF SYS_GETWINDOWSVERSION_METHODDEF SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF + SYS__IS_IMMORTAL_METHODDEF SYS_INTERN_METHODDEF SYS__IS_INTERNED_METHODDEF SYS_IS_FINALIZING_METHODDEF From 54f74b80aef8b581f2b124d150903cec83aff005 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 31 Jan 2025 17:13:20 +0000 Subject: [PATCH 035/311] GH-128563: Move some labels, to simplify implementing tailcalling interpreter. (GH-129525) --- Python/bytecodes.c | 33 +++++++++++++-- Python/ceval.c | 70 +++++++++++++------------------ Python/ceval_macros.h | 4 +- Python/generated_cases.c.h | 28 +++++++++++-- Tools/cases_generator/analyzer.py | 2 - 5 files changed, 86 insertions(+), 51 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f659a5e5c920a7..effc8e0b6f6578 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5303,14 +5303,40 @@ dummy_func( tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; return NULL; } - goto resume_with_error; + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + goto error; } - label(resume_with_error) { + label(start_frame) { + if (_Py_EnterRecursivePy(tstate)) { + goto exit_unwind; + } next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + + #ifdef LLTRACE + { + int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); + frame->lltrace = lltrace; + if (lltrace < 0) { + goto exit_unwind; + } + } + #endif + + #ifdef Py_DEBUG + /* _PyEval_EvalFrameDefault() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!_PyErr_Occurred(tstate)); + #endif + + DISPATCH(); } + + + // END BYTECODES // } @@ -5320,7 +5346,6 @@ dummy_func( exit_unwind: handle_eval_breaker: resume_frame: - resume_with_error: start_frame: unbound_local_error: ; diff --git a/Python/ceval.c b/Python/ceval.c index e3b87441f8088d..11518684c136bd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -792,6 +792,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int return NULL; } + /* Local "register" variables. + * These are cached values from the frame and code object. */ + _Py_CODEUNIT *next_instr; + _PyStackRef *stack_pointer; + #if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG) /* Set these to invalid but identifiable values for debugging. */ entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0}; @@ -819,67 +824,36 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* support for generator.throw() */ if (throwflag) { if (_Py_EnterRecursivePy(tstate)) { - goto exit_unwind; + goto early_exit; } - /* Because this avoids the RESUME, - * we need to update instrumentation */ #ifdef Py_GIL_DISABLED /* Load thread-local bytecode */ if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); if (bytecode == NULL) { - goto exit_unwind; + goto early_exit; } ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; } #endif + /* Because this avoids the RESUME, we need to update instrumentation */ _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); - monitor_throw(tstate, frame, frame->instr_ptr); - /* TO DO -- Monitor throw entry. */ - goto resume_with_error; + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + monitor_throw(tstate, frame, next_instr); + goto error; } - /* Local "register" variables. - * These are cached values from the frame and code object. */ - _Py_CODEUNIT *next_instr; - _PyStackRef *stack_pointer; - #if defined(_Py_TIER2) && !defined(_Py_JIT) /* Tier 2 interpreter state */ _PyExecutorObject *current_executor = NULL; const _PyUOpInstruction *next_uop = NULL; #endif -start_frame: - if (_Py_EnterRecursivePy(tstate)) { - goto exit_unwind; - } - - next_instr = frame->instr_ptr; -resume_frame: - stack_pointer = _PyFrame_GetStackPointer(frame); - -#ifdef LLTRACE - { - int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); - frame->lltrace = lltrace; - if (lltrace < 0) { - goto exit_unwind; - } - } -#endif - -#ifdef Py_DEBUG - /* _PyEval_EvalFrameDefault() must not be called with an exception set, - because it can clear it (directly or indirectly) and so the - caller loses its exception */ - assert(!_PyErr_Occurred(tstate)); -#endif - - DISPATCH(); + goto start_frame; #include "generated_cases.c.h" @@ -983,10 +957,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); assert(next_uop[-1].format == UOP_FORMAT_TARGET); frame->return_offset = 0; // Don't leave this random - _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(current_executor); tstate->previous_executor = NULL; - goto resume_with_error; + next_instr = frame->instr_ptr; + goto error; jump_to_jump_target: assert(next_uop[-1].format == UOP_FORMAT_JUMP); @@ -1018,6 +992,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif // _Py_TIER2 +early_exit: + assert(_PyErr_Occurred(tstate)); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + frame->return_offset = 0; + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); + /* Restore previous frame and exit */ + tstate->current_frame = frame->previous; + tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; + return NULL; } #if defined(__GNUC__) diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 62c80c96e422fd..c2fc38f3c18e53 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -381,7 +381,9 @@ do { \ tstate->previous_executor = NULL; \ frame = tstate->current_frame; \ if (next_instr == NULL) { \ - goto resume_with_error; \ + next_instr = frame->instr_ptr; \ + stack_pointer = _PyFrame_GetStackPointer(frame); \ + goto error; \ } \ stack_pointer = _PyFrame_GetStackPointer(frame); \ DISPATCH(); \ diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ffdad70815caef..38ea63d71ab044 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8703,14 +8703,36 @@ tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; return NULL; } - goto resume_with_error; + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + goto error; } - resume_with_error: + start_frame: { + if (_Py_EnterRecursivePy(tstate)) { + goto exit_unwind; + } next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + #ifdef LLTRACE + { + int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); + frame->lltrace = lltrace; + if (lltrace < 0) { + goto exit_unwind; + } + } + #endif + + #ifdef Py_DEBUG + /* _PyEval_EvalFrameDefault() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!_PyErr_Occurred(tstate)); + #endif + + DISPATCH(); } /* END LABELS */ diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index b9293ff4b19951..acf9458019fb4b 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -511,7 +511,6 @@ def has_error_with_pop(op: parser.InstDef) -> bool: variable_used(op, "ERROR_IF") or variable_used(op, "pop_1_error") or variable_used(op, "exception_unwind") - or variable_used(op, "resume_with_error") ) @@ -520,7 +519,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: variable_used(op, "ERROR_NO_POP") or variable_used(op, "pop_1_error") or variable_used(op, "exception_unwind") - or variable_used(op, "resume_with_error") ) From d89a5f6a6e65511a5f6e0618c4c30a7aa5aba56a Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Fri, 31 Jan 2025 11:41:34 -0600 Subject: [PATCH 036/311] gh-105704: Disallow square brackets (`[` and `]`) in domain names for parsed URLs (#129418) * gh-105704: Disallow square brackets ( and ) in domain names for parsed URLs * Use Sphinx references Co-authored-by: Peter Bierma * Add mismatched bracket test cases, fix news format * Add more test coverage for ports --------- Co-authored-by: Peter Bierma --- Lib/test/test_urlparse.py | 37 ++++++++++++++++++- Lib/urllib/parse.py | 20 +++++++++- ...-01-28-14-08-03.gh-issue-105704.EnhHxu.rst | 4 ++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 4516bdea6adb19..b51cc006b73280 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -1412,16 +1412,51 @@ def test_invalid_bracketed_hosts(self): self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query') self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query') self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip') + self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix') def test_splitting_bracketed_hosts(self): - p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query') + p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query') self.assertEqual(p1.hostname, 'v6a.ip') self.assertEqual(p1.username, 'user') self.assertEqual(p1.path, '/path') + self.assertEqual(p1.port, 1234) p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query') self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test') self.assertEqual(p2.username, 'user') self.assertEqual(p2.path, '/path') + self.assertIs(p2.port, None) p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query') self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test') self.assertEqual(p3.username, 'user') diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index c412c729852272..9d51f4c6812b57 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -439,6 +439,23 @@ def _checknetloc(netloc): raise ValueError("netloc '" + netloc + "' contains invalid " + "characters under NFKC normalization") +def _check_bracketed_netloc(netloc): + # Note that this function must mirror the splitting + # done in NetlocResultMixins._hostinfo(). + hostname_and_port = netloc.rpartition('@')[2] + before_bracket, have_open_br, bracketed = hostname_and_port.partition('[') + if have_open_br: + # No data is allowed before a bracket. + if before_bracket: + raise ValueError("Invalid IPv6 URL") + hostname, _, port = bracketed.partition(']') + # No data is allowed after the bracket but before the port delimiter. + if port and not port.startswith(":"): + raise ValueError("Invalid IPv6 URL") + else: + hostname, _, port = hostname_and_port.partition(':') + _check_bracketed_host(hostname) + # Valid bracketed hosts are defined in # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/ def _check_bracketed_host(hostname): @@ -505,8 +522,7 @@ def _urlsplit(url, scheme=None, allow_fragments=True): (']' in netloc and '[' not in netloc)): raise ValueError("Invalid IPv6 URL") if '[' in netloc and ']' in netloc: - bracketed_host = netloc.partition('[')[2].partition(']')[0] - _check_bracketed_host(bracketed_host) + _check_bracketed_netloc(netloc) if allow_fragments and '#' in url: url, fragment = url.split('#', 1) if '?' in url: diff --git a/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst new file mode 100644 index 00000000000000..bff1bc6b0d609c --- /dev/null +++ b/Misc/NEWS.d/next/Security/2025-01-28-14-08-03.gh-issue-105704.EnhHxu.rst @@ -0,0 +1,4 @@ +When using :func:`urllib.parse.urlsplit` and :func:`urllib.parse.urlparse` host +parsing would not reject domain names containing square brackets (``[`` and +``]``). Square brackets are only valid for IPv6 and IPvFuture hosts according to +`RFC 3986 Section 3.2.2 `__. From 7d0521d5fc32f4837257e89e6ead652223ecfada Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Sat, 1 Feb 2025 11:39:44 +0000 Subject: [PATCH 037/311] gh-126835: Move optimization of constant sequence creation from codegen to CFG (#129426) Codegen phase has an optimization that transforms ``` LOAD_CONST x LOAD_CONST y LOAD_CONXT z BUILD_LIST/BUILD_SET (3) ``` -> ``` BUILD_LIST/BUILD_SET (0) LOAD_CONST (x, y, z) LIST_EXTEND/SET_UPDATE 1 ``` This optimization has now been moved to CFG phase to make #128802 work. Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Co-authored-by: Yan Yanchii --- Lib/test/test_dis.py | 4 +-- Python/codegen.c | 43 ------------------------- Python/flowgraph.c | 75 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index e99289cf66af67..0b273cbd63e21e 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -892,7 +892,7 @@ def loop_test(): %3d RESUME_CHECK 0 %3d BUILD_LIST 0 - LOAD_CONST_MORTAL 0 ((1, 2, 3)) + LOAD_CONST_MORTAL 1 ((1, 2, 3)) LIST_EXTEND 1 LOAD_SMALL_INT 3 BINARY_OP 5 (*) @@ -908,7 +908,7 @@ def loop_test(): %3d L2: END_FOR POP_ITER - LOAD_CONST_IMMORTAL 1 (None) + LOAD_CONST_IMMORTAL 0 (None) RETURN_VALUE """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, diff --git a/Python/codegen.c b/Python/codegen.c index df3b5aaac1d0d9..0bf9526cdc8435 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -201,9 +201,6 @@ static int codegen_subscript(compiler *, expr_ty); static int codegen_slice_two_parts(compiler *, expr_ty); static int codegen_slice(compiler *, expr_ty); -static bool are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t); - - static int codegen_with(compiler *, stmt_ty, int); static int codegen_async_with(compiler *, stmt_ty, int); static int codegen_async_for(compiler *, stmt_ty); @@ -3210,34 +3207,6 @@ starunpack_helper_impl(compiler *c, location loc, int build, int add, int extend, int tuple) { Py_ssize_t n = asdl_seq_LEN(elts); - if (!injected_arg && n > 2 && are_all_items_const(elts, 0, n)) { - PyObject *folded = PyTuple_New(n); - if (folded == NULL) { - return ERROR; - } - for (Py_ssize_t i = 0; i < n; i++) { - PyObject *val = ((expr_ty)asdl_seq_GET(elts, i))->v.Constant.value; - PyTuple_SET_ITEM(folded, i, Py_NewRef(val)); - } - if (tuple && !pushed) { - ADDOP_LOAD_CONST_NEW(c, loc, folded); - } else { - if (add == SET_ADD) { - Py_SETREF(folded, PyFrozenSet_New(folded)); - if (folded == NULL) { - return ERROR; - } - } - ADDOP_I(c, loc, build, pushed); - ADDOP_LOAD_CONST_NEW(c, loc, folded); - ADDOP_I(c, loc, extend, 1); - if (tuple) { - ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); - } - } - return SUCCESS; - } - int big = n + pushed + (injected_arg ? 1 : 0) > STACK_USE_GUIDELINE; int seen_star = 0; for (Py_ssize_t i = 0; i < n; i++) { @@ -3389,18 +3358,6 @@ codegen_set(compiler *c, expr_ty e) BUILD_SET, SET_ADD, SET_UPDATE, 0); } -static bool -are_all_items_const(asdl_expr_seq *seq, Py_ssize_t begin, Py_ssize_t end) -{ - for (Py_ssize_t i = begin; i < end; i++) { - expr_ty key = (expr_ty)asdl_seq_GET(seq, i); - if (key == NULL || key->kind != Constant_kind) { - return false; - } - } - return true; -} - static int codegen_subdict(compiler *c, expr_ty e, Py_ssize_t begin, Py_ssize_t end) { diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 24561c1ee04db9..a0b76050fd4af6 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1336,6 +1336,17 @@ add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache) return (int)index; } +static bool +is_constant_sequence(cfg_instr *inst, int n) +{ + for (int i = 0; i < n; i++) { + if(!loads_const(inst[i].i_opcode)) { + return false; + } + } + return true; +} + /* Replace LOAD_CONST c1, LOAD_CONST c2 ... LOAD_CONST cn, BUILD_TUPLE n with LOAD_CONST (c1, c2, ... cn). The consts table must still be in list form so that the @@ -1353,10 +1364,8 @@ fold_tuple_on_constants(PyObject *const_cache, assert(inst[n].i_opcode == BUILD_TUPLE); assert(inst[n].i_oparg == n); - for (int i = 0; i < n; i++) { - if (!loads_const(inst[i].i_opcode)) { - return SUCCESS; - } + if (!is_constant_sequence(inst, n)) { + return SUCCESS; } /* Buildup new tuple of constants */ @@ -1384,6 +1393,56 @@ fold_tuple_on_constants(PyObject *const_cache, return SUCCESS; } +#define MIN_CONST_SEQUENCE_SIZE 3 +/* Replace LOAD_CONST c1, LOAD_CONST c2 ... LOAD_CONST cN, BUILD_LIST N + with BUILD_LIST 0, LOAD_CONST (c1, c2, ... cN), LIST_EXTEND 1, + or BUILD_SET & SET_UPDATE respectively. +*/ +static int +optimize_if_const_list_or_set(PyObject *const_cache, cfg_instr* inst, int n, PyObject *consts) +{ + assert(PyDict_CheckExact(const_cache)); + assert(PyList_CheckExact(consts)); + assert(inst[n].i_oparg == n); + + int build = inst[n].i_opcode; + assert(build == BUILD_LIST || build == BUILD_SET); + int extend = build == BUILD_LIST ? LIST_EXTEND : SET_UPDATE; + + if (n < MIN_CONST_SEQUENCE_SIZE || !is_constant_sequence(inst, n)) { + return SUCCESS; + } + PyObject *newconst = PyTuple_New(n); + if (newconst == NULL) { + return ERROR; + } + for (int i = 0; i < n; i++) { + int op = inst[i].i_opcode; + int arg = inst[i].i_oparg; + PyObject *constant = get_const_value(op, arg, consts); + if (constant == NULL) { + return ERROR; + } + PyTuple_SET_ITEM(newconst, i, constant); + } + if (build == BUILD_SET) { + PyObject *frozenset = PyFrozenSet_New(newconst); + if (frozenset == NULL) { + return ERROR; + } + Py_SETREF(newconst, frozenset); + } + int index = add_const(newconst, consts, const_cache); + RETURN_IF_ERROR(index); + INSTR_SET_OP1(&inst[0], build, 0); + for (int i = 1; i < n - 1; i++) { + INSTR_SET_OP0(&inst[i], NOP); + } + INSTR_SET_OP1(&inst[n-1], LOAD_CONST, index); + INSTR_SET_OP1(&inst[n], extend, 1); + return SUCCESS; +} + #define VISITED (-1) // Replace an arbitrary run of SWAPs and NOPs with an optimal one that has the @@ -1751,6 +1810,14 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) } } break; + case BUILD_LIST: + case BUILD_SET: + if (i >= oparg) { + if (optimize_if_const_list_or_set(const_cache, inst-oparg, oparg, consts) < 0) { + goto error; + } + } + break; case POP_JUMP_IF_NOT_NONE: case POP_JUMP_IF_NONE: switch (target->i_opcode) { From 71ae93374defd192e5e88fe0912eff4f8e56f286 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 1 Feb 2025 13:39:16 +0100 Subject: [PATCH 038/311] gh-93649: Move _testcapi tests to specific files (#129544) Move many functions from _testcapimodule.c into more specific files in Modules/_testcapi/. In moved code: * Replace get_testerror() with PyExc_AssertionError. * Replace raiseTestError() with PyErr_Format(PyExc_AssertionError, ...). --- Lib/test/test_capi/test_misc.py | 36 -- Lib/test/test_capi/test_object.py | 40 ++ Modules/_testcapi/dict.c | 78 ++++ Modules/_testcapi/float.c | 59 +++ Modules/_testcapi/list.c | 51 ++- Modules/_testcapi/mem.c | 101 +++++ Modules/_testcapi/object.c | 308 +++++++++++++++- Modules/_testcapi/set.c | 31 +- Modules/_testcapimodule.c | 593 ------------------------------ 9 files changed, 651 insertions(+), 646 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index acc4803d785366..b218f72f1bbce0 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -403,42 +403,6 @@ def test_buildvalue_ints(self): def test_buildvalue_N(self): _testcapi.test_buildvalue_N() - def check_negative_refcount(self, code): - # bpo-35059: Check that Py_DECREF() reports the correct filename - # when calling _Py_NegativeRefcount() to abort Python. - code = textwrap.dedent(code) - rc, out, err = assert_python_failure('-c', code) - self.assertRegex(err, - br'_testcapimodule\.c:[0-9]+: ' - br'_Py_NegativeRefcount: Assertion failed: ' - br'object has negative ref count') - - @unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'), - 'need _testcapi.negative_refcount()') - def test_negative_refcount(self): - code = """ - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.negative_refcount() - """ - self.check_negative_refcount(code) - - @unittest.skipUnless(hasattr(_testcapi, 'decref_freed_object'), - 'need _testcapi.decref_freed_object()') - @support.skip_if_sanitizer("use after free on purpose", - address=True, memory=True, ub=True) - def test_decref_freed_object(self): - code = """ - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.decref_freed_object() - """ - self.check_negative_refcount(code) - def test_trashcan_subclass(self): # bpo-35983: Check that the trashcan mechanism for "list" is NOT # activated when its tp_dealloc is being called by a subclass diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py index b0d39937fd865f..5d0a383de64520 100644 --- a/Lib/test/test_capi/test_object.py +++ b/Lib/test/test_capi/test_object.py @@ -1,9 +1,12 @@ import enum +import textwrap import unittest from test import support from test.support import import_helper from test.support import os_helper from test.support import threading_helper +from test.support.script_helper import assert_python_failure + _testlimitedcapi = import_helper.import_module('_testlimitedcapi') _testcapi = import_helper.import_module('_testcapi') @@ -170,5 +173,42 @@ def silly_func(obj): self.assertTrue(_testinternalcapi.has_deferred_refcount(silly_list)) +class CAPITest(unittest.TestCase): + def check_negative_refcount(self, code): + # bpo-35059: Check that Py_DECREF() reports the correct filename + # when calling _Py_NegativeRefcount() to abort Python. + code = textwrap.dedent(code) + rc, out, err = assert_python_failure('-c', code) + self.assertRegex(err, + br'object\.c:[0-9]+: ' + br'_Py_NegativeRefcount: Assertion failed: ' + br'object has negative ref count') + + @unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'), + 'need _testcapi.negative_refcount()') + def test_negative_refcount(self): + code = """ + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.negative_refcount() + """ + self.check_negative_refcount(code) + + @unittest.skipUnless(hasattr(_testcapi, 'decref_freed_object'), + 'need _testcapi.decref_freed_object()') + @support.skip_if_sanitizer("use after free on purpose", + address=True, memory=True, ub=True) + def test_decref_freed_object(self): + code = """ + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.decref_freed_object() + """ + self.check_negative_refcount(code) + if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapi/dict.c b/Modules/_testcapi/dict.c index 307797f98f12ae..b7c73d7332bd4e 100644 --- a/Modules/_testcapi/dict.c +++ b/Modules/_testcapi/dict.c @@ -181,6 +181,83 @@ dict_popstring_null(PyObject *self, PyObject *args) RETURN_INT(PyDict_PopString(dict, key, NULL)); } + +static int +test_dict_inner(PyObject *self, int count) +{ + Py_ssize_t pos = 0, iterations = 0; + int i; + PyObject *dict = PyDict_New(); + PyObject *v, *k; + + if (dict == NULL) + return -1; + + for (i = 0; i < count; i++) { + v = PyLong_FromLong(i); + if (v == NULL) { + goto error; + } + if (PyDict_SetItem(dict, v, v) < 0) { + Py_DECREF(v); + goto error; + } + Py_DECREF(v); + } + + k = v = UNINITIALIZED_PTR; + while (PyDict_Next(dict, &pos, &k, &v)) { + PyObject *o; + iterations++; + + assert(k != UNINITIALIZED_PTR); + assert(v != UNINITIALIZED_PTR); + i = PyLong_AS_LONG(v) + 1; + o = PyLong_FromLong(i); + if (o == NULL) { + goto error; + } + if (PyDict_SetItem(dict, k, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + k = v = UNINITIALIZED_PTR; + } + assert(k == UNINITIALIZED_PTR); + assert(v == UNINITIALIZED_PTR); + + Py_DECREF(dict); + + if (iterations != count) { + PyErr_SetString( + PyExc_AssertionError, + "test_dict_iteration: dict iteration went wrong "); + return -1; + } else { + return 0; + } +error: + Py_DECREF(dict); + return -1; +} + + +static PyObject* +test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) +{ + int i; + + for (i = 0; i < 200; i++) { + if (test_dict_inner(self, i) < 0) { + return NULL; + } + } + + Py_RETURN_NONE; +} + + static PyMethodDef test_methods[] = { {"dict_containsstring", dict_containsstring, METH_VARARGS}, {"dict_getitemref", dict_getitemref, METH_VARARGS}, @@ -191,6 +268,7 @@ static PyMethodDef test_methods[] = { {"dict_pop_null", dict_pop_null, METH_VARARGS}, {"dict_popstring", dict_popstring, METH_VARARGS}, {"dict_popstring_null", dict_popstring_null, METH_VARARGS}, + {"test_dict_iteration", test_dict_iteration, METH_NOARGS}, {NULL}, }; diff --git a/Modules/_testcapi/float.c b/Modules/_testcapi/float.c index 15ea97ec4520b7..e3869134c84d43 100644 --- a/Modules/_testcapi/float.c +++ b/Modules/_testcapi/float.c @@ -99,9 +99,68 @@ _testcapi_float_unpack_impl(PyObject *module, const char *data, return PyFloat_FromDouble(d); } + +/* Test PyOS_string_to_double. */ +static PyObject * +test_string_to_double(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + double result; + const char *msg; + +#define CHECK_STRING(STR, expected) \ + do { \ + result = PyOS_string_to_double(STR, NULL, NULL); \ + if (result == -1.0 && PyErr_Occurred()) { \ + return NULL; \ + } \ + if (result != (double)expected) { \ + msg = "conversion of " STR " to float failed"; \ + goto fail; \ + } \ + } while (0) + +#define CHECK_INVALID(STR) \ + do { \ + result = PyOS_string_to_double(STR, NULL, NULL); \ + if (result == -1.0 && PyErr_Occurred()) { \ + if (PyErr_ExceptionMatches(PyExc_ValueError)) { \ + PyErr_Clear(); \ + } \ + else { \ + return NULL; \ + } \ + } \ + else { \ + msg = "conversion of " STR " didn't raise ValueError"; \ + goto fail; \ + } \ + } while (0) + + CHECK_STRING("0.1", 0.1); + CHECK_STRING("1.234", 1.234); + CHECK_STRING("-1.35", -1.35); + CHECK_STRING(".1e01", 1.0); + CHECK_STRING("2.e-2", 0.02); + + CHECK_INVALID(" 0.1"); + CHECK_INVALID("\t\n-3"); + CHECK_INVALID(".123 "); + CHECK_INVALID("3\n"); + CHECK_INVALID("123abc"); + + Py_RETURN_NONE; + fail: + PyErr_Format(PyExc_AssertionError, "test_string_to_double: %s", msg); + return NULL; +#undef CHECK_STRING +#undef CHECK_INVALID +} + + static PyMethodDef test_methods[] = { _TESTCAPI_FLOAT_PACK_METHODDEF _TESTCAPI_FLOAT_UNPACK_METHODDEF + {"test_string_to_double", test_string_to_double, METH_NOARGS}, {NULL}, }; diff --git a/Modules/_testcapi/list.c b/Modules/_testcapi/list.c index 09cec4c30c8c36..530b47780ac94e 100644 --- a/Modules/_testcapi/list.c +++ b/Modules/_testcapi/list.c @@ -60,22 +60,61 @@ list_extend(PyObject* Py_UNUSED(module), PyObject *args) } +static PyObject* +test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject* list; + int i; + + /* SF bug 132008: PyList_Reverse segfaults */ +#define NLIST 30 + list = PyList_New(NLIST); + if (list == (PyObject*)NULL) + return (PyObject*)NULL; + /* list = range(NLIST) */ + for (i = 0; i < NLIST; ++i) { + PyObject* anint = PyLong_FromLong(i); + if (anint == (PyObject*)NULL) { + Py_DECREF(list); + return (PyObject*)NULL; + } + PyList_SET_ITEM(list, i, anint); + } + /* list.reverse(), via PyList_Reverse() */ + i = PyList_Reverse(list); /* should not blow up! */ + if (i != 0) { + Py_DECREF(list); + return (PyObject*)NULL; + } + /* Check that list == range(29, -1, -1) now */ + for (i = 0; i < NLIST; ++i) { + PyObject* anint = PyList_GET_ITEM(list, i); + if (PyLong_AS_LONG(anint) != NLIST-1-i) { + PyErr_SetString(PyExc_AssertionError, + "test_list_api: reverse screwed up"); + Py_DECREF(list); + return (PyObject*)NULL; + } + } + Py_DECREF(list); +#undef NLIST + + Py_RETURN_NONE; +} + + static PyMethodDef test_methods[] = { {"list_get_size", list_get_size, METH_O}, {"list_get_item", list_get_item, METH_VARARGS}, {"list_set_item", list_set_item, METH_VARARGS}, {"list_clear", list_clear, METH_O}, {"list_extend", list_extend, METH_VARARGS}, - + {"test_list_api", test_list_api, METH_NOARGS}, {NULL}, }; int _PyTestCapi_Init_List(PyObject *m) { - if (PyModule_AddFunctions(m, test_methods) < 0) { - return -1; - } - - return 0; + return PyModule_AddFunctions(m, test_methods); } diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c index ecae5ba26226a6..7237fb94c3f51f 100644 --- a/Modules/_testcapi/mem.c +++ b/Modules/_testcapi/mem.c @@ -584,6 +584,106 @@ tracemalloc_untrack(PyObject *self, PyObject *args) Py_RETURN_NONE; } + +static void +tracemalloc_track_race_thread(void *data) +{ + PyTraceMalloc_Track(123, 10, 1); + PyTraceMalloc_Untrack(123, 10); + + PyThread_type_lock lock = (PyThread_type_lock)data; + PyThread_release_lock(lock); +} + +// gh-128679: Test fix for tracemalloc.stop() race condition +static PyObject * +tracemalloc_track_race(PyObject *self, PyObject *args) +{ +#define NTHREAD 50 + PyObject *tracemalloc = NULL; + PyObject *stop = NULL; + PyThread_type_lock locks[NTHREAD]; + memset(locks, 0, sizeof(locks)); + + // Call tracemalloc.start() + tracemalloc = PyImport_ImportModule("tracemalloc"); + if (tracemalloc == NULL) { + goto error; + } + PyObject *start = PyObject_GetAttrString(tracemalloc, "start"); + if (start == NULL) { + goto error; + } + PyObject *res = PyObject_CallNoArgs(start); + Py_DECREF(start); + if (res == NULL) { + goto error; + } + Py_DECREF(res); + + stop = PyObject_GetAttrString(tracemalloc, "stop"); + Py_CLEAR(tracemalloc); + if (stop == NULL) { + goto error; + } + + // Start threads + for (size_t i = 0; i < NTHREAD; i++) { + PyThread_type_lock lock = PyThread_allocate_lock(); + if (!lock) { + PyErr_NoMemory(); + goto error; + } + locks[i] = lock; + PyThread_acquire_lock(lock, 1); + + unsigned long thread; + thread = PyThread_start_new_thread(tracemalloc_track_race_thread, + (void*)lock); + if (thread == (unsigned long)-1) { + PyErr_SetString(PyExc_RuntimeError, "can't start new thread"); + goto error; + } + } + + // Call tracemalloc.stop() while threads are running + res = PyObject_CallNoArgs(stop); + Py_CLEAR(stop); + if (res == NULL) { + goto error; + } + Py_DECREF(res); + + // Wait until threads complete with the GIL released + Py_BEGIN_ALLOW_THREADS + for (size_t i = 0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + PyThread_acquire_lock(lock, 1); + PyThread_release_lock(lock); + } + Py_END_ALLOW_THREADS + + // Free threads locks + for (size_t i=0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + PyThread_free_lock(lock); + } + Py_RETURN_NONE; + +error: + Py_CLEAR(tracemalloc); + Py_CLEAR(stop); + for (size_t i=0; i < NTHREAD; i++) { + PyThread_type_lock lock = locks[i]; + if (lock) { + PyThread_free_lock(lock); + } + } + return NULL; +#undef NTHREAD +} + + static PyMethodDef test_methods[] = { {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS}, @@ -602,6 +702,7 @@ static PyMethodDef test_methods[] = { // Tracemalloc tests {"tracemalloc_track", tracemalloc_track, METH_VARARGS}, {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, + {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, {NULL}, }; diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 409b0c83c18cfd..2d538627d213fd 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -185,6 +185,292 @@ test_py_try_inc_ref(PyObject *self, PyObject *unused) Py_RETURN_NONE; } + +static PyObject * +_test_incref(PyObject *ob) +{ + return Py_NewRef(ob); +} + +static PyObject * +test_xincref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_XINCREF(_test_incref(obj)); + Py_DECREF(obj); + Py_DECREF(obj); + Py_DECREF(obj); + Py_RETURN_NONE; +} + + +static PyObject * +test_incref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_INCREF(_test_incref(obj)); + Py_DECREF(obj); + Py_DECREF(obj); + Py_DECREF(obj); + Py_RETURN_NONE; +} + + +static PyObject * +test_xdecref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + Py_XDECREF(PyLong_FromLong(0)); + Py_RETURN_NONE; +} + + +static PyObject * +test_decref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + Py_DECREF(PyLong_FromLong(0)); + Py_RETURN_NONE; +} + + +static PyObject * +test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_IncRef(obj); + Py_DecRef(obj); + Py_DecRef(obj); + Py_RETURN_NONE; +} + + +#ifdef Py_REF_DEBUG +static PyObject * +negative_refcount(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *obj = PyUnicode_FromString("negative_refcount"); + if (obj == NULL) { + return NULL; + } + assert(Py_REFCNT(obj) == 1); + + Py_SET_REFCNT(obj, 0); + /* Py_DECREF() must call _Py_NegativeRefcount() and abort Python */ + Py_DECREF(obj); + + Py_RETURN_NONE; +} + + +static PyObject * +decref_freed_object(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *obj = PyUnicode_FromString("decref_freed_object"); + if (obj == NULL) { + return NULL; + } + assert(Py_REFCNT(obj) == 1); + + // Deallocate the memory + Py_DECREF(obj); + // obj is a now a dangling pointer + + // gh-109496: If Python is built in debug mode, Py_DECREF() must call + // _Py_NegativeRefcount() and abort Python. + Py_DECREF(obj); + + Py_RETURN_NONE; +} +#endif + + +// Test Py_CLEAR() macro +static PyObject* +test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // simple case with a variable + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + Py_CLEAR(obj); + assert(obj == NULL); + + // gh-98724: complex case, Py_CLEAR() argument has a side effect + PyObject* array[1]; + array[0] = PyList_New(0); + if (array[0] == NULL) { + return NULL; + } + + PyObject **p = array; + Py_CLEAR(*p++); + assert(array[0] == NULL); + assert(p == array + 1); + + Py_RETURN_NONE; +} + + +// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear() +static PyObject* +test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // Py_SETREF() simple case with a variable + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + Py_SETREF(obj, NULL); + assert(obj == NULL); + + // Py_XSETREF() simple case with a variable + PyObject *obj2 = PyList_New(0); + if (obj2 == NULL) { + return NULL; + } + Py_XSETREF(obj2, NULL); + assert(obj2 == NULL); + // test Py_XSETREF() when the argument is NULL + Py_XSETREF(obj2, NULL); + assert(obj2 == NULL); + + // gh-98724: complex case, Py_SETREF() argument has a side effect + PyObject* array[1]; + array[0] = PyList_New(0); + if (array[0] == NULL) { + return NULL; + } + + PyObject **p = array; + Py_SETREF(*p++, NULL); + assert(array[0] == NULL); + assert(p == array + 1); + + // gh-98724: complex case, Py_XSETREF() argument has a side effect + PyObject* array2[1]; + array2[0] = PyList_New(0); + if (array2[0] == NULL) { + return NULL; + } + + PyObject **p2 = array2; + Py_XSETREF(*p2++, NULL); + assert(array2[0] == NULL); + assert(p2 == array2 + 1); + + // test Py_XSETREF() when the argument is NULL + p2 = array2; + Py_XSETREF(*p2++, NULL); + assert(array2[0] == NULL); + assert(p2 == array2 + 1); + + Py_RETURN_NONE; +} + + +#define TEST_REFCOUNT() \ + do { \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + assert(Py_REFCNT(obj) == 1); \ + \ + /* test Py_NewRef() */ \ + PyObject *ref = Py_NewRef(obj); \ + assert(ref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(ref); \ + \ + /* test Py_XNewRef() */ \ + PyObject *xref = Py_XNewRef(obj); \ + assert(xref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(xref); \ + \ + assert(Py_XNewRef(NULL) == NULL); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) + + +// Test Py_NewRef() and Py_XNewRef() macros +static PyObject* +test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_REFCOUNT(); +} + +#undef Py_NewRef +#undef Py_XNewRef + +// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros. +static PyObject* +test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_REFCOUNT(); +} + + +// Test Py_Is() function +#define TEST_PY_IS() \ + do { \ + PyObject *o_none = Py_None; \ + PyObject *o_true = Py_True; \ + PyObject *o_false = Py_False; \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + \ + /* test Py_Is() */ \ + assert(Py_Is(obj, obj)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_None */ \ + assert(Py_Is(o_none, o_none)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_True */ \ + assert(Py_Is(o_true, o_true)); \ + assert(!Py_Is(o_false, o_true)); \ + assert(!Py_Is(obj, o_true)); \ + \ + /* test Py_False */ \ + assert(Py_Is(o_false, o_false)); \ + assert(!Py_Is(o_true, o_false)); \ + assert(!Py_Is(obj, o_false)); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) + +// Test Py_Is() macro +static PyObject* +test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); +} + +#undef Py_Is + +// Test Py_Is() function, after undefining its macro. +static PyObject* +test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); +} + + +static PyObject * +clear_managed_dict(PyObject *self, PyObject *obj) +{ + PyObject_ClearManagedDict(obj); + Py_RETURN_NONE; +} + + static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, @@ -193,15 +479,27 @@ static PyMethodDef test_methods[] = { {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, + {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, + {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, + {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, + {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, + {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, +#ifdef Py_REF_DEBUG + {"negative_refcount", negative_refcount, METH_NOARGS}, + {"decref_freed_object", decref_freed_object, METH_NOARGS}, +#endif + {"test_py_clear", test_py_clear, METH_NOARGS}, + {"test_py_setref", test_py_setref, METH_NOARGS}, + {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, + {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, + {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, + {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, + {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, {NULL}, }; int _PyTestCapi_Init_Object(PyObject *m) { - if (PyModule_AddFunctions(m, test_methods) < 0) { - return -1; - } - - return 0; + return PyModule_AddFunctions(m, test_methods); } diff --git a/Modules/_testcapi/set.c b/Modules/_testcapi/set.c index 31b52cee5e9623..092715ab7d0aa4 100644 --- a/Modules/_testcapi/set.c +++ b/Modules/_testcapi/set.c @@ -8,18 +8,37 @@ set_get_size(PyObject *self, PyObject *obj) RETURN_SIZE(PySet_GET_SIZE(obj)); } + +static PyObject* +test_set_type_size(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + + // Ensure that following tests don't modify the object, + // to ensure that Py_DECREF() will not crash. + assert(Py_TYPE(obj) == &PyList_Type); + assert(Py_SIZE(obj) == 0); + + // bpo-39573: Test Py_SET_TYPE() and Py_SET_SIZE() functions. + Py_SET_TYPE(obj, &PyList_Type); + Py_SET_SIZE(obj, 0); + + Py_DECREF(obj); + Py_RETURN_NONE; +} + + static PyMethodDef test_methods[] = { {"set_get_size", set_get_size, METH_O}, - + {"test_set_type_size", test_set_type_size, METH_NOARGS}, {NULL}, }; int _PyTestCapi_Init_Set(PyObject *m) { - if (PyModule_AddFunctions(m, test_methods) < 0) { - return -1; - } - - return 0; + return PyModule_AddFunctions(m, test_methods); } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5b5b630e7e7581..09e74fd3cf20af 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -163,124 +163,6 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored)) #endif } -static PyObject* -test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyObject* list; - int i; - - /* SF bug 132008: PyList_Reverse segfaults */ -#define NLIST 30 - list = PyList_New(NLIST); - if (list == (PyObject*)NULL) - return (PyObject*)NULL; - /* list = range(NLIST) */ - for (i = 0; i < NLIST; ++i) { - PyObject* anint = PyLong_FromLong(i); - if (anint == (PyObject*)NULL) { - Py_DECREF(list); - return (PyObject*)NULL; - } - PyList_SET_ITEM(list, i, anint); - } - /* list.reverse(), via PyList_Reverse() */ - i = PyList_Reverse(list); /* should not blow up! */ - if (i != 0) { - Py_DECREF(list); - return (PyObject*)NULL; - } - /* Check that list == range(29, -1, -1) now */ - for (i = 0; i < NLIST; ++i) { - PyObject* anint = PyList_GET_ITEM(list, i); - if (PyLong_AS_LONG(anint) != NLIST-1-i) { - PyErr_SetString(get_testerror(self), - "test_list_api: reverse screwed up"); - Py_DECREF(list); - return (PyObject*)NULL; - } - } - Py_DECREF(list); -#undef NLIST - - Py_RETURN_NONE; -} - -static int -test_dict_inner(PyObject *self, int count) -{ - Py_ssize_t pos = 0, iterations = 0; - int i; - PyObject *dict = PyDict_New(); - PyObject *v, *k; - - if (dict == NULL) - return -1; - - for (i = 0; i < count; i++) { - v = PyLong_FromLong(i); - if (v == NULL) { - goto error; - } - if (PyDict_SetItem(dict, v, v) < 0) { - Py_DECREF(v); - goto error; - } - Py_DECREF(v); - } - - k = v = UNINITIALIZED_PTR; - while (PyDict_Next(dict, &pos, &k, &v)) { - PyObject *o; - iterations++; - - assert(k != UNINITIALIZED_PTR); - assert(v != UNINITIALIZED_PTR); - i = PyLong_AS_LONG(v) + 1; - o = PyLong_FromLong(i); - if (o == NULL) { - goto error; - } - if (PyDict_SetItem(dict, k, o) < 0) { - Py_DECREF(o); - goto error; - } - Py_DECREF(o); - k = v = UNINITIALIZED_PTR; - } - assert(k == UNINITIALIZED_PTR); - assert(v == UNINITIALIZED_PTR); - - Py_DECREF(dict); - - if (iterations != count) { - PyErr_SetString( - get_testerror(self), - "test_dict_iteration: dict iteration went wrong "); - return -1; - } else { - return 0; - } -error: - Py_DECREF(dict); - return -1; -} - - - -static PyObject* -test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) -{ - int i; - - for (i = 0; i < 200; i++) { - if (test_dict_inner(self, i) < 0) { - return NULL; - } - } - - Py_RETURN_NONE; -} - /* Issue #4701: Check that PyObject_Hash implicitly calls * PyType_Ready if it hasn't already been called */ @@ -755,61 +637,6 @@ pending_threadfunc(PyObject *self, PyObject *arg, PyObject *kwargs) return PyLong_FromUnsignedLong((unsigned long)num_added); } -/* Test PyOS_string_to_double. */ -static PyObject * -test_string_to_double(PyObject *self, PyObject *Py_UNUSED(ignored)) { - double result; - const char *msg; - -#define CHECK_STRING(STR, expected) \ - do { \ - result = PyOS_string_to_double(STR, NULL, NULL); \ - if (result == -1.0 && PyErr_Occurred()) { \ - return NULL; \ - } \ - if (result != (double)expected) { \ - msg = "conversion of " STR " to float failed"; \ - goto fail; \ - } \ - } while (0) - -#define CHECK_INVALID(STR) \ - do { \ - result = PyOS_string_to_double(STR, NULL, NULL); \ - if (result == -1.0 && PyErr_Occurred()) { \ - if (PyErr_ExceptionMatches(PyExc_ValueError)) { \ - PyErr_Clear(); \ - } \ - else { \ - return NULL; \ - } \ - } \ - else { \ - msg = "conversion of " STR " didn't raise ValueError"; \ - goto fail; \ - } \ - } while (0) - - CHECK_STRING("0.1", 0.1); - CHECK_STRING("1.234", 1.234); - CHECK_STRING("-1.35", -1.35); - CHECK_STRING(".1e01", 1.0); - CHECK_STRING("2.e-2", 0.02); - - CHECK_INVALID(" 0.1"); - CHECK_INVALID("\t\n-3"); - CHECK_INVALID(".123 "); - CHECK_INVALID("3\n"); - CHECK_INVALID("123abc"); - - Py_RETURN_NONE; - fail: - return raiseTestError(self, "test_string_to_double", msg); -#undef CHECK_STRING -#undef CHECK_INVALID -} - - /* Coverage testing of capsule objects. */ static const char *capsule_name = "capsule name"; @@ -1391,48 +1218,6 @@ static PyMethodDef ml = { NULL }; -static PyObject * -_test_incref(PyObject *ob) -{ - return Py_NewRef(ob); -} - -static PyObject * -test_xincref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_XINCREF(_test_incref(obj)); - Py_DECREF(obj); - Py_DECREF(obj); - Py_DECREF(obj); - Py_RETURN_NONE; -} - -static PyObject * -test_incref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_INCREF(_test_incref(obj)); - Py_DECREF(obj); - Py_DECREF(obj); - Py_DECREF(obj); - Py_RETURN_NONE; -} - -static PyObject * -test_xdecref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - Py_XDECREF(PyLong_FromLong(0)); - Py_RETURN_NONE; -} - -static PyObject * -test_decref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - Py_DECREF(PyLong_FromLong(0)); - Py_RETURN_NONE; -} - static PyObject * test_structseq_newtype_doesnt_leak(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) @@ -1479,16 +1264,6 @@ test_structseq_newtype_null_descr_doc(PyObject *Py_UNUSED(self), Py_RETURN_NONE; } -static PyObject * -test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_IncRef(obj); - Py_DecRef(obj); - Py_DecRef(obj); - Py_RETURN_NONE; -} - typedef struct { PyThread_type_lock start_event; PyThread_type_lock exit_event; @@ -1906,45 +1681,6 @@ bad_get(PyObject *module, PyObject *args) } -#ifdef Py_REF_DEBUG -static PyObject * -negative_refcount(PyObject *self, PyObject *Py_UNUSED(args)) -{ - PyObject *obj = PyUnicode_FromString("negative_refcount"); - if (obj == NULL) { - return NULL; - } - assert(Py_REFCNT(obj) == 1); - - Py_SET_REFCNT(obj, 0); - /* Py_DECREF() must call _Py_NegativeRefcount() and abort Python */ - Py_DECREF(obj); - - Py_RETURN_NONE; -} - -static PyObject * -decref_freed_object(PyObject *self, PyObject *Py_UNUSED(args)) -{ - PyObject *obj = PyUnicode_FromString("decref_freed_object"); - if (obj == NULL) { - return NULL; - } - assert(Py_REFCNT(obj) == 1); - - // Deallocate the memory - Py_DECREF(obj); - // obj is a now a dangling pointer - - // gh-109496: If Python is built in debug mode, Py_DECREF() must call - // _Py_NegativeRefcount() and abort Python. - Py_DECREF(obj); - - Py_RETURN_NONE; -} -#endif - - /* Functions for testing C calling conventions (METH_*) are named meth_*, * e.g. "meth_varargs" for METH_VARARGS. * @@ -2048,208 +1784,6 @@ pynumber_tobase(PyObject *module, PyObject *args) return PyNumber_ToBase(obj, base); } -static PyObject* -test_set_type_size(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - - // Ensure that following tests don't modify the object, - // to ensure that Py_DECREF() will not crash. - assert(Py_TYPE(obj) == &PyList_Type); - assert(Py_SIZE(obj) == 0); - - // bpo-39573: Test Py_SET_TYPE() and Py_SET_SIZE() functions. - Py_SET_TYPE(obj, &PyList_Type); - Py_SET_SIZE(obj, 0); - - Py_DECREF(obj); - Py_RETURN_NONE; -} - - -// Test Py_CLEAR() macro -static PyObject* -test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - // simple case with a variable - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - Py_CLEAR(obj); - assert(obj == NULL); - - // gh-98724: complex case, Py_CLEAR() argument has a side effect - PyObject* array[1]; - array[0] = PyList_New(0); - if (array[0] == NULL) { - return NULL; - } - - PyObject **p = array; - Py_CLEAR(*p++); - assert(array[0] == NULL); - assert(p == array + 1); - - Py_RETURN_NONE; -} - - -// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear() -static PyObject* -test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - // Py_SETREF() simple case with a variable - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - Py_SETREF(obj, NULL); - assert(obj == NULL); - - // Py_XSETREF() simple case with a variable - PyObject *obj2 = PyList_New(0); - if (obj2 == NULL) { - return NULL; - } - Py_XSETREF(obj2, NULL); - assert(obj2 == NULL); - // test Py_XSETREF() when the argument is NULL - Py_XSETREF(obj2, NULL); - assert(obj2 == NULL); - - // gh-98724: complex case, Py_SETREF() argument has a side effect - PyObject* array[1]; - array[0] = PyList_New(0); - if (array[0] == NULL) { - return NULL; - } - - PyObject **p = array; - Py_SETREF(*p++, NULL); - assert(array[0] == NULL); - assert(p == array + 1); - - // gh-98724: complex case, Py_XSETREF() argument has a side effect - PyObject* array2[1]; - array2[0] = PyList_New(0); - if (array2[0] == NULL) { - return NULL; - } - - PyObject **p2 = array2; - Py_XSETREF(*p2++, NULL); - assert(array2[0] == NULL); - assert(p2 == array2 + 1); - - // test Py_XSETREF() when the argument is NULL - p2 = array2; - Py_XSETREF(*p2++, NULL); - assert(array2[0] == NULL); - assert(p2 == array2 + 1); - - Py_RETURN_NONE; -} - - -#define TEST_REFCOUNT() \ - do { \ - PyObject *obj = PyList_New(0); \ - if (obj == NULL) { \ - return NULL; \ - } \ - assert(Py_REFCNT(obj) == 1); \ - \ - /* test Py_NewRef() */ \ - PyObject *ref = Py_NewRef(obj); \ - assert(ref == obj); \ - assert(Py_REFCNT(obj) == 2); \ - Py_DECREF(ref); \ - \ - /* test Py_XNewRef() */ \ - PyObject *xref = Py_XNewRef(obj); \ - assert(xref == obj); \ - assert(Py_REFCNT(obj) == 2); \ - Py_DECREF(xref); \ - \ - assert(Py_XNewRef(NULL) == NULL); \ - \ - Py_DECREF(obj); \ - Py_RETURN_NONE; \ - } while (0) - - -// Test Py_NewRef() and Py_XNewRef() macros -static PyObject* -test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_REFCOUNT(); -} - -#undef Py_NewRef -#undef Py_XNewRef - -// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros. -static PyObject* -test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_REFCOUNT(); -} - - -// Test Py_Is() function -#define TEST_PY_IS() \ - do { \ - PyObject *o_none = Py_None; \ - PyObject *o_true = Py_True; \ - PyObject *o_false = Py_False; \ - PyObject *obj = PyList_New(0); \ - if (obj == NULL) { \ - return NULL; \ - } \ - \ - /* test Py_Is() */ \ - assert(Py_Is(obj, obj)); \ - assert(!Py_Is(obj, o_none)); \ - \ - /* test Py_None */ \ - assert(Py_Is(o_none, o_none)); \ - assert(!Py_Is(obj, o_none)); \ - \ - /* test Py_True */ \ - assert(Py_Is(o_true, o_true)); \ - assert(!Py_Is(o_false, o_true)); \ - assert(!Py_Is(obj, o_true)); \ - \ - /* test Py_False */ \ - assert(Py_Is(o_false, o_false)); \ - assert(!Py_Is(o_true, o_false)); \ - assert(!Py_Is(obj, o_false)); \ - \ - Py_DECREF(obj); \ - Py_RETURN_NONE; \ - } while (0) - -// Test Py_Is() macro -static PyObject* -test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_PY_IS(); -} - -#undef Py_Is - -// Test Py_Is() function, after undefining its macro. -static PyObject* -test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_PY_IS(); -} - - /* We only use 2 in test_capi/test_misc.py. */ #define NUM_BASIC_STATIC_TYPES 2 static PyTypeObject BasicStaticTypes[NUM_BASIC_STATIC_TYPES] = { @@ -2608,14 +2142,6 @@ settrace_to_error(PyObject *self, PyObject *list) Py_RETURN_NONE; } -static PyObject * -clear_managed_dict(PyObject *self, PyObject *obj) -{ - PyObject_ClearManagedDict(obj); - Py_RETURN_NONE; -} - - static PyObject * test_macros(PyObject *self, PyObject *Py_UNUSED(args)) { @@ -2969,124 +2495,18 @@ code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf) } -static void -tracemalloc_track_race_thread(void *data) -{ - PyTraceMalloc_Track(123, 10, 1); - PyTraceMalloc_Untrack(123, 10); - - PyThread_type_lock lock = (PyThread_type_lock)data; - PyThread_release_lock(lock); -} - -// gh-128679: Test fix for tracemalloc.stop() race condition -static PyObject * -tracemalloc_track_race(PyObject *self, PyObject *args) -{ -#define NTHREAD 50 - PyObject *tracemalloc = NULL; - PyObject *stop = NULL; - PyThread_type_lock locks[NTHREAD]; - memset(locks, 0, sizeof(locks)); - - // Call tracemalloc.start() - tracemalloc = PyImport_ImportModule("tracemalloc"); - if (tracemalloc == NULL) { - goto error; - } - PyObject *start = PyObject_GetAttrString(tracemalloc, "start"); - if (start == NULL) { - goto error; - } - PyObject *res = PyObject_CallNoArgs(start); - Py_DECREF(start); - if (res == NULL) { - goto error; - } - Py_DECREF(res); - - stop = PyObject_GetAttrString(tracemalloc, "stop"); - Py_CLEAR(tracemalloc); - if (stop == NULL) { - goto error; - } - - // Start threads - for (size_t i = 0; i < NTHREAD; i++) { - PyThread_type_lock lock = PyThread_allocate_lock(); - if (!lock) { - PyErr_NoMemory(); - goto error; - } - locks[i] = lock; - PyThread_acquire_lock(lock, 1); - - unsigned long thread; - thread = PyThread_start_new_thread(tracemalloc_track_race_thread, - (void*)lock); - if (thread == (unsigned long)-1) { - PyErr_SetString(PyExc_RuntimeError, "can't start new thread"); - goto error; - } - } - - // Call tracemalloc.stop() while threads are running - res = PyObject_CallNoArgs(stop); - Py_CLEAR(stop); - if (res == NULL) { - goto error; - } - Py_DECREF(res); - - // Wait until threads complete with the GIL released - Py_BEGIN_ALLOW_THREADS - for (size_t i = 0; i < NTHREAD; i++) { - PyThread_type_lock lock = locks[i]; - PyThread_acquire_lock(lock, 1); - PyThread_release_lock(lock); - } - Py_END_ALLOW_THREADS - - // Free threads locks - for (size_t i=0; i < NTHREAD; i++) { - PyThread_type_lock lock = locks[i]; - PyThread_free_lock(lock); - } - Py_RETURN_NONE; - -error: - Py_CLEAR(tracemalloc); - Py_CLEAR(stop); - for (size_t i=0; i < NTHREAD; i++) { - PyThread_type_lock lock = locks[i]; - if (lock) { - PyThread_free_lock(lock); - } - } - return NULL; -#undef NTHREAD -} - static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, {"test_sizeof_c_types", test_sizeof_c_types, METH_NOARGS}, - {"test_list_api", test_list_api, METH_NOARGS}, - {"test_dict_iteration", test_dict_iteration, METH_NOARGS}, {"test_lazy_hash_inheritance", test_lazy_hash_inheritance,METH_NOARGS}, - {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, - {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, - {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, - {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, {"test_structseq_newtype_doesnt_leak", test_structseq_newtype_doesnt_leak, METH_NOARGS}, {"test_structseq_newtype_null_descr_doc", test_structseq_newtype_null_descr_doc, METH_NOARGS}, - {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, {"pyobject_repr_from_null", pyobject_repr_from_null, METH_NOARGS}, {"pyobject_str_from_null", pyobject_str_from_null, METH_NOARGS}, {"pyobject_bytes_from_null", pyobject_bytes_from_null, METH_NOARGS}, - {"test_string_to_double", test_string_to_double, METH_NOARGS}, {"test_capsule", (PyCFunction)test_capsule, METH_NOARGS}, {"test_from_contiguous", (PyCFunction)test_from_contiguous, METH_NOARGS}, #if (defined(__linux__) || defined(__FreeBSD__)) && defined(__GNUC__) @@ -3145,10 +2565,6 @@ static PyMethodDef TestMethods[] = { #endif {"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS}, {"bad_get", bad_get, METH_VARARGS}, -#ifdef Py_REF_DEBUG - {"negative_refcount", negative_refcount, METH_NOARGS}, - {"decref_freed_object", decref_freed_object, METH_NOARGS}, -#endif {"meth_varargs", meth_varargs, METH_VARARGS}, {"meth_varargs_keywords", _PyCFunction_CAST(meth_varargs_keywords), METH_VARARGS|METH_KEYWORDS}, {"meth_o", meth_o, METH_O}, @@ -3157,13 +2573,6 @@ static PyMethodDef TestMethods[] = { {"meth_fastcall_keywords", _PyCFunction_CAST(meth_fastcall_keywords), METH_FASTCALL|METH_KEYWORDS}, {"pycfunction_call", test_pycfunction_call, METH_VARARGS}, {"pynumber_tobase", pynumber_tobase, METH_VARARGS}, - {"test_set_type_size", test_set_type_size, METH_NOARGS}, - {"test_py_clear", test_py_clear, METH_NOARGS}, - {"test_py_setref", test_py_setref, METH_NOARGS}, - {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, - {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, - {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, - {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL}, {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"gen_get_code", gen_get_code, METH_O, NULL}, @@ -3172,14 +2581,12 @@ static PyMethodDef TestMethods[] = { {"settrace_to_error", settrace_to_error, METH_O, NULL}, {"settrace_to_record", settrace_to_record, METH_O, NULL}, {"test_macros", test_macros, METH_NOARGS, NULL}, - {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, {"test_weakref_capi", test_weakref_capi, METH_NOARGS}, {"function_set_warning", function_set_warning, METH_NOARGS}, {"test_critical_sections", test_critical_sections, METH_NOARGS}, {"finalize_thread_hang", finalize_thread_hang, METH_O, NULL}, {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, - {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; From cf4c4ecc26c7e3b89f2e56893260a8a3319dab3d Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:49:45 +0000 Subject: [PATCH 039/311] gh-126022: Replace TeX quotation marks with ``"`` in Doc/license.rst (#129535) --- Doc/license.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/license.rst b/Doc/license.rst index 61a26ab2867498..90783e3e31a69d 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -374,7 +374,7 @@ Project, https://www.wide.ad.jp/. :: may be used to endorse or promote products derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE @@ -583,7 +583,7 @@ interface:: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE @@ -884,7 +884,7 @@ sources unless the build is configured ``--with-system-libffi``:: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the - ``Software''), to deal in the Software without restriction, including + "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to @@ -893,7 +893,7 @@ sources unless the build is configured ``--with-system-libffi``:: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -1122,7 +1122,7 @@ The file is distributed under the 2-Clause BSD License:: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, From e1006ce1ded1b18972888ef057718dba6f2c7edd Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 1 Feb 2025 20:46:56 -0800 Subject: [PATCH 040/311] gh-119461: Restore the testSocket VSOCK skipUnless removed by PR #119465 (#129561) Restore the skipUnless removed by #119465. This test can only pass on virtual machines, not actual machines. actual machines see: ``` self.cli.connect((cid, VSOCKPORT)) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ OSError: [Errno 19] No such device ``` Reproduced on (Linux) Ubuntu 24.04.1 running 6.8.0-52-generic. --- Lib/test/test_socket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index faf326d9164e1b..b77fa3cb21512a 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -520,6 +520,8 @@ def clientTearDown(self): @unittest.skipIf(WSL, 'VSOCK does not work on Microsoft WSL') @unittest.skipUnless(HAVE_SOCKET_VSOCK, 'VSOCK sockets required for this test.') +@unittest.skipUnless(get_cid() != 2, # VMADDR_CID_HOST + "This test can only be run on a virtual guest.") class ThreadedVSOCKSocketStreamTest(unittest.TestCase, ThreadableTest): def __init__(self, methodName='runTest'): From df4a2f5bd74fc582d99e6a82e070058d7765f44d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 2 Feb 2025 10:16:48 +0100 Subject: [PATCH 041/311] gh-129539: Reorganize posixmodule.c header (#129558) Add sections: Python includes, system includes, etc. Add a comment explaining why an include is needed. --- Modules/posixmodule.c | 712 ++++++++++++++++++++++-------------------- 1 file changed, 368 insertions(+), 344 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b3b5572e1cfa30..6dfe73017abf9d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7,6 +7,8 @@ of the compiler used. Different compilers define their own feature test macro, e.g. '_MSC_VER'. */ +// --- Python includes ------------------------------------------------------ + #include "Python.h" #ifdef __VXWORKS__ @@ -26,255 +28,63 @@ #include "pycore_time.h" // _PyLong_FromTime_t() #include "pycore_typeobject.h" // _PyType_AddMethod() -#ifdef HAVE_UNISTD_H -# include // symlink() -#endif - -#ifdef MS_WINDOWS -# include -# if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP) -# include -# endif -# include -# include // UNLEN -# include "osdefs.h" // SEP -# include // SetEntriesInAcl -# include // SDDL_REVISION_1 -# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) -# define HAVE_SYMLINK -# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ -#endif - #ifndef MS_WINDOWS -# include "posixmodule.h" +# include "posixmodule.h" // _PyLong_FromUid() #else -# include "pycore_fileutils_windows.h" -# include "winreparse.h" +# include "pycore_fileutils_windows.h" // _Py_GetFileInformationByName() +# include "osdefs.h" // SEP +# include "winreparse.h" // _Py_REPARSE_DATA_BUFFER #endif -#if !defined(EX_OK) && defined(EXIT_SUCCESS) -# define EX_OK EXIT_SUCCESS + +// --- System includes ------------------------------------------------------ + +#include // ctermid() +#include // system() + +#ifdef HAVE_UNISTD_H +# include // symlink() #endif #ifdef __APPLE__ - /* Needed for the implementation of os.statvfs */ + /* Needed for the implementation of os.statvfs */ # include # include #endif -/* On android API level 21, 'AT_EACCESS' is not declared although - * HAVE_FACCESSAT is defined. */ -#ifdef __ANDROID__ -# undef HAVE_FACCESSAT -#endif - -#include // ctermid() -#include // system() #ifdef HAVE_SYS_TIME_H # include // futimes() #endif + #ifdef HAVE_SYS_PIDFD_H # include // PIDFD_NONBLOCK #endif - -// SGI apparently needs this forward declaration -#ifdef HAVE__GETPTY -# include // mode_t - extern char * _getpty(int *, int, mode_t, int); -#endif - #ifdef __EMSCRIPTEN__ -#include "emscripten.h" // emscripten_debugger() -#endif - -/* - * A number of APIs are available on macOS from a certain macOS version. - * To support building with a new SDK while deploying to older versions - * the availability test is split into two: - * - HAVE_: The configure check for compile time availability - * - HAVE__RUNTIME: Runtime check for availability - * - * The latter is always true when not on macOS, or when using a compiler - * that does not support __has_builtin (older versions of Xcode). - * - * Due to compiler restrictions there is one valid use of HAVE__RUNTIME: - * if (HAVE__RUNTIME) { ... } - * - * In mixing the test with other tests or using negations will result in compile - * errors. - */ -#if defined(__APPLE__) - -#include - -#if defined(__has_builtin) -#if __has_builtin(__builtin_available) -#define HAVE_BUILTIN_AVAILABLE 1 -#endif -#endif - -#ifdef HAVE_BUILTIN_AVAILABLE -# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) -# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) -# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) -# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -# define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) - -# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) - -#else /* Xcode 8 or earlier */ - - /* __builtin_available is not present in these compilers, but - * some of the symbols might be weak linked (10.10 SDK or later - * deploying on 10.9. - * - * Fall back to the older style of availability checking for - * symbols introduced in macOS 10.10. - */ - -# ifdef HAVE_FSTATAT -# define HAVE_FSTATAT_RUNTIME (fstatat != NULL) -# endif - -# ifdef HAVE_FACCESSAT -# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL) -# endif - -# ifdef HAVE_FCHMODAT -# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL) -# endif - -# ifdef HAVE_FCHOWNAT -# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL) -# endif - -# ifdef HAVE_LINKAT -# define HAVE_LINKAT_RUNTIME (linkat != NULL) -# endif - -# ifdef HAVE_FDOPENDIR -# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL) -# endif - -# ifdef HAVE_MKDIRAT -# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL) -# endif - -# ifdef HAVE_RENAMEAT -# define HAVE_RENAMEAT_RUNTIME (renameat != NULL) -# endif - -# ifdef HAVE_UNLINKAT -# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL) -# endif - -# ifdef HAVE_OPENAT -# define HAVE_OPENAT_RUNTIME (openat != NULL) -# endif - -# ifdef HAVE_READLINKAT -# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL) -# endif - -# ifdef HAVE_SYMLINKAT -# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL) -# endif - -# ifdef HAVE_UTIMENSAT -# define HAVE_UTIMENSAT_RUNTIME (utimensat != NULL) -# endif - -# ifdef HAVE_FUTIMENS -# define HAVE_FUTIMENS_RUNTIME (futimens != NULL) -# endif - -# ifdef HAVE_PWRITEV -# define HAVE_PWRITEV_RUNTIME (pwritev != NULL) -# endif - -# ifdef HAVE_MKFIFOAT -# define HAVE_MKFIFOAT_RUNTIME (mkfifoat != NULL) -# endif - -# ifdef HAVE_MKNODAT -# define HAVE_MKNODAT_RUNTIME (mknodat != NULL) -# endif - -# ifdef HAVE_PTSNAME_R -# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL) -# endif - -#endif - -#ifdef HAVE_FUTIMESAT -/* Some of the logic for weak linking depends on this assertion */ -# error "HAVE_FUTIMESAT unexpectedly defined" -#endif - -#else -# define HAVE_FSTATAT_RUNTIME 1 -# define HAVE_FACCESSAT_RUNTIME 1 -# define HAVE_FCHMODAT_RUNTIME 1 -# define HAVE_FCHOWNAT_RUNTIME 1 -# define HAVE_LINKAT_RUNTIME 1 -# define HAVE_FDOPENDIR_RUNTIME 1 -# define HAVE_MKDIRAT_RUNTIME 1 -# define HAVE_RENAMEAT_RUNTIME 1 -# define HAVE_UNLINKAT_RUNTIME 1 -# define HAVE_OPENAT_RUNTIME 1 -# define HAVE_READLINKAT_RUNTIME 1 -# define HAVE_SYMLINKAT_RUNTIME 1 -# define HAVE_FUTIMENS_RUNTIME 1 -# define HAVE_UTIMENSAT_RUNTIME 1 -# define HAVE_PWRITEV_RUNTIME 1 -# define HAVE_MKFIFOAT_RUNTIME 1 -# define HAVE_MKNODAT_RUNTIME 1 -# define HAVE_PTSNAME_R_RUNTIME 1 +# include "emscripten.h" // emscripten_debugger() #endif - -PyDoc_STRVAR(posix__doc__, -"This module provides access to operating system functionality that is\n\ -standardized by the C Standard and the POSIX standard (a thinly\n\ -disguised Unix interface). Refer to the library manual and\n\ -corresponding Unix manual entries for more information on calls."); - - #ifdef HAVE_SYS_UIO_H # include #endif #ifdef HAVE_SYS_TYPES_H -/* Should be included before on HP-UX v3 */ + /* Should be included before on HP-UX v3 */ # include -#endif /* HAVE_SYS_TYPES_H */ - +#endif #ifdef HAVE_SYS_SYSMACROS_H -/* GNU C Library: major(), minor(), makedev() */ + /* GNU C Library: major(), minor(), makedev() */ # include #endif #ifdef HAVE_SYS_STAT_H # include -#endif /* HAVE_SYS_STAT_H */ +#endif #ifdef HAVE_SYS_WAIT_H # include // WNOHANG #endif + #ifdef HAVE_LINUX_WAIT_H # include // P_PIDFD #endif @@ -284,54 +94,34 @@ corresponding Unix manual entries for more information on calls."); #endif #ifdef HAVE_FCNTL_H -# include +# include // fcntl() #endif #ifdef HAVE_GRP_H -# include +# include // setgroups() #endif #ifdef HAVE_SYSEXITS_H -# include +# include // EX_OK #endif #ifdef HAVE_SYS_LOADAVG_H -# include +# include // getloadavg() #endif #ifdef HAVE_SYS_SENDFILE_H -# include +# include // sendfile() #endif #if defined(__APPLE__) -# include +# include // fcopyfile() #endif #ifdef HAVE_SCHED_H -# include +# include // sched_setscheduler() #endif - #ifdef HAVE_LINUX_SCHED_H -# include -#endif - -#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY) -# undef HAVE_SCHED_SETAFFINITY -#endif - -#if defined(HAVE_SYS_XATTR_H) -# if defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) -# define USE_XATTRS -# include // Needed for XATTR_SIZE_MAX on musl libc. -# endif -# if defined(__CYGWIN__) -# define USE_XATTRS -# include // Needed for XATTR_SIZE_MAX and XATTR_LIST_MAX. -# endif -#endif - -#ifdef USE_XATTRS -# include +# include // SCHED_IDLE, SCHED_RR #endif #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) @@ -357,36 +147,141 @@ corresponding Unix manual entries for more information on calls."); #endif #ifdef HAVE_LINUX_RANDOM_H -# include +# include // GRND_RANDOM #endif #ifdef HAVE_GETRANDOM_SYSCALL -# include +# include // syscall() #endif -#ifdef HAVE_WINDOWS_CONSOLE_IO -# define TERMSIZE_USE_CONIO -#elif defined(HAVE_SYS_IOCTL_H) -# include -# if defined(HAVE_TERMIOS_H) -# include -# endif -# if defined(TIOCGWINSZ) -# define TERMSIZE_USE_IOCTL -# endif -#endif /* HAVE_WINDOWS_CONSOLE_IO */ +#ifdef HAVE_POSIX_SPAWN +# include // posix_spawn() +#endif -/* Various compilers have only certain posix functions */ -/* XXX Gosh I wish these were all moved into pyconfig.h */ -#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ -# define HAVE_OPENDIR 1 -# define HAVE_SYSTEM 1 -# include -#elif defined( _MSC_VER) - /* Microsoft compiler */ -# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) -# define HAVE_GETPPID 1 -# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_APP | MS_WINDOWS_SYSTEM */ -# if defined(MS_WINDOWS_DESKTOP) +#ifdef HAVE_UTIME_H +# include // utime() +#endif + +#ifdef HAVE_SYS_UTIME_H +# include +# define HAVE_UTIME_H /* pretend we do for the rest of this file */ +#endif + +#ifdef HAVE_SYS_TIMES_H +# include // times() +#endif + +#ifdef HAVE_SYS_PARAM_H +# include +#endif + +#ifdef HAVE_SYS_UTSNAME_H +# include // uname() +#endif + +/* memfd_create is either defined in sys/mman.h or sys/memfd.h + * linux/memfd.h defines additional flags + */ +#ifdef HAVE_SYS_MMAN_H +# include // memfd_create() +#endif +#ifdef HAVE_SYS_MEMFD_H +# include // memfd_create() +#endif +#ifdef HAVE_LINUX_MEMFD_H +# include // memfd_create(), MFD_CLOEXEC +#endif + +#ifdef HAVE_SYS_EVENTFD_H +# include // eventfd() +#endif + +#ifdef HAVE_SYS_TIMERFD_H +# include // timerfd_create() +#endif + +#ifdef _Py_MEMORY_SANITIZER +# include // __msan_unpoison() +#endif + + +// --- More complex system includes ----------------------------------------- + +#ifdef MS_WINDOWS +# include +# if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP) +# include // PathCchSkipRoot() +# endif +# include // SetEntriesInAcl +# include // UNLEN +# include // SDDL_REVISION_1 +# include // FSCTL_GET_REPARSE_POINT +# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) +# define HAVE_SYMLINK +# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ +#endif + + +#ifdef _MSC_VER +# ifdef HAVE_DIRECT_H +# include +# endif +# ifdef HAVE_IO_H +# include +# endif +# ifdef HAVE_PROCESS_H +# include // getpid(), _cwait() +# endif +# include +#endif /* _MSC_VER */ + + +#ifdef HAVE__GETPTY +# include // mode_t + // SGI apparently needs this forward declaration + extern char * _getpty(int *, int, mode_t, int); +#endif + + +#if defined(HAVE_SYS_XATTR_H) +# if defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +# define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX on musl libc. +# endif +# if defined(__CYGWIN__) +# define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX and XATTR_LIST_MAX. +# endif +#endif +#ifdef USE_XATTRS +# include // fgetxattr() +#endif + + +#ifdef HAVE_WINDOWS_CONSOLE_IO +# define TERMSIZE_USE_CONIO +#elif defined(HAVE_SYS_IOCTL_H) +# include // ioctl(), TIOCGWINSZ +# if defined(HAVE_TERMIOS_H) +# include +# endif +# if defined(TIOCGWINSZ) +# define TERMSIZE_USE_IOCTL +# endif +#endif + + +/* Various compilers have only certain posix functions */ +/* XXX Gosh I wish these were all moved into pyconfig.h */ +#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ +# define HAVE_OPENDIR 1 +# define HAVE_SYSTEM 1 +# include +#elif defined( _MSC_VER) + /* Microsoft compiler */ +# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) +# define HAVE_GETPPID 1 +# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_APP | MS_WINDOWS_SYSTEM */ +# if defined(MS_WINDOWS_DESKTOP) # define HAVE_GETLOGIN 1 # endif /* MS_WINDOWS_DESKTOP */ # if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) @@ -400,23 +295,15 @@ corresponding Unix manual entries for more information on calls."); # define HAVE_PIPE 1 # define HAVE_FSYNC 1 # define fsync _commit -#endif /* ! __WATCOMC__ || __QNX__ */ - -/*[clinic input] -# one of the few times we lie about this name! -module os -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=94a0f0f978acae17]*/ +#endif -#ifndef _MSC_VER -#if defined(__sgi)&&_COMPILER_VERSION>=700 +#if !defined(_MSC_VER) && defined(__sgi) && _COMPILER_VERSION>=700 /* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode (default) */ -extern char *ctermid_r(char *); +extern char *ctermid_r(char *); #endif -#endif /* !_MSC_VER */ #if defined(__VXWORKS__) # include @@ -430,33 +317,9 @@ extern char *ctermid_r(char *); # endif #endif /* __VXWORKS__ */ -#ifdef HAVE_POSIX_SPAWN -# include -#endif - -#ifdef HAVE_UTIME_H -# include -#endif /* HAVE_UTIME_H */ - -#ifdef HAVE_SYS_UTIME_H -# include -# define HAVE_UTIME_H /* pretend we do for the rest of this file */ -#endif /* HAVE_SYS_UTIME_H */ - -#ifdef HAVE_SYS_TIMES_H -# include -#endif /* HAVE_SYS_TIMES_H */ - -#ifdef HAVE_SYS_PARAM_H -# include -#endif /* HAVE_SYS_PARAM_H */ - -#ifdef HAVE_SYS_UTSNAME_H -# include -#endif /* HAVE_SYS_UTSNAME_H */ #ifdef HAVE_DIRENT_H -# include +# include // opendir() # define NAMLEN(dirent) strlen((dirent)->d_name) #else # if defined(__WATCOMC__) && !defined(__QNX__) @@ -477,18 +340,20 @@ extern char *ctermid_r(char *); # endif #endif -#ifdef _MSC_VER -# ifdef HAVE_DIRECT_H -# include -# endif -# ifdef HAVE_IO_H -# include + +#if defined(MAJOR_IN_MKDEV) +# include +#else +# if defined(MAJOR_IN_SYSMACROS) +# include # endif -# ifdef HAVE_PROCESS_H -# include +# if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) +# include # endif -# include -#endif /* _MSC_VER */ +#endif + + +// --- Macros --------------------------------------------------------------- #ifndef MAXPATHLEN # if defined(PATH_MAX) && PATH_MAX > 1024 @@ -498,6 +363,7 @@ extern char *ctermid_r(char *); # endif #endif /* MAXPATHLEN */ + #ifdef UNION_WAIT /* Emulate some macros on systems that have a union instead of macros */ # ifndef WIFEXITED @@ -517,12 +383,14 @@ extern char *ctermid_r(char *); # define WAIT_STATUS_INT(s) (s) #endif /* UNION_WAIT */ + /* Don't use the "_r" form if we don't need it (also, won't have a prototype for it, at least on Solaris -- maybe others as well?). */ #if defined(HAVE_CTERMID_R) # define USE_CTERMID_R #endif + /* choose the appropriate stat and fstat functions and return structs */ #undef STAT #undef FSTAT @@ -539,25 +407,19 @@ extern char *ctermid_r(char *); # define STRUCT_STAT struct stat #endif -#if defined(MAJOR_IN_MKDEV) -# include -#else -# if defined(MAJOR_IN_SYSMACROS) -# include -# endif -# if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) -# include -# endif + +#if !defined(EX_OK) && defined(EXIT_SUCCESS) +# define EX_OK EXIT_SUCCESS #endif -#ifdef MS_WINDOWS -# define INITFUNC PyInit_nt -# define MODNAME "nt" -# define MODNAME_OBJ &_Py_ID(nt) -#else -# define INITFUNC PyInit_posix -# define MODNAME "posix" -# define MODNAME_OBJ &_Py_ID(posix) +#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY) +# undef HAVE_SCHED_SETAFFINITY +#endif + +/* On android API level 21, 'AT_EACCESS' is not declared although + * HAVE_FACCESSAT is defined. */ +#ifdef __ANDROID__ +# undef HAVE_FACCESSAT #endif #if defined(__sun) @@ -565,33 +427,195 @@ extern char *ctermid_r(char *); # define HAVE_STRUCT_STAT_ST_FSTYPE 1 #endif -/* memfd_create is either defined in sys/mman.h or sys/memfd.h - * linux/memfd.h defines additional flags + +// --- Apple __builtin_available() macros ----------------------------------- + +/* + * A number of APIs are available on macOS from a certain macOS version. + * To support building with a new SDK while deploying to older versions + * the availability test is split into two: + * - HAVE_: The configure check for compile time availability + * - HAVE__RUNTIME: Runtime check for availability + * + * The latter is always true when not on macOS, or when using a compiler + * that does not support __has_builtin (older versions of Xcode). + * + * Due to compiler restrictions there is one valid use of HAVE__RUNTIME: + * if (HAVE__RUNTIME) { ... } + * + * In mixing the test with other tests or using negations will result in compile + * errors. */ -#ifdef HAVE_SYS_MMAN_H -# include +#if defined(__APPLE__) + +#include + +#if defined(__has_builtin) +#if __has_builtin(__builtin_available) +#define HAVE_BUILTIN_AVAILABLE 1 #endif -#ifdef HAVE_SYS_MEMFD_H -# include #endif -#ifdef HAVE_LINUX_MEMFD_H -# include + +#ifdef HAVE_BUILTIN_AVAILABLE +# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) +# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) +# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +# define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) + +# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) + +#else /* Xcode 8 or earlier */ + + /* __builtin_available is not present in these compilers, but + * some of the symbols might be weak linked (10.10 SDK or later + * deploying on 10.9. + * + * Fall back to the older style of availability checking for + * symbols introduced in macOS 10.10. + */ + +# ifdef HAVE_FSTATAT +# define HAVE_FSTATAT_RUNTIME (fstatat != NULL) +# endif + +# ifdef HAVE_FACCESSAT +# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL) +# endif + +# ifdef HAVE_FCHMODAT +# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL) +# endif + +# ifdef HAVE_FCHOWNAT +# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL) +# endif + +# ifdef HAVE_LINKAT +# define HAVE_LINKAT_RUNTIME (linkat != NULL) +# endif + +# ifdef HAVE_FDOPENDIR +# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL) +# endif + +# ifdef HAVE_MKDIRAT +# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL) +# endif + +# ifdef HAVE_RENAMEAT +# define HAVE_RENAMEAT_RUNTIME (renameat != NULL) +# endif + +# ifdef HAVE_UNLINKAT +# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL) +# endif + +# ifdef HAVE_OPENAT +# define HAVE_OPENAT_RUNTIME (openat != NULL) +# endif + +# ifdef HAVE_READLINKAT +# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL) +# endif + +# ifdef HAVE_SYMLINKAT +# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL) +# endif + +# ifdef HAVE_UTIMENSAT +# define HAVE_UTIMENSAT_RUNTIME (utimensat != NULL) +# endif + +# ifdef HAVE_FUTIMENS +# define HAVE_FUTIMENS_RUNTIME (futimens != NULL) +# endif + +# ifdef HAVE_PWRITEV +# define HAVE_PWRITEV_RUNTIME (pwritev != NULL) +# endif + +# ifdef HAVE_MKFIFOAT +# define HAVE_MKFIFOAT_RUNTIME (mkfifoat != NULL) +# endif + +# ifdef HAVE_MKNODAT +# define HAVE_MKNODAT_RUNTIME (mknodat != NULL) +# endif + +# ifdef HAVE_PTSNAME_R +# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL) +# endif + #endif -/* eventfd() */ -#ifdef HAVE_SYS_EVENTFD_H -# include +#ifdef HAVE_FUTIMESAT +/* Some of the logic for weak linking depends on this assertion */ +# error "HAVE_FUTIMESAT unexpectedly defined" #endif -/* timerfd_create() */ -#ifdef HAVE_SYS_TIMERFD_H -# include +#else +# define HAVE_FSTATAT_RUNTIME 1 +# define HAVE_FACCESSAT_RUNTIME 1 +# define HAVE_FCHMODAT_RUNTIME 1 +# define HAVE_FCHOWNAT_RUNTIME 1 +# define HAVE_LINKAT_RUNTIME 1 +# define HAVE_FDOPENDIR_RUNTIME 1 +# define HAVE_MKDIRAT_RUNTIME 1 +# define HAVE_RENAMEAT_RUNTIME 1 +# define HAVE_UNLINKAT_RUNTIME 1 +# define HAVE_OPENAT_RUNTIME 1 +# define HAVE_READLINKAT_RUNTIME 1 +# define HAVE_SYMLINKAT_RUNTIME 1 +# define HAVE_FUTIMENS_RUNTIME 1 +# define HAVE_UTIMENSAT_RUNTIME 1 +# define HAVE_PWRITEV_RUNTIME 1 +# define HAVE_MKFIFOAT_RUNTIME 1 +# define HAVE_MKNODAT_RUNTIME 1 +# define HAVE_PTSNAME_R_RUNTIME 1 #endif -#ifdef _Py_MEMORY_SANITIZER -# include + +// --- os module ------------------------------------------------------------ + +#ifdef MS_WINDOWS +# define INITFUNC PyInit_nt +# define MODNAME "nt" +# define MODNAME_OBJ &_Py_ID(nt) +#else +# define INITFUNC PyInit_posix +# define MODNAME "posix" +# define MODNAME_OBJ &_Py_ID(posix) #endif +/*[clinic input] +# one of the few times we lie about this name! +module os +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=94a0f0f978acae17]*/ + +PyDoc_STRVAR(posix__doc__, +"This module provides access to operating system functionality that is\n\ +standardized by the C Standard and the POSIX standard (a thinly\n\ +disguised Unix interface). Refer to the library manual and\n\ +corresponding Unix manual entries for more information on calls."); + + +// --- Functions ------------------------------------------------------------ + #ifdef HAVE_FORK static void run_at_forkers(PyObject *lst, int reverse) From 237f186da48d45ef75ec3add3f2ffbfac75fa281 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:30:34 +0000 Subject: [PATCH 042/311] gh-104400: Remove ``fintl.gettext`` from pygettext (#129580) The ``fintl`` module is never installed or tested, meaning that the fallback identity function is unconditionally used for ``_()``. This means we can simplify, converting the docstring to a real docstring, and converting some other strings to f-strings. We also convert the module to UTF-8, sort imports, and remove the history comment, which was last updated in 2002. Consult the git history for a more accurate summary of changes. --- Tools/i18n/pygettext.py | 65 +++++++++++++---------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index f78ff16bff9039..81d9fdbb36017b 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -1,26 +1,6 @@ #! /usr/bin/env python3 -# -*- coding: iso-8859-1 -*- -# Originally written by Barry Warsaw -# -# Minimally patched to make it even more xgettext compatible -# by Peter Funk -# -# 2002-11-22 Jrgen Hermann -# Added checks that _() only contains string literals, and -# command line args are resolved to module lists, i.e. you -# can now pass a filename, a module or package name, or a -# directory (including globbing chars, important for Win32). -# Made docstring fit in 80 chars wide displays using pydoc. -# - -# for selftesting -try: - import fintl - _ = fintl.gettext -except ImportError: - _ = lambda s: s -__doc__ = _("""pygettext -- Python equivalent of xgettext(1) +"""pygettext -- Python equivalent of xgettext(1) Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the internationalization of C programs. Most of these tools are independent of @@ -153,16 +133,16 @@ conjunction with the -D option above. If `inputfile' is -, standard input is read. -""") +""" -import os +import ast +import getopt +import glob import importlib.machinery import importlib.util +import os import sys -import glob import time -import getopt -import ast import tokenize from collections import defaultdict from dataclasses import dataclass, field @@ -173,7 +153,7 @@ # The normal pot-file header. msgmerge and Emacs's po-mode work better if it's # there. -pot_header = _('''\ +pot_header = '''\ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ORGANIZATION # FIRST AUTHOR , YEAR. @@ -190,7 +170,7 @@ "Content-Transfer-Encoding: %(encoding)s\\n" "Generated-By: pygettext.py %(version)s\\n" -''') +''' def usage(code, msg=''): @@ -204,7 +184,7 @@ def make_escapes(pass_nonascii): global escapes, escape if pass_nonascii: # Allow non-ascii characters to pass through so that e.g. 'msgid - # "Hhe"' would result not result in 'msgid "H\366he"'. Otherwise we + # "Höhe"' would result not result in 'msgid "H\366he"'. Otherwise we # escape any character outside the 32..126 range. mod = 128 escape = escape_ascii @@ -224,6 +204,7 @@ def make_escapes(pass_nonascii): def escape_ascii(s, encoding): return ''.join(escapes[ord(c)] if ord(c) < 128 else c for c in s) + def escape_nonascii(s, encoding): return ''.join(escapes[b] for b in s.encode(encoding)) @@ -416,7 +397,7 @@ def __waiting(self, ttype, tstring, lineno): if func_name not in opts.keywords: continue if len(call.args) != 1: - print(_( + print(( '*** %(file)s:%(lineno)s: Seen unexpected amount of' ' positional arguments in gettext call: %(source_segment)s' ) % { @@ -426,7 +407,7 @@ def __waiting(self, ttype, tstring, lineno): }, file=sys.stderr) continue if call.keywords: - print(_( + print(( '*** %(file)s:%(lineno)s: Seen unexpected keyword arguments' ' in gettext call: %(source_segment)s' ) % { @@ -437,7 +418,7 @@ def __waiting(self, ttype, tstring, lineno): continue arg = call.args[0] if not isinstance(arg, ast.Constant): - print(_( + print(( '*** %(file)s:%(lineno)s: Seen unexpected argument type' ' in gettext call: %(source_segment)s' ) % { @@ -550,7 +531,7 @@ def __addentry(self, msg, lineno=None, *, is_docstring=False): ) def warn_unexpected_token(self, token): - print(_( + print(( '*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"' ) % { 'token': token, @@ -677,7 +658,7 @@ class Options: elif opt in ('-S', '--style'): options.locationstyle = locations.get(arg.lower()) if options.locationstyle is None: - usage(1, _('Invalid value for --style: %s') % arg) + usage(1, f'Invalid value for --style: {arg}') elif opt in ('-o', '--output'): options.outfile = arg elif opt in ('-p', '--output-dir'): @@ -685,13 +666,13 @@ class Options: elif opt in ('-v', '--verbose'): options.verbose = 1 elif opt in ('-V', '--version'): - print(_('pygettext.py (xgettext for Python) %s') % __version__) + print(f'pygettext.py (xgettext for Python) {__version__}') sys.exit(0) elif opt in ('-w', '--width'): try: options.width = int(arg) except ValueError: - usage(1, _('--width argument must be an integer: %s') % arg) + usage(1, f'--width argument must be an integer: {arg}') elif opt in ('-x', '--exclude-file'): options.excludefilename = arg elif opt in ('-X', '--no-docstrings'): @@ -719,8 +700,8 @@ class Options: with open(options.excludefilename) as fp: options.toexclude = fp.readlines() except IOError: - print(_( - "Can't read --exclude-file: %s") % options.excludefilename, file=sys.stderr) + print(f"Can't read --exclude-file: {options.excludefilename}", + file=sys.stderr) sys.exit(1) else: options.toexclude = [] @@ -739,12 +720,12 @@ class Options: for filename in args: if filename == '-': if options.verbose: - print(_('Reading standard input')) + print('Reading standard input') fp = sys.stdin.buffer closep = 0 else: if options.verbose: - print(_('Working on %s') % filename) + print(f'Working on {filename}') fp = open(filename, 'rb') closep = 1 try: @@ -779,7 +760,3 @@ class Options: if __name__ == '__main__': main() - # some more test strings - # this one creates a warning - _('*** Seen unexpected token "%(token)s"') % {'token': 'test'} - _('more' 'than' 'one' 'string') From 853a6b7de222964d8cd0e9478cb5c78d490032e6 Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Sun, 2 Feb 2025 06:59:15 -0800 Subject: [PATCH 043/311] Revert "gh-129005: Align FileIO.readall() allocation (#129458)" (#129572) This reverts commit f927204f64b3f8dbecec784e05bc8e25d2a78b2e. --- Lib/_pyio.py | 27 +++++++------------ ...-01-28-21-22-44.gh-issue-129005.h57i9j.rst | 2 -- 2 files changed, 9 insertions(+), 20 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 76a27910da4d5f..023478aa78c6a0 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1674,31 +1674,22 @@ def readall(self): except OSError: pass - result = bytearray(bufsize) - bytes_read = 0 + result = bytearray() while True: - if bytes_read >= bufsize: - # Parallels _io/fileio.c new_buffersize - if bufsize > 65536: - addend = bufsize >> 3 - else: - addend = bufsize + 256 - if addend < DEFAULT_BUFFER_SIZE: - addend = DEFAULT_BUFFER_SIZE - bufsize += addend - result[bytes_read:bufsize] = b'\0' - assert bufsize - bytes_read > 0, "Should always try and read at least one byte" + if len(result) >= bufsize: + bufsize = len(result) + bufsize += max(bufsize, DEFAULT_BUFFER_SIZE) + n = bufsize - len(result) try: - n = os.readinto(self._fd, memoryview(result)[bytes_read:]) + chunk = os.read(self._fd, n) except BlockingIOError: - if bytes_read > 0: + if result: break return None - if n == 0: # reached the end of the file + if not chunk: # reached the end of the file break - bytes_read += n + result += chunk - del result[bytes_read:] return bytes(result) def readinto(self, buffer): diff --git a/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst b/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst deleted file mode 100644 index c76fb05e196f87..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-01-28-21-22-44.gh-issue-129005.h57i9j.rst +++ /dev/null @@ -1,2 +0,0 @@ -``_pyio.FileIO.readall()`` now allocates, resizes, and fills a data buffer using -the same algorithm ``_io.FileIO.readall()`` uses. From 4e38eeafe2ff3bfc686514731d6281fed34a435e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Bo=C4=8Dek?= Date: Sun, 2 Feb 2025 16:11:25 +0100 Subject: [PATCH 044/311] gh-115514: Fix incomplete writes after close while using ssl in asyncio(#128037) Co-authored-by: Kumar Aditya --- Lib/asyncio/selector_events.py | 12 +- Lib/test/test_asyncio/test_selector_events.py | 42 +++++ Lib/test/test_asyncio/test_ssl.py | 161 ++++++++++++++++++ Misc/ACKS | 1 + ...-12-17-16-48-02.gh-issue-115514.1yOJ7T.rst | 2 + 5 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-17-16-48-02.gh-issue-115514.1yOJ7T.rst diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 50992a607b3a1c..22147451fa7ebd 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -1185,10 +1185,13 @@ def can_write_eof(self): return True def _call_connection_lost(self, exc): - super()._call_connection_lost(exc) - if self._empty_waiter is not None: - self._empty_waiter.set_exception( - ConnectionError("Connection is closed by peer")) + try: + super()._call_connection_lost(exc) + finally: + self._write_ready = None + if self._empty_waiter is not None: + self._empty_waiter.set_exception( + ConnectionError("Connection is closed by peer")) def _make_empty_waiter(self): if self._empty_waiter is not None: @@ -1203,7 +1206,6 @@ def _reset_empty_waiter(self): def close(self): self._read_ready_cb = None - self._write_ready = None super().close() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index c9217d04bcd322..9d094a7b041276 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -1051,6 +1051,48 @@ def test_transport_close_remove_writer(self, m_log): transport.close() remove_writer.assert_called_with(self.sock_fd) + def test_write_buffer_after_close(self): + # gh-115514: If the transport is closed while: + # * Transport write buffer is not empty + # * Transport is paused + # * Protocol has data in its buffer, like SSLProtocol in self._outgoing + # The data is still written out. + + # Also tested with real SSL transport in + # test.test_asyncio.test_ssl.TestSSL.test_remote_shutdown_receives_trailing_data + + data = memoryview(b'data') + self.sock.send.return_value = 2 + self.sock.send.fileno.return_value = 7 + + def _resume_writing(): + transport.write(b"data") + self.protocol.resume_writing.side_effect = None + + self.protocol.resume_writing.side_effect = _resume_writing + + transport = self.socket_transport() + transport._high_water = 1 + + transport.write(data) + + self.assertTrue(transport._protocol_paused) + self.assertTrue(self.sock.send.called) + self.loop.assert_writer(7, transport._write_ready) + + transport.close() + + # not called, we still have data in write buffer + self.assertFalse(self.protocol.connection_lost.called) + + self.loop.writers[7]._run() + # during this ^ run, the _resume_writing mock above was called and added more data + + self.assertEqual(transport.get_write_buffer_size(), 2) + self.loop.writers[7]._run() + + self.assertEqual(transport.get_write_buffer_size(), 0) + self.assertTrue(self.protocol.connection_lost.called) class SelectorSocketTransportBufferedProtocolTests(test_utils.TestCase): diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py index 125a6c35793c44..ac774307c7942b 100644 --- a/Lib/test/test_asyncio/test_ssl.py +++ b/Lib/test/test_asyncio/test_ssl.py @@ -12,6 +12,7 @@ import tempfile import threading import time +import unittest.mock import weakref import unittest @@ -1431,6 +1432,166 @@ def wrapper(sock): with self.tcp_server(run(eof_server)) as srv: self.loop.run_until_complete(client(srv.addr)) + def test_remote_shutdown_receives_trailing_data_on_slow_socket(self): + # This test is the same as test_remote_shutdown_receives_trailing_data, + # except it simulates a socket that is not able to write data in time, + # thus triggering different code path in _SelectorSocketTransport. + # This triggers bug gh-115514, also tested using mocks in + # test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer_after_close + # The slow path is triggered here by setting SO_SNDBUF, see code and comment below. + + CHUNK = 1024 * 128 + SIZE = 32 + + sslctx = self._create_server_ssl_context( + test_utils.ONLYCERT, + test_utils.ONLYKEY + ) + client_sslctx = self._create_client_ssl_context() + future = None + + def server(sock): + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + sslobj = sslctx.wrap_bio(incoming, outgoing, server_side=True) + + while True: + try: + sslobj.do_handshake() + except ssl.SSLWantReadError: + if outgoing.pending: + sock.send(outgoing.read()) + incoming.write(sock.recv(16384)) + else: + if outgoing.pending: + sock.send(outgoing.read()) + break + + while True: + try: + data = sslobj.read(4) + except ssl.SSLWantReadError: + incoming.write(sock.recv(16384)) + else: + break + + self.assertEqual(data, b'ping') + sslobj.write(b'pong') + sock.send(outgoing.read()) + + time.sleep(0.2) # wait for the peer to fill its backlog + + # send close_notify but don't wait for response + with self.assertRaises(ssl.SSLWantReadError): + sslobj.unwrap() + sock.send(outgoing.read()) + + # should receive all data + data_len = 0 + while True: + try: + chunk = len(sslobj.read(16384)) + data_len += chunk + except ssl.SSLWantReadError: + incoming.write(sock.recv(16384)) + except ssl.SSLZeroReturnError: + break + + self.assertEqual(data_len, CHUNK * SIZE*2) + + # verify that close_notify is received + sslobj.unwrap() + + sock.close() + + def eof_server(sock): + sock.starttls(sslctx, server_side=True) + self.assertEqual(sock.recv_all(4), b'ping') + sock.send(b'pong') + + time.sleep(0.2) # wait for the peer to fill its backlog + + # send EOF + sock.shutdown(socket.SHUT_WR) + + # should receive all data + data = sock.recv_all(CHUNK * SIZE) + self.assertEqual(len(data), CHUNK * SIZE) + + sock.close() + + async def client(addr): + nonlocal future + future = self.loop.create_future() + + reader, writer = await asyncio.open_connection( + *addr, + ssl=client_sslctx, + server_hostname='') + writer.write(b'ping') + data = await reader.readexactly(4) + self.assertEqual(data, b'pong') + + # fill write backlog in a hacky way - renegotiation won't help + for _ in range(SIZE*2): + writer.transport._test__append_write_backlog(b'x' * CHUNK) + + try: + data = await reader.read() + self.assertEqual(data, b'') + except (BrokenPipeError, ConnectionResetError): + pass + + # Make sure _SelectorSocketTransport enters the delayed write + # path in its `write` method by wrapping socket in a fake class + # that acts as if there is not enough space in socket buffer. + # This triggers bug gh-115514, also tested using mocks in + # test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer_after_close + socket_transport = writer.transport._ssl_protocol._transport + + class SocketWrapper: + def __init__(self, sock) -> None: + self.sock = sock + + def __getattr__(self, name): + return getattr(self.sock, name) + + def send(self, data): + # Fake that our write buffer is full, send only half + to_send = len(data)//2 + return self.sock.send(data[:to_send]) + + def _fake_full_write_buffer(data): + if socket_transport._read_ready_cb is None and not isinstance(socket_transport._sock, SocketWrapper): + socket_transport._sock = SocketWrapper(socket_transport._sock) + return unittest.mock.DEFAULT + + with unittest.mock.patch.object( + socket_transport, "write", + wraps=socket_transport.write, + side_effect=_fake_full_write_buffer + ): + await future + + writer.close() + await self.wait_closed(writer) + + def run(meth): + def wrapper(sock): + try: + meth(sock) + except Exception as ex: + self.loop.call_soon_threadsafe(future.set_exception, ex) + else: + self.loop.call_soon_threadsafe(future.set_result, None) + return wrapper + + with self.tcp_server(run(server)) as srv: + self.loop.run_until_complete(client(srv.addr)) + + with self.tcp_server(run(eof_server)) as srv: + self.loop.run_until_complete(client(srv.addr)) + def test_connect_timeout_warning(self): s = socket.socket(socket.AF_INET) s.bind(('127.0.0.1', 0)) diff --git a/Misc/ACKS b/Misc/ACKS index 4901609a178bc3..a10b0b640970f6 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -189,6 +189,7 @@ Stéphane Blondon Eric Blossom Sergey Bobrov Finn Bock +Vojtěch Boček Paul Boddie Matthew Boedicker Robin Boerdijk diff --git a/Misc/NEWS.d/next/Library/2024-12-17-16-48-02.gh-issue-115514.1yOJ7T.rst b/Misc/NEWS.d/next/Library/2024-12-17-16-48-02.gh-issue-115514.1yOJ7T.rst new file mode 100644 index 00000000000000..24e836a0b0b7f9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-17-16-48-02.gh-issue-115514.1yOJ7T.rst @@ -0,0 +1,2 @@ +Fix exceptions and incomplete writes after :class:`!asyncio._SelectorTransport` +is closed before writes are completed. From 0612a89ffcf0bb52b1750a3466671ba8daad1d87 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 2 Feb 2025 16:12:01 +0000 Subject: [PATCH 045/311] gh-126609: Allow translating the ``availability`` directive (#129549) --- Doc/tools/extensions/availability.py | 3 ++- Doc/tools/templates/dummy.html | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py index 47833fdcb87590..1a2c7b02b44439 100644 --- a/Doc/tools/extensions/availability.py +++ b/Doc/tools/extensions/availability.py @@ -6,6 +6,7 @@ from docutils import nodes from sphinx import addnodes +from sphinx.locale import _ as sphinx_gettext from sphinx.util import logging from sphinx.util.docutils import SphinxDirective @@ -55,7 +56,7 @@ class Availability(SphinxDirective): final_argument_whitespace = True def run(self) -> list[nodes.container]: - title = "Availability" + title = sphinx_gettext("Availability") refnode = addnodes.pending_xref( title, nodes.inline(title, title, classes=["xref", "std", "std-ref"]), diff --git a/Doc/tools/templates/dummy.html b/Doc/tools/templates/dummy.html index 49c2a71a5e40cf..4f0f6f91436a87 100644 --- a/Doc/tools/templates/dummy.html +++ b/Doc/tools/templates/dummy.html @@ -7,6 +7,10 @@ {% trans %}Deprecated since version {deprecated}, will be removed in version {removed}{% endtrans %} {% trans %}Deprecated since version {deprecated}, removed in version {removed}{% endtrans %} +In extensions/availability.py: + +{% trans %}Availability{% endtrans %} + In extensions/c_annotations.py: {% trans %}Part of the{% endtrans %} From ae4788809d674f8e27faef2678953be8cf67d4a3 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 2 Feb 2025 16:17:02 +0000 Subject: [PATCH 046/311] GH-121970: Extract ``misc_news`` into a new extension (#129577) --- Doc/conf.py | 1 + Doc/make.bat | 10 ++-- Doc/tools/extensions/misc_news.py | 75 ++++++++++++++++++++++++++++++ Doc/tools/extensions/pyspecific.py | 41 ---------------- Doc/whatsnew/changelog.rst | 2 + Misc/NEWS.d/3.5.3rc1.rst | 2 +- Misc/NEWS.d/3.6.0a4.rst | 2 +- Misc/NEWS.d/3.6.0b1.rst | 2 +- 8 files changed, 85 insertions(+), 50 deletions(-) create mode 100644 Doc/tools/extensions/misc_news.py diff --git a/Doc/conf.py b/Doc/conf.py index 1aeecaeb3073f5..94af54084ee338 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -28,6 +28,7 @@ 'changes', 'glossary_search', 'lexers', + 'misc_news', 'pydoc_topics', 'pyspecific', 'sphinx.ext.coverage', diff --git a/Doc/make.bat b/Doc/make.bat index ede793ed3c6d70..99f0d5c44f0098 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -127,16 +127,14 @@ goto end :build if not exist "%BUILDDIR%" mkdir "%BUILDDIR%" -rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py -if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1 -if not exist "%PY_MISC_NEWS_DIR%" mkdir "%PY_MISC_NEWS_DIR%" +if not exist build mkdir build if exist ..\Misc\NEWS ( - echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS - copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\NEWS" > nul + echo.Copying existing Misc\NEWS file to Doc\build\NEWS + copy ..\Misc\NEWS build\NEWS > nul ) else if exist ..\Misc\NEWS.D ( if defined BLURB ( echo.Merging Misc/NEWS with %BLURB% - %BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS" + %BLURB% merge -f build\NEWS ) else ( echo.No Misc/NEWS file and Blurb is not available. exit /B 1 diff --git a/Doc/tools/extensions/misc_news.py b/Doc/tools/extensions/misc_news.py new file mode 100644 index 00000000000000..a24c440595ee92 --- /dev/null +++ b/Doc/tools/extensions/misc_news.py @@ -0,0 +1,75 @@ +"""Support for including Misc/NEWS.""" + +from __future__ import annotations + +import re +from pathlib import Path +from typing import TYPE_CHECKING + +from docutils import nodes +from sphinx.locale import _ as sphinx_gettext +from sphinx.util.docutils import SphinxDirective + +if TYPE_CHECKING: + from typing import Final + + from docutils.nodes import Node + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata + + +BLURB_HEADER = """\ ++++++++++++ +Python News ++++++++++++ +""" + +bpo_issue_re: Final[re.Pattern[str]] = re.compile( + "(?:issue #|bpo-)([0-9]+)", re.ASCII +) +gh_issue_re: Final[re.Pattern[str]] = re.compile( + "gh-(?:issue-)?([0-9]+)", re.ASCII | re.IGNORECASE +) +whatsnew_re: Final[re.Pattern[str]] = re.compile( + r"^what's new in (.*?)\??$", re.ASCII | re.IGNORECASE | re.MULTILINE +) + + +class MiscNews(SphinxDirective): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self) -> list[Node]: + # Get content of NEWS file + source, _ = self.get_source_info() + news_file = Path(source).resolve().parent / self.arguments[0] + self.env.note_dependency(news_file) + try: + news_text = news_file.read_text(encoding="utf-8") + except (OSError, UnicodeError): + text = sphinx_gettext("The NEWS file is not available.") + return [nodes.strong(text, text)] + + # remove first 3 lines as they are the main heading + news_text = news_text.removeprefix(BLURB_HEADER) + + news_text = bpo_issue_re.sub(r":issue:`\1`", news_text) + # Fallback handling for GitHub issues + news_text = gh_issue_re.sub(r":gh:`\1`", news_text) + news_text = whatsnew_re.sub(r"\1", news_text) + + self.state_machine.insert_input(news_text.splitlines(), str(news_file)) + return [] + + +def setup(app: Sphinx) -> ExtensionMetadata: + app.add_directive("miscnews", MiscNews) + + return { + "version": "1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index f363dfd4216929..57cf80a7e77324 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -141,46 +141,6 @@ def run(self): return PyMethod.run(self) -# Support for including Misc/NEWS - -issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I) -gh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I) -whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$") - - -class MiscNews(SphinxDirective): - has_content = False - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = {} - - def run(self): - fname = self.arguments[0] - source = self.state_machine.input_lines.source( - self.lineno - self.state_machine.input_offset - 1) - source_dir = getenv('PY_MISC_NEWS_DIR') - if not source_dir: - source_dir = path.dirname(path.abspath(source)) - fpath = path.join(source_dir, fname) - self.env.note_dependency(path.abspath(fpath)) - try: - with io.open(fpath, encoding='utf-8') as fp: - content = fp.read() - except Exception: - text = 'The NEWS file is not available.' - node = nodes.strong(text, text) - return [node] - content = issue_re.sub(r':issue:`\1`', content) - # Fallback handling for the GitHub issue - content = gh_issue_re.sub(r':gh:`\1`', content) - content = whatsnew_re.sub(r'\1', content) - # remove first 3 lines as they are the main heading - lines = ['.. default-role:: obj', ''] + content.splitlines()[3:] - self.state_machine.insert_input(lines, fname) - return [] - - # Support for documenting Opcodes opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?') @@ -268,6 +228,5 @@ def setup(app): app.add_directive_to_domain('py', 'awaitablefunction', PyAwaitableFunction) app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod) app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) - app.add_directive('miscnews', MiscNews) app.connect('env-check-consistency', patch_pairindextypes) return {'version': '1.0', 'parallel_read_safe': True} diff --git a/Doc/whatsnew/changelog.rst b/Doc/whatsnew/changelog.rst index b4356143659031..e796d4157cec76 100644 --- a/Doc/whatsnew/changelog.rst +++ b/Doc/whatsnew/changelog.rst @@ -1,5 +1,7 @@ .. _changelog: +.. default-role:: py:obj + +++++++++ Changelog +++++++++ diff --git a/Misc/NEWS.d/3.5.3rc1.rst b/Misc/NEWS.d/3.5.3rc1.rst index 2424604249a65c..cfc729dd82556f 100644 --- a/Misc/NEWS.d/3.5.3rc1.rst +++ b/Misc/NEWS.d/3.5.3rc1.rst @@ -1146,7 +1146,7 @@ after a commit. .. section: Library A new version of typing.py from https://github.com/python/typing: -Collection (only for 3.6) (Issue #27598). Add FrozenSet to __all__ +Collection (only for 3.6) (issue #27598). Add FrozenSet to __all__ (upstream #261). Fix crash in _get_type_vars() (upstream #259). Remove the dict constraint in ForwardRef._eval_type (upstream #252). diff --git a/Misc/NEWS.d/3.6.0a4.rst b/Misc/NEWS.d/3.6.0a4.rst index 3abbdecb57038b..6f3f5262e5749d 100644 --- a/Misc/NEWS.d/3.6.0a4.rst +++ b/Misc/NEWS.d/3.6.0a4.rst @@ -177,7 +177,7 @@ Support keyword arguments to zlib.decompress(). Patch by Xiang Zhang. .. section: Library Prevent segfault after interpreter re-initialization due to ref count -problem introduced in code for Issue #27038 in 3.6.0a3. Patch by Xiang +problem introduced in code for issue #27038 in 3.6.0a3. Patch by Xiang Zhang. .. diff --git a/Misc/NEWS.d/3.6.0b1.rst b/Misc/NEWS.d/3.6.0b1.rst index bd54cf601d053b..1e2dcdd6c642bb 100644 --- a/Misc/NEWS.d/3.6.0b1.rst +++ b/Misc/NEWS.d/3.6.0b1.rst @@ -1137,7 +1137,7 @@ chunked transfer-encoding. .. section: Library A new version of typing.py from https://github.com/python/typing: - -Collection (only for 3.6) (Issue #27598) - Add FrozenSet to __all__ +Collection (only for 3.6) (issue #27598) - Add FrozenSet to __all__ (upstream #261) - fix crash in _get_type_vars() (upstream #259) - Remove the dict constraint in ForwardRef._eval_type (upstream #252) From 2ad069d906c6952250dabbffbcb882676011b310 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 2 Feb 2025 23:17:30 +0100 Subject: [PATCH 047/311] gh-91417: Remove PySequence_Fast() from the limited C API (#129398) The function never worked with the limited C API. It was added by mistake. --- Doc/data/stable_abi.dat | 1 - Doc/whatsnew/3.14.rst | 5 ++++ Include/abstract.h | 25 ------------------ Include/cpython/abstract.h | 26 +++++++++++++++++++ ...5-01-28-13-21-17.gh-issue-91417.AfiR0t.rst | 3 +++ Misc/stable_abi.toml | 1 + 6 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-28-13-21-17.gh-issue-91417.AfiR0t.rst diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index c15f82603aa944..59e7a31bc2ef06 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -582,7 +582,6 @@ func,PySequence_Contains,3.2,, func,PySequence_Count,3.2,, func,PySequence_DelItem,3.2,, func,PySequence_DelSlice,3.2,, -func,PySequence_Fast,3.2,, func,PySequence_GetItem,3.2,, func,PySequence_GetSlice,3.2,, func,PySequence_In,3.2,, diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index daed0e8aa509a1..59c432d30a342b 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1343,6 +1343,11 @@ Limited C API changes implementation details. (Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.) +* Remove :c:func:`PySequence_Fast` from the limited C API, since this function + has to be used with :c:macro:`PySequence_Fast_GET_ITEM` which never worked + in the limited C API. + (Contributed by Victor Stinner in :gh:`91417`.) + Porting to Python 3.14 ---------------------- diff --git a/Include/abstract.h b/Include/abstract.h index 7cfee1332ccaa4..4efe4fcb014903 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -726,31 +726,6 @@ PyAPI_FUNC(PyObject *) PySequence_Tuple(PyObject *o); This is equivalent to the Python expression: list(o) */ PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o); -/* Return the sequence 'o' as a list, unless it's already a tuple or list. - - Use PySequence_Fast_GET_ITEM to access the members of this list, and - PySequence_Fast_GET_SIZE to get its length. - - Returns NULL on failure. If the object does not support iteration, raises a - TypeError exception with 'm' as the message text. */ -PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m); - -/* Return the size of the sequence 'o', assuming that 'o' was returned by - PySequence_Fast and is not NULL. */ -#define PySequence_Fast_GET_SIZE(o) \ - (PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o)) - -/* Return the 'i'-th element of the sequence 'o', assuming that o was returned - by PySequence_Fast, and that i is within bounds. */ -#define PySequence_Fast_GET_ITEM(o, i)\ - (PyList_Check(o) ? PyList_GET_ITEM((o), (i)) : PyTuple_GET_ITEM((o), (i))) - -/* Return a pointer to the underlying item array for - an object returned by PySequence_Fast */ -#define PySequence_Fast_ITEMS(sf) \ - (PyList_Check(sf) ? ((PyListObject *)(sf))->ob_item \ - : ((PyTupleObject *)(sf))->ob_item) - /* Return the number of occurrences on value on 'o', that is, return the number of keys for which o[key] == value. diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h index 4e7b7a46703a6d..8fed1d3110988b 100644 --- a/Include/cpython/abstract.h +++ b/Include/cpython/abstract.h @@ -85,3 +85,29 @@ PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t); need to be corrected for a negative index. */ #define PySequence_ITEM(o, i)\ ( Py_TYPE(o)->tp_as_sequence->sq_item((o), (i)) ) + +/* Return the sequence 'o' as a list, unless it's already a tuple or list. + + Use PySequence_Fast_GET_ITEM to access the members of this list, and + PySequence_Fast_GET_SIZE to get its length. + + Returns NULL on failure. If the object does not support iteration, raises a + TypeError exception with 'm' as the message text. */ +PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m); + +/* Return the size of the sequence 'o', assuming that 'o' was returned by + PySequence_Fast and is not NULL. */ +#define PySequence_Fast_GET_SIZE(o) \ + (PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o)) + +/* Return the 'i'-th element of the sequence 'o', assuming that o was returned + by PySequence_Fast, and that i is within bounds. */ +#define PySequence_Fast_GET_ITEM(o, i)\ + (PyList_Check(o) ? PyList_GET_ITEM((o), (i)) : PyTuple_GET_ITEM((o), (i))) + +/* Return a pointer to the underlying item array for + an object returned by PySequence_Fast */ +#define PySequence_Fast_ITEMS(sf) \ + (PyList_Check(sf) ? ((PyListObject *)(sf))->ob_item \ + : ((PyTupleObject *)(sf))->ob_item) + diff --git a/Misc/NEWS.d/next/C_API/2025-01-28-13-21-17.gh-issue-91417.AfiR0t.rst b/Misc/NEWS.d/next/C_API/2025-01-28-13-21-17.gh-issue-91417.AfiR0t.rst new file mode 100644 index 00000000000000..e1017188b8d0ce --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-01-28-13-21-17.gh-issue-91417.AfiR0t.rst @@ -0,0 +1,3 @@ +Remove :c:func:`PySequence_Fast` from the limited C API, since this function +has to be used with :c:macro:`PySequence_Fast_GET_ITEM` which never worked +in the limited C API. Patch by Victor Stinner. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 276526a1b6908e..9317be605f0065 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1253,6 +1253,7 @@ added = '3.2' [function.PySequence_Fast] added = '3.2' + abi_only = true [function.PySequence_GetItem] added = '3.2' [function.PySequence_GetSlice] From ecd2f84555bf494007420ba5a37adf8d1c23e856 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 3 Feb 2025 00:03:54 +0100 Subject: [PATCH 048/311] gh-105875: amend sqlite3 docstring wrt. SQLite requirement (#129599) --- Lib/sqlite3/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/sqlite3/__init__.py b/Lib/sqlite3/__init__.py index 34a9c047dd607c..ed727fae609d1d 100644 --- a/Lib/sqlite3/__init__.py +++ b/Lib/sqlite3/__init__.py @@ -22,7 +22,7 @@ """ The sqlite3 extension module provides a DB-API 2.0 (PEP 249) compliant -interface to the SQLite library, and requires SQLite 3.7.15 or newer. +interface to the SQLite library, and requires SQLite 3.15.2 or newer. To use the module, start by creating a database Connection object: From e6c76b947b2d0708cea1d5e090d02e79fda1cc26 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Mon, 3 Feb 2025 00:09:30 +0100 Subject: [PATCH 049/311] GH-128872: Remove unused argument from _PyCode_Quicken (GH-128873) Co-authored-by: Kirill Podoprigora --- Objects/codeobject.c | 10 ++++------ Python/specialize.c | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 7eea61968bc4d9..a7b46aa2dfbbc0 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -459,8 +459,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) } extern void -_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, - int enable_counters); +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters); #ifdef Py_GIL_DISABLED static _PyCodeArray * _PyCodeArray_New(Py_ssize_t size); @@ -543,10 +542,9 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) } co->_co_firsttraceable = entry_point; #ifdef Py_GIL_DISABLED - _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), co->co_consts, - interp->config.tlbc_enabled); + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->config.tlbc_enabled); #else - _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), co->co_consts, 1); + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), 1); #endif notify_code_watchers(PY_CODE_EVENT_CREATE, co); return 0; @@ -2819,7 +2817,7 @@ copy_code(_Py_CODEUNIT *dst, PyCodeObject *co) for (int i = 0; i < code_len; i += _PyInstruction_GetLength(co, i)) { dst[i] = _Py_GetBaseCodeUnit(co, i); } - _PyCode_Quicken(dst, code_len, co->co_consts, 1); + _PyCode_Quicken(dst, code_len, 1); } static Py_ssize_t diff --git a/Python/specialize.c b/Python/specialize.c index abb130d73eeebd..bc251777c27d00 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -441,8 +441,7 @@ do { \ // Initialize warmup counters and optimize instructions. This cannot fail. void -_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts, - int enable_counters) +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters) { #if ENABLE_SPECIALIZATION_FT _Py_BackoffCounter jump_counter, adaptive_counter; From 567394517a10c9a9f3af25a31009589ae2c50f1b Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Sun, 2 Feb 2025 23:17:53 +0000 Subject: [PATCH 050/311] GH-128842: Collect JIT memory stats (GH-128941) --- Include/cpython/pystats.h | 8 ++ Include/internal/pycore_code.h | 2 + ...-01-17-13-16-14.gh-issue-128842.OMs5X6.rst | 1 + Python/jit.c | 8 ++ Python/specialize.c | 8 ++ Tools/scripts/summarize_stats.py | 98 +++++++++++++++++-- 6 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-17-13-16-14.gh-issue-128842.OMs5X6.rst diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index ee8885cda7b60d..f52348e42b1330 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -141,6 +141,14 @@ typedef struct _optimization_stats { uint64_t remove_globals_builtins_changed; uint64_t remove_globals_incorrect_keys; uint64_t error_in_opcode[PYSTATS_MAX_UOP_ID + 1]; + // JIT memory stats + uint64_t jit_total_memory_size; + uint64_t jit_code_size; + uint64_t jit_trampoline_size; + uint64_t jit_data_size; + uint64_t jit_padding_size; + uint64_t jit_freed_memory_size; + uint64_t trace_total_memory_hist[_Py_UOP_HIST_SIZE]; } OptimizationStats; typedef struct _rare_event_stats { diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 01d41446fdb0cf..65c3d142458577 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -373,6 +373,7 @@ extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr); do { if (_Py_stats && PyFunction_Check(callable)) _Py_stats->call_stats.eval_calls[name]++; } while (0) #define GC_STAT_ADD(gen, name, n) do { if (_Py_stats) _Py_stats->gc_stats[(gen)].name += (n); } while (0) #define OPT_STAT_INC(name) do { if (_Py_stats) _Py_stats->optimization_stats.name++; } while (0) +#define OPT_STAT_ADD(name, n) do { if (_Py_stats) _Py_stats->optimization_stats.name += (n); } while (0) #define UOP_STAT_INC(opname, name) do { if (_Py_stats) { assert(opname < 512); _Py_stats->optimization_stats.opcode[opname].name++; } } while (0) #define UOP_PAIR_INC(uopcode, lastuop) \ do { \ @@ -408,6 +409,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); #define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) ((void)0) #define GC_STAT_ADD(gen, name, n) ((void)0) #define OPT_STAT_INC(name) ((void)0) +#define OPT_STAT_ADD(name, n) ((void)0) #define UOP_STAT_INC(opname, name) ((void)0) #define UOP_PAIR_INC(uopcode, lastuop) ((void)0) #define OPT_UNSUPPORTED_OPCODE(opname) ((void)0) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-17-13-16-14.gh-issue-128842.OMs5X6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-17-13-16-14.gh-issue-128842.OMs5X6.rst new file mode 100644 index 00000000000000..9898060076db79 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-17-13-16-14.gh-issue-128842.OMs5X6.rst @@ -0,0 +1 @@ +Collect JIT memory stats using pystats. Patch by Diego Russo. diff --git a/Python/jit.c b/Python/jit.c index 33c2418084b1fd..e6a337a5899b05 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -87,6 +87,7 @@ jit_free(unsigned char *memory, size_t size) jit_error("unable to free memory"); return -1; } + OPT_STAT_ADD(jit_freed_memory_size, size); return 0; } @@ -510,6 +511,13 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz #ifdef MAP_JIT pthread_jit_write_protect_np(0); #endif + // Collect memory stats + OPT_STAT_ADD(jit_total_memory_size, total_size); + OPT_STAT_ADD(jit_code_size, code_size); + OPT_STAT_ADD(jit_trampoline_size, state.trampolines.size); + OPT_STAT_ADD(jit_data_size, data_size); + OPT_STAT_ADD(jit_padding_size, padding); + OPT_HIST(total_size, trace_total_memory_hist); // Update the offsets of each instruction: for (size_t i = 0; i < length; i++) { state.instruction_starts[i] += (uintptr_t)memory; diff --git a/Python/specialize.c b/Python/specialize.c index bc251777c27d00..8831cfaa82be9b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -309,6 +309,14 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) ); } } + fprintf(out, "JIT total memory size: %" PRIu64 "\n", stats->jit_total_memory_size); + fprintf(out, "JIT code size: %" PRIu64 "\n", stats->jit_code_size); + fprintf(out, "JIT trampoline size: %" PRIu64 "\n", stats->jit_trampoline_size); + fprintf(out, "JIT data size: %" PRIu64 "\n", stats->jit_data_size); + fprintf(out, "JIT padding size: %" PRIu64 "\n", stats->jit_padding_size); + fprintf(out, "JIT freed memory size: %" PRIu64 "\n", stats->jit_freed_memory_size); + + print_histogram(out, "Trace total memory size", stats->trace_total_memory_hist); } #endif diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index bc7ccfe33e777d..161af09183a282 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -545,6 +545,41 @@ def get_optimizer_stats(self) -> dict[str, tuple[int, int | None]]: ): (incorrect_keys, attempts), } + def get_jit_memory_stats(self) -> dict[Doc, tuple[int, int | None]]: + jit_total_memory_size = self._data["JIT total memory size"] + jit_code_size = self._data["JIT code size"] + jit_trampoline_size = self._data["JIT trampoline size"] + jit_data_size = self._data["JIT data size"] + jit_padding_size = self._data["JIT padding size"] + jit_freed_memory_size = self._data["JIT freed memory size"] + + return { + Doc( + "Total memory size", + "The total size of the memory allocated for the JIT traces", + ): (jit_total_memory_size, None), + Doc( + "Code size", + "The size of the memory allocated for the code of the JIT traces", + ): (jit_code_size, jit_total_memory_size), + Doc( + "Trampoline size", + "The size of the memory allocated for the trampolines of the JIT traces", + ): (jit_trampoline_size, jit_total_memory_size), + Doc( + "Data size", + "The size of the memory allocated for the data of the JIT traces", + ): (jit_data_size, jit_total_memory_size), + Doc( + "Padding size", + "The size of the memory allocated for the padding of the JIT traces", + ): (jit_padding_size, jit_total_memory_size), + Doc( + "Freed memory size", + "The size of the memory freed from the JIT traces", + ): (jit_freed_memory_size, jit_total_memory_size), + } + def get_histogram(self, prefix: str) -> list[tuple[int, int]]: rows = [] for k, v in self._data.items(): @@ -1161,16 +1196,31 @@ def calc_optimizer_table(stats: Stats) -> Rows: for label, (value, den) in optimizer_stats.items() ] - def calc_histogram_table(key: str, den: str) -> RowCalculator: + def calc_jit_memory_table(stats: Stats) -> Rows: + jit_memory_stats = stats.get_jit_memory_stats() + + return [ + ( + label, + Count(value), + Ratio(value, den, percentage=label != "Total memory size"), + ) + for label, (value, den) in jit_memory_stats.items() + ] + + def calc_histogram_table(key: str, den: str | None = None) -> RowCalculator: def calc(stats: Stats) -> Rows: histogram = stats.get_histogram(key) - denominator = stats.get(den) + + if den: + denominator = stats.get(den) + else: + denominator = 0 + for _, v in histogram: + denominator += v rows: Rows = [] - last_non_zero = 0 for k, v in histogram: - if v != 0: - last_non_zero = len(rows) rows.append( ( f"<= {k:,d}", @@ -1178,9 +1228,19 @@ def calc(stats: Stats) -> Rows: Ratio(v, denominator), ) ) - # Don't include any zero entries at the end - rows = rows[: last_non_zero + 1] - return rows + # Don't include any leading and trailing zero entries + start = 0 + end = len(rows) - 1 + + while start <= end: + if rows[start][1] == 0: + start += 1 + elif rows[end][1] == 0: + end -= 1 + else: + break + + return rows[start:end+1] return calc @@ -1214,6 +1274,28 @@ def iter_optimization_tables(base_stats: Stats, head_stats: Stats | None = None) yield Table(("", "Count:", "Ratio:"), calc_optimization_table, JoinMode.CHANGE) yield Table(("", "Count:", "Ratio:"), calc_optimizer_table, JoinMode.CHANGE) + yield Section( + "JIT memory stats", + "JIT memory stats", + [ + Table( + ("", "Size (bytes):", "Ratio:"), + calc_jit_memory_table, + JoinMode.CHANGE + ) + ], + ) + yield Section( + "JIT trace total memory histogram", + "JIT trace total memory histogram", + [ + Table( + ("Size (bytes)", "Count", "Ratio:"), + calc_histogram_table("Trace total memory size"), + JoinMode.CHANGE_NO_SORT, + ) + ], + ) for name, den in [ ("Trace length", "Optimization traces created"), ("Optimized trace length", "Optimization traces created"), From a29a9c0f3890fec843b7151f6a1defa25f570504 Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Sun, 2 Feb 2025 23:19:55 +0000 Subject: [PATCH 051/311] GH-129231: Group executable JIT code in memory (GH-129232) --- .../2025-01-24-11-37-22.gh-issue-129231.ZsAP9v.rst | 1 + Python/jit.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-24-11-37-22.gh-issue-129231.ZsAP9v.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-24-11-37-22.gh-issue-129231.ZsAP9v.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-24-11-37-22.gh-issue-129231.ZsAP9v.rst new file mode 100644 index 00000000000000..b30492a1947058 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-24-11-37-22.gh-issue-129231.ZsAP9v.rst @@ -0,0 +1 @@ +Improve memory layout of JIT traces. Patch by Diego Russo diff --git a/Python/jit.c b/Python/jit.c index e6a337a5899b05..092b873bc734e1 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -502,8 +502,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz // Round up to the nearest page: size_t page_size = get_page_size(); assert((page_size & (page_size - 1)) == 0); - size_t padding = page_size - ((code_size + data_size + state.trampolines.size) & (page_size - 1)); - size_t total_size = code_size + data_size + state.trampolines.size + padding; + size_t padding = page_size - ((code_size + state.trampolines.size + data_size) & (page_size - 1)); + size_t total_size = code_size + state.trampolines.size + data_size + padding; unsigned char *memory = jit_alloc(total_size); if (memory == NULL) { return -1; @@ -524,8 +524,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz } // Loop again to emit the code: unsigned char *code = memory; - unsigned char *data = memory + code_size; - state.trampolines.mem = memory + code_size + data_size; + state.trampolines.mem = memory + code_size; + unsigned char *data = memory + code_size + state.trampolines.size; // Compile the shim, which handles converting between the native // calling convention and the calling convention used by jitted code // (which may be different for efficiency reasons). @@ -547,7 +547,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz code += group->code_size; data += group->data_size; assert(code == memory + code_size); - assert(data == memory + code_size + data_size); + assert(data == memory + code_size + state.trampolines.size + data_size); #ifdef MAP_JIT pthread_jit_write_protect_np(1); #endif From a33dcb9e431c463c20ecdc02a206ddf0b7388687 Mon Sep 17 00:00:00 2001 From: Illia Volochii Date: Mon, 3 Feb 2025 12:23:27 +0200 Subject: [PATCH 052/311] gh-81340: Use copy_file_range in shutil.copyfile copy functions (GH-93152) This allows the underlying file system an opportunity to optimise or avoid the actual copy. --- Doc/library/shutil.rst | 8 +- Lib/shutil.py | 96 +++++++++++++++---- Lib/test/test_shutil.py | 71 ++++++++++---- Misc/ACKS | 1 + ...2-05-23-21-23-29.gh-issue-81340.D11RkZ.rst | 5 + 5 files changed, 139 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-05-23-21-23-29.gh-issue-81340.D11RkZ.rst diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 2a8592f8bd69c1..06800c4588b663 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -512,7 +512,9 @@ the use of userspace buffers in Python as in "``outfd.write(infd.read())``". On macOS `fcopyfile`_ is used to copy the file content (not metadata). -On Linux and Solaris :func:`os.sendfile` is used. +On Linux :func:`os.copy_file_range` or :func:`os.sendfile` is used. + +On Solaris :func:`os.sendfile` is used. On Windows :func:`shutil.copyfile` uses a bigger default buffer size (1 MiB instead of 64 KiB) and a :func:`memoryview`-based variant of @@ -527,6 +529,10 @@ file then shutil will silently fallback on using less efficient .. versionchanged:: 3.14 Solaris now uses :func:`os.sendfile`. +.. versionchanged:: next + Copy-on-write or server-side copy may be used internally via + :func:`os.copy_file_range` on supported Linux filesystems. + .. _shutil-copytree-example: copytree example diff --git a/Lib/shutil.py b/Lib/shutil.py index 171489ca41f2a7..510ae8c6f22d59 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -49,6 +49,7 @@ # https://bugs.python.org/issue43743#msg393429 _USE_CP_SENDFILE = (hasattr(os, "sendfile") and sys.platform.startswith(("linux", "android", "sunos"))) +_USE_CP_COPY_FILE_RANGE = hasattr(os, "copy_file_range") _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS # CMD defaults in Windows 10 @@ -107,6 +108,66 @@ def _fastcopy_fcopyfile(fsrc, fdst, flags): else: raise err from None +def _determine_linux_fastcopy_blocksize(infd): + """Determine blocksize for fastcopying on Linux. + + Hopefully the whole file will be copied in a single call. + The copying itself should be performed in a loop 'till EOF is + reached (0 return) so a blocksize smaller or bigger than the actual + file size should not make any difference, also in case the file + content changes while being copied. + """ + try: + blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8 MiB + except OSError: + blocksize = 2 ** 27 # 128 MiB + # On 32-bit architectures truncate to 1 GiB to avoid OverflowError, + # see gh-82500. + if sys.maxsize < 2 ** 32: + blocksize = min(blocksize, 2 ** 30) + return blocksize + +def _fastcopy_copy_file_range(fsrc, fdst): + """Copy data from one regular mmap-like fd to another by using + a high-performance copy_file_range(2) syscall that gives filesystems + an opportunity to implement the use of reflinks or server-side copy. + + This should work on Linux >= 4.5 only. + """ + try: + infd = fsrc.fileno() + outfd = fdst.fileno() + except Exception as err: + raise _GiveupOnFastCopy(err) # not a regular file + + blocksize = _determine_linux_fastcopy_blocksize(infd) + offset = 0 + while True: + try: + n_copied = os.copy_file_range(infd, outfd, blocksize, offset_dst=offset) + except OSError as err: + # ...in oder to have a more informative exception. + err.filename = fsrc.name + err.filename2 = fdst.name + + if err.errno == errno.ENOSPC: # filesystem is full + raise err from None + + # Give up on first call and if no data was copied. + if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0: + raise _GiveupOnFastCopy(err) + + raise err + else: + if n_copied == 0: + # If no bytes have been copied yet, copy_file_range + # might silently fail. + # https://lore.kernel.org/linux-fsdevel/20210126233840.GG4626@dread.disaster.area/T/#m05753578c7f7882f6e9ffe01f981bc223edef2b0 + if offset == 0: + raise _GiveupOnFastCopy() + break + offset += n_copied + def _fastcopy_sendfile(fsrc, fdst): """Copy data from one regular mmap-like fd to another by using high-performance sendfile(2) syscall. @@ -128,20 +189,7 @@ def _fastcopy_sendfile(fsrc, fdst): except Exception as err: raise _GiveupOnFastCopy(err) # not a regular file - # Hopefully the whole file will be copied in a single call. - # sendfile() is called in a loop 'till EOF is reached (0 return) - # so a bufsize smaller or bigger than the actual file size - # should not make any difference, also in case the file content - # changes while being copied. - try: - blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB - except OSError: - blocksize = 2 ** 27 # 128MiB - # On 32-bit architectures truncate to 1GiB to avoid OverflowError, - # see bpo-38319. - if sys.maxsize < 2 ** 32: - blocksize = min(blocksize, 2 ** 30) - + blocksize = _determine_linux_fastcopy_blocksize(infd) offset = 0 while True: try: @@ -266,12 +314,20 @@ def copyfile(src, dst, *, follow_symlinks=True): except _GiveupOnFastCopy: pass # Linux / Android / Solaris - elif _USE_CP_SENDFILE: - try: - _fastcopy_sendfile(fsrc, fdst) - return dst - except _GiveupOnFastCopy: - pass + elif _USE_CP_SENDFILE or _USE_CP_COPY_FILE_RANGE: + # reflink may be implicit in copy_file_range. + if _USE_CP_COPY_FILE_RANGE: + try: + _fastcopy_copy_file_range(fsrc, fdst) + return dst + except _GiveupOnFastCopy: + pass + if _USE_CP_SENDFILE: + try: + _fastcopy_sendfile(fsrc, fdst) + return dst + except _GiveupOnFastCopy: + pass # Windows, see: # https://github.com/python/cpython/pull/7160#discussion_r195405230 elif _WINDOWS and file_size > 0: diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 1f18b1f09b5858..078ddd6c431b37 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -3239,12 +3239,8 @@ def test_filesystem_full(self): self.assertRaises(OSError, self.zerocopy_fun, src, dst) -@unittest.skipIf(not SUPPORTS_SENDFILE, 'os.sendfile() not supported') -class TestZeroCopySendfile(_ZeroCopyFileTest, unittest.TestCase): - PATCHPOINT = "os.sendfile" - - def zerocopy_fun(self, fsrc, fdst): - return shutil._fastcopy_sendfile(fsrc, fdst) +class _ZeroCopyFileLinuxTest(_ZeroCopyFileTest): + BLOCKSIZE_INDEX = None def test_non_regular_file_src(self): with io.BytesIO(self.FILEDATA) as src: @@ -3265,65 +3261,65 @@ def test_non_regular_file_dst(self): self.assertEqual(dst.read(), self.FILEDATA) def test_exception_on_second_call(self): - def sendfile(*args, **kwargs): + def syscall(*args, **kwargs): if not flag: flag.append(None) - return orig_sendfile(*args, **kwargs) + return orig_syscall(*args, **kwargs) else: raise OSError(errno.EBADF, "yo") flag = [] - orig_sendfile = os.sendfile - with unittest.mock.patch('os.sendfile', create=True, - side_effect=sendfile): + orig_syscall = eval(self.PATCHPOINT) + with unittest.mock.patch(self.PATCHPOINT, create=True, + side_effect=syscall): with self.get_files() as (src, dst): with self.assertRaises(OSError) as cm: - shutil._fastcopy_sendfile(src, dst) + self.zerocopy_fun(src, dst) assert flag self.assertEqual(cm.exception.errno, errno.EBADF) def test_cant_get_size(self): # Emulate a case where src file size cannot be determined. # Internally bufsize will be set to a small value and - # sendfile() will be called repeatedly. + # a system call will be called repeatedly. with unittest.mock.patch('os.fstat', side_effect=OSError) as m: with self.get_files() as (src, dst): - shutil._fastcopy_sendfile(src, dst) + self.zerocopy_fun(src, dst) assert m.called self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA) def test_small_chunks(self): # Force internal file size detection to be smaller than the - # actual file size. We want to force sendfile() to be called + # actual file size. We want to force a system call to be called # multiple times, also in order to emulate a src fd which gets # bigger while it is being copied. mock = unittest.mock.Mock() mock.st_size = 65536 + 1 with unittest.mock.patch('os.fstat', return_value=mock) as m: with self.get_files() as (src, dst): - shutil._fastcopy_sendfile(src, dst) + self.zerocopy_fun(src, dst) assert m.called self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA) def test_big_chunk(self): # Force internal file size detection to be +100MB bigger than - # the actual file size. Make sure sendfile() does not rely on + # the actual file size. Make sure a system call does not rely on # file size value except for (maybe) a better throughput / # performance. mock = unittest.mock.Mock() mock.st_size = self.FILESIZE + (100 * 1024 * 1024) with unittest.mock.patch('os.fstat', return_value=mock) as m: with self.get_files() as (src, dst): - shutil._fastcopy_sendfile(src, dst) + self.zerocopy_fun(src, dst) assert m.called self.assertEqual(read_file(TESTFN2, binary=True), self.FILEDATA) def test_blocksize_arg(self): - with unittest.mock.patch('os.sendfile', + with unittest.mock.patch(self.PATCHPOINT, side_effect=ZeroDivisionError) as m: self.assertRaises(ZeroDivisionError, shutil.copyfile, TESTFN, TESTFN2) - blocksize = m.call_args[0][3] + blocksize = m.call_args[0][self.BLOCKSIZE_INDEX] # Make sure file size and the block size arg passed to # sendfile() are the same. self.assertEqual(blocksize, os.path.getsize(TESTFN)) @@ -3333,9 +3329,19 @@ def test_blocksize_arg(self): self.addCleanup(os_helper.unlink, TESTFN2 + '3') self.assertRaises(ZeroDivisionError, shutil.copyfile, TESTFN2, TESTFN2 + '3') - blocksize = m.call_args[0][3] + blocksize = m.call_args[0][self.BLOCKSIZE_INDEX] self.assertEqual(blocksize, 2 ** 23) + +@unittest.skipIf(not SUPPORTS_SENDFILE, 'os.sendfile() not supported') +@unittest.mock.patch.object(shutil, "_USE_CP_COPY_FILE_RANGE", False) +class TestZeroCopySendfile(_ZeroCopyFileLinuxTest, unittest.TestCase): + PATCHPOINT = "os.sendfile" + BLOCKSIZE_INDEX = 3 + + def zerocopy_fun(self, fsrc, fdst): + return shutil._fastcopy_sendfile(fsrc, fdst) + def test_file2file_not_supported(self): # Emulate a case where sendfile() only support file->socket # fds. In such a case copyfile() is supposed to skip the @@ -3358,6 +3364,29 @@ def test_file2file_not_supported(self): shutil._USE_CP_SENDFILE = True +@unittest.skipUnless(shutil._USE_CP_COPY_FILE_RANGE, "os.copy_file_range() not supported") +class TestZeroCopyCopyFileRange(_ZeroCopyFileLinuxTest, unittest.TestCase): + PATCHPOINT = "os.copy_file_range" + BLOCKSIZE_INDEX = 2 + + def zerocopy_fun(self, fsrc, fdst): + return shutil._fastcopy_copy_file_range(fsrc, fdst) + + def test_empty_file(self): + srcname = f"{TESTFN}src" + dstname = f"{TESTFN}dst" + self.addCleanup(lambda: os_helper.unlink(srcname)) + self.addCleanup(lambda: os_helper.unlink(dstname)) + with open(srcname, "wb"): + pass + + with open(srcname, "rb") as src, open(dstname, "wb") as dst: + # _fastcopy_copy_file_range gives up copying empty files due + # to a bug in older Linux. + with self.assertRaises(shutil._GiveupOnFastCopy): + self.zerocopy_fun(src, dst) + + @unittest.skipIf(not MACOS, 'macOS only') class TestZeroCopyMACOS(_ZeroCopyFileTest, unittest.TestCase): PATCHPOINT = "posix._fcopyfile" diff --git a/Misc/ACKS b/Misc/ACKS index a10b0b640970f6..47c8d2b40aafb7 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1972,6 +1972,7 @@ Johannes Vogel Michael Vogt Radu Voicilas Alex Volkov +Illia Volochii Ruben Vorderman Guido Vranken Martijn Vries diff --git a/Misc/NEWS.d/next/Library/2022-05-23-21-23-29.gh-issue-81340.D11RkZ.rst b/Misc/NEWS.d/next/Library/2022-05-23-21-23-29.gh-issue-81340.D11RkZ.rst new file mode 100644 index 00000000000000..49e6305bf83138 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-05-23-21-23-29.gh-issue-81340.D11RkZ.rst @@ -0,0 +1,5 @@ +Use :func:`os.copy_file_range` in :func:`shutil.copy`, :func:`shutil.copy2`, +and :func:`shutil.copyfile` functions by default. An underlying Linux system +call gives filesystems an opportunity to implement the use of copy-on-write +(in case of btrfs and XFS) or server-side copy (in the case of NFS.) +Patch by Illia Volochii. From 632ca568219f86679661bc288f46fa5838102ede Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 3 Feb 2025 12:36:41 +0100 Subject: [PATCH 053/311] gh-129342: Explain how to replace Py_GetProgramName() in C (#129361) --- Doc/c-api/init.rst | 28 +++++++++------ .../c-api-pending-removal-in-3.15.rst | 36 ++++++++++++------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index dc44f3eaf87765..8e3be97dfeefd1 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -622,7 +622,8 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :data:`sys.executable` instead. + Use :c:func:`PyConfig_Get("executable") ` + (:data:`sys.executable`) instead. .. c:function:: wchar_t* Py_GetPrefix() @@ -644,8 +645,10 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :data:`sys.base_prefix` instead, or :data:`sys.prefix` if - :ref:`virtual environments ` need to be handled. + Use :c:func:`PyConfig_Get("base_prefix") ` + (:data:`sys.base_prefix`) instead. Use :c:func:`PyConfig_Get("prefix") + ` (:data:`sys.prefix`) if :ref:`virtual environments + ` need to be handled. .. c:function:: wchar_t* Py_GetExecPrefix() @@ -690,9 +693,11 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :data:`sys.base_exec_prefix` instead, or :data:`sys.exec_prefix` if - :ref:`virtual environments ` need to be handled. - + Use :c:func:`PyConfig_Get("base_exec_prefix") ` + (:data:`sys.base_exec_prefix`) instead. Use + :c:func:`PyConfig_Get("exec_prefix") ` + (:data:`sys.exec_prefix`) if :ref:`virtual environments ` need + to be handled. .. c:function:: wchar_t* Py_GetProgramFullPath() @@ -712,7 +717,8 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :data:`sys.executable` instead. + Use :c:func:`PyConfig_Get("executable") ` + (:data:`sys.executable`) instead. .. c:function:: wchar_t* Py_GetPath() @@ -740,8 +746,8 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :data:`sys.path` instead. - + Use :c:func:`PyConfig_Get("module_search_paths") ` + (:data:`sys.path`) instead. .. c:function:: const char* Py_GetVersion() @@ -926,8 +932,8 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. .. deprecated-removed:: 3.13 3.15 - Get :c:member:`PyConfig.home` or :envvar:`PYTHONHOME` environment - variable instead. + Use :c:func:`PyConfig_Get("home") ` or the + :envvar:`PYTHONHOME` environment variable instead. .. _threads: diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index ac31b3cc8cd451..666a1622dd0b29 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -10,25 +10,35 @@ Pending removal in Python 3.15 :c:func:`PyWeakref_GetRef` on Python 3.12 and older. * :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro: Use :c:type:`wchar_t` instead. -* Python initialization functions: +* Python initialization functions, deprecated in Python 3.13: - * :c:func:`PySys_ResetWarnOptions`: - Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead. - * :c:func:`Py_GetExecPrefix`: - Get :data:`sys.base_exec_prefix` and :data:`sys.exec_prefix` instead. * :c:func:`Py_GetPath`: - Get :data:`sys.path` instead. + Use :c:func:`PyConfig_Get("module_search_paths") ` + (:data:`sys.path`) instead. * :c:func:`Py_GetPrefix`: - Get :data:`sys.base_prefix` and :data:`sys.prefix` instead. + Use :c:func:`PyConfig_Get("base_prefix") ` + (:data:`sys.base_prefix`) instead. Use :c:func:`PyConfig_Get("prefix") + ` (:data:`sys.prefix`) if :ref:`virtual environments + ` need to be handled. + * :c:func:`Py_GetExecPrefix`: + Use :c:func:`PyConfig_Get("base_exec_prefix") ` + (:data:`sys.base_exec_prefix`) instead. Use + :c:func:`PyConfig_Get("exec_prefix") ` + (:data:`sys.exec_prefix`) if :ref:`virtual environments ` need to + be handled. * :c:func:`Py_GetProgramFullPath`: - Get :data:`sys.executable` instead. + Use :c:func:`PyConfig_Get("executable") ` + (:data:`sys.executable`) instead. * :c:func:`Py_GetProgramName`: - Get :data:`sys.executable` instead. + Use :c:func:`PyConfig_Get("executable") ` + (:data:`sys.executable`) instead. * :c:func:`Py_GetPythonHome`: - Get :c:func:`PyConfig_Get("home") ` - or the :envvar:`PYTHONHOME` environment variable instead. + Use :c:func:`PyConfig_Get("home") ` or the + :envvar:`PYTHONHOME` environment variable instead. - See also the :c:func:`PyConfig_Get` function. + The `pythoncapi-compat project + `__ can be used to get + :c:func:`PyConfig_Get` on Python 3.13 and older. * Functions to configure Python's initialization, deprecated in Python 3.11: @@ -40,6 +50,8 @@ Pending removal in Python 3.15 Set :c:member:`PyConfig.program_name` instead. * :c:func:`!Py_SetPythonHome()`: Set :c:member:`PyConfig.home` instead. + * :c:func:`PySys_ResetWarnOptions`: + Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead. The :c:func:`Py_InitializeFromConfig` API should be used with :c:type:`PyConfig` instead. From 39b754a35976924f6df46cd475e889bcf8598ca1 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Mon, 3 Feb 2025 06:46:13 -0500 Subject: [PATCH 054/311] gh-129407: Clarify that a `SystemError` isn't always CPython's fault (#129410) --- Doc/library/exceptions.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index f72b11e34c5c3d..319d261ef3fb4d 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -562,9 +562,13 @@ The following exceptions are the exceptions that are usually raised. Raised when the interpreter finds an internal error, but the situation does not look so serious to cause it to abandon all hope. The associated value is a - string indicating what went wrong (in low-level terms). + string indicating what went wrong (in low-level terms). In :term:`CPython`, + this could be raised by incorrectly using Python's C API, such as returning + a ``NULL`` value without an exception set. - You should report this to the author or maintainer of your Python interpreter. + If you're confident that this exception wasn't your fault, or the fault of + a package you're using, you should report this to the author or maintainer + of your Python interpreter. Be sure to report the version of the Python interpreter (``sys.version``; it is also printed at the start of an interactive Python session), the exact error message (the exception's associated value) and if possible the source of the From 04264a286e5ddfe8ac7423f7376ca34a2ca8b7ba Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 3 Feb 2025 12:55:22 +0100 Subject: [PATCH 055/311] gh-101944: Clarify PyModule_AddObjectRef() documentation (#129433) --- Doc/c-api/module.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index f82a050ab75de0..f71089370152ce 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -523,9 +523,6 @@ state: On success, return ``0``. On error, raise an exception and return ``-1``. - Return ``-1`` if *value* is ``NULL``. It must be called with an exception - raised in this case. - Example usage:: static int @@ -540,6 +537,10 @@ state: return res; } + To be convenient, the function accepts ``NULL`` *value* with an exception + set. In this case, return ``-1`` and just leave the raised exception + unchanged. + The example can also be written without checking explicitly if *obj* is ``NULL``:: From 218f205f2091cb173b5fe3f4b265c102cdf093b3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 3 Feb 2025 13:10:18 +0100 Subject: [PATCH 056/311] gh-129354: Use PyErr_FormatUnraisable() function (#129524) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). Update test_sqlite3 tests. Co-authored-by: Erlend E. Aasland --- Lib/test/test_sqlite3/test_hooks.py | 4 ++-- Lib/test/test_sqlite3/test_userfunctions.py | 20 ++++++++++---------- Lib/test/test_sqlite3/util.py | 16 ++++++++++------ Modules/_sqlite/connection.c | 9 ++++++--- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 49e72f8fcfbcbd..53b8a39bf29a75 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -196,7 +196,7 @@ def progress(): con.execute("select 1 union select 2 union select 3").fetchall() self.assertEqual(action, 0, "progress handler was not cleared") - @with_tracebacks(ZeroDivisionError, name="bad_progress") + @with_tracebacks(ZeroDivisionError, msg_regex="bad_progress") def test_error_in_progress_handler(self): def bad_progress(): 1 / 0 @@ -206,7 +206,7 @@ def bad_progress(): create table foo(a, b) """) - @with_tracebacks(ZeroDivisionError, name="bad_progress") + @with_tracebacks(ZeroDivisionError, msg_regex="bad_progress") def test_error_in_progress_handler_result(self): class BadBool: def __bool__(self): diff --git a/Lib/test/test_sqlite3/test_userfunctions.py b/Lib/test/test_sqlite3/test_userfunctions.py index c6c3db159add64..5bb2eff55ebc8f 100644 --- a/Lib/test/test_sqlite3/test_userfunctions.py +++ b/Lib/test/test_sqlite3/test_userfunctions.py @@ -254,7 +254,7 @@ def test_func_return_nan(self): cur.execute("select returnnan()") self.assertIsNone(cur.fetchone()[0]) - @with_tracebacks(ZeroDivisionError, name="func_raiseexception") + @with_tracebacks(ZeroDivisionError, msg_regex="func_raiseexception") def test_func_exception(self): cur = self.con.cursor() with self.assertRaises(sqlite.OperationalError) as cm: @@ -262,14 +262,14 @@ def test_func_exception(self): cur.fetchone() self.assertEqual(str(cm.exception), 'user-defined function raised exception') - @with_tracebacks(MemoryError, name="func_memoryerror") + @with_tracebacks(MemoryError, msg_regex="func_memoryerror") def test_func_memory_error(self): cur = self.con.cursor() with self.assertRaises(MemoryError): cur.execute("select memoryerror()") cur.fetchone() - @with_tracebacks(OverflowError, name="func_overflowerror") + @with_tracebacks(OverflowError, msg_regex="func_overflowerror") def test_func_overflow_error(self): cur = self.con.cursor() with self.assertRaises(sqlite.DataError): @@ -389,7 +389,7 @@ def test_func_return_too_large_int(self): with self.assertRaisesRegex(sqlite.DataError, msg): cur.execute("select largeint()") - @with_tracebacks(UnicodeEncodeError, "surrogates not allowed", "chr") + @with_tracebacks(UnicodeEncodeError, "surrogates not allowed") def test_func_return_text_with_surrogates(self): cur = self.con.cursor() self.con.create_function("pychr", 1, chr) @@ -641,7 +641,7 @@ def test_aggr_error_on_create(self): with self.assertRaises(sqlite.OperationalError): self.con.create_function("bla", -100, AggrSum) - @with_tracebacks(AttributeError, name="AggrNoStep") + @with_tracebacks(AttributeError, msg_regex="AggrNoStep") def test_aggr_no_step(self): cur = self.con.cursor() with self.assertRaises(sqlite.OperationalError) as cm: @@ -656,7 +656,7 @@ def test_aggr_no_finalize(self): cur.execute("select nofinalize(t) from test") val = cur.fetchone()[0] - @with_tracebacks(ZeroDivisionError, name="AggrExceptionInInit") + @with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInInit") def test_aggr_exception_in_init(self): cur = self.con.cursor() with self.assertRaises(sqlite.OperationalError) as cm: @@ -664,7 +664,7 @@ def test_aggr_exception_in_init(self): val = cur.fetchone()[0] self.assertEqual(str(cm.exception), "user-defined aggregate's '__init__' method raised error") - @with_tracebacks(ZeroDivisionError, name="AggrExceptionInStep") + @with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInStep") def test_aggr_exception_in_step(self): cur = self.con.cursor() with self.assertRaises(sqlite.OperationalError) as cm: @@ -672,7 +672,7 @@ def test_aggr_exception_in_step(self): val = cur.fetchone()[0] self.assertEqual(str(cm.exception), "user-defined aggregate's 'step' method raised error") - @with_tracebacks(ZeroDivisionError, name="AggrExceptionInFinalize") + @with_tracebacks(ZeroDivisionError, msg_regex="AggrExceptionInFinalize") def test_aggr_exception_in_finalize(self): cur = self.con.cursor() with self.assertRaises(sqlite.OperationalError) as cm: @@ -822,11 +822,11 @@ def authorizer_cb(action, arg1, arg2, dbname, source): raise ValueError return sqlite.SQLITE_OK - @with_tracebacks(ValueError, name="authorizer_cb") + @with_tracebacks(ValueError, msg_regex="authorizer_cb") def test_table_access(self): super().test_table_access() - @with_tracebacks(ValueError, name="authorizer_cb") + @with_tracebacks(ValueError, msg_regex="authorizer_cb") def test_column_access(self): super().test_table_access() diff --git a/Lib/test/test_sqlite3/util.py b/Lib/test/test_sqlite3/util.py index 5599823838beea..8643835cca46e2 100644 --- a/Lib/test/test_sqlite3/util.py +++ b/Lib/test/test_sqlite3/util.py @@ -22,15 +22,16 @@ def cx_limit(cx, category=sqlite3.SQLITE_LIMIT_SQL_LENGTH, limit=128): cx.setlimit(category, _prev) -def with_tracebacks(exc, regex="", name=""): +def with_tracebacks(exc, regex="", name="", msg_regex=""): """Convenience decorator for testing callback tracebacks.""" def decorator(func): - _regex = re.compile(regex) if regex else None + exc_regex = re.compile(regex) if regex else None + _msg_regex = re.compile(msg_regex) if msg_regex else None @functools.wraps(func) def wrapper(self, *args, **kwargs): with test.support.catch_unraisable_exception() as cm: # First, run the test with traceback enabled. - with check_tracebacks(self, cm, exc, _regex, name): + with check_tracebacks(self, cm, exc, exc_regex, _msg_regex, name): func(self, *args, **kwargs) # Then run the test with traceback disabled. @@ -40,7 +41,7 @@ def wrapper(self, *args, **kwargs): @contextlib.contextmanager -def check_tracebacks(self, cm, exc, regex, obj_name): +def check_tracebacks(self, cm, exc, exc_regex, msg_regex, obj_name): """Convenience context manager for testing callback tracebacks.""" sqlite3.enable_callback_tracebacks(True) try: @@ -49,9 +50,12 @@ def check_tracebacks(self, cm, exc, regex, obj_name): yield self.assertEqual(cm.unraisable.exc_type, exc) - if regex: + if exc_regex: msg = str(cm.unraisable.exc_value) - self.assertIsNotNone(regex.search(msg)) + self.assertIsNotNone(exc_regex.search(msg), (exc_regex, msg)) + if msg_regex: + msg = cm.unraisable.err_msg + self.assertIsNotNone(msg_regex.search(msg), (msg_regex, msg)) if obj_name: self.assertEqual(cm.unraisable.object.__name__, obj_name) finally: diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 80021ccad4629e..16afd7eada113f 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -497,7 +497,8 @@ connection_finalize(PyObject *self) if (PyErr_ResourceWarning(self, 1, "unclosed database in %R", self)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored while finalizing " + "database connection %R", self); } } } @@ -506,7 +507,8 @@ connection_finalize(PyObject *self) PyErr_Clear(); } else { - PyErr_WriteUnraisable((PyObject *)self); + PyErr_FormatUnraisable("Exception ignored while closing database %R", + self); } } @@ -893,7 +895,8 @@ print_or_clear_traceback(callback_context *ctx) assert(ctx != NULL); assert(ctx->state != NULL); if (ctx->state->enable_callback_tracebacks) { - PyErr_WriteUnraisable(ctx->callable); + PyErr_FormatUnraisable("Exception ignored on sqlite3 callback %R", + ctx->callable); } else { PyErr_Clear(); From 808071b994370886a169cfb97cef1ca3837f89c1 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 3 Feb 2025 12:41:32 +0000 Subject: [PATCH 057/311] GH-128682: Make `PyStackRef_CLOSE` escaping. (GH-129404) --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 12 +- Lib/test/test_generated_cases.py | 20 +- Python/bytecodes.c | 79 +- Python/executor_cases.c.h | 804 ++++++++++++++----- Python/generated_cases.c.h | 879 ++++++++++++++------- Python/optimizer.c | 1 - Python/optimizer_bytecodes.c | 15 +- Python/optimizer_cases.c.h | 46 +- Tools/cases_generator/analyzer.py | 45 +- Tools/cases_generator/generators_common.py | 62 +- Tools/cases_generator/tier2_generator.py | 40 +- 12 files changed, 1348 insertions(+), 657 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index fe791c090120c9..377a885dbb8c34 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2136,7 +2136,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, - [LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, + [LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_METHOD_LAZY_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_EXIT_FLAG }, [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 80f89defb7e2eb..0ed4c7c3a35436 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -154,7 +154,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_TYPE_VERSION] = HAS_EXIT_FLAG, [_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG, [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG, - [_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG, + [_LOAD_ATTR_INSTANCE_VALUE] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_CHECK_ATTR_MODULE_PUSH_KEYS] = HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE_FROM_KEYS] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_CHECK_ATTR_WITH_HINT] = HAS_EXIT_FLAG, @@ -964,7 +964,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CHECK_METHOD_VERSION: return 0; case _EXPAND_METHOD: - return 2 + oparg; + return 0; case _CHECK_IS_NOT_PY_CALLABLE: return 0; case _CALL_NON_PY_GENERAL: @@ -972,7 +972,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: return 0; case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: - return 2 + oparg; + return 0; case _CHECK_PEP_523: return 0; case _CHECK_FUNCTION_EXACT_ARGS: @@ -1036,7 +1036,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _CHECK_METHOD_VERSION_KW: return 0; case _EXPAND_METHOD_KW: - return 3 + oparg; + return 0; case _CHECK_IS_NOT_PY_CALLABLE_KW: return 0; case _CALL_KW_NON_PY: @@ -1062,7 +1062,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _BINARY_OP: return 2; case _SWAP: - return 2 + (oparg-2); + return 0; case _GUARD_IS_TRUE_POP: return 1; case _GUARD_IS_FALSE_POP: @@ -1110,7 +1110,7 @@ int _PyUop_num_popped(int opcode, int oparg) case _DEOPT: return 0; case _ERROR_POP_N: - return oparg; + return 0; case _TIER2_RESUME_CHECK: return 0; default: diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index bb78dd9af83b62..35600ce5486642 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -538,7 +538,9 @@ def test_error_if_plain(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); - if (cond) goto label; + if (cond) { + goto label; + } DISPATCH(); } """ @@ -555,7 +557,9 @@ def test_error_if_plain_with_comment(self): frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); - if (cond) goto label; + if (cond) { + goto label; + } // Comment is ok DISPATCH(); } @@ -582,7 +586,9 @@ def test_error_if_pop(self): right = stack_pointer[-1]; left = stack_pointer[-2]; SPAM(left, right); - if (cond) goto pop_2_label; + if (cond) { + goto pop_2_label; + } res = 0; stack_pointer[-2] = res; stack_pointer += -1; @@ -611,7 +617,9 @@ def test_error_if_pop_with_result(self): right = stack_pointer[-1]; left = stack_pointer[-2]; res = SPAM(left, right); - if (cond) goto pop_2_label; + if (cond) { + goto pop_2_label; + } stack_pointer[-2] = res; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -1392,7 +1400,9 @@ def test_pop_on_error_peeks(self): // THIRD { // Mark j and k as used - if (cond) goto pop_2_error; + if (cond) { + goto pop_2_error; + } } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index effc8e0b6f6578..908eb0c2a698c5 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2130,8 +2130,7 @@ dummy_func( PyStackRef_CLOSE(self_st); self_or_null = PyStackRef_NULL; } - PyStackRef_CLOSE(class_st); - PyStackRef_CLOSE(global_super_st); + DECREF_INPUTS(); attr = PyStackRef_FromPyObjectSteal(attr_o); } @@ -2245,7 +2244,7 @@ dummy_func( attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - DECREF_INPUTS(); + PyStackRef_CLOSE(owner); } macro(LOAD_ATTR_INSTANCE_VALUE) = @@ -3671,15 +3670,14 @@ dummy_func( EXIT_IF(!PyStackRef_IsNull(null[0])); } - op(_EXPAND_METHOD, (callable[1], null[1], unused[oparg] -- method[1], self[1], unused[oparg])) { + op(_EXPAND_METHOD, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - assert(PyStackRef_IsNull(null[0])); - DEAD(null); + assert(PyStackRef_IsNull(self_or_null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); PyStackRef_CLOSE(temp); } @@ -3740,13 +3738,13 @@ dummy_func( EXIT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(callable[0])) != &PyMethod_Type); } - op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], null[1], unused[oparg] -- func[1], self[1], unused[oparg])) { - DEAD(null); + op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) { + assert(PyStackRef_IsNull(self_or_null[0])); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); STAT_INC(CALL, hit); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); PyStackRef_CLOSE(temp); } @@ -4171,8 +4169,9 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } @@ -4183,8 +4182,8 @@ dummy_func( EXIT_IF(meth->ml_flags != METH_O); // CPython promises to check all non-vectorcall function calls. EXIT_IF(tstate->c_recursion_remaining <= 0); - _PyStackRef arg_stackref = args[1]; - _PyStackRef self_stackref = args[0]; + _PyStackRef arg_stackref = arguments[1]; + _PyStackRef self_stackref = arguments[0]; EXIT_IF(!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), method->d_common.d_type)); STAT_INC(CALL, hit); @@ -4195,11 +4194,7 @@ dummy_func( PyStackRef_AsPyObjectBorrow(arg_stackref)); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - PyStackRef_CLOSE(arg_stackref); - DEAD(args); - DEAD(self_or_null); - PyStackRef_CLOSE(callable[0]); + DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4486,15 +4481,14 @@ dummy_func( EXIT_IF(!PyStackRef_IsNull(null[0])); } - op(_EXPAND_METHOD_KW, (callable[1], null[1], unused[oparg], unused -- method[1], self[1], unused[oparg], unused)) { + op(_EXPAND_METHOD_KW, (callable[1], self_or_null[1], unused[oparg], unused -- callable[1], self_or_null[1], unused[oparg], unused)) { + assert(PyStackRef_IsNull(self_or_null[0])); _PyStackRef callable_s = callable[0]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable_s); - - assert(PyStackRef_IsNull(null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); PyStackRef_CLOSE(callable_s); } @@ -4600,7 +4594,8 @@ dummy_func( } } - op(_DO_CALL_FUNCTION_EX, (func_st, unused, callargs_st, kwargs_st -- result)) { + op(_DO_CALL_FUNCTION_EX, (func_st, null, callargs_st, kwargs_st -- result)) { + (void)null; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); // DICT_MERGE is called before this opcode if there are kwargs. @@ -4671,8 +4666,8 @@ dummy_func( result_o = PyObject_Call(func, callargs, kwargs); } PyStackRef_XCLOSE(kwargs_st); - DEAD(kwargs_st); PyStackRef_CLOSE(callargs_st); + DEAD(null); PyStackRef_CLOSE(func_st); ERROR_IF(result_o == NULL, error); result = PyStackRef_FromPyObjectSteal(result_o); @@ -4809,12 +4804,11 @@ dummy_func( macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP; - pure inst(SWAP, (bottom_in, unused[oparg-2], top_in -- - top_out, unused[oparg-2], bottom_out)) { - bottom_out = bottom_in; - DEAD(bottom_in); - top_out = top_in; - DEAD(top_in); + pure inst(SWAP, (bottom[1], unused[oparg-2], top[1] -- + bottom[1], unused[oparg-2], top[1])) { + _PyStackRef temp = bottom[0]; + bottom[0] = top[0]; + top[0] = temp; assert(oparg >= 2); } @@ -5174,7 +5168,8 @@ dummy_func( EXIT_TO_TIER1(); } - tier2 op(_ERROR_POP_N, (target/2, unused[oparg] --)) { + tier2 op(_ERROR_POP_N, (target/2 --)) { + assert(oparg == 0); frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; SYNC_SP(); GOTO_UNWIND(); @@ -5193,18 +5188,18 @@ dummy_func( } label(pop_4_error) { - STACK_SHRINK(1); - goto pop_3_error; + STACK_SHRINK(4); + goto error; } label(pop_3_error) { - STACK_SHRINK(1); - goto pop_2_error; + STACK_SHRINK(3); + goto error; } label(pop_2_error) { - STACK_SHRINK(1); - goto pop_1_error; + STACK_SHRINK(2); + goto error; } label(pop_1_error) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4addfbcf6d6419..c1c2c8fda20a7a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -19,7 +19,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) JUMP_TO_ERROR(); + if (err != 0) { + JUMP_TO_ERROR(); + } } break; } @@ -33,7 +35,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) JUMP_TO_ERROR(); + if (err != 0) { + JUMP_TO_ERROR(); + } } } break; @@ -81,7 +85,7 @@ PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (1) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } value = PyStackRef_DUP(value_s); stack_pointer[0] = value; @@ -422,9 +426,11 @@ * This has the benign side effect that if value is * finalized it will see the location as the FOR_ITER's. */ - PyStackRef_CLOSE(value); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -451,7 +457,11 @@ PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; break; @@ -476,7 +486,11 @@ int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (err < 0) JUMP_TO_ERROR(); + if (err < 0) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = err ? PyStackRef_True : PyStackRef_False; stack_pointer[-1] = res; break; @@ -587,7 +601,11 @@ PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; break; @@ -647,7 +665,11 @@ PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -669,7 +691,11 @@ PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -691,7 +717,11 @@ PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -754,7 +784,11 @@ ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -777,7 +811,11 @@ ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -800,7 +838,11 @@ ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -840,7 +882,11 @@ PyObject *res_o = PyUnicode_Concat(left_o, right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -887,7 +933,11 @@ PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (PyStackRef_IsNull(*target_local)) JUMP_TO_ERROR(); + if (PyStackRef_IsNull(*target_local)) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } #if TIER_ONE // The STORE_FAST is already done. This is done here in tier one, // and during trace projection in tier two: @@ -957,7 +1007,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -993,11 +1047,17 @@ stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(container); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -1031,7 +1091,11 @@ } PyStackRef_CLOSE(v); PyStackRef_CLOSE(container); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -4; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -4; assert(WITHIN_STACK_BOUNDS()); break; @@ -1079,10 +1143,14 @@ Py_INCREF(res_o); #endif PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(list_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -1121,10 +1189,14 @@ STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(str_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -1160,10 +1232,14 @@ assert(res_o != NULL); Py_INCREF(res_o); PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(tuple_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -1192,7 +1268,11 @@ } PyStackRef_CLOSE(dict_st); PyStackRef_CLOSE(sub_st); - if (rc <= 0) JUMP_TO_ERROR(); + if (rc <= 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } // not found or error res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1262,7 +1342,11 @@ list = stack_pointer[-2 - (oparg-1)]; int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), PyStackRef_AsPyObjectSteal(v)); - if (err < 0) JUMP_TO_ERROR(); + if (err < 0) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1279,7 +1363,11 @@ PyStackRef_AsPyObjectBorrow(v)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1299,7 +1387,11 @@ PyStackRef_CLOSE(v); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); break; @@ -1346,10 +1438,10 @@ assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(list_st); Py_DECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); break; @@ -1373,10 +1465,14 @@ PyStackRef_AsPyObjectSteal(sub), PyStackRef_AsPyObjectSteal(value)); stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(dict_st); - if (err) JUMP_TO_ERROR(); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_ERROR(); + } break; } @@ -1392,7 +1488,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; @@ -1408,7 +1508,11 @@ PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; break; @@ -1429,7 +1533,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value2_st); PyStackRef_CLOSE(value1_st); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -1481,13 +1589,19 @@ type->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(obj); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); iter_o = (*getter)(obj_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(obj); - if (iter_o == NULL) JUMP_TO_ERROR(); + if (iter_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } if (Py_TYPE(iter_o)->tp_as_async == NULL || Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { stack_pointer += -1; @@ -1499,7 +1613,7 @@ Py_TYPE(iter_o)->tp_name); Py_DECREF(iter_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; @@ -1532,7 +1646,11 @@ PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (iter_o == NULL) JUMP_TO_ERROR(); + if (iter_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; break; @@ -1653,13 +1771,15 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) JUMP_TO_ERROR(); + if (err < 0) { + JUMP_TO_ERROR(); + } if (bc_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetString(tstate, PyExc_NameError, "__build_class__ not found"); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } bc = PyStackRef_FromPyObjectSteal(bc_o); stack_pointer[0] = bc; @@ -1681,7 +1801,9 @@ "no locals found when storing %R", name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } if (PyDict_CheckExact(ns)) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1694,7 +1816,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(v); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1738,7 +1864,11 @@ int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); - if (res == 0) JUMP_TO_ERROR(); + if (res == 0) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); break; @@ -1842,7 +1972,11 @@ int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); - if (res == 0) JUMP_TO_ERROR(); + if (res == 0) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += (oparg & 0xFF) + (oparg >> 8); assert(WITHIN_STACK_BOUNDS()); break; @@ -1861,7 +1995,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); PyStackRef_CLOSE(owner); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; @@ -1876,7 +2014,11 @@ int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1891,7 +2033,11 @@ int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - if (err) JUMP_TO_ERROR(); + if (err) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -1925,7 +2071,7 @@ _PyErr_SetString(tstate, PyExc_SystemError, "no locals found"); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } locals = PyStackRef_FromPyObjectNew(l); stack_pointer[0] = locals; @@ -1943,7 +2089,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *v_o = _PyEval_LoadName(tstate, frame, name); stack_pointer = _PyFrame_GetStackPointer(frame); - if (v_o == NULL) JUMP_TO_ERROR(); + if (v_o == NULL) { + JUMP_TO_ERROR(); + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; @@ -1959,7 +2107,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*res)) JUMP_TO_ERROR(); + if (PyStackRef_IsNull(*res)) { + JUMP_TO_ERROR(); + } stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; @@ -2103,7 +2253,7 @@ PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (1) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } SETLOCAL(oparg, PyStackRef_NULL); break; @@ -2167,9 +2317,15 @@ JUMP_TO_ERROR(); } } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(class_dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); value = PyStackRef_FromPyObjectSteal(value_o); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -2182,7 +2338,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } value = PyStackRef_FromPyObjectSteal(value_o); stack_pointer[0] = value; @@ -2230,14 +2386,20 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(pieces[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(pieces[_i]); } - if (str_o == NULL) JUMP_TO_ERROR(); + if (str_o == NULL) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } str = PyStackRef_FromPyObjectSteal(str_o); stack_pointer[-oparg] = str; stack_pointer += 1 - oparg; @@ -2303,7 +2465,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(iterable_st); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } assert(Py_IsNone(none_val)); PyStackRef_CLOSE(iterable_st); @@ -2323,7 +2487,11 @@ PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (err < 0) JUMP_TO_ERROR(); + if (err < 0) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); break; @@ -2341,7 +2509,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(values[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } int err = 0; for (int i = 0; i < oparg; i++) { @@ -2360,7 +2530,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } set = PyStackRef_FromPyObjectSteal(set_o); stack_pointer[-oparg] = set; @@ -2379,7 +2549,9 @@ for (int _i = oparg*2; --_i >= 0;) { PyStackRef_CLOSE(values[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -oparg*2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *map_o = _PyDict_FromItems( @@ -2391,7 +2563,11 @@ for (int _i = oparg*2; --_i >= 0;) { PyStackRef_CLOSE(values[_i]); } - if (map_o == NULL) JUMP_TO_ERROR(); + if (map_o == NULL) { + stack_pointer += -oparg*2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } map = PyStackRef_FromPyObjectSteal(map_o); stack_pointer[-oparg*2] = map; stack_pointer += 1 - oparg*2; @@ -2406,24 +2582,30 @@ _PyErr_Format(tstate, PyExc_SystemError, "no locals found when setting up annotations"); stack_pointer = _PyFrame_GetStackPointer(frame); - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } /* check if __annotations__ in locals()... */ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) JUMP_TO_ERROR(); + if (err < 0) { + JUMP_TO_ERROR(); + } if (ann_dict == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); ann_dict = PyDict_New(); stack_pointer = _PyFrame_GetStackPointer(frame); - if (ann_dict == NULL) JUMP_TO_ERROR(); + if (ann_dict == NULL) { + JUMP_TO_ERROR(); + } _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); Py_DECREF(ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) JUMP_TO_ERROR(); + if (err) { + JUMP_TO_ERROR(); + } } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2456,7 +2638,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(update); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } PyStackRef_CLOSE(update); stack_pointer += -1; @@ -2483,7 +2667,9 @@ _PyEval_FormatKwargsError(tstate, callable_o, update_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(update); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } PyStackRef_CLOSE(update); stack_pointer += -1; @@ -2510,7 +2696,11 @@ PyStackRef_AsPyObjectSteal(value) ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) JUMP_TO_ERROR(); + if (err != 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; @@ -2547,7 +2737,11 @@ PyStackRef_CLOSE(global_super_st); PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); - if (attr == NULL) JUMP_TO_ERROR(); + if (attr == NULL) { + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } attr_st = PyStackRef_FromPyObjectSteal(attr); stack_pointer[-3] = attr_st; stack_pointer += -2; @@ -2591,11 +2785,17 @@ if (method_found) { self_or_null = self_st; // transfer ownership } else { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self_st); + stack_pointer = _PyFrame_GetStackPointer(frame); self_or_null = PyStackRef_NULL; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); } - PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(global_super_st); + PyStackRef_CLOSE(class_st); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[-3] = attr; stack_pointer[-2] = self_or_null; @@ -2635,7 +2835,11 @@ meth | NULL | arg1 | ... | argN */ PyStackRef_CLOSE(owner); - if (attr_o == NULL) JUMP_TO_ERROR(); + if (attr_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } self_or_null[0] = PyStackRef_NULL; } } @@ -2645,7 +2849,11 @@ attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); - if (attr_o == NULL) JUMP_TO_ERROR(); + if (attr_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } } attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[-1] = attr; @@ -2724,8 +2932,10 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -2784,8 +2994,10 @@ attr = PyStackRef_FromPyObjectSteal(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - PyStackRef_CLOSE(owner); stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -3001,10 +3213,10 @@ _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); break; @@ -3071,10 +3283,10 @@ // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. STAT_INC(STORE_ATTR, hit); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); break; @@ -3096,10 +3308,10 @@ PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); break; @@ -3120,7 +3332,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } if (oparg & 16) { stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -3128,7 +3344,9 @@ int res_bool = PyObject_IsTrue(res_o); Py_DECREF(res_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_bool < 0) JUMP_TO_ERROR(); + if (res_bool < 0) { + JUMP_TO_ERROR(); + } res = res_bool ? PyStackRef_True : PyStackRef_False; } else { @@ -3256,7 +3474,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) JUMP_TO_ERROR(); + if (res < 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; stack_pointer += -1; @@ -3284,7 +3506,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) JUMP_TO_ERROR(); + if (res < 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; stack_pointer += -1; @@ -3311,7 +3537,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) JUMP_TO_ERROR(); + if (res < 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; stack_pointer += -1; @@ -3334,7 +3564,9 @@ if (err < 0) { PyStackRef_CLOSE(exc_value_st); PyStackRef_CLOSE(match_type_st); - if (true) JUMP_TO_ERROR(); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } PyObject *match_o = NULL; PyObject *rest_o = NULL; @@ -3344,9 +3576,17 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(exc_value_st); PyStackRef_CLOSE(match_type_st); - if (res < 0) JUMP_TO_ERROR(); + if (res < 0) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } assert((match_o == NULL) == (rest_o == NULL)); - if (match_o == NULL) JUMP_TO_ERROR(); + if (match_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } if (!Py_IsNone(match_o)) { stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -3377,7 +3617,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { PyStackRef_CLOSE(right); - if (true) JUMP_TO_ERROR(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyErr_GivenExceptionMatches(left_o, right_o); @@ -3403,7 +3645,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(level); PyStackRef_CLOSE(fromlist); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -3420,7 +3666,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -3455,9 +3703,13 @@ _PyFrame_SetStackPointer(frame, stack_pointer); Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (len_i < 0) JUMP_TO_ERROR(); + if (len_i < 0) { + JUMP_TO_ERROR(); + } PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) JUMP_TO_ERROR(); + if (len_o == NULL) { + JUMP_TO_ERROR(); + } len = PyStackRef_FromPyObjectSteal(len_o); stack_pointer[0] = len; stack_pointer += 1; @@ -3491,7 +3743,11 @@ attrs = PyStackRef_FromPyObjectSteal(attrs_o); } else { - if (_PyErr_Occurred(tstate)) JUMP_TO_ERROR(); + if (_PyErr_Occurred(tstate)) { + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } // Error! attrs = PyStackRef_None; // Failure! } @@ -3536,7 +3792,9 @@ PyObject *values_or_none_o = _PyEval_MatchKeys(tstate, PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (values_or_none_o == NULL) JUMP_TO_ERROR(); + if (values_or_none_o == NULL) { + JUMP_TO_ERROR(); + } values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); stack_pointer[0] = values_or_none; stack_pointer += 1; @@ -3553,7 +3811,11 @@ PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (iter_o == NULL) JUMP_TO_ERROR(); + if (iter_o == NULL) { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; break; @@ -3773,7 +4035,9 @@ r->start = value + r->step; r->len--; PyObject *res = PyLong_FromLong(value); - if (res == NULL) JUMP_TO_ERROR(); + if (res == NULL) { + JUMP_TO_ERROR(); + } next = PyStackRef_FromPyObjectSteal(res); stack_pointer[0] = next; stack_pointer += 1; @@ -3833,7 +4097,7 @@ Py_TYPE(owner_o)->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); } - if (true) JUMP_TO_ERROR(); + JUMP_TO_ERROR(); } attr = PyStackRef_FromPyObjectSteal(attr_o); self_or_null = self_or_null_o == NULL ? @@ -3886,7 +4150,9 @@ PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -4069,7 +4335,9 @@ PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } break; } @@ -4175,23 +4443,21 @@ } case _EXPAND_METHOD: { - _PyStackRef *null; + _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *method; - _PyStackRef *self; oparg = CURRENT_OPARG(); - null = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - method = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - assert(PyStackRef_IsNull(null[0])); + assert(PyStackRef_IsNull(self_or_null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -4238,7 +4504,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -4253,7 +4521,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -4279,21 +4551,20 @@ } case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: { - _PyStackRef *null; + _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *func; - _PyStackRef *self; oparg = CURRENT_OPARG(); - null = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - func = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; + assert(PyStackRef_IsNull(self_or_null[0])); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); STAT_INC(CALL, hit); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -4525,10 +4796,12 @@ } STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectSteal(Py_NewRef(Py_TYPE(arg_o))); - PyStackRef_CLOSE(arg); stack_pointer[-3] = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(arg); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -4556,11 +4829,17 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Str(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -4589,11 +4868,17 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -4645,7 +4930,9 @@ self[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; init[0] = PyStackRef_FromPyObjectNew(init_func); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -4738,7 +5025,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); @@ -4749,7 +5038,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -4799,12 +5092,20 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -4844,7 +5145,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( @@ -4859,7 +5162,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -4906,7 +5213,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); @@ -4918,7 +5227,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -4965,11 +5278,17 @@ if (res_o == NULL) { GOTO_ERROR(error); } + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -5050,17 +5369,25 @@ STAT_INC(CALL, hit); int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); UNLOCK_OBJECT(self_o); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable); - if (err) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_ERROR(); + } #if TIER_ONE // Skip the following POP_TOP. This is done here in tier one, and // during trace projection in tier two: assert(next_instr->op.code == POP_TOP); SKIP_OVER(1); #endif - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); break; } @@ -5075,8 +5402,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -5098,8 +5426,8 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - _PyStackRef arg_stackref = args[1]; - _PyStackRef self_stackref = args[0]; + _PyStackRef arg_stackref = arguments[1]; + _PyStackRef self_stackref = arguments[0]; if (!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), method->d_common.d_type)) { UOP_STAT_INC(uopcode, miss); @@ -5115,10 +5443,16 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - PyStackRef_CLOSE(arg_stackref); PyStackRef_CLOSE(callable[0]); - if (res_o == NULL) JUMP_TO_ERROR(); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -5167,7 +5501,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = @@ -5181,7 +5517,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -5238,12 +5578,20 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } @@ -5289,7 +5637,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (true) JUMP_TO_ERROR(); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFast cfunc = @@ -5303,7 +5653,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; @@ -5335,7 +5689,9 @@ PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } kwnames_out = kwnames_in; stack_pointer[-1] = kwnames_out; @@ -5374,10 +5730,14 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. - stack_pointer += -3 - oparg; + stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { JUMP_TO_ERROR(); @@ -5436,23 +5796,21 @@ } case _EXPAND_METHOD_KW: { - _PyStackRef *null; + _PyStackRef *self_or_null; _PyStackRef *callable; - _PyStackRef *method; - _PyStackRef *self; oparg = CURRENT_OPARG(); - null = &stack_pointer[-2 - oparg]; + self_or_null = &stack_pointer[-2 - oparg]; callable = &stack_pointer[-3 - oparg]; - method = &stack_pointer[-3 - oparg]; - self = &stack_pointer[-2 - oparg]; + assert(PyStackRef_IsNull(self_or_null[0])); _PyStackRef callable_s = callable[0]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable_s); - assert(PyStackRef_IsNull(null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable_s); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -5502,7 +5860,9 @@ PyStackRef_CLOSE(args[_i]); } PyStackRef_CLOSE(kwnames); - if (true) JUMP_TO_ERROR(); + stack_pointer += -3 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); @@ -5512,7 +5872,11 @@ positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); PyStackRef_CLOSE(callable[0]); @@ -5520,10 +5884,14 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-3 - oparg] = res; - stack_pointer += -2 - oparg; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); break; } @@ -5558,8 +5926,14 @@ JUMP_TO_ERROR(); } kwargs_out = kwargs_in; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callargs); + stack_pointer = _PyFrame_GetStackPointer(frame); tuple = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer += 2; + assert(WITHIN_STACK_BOUNDS()); } stack_pointer[-2] = tuple; stack_pointer[-1] = kwargs_out; @@ -5577,12 +5951,20 @@ PyFunctionObject *func_obj = (PyFunctionObject *) PyFunction_New(codeobj, GLOBALS()); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(codeobj_st); - if (func_obj == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (func_obj == NULL) { + JUMP_TO_ERROR(); + } _PyFunction_SetVersion( func_obj, ((PyCodeObject *)codeobj)->co_version); func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); - stack_pointer[-1] = func; + stack_pointer[0] = func; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -5615,7 +5997,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); stack_pointer = _PyFrame_GetStackPointer(frame); - if (gen == NULL) JUMP_TO_ERROR(); + if (gen == NULL) { + JUMP_TO_ERROR(); + } assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; @@ -5650,7 +6034,11 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - if (slice_o == NULL) JUMP_TO_ERROR(); + if (slice_o == NULL) { + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } slice = PyStackRef_FromPyObjectSteal(slice_o); stack_pointer[-oparg] = slice; stack_pointer += 1 - oparg; @@ -5669,10 +6057,18 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); - if (result_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_ERROR(); + } result = PyStackRef_FromPyObjectSteal(result_o); - stack_pointer[-1] = result; + stack_pointer[0] = result; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -5687,14 +6083,24 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Format(value_o, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); - if (res_o == NULL) JUMP_TO_ERROR(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); } else { res = value; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -5709,7 +6115,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); PyStackRef_CLOSE(fmt_spec); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -5745,7 +6155,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(lhs); PyStackRef_CLOSE(rhs); - if (res_o == NULL) JUMP_TO_ERROR(); + if (res_o == NULL) { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_ERROR(); + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -5754,18 +6168,15 @@ } case _SWAP: { - _PyStackRef top_in; - _PyStackRef bottom_in; - _PyStackRef top_out; - _PyStackRef bottom_out; + _PyStackRef *top; + _PyStackRef *bottom; oparg = CURRENT_OPARG(); - top_in = stack_pointer[-1]; - bottom_in = stack_pointer[-2 - (oparg-2)]; - bottom_out = bottom_in; - top_out = top_in; + top = &stack_pointer[-1]; + bottom = &stack_pointer[-2 - (oparg-2)]; + _PyStackRef temp = bottom[0]; + bottom[0] = top[0]; + top[0] = temp; assert(oparg >= 2); - stack_pointer[-2 - (oparg-2)] = top_out; - stack_pointer[-1] = bottom_out; break; } @@ -5818,9 +6229,11 @@ val = stack_pointer[-1]; int is_none = PyStackRef_IsNone(val); if (!is_none) { - PyStackRef_CLOSE(val); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(val); + stack_pointer = _PyFrame_GetStackPointer(frame); if (1) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -5835,9 +6248,11 @@ _PyStackRef val; val = stack_pointer[-1]; int is_none = PyStackRef_IsNone(val); - PyStackRef_CLOSE(val); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(val); + stack_pointer = _PyFrame_GetStackPointer(frame); if (is_none) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -5975,9 +6390,15 @@ _PyStackRef value; pop = stack_pointer[-1]; PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(pop); + stack_pointer = _PyFrame_GetStackPointer(frame); value = PyStackRef_FromPyObjectImmortal(ptr); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); break; } @@ -6144,8 +6565,7 @@ case _ERROR_POP_N: { oparg = CURRENT_OPARG(); uint32_t target = (uint32_t)CURRENT_OPERAND0(); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); + assert(oparg == 0); _PyFrame_SetStackPointer(frame, stack_pointer); frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 38ea63d71ab044..44a78c410485c0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -57,7 +57,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(lhs); PyStackRef_CLOSE(rhs); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -95,7 +97,9 @@ ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -132,7 +136,9 @@ PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -169,7 +175,9 @@ PyObject *res_o = PyUnicode_Concat(left_o, right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -275,7 +283,9 @@ PyUnicode_Append(&temp, right_o); *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (PyStackRef_IsNull(*target_local)) goto pop_2_error; + if (PyStackRef_IsNull(*target_local)) { + goto pop_2_error; + } #if TIER_ONE // The STORE_FAST is already done. This is done here in tier one, // and during trace projection in tier two: @@ -317,7 +327,9 @@ ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -354,7 +366,9 @@ PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -392,7 +406,9 @@ ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -429,7 +445,9 @@ PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -478,12 +496,18 @@ stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); } + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(container); - if (res_o == NULL) goto pop_3_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -526,7 +550,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); } stack_pointer[-2] = res; @@ -561,7 +587,9 @@ } PyStackRef_CLOSE(dict_st); PyStackRef_CLOSE(sub_st); - if (rc <= 0) goto pop_2_error; + if (rc <= 0) { + goto pop_2_error; + } // not found or error res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -661,10 +689,14 @@ Py_INCREF(res_o); #endif PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(list_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -693,10 +725,14 @@ STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(str_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -725,10 +761,14 @@ assert(res_o != NULL); Py_INCREF(res_o); PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(tuple_st); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -763,11 +803,9 @@ for (int _i = oparg*2; --_i >= 0;) { PyStackRef_CLOSE(values[_i]); } - { - stack_pointer += -oparg*2; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -oparg*2; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *map_o = _PyDict_FromItems( @@ -805,11 +843,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(values[_i]); } - { - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } int err = 0; for (int i = 0; i < oparg; i++) { @@ -875,11 +911,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(pieces[_i]); } - { - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); @@ -969,7 +1003,9 @@ PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } } // _DO_CALL @@ -1017,11 +1053,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -1075,7 +1109,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -1134,7 +1170,9 @@ self[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; init[0] = PyStackRef_FromPyObjectNew(init_func); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } // _CREATE_INIT_FRAME { @@ -1193,8 +1231,6 @@ static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef *null; - _PyStackRef *func; - _PyStackRef *self; _PyStackRef *self_or_null; _PyStackRef *args; _PyInterpreterFrame *new_frame; @@ -1212,19 +1248,20 @@ } // _INIT_CALL_BOUND_METHOD_EXACT_ARGS { - func = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; + self_or_null = null; + assert(PyStackRef_IsNull(self_or_null[0])); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); STAT_INC(CALL, hit); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - func[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } // flush // _CHECK_FUNCTION_VERSION { - callable = &stack_pointer[-2 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); DEOPT_IF(!PyFunction_Check(callable_o), CALL); @@ -1233,7 +1270,6 @@ } // _CHECK_FUNCTION_EXACT_ARGS { - self_or_null = &stack_pointer[-1 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); assert(PyFunction_Check(callable_o)); PyFunctionObject *func = (PyFunctionObject *)callable_o; @@ -1297,8 +1333,6 @@ static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef *null; - _PyStackRef *method; - _PyStackRef *self; _PyStackRef *self_or_null; _PyStackRef *args; _PyInterpreterFrame *new_frame; @@ -1321,23 +1355,22 @@ } // _EXPAND_METHOD { - method = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; + self_or_null = null; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - assert(PyStackRef_IsNull(null[0])); + assert(PyStackRef_IsNull(self_or_null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); _PyStackRef temp = callable[0]; - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } // flush // _PY_FRAME_GENERAL { args = &stack_pointer[-oparg]; - self_or_null = &stack_pointer[-1 - oparg]; - callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; @@ -1423,11 +1456,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); @@ -1456,7 +1487,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -1503,11 +1536,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( @@ -1540,7 +1571,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -1591,11 +1624,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); @@ -1625,7 +1656,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -1673,11 +1706,15 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); goto error; } res = PyStackRef_FromPyObjectSteal(res_o); @@ -1687,19 +1724,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 1 + oparg; + if (err != 0) { + goto error; + } + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -1717,6 +1756,7 @@ _PyStackRef tuple; _PyStackRef kwargs_out; _PyStackRef func_st; + _PyStackRef null; _PyStackRef callargs_st; _PyStackRef kwargs_st; _PyStackRef result; @@ -1744,15 +1784,23 @@ goto error; } kwargs_out = kwargs_in; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callargs); + stack_pointer = _PyFrame_GetStackPointer(frame); tuple = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer += 2; + assert(WITHIN_STACK_BOUNDS()); } } // _DO_CALL_FUNCTION_EX { kwargs_st = kwargs_out; callargs_st = tuple; + null = stack_pointer[-3]; func_st = func; + (void)null; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); // DICT_MERGE is called before this opcode if there are kwargs. // It converts all dict subtypes in kwargs into regular dicts. @@ -1810,7 +1858,7 @@ Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); - stack_pointer += -3; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( @@ -1818,7 +1866,7 @@ nargs, callargs, kwargs, frame); stack_pointer = _PyFrame_GetStackPointer(frame); // Need to sync the stack since we exit with DISPATCH_INLINED. - stack_pointer += -1; + stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); if (new_frame == NULL) { goto error; @@ -1837,12 +1885,24 @@ result_o = PyObject_Call(func, callargs, kwargs); stack_pointer = _PyFrame_GetStackPointer(frame); } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(kwargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(func_st); - if (result_o == NULL) goto pop_4_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + goto error; + } result = PyStackRef_FromPyObjectSteal(result_o); } // _CHECK_PERIODIC @@ -1850,19 +1910,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-4] = result; - stack_pointer += -3; + stack_pointer[0] = result; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 3; + if (err != 0) { + goto error; + } + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-4] = result; - stack_pointer += -3; + stack_pointer[0] = result; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -1879,7 +1941,9 @@ PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) goto pop_1_error; + if (res_o == NULL) { + goto pop_1_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; DISPATCH(); @@ -1902,7 +1966,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value2_st); PyStackRef_CLOSE(value1_st); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -2005,7 +2071,9 @@ PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } kwnames_out = kwnames_in; } @@ -2039,9 +2107,13 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); // Sync stack explicitly since we leave using DISPATCH_INLINED(). - stack_pointer += -3 - oparg; + stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. @@ -2061,11 +2133,9 @@ PyStackRef_CLOSE(args[_i]); } PyStackRef_CLOSE(kwnames); - { - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -3 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } stack_pointer[-1] = kwnames; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2124,8 +2194,6 @@ _PyStackRef *callable; _PyStackRef *null; _PyStackRef kwnames; - _PyStackRef *method; - _PyStackRef *self; _PyStackRef *self_or_null; _PyStackRef *args; _PyInterpreterFrame *new_frame; @@ -2148,24 +2216,23 @@ } // _EXPAND_METHOD_KW { - method = &stack_pointer[-3 - oparg]; - self = &stack_pointer[-2 - oparg]; + self_or_null = null; + assert(PyStackRef_IsNull(self_or_null[0])); _PyStackRef callable_s = callable[0]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable_s); - assert(PyStackRef_IsNull(null[0])); assert(Py_TYPE(callable_o) == &PyMethod_Type); - self[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - method[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(method[0])); + self_or_null[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + callable[0] = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable[0])); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable_s); + stack_pointer = _PyFrame_GetStackPointer(frame); } // flush // _PY_FRAME_KW { kwnames = stack_pointer[-1]; args = &stack_pointer[-1 - oparg]; - self_or_null = &stack_pointer[-2 - oparg]; - callable = &stack_pointer[-3 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); // oparg counts all of the args, but *not* self: int total_args = oparg; @@ -2185,10 +2252,14 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. - stack_pointer += -3 - oparg; + stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { goto error; @@ -2265,11 +2336,9 @@ PyStackRef_CLOSE(args[_i]); } PyStackRef_CLOSE(kwnames); - { - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -3 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); @@ -2279,7 +2348,11 @@ positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); PyStackRef_CLOSE(callable[0]); @@ -2288,7 +2361,7 @@ PyStackRef_CLOSE(args[_i]); } if (res_o == NULL) { - stack_pointer += -3 - oparg; + stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); goto error; } @@ -2299,19 +2372,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-3 - oparg] = res; - stack_pointer += -2 - oparg; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 2 + oparg; + if (err != 0) { + goto error; + } + stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-3 - oparg] = res; - stack_pointer += -2 - oparg; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -2365,10 +2440,14 @@ arguments, positional_args, kwnames_o, frame ); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. - stack_pointer += -3 - oparg; + stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { goto error; @@ -2440,11 +2519,17 @@ if (res_o == NULL) { GOTO_ERROR(error); } + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -2473,17 +2558,25 @@ STAT_INC(CALL, hit); int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); UNLOCK_OBJECT(self_o); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable); - if (err) goto pop_3_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + goto error; + } #if TIER_ONE // Skip the following POP_TOP. This is done here in tier one, and // during trace projection in tier two: assert(next_instr->op.code == POP_TOP); SKIP_OVER(1); #endif - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -2526,11 +2619,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFast cfunc = @@ -2562,7 +2653,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -2612,11 +2705,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = @@ -2648,7 +2739,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -2700,11 +2793,15 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); goto error; } res = PyStackRef_FromPyObjectSteal(res_o); @@ -2714,19 +2811,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 1 + oparg; + if (err != 0) { + goto error; + } + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -2749,8 +2848,9 @@ callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); int total_args = oparg; + _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - args--; + arguments--; total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -2760,8 +2860,8 @@ DEOPT_IF(meth->ml_flags != METH_O, CALL); // CPython promises to check all non-vectorcall function calls. DEOPT_IF(tstate->c_recursion_remaining <= 0, CALL); - _PyStackRef arg_stackref = args[1]; - _PyStackRef self_stackref = args[0]; + _PyStackRef arg_stackref = arguments[1]; + _PyStackRef self_stackref = arguments[0]; DEOPT_IF(!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), method->d_common.d_type), CALL); STAT_INC(CALL, hit); @@ -2774,9 +2874,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - PyStackRef_CLOSE(arg_stackref); PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2795,7 +2897,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -2846,11 +2950,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -2883,7 +2985,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -3080,8 +3184,14 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Str(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); - if (res_o == NULL) goto pop_3_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); } // _CHECK_PERIODIC @@ -3089,19 +3199,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 2; + if (err != 0) { + goto error; + } + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -3131,8 +3243,14 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg); - if (res_o == NULL) goto pop_3_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); } // _CHECK_PERIODIC @@ -3140,19 +3258,21 @@ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); QSBR_QUIESCENT_STATE(tstate); if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; - stack_pointer += 2; + if (err != 0) { + goto error; + } + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); } } - stack_pointer[-3] = res; - stack_pointer += -2; + stack_pointer[0] = res; + stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -3178,10 +3298,12 @@ DEOPT_IF(callable_o != (PyObject *)&PyType_Type, CALL); STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectSteal(Py_NewRef(Py_TYPE(arg_o))); - PyStackRef_CLOSE(arg); stack_pointer[-3] = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(arg); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -3213,9 +3335,13 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(exc_value_st); PyStackRef_CLOSE(match_type_st); - if (res < 0) goto pop_2_error; + if (res < 0) { + goto pop_2_error; + } assert((match_o == NULL) == (rest_o == NULL)); - if (match_o == NULL) goto pop_2_error; + if (match_o == NULL) { + goto pop_2_error; + } if (!Py_IsNone(match_o)) { stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -3338,7 +3464,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } if (oparg & 16) { stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -3346,7 +3474,9 @@ int res_bool = PyObject_IsTrue(res_o); Py_DECREF(res_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_bool < 0) goto error; + if (res_bool < 0) { + goto error; + } res = res_bool ? PyStackRef_True : PyStackRef_False; } else { @@ -3517,7 +3647,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) goto pop_2_error; + if (res < 0) { + goto pop_2_error; + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; } stack_pointer[-2] = b; @@ -3546,7 +3678,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) goto pop_2_error; + if (res < 0) { + goto pop_2_error; + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; stack_pointer += -1; @@ -3575,7 +3709,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); - if (res < 0) goto pop_2_error; + if (res < 0) { + goto pop_2_error; + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; stack_pointer += -1; @@ -3596,10 +3732,18 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); - if (result_o == NULL) goto pop_1_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + goto error; + } result = PyStackRef_FromPyObjectSteal(result_o); - stack_pointer[-1] = result; + stack_pointer[0] = result; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -3647,7 +3791,9 @@ int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); - if (err) goto pop_1_error; + if (err) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -3757,7 +3903,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (err) goto pop_2_error; + if (err) { + goto pop_2_error; + } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -3866,9 +4014,11 @@ * This has the benign side effect that if value is * finalized it will see the location as the FOR_ITER's. */ - PyStackRef_CLOSE(value); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -3968,14 +4118,24 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Format(value_o, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); - if (res_o == NULL) goto pop_1_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); } else { res = value; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); } - stack_pointer[-1] = res; + stack_pointer[0] = res; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -3993,7 +4153,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); PyStackRef_CLOSE(fmt_spec); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -4196,7 +4358,9 @@ r->start = value + r->step; r->len--; PyObject *res = PyLong_FromLong(value); - if (res == NULL) goto error; + if (res == NULL) { + goto error; + } next = PyStackRef_FromPyObjectSteal(res); } stack_pointer[0] = next; @@ -4281,7 +4445,9 @@ iter_o = (*getter)(obj_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(obj); - if (iter_o == NULL) goto pop_1_error; + if (iter_o == NULL) { + goto pop_1_error; + } if (Py_TYPE(iter_o)->tp_as_async == NULL || Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { stack_pointer += -1; @@ -4331,7 +4497,9 @@ PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (iter_o == NULL) goto pop_1_error; + if (iter_o == NULL) { + goto pop_1_error; + } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; DISPATCH(); @@ -4349,7 +4517,9 @@ PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (iter_o == NULL) goto pop_1_error; + if (iter_o == NULL) { + goto pop_1_error; + } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; DISPATCH(); @@ -4366,9 +4536,13 @@ _PyFrame_SetStackPointer(frame, stack_pointer); Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (len_i < 0) goto error; + if (len_i < 0) { + goto error; + } PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) goto error; + if (len_o == NULL) { + goto error; + } len = PyStackRef_FromPyObjectSteal(len_o); stack_pointer[0] = len; stack_pointer += 1; @@ -4430,7 +4604,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) goto error; + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -4455,7 +4631,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(level); PyStackRef_CLOSE(fromlist); - if (res_o == NULL) goto pop_2_error; + if (res_o == NULL) { + goto pop_2_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; stack_pointer += -1; @@ -4489,7 +4667,9 @@ PyObject *method = ((PyMethodObject *)callable_o)->im_func; _PyStackRef temp = callable[0]; func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); } } // _MONITOR_CALL @@ -4517,7 +4697,9 @@ frame, this_instr, function, arg0 ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; + if (err) { + goto error; + } } // _DO_CALL { @@ -4563,11 +4745,9 @@ for (int _i = oparg; --_i >= 0;) { PyStackRef_CLOSE(args[_i]); } - { - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - goto error; - } + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -4621,7 +4801,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); } @@ -4659,7 +4841,9 @@ tstate, PY_MONITORING_EVENT_CALL, frame, this_instr, function, arg); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; + if (err) { + goto error; + } PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); goto PREDICTED_CALL_KW; } @@ -4709,10 +4893,12 @@ } } val = value; - PyStackRef_CLOSE(receiver); stack_pointer[-2] = val; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(receiver); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -4762,7 +4948,9 @@ int next_opcode = _Py_call_instrumentation_instruction( tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); - if (next_opcode < 0) goto error; + if (next_opcode < 0) { + goto error; + } next_instr = this_instr; if (_PyOpcode_Caches[next_opcode]) { PAUSE_ADAPTIVE_COUNTER(next_instr[1].counter); @@ -4786,7 +4974,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } // _MONITOR_JUMP_BACKWARD @@ -4875,9 +5065,11 @@ _PyStackRef iter; iter = stack_pointer[-1]; INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); - PyStackRef_CLOSE(iter); stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iter); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -4910,7 +5102,9 @@ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } else { + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } @@ -4925,7 +5119,9 @@ int jump = !PyStackRef_IsNone(value_stackref); RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); if (jump) { + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value_stackref); + stack_pointer = _PyFrame_GetStackPointer(frame); INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } DISPATCH(); @@ -4961,7 +5157,9 @@ _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (bytecode == NULL) goto error; + if (bytecode == NULL) { + goto error; + } _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5000,7 +5198,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } } @@ -5010,7 +5210,9 @@ int err = _Py_call_instrumentation( tstate, oparg > 0, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; + if (err) { + goto error; + } if (frame->instr_ptr != this_instr) { /* Instrumentation has jumped */ next_instr = frame->instr_ptr; @@ -5035,7 +5237,9 @@ tstate, PY_MONITORING_EVENT_PY_RETURN, frame, this_instr, PyStackRef_AsPyObjectBorrow(val)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; + if (err) { + goto error; + } } // _RETURN_VALUE { @@ -5194,7 +5398,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -5225,7 +5431,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -5255,7 +5463,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { this_instr[1].counter = restart_backoff_counter(counter); - if (optimized < 0) goto error; + if (optimized < 0) { + goto error; + } } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5302,7 +5512,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -5336,7 +5548,9 @@ list = stack_pointer[-2 - (oparg-1)]; int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), PyStackRef_AsPyObjectSteal(v)); - if (err < 0) goto pop_1_error; + if (err < 0) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -5435,7 +5649,9 @@ meth | NULL | arg1 | ... | argN */ PyStackRef_CLOSE(owner); - if (attr_o == NULL) goto pop_1_error; + if (attr_o == NULL) { + goto pop_1_error; + } self_or_null[0] = PyStackRef_NULL; } } @@ -5445,7 +5661,9 @@ attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); - if (attr_o == NULL) goto pop_1_error; + if (attr_o == NULL) { + goto pop_1_error; + } } attr = PyStackRef_FromPyObjectSteal(attr_o); } @@ -5616,14 +5834,16 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { null = PyStackRef_NULL; } - stack_pointer[-1] = attr; if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); @@ -5804,14 +6024,16 @@ attr = PyStackRef_FromPyObjectSteal(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { null = PyStackRef_NULL; } - stack_pointer[-1] = attr; if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); @@ -6087,7 +6309,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) goto error; + if (err < 0) { + goto error; + } if (bc_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetString(tstate, PyExc_NameError, @@ -6301,9 +6525,15 @@ goto error; } } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(class_dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); value = PyStackRef_FromPyObjectSteal(value_o); - stack_pointer[-1] = value; + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -6320,7 +6550,9 @@ int err = PyMapping_GetOptionalItem(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &v_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(mod_or_class_dict); - if (err < 0) goto pop_1_error; + if (err < 0) { + goto pop_1_error; + } if (v_o == NULL) { if (PyDict_CheckExact(GLOBALS()) && PyDict_CheckExact(BUILTINS())) @@ -6352,13 +6584,17 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) goto error; + if (err < 0) { + goto error; + } if (v_o == NULL) { /* namespace 2: builtins */ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) goto error; + if (err < 0) { + goto error; + } if (v_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcCheckArg( @@ -6413,7 +6649,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*res)) goto error; + if (PyStackRef_IsNull(*res)) { + goto error; + } } // _PUSH_NULL_CONDITIONAL { @@ -6556,7 +6794,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *v_o = _PyEval_LoadName(tstate, frame, name); stack_pointer = _PyFrame_GetStackPointer(frame); - if (v_o == NULL) goto error; + if (v_o == NULL) { + goto error; + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; @@ -6695,7 +6935,9 @@ PyStackRef_CLOSE(global_super_st); PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); - if (super == NULL) goto pop_3_error; + if (super == NULL) { + goto pop_3_error; + } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); @@ -6703,7 +6945,9 @@ PyObject *attr_o = PyObject_GetAttr(super, name); Py_DECREF(super); stack_pointer = _PyFrame_GetStackPointer(frame); - if (attr_o == NULL) goto error; + if (attr_o == NULL) { + goto error; + } attr = PyStackRef_FromPyObjectSteal(attr_o); } // _PUSH_NULL_CONDITIONAL @@ -6744,7 +6988,9 @@ PyStackRef_CLOSE(global_super_st); PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); - if (attr == NULL) goto pop_3_error; + if (attr == NULL) { + goto pop_3_error; + } attr_st = PyStackRef_FromPyObjectSteal(attr); stack_pointer[-3] = attr_st; stack_pointer += -2; @@ -6786,11 +7032,17 @@ if (method_found) { self_or_null = self_st; // transfer ownership } else { + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(self_st); + stack_pointer = _PyFrame_GetStackPointer(frame); self_or_null = PyStackRef_NULL; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); } - PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(global_super_st); + PyStackRef_CLOSE(class_st); attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[-3] = attr; stack_pointer[-2] = self_or_null; @@ -6826,12 +7078,20 @@ PyFunctionObject *func_obj = (PyFunctionObject *) PyFunction_New(codeobj, GLOBALS()); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(codeobj_st); - if (func_obj == NULL) goto pop_1_error; + stack_pointer = _PyFrame_GetStackPointer(frame); + if (func_obj == NULL) { + goto error; + } _PyFunction_SetVersion( func_obj, ((PyCodeObject *)codeobj)->co_version); func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); - stack_pointer[-1] = func; + stack_pointer[0] = func; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -6856,7 +7116,9 @@ PyStackRef_AsPyObjectSteal(value) ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto pop_2_error; + if (err != 0) { + goto pop_2_error; + } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -6890,7 +7152,9 @@ attrs = PyStackRef_FromPyObjectSteal(attrs_o); } else { - if (_PyErr_Occurred(tstate)) goto pop_3_error; + if (_PyErr_Occurred(tstate)) { + goto pop_3_error; + } // Error! attrs = PyStackRef_None; // Failure! } @@ -6914,7 +7178,9 @@ PyObject *values_or_none_o = _PyEval_MatchKeys(tstate, PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (values_or_none_o == NULL) goto error; + if (values_or_none_o == NULL) { + goto error; + } values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); stack_pointer[0] = values_or_none; stack_pointer += 1; @@ -7237,7 +7503,9 @@ _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); stack_pointer = _PyFrame_GetStackPointer(frame); - if (bytecode == NULL) goto error; + if (bytecode == NULL) { + goto error; + } _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -7284,7 +7552,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) goto error; + if (err != 0) { + goto error; + } } } } @@ -7321,7 +7591,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); stack_pointer = _PyFrame_GetStackPointer(frame); - if (gen == NULL) goto error; + if (gen == NULL) { + goto error; + } assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; @@ -7455,10 +7727,16 @@ goto pop_1_error; } } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); retval = PyStackRef_FromPyObjectSteal(retval_o); } - stack_pointer[-1] = retval; + stack_pointer[0] = retval; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -7530,18 +7808,24 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) goto error; + if (err < 0) { + goto error; + } if (ann_dict == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); ann_dict = PyDict_New(); stack_pointer = _PyFrame_GetStackPointer(frame); - if (ann_dict == NULL) goto error; + if (ann_dict == NULL) { + goto error; + } _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); Py_DECREF(ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) goto error; + if (err) { + goto error; + } } else { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -7564,7 +7848,9 @@ PyStackRef_AsPyObjectBorrow(v)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - if (err) goto pop_1_error; + if (err) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7607,7 +7893,9 @@ PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); - if (err < 0) goto pop_1_error; + if (err < 0) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7651,7 +7939,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); PyStackRef_CLOSE(owner); - if (err) goto pop_2_error; + if (err) { + goto pop_2_error; + } } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -7707,10 +7997,10 @@ _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } @@ -7745,10 +8035,10 @@ PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } @@ -7812,10 +8102,10 @@ // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. STAT_INC(STORE_ATTR, hit); - PyStackRef_CLOSE(owner); stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } @@ -7892,7 +8182,9 @@ int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - if (err) goto pop_1_error; + if (err) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7926,7 +8218,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(v); - if (err) goto pop_1_error; + if (err) { + goto pop_1_error; + } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7973,7 +8267,9 @@ } PyStackRef_CLOSE(v); PyStackRef_CLOSE(container); - if (err) goto pop_4_error; + if (err) { + goto pop_4_error; + } } stack_pointer += -4; assert(WITHIN_STACK_BOUNDS()); @@ -8018,7 +8314,9 @@ PyStackRef_CLOSE(v); PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); - if (err) goto pop_3_error; + if (err) { + goto pop_3_error; + } } stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); @@ -8045,10 +8343,14 @@ PyStackRef_AsPyObjectSteal(sub), PyStackRef_AsPyObjectSteal(value)); stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(dict_st); - if (err) goto pop_3_error; stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + goto error; + } DISPATCH(); } @@ -8083,10 +8385,10 @@ assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - PyStackRef_CLOSE(list_st); stack_pointer += -3; assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(list_st); Py_DECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); @@ -8096,17 +8398,14 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SWAP); - _PyStackRef bottom_in; - _PyStackRef top_in; - _PyStackRef top_out; - _PyStackRef bottom_out; - top_in = stack_pointer[-1]; - bottom_in = stack_pointer[-2 - (oparg-2)]; - bottom_out = bottom_in; - top_out = top_in; + _PyStackRef *bottom; + _PyStackRef *top; + top = &stack_pointer[-1]; + bottom = &stack_pointer[-2 - (oparg-2)]; + _PyStackRef temp = bottom[0]; + bottom[0] = top[0]; + top[0] = temp; assert(oparg >= 2); - stack_pointer[-2 - (oparg-2)] = top_out; - stack_pointer[-1] = bottom_out; DISPATCH(); } @@ -8143,7 +8442,9 @@ int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (err < 0) goto pop_1_error; + if (err < 0) { + goto pop_1_error; + } res = err ? PyStackRef_True : PyStackRef_False; } stack_pointer[-1] = res; @@ -8291,7 +8592,9 @@ PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) goto pop_1_error; + if (res_o == NULL) { + goto pop_1_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; DISPATCH(); @@ -8308,7 +8611,9 @@ PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); - if (res_o == NULL) goto pop_1_error; + if (res_o == NULL) { + goto pop_1_error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; DISPATCH(); @@ -8341,7 +8646,9 @@ int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg & 0xFF, oparg >> 8, top); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); - if (res == 0) goto pop_1_error; + if (res == 0) { + goto pop_1_error; + } stack_pointer += (oparg & 0xFF) + (oparg >> 8); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -8383,7 +8690,9 @@ int res = _PyEval_UnpackIterableStackRef(tstate, seq, oparg, -1, top); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); - if (res == 0) goto pop_1_error; + if (res == 0) { + goto pop_1_error; + } } stack_pointer += -1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -8512,7 +8821,9 @@ PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) goto error; + if (res_o == NULL) { + goto error; + } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -8591,20 +8902,20 @@ pop_4_error: { - STACK_SHRINK(1); - goto pop_3_error; + STACK_SHRINK(4); + goto error; } pop_3_error: { - STACK_SHRINK(1); - goto pop_2_error; + STACK_SHRINK(3); + goto error; } pop_2_error: { - STACK_SHRINK(1); - goto pop_1_error; + STACK_SHRINK(2); + goto error; } pop_1_error: diff --git a/Python/optimizer.c b/Python/optimizer.c index b16695a3c3d33e..d71abd3224240b 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -989,7 +989,6 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) current_error = next_spare; current_error_target = target; make_exit(&buffer[next_spare], _ERROR_POP_N, 0); - buffer[next_spare].oparg = popped; buffer[next_spare].operand0 = target; next_spare++; } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index fb14c4b7c8645b..91573e82841cc9 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -512,10 +512,11 @@ dummy_func(void) { top = bottom; } - op(_SWAP, (bottom_in, unused[oparg-2], top_in -- - top_out, unused[oparg-2], bottom_out)) { - bottom_out = bottom_in; - top_out = top_in; + op(_SWAP, (bottom[1], unused[oparg-2], top[1] -- bottom[1], unused[oparg-2], top[1])) { + JitOptSymbol *temp = bottom[0]; + bottom[0] = top[0]; + top[0] = temp; + assert(oparg >= 2); } op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { @@ -629,10 +630,10 @@ dummy_func(void) { ctx->done = true; } - op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, unused, unused[oparg] -- func, self, unused[oparg])) { + op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) { (void)callable; - func = sym_new_not_null(ctx); - self = sym_new_not_null(ctx); + callable[0] = sym_new_not_null(ctx); + self_or_null[0] = sym_new_not_null(ctx); } op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 2497754745c28b..144c7c503b2661 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1837,12 +1837,6 @@ } case _EXPAND_METHOD: { - JitOptSymbol **method; - JitOptSymbol **self; - method = &stack_pointer[-2 - oparg]; - self = &stack_pointer[-1 - oparg]; - method[0] = sym_new_not_null(ctx); - self[0] = sym_new_not_null(ctx); break; } @@ -1870,15 +1864,13 @@ } case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: { - JitOptSymbol *callable; - JitOptSymbol *func; - JitOptSymbol *self; - callable = stack_pointer[-2 - oparg]; + JitOptSymbol **self_or_null; + JitOptSymbol **callable; + self_or_null = &stack_pointer[-1 - oparg]; + callable = &stack_pointer[-2 - oparg]; (void)callable; - func = sym_new_not_null(ctx); - self = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = func; - stack_pointer[-1 - oparg] = self; + callable[0] = sym_new_not_null(ctx); + self_or_null[0] = sym_new_not_null(ctx); break; } @@ -2214,12 +2206,6 @@ } case _EXPAND_METHOD_KW: { - JitOptSymbol **method; - JitOptSymbol **self; - method = &stack_pointer[-3 - oparg]; - self = &stack_pointer[-2 - oparg]; - method[0] = sym_new_not_null(ctx); - self[0] = sym_new_not_null(ctx); break; } @@ -2410,16 +2396,14 @@ } case _SWAP: { - JitOptSymbol *top_in; - JitOptSymbol *bottom_in; - JitOptSymbol *top_out; - JitOptSymbol *bottom_out; - top_in = stack_pointer[-1]; - bottom_in = stack_pointer[-2 - (oparg-2)]; - bottom_out = bottom_in; - top_out = top_in; - stack_pointer[-2 - (oparg-2)] = top_out; - stack_pointer[-1] = bottom_out; + JitOptSymbol **top; + JitOptSymbol **bottom; + top = &stack_pointer[-1]; + bottom = &stack_pointer[-2 - (oparg-2)]; + JitOptSymbol *temp = bottom[0]; + bottom[0] = top[0]; + top[0] = temp; + assert(oparg >= 2); break; } @@ -2639,8 +2623,6 @@ } case _ERROR_POP_N: { - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); break; } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index acf9458019fb4b..afb20b0330dd88 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -5,9 +5,16 @@ import re from typing import Optional +@dataclass +class EscapingCall: + start: lexer.Token + call: lexer.Token + end: lexer.Token + kills: lexer.Token | None + @dataclass class Properties: - escaping_calls: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] + escaping_calls: dict[lexer.Token, EscapingCall] escapes: bool error_with_pop: bool error_without_pop: bool @@ -41,7 +48,7 @@ def dump(self, indent: str) -> None: @staticmethod def from_list(properties: list["Properties"]) -> "Properties": - escaping_calls: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] = {} + escaping_calls: dict[lexer.Token, EscapingCall] = {} for p in properties: escaping_calls.update(p.escaping_calls) return Properties( @@ -330,6 +337,17 @@ def convert_stack_item( cond = replace_op_arg_1 return StackItem(item.name, item.type, cond, item.size) +def check_unused(stack: list[StackItem], input_names: dict[str, lexer.Token]) -> None: + "Unused items cannot be on the stack above used, non-peek items" + seen_unused = False + for item in reversed(stack): + if item.name == "unused": + seen_unused = True + elif item.peek: + break + elif seen_unused: + raise analysis_error(f"Cannot have used input '{item.name}' below an unused value on the stack", input_names[item.name]) + def analyze_stack( op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None @@ -374,6 +392,7 @@ def analyze_stack( for output in outputs: if variable_used(op, output.name): output.used = True + check_unused(inputs, input_names) return StackEffect(inputs, outputs) @@ -548,7 +567,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "PyStackRef_AsPyObjectNew", "PyStackRef_AsPyObjectSteal", "PyStackRef_CLEAR", - "PyStackRef_CLOSE", "PyStackRef_CLOSE_SPECIALIZED", "PyStackRef_DUP", "PyStackRef_False", @@ -665,8 +683,8 @@ def find_stmt_end(node: parser.InstDef, idx: int) -> lexer.Token: if tkn.kind == "SEMI": return node.block.tokens[idx+1] -def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, tuple[lexer.Token, lexer.Token]]) -> None: - calls = {escapes[t][0] for t in escapes} +def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, EscapingCall]) -> None: + calls = {e.call for e in escapes.values()} in_if = 0 tkn_iter = iter(instr.block.tokens) for tkn in tkn_iter: @@ -684,8 +702,8 @@ def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, tuple elif tkn in calls and in_if: raise analysis_error(f"Escaping call '{tkn.text} in condition", tkn) -def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, tuple[lexer.Token, lexer.Token]]: - result: dict[lexer.Token, tuple[lexer.Token, lexer.Token]] = {} +def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, EscapingCall]: + result: dict[lexer.Token, EscapingCall] = {} tokens = instr.block.tokens for idx, tkn in enumerate(tokens): try: @@ -720,9 +738,17 @@ def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, tuple[le continue elif tkn.kind != "RBRACKET": continue + if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"): + if len(tokens) <= idx+2: + raise analysis_error("Unexpected end of file", next_tkn) + kills = tokens[idx+2] + if kills.kind != "IDENTIFIER": + raise analysis_error(f"Expected identifier, got '{kills.text}'", kills) + else: + kills = None start = find_stmt_start(instr, idx) end = find_stmt_end(instr, idx) - result[start] = tkn, end + result[start] = EscapingCall(start, tkn, end, kills) check_escaping_calls(instr, result) return result @@ -821,9 +847,6 @@ def compute_properties(op: parser.InstDef) -> Properties: variable_used(op, "Py_DECREF") or variable_used(op, "Py_XDECREF") or variable_used(op, "Py_CLEAR") or - variable_used(op, "PyStackRef_CLOSE") or - variable_used(op, "PyStackRef_XCLOSE") or - variable_used(op, "PyStackRef_CLEAR") or variable_used(op, "SETLOCAL") ) return Properties( diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index f1f166ae104ba5..d3eb948f3563e7 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -120,8 +120,6 @@ def __init__(self, out: CWriter): "SYNC_SP": self.sync_sp, "SAVE_STACK": self.save_stack, "RELOAD_STACK": self.reload_stack, - "PyStackRef_CLOSE": self.stackref_close, - "PyStackRef_XCLOSE": self.stackref_close, "PyStackRef_CLOSE_SPECIALIZED": self.stackref_close_specialized, "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, @@ -166,6 +164,13 @@ def deopt_if( exit_if = deopt_if + def goto_error(self, offset: int, label: str, storage: Storage) -> str: + if offset > 0: + return f"goto pop_{offset}_{label};" + if offset < 0: + storage.copy().flush(self.out) + return f"goto {label};" + def error_if( self, tkn: Token, @@ -188,30 +193,20 @@ def error_if( self.out.emit_at("if ", tkn) self.emit(lparen) emit_to(self.out, tkn_iter, "COMMA") - self.out.emit(") ") + self.out.emit(") {\n") label = next(tkn_iter).text next(tkn_iter) # RPAREN next(tkn_iter) # Semi colon storage.clear_inputs("at ERROR_IF") + c_offset = storage.stack.peek_offset() try: offset = -int(c_offset) except ValueError: offset = -1 - if offset > 0: - self.out.emit(f"goto pop_{offset}_") - self.out.emit(label) - self.out.emit(";\n") - elif offset == 0: - self.out.emit("goto ") - self.out.emit(label) - self.out.emit(";\n") - else: - self.out.emit("{\n") - storage.copy().flush(self.out) - self.out.emit("goto ") - self.out.emit(label) - self.out.emit(";\n") + self.out.emit(self.goto_error(offset, label, storage)) + self.out.emit("\n") + if not unconditional: self.out.emit("}\n") return not unconditional @@ -226,7 +221,7 @@ def error_no_pop( next(tkn_iter) # LPAREN next(tkn_iter) # RPAREN next(tkn_iter) # Semi colon - self.out.emit_at("goto error;", tkn) + self.out.emit_at(self.goto_error(0, "error", storage), tkn) return False def decref_inputs( @@ -320,26 +315,6 @@ def stackref_kill( live = var.name return True - def stackref_close( - self, - tkn: Token, - tkn_iter: TokenIterator, - uop: Uop, - storage: Storage, - inst: Instruction | None, - ) -> bool: - self.out.emit(tkn) - tkn = next(tkn_iter) - assert tkn.kind == "LPAREN" - self.out.emit(tkn) - name = next(tkn_iter) - self.out.emit(name) - if name.kind == "IDENTIFIER": - return self.stackref_kill(name, storage, True) - rparen = emit_to(self.out, tkn_iter, "RPAREN") - self.emit(rparen) - return True - def stackref_close_specialized( self, tkn: Token, @@ -590,9 +565,15 @@ def _emit_block( self.out.start_line() line = tkn.line if tkn in escaping_calls: - if tkn != reload: + escape = escaping_calls[tkn] + if escape.kills is not None: + if tkn == reload: + self.emit_reload(storage) + self.stackref_kill(escape.kills, storage, True) + self.emit_save(storage) + elif tkn != reload: self.emit_save(storage) - _, reload = escaping_calls[tkn] + reload = escape.end elif tkn == reload: self.emit_reload(storage) if tkn.kind == "LBRACE": @@ -634,7 +615,6 @@ def _emit_block( raise analysis_error(ex.args[0], tkn) from None raise analysis_error("Expecting closing brace. Reached end of file", tkn) - def emit_tokens( self, uop: Uop, diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index dd16a1a7eb28b5..4540eb252634ba 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -69,41 +69,11 @@ def __init__(self, out: CWriter): super().__init__(out) self._replacers["oparg"] = self.oparg - def error_if( - self, - tkn: Token, - tkn_iter: TokenIterator, - uop: Uop, - storage: Storage, - inst: Instruction | None, - ) -> bool: - self.out.emit_at("if ", tkn) - lparen = next(tkn_iter) - self.emit(lparen) - assert lparen.kind == "LPAREN" - first_tkn = next(tkn_iter) - self.out.emit(first_tkn) - emit_to(self.out, tkn_iter, "COMMA") - label = next(tkn_iter).text - next(tkn_iter) # RPAREN - next(tkn_iter) # Semi colon - self.emit(") JUMP_TO_ERROR();\n") - return not always_true(first_tkn) - - - def error_no_pop( - self, - tkn: Token, - tkn_iter: TokenIterator, - uop: Uop, - storage: Storage, - inst: Instruction | None, - ) -> bool: - next(tkn_iter) # LPAREN - next(tkn_iter) # RPAREN - next(tkn_iter) # Semi colon - self.out.emit_at("JUMP_TO_ERROR();", tkn) - return False + def goto_error(self, offset: int, label: str, storage: Storage) -> str: + # To do: Add jump targets for popping values. + if offset != 0: + storage.copy().flush(self.out) + return f"JUMP_TO_ERROR();" def deopt_if( self, From 75b628adebd4594529da25ea9915600f2872fc2b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 3 Feb 2025 15:09:21 +0000 Subject: [PATCH 058/311] GH-128563: Generate `opcode = ...` in instructions that need `opcode` (GH-129608) * Remove support for GO_TO_INSTRUCTION --- Include/internal/pycore_opcode_metadata.h | 22 +- Include/internal/pycore_uop_ids.h | 72 ++-- Include/opcode_ids.h | 30 +- Lib/_opcode_metadata.py | 30 +- Python/bytecodes.c | 52 ++- Python/executor_cases.c.h | 6 +- Python/generated_cases.c.h | 466 +++++++++++++++++++-- Python/opcode_targets.h | 6 +- Python/optimizer_cases.c.h | 6 +- Tools/cases_generator/analyzer.py | 18 +- Tools/cases_generator/generators_common.py | 20 +- Tools/cases_generator/tier1_generator.py | 2 + 12 files changed, 558 insertions(+), 172 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 377a885dbb8c34..beb0baa7bb69a6 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -226,9 +226,9 @@ int _PyOpcode_num_popped(int opcode, int oparg) { case INSTRUMENTED_CALL: return 2 + oparg; case INSTRUMENTED_CALL_FUNCTION_EX: - return 0; + return 4; case INSTRUMENTED_CALL_KW: - return 0; + return 3 + oparg; case INSTRUMENTED_END_FOR: return 2; case INSTRUMENTED_END_SEND: @@ -244,7 +244,7 @@ int _PyOpcode_num_popped(int opcode, int oparg) { case INSTRUMENTED_LINE: return 0; case INSTRUMENTED_LOAD_SUPER_ATTR: - return 0; + return 3; case INSTRUMENTED_NOT_TAKEN: return 0; case INSTRUMENTED_POP_ITER: @@ -701,9 +701,9 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case INSTRUMENTED_CALL: return 1; case INSTRUMENTED_CALL_FUNCTION_EX: - return 0; + return 1; case INSTRUMENTED_CALL_KW: - return 0; + return 1; case INSTRUMENTED_END_FOR: return 1; case INSTRUMENTED_END_SEND: @@ -719,7 +719,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case INSTRUMENTED_LINE: return 0; case INSTRUMENTED_LOAD_SUPER_ATTR: - return 0; + return 1 + (oparg & 1); case INSTRUMENTED_NOT_TAKEN: return 0; case INSTRUMENTED_POP_ITER: @@ -1388,7 +1388,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case INSTRUMENTED_CALL_KW: { - *effect = 0; + *effect = Py_MAX(0, -2 - oparg); return 0; } case INSTRUMENTED_END_FOR: { @@ -1420,7 +1420,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { return 0; } case INSTRUMENTED_LOAD_SUPER_ATTR: { - *effect = 0; + *effect = Py_MAX(-2, -2 + (oparg & 1)); return 0; } case INSTRUMENTED_NOT_TAKEN: { @@ -2104,8 +2104,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [IMPORT_FROM] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [IMPORT_NAME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [INSTRUMENTED_CALL_FUNCTION_EX] = { true, INSTR_FMT_IX, 0 }, - [INSTRUMENTED_CALL_KW] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [INSTRUMENTED_CALL_FUNCTION_EX] = { true, INSTR_FMT_IX, HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, + [INSTRUMENTED_CALL_KW] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_END_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG }, [INSTRUMENTED_END_SEND] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, @@ -2113,7 +2113,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [INSTRUMENTED_JUMP_BACKWARD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [INSTRUMENTED_LINE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, - [INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IXC, 0 }, + [INSTRUMENTED_LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [INSTRUMENTED_NOT_TAKEN] = { true, INSTR_FMT_IX, 0 }, [INSTRUMENTED_POP_ITER] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 7a6c0d22fe24e5..ca40af55406089 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -152,13 +152,10 @@ extern "C" { #define _INIT_CALL_PY_EXACT_ARGS_2 396 #define _INIT_CALL_PY_EXACT_ARGS_3 397 #define _INIT_CALL_PY_EXACT_ARGS_4 398 -#define _INSTRUMENTED_CALL_FUNCTION_EX INSTRUMENTED_CALL_FUNCTION_EX -#define _INSTRUMENTED_CALL_KW INSTRUMENTED_CALL_KW #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER #define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD #define _INSTRUMENTED_LINE INSTRUMENTED_LINE -#define _INSTRUMENTED_LOAD_SUPER_ATTR INSTRUMENTED_LOAD_SUPER_ATTR #define _INSTRUMENTED_NOT_TAKEN INSTRUMENTED_NOT_TAKEN #define _INSTRUMENTED_POP_JUMP_IF_FALSE INSTRUMENTED_POP_JUMP_IF_FALSE #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE @@ -242,58 +239,59 @@ extern "C" { #define _MAYBE_EXPAND_METHOD 447 #define _MAYBE_EXPAND_METHOD_KW 448 #define _MONITOR_CALL 449 -#define _MONITOR_JUMP_BACKWARD 450 -#define _MONITOR_RESUME 451 +#define _MONITOR_CALL_KW 450 +#define _MONITOR_JUMP_BACKWARD 451 +#define _MONITOR_RESUME 452 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 452 -#define _POP_JUMP_IF_TRUE 453 +#define _POP_JUMP_IF_FALSE 453 +#define _POP_JUMP_IF_TRUE 454 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 454 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 455 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 455 +#define _PUSH_FRAME 456 #define _PUSH_NULL PUSH_NULL -#define _PUSH_NULL_CONDITIONAL 456 -#define _PY_FRAME_GENERAL 457 -#define _PY_FRAME_KW 458 -#define _QUICKEN_RESUME 459 -#define _REPLACE_WITH_TRUE 460 +#define _PUSH_NULL_CONDITIONAL 457 +#define _PY_FRAME_GENERAL 458 +#define _PY_FRAME_KW 459 +#define _QUICKEN_RESUME 460 +#define _REPLACE_WITH_TRUE 461 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 461 -#define _SEND 462 -#define _SEND_GEN_FRAME 463 +#define _SAVE_RETURN_OFFSET 462 +#define _SEND 463 +#define _SEND_GEN_FRAME 464 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 464 -#define _STORE_ATTR 465 -#define _STORE_ATTR_INSTANCE_VALUE 466 -#define _STORE_ATTR_SLOT 467 -#define _STORE_ATTR_WITH_HINT 468 +#define _START_EXECUTOR 465 +#define _STORE_ATTR 466 +#define _STORE_ATTR_INSTANCE_VALUE 467 +#define _STORE_ATTR_SLOT 468 +#define _STORE_ATTR_WITH_HINT 469 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 469 -#define _STORE_FAST_0 470 -#define _STORE_FAST_1 471 -#define _STORE_FAST_2 472 -#define _STORE_FAST_3 473 -#define _STORE_FAST_4 474 -#define _STORE_FAST_5 475 -#define _STORE_FAST_6 476 -#define _STORE_FAST_7 477 +#define _STORE_FAST 470 +#define _STORE_FAST_0 471 +#define _STORE_FAST_1 472 +#define _STORE_FAST_2 473 +#define _STORE_FAST_3 474 +#define _STORE_FAST_4 475 +#define _STORE_FAST_5 476 +#define _STORE_FAST_6 477 +#define _STORE_FAST_7 478 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 478 -#define _STORE_SUBSCR 479 +#define _STORE_SLICE 479 +#define _STORE_SUBSCR 480 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 480 -#define _TO_BOOL 481 +#define _TIER2_RESUME_CHECK 481 +#define _TO_BOOL 482 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -303,13 +301,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 482 +#define _UNPACK_SEQUENCE 483 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 482 +#define MAX_UOP_ID 483 #ifdef __cplusplus } diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index 4a9fc15dcd2880..dfe7fa36cccd31 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -214,21 +214,21 @@ extern "C" { #define INSTRUMENTED_END_FOR 235 #define INSTRUMENTED_POP_ITER 236 #define INSTRUMENTED_END_SEND 237 -#define INSTRUMENTED_LOAD_SUPER_ATTR 238 -#define INSTRUMENTED_FOR_ITER 239 -#define INSTRUMENTED_CALL_KW 240 -#define INSTRUMENTED_CALL_FUNCTION_EX 241 -#define INSTRUMENTED_INSTRUCTION 242 -#define INSTRUMENTED_JUMP_FORWARD 243 -#define INSTRUMENTED_NOT_TAKEN 244 -#define INSTRUMENTED_POP_JUMP_IF_TRUE 245 -#define INSTRUMENTED_POP_JUMP_IF_FALSE 246 -#define INSTRUMENTED_POP_JUMP_IF_NONE 247 -#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 248 -#define INSTRUMENTED_RESUME 249 -#define INSTRUMENTED_RETURN_VALUE 250 -#define INSTRUMENTED_YIELD_VALUE 251 -#define INSTRUMENTED_CALL 252 +#define INSTRUMENTED_FOR_ITER 238 +#define INSTRUMENTED_INSTRUCTION 239 +#define INSTRUMENTED_JUMP_FORWARD 240 +#define INSTRUMENTED_NOT_TAKEN 241 +#define INSTRUMENTED_POP_JUMP_IF_TRUE 242 +#define INSTRUMENTED_POP_JUMP_IF_FALSE 243 +#define INSTRUMENTED_POP_JUMP_IF_NONE 244 +#define INSTRUMENTED_POP_JUMP_IF_NOT_NONE 245 +#define INSTRUMENTED_RESUME 246 +#define INSTRUMENTED_RETURN_VALUE 247 +#define INSTRUMENTED_YIELD_VALUE 248 +#define INSTRUMENTED_LOAD_SUPER_ATTR 249 +#define INSTRUMENTED_CALL 250 +#define INSTRUMENTED_CALL_KW 251 +#define INSTRUMENTED_CALL_FUNCTION_EX 252 #define INSTRUMENTED_JUMP_BACKWARD 253 #define INSTRUMENTED_LINE 254 #define ENTER_EXECUTOR 255 diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index 12c41374592185..ae3e9bd0ab4940 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -334,21 +334,21 @@ 'INSTRUMENTED_END_FOR': 235, 'INSTRUMENTED_POP_ITER': 236, 'INSTRUMENTED_END_SEND': 237, - 'INSTRUMENTED_LOAD_SUPER_ATTR': 238, - 'INSTRUMENTED_FOR_ITER': 239, - 'INSTRUMENTED_CALL_KW': 240, - 'INSTRUMENTED_CALL_FUNCTION_EX': 241, - 'INSTRUMENTED_INSTRUCTION': 242, - 'INSTRUMENTED_JUMP_FORWARD': 243, - 'INSTRUMENTED_NOT_TAKEN': 244, - 'INSTRUMENTED_POP_JUMP_IF_TRUE': 245, - 'INSTRUMENTED_POP_JUMP_IF_FALSE': 246, - 'INSTRUMENTED_POP_JUMP_IF_NONE': 247, - 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 248, - 'INSTRUMENTED_RESUME': 249, - 'INSTRUMENTED_RETURN_VALUE': 250, - 'INSTRUMENTED_YIELD_VALUE': 251, - 'INSTRUMENTED_CALL': 252, + 'INSTRUMENTED_FOR_ITER': 238, + 'INSTRUMENTED_INSTRUCTION': 239, + 'INSTRUMENTED_JUMP_FORWARD': 240, + 'INSTRUMENTED_NOT_TAKEN': 241, + 'INSTRUMENTED_POP_JUMP_IF_TRUE': 242, + 'INSTRUMENTED_POP_JUMP_IF_FALSE': 243, + 'INSTRUMENTED_POP_JUMP_IF_NONE': 244, + 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 245, + 'INSTRUMENTED_RESUME': 246, + 'INSTRUMENTED_RETURN_VALUE': 247, + 'INSTRUMENTED_YIELD_VALUE': 248, + 'INSTRUMENTED_LOAD_SUPER_ATTR': 249, + 'INSTRUMENTED_CALL': 250, + 'INSTRUMENTED_CALL_KW': 251, + 'INSTRUMENTED_CALL_FUNCTION_EX': 252, 'INSTRUMENTED_JUMP_BACKWARD': 253, 'JUMP': 256, 'JUMP_IF_FALSE': 257, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 908eb0c2a698c5..e679d90620ea9e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -45,7 +45,6 @@ #include "ceval_macros.h" /* Flow control macros */ -#define GO_TO_INSTRUCTION(instname) ((void)0) #define inst(name, ...) case name: #define op(name, ...) /* NAME is ignored */ @@ -2019,12 +2018,10 @@ dummy_func( ERROR_IF(err != 0, error); } - inst(INSTRUMENTED_LOAD_SUPER_ATTR, (unused/1 -- )) { - // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we - // don't want to specialize instrumented instructions - PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); - GO_TO_INSTRUCTION(LOAD_SUPER_ATTR); - } + macro(INSTRUMENTED_LOAD_SUPER_ATTR) = + counter/1 + + _LOAD_SUPER_ATTR + + _PUSH_NULL_CONDITIONAL; family(LOAD_SUPER_ATTR, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = { LOAD_SUPER_ATTR_ATTR, @@ -2088,7 +2085,10 @@ dummy_func( attr = PyStackRef_FromPyObjectSteal(attr_o); } - macro(LOAD_SUPER_ATTR) = _SPECIALIZE_LOAD_SUPER_ATTR + _LOAD_SUPER_ATTR + _PUSH_NULL_CONDITIONAL; + macro(LOAD_SUPER_ATTR) = + _SPECIALIZE_LOAD_SUPER_ATTR + + _LOAD_SUPER_ATTR + + _PUSH_NULL_CONDITIONAL; inst(LOAD_SUPER_ATTR_ATTR, (unused/1, global_super_st, class_st, self_st -- attr_st)) { PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); @@ -4331,18 +4331,23 @@ dummy_func( CALL_KW_NON_PY, }; - inst(INSTRUMENTED_CALL_KW, (counter/1, version/2 -- )) { - int is_meth = !PyStackRef_IsNull(PEEK(oparg + 2)); - int total_args = oparg + is_meth; - PyObject *function = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 3)); - PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING - : PyStackRef_AsPyObjectBorrow(PEEK(total_args + 1)); + op(_MONITOR_CALL_KW, (callable[1], self_or_null[1], args[oparg], kwnames -- callable[1], self_or_null[1], args[oparg], kwnames)) { + int is_meth = !PyStackRef_IsNull(self_or_null[0]); + PyObject *arg; + if (is_meth) { + arg = PyStackRef_AsPyObjectBorrow(self_or_null[0]); + } + else if (args) { + arg = PyStackRef_AsPyObjectBorrow(args[0]); + } + else { + arg = &_PyInstrumentation_MISSING; + } + PyObject *function = PyStackRef_AsPyObjectBorrow(callable[0]); int err = _Py_call_instrumentation_2args( tstate, PY_MONITORING_EVENT_CALL, frame, this_instr, function, arg); ERROR_IF(err, error); - PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); - GO_TO_INSTRUCTION(CALL_KW); } op(_MAYBE_EXPAND_METHOD_KW, (callable[1], self_or_null[1], args[oparg], kwnames_in -- func[1], maybe_self[1], args[oparg], kwnames_out)) { @@ -4520,6 +4525,13 @@ dummy_func( _MAYBE_EXPAND_METHOD_KW + _DO_CALL_KW; + macro(INSTRUMENTED_CALL_KW) = + counter/1 + + unused/2 + + _MONITOR_CALL_KW + + _MAYBE_EXPAND_METHOD_KW + + _DO_CALL_KW; + op(_CHECK_IS_NOT_PY_CALLABLE_KW, (callable[1], unused[1], unused[oparg], kwnames -- callable[1], unused[1], unused[oparg], kwnames)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); EXIT_IF(PyFunction_Check(callable_o)); @@ -4566,10 +4578,6 @@ dummy_func( _CALL_KW_NON_PY + _CHECK_PERIODIC; - inst(INSTRUMENTED_CALL_FUNCTION_EX, ( -- )) { - GO_TO_INSTRUCTION(CALL_FUNCTION_EX); - } - op(_MAKE_CALLARGS_A_TUPLE, (func, unused, callargs, kwargs_in -- func, unused, tuple, kwargs_out)) { PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (PyTuple_CheckExact(callargs_o)) { @@ -4678,6 +4686,10 @@ dummy_func( _DO_CALL_FUNCTION_EX + _CHECK_PERIODIC; + macro(INSTRUMENTED_CALL_FUNCTION_EX) = + _MAKE_CALLARGS_A_TUPLE + + _DO_CALL_FUNCTION_EX + + _CHECK_PERIODIC; inst(MAKE_FUNCTION, (codeobj_st -- func)) { PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index c1c2c8fda20a7a..59f1a1ba4dc92a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2706,8 +2706,6 @@ break; } - /* _INSTRUMENTED_LOAD_SUPER_ATTR is not a viable micro-op for tier 2 because it is instrumented */ - case _LOAD_SUPER_ATTR_ATTR: { _PyStackRef self_st; _PyStackRef class_st; @@ -5665,7 +5663,7 @@ break; } - /* _INSTRUMENTED_CALL_KW is not a viable micro-op for tier 2 because it is instrumented */ + /* _MONITOR_CALL_KW is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ case _MAYBE_EXPAND_METHOD_KW: { _PyStackRef kwnames_in; @@ -5896,8 +5894,6 @@ break; } - /* _INSTRUMENTED_CALL_FUNCTION_EX is not a viable micro-op for tier 2 because it is instrumented */ - case _MAKE_CALLARGS_A_TUPLE: { _PyStackRef kwargs_in; _PyStackRef callargs; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 44a78c410485c0..7dd9d6528bb49d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -966,6 +966,7 @@ PREDICTED_CALL:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; + opcode = CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -1744,12 +1745,11 @@ } TARGET(CALL_FUNCTION_EX) { - frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + (void)this_instr; next_instr += 1; INSTRUCTION_STATS(CALL_FUNCTION_EX); - PREDICTED_CALL_FUNCTION_EX:; - _Py_CODEUNIT* const this_instr = next_instr - 1; - (void)this_instr; + opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; _PyStackRef kwargs_in; @@ -2030,6 +2030,7 @@ PREDICTED_CALL_KW:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; + opcode = CALL_KW; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -2297,6 +2298,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW_NON_PY); + opcode = CALL_KW_NON_PY; static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef kwnames; @@ -2914,6 +2916,7 @@ frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_NON_PY_GENERAL); + opcode = CALL_NON_PY_GENERAL; static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef *self_or_null; @@ -4045,6 +4048,7 @@ (void)this_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); + opcode = ENTER_EXECUTOR; #ifdef _Py_TIER2 PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; @@ -4097,6 +4101,7 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(EXTENDED_ARG); + opcode = EXTENDED_ARG; assert(oparg); opcode = next_instr->op.code; oparg = oparg << 8 | next_instr->op.arg; @@ -4646,6 +4651,7 @@ (void)this_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL); + opcode = INSTRUMENTED_CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -4815,11 +4821,188 @@ } TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { - frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); - - goto PREDICTED_CALL_FUNCTION_EX; + opcode = INSTRUMENTED_CALL_FUNCTION_EX; + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef kwargs_in; + _PyStackRef tuple; + _PyStackRef kwargs_out; + _PyStackRef func_st; + _PyStackRef null; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef result; + // _MAKE_CALLARGS_A_TUPLE + { + kwargs_in = stack_pointer[-1]; + callargs = stack_pointer[-2]; + func = stack_pointer[-4]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (PyTuple_CheckExact(callargs_o)) { + tuple = callargs; + kwargs_out = kwargs_in; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + goto error; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + goto error; + } + kwargs_out = kwargs_in; + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + tuple = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer += 2; + assert(WITHIN_STACK_BOUNDS()); + } + } + // _DO_CALL_FUNCTION_EX + { + kwargs_st = kwargs_out; + callargs_st = tuple; + null = stack_pointer[-3]; + func_st = func; + (void)null; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + // DICT_MERGE is called before this opcode if there are kwargs. + // It converts all dict subtypes in kwargs into regular dicts. + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); + PyObject *result_o; + assert(!_PyErr_Occurred(tstate)); + if (opcode == INSTRUMENTED_CALL_FUNCTION_EX) { + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + assert(PyTuple_CheckExact(callargs)); + PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? + PyTuple_GET_ITEM(callargs, 0) : &_PyInstrumentation_MISSING; + stack_pointer[-2] = callargs_st; + stack_pointer[-1] = kwargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + goto error; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyFunction_Check(func) && !PyMethod_Check(func)) { + if (result_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + Py_CLEAR(result_o); + } + } + } + } + else { + if (Py_TYPE(func) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + // Need to sync the stack since we exit with DISPATCH_INLINED. + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + if (new_frame == NULL) { + goto error; + } + assert( 1 == 1); + frame->return_offset = 1; + DISPATCH_INLINED(new_frame); + } + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[-2] = callargs_st; + stack_pointer[-1] = kwargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + goto error; + } + result = PyStackRef_FromPyObjectSteal(result_o); + } + // _CHECK_PERIODIC + { + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); + QSBR_QUIESCENT_STATE(tstate); + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + stack_pointer[0] = result; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_HandlePending(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + goto error; + } + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + } + } + stack_pointer[0] = result; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); } TARGET(INSTRUMENTED_CALL_KW) { @@ -4827,25 +5010,170 @@ (void)this_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); - uint16_t counter = read_u16(&this_instr[1].cache); - (void)counter; - uint32_t version = read_u32(&this_instr[2].cache); - (void)version; - int is_meth = !PyStackRef_IsNull(PEEK(oparg + 2)); - int total_args = oparg + is_meth; - PyObject *function = PyStackRef_AsPyObjectBorrow(PEEK(oparg + 3)); - PyObject *arg = total_args == 0 ? &_PyInstrumentation_MISSING - : PyStackRef_AsPyObjectBorrow(PEEK(total_args + 1)); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_CALL, - frame, this_instr, function, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - goto error; + opcode = INSTRUMENTED_CALL_KW; + _PyStackRef *callable; + _PyStackRef *self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef kwnames_in; + _PyStackRef *func; + _PyStackRef *maybe_self; + _PyStackRef kwnames_out; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _MONITOR_CALL_KW + { + args = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-2 - oparg]; + callable = &stack_pointer[-3 - oparg]; + int is_meth = !PyStackRef_IsNull(self_or_null[0]); + PyObject *arg; + if (is_meth) { + arg = PyStackRef_AsPyObjectBorrow(self_or_null[0]); + } + else { + if (args) { + arg = PyStackRef_AsPyObjectBorrow(args[0]); + } + else { + arg = &_PyInstrumentation_MISSING; + } + } + PyObject *function = PyStackRef_AsPyObjectBorrow(callable[0]); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + goto error; + } } - PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); - goto PREDICTED_CALL_KW; + // _MAYBE_EXPAND_METHOD_KW + { + kwnames_in = stack_pointer[-1]; + func = &stack_pointer[-3 - oparg]; + maybe_self = &stack_pointer[-2 - oparg]; + if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + maybe_self[0] = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable[0]; + func[0] = PyStackRef_FromPyObjectNew(method); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + kwnames_out = kwnames_in; + } + // _DO_CALL_KW + { + kwnames = kwnames_out; + args = &stack_pointer[-1 - oparg]; + self_or_null = &stack_pointer[-2 - oparg]; + callable = &stack_pointer[-3 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + // oparg counts all of the args, but *not* self: + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null[0])) { + arguments--; + total_args++; + } + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + // Check if the call can be inlined or not + if (Py_TYPE(callable_o) == &PyFunction_Type && + tstate->interp->eval_frame == NULL && + ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + stack_pointer[-1] = kwnames; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, callable[0], locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + // Sync stack explicitly since we leave using DISPATCH_INLINED(). + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + // The frame has stolen all the arguments from the stack, + // so there is no need to clean them up. + if (new_frame == NULL) { + goto error; + } + assert( 4 == 1 + INLINE_CACHE_ENTRIES_CALL_KW); + frame->return_offset = 4 ; + DISPATCH_INLINED(new_frame); + } + /* Callable is not a normal Python function */ + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } + PyStackRef_CLOSE(kwnames); + stack_pointer += -3 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; + } + stack_pointer[-1] = kwnames; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_Vectorcall( + callable_o, args_o, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + if (opcode == INSTRUMENTED_CALL_KW) { + PyObject *arg = total_args == 0 ? + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); + if (res_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable_o, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable_o, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + Py_CLEAR(res_o); + } + } + } + PyStackRef_CLOSE(callable[0]); + PyStackRef_XCLOSE(self_or_null[0]); + for (int _i = oparg; --_i >= 0;) { + PyStackRef_CLOSE(args[_i]); + } + PyStackRef_CLOSE(kwnames); + if (res_o == NULL) { + stack_pointer += -3 - oparg; + assert(WITHIN_STACK_BOUNDS()); + goto error; + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); } TARGET(INSTRUMENTED_END_FOR) { @@ -4944,6 +5272,7 @@ (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); + opcode = INSTRUMENTED_INSTRUCTION; _PyFrame_SetStackPointer(frame, stack_pointer); int next_opcode = _Py_call_instrumentation_instruction( tstate, frame, this_instr); @@ -5001,6 +5330,7 @@ (void)this_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_LINE); + opcode = INSTRUMENTED_LINE; int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); @@ -5038,11 +5368,88 @@ (void)this_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); + opcode = INSTRUMENTED_LOAD_SUPER_ATTR; + _PyStackRef global_super_st; + _PyStackRef class_st; + _PyStackRef self_st; + _PyStackRef attr; + _PyStackRef null = PyStackRef_NULL; /* Skip 1 cache entry */ - // cancel out the decrement that will happen in LOAD_SUPER_ATTR; we - // don't want to specialize instrumented instructions - PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); - goto PREDICTED_LOAD_SUPER_ATTR; + // _LOAD_SUPER_ATTR + { + self_st = stack_pointer[-1]; + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + PyStackRef_CLOSE(global_super_st); + PyStackRef_CLOSE(class_st); + PyStackRef_CLOSE(self_st); + goto pop_3_error; + } + } + // we make no attempt to optimize here; specializations should + // handle any case whose performance we care about + PyObject *stack[] = {class, self}; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + if (super == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + Py_CLEAR(super); + } + } + } + PyStackRef_CLOSE(global_super_st); + PyStackRef_CLOSE(class_st); + PyStackRef_CLOSE(self_st); + if (super == NULL) { + goto pop_3_error; + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attr_o = PyObject_GetAttr(super, name); + Py_DECREF(super); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attr_o == NULL) { + goto error; + } + attr = PyStackRef_FromPyObjectSteal(attr_o); + } + // _PUSH_NULL_CONDITIONAL + { + null = PyStackRef_NULL; + } + stack_pointer[0] = attr; + if (oparg & 1) stack_pointer[1] = null; + stack_pointer += 1 + (oparg & 1); + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); } TARGET(INSTRUMENTED_NOT_TAKEN) { @@ -6862,6 +7269,7 @@ PREDICTED_LOAD_SUPER_ATTR:; _Py_CODEUNIT* const this_instr = next_instr - 2; (void)this_instr; + opcode = LOAD_SUPER_ATTR; _PyStackRef global_super_st; _PyStackRef class_st; _PyStackRef self_st; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 09a834bb38fa67..2b84f0281e5356 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -237,10 +237,7 @@ static void *opcode_targets[256] = { &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_POP_ITER, &&TARGET_INSTRUMENTED_END_SEND, - &&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR, &&TARGET_INSTRUMENTED_FOR_ITER, - &&TARGET_INSTRUMENTED_CALL_KW, - &&TARGET_INSTRUMENTED_CALL_FUNCTION_EX, &&TARGET_INSTRUMENTED_INSTRUCTION, &&TARGET_INSTRUMENTED_JUMP_FORWARD, &&TARGET_INSTRUMENTED_NOT_TAKEN, @@ -251,7 +248,10 @@ static void *opcode_targets[256] = { &&TARGET_INSTRUMENTED_RESUME, &&TARGET_INSTRUMENTED_RETURN_VALUE, &&TARGET_INSTRUMENTED_YIELD_VALUE, + &&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR, &&TARGET_INSTRUMENTED_CALL, + &&TARGET_INSTRUMENTED_CALL_KW, + &&TARGET_INSTRUMENTED_CALL_FUNCTION_EX, &&TARGET_INSTRUMENTED_JUMP_BACKWARD, &&TARGET_INSTRUMENTED_LINE, &&TARGET_ENTER_EXECUTOR, diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 144c7c503b2661..11f1815a977798 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1105,8 +1105,6 @@ break; } - /* _INSTRUMENTED_LOAD_SUPER_ATTR is not a viable micro-op for tier 2 */ - case _LOAD_SUPER_ATTR_ATTR: { JitOptSymbol *attr_st; attr_st = sym_new_not_null(ctx); @@ -2153,7 +2151,7 @@ break; } - /* _INSTRUMENTED_CALL_KW is not a viable micro-op for tier 2 */ + /* _MONITOR_CALL_KW is not a viable micro-op for tier 2 */ case _MAYBE_EXPAND_METHOD_KW: { JitOptSymbol **func; @@ -2222,8 +2220,6 @@ break; } - /* _INSTRUMENTED_CALL_FUNCTION_EX is not a viable micro-op for tier 2 */ - case _MAKE_CALLARGS_A_TUPLE: { JitOptSymbol *tuple; JitOptSymbol *kwargs_out; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index afb20b0330dd88..eda8d687a70ccd 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -31,6 +31,7 @@ class Properties: has_free: bool side_exit: bool pure: bool + uses_opcode: bool tier: int | None = None oparg_and_1: bool = False const_oparg: int = -1 @@ -66,6 +67,7 @@ def from_list(properties: list["Properties"]) -> "Properties": uses_co_consts=any(p.uses_co_consts for p in properties), uses_co_names=any(p.uses_co_names for p in properties), uses_locals=any(p.uses_locals for p in properties), + uses_opcode=any(p.uses_opcode for p in properties), has_free=any(p.has_free for p in properties), side_exit=any(p.side_exit for p in properties), pure=all(p.pure for p in properties), @@ -92,6 +94,7 @@ def infallible(self) -> bool: uses_co_consts=False, uses_co_names=False, uses_locals=False, + uses_opcode=False, has_free=False, side_exit=False, pure=True, @@ -755,7 +758,6 @@ def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, Escaping EXITS = { "DISPATCH", - "GO_TO_INSTRUCTION", "Py_UNREACHABLE", "DISPATCH_INLINED", "DISPATCH_GOTO", @@ -865,7 +867,8 @@ def compute_properties(op: parser.InstDef) -> Properties: uses_co_consts=variable_used(op, "FRAME_CO_CONSTS"), uses_co_names=variable_used(op, "FRAME_CO_NAMES"), uses_locals=(variable_used(op, "GETLOCAL") or variable_used(op, "SETLOCAL")) - and not has_free, + and not has_free, + uses_opcode=variable_used(op, "opcode"), has_free=has_free, pure="pure" in op.annotations, no_save_ip="no_save_ip" in op.annotations, @@ -1200,17 +1203,6 @@ def analyze_forest(forest: list[parser.AstNode]) -> Analysis: add_label(node, labels) case _: pass - for uop in uops.values(): - tkn_iter = iter(uop.body) - for tkn in tkn_iter: - if tkn.kind == "IDENTIFIER" and tkn.text == "GO_TO_INSTRUCTION": - if next(tkn_iter).kind != "LPAREN": - continue - target = next(tkn_iter) - if target.kind != "IDENTIFIER": - continue - if target.text in instructions: - instructions[target.text].is_target = True for uop in uops.values(): uop.instruction_size = get_instruction_size_for_uop(instructions, uop) # Special case BINARY_OP_INPLACE_ADD_UNICODE diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index d3eb948f3563e7..6f2af5fc01c47b 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -124,8 +124,7 @@ def __init__(self, out: CWriter): "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, - "POP_INPUT": self.pop_input, - "GO_TO_INSTRUCTION": self.go_to_instruction, + "POP_INPUT": self.pop_input } self.out = out @@ -381,23 +380,6 @@ def sync_sp( self._print_storage(storage) return True - def go_to_instruction( - self, - tkn: Token, - tkn_iter: TokenIterator, - uop: Uop, - storage: Storage, - inst: Instruction | None, - ) -> bool: - next(tkn_iter) - name = next(tkn_iter) - next(tkn_iter) - next(tkn_iter) - assert name.kind == "IDENTIFIER" - self.emit("\n") - self.emit(f"goto PREDICTED_{name.text};\n") - return True - def emit_save(self, storage: Storage) -> None: storage.save(self.out) self._print_storage(storage) diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index 13430524b26dcd..eed3086c327926 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -220,6 +220,8 @@ def generate_tier1_cases( if needs_this: out.emit(f"_Py_CODEUNIT* const this_instr = next_instr - {inst.size};\n") out.emit(unused_guard) + if inst.properties.uses_opcode: + out.emit(f"opcode = {name};\n") if inst.family is not None: out.emit( f"static_assert({inst.family.size} == {inst.size-1}" From bb5c6875d6e84bf2b4e134ed482141a51d223f09 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Mon, 3 Feb 2025 20:42:12 +0400 Subject: [PATCH 059/311] gh-111637: Clarify MIME type recognition behavior in mimetypes documentation (GH-129546) --- Doc/library/mimetypes.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst index 8ad4850584a7e1..514e773359a9aa 100644 --- a/Doc/library/mimetypes.rst +++ b/Doc/library/mimetypes.rst @@ -47,9 +47,11 @@ the information :func:`init` sets up. The optional *strict* argument is a flag specifying whether the list of known MIME types is limited to only the official types `registered with IANA `_. - When *strict* is ``True`` (the default), only the IANA types are supported; when - *strict* is ``False``, some additional non-standard but commonly used MIME types - are also recognized. + However, the behavior of this module also depends on the underlying operating + system. Only file types recognized by the OS or explicitly registered with + Python's internal database can be identified. When *strict* is ``True`` (the + default), only the IANA types are supported; when *strict* is ``False``, some + additional non-standard but commonly used MIME types are also recognized. .. versionchanged:: 3.8 Added support for *url* being a :term:`path-like object`. From 0664c1af9b29a5af2404e04a522f8e9e175ba05a Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Tue, 4 Feb 2025 09:10:55 +0100 Subject: [PATCH 060/311] gh-126835: Move constant subscript folding to CFG (#129568) Move folding of constant subscription from AST optimizer to CFG. Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Include/internal/pycore_long.h | 2 + Lib/test/test_ast/test_ast.py | 10 ----- Lib/test/test_peepholer.py | 53 ++++++++++++++++++++++ Python/ast_opt.c | 20 --------- Python/codegen.c | 2 +- Python/flowgraph.c | 82 ++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 31 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index c52eb77692dd6a..df0656a7cb8f0c 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -65,6 +65,8 @@ PyAPI_FUNC(void) _PyLong_ExactDealloc(PyObject *self); # error "_PY_NSMALLPOSINTS must be greater than or equal to 257" #endif +#define _PY_IS_SMALL_INT(val) ((val) >= 0 && (val) < 256 && (val) < _PY_NSMALLPOSINTS) + // Return a reference to the immortal zero singleton. // The function cannot return NULL. static inline PyObject* _PyLong_GetZero(void) diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index c268a1f00f938e..a438c8e81e4fd1 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -3279,16 +3279,6 @@ def test_folding_iter(self): self.assert_ast(code % (left, right), non_optimized_target, optimized_target) - def test_folding_subscript(self): - code = "(1,)[0]" - - non_optimized_target = self.wrap_expr( - ast.Subscript(value=ast.Tuple(elts=[ast.Constant(value=1)]), slice=ast.Constant(value=0)) - ) - optimized_target = self.wrap_expr(ast.Constant(value=1)) - - self.assert_ast(code, non_optimized_target, optimized_target) - def test_folding_type_param_in_function_def(self): code = "def foo[%s = 1 + 1](): pass" diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index b5b2b350e77a3b..9f2f9350d74661 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -473,6 +473,59 @@ def test_constant_folding(self): self.assertFalse(instr.opname.startswith('BUILD_')) self.check_lnotab(code) + def test_constant_folding_small_int(self): + tests = [ + # subscript + ('(0, )[0]', 0), + ('(1 + 2, )[0]', 3), + ('(2 + 2 * 2, )[0]', 6), + ('(1, (1 + 2 + 3, ))[1][0]', 6), + ('(255, )[0]', 255), + ('(256, )[0]', None), + ('(1000, )[0]', None), + ('(1 - 2, )[0]', None), + ] + for expr, oparg in tests: + with self.subTest(expr=expr, oparg=oparg): + code = compile(expr, '', 'single') + if oparg is not None: + self.assertInBytecode(code, 'LOAD_SMALL_INT', oparg) + else: + self.assertNotInBytecode(code, 'LOAD_SMALL_INT') + self.check_lnotab(code) + + def test_folding_subscript(self): + tests = [ + ('(1, )[0]', False), + ('(1, )[-1]', False), + ('(1 + 2, )[0]', False), + ('(1, (1, 2))[1][1]', False), + ('(1, 2)[2-1]', False), + ('(1, (1, 2))[1][2-1]', False), + ('(1, (1, 2))[1:6][0][2-1]', False), + ('"a"[0]', False), + ('("a" + "b")[1]', False), + ('("a" + "b", )[0][1]', False), + ('("a" * 10)[9]', False), + ('(1, )[1]', True), + ('(1, )[-2]', True), + ('"a"[1]', True), + ('"a"[-2]', True), + ('("a" + "b")[2]', True), + ('("a" + "b", )[0][2]', True), + ('("a" + "b", )[1][0]', True), + ('("a" * 10)[10]', True), + ('(1, (1, 2))[2:6][0][2-1]', True), + ] + for expr, has_error in tests: + with self.subTest(expr=expr, has_error=has_error): + code = compile(expr, '', 'single') + if not has_error: + self.assertNotInBytecode(code, 'BINARY_SUBSCR') + else: + self.assertInBytecode(code, 'BINARY_SUBSCR') + self.check_lnotab(code) + def test_in_literal_list(self): def containtest(): return x in [a, b] diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 01e208b88eca8b..78d84002d593fb 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -567,25 +567,6 @@ fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) return make_const(node, newval, arena); } -static int -fold_subscr(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) -{ - PyObject *newval; - expr_ty arg, idx; - - arg = node->v.Subscript.value; - idx = node->v.Subscript.slice; - if (node->v.Subscript.ctx != Load || - arg->kind != Constant_kind || - idx->kind != Constant_kind) - { - return 1; - } - - newval = PyObject_GetItem(arg->v.Constant.value, idx->v.Constant.value); - return make_const(node, newval, arena); -} - /* Change literal list or set of constants into constant tuple or frozenset respectively. Change literal list of non-constants into tuple. @@ -822,7 +803,6 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) case Subscript_kind: CALL(astfold_expr, expr_ty, node_->v.Subscript.value); CALL(astfold_expr, expr_ty, node_->v.Subscript.slice); - CALL(fold_subscr, expr_ty, node_); break; case Starred_kind: CALL(astfold_expr, expr_ty, node_->v.Starred.value); diff --git a/Python/codegen.c b/Python/codegen.c index 0bf9526cdc8435..e9853d7302f67f 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -284,7 +284,7 @@ codegen_addop_load_const(compiler *c, location loc, PyObject *o) if (PyLong_CheckExact(o)) { int overflow; long val = PyLong_AsLongAndOverflow(o, &overflow); - if (!overflow && val >= 0 && val < 256 && val < _PY_NSMALLPOSINTS) { + if (!overflow && _PY_IS_SMALL_INT(val)) { ADDOP_I(c, loc, LOAD_SMALL_INT, val); return SUCCESS; } diff --git a/Python/flowgraph.c b/Python/flowgraph.c index a0b76050fd4af6..9ca7fadb8d7665 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -6,6 +6,7 @@ #include "pycore_compile.h" #include "pycore_intrinsics.h" #include "pycore_pymem.h" // _PyMem_IsPtrFreed() +#include "pycore_long.h" // _PY_IS_SMALL_INT() #include "pycore_opcode_utils.h" #include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc @@ -1443,6 +1444,84 @@ optimize_if_const_list_or_set(PyObject *const_cache, cfg_instr* inst, int n, PyO return SUCCESS; } +/* + Walk basic block upwards starting from "start" to collect instruction pair + that loads consts skipping NOP's in between. +*/ +static bool +find_load_const_pair(basicblock *bb, int start, cfg_instr **first, cfg_instr **second) +{ + cfg_instr *second_load_const = NULL; + while (start >= 0) { + cfg_instr *inst = &bb->b_instr[start--]; + if (inst->i_opcode == NOP) { + continue; + } + if (!loads_const(inst->i_opcode)) { + return false; + } + if (second_load_const == NULL) { + second_load_const = inst; + continue; + } + *first = inst; + *second = second_load_const; + return true; + } + return false; +} + +/* Determine opcode & oparg for freshly folded constant. */ +static int +newop_from_folded(PyObject *newconst, PyObject *consts, + PyObject *const_cache, int *newopcode, int *newoparg) +{ + if (PyLong_CheckExact(newconst)) { + int overflow; + long val = PyLong_AsLongAndOverflow(newconst, &overflow); + if (!overflow && _PY_IS_SMALL_INT(val)) { + *newopcode = LOAD_SMALL_INT; + *newoparg = val; + return SUCCESS; + } + } + *newopcode = LOAD_CONST; + *newoparg = add_const(newconst, consts, const_cache); + RETURN_IF_ERROR(*newoparg); + return SUCCESS; +} + +static int +optimize_if_const_subscr(basicblock *bb, int n, PyObject *consts, PyObject *const_cache) +{ + cfg_instr *subscr = &bb->b_instr[n]; + assert(subscr->i_opcode == BINARY_SUBSCR); + cfg_instr *arg, *idx; + if (!find_load_const_pair(bb, n-1, &arg, &idx)) { + return SUCCESS; + } + PyObject *o, *key; + if ((o = get_const_value(arg->i_opcode, arg->i_oparg, consts)) == NULL + || (key = get_const_value(idx->i_opcode, idx->i_oparg, consts)) == NULL) + { + return ERROR; + } + PyObject *newconst = PyObject_GetItem(o, key); + if (newconst == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) { + return ERROR; + } + PyErr_Clear(); + return SUCCESS; + } + int newopcode, newoparg; + RETURN_IF_ERROR(newop_from_folded(newconst, consts, const_cache, &newopcode, &newoparg)); + INSTR_SET_OP1(subscr, newopcode, newoparg); + INSTR_SET_OP0(arg, NOP); + INSTR_SET_OP0(idx, NOP); + return SUCCESS; +} + #define VISITED (-1) // Replace an arbitrary run of SWAPs and NOPs with an optimal one that has the @@ -1948,6 +2027,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); } break; + case BINARY_SUBSCR: + RETURN_IF_ERROR(optimize_if_const_subscr(bb, i, consts, const_cache)); + break; } } From d3c54f37889436ded4520e78e4e59d3f3350aa44 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Tue, 4 Feb 2025 11:38:06 +0100 Subject: [PATCH 061/311] gh-126835: Fix reference leak in `Python/flowgrapc.::optimize_if_const_subscr` (#129634) --- Python/flowgraph.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 9ca7fadb8d7665..95ab53ce64301c 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1500,13 +1500,15 @@ optimize_if_const_subscr(basicblock *bb, int n, PyObject *consts, PyObject *cons if (!find_load_const_pair(bb, n-1, &arg, &idx)) { return SUCCESS; } - PyObject *o, *key; + PyObject *o = NULL, *key = NULL; if ((o = get_const_value(arg->i_opcode, arg->i_oparg, consts)) == NULL || (key = get_const_value(idx->i_opcode, idx->i_oparg, consts)) == NULL) { - return ERROR; + goto error; } PyObject *newconst = PyObject_GetItem(o, key); + Py_DECREF(o); + Py_DECREF(key); if (newconst == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) { return ERROR; @@ -1520,6 +1522,10 @@ optimize_if_const_subscr(basicblock *bb, int n, PyObject *consts, PyObject *cons INSTR_SET_OP0(arg, NOP); INSTR_SET_OP0(idx, NOP); return SUCCESS; +error: + Py_XDECREF(o); + Py_XDECREF(key); + return ERROR; } #define VISITED (-1) From 2effea4dab05160abc05f1a548d519a5f6d684bc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 4 Feb 2025 12:18:31 +0000 Subject: [PATCH 062/311] GH-128682: Spill the stack pointer in labels, as well as instructions (GH-129618) --- Include/internal/pycore_optimizer.h | 2 +- Lib/test/test_generated_cases.py | 90 ++++++++++++++++++-- Python/bytecodes.c | 45 +++++----- Python/executor_cases.c.h | 4 +- Python/generated_cases.c.h | 48 ++++++++--- Python/optimizer.c | 3 +- Tools/cases_generator/analyzer.py | 58 ++++++++----- Tools/cases_generator/generators_common.py | 87 +++++++++++++------ Tools/cases_generator/lexer.py | 2 + Tools/cases_generator/optimizer_generator.py | 5 +- Tools/cases_generator/parser.py | 1 + Tools/cases_generator/parsing.py | 6 +- Tools/cases_generator/stack.py | 2 +- Tools/cases_generator/tier1_generator.py | 12 ++- Tools/cases_generator/tier2_generator.py | 14 +-- 15 files changed, 277 insertions(+), 102 deletions(-) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index e806e306d2d57f..00fc4338b0a412 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -282,7 +282,7 @@ extern int _Py_uop_frame_pop(JitOptContext *ctx); PyAPI_FUNC(PyObject *) _Py_uop_symbols_test(PyObject *self, PyObject *ignored); -PyAPI_FUNC(int) _PyOptimizer_Optimize(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *start, _PyStackRef *stack_pointer, _PyExecutorObject **exec_ptr, int chain_depth); +PyAPI_FUNC(int) _PyOptimizer_Optimize(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *start, _PyExecutorObject **exec_ptr, int chain_depth); static inline int is_terminator(const _PyUOpInstruction *uop) { diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 35600ce5486642..d2b33706ea6b75 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -286,7 +286,7 @@ def run_cases_test(self, input: str, expected: str): instructions, labels_with_prelude_and_postlude = rest.split(tier1_generator.INSTRUCTION_END_MARKER) _, labels_with_postlude = labels_with_prelude_and_postlude.split(tier1_generator.LABEL_START_MARKER) labels, _ = labels_with_postlude.split(tier1_generator.LABEL_END_MARKER) - actual = instructions + labels + actual = instructions.strip() + "\n\n " + labels.strip() # if actual.strip() != expected.strip(): # print("Actual:") # print(actual) @@ -652,6 +652,9 @@ def test_cache_effect(self): def test_suppress_dispatch(self): input = """ + label(somewhere) { + } + inst(OP, (--)) { goto somewhere; } @@ -663,6 +666,11 @@ def test_suppress_dispatch(self): INSTRUCTION_STATS(OP); goto somewhere; } + + somewhere: + { + + } """ self.run_cases_test(input, output) @@ -1768,9 +1776,15 @@ def test_kill_in_wrong_order(self): def test_complex_label(self): input = """ + label(other_label) { + } + + label(other_label2) { + } + label(my_label) { // Comment - do_thing() + do_thing(); if (complex) { goto other_label; } @@ -1779,10 +1793,22 @@ def test_complex_label(self): """ output = """ + other_label: + { + + } + + other_label2: + { + + } + my_label: { // Comment - do_thing() + _PyFrame_SetStackPointer(frame, stack_pointer); + do_thing(); + stack_pointer = _PyFrame_GetStackPointer(frame); if (complex) { goto other_label; } @@ -1791,6 +1817,60 @@ def test_complex_label(self): """ self.run_cases_test(input, output) + def test_spilled_label(self): + input = """ + spilled label(one) { + RELOAD_STACK(); + goto two; + } + + label(two) { + SAVE_STACK(); + goto one; + } + """ + + output = """ + one: + { + /* STACK SPILLED */ + stack_pointer = _PyFrame_GetStackPointer(frame); + goto two; + } + + two: + { + _PyFrame_SetStackPointer(frame, stack_pointer); + goto one; + } + """ + self.run_cases_test(input, output) + + + def test_incorrect_spills(self): + input1 = """ + spilled label(one) { + goto two; + } + + label(two) { + } + """ + + input2 = """ + spilled label(one) { + } + + label(two) { + goto one; + } + """ + with self.assertRaisesRegex(SyntaxError, ".*reload.*"): + self.run_cases_test(input1, "") + with self.assertRaisesRegex(SyntaxError, ".*spill.*"): + self.run_cases_test(input2, "") + + def test_multiple_labels(self): input = """ label(my_label_1) { @@ -1802,7 +1882,7 @@ def test_multiple_labels(self): label(my_label_2) { // Comment do_thing2(); - goto my_label_3; + goto my_label_1; } """ @@ -1818,7 +1898,7 @@ def test_multiple_labels(self): { // Comment do_thing2(); - goto my_label_3; + goto my_label_1; } """ diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e679d90620ea9e..cb88ba74f9a5fe 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2808,7 +2808,7 @@ dummy_func( start--; } _PyExecutorObject *executor; - int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0); + int optimized = _PyOptimizer_Optimize(frame, start, &executor, 0); if (optimized <= 0) { this_instr[1].counter = restart_backoff_counter(counter); ERROR_IF(optimized < 0, error); @@ -5033,7 +5033,7 @@ dummy_func( } else { int chain_depth = current_executor->vm_data.chain_depth + 1; - int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor, chain_depth); + int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth); if (optimized <= 0) { exit->temperature = restart_backoff_counter(temperature); if (optimized < 0) { @@ -5134,7 +5134,7 @@ dummy_func( exit->temperature = advance_backoff_counter(exit->temperature); GOTO_TIER_ONE(target); } - int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor, 0); + int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0); if (optimized <= 0) { exit->temperature = restart_backoff_counter(exit->temperature); if (optimized < 0) { @@ -5242,29 +5242,29 @@ dummy_func( goto exception_unwind; } - label(exception_unwind) { + spilled label(exception_unwind) { /* We can't use frame->instr_ptr here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; - if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) { + int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti); + if (handled == 0) { // No handlers, so exit. assert(_PyErr_Occurred(tstate)); - /* Pop remaining stack entries. */ _PyStackRef *stackbase = _PyFrame_Stackbase(frame); - while (stack_pointer > stackbase) { - PyStackRef_XCLOSE(POP()); + while (frame->stackpointer > stackbase) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); } - assert(STACK_LEVEL() == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); monitor_unwind(tstate, frame, next_instr-1); goto exit_unwind; } - assert(STACK_LEVEL() >= level); _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; - while (stack_pointer > new_top) { - PyStackRef_XCLOSE(POP()); + assert(frame->stackpointer >= new_top); + while (frame->stackpointer > new_top) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); } if (lasti) { int frame_lasti = _PyInterpreterFrame_LASTI(frame); @@ -5272,7 +5272,7 @@ dummy_func( if (lasti == NULL) { goto exception_unwind; } - PUSH(PyStackRef_FromPyObjectSteal(lasti)); + _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti)); } /* Make the raw exception data @@ -5280,10 +5280,11 @@ dummy_func( so a program can emulate the Python main loop. */ PyObject *exc = _PyErr_GetRaisedException(tstate); - PUSH(PyStackRef_FromPyObjectSteal(exc)); + _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc)); next_instr = _PyFrame_GetBytecode(frame) + handler; - if (monitor_handled(tstate, frame, next_instr, exc) < 0) { + int err = monitor_handled(tstate, frame, next_instr, exc); + if (err < 0) { goto exception_unwind; } /* Resume normal execution */ @@ -5292,10 +5293,11 @@ dummy_func( lltrace_resume_frame(frame); } #endif + RELOAD_STACK(); DISPATCH(); } - label(exit_unwind) { + spilled label(exit_unwind) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -5311,16 +5313,16 @@ dummy_func( return NULL; } next_instr = frame->instr_ptr; - stack_pointer = _PyFrame_GetStackPointer(frame); + RELOAD_STACK(); goto error; } - label(start_frame) { - if (_Py_EnterRecursivePy(tstate)) { + spilled label(start_frame) { + int too_deep = _Py_EnterRecursivePy(tstate); + if (too_deep) { goto exit_unwind; } next_instr = frame->instr_ptr; - stack_pointer = _PyFrame_GetStackPointer(frame); #ifdef LLTRACE { @@ -5339,6 +5341,7 @@ dummy_func( assert(!_PyErr_Occurred(tstate)); #endif + RELOAD_STACK(); DISPATCH(); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 59f1a1ba4dc92a..5b19ec182b5805 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -6331,7 +6331,7 @@ else { int chain_depth = current_executor->vm_data.chain_depth + 1; _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor, chain_depth); + int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth); stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { exit->temperature = restart_backoff_counter(temperature); @@ -6498,7 +6498,7 @@ GOTO_TIER_ONE(target); } _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, target, stack_pointer, &executor, 0); + int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0); stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { exit->temperature = restart_backoff_counter(exit->temperature); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7dd9d6528bb49d..0bc92f30bfded2 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3420,6 +3420,7 @@ _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); goto exception_unwind; } stack_pointer[-3] = none; @@ -4000,6 +4001,7 @@ _PyErr_SetRaisedException(tstate, exc); monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); goto exception_unwind; } stack_pointer += -2; @@ -5866,7 +5868,7 @@ } _PyExecutorObject *executor; _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0); + int optimized = _PyOptimizer_Optimize(frame, start, &executor, 0); stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { this_instr[1].counter = restart_backoff_counter(counter); @@ -7838,6 +7840,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); goto exception_unwind; } goto error; @@ -7883,6 +7886,7 @@ _PyErr_SetRaisedException(tstate, exc); monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); goto exception_unwind; } @@ -9337,8 +9341,10 @@ /* Double-check exception status. */ #ifdef NDEBUG if (!_PyErr_Occurred(tstate)) { + _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetString(tstate, PyExc_SystemError, "error return without exception set"); + stack_pointer = _PyFrame_GetStackPointer(frame); } #else assert(_PyErr_Occurred(tstate)); @@ -9347,37 +9353,47 @@ /* Log traceback info. */ assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); if (!_PyFrame_IsIncomplete(frame)) { + _PyFrame_SetStackPointer(frame, stack_pointer); PyFrameObject *f = _PyFrame_GetFrameObject(frame); + stack_pointer = _PyFrame_GetStackPointer(frame); if (f != NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); PyTraceBack_Here(f); + stack_pointer = _PyFrame_GetStackPointer(frame); } } + _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_MonitorRaise(tstate, frame, next_instr-1); + stack_pointer = _PyFrame_GetStackPointer(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); goto exception_unwind; } exception_unwind: { + /* STACK SPILLED */ /* We can't use frame->instr_ptr here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; - if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) { + int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti); + if (handled == 0) { // No handlers, so exit. assert(_PyErr_Occurred(tstate)); /* Pop remaining stack entries. */ _PyStackRef *stackbase = _PyFrame_Stackbase(frame); - while (stack_pointer > stackbase) { - PyStackRef_XCLOSE(POP()); + while (frame->stackpointer > stackbase) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); } - assert(STACK_LEVEL() == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); monitor_unwind(tstate, frame, next_instr-1); goto exit_unwind; } assert(STACK_LEVEL() >= level); _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; - while (stack_pointer > new_top) { - PyStackRef_XCLOSE(POP()); + assert(frame->stackpointer >= new_top); + while (frame->stackpointer > new_top) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); } if (lasti) { int frame_lasti = _PyInterpreterFrame_LASTI(frame); @@ -9385,16 +9401,17 @@ if (lasti == NULL) { goto exception_unwind; } - PUSH(PyStackRef_FromPyObjectSteal(lasti)); + _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti)); } /* Make the raw exception data available to the handler, so a program can emulate the Python main loop. */ PyObject *exc = _PyErr_GetRaisedException(tstate); - PUSH(PyStackRef_FromPyObjectSteal(exc)); + _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc)); next_instr = _PyFrame_GetBytecode(frame) + handler; - if (monitor_handled(tstate, frame, next_instr, exc) < 0) { + int err = monitor_handled(tstate, frame, next_instr, exc); + if (err < 0) { goto exception_unwind; } /* Resume normal execution */ @@ -9403,11 +9420,13 @@ lltrace_resume_frame(frame); } #endif + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } exit_unwind: { + /* STACK SPILLED */ assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -9429,11 +9448,12 @@ start_frame: { - if (_Py_EnterRecursivePy(tstate)) { + /* STACK SPILLED */ + int too_deep = _Py_EnterRecursivePy(tstate); + if (too_deep) { goto exit_unwind; } next_instr = frame->instr_ptr; - stack_pointer = _PyFrame_GetStackPointer(frame); #ifdef LLTRACE { int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); @@ -9450,7 +9470,7 @@ caller loses its exception */ assert(!_PyErr_Occurred(tstate)); #endif - + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } diff --git a/Python/optimizer.c b/Python/optimizer.c index d71abd3224240b..97831f58098c95 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -105,8 +105,9 @@ uop_optimize(_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, int _PyOptimizer_Optimize( _PyInterpreterFrame *frame, _Py_CODEUNIT *start, - _PyStackRef *stack_pointer, _PyExecutorObject **executor_ptr, int chain_depth) + _PyExecutorObject **executor_ptr, int chain_depth) { + _PyStackRef *stack_pointer = frame->stackpointer; assert(_PyInterpreterState_GET()->jit); // The first executor in a chain and the MAX_CHAIN_DEPTH'th executor *must* // make progress in order to avoid infinite loops or excessively-long diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index eda8d687a70ccd..724fba5f953a4e 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -130,6 +130,8 @@ def size(self) -> int: return 0 + + @dataclass class StackItem: name: str @@ -228,7 +230,24 @@ def is_super(self) -> bool: return False +class Label: + + def __init__(self, name: str, spilled: bool, body: list[lexer.Token], properties: Properties): + self.name = name + self.spilled = spilled + self.body = body + self.properties = properties + + size:int = 0 + output_stores: list[lexer.Token] = [] + instruction_size = None + + def __str__(self) -> str: + return f"label({self.name})" + + Part = Uop | Skip | Flush +CodeSection = Uop | Label @dataclass @@ -268,12 +287,6 @@ def is_super(self) -> bool: return False -@dataclass -class Label: - name: str - body: list[lexer.Token] - - @dataclass class PseudoInstruction: name: str @@ -503,22 +516,24 @@ def in_frame_push(idx: int) -> bool: return refs -def variable_used(node: parser.InstDef, name: str) -> bool: +def variable_used(node: parser.CodeDef, name: str) -> bool: """Determine whether a variable with a given name is used in a node.""" return any( token.kind == "IDENTIFIER" and token.text == name for token in node.block.tokens ) -def oparg_used(node: parser.InstDef) -> bool: +def oparg_used(node: parser.CodeDef) -> bool: """Determine whether `oparg` is used in a node.""" return any( token.kind == "IDENTIFIER" and token.text == "oparg" for token in node.tokens ) -def tier_variable(node: parser.InstDef) -> int | None: +def tier_variable(node: parser.CodeDef) -> int | None: """Determine whether a tier variable is used in a node.""" + if isinstance(node, parser.LabelDef): + return None for token in node.tokens: if token.kind == "ANNOTATION": if token.text == "specializing": @@ -528,7 +543,7 @@ def tier_variable(node: parser.InstDef) -> int | None: return None -def has_error_with_pop(op: parser.InstDef) -> bool: +def has_error_with_pop(op: parser.CodeDef) -> bool: return ( variable_used(op, "ERROR_IF") or variable_used(op, "pop_1_error") @@ -536,7 +551,7 @@ def has_error_with_pop(op: parser.InstDef) -> bool: ) -def has_error_without_pop(op: parser.InstDef) -> bool: +def has_error_without_pop(op: parser.CodeDef) -> bool: return ( variable_used(op, "ERROR_NO_POP") or variable_used(op, "pop_1_error") @@ -665,7 +680,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "restart_backoff_counter", ) -def find_stmt_start(node: parser.InstDef, idx: int) -> lexer.Token: +def find_stmt_start(node: parser.CodeDef, idx: int) -> lexer.Token: assert idx < len(node.block.tokens) while True: tkn = node.block.tokens[idx-1] @@ -678,7 +693,7 @@ def find_stmt_start(node: parser.InstDef, idx: int) -> lexer.Token: return node.block.tokens[idx] -def find_stmt_end(node: parser.InstDef, idx: int) -> lexer.Token: +def find_stmt_end(node: parser.CodeDef, idx: int) -> lexer.Token: assert idx < len(node.block.tokens) while True: idx += 1 @@ -686,7 +701,7 @@ def find_stmt_end(node: parser.InstDef, idx: int) -> lexer.Token: if tkn.kind == "SEMI": return node.block.tokens[idx+1] -def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, EscapingCall]) -> None: +def check_escaping_calls(instr: parser.CodeDef, escapes: dict[lexer.Token, EscapingCall]) -> None: calls = {e.call for e in escapes.values()} in_if = 0 tkn_iter = iter(instr.block.tokens) @@ -705,7 +720,7 @@ def check_escaping_calls(instr: parser.InstDef, escapes: dict[lexer.Token, Escap elif tkn in calls and in_if: raise analysis_error(f"Escaping call '{tkn.text} in condition", tkn) -def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, EscapingCall]: +def find_escaping_api_calls(instr: parser.CodeDef) -> dict[lexer.Token, EscapingCall]: result: dict[lexer.Token, EscapingCall] = {} tokens = instr.block.tokens for idx, tkn in enumerate(tokens): @@ -764,7 +779,7 @@ def find_escaping_api_calls(instr: parser.InstDef) -> dict[lexer.Token, Escaping } -def always_exits(op: parser.InstDef) -> bool: +def always_exits(op: parser.CodeDef) -> bool: depth = 0 tkn_iter = iter(op.tokens) for tkn in tkn_iter: @@ -823,7 +838,7 @@ def effect_depends_on_oparg_1(op: parser.InstDef) -> bool: return False -def compute_properties(op: parser.InstDef) -> Properties: +def compute_properties(op: parser.CodeDef) -> Properties: escaping_calls = find_escaping_api_calls(op) has_free = ( variable_used(op, "PyCell_New") @@ -851,6 +866,8 @@ def compute_properties(op: parser.InstDef) -> Properties: variable_used(op, "Py_CLEAR") or variable_used(op, "SETLOCAL") ) + pure = False if isinstance(op, parser.LabelDef) else "pure" in op.annotations + no_save_ip = False if isinstance(op, parser.LabelDef) else "no_save_ip" in op.annotations return Properties( escaping_calls=escaping_calls, escapes=escapes, @@ -870,8 +887,8 @@ def compute_properties(op: parser.InstDef) -> Properties: and not has_free, uses_opcode=variable_used(op, "opcode"), has_free=has_free, - pure="pure" in op.annotations, - no_save_ip="no_save_ip" in op.annotations, + pure=pure, + no_save_ip=no_save_ip, tier=tier_variable(op), needs_prev=variable_used(op, "prev_instr"), ) @@ -1050,7 +1067,8 @@ def add_label( label: parser.LabelDef, labels: dict[str, Label], ) -> None: - labels[label.name] = Label(label.name, label.block.tokens) + properties = compute_properties(label) + labels[label.name] = Label(label.name, label.spilled, label.block.tokens, properties) def assign_opcodes( diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 6f2af5fc01c47b..1c572ec0512b37 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -7,6 +7,8 @@ Properties, StackItem, analysis_error, + Label, + CodeSection, ) from cwriter import CWriter from typing import Callable, TextIO, Iterator, Iterable @@ -90,7 +92,7 @@ def emit_to(out: CWriter, tkn_iter: TokenIterator, end: str) -> Token: ReplacementFunctionType = Callable[ - [Token, TokenIterator, Uop, Storage, Instruction | None], bool + [Token, TokenIterator, CodeSection, Storage, Instruction | None], bool ] def always_true(tkn: Token | None) -> bool: @@ -106,9 +108,10 @@ def always_true(tkn: Token | None) -> bool: class Emitter: out: CWriter + labels: dict[str, Label] _replacers: dict[str, ReplacementFunctionType] - def __init__(self, out: CWriter): + def __init__(self, out: CWriter, labels: dict[str, Label]): self._replacers = { "EXIT_IF": self.exit_if, "DEOPT_IF": self.deopt_if, @@ -124,18 +127,22 @@ def __init__(self, out: CWriter): "PyStackRef_AsPyObjectSteal": self.stackref_steal, "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, - "POP_INPUT": self.pop_input + "POP_INPUT": self.pop_input, + "stack_pointer": self.stack_pointer, } self.out = out + self.labels = labels def dispatch( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: + if storage.spilled: + raise analysis_error("stack_pointer needs reloading before dispatch", tkn) self.emit(tkn) return False @@ -143,7 +150,7 @@ def deopt_if( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -174,7 +181,7 @@ def error_if( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -213,7 +220,7 @@ def error_no_pop( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -227,7 +234,7 @@ def decref_inputs( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -263,7 +270,7 @@ def kill_inputs( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -278,7 +285,7 @@ def kill( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -318,7 +325,7 @@ def stackref_close_specialized( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -348,7 +355,7 @@ def stackref_steal( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -368,7 +375,7 @@ def sync_sp( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -380,6 +387,32 @@ def sync_sp( self._print_storage(storage) return True + def stack_pointer( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + if storage.spilled: + raise analysis_error("stack_pointer is invalid when stack is spilled to memory", tkn) + self.emit(tkn) + return True + + def goto_label(self, goto: Token, label: Token, storage: Storage) -> None: + if label.text not in self.labels: + print(self.labels.keys()) + raise analysis_error(f"Label '{label.text}' does not exist", label) + label_node = self.labels[label.text] + if label_node.spilled: + if not storage.spilled: + self.emit_save(storage) + elif storage.spilled: + raise analysis_error("Cannot jump from spilled label without reloading the stack pointer", goto) + self.out.emit(goto) + self.out.emit(label) + def emit_save(self, storage: Storage) -> None: storage.save(self.out) self._print_storage(storage) @@ -388,7 +421,7 @@ def save_stack( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -402,7 +435,7 @@ def pop_input( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -429,7 +462,7 @@ def reload_stack( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -442,7 +475,7 @@ def reload_stack( def instruction_size(self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -461,7 +494,7 @@ def _print_storage(self, storage: Storage) -> None: def _emit_if( self, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> tuple[bool, Token, Storage]: @@ -521,7 +554,7 @@ def _emit_if( def _emit_block( self, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, emit_first_brace: bool @@ -568,8 +601,9 @@ def _emit_block( return reachable, tkn, storage self.out.emit(tkn) elif tkn.kind == "GOTO": + label_tkn = next(tkn_iter) + self.goto_label(tkn, label_tkn, storage) reachable = False; - self.out.emit(tkn) elif tkn.kind == "IDENTIFIER": if tkn.text in self._replacers: if not self._replacers[tkn.text](tkn, tkn_iter, uop, storage, inst): @@ -599,17 +633,18 @@ def _emit_block( def emit_tokens( self, - uop: Uop, + code: CodeSection, storage: Storage, inst: Instruction | None, ) -> Storage: - tkn_iter = TokenIterator(uop.body) + tkn_iter = TokenIterator(code.body) self.out.start_line() - _, rbrace, storage = self._emit_block(tkn_iter, uop, storage, inst, False) + reachable, rbrace, storage = self._emit_block(tkn_iter, code, storage, inst, False) try: - self._print_storage(storage) - storage.push_outputs() - self._print_storage(storage) + if reachable: + self._print_storage(storage) + storage.push_outputs() + self._print_storage(storage) except StackError as ex: raise analysis_error(ex.args[0], rbrace) from None return storage diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index cf3c39762f29cb..6afca750be9b19 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -216,6 +216,8 @@ def choice(*opts: str) -> str: # A label in the DSL LABEL = "LABEL" kwds.append(LABEL) +SPILLED = "SPILLED" +kwds.append(SPILLED) keywords = {name.lower(): name for name in kwds} ANNOTATION = "ANNOTATION" diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index 5cfec4bfecbf07..6c33debd58e1fe 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -112,6 +112,9 @@ def emit_save(self, storage: Storage) -> None: def emit_reload(self, storage: Storage) -> None: pass + def goto_label(self, goto: Token, label: Token, storage: Storage) -> None: + self.out.emit(goto) + self.out.emit(label) def write_uop( override: Uop | None, @@ -145,7 +148,7 @@ def write_uop( cast = f"uint{cache.size*16}_t" out.emit(f"{type}{cache.name} = ({cast})this_instr->operand0;\n") if override: - emitter = OptimizerEmitter(out) + emitter = OptimizerEmitter(out, {}) # No reference management of inputs needed. for var in storage.inputs: # type: ignore[possibly-undefined] var.defined = False diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index 68bbb88719e682..696c5c16432990 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -13,6 +13,7 @@ AstNode, ) +CodeDef = InstDef | LabelDef def prettify_filename(filename: str) -> str: # Make filename more user-friendly and less platform-specific, diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index eb8c8a7ecd32e8..011f34de288871 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -153,6 +153,7 @@ class Pseudo(Node): @dataclass class LabelDef(Node): name: str + spilled: bool block: Block @@ -176,12 +177,15 @@ def definition(self) -> AstNode | None: @contextual def label_def(self) -> LabelDef | None: + spilled = False + if self.expect(lx.SPILLED): + spilled = True if self.expect(lx.LABEL): if self.expect(lx.LPAREN): if tkn := self.expect(lx.IDENTIFIER): if self.expect(lx.RPAREN): if block := self.block(): - return LabelDef(tkn.text, block) + return LabelDef(tkn.text, spilled, block) return None @contextual diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 5121837ed8334b..729973f1e32758 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -570,7 +570,7 @@ def copy(self) -> "Storage": assert [v.name for v in inputs] == [v.name for v in self.inputs], (inputs, self.inputs) return Storage( new_stack, inputs, - self.copy_list(self.outputs), self.copy_list(self.peeks) + self.copy_list(self.outputs), self.copy_list(self.peeks), self.spilled ) def sanity_check(self) -> None: diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index eed3086c327926..c7cf09e2ec4ede 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -184,19 +184,25 @@ def generate_tier1_labels( analysis: Analysis, outfile: TextIO, lines: bool ) -> None: out = CWriter(outfile, 2, lines) + emitter = Emitter(out, analysis.labels) out.emit("\n") for name, label in analysis.labels.items(): out.emit(f"{name}:\n") - for tkn in label.body: - out.emit(tkn) + out.emit("{\n") + storage = Storage(Stack(), [], [], []) + if label.spilled: + storage.spilled = 1 + out.emit("/* STACK SPILLED */\n") + emitter.emit_tokens(label, storage, None) out.emit("\n") + out.emit("}\n") out.emit("\n") def generate_tier1_cases( analysis: Analysis, outfile: TextIO, lines: bool ) -> None: out = CWriter(outfile, 2, lines) - emitter = Emitter(out) + emitter = Emitter(out, analysis.labels) out.emit("\n") for name, inst in sorted(analysis.instructions.items()): needs_this = uses_this(inst) diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index 4540eb252634ba..5e23360cdc0aaf 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -9,6 +9,8 @@ Analysis, Instruction, Uop, + Label, + CodeSection, analyze_files, StackItem, analysis_error, @@ -65,8 +67,8 @@ def declare_variables(uop: Uop, out: CWriter) -> None: class Tier2Emitter(Emitter): - def __init__(self, out: CWriter): - super().__init__(out) + def __init__(self, out: CWriter, labels: dict[str, Label]): + super().__init__(out, labels) self._replacers["oparg"] = self.oparg def goto_error(self, offset: int, label: str, storage: Storage) -> str: @@ -79,7 +81,7 @@ def deopt_if( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -100,7 +102,7 @@ def exit_if( # type: ignore[override] self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -120,7 +122,7 @@ def oparg( self, tkn: Token, tkn_iter: TokenIterator, - uop: Uop, + uop: CodeSection, storage: Storage, inst: Instruction | None, ) -> bool: @@ -180,7 +182,7 @@ def generate_tier2( """ ) out = CWriter(outfile, 2, lines) - emitter = Tier2Emitter(out) + emitter = Tier2Emitter(out, analysis.labels) out.emit("\n") for name, uop in analysis.uops.items(): if uop.properties.tier == 1: From 96ff4c2486e0c3efb62ce3e712c9bd919dbb6b20 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 4 Feb 2025 14:00:51 +0000 Subject: [PATCH 063/311] GH-128682: Mark two more macros as escaping. (GH-129645) Expand out SETLOCAL so that code generator can see the decref. Mark Py_CLEAR as escaping --- Python/bytecodes.c | 25 +++++++---- Python/ceval.c | 1 + Python/ceval_macros.h | 9 ---- Python/executor_cases.c.h | 69 +++++++++++++++++++++++++------ Python/generated_cases.c.h | 57 +++++++++++++++++++++---- Tools/cases_generator/analyzer.py | 12 +----- 6 files changed, 127 insertions(+), 46 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index cb88ba74f9a5fe..b650613650cf36 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -272,7 +272,6 @@ dummy_func( inst(LOAD_FAST_AND_CLEAR, (-- value)) { value = GETLOCAL(oparg); - // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = PyStackRef_NULL; } @@ -328,8 +327,10 @@ dummy_func( } replicate(8) inst(STORE_FAST, (value --)) { - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; DEAD(value); + PyStackRef_XCLOSE(tmp); } pseudo(STORE_FAST_MAYBE_NULL, (unused --)) = { @@ -339,18 +340,24 @@ dummy_func( inst(STORE_FAST_LOAD_FAST, (value1 -- value2)) { uint32_t oparg1 = oparg >> 4; uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; DEAD(value1); value2 = PyStackRef_DUP(GETLOCAL(oparg2)); + PyStackRef_XCLOSE(tmp); } inst(STORE_FAST_STORE_FAST, (value2, value1 --)) { uint32_t oparg1 = oparg >> 4; uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; DEAD(value1); - SETLOCAL(oparg2, value2); + PyStackRef_XCLOSE(tmp); + tmp = GETLOCAL(oparg2); + GETLOCAL(oparg2) = value2; DEAD(value2); + PyStackRef_XCLOSE(tmp); } pure inst(POP_TOP, (value --)) { @@ -1775,7 +1782,9 @@ dummy_func( ); ERROR_IF(1, error); } - SETLOCAL(oparg, PyStackRef_NULL); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + PyStackRef_XCLOSE(tmp); } inst(MAKE_CELL, (--)) { @@ -1786,7 +1795,9 @@ dummy_func( if (cell == NULL) { ERROR_NO_POP(); } - SETLOCAL(oparg, PyStackRef_FromPyObjectSteal(cell)); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); + PyStackRef_XCLOSE(tmp); } inst(DELETE_DEREF, (--)) { diff --git a/Python/ceval.c b/Python/ceval.c index 11518684c136bd..4f628fdbabddbb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -194,6 +194,7 @@ lltrace_instruction(_PyInterpreterFrame *frame, } fflush(stdout); } + static void lltrace_resume_frame(_PyInterpreterFrame *frame) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index c2fc38f3c18e53..b6d9df32953892 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -221,15 +221,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define LOCALS_ARRAY (frame->localsplus) #define GETLOCAL(i) (frame->localsplus[i]) -/* The SETLOCAL() macro must not DECREF the local variable in-place and - then store the new value; it must copy the old value to a temporary - value, then store the new value, and then DECREF the temporary value. - This is because it is possible that during the DECREF the frame is - accessed by other code (e.g. a __del__ method or gc.collect()) and the - variable would be pointing to already-freed memory. */ -#define SETLOCAL(i, value) do { _PyStackRef tmp = GETLOCAL(i); \ - GETLOCAL(i) = value; \ - PyStackRef_XCLOSE(tmp); } while (0) #ifdef Py_STATS #define UPDATE_MISS_STATS(INSTNAME) \ diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 5b19ec182b5805..22d11059fcadb8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -205,7 +205,6 @@ _PyStackRef value; oparg = CURRENT_OPARG(); value = GETLOCAL(oparg); - // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = PyStackRef_NULL; stack_pointer[0] = value; stack_pointer += 1; @@ -307,9 +306,13 @@ oparg = 0; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -318,9 +321,13 @@ oparg = 1; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -329,9 +336,13 @@ oparg = 2; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -340,9 +351,13 @@ oparg = 3; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -351,9 +366,13 @@ oparg = 4; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -362,9 +381,13 @@ oparg = 5; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -373,9 +396,13 @@ oparg = 6; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -384,9 +411,13 @@ oparg = 7; assert(oparg == CURRENT_OPARG()); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -394,9 +425,13 @@ _PyStackRef value; oparg = CURRENT_OPARG(); value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -2255,7 +2290,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_ERROR(); } - SETLOCAL(oparg, PyStackRef_NULL); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -2268,7 +2307,11 @@ if (cell == NULL) { JUMP_TO_ERROR(); } - SETLOCAL(oparg, PyStackRef_FromPyObjectSteal(cell)); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -6314,7 +6357,9 @@ #endif if (exit->executor && !exit->executor->vm_data.valid) { exit->temperature = initial_temperature_backoff_counter(); + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(exit->executor); + stack_pointer = _PyFrame_GetStackPointer(frame); } if (exit->executor == NULL) { _Py_BackoffCounter temperature = exit->temperature; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0bc92f30bfded2..5820147ff712a9 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1082,7 +1082,9 @@ frame, this_instr, callable_o, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -1842,7 +1844,9 @@ frame, this_instr, func, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(result_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -2163,7 +2167,9 @@ frame, this_instr, callable_o, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -3837,7 +3843,11 @@ stack_pointer = _PyFrame_GetStackPointer(frame); goto error; } - SETLOCAL(oparg, PyStackRef_NULL); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -4781,7 +4791,9 @@ frame, this_instr, callable_o, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -4920,7 +4932,9 @@ frame, this_instr, func, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(result_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -5155,7 +5169,9 @@ frame, this_instr, callable_o, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -5421,7 +5437,9 @@ frame, this_instr, global_super, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(super); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -6858,7 +6876,6 @@ INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); _PyStackRef value; value = GETLOCAL(oparg); - // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = PyStackRef_NULL; stack_pointer[0] = value; stack_pointer += 1; @@ -7338,7 +7355,9 @@ frame, this_instr, global_super, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); Py_CLEAR(super); + stack_pointer = _PyFrame_GetStackPointer(frame); } } } @@ -7472,7 +7491,11 @@ if (cell == NULL) { goto error; } - SETLOCAL(oparg, PyStackRef_FromPyObjectSteal(cell)); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -8545,9 +8568,13 @@ INSTRUCTION_STATS(STORE_FAST); _PyStackRef value; value = stack_pointer[-1]; - SETLOCAL(oparg, value); + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -8560,9 +8587,13 @@ value1 = stack_pointer[-1]; uint32_t oparg1 = oparg >> 4; uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; value2 = PyStackRef_DUP(GETLOCAL(oparg2)); stack_pointer[-1] = value2; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } @@ -8576,10 +8607,20 @@ value2 = stack_pointer[-2]; uint32_t oparg1 = oparg >> 4; uint32_t oparg2 = oparg & 15; - SETLOCAL(oparg1, value1); - SETLOCAL(oparg2, value2); - stack_pointer += -2; + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; + stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + tmp = GETLOCAL(oparg2); + GETLOCAL(oparg2) = value2; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 724fba5f953a4e..11a559bca474b0 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -607,7 +607,6 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "PyUnicode_GET_LENGTH", "PyUnicode_READ_CHAR", "Py_ARRAY_LENGTH", - "Py_CLEAR", "Py_FatalError", "Py_INCREF", "Py_IS_TYPE", @@ -859,13 +858,7 @@ def compute_properties(op: parser.CodeDef) -> Properties: ) error_with_pop = has_error_with_pop(op) error_without_pop = has_error_without_pop(op) - escapes = ( - bool(escaping_calls) or - variable_used(op, "Py_DECREF") or - variable_used(op, "Py_XDECREF") or - variable_used(op, "Py_CLEAR") or - variable_used(op, "SETLOCAL") - ) + escapes = bool(escaping_calls) pure = False if isinstance(op, parser.LabelDef) else "pure" in op.annotations no_save_ip = False if isinstance(op, parser.LabelDef) else "no_save_ip" in op.annotations return Properties( @@ -883,8 +876,7 @@ def compute_properties(op: parser.CodeDef) -> Properties: stores_sp=variable_used(op, "SYNC_SP"), uses_co_consts=variable_used(op, "FRAME_CO_CONSTS"), uses_co_names=variable_used(op, "FRAME_CO_NAMES"), - uses_locals=(variable_used(op, "GETLOCAL") or variable_used(op, "SETLOCAL")) - and not has_free, + uses_locals=variable_used(op, "GETLOCAL") and not has_free, uses_opcode=variable_used(op, "opcode"), has_free=has_free, pure=pure, From 078ab828b9a21ed0e683941bd141d5017b5f62d9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 4 Feb 2025 16:16:41 +0200 Subject: [PATCH 064/311] Use roles :data: and :const: for referencing module variables (GH-129507) --- Doc/howto/free-threading-python.rst | 2 +- Doc/library/datetime.rst | 6 +++--- Doc/library/decimal.rst | 2 +- Doc/library/filecmp.rst | 2 +- Doc/library/importlib.metadata.rst | 2 +- Doc/library/importlib.rst | 20 +++++++++---------- Doc/library/logging.rst | 6 +++--- Doc/library/plistlib.rst | 2 +- Doc/library/select.rst | 2 +- Doc/library/site.rst | 2 +- Doc/library/ssl.rst | 6 +++--- Doc/whatsnew/2.3.rst | 2 +- Doc/whatsnew/2.7.rst | 2 +- Doc/whatsnew/3.12.rst | 4 ++-- Doc/whatsnew/3.13.rst | 4 ++-- Doc/whatsnew/3.4.rst | 2 +- Misc/NEWS.d/3.11.0a4.rst | 4 ++-- Misc/NEWS.d/3.14.0a1.rst | 6 +++--- ...-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Doc/howto/free-threading-python.rst b/Doc/howto/free-threading-python.rst index b21e3287ecaa3f..cd920553a3a461 100644 --- a/Doc/howto/free-threading-python.rst +++ b/Doc/howto/free-threading-python.rst @@ -43,7 +43,7 @@ Identifying free-threaded Python ================================ To check if the current interpreter supports free-threading, :option:`python -VV <-V>` -and :attr:`sys.version` contain "experimental free-threading build". +and :data:`sys.version` contain "experimental free-threading build". The new :func:`sys._is_gil_enabled` function can be used to check whether the GIL is actually disabled in the running process. diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 2f81080d525f86..1af7d6be750102 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -93,7 +93,7 @@ The :mod:`!datetime` module exports the following constants: The largest year number allowed in a :class:`date` or :class:`.datetime` object. :const:`MAXYEAR` is 9999. -.. attribute:: UTC +.. data:: UTC Alias for the UTC time zone singleton :attr:`datetime.timezone.utc`. @@ -970,7 +970,7 @@ Other constructors, all class methods: .. deprecated:: 3.12 - Use :meth:`datetime.now` with :attr:`UTC` instead. + Use :meth:`datetime.now` with :const:`UTC` instead. .. classmethod:: datetime.fromtimestamp(timestamp, tz=None) @@ -1042,7 +1042,7 @@ Other constructors, all class methods: .. deprecated:: 3.12 - Use :meth:`datetime.fromtimestamp` with :attr:`UTC` instead. + Use :meth:`datetime.fromtimestamp` with :const:`UTC` instead. .. classmethod:: datetime.fromordinal(ordinal) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 185eaf3f721c72..9318af60b60f95 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -2262,7 +2262,7 @@ value for :attr:`~Context.prec` as well [#]_:: Decimal('904625697166532776746648320380374280103671755200316906558262375061821325312') -For inexact results, :attr:`MAX_PREC` is far too large on 64-bit platforms and +For inexact results, :const:`MAX_PREC` is far too large on 64-bit platforms and the available memory will be insufficient:: >>> Decimal(1) / 3 diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst index 282d0e0d8db5cf..abd1b8c826d170 100644 --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -189,7 +189,7 @@ The :class:`dircmp` class are the same type as *self*, if *self* is a subclass of :class:`dircmp`. -.. attribute:: DEFAULT_IGNORES +.. data:: DEFAULT_IGNORES .. versionadded:: 3.4 diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index d80255f5313061..45bf19ec29286d 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -375,7 +375,7 @@ Mapping import to distribution packages .. function:: packages_distributions() Return a mapping from the top level module and import package - names found via :attr:`sys.meta_path` to the names of the distribution + names found via :data:`sys.meta_path` to the names of the distribution packages (if any) that provide the corresponding files. To allow for namespace packages (which may have members provided by diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index b935fc0e42a4bd..245807e55ec936 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -746,7 +746,7 @@ ABC hierarchy:: suitable for reading (same as :attr:`pathlib.Path.open`). When opening as text, accepts encoding parameters such as those - accepted by :attr:`io.TextIOWrapper`. + accepted by :class:`io.TextIOWrapper`. .. method:: read_bytes() @@ -794,14 +794,14 @@ ABC hierarchy:: This module contains the various objects that help :keyword:`import` find and load modules. -.. attribute:: SOURCE_SUFFIXES +.. data:: SOURCE_SUFFIXES A list of strings representing the recognized file suffixes for source modules. .. versionadded:: 3.3 -.. attribute:: DEBUG_BYTECODE_SUFFIXES +.. data:: DEBUG_BYTECODE_SUFFIXES A list of strings representing the file suffixes for non-optimized bytecode modules. @@ -809,9 +809,9 @@ find and load modules. .. versionadded:: 3.3 .. deprecated:: 3.5 - Use :attr:`BYTECODE_SUFFIXES` instead. + Use :const:`BYTECODE_SUFFIXES` instead. -.. attribute:: OPTIMIZED_BYTECODE_SUFFIXES +.. data:: OPTIMIZED_BYTECODE_SUFFIXES A list of strings representing the file suffixes for optimized bytecode modules. @@ -819,9 +819,9 @@ find and load modules. .. versionadded:: 3.3 .. deprecated:: 3.5 - Use :attr:`BYTECODE_SUFFIXES` instead. + Use :const:`BYTECODE_SUFFIXES` instead. -.. attribute:: BYTECODE_SUFFIXES +.. data:: BYTECODE_SUFFIXES A list of strings representing the recognized file suffixes for bytecode modules (including the leading dot). @@ -831,7 +831,7 @@ find and load modules. .. versionchanged:: 3.5 The value is no longer dependent on ``__debug__``. -.. attribute:: EXTENSION_SUFFIXES +.. data:: EXTENSION_SUFFIXES A list of strings representing the recognized file suffixes for extension modules. @@ -1109,7 +1109,7 @@ find and load modules. .. method:: is_package(fullname) Returns ``True`` if the file path points to a package's ``__init__`` - module based on :attr:`EXTENSION_SUFFIXES`. + module based on :const:`EXTENSION_SUFFIXES`. .. method:: get_code(fullname) @@ -1294,7 +1294,7 @@ find and load modules. This module contains the various objects that help in the construction of an :term:`importer`. -.. attribute:: MAGIC_NUMBER +.. data:: MAGIC_NUMBER The bytes which represent the bytecode version number. If you need help with loading/writing bytecode then consider :class:`importlib.abc.SourceLoader`. diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 235bcc281ac8f8..19ae024f9eeffa 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -342,7 +342,7 @@ in a module, ``__name__`` is the module's name in the Python package namespace. If no handler is attached to this logger (or any of its ancestors, taking into account the relevant :attr:`Logger.propagate` attributes), - the message will be sent to the handler set on :attr:`lastResort`. + the message will be sent to the handler set on :data:`lastResort`. .. versionchanged:: 3.2 The *stack_info* parameter was added. @@ -1495,7 +1495,7 @@ functions. Module-Level Attributes ----------------------- -.. attribute:: lastResort +.. data:: lastResort A "handler of last resort" is available through this attribute. This is a :class:`StreamHandler` writing to ``sys.stderr`` with a level of @@ -1507,7 +1507,7 @@ Module-Level Attributes .. versionadded:: 3.2 -.. attribute:: raiseExceptions +.. data:: raiseExceptions Used to see if exceptions during handling should be propagated. diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 2906ebe7822f52..075b974501e3da 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -71,7 +71,7 @@ This module defines the following functions: When *aware_datetime* is true, fields with type ``datetime.datetime`` will be created as :ref:`aware object `, with - :attr:`!tzinfo` as :attr:`datetime.UTC`. + :attr:`!tzinfo` as :const:`datetime.UTC`. XML data for the :data:`FMT_XML` format is parsed using the Expat parser from :mod:`xml.parsers.expat` -- see its documentation for possible diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 457970aed2dc73..d2094283d54736 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -165,7 +165,7 @@ The module defines the following: :exc:`InterruptedError`. -.. attribute:: PIPE_BUF +.. data:: PIPE_BUF The minimum number of bytes which can be written without blocking to a pipe when the pipe has been reported as ready for writing by :func:`~select.select`, diff --git a/Doc/library/site.rst b/Doc/library/site.rst index 5f2a0f610e1aa5..e98dd83b60eb60 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -35,7 +35,7 @@ are skipped. For the tail part, it uses the empty string and then :file:`lib/site-packages` (on Windows) or :file:`lib/python{X.Y[t]}/site-packages` (on Unix and macOS). (The optional suffix "t" indicates the :term:`free threading` build, and is -appended if ``"t"`` is present in the :attr:`sys.abiflags` constant.) +appended if ``"t"`` is present in the :data:`sys.abiflags` constant.) For each of the distinct head-tail combinations, it sees if it refers to an existing directory, and if so, adds it to ``sys.path`` and also inspects the newly diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 37ea32dc8a56e5..c0dcecf737ef76 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1937,8 +1937,8 @@ to speed up repeated connections from the same clients. A :class:`TLSVersion` enum member representing the highest supported TLS version. The value defaults to :attr:`TLSVersion.MAXIMUM_SUPPORTED`. - The attribute is read-only for protocols other than :attr:`PROTOCOL_TLS`, - :attr:`PROTOCOL_TLS_CLIENT`, and :attr:`PROTOCOL_TLS_SERVER`. + The attribute is read-only for protocols other than :const:`PROTOCOL_TLS`, + :const:`PROTOCOL_TLS_CLIENT`, and :const:`PROTOCOL_TLS_SERVER`. The attributes :attr:`~SSLContext.maximum_version`, :attr:`~SSLContext.minimum_version` and @@ -1961,7 +1961,7 @@ to speed up repeated connections from the same clients. .. attribute:: SSLContext.num_tickets Control the number of TLS 1.3 session tickets of a - :attr:`PROTOCOL_TLS_SERVER` context. The setting has no impact on TLS + :const:`PROTOCOL_TLS_SERVER` context. The setting has no impact on TLS 1.0 to 1.2 connections. .. versionadded:: 3.8 diff --git a/Doc/whatsnew/2.3.rst b/Doc/whatsnew/2.3.rst index ac463f82cfb8ca..b7e4e73f4ce4aa 100644 --- a/Doc/whatsnew/2.3.rst +++ b/Doc/whatsnew/2.3.rst @@ -353,7 +353,7 @@ convert them to Unicode using the ``mbcs`` encoding. Other systems also allow Unicode strings as file names but convert them to byte strings before passing them to the system, which can cause a :exc:`UnicodeError` to be raised. Applications can test whether arbitrary Unicode strings are -supported as file names by checking :attr:`os.path.supports_unicode_filenames`, +supported as file names by checking :const:`os.path.supports_unicode_filenames`, a Boolean value. Under MacOS, :func:`os.listdir` may now return Unicode filenames. diff --git a/Doc/whatsnew/2.7.rst b/Doc/whatsnew/2.7.rst index 0e4dee0bd24fb2..caed3192be871d 100644 --- a/Doc/whatsnew/2.7.rst +++ b/Doc/whatsnew/2.7.rst @@ -1602,7 +1602,7 @@ changes, or look through the Subversion logs for all the details. identifier instead of the previous default value of ``'python'``. (Changed by Sean Reifschneider; :issue:`8451`.) -* The :attr:`sys.version_info` value is now a named tuple, with attributes +* The :data:`sys.version_info` value is now a named tuple, with attributes named :attr:`!major`, :attr:`!minor`, :attr:`!micro`, :attr:`!releaselevel`, and :attr:`!serial`. (Contributed by Ross Light; :issue:`4285`.) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 4fffc78a237791..994ccc708e26f2 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1467,8 +1467,8 @@ imp ``imp.NullImporter`` Insert ``None`` into ``sys.path_importer_cache`` ``imp.cache_from_source()`` :func:`importlib.util.cache_from_source` ``imp.find_module()`` :func:`importlib.util.find_spec` - ``imp.get_magic()`` :attr:`importlib.util.MAGIC_NUMBER` - ``imp.get_suffixes()`` :attr:`importlib.machinery.SOURCE_SUFFIXES`, :attr:`importlib.machinery.EXTENSION_SUFFIXES`, and :attr:`importlib.machinery.BYTECODE_SUFFIXES` + ``imp.get_magic()`` :const:`importlib.util.MAGIC_NUMBER` + ``imp.get_suffixes()`` :const:`importlib.machinery.SOURCE_SUFFIXES`, :const:`importlib.machinery.EXTENSION_SUFFIXES`, and :const:`importlib.machinery.BYTECODE_SUFFIXES` ``imp.get_tag()`` :attr:`sys.implementation.cache_tag ` ``imp.load_module()`` :func:`importlib.import_module` ``imp.new_module(name)`` ``types.ModuleType(name)`` diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index f2a8c711f4df88..2090759d3c4fde 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -325,7 +325,7 @@ enabled at runtime using the environment variable :envvar:`PYTHON_GIL` or the command-line option :option:`-X gil=1`. To check if the current interpreter supports free-threading, :option:`python -VV <-V>` -and :attr:`sys.version` contain "experimental free-threading build". +and :data:`sys.version` contain "experimental free-threading build". The new :func:`!sys._is_gil_enabled` function can be used to check whether the GIL is actually disabled in the running process. @@ -1062,7 +1062,7 @@ os which makes the newly spawned process use the current process environment. (Contributed by Jakub Kulik in :gh:`113119`.) -* :func:`~os.posix_spawn` can now use the :attr:`~os.POSIX_SPAWN_CLOSEFROM` +* :func:`~os.posix_spawn` can now use the :const:`~os.POSIX_SPAWN_CLOSEFROM` attribute in the *file_actions* parameter on platforms that support :c:func:`!posix_spawn_file_actions_addclosefrom_np`. (Contributed by Jakub Kulik in :gh:`113117`.) diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index 71b186aeed7359..e4f602a17ee968 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -994,7 +994,7 @@ The :func:`~importlib.reload` function has been moved from :mod:`!imp` to :mod:`importlib` as part of the :mod:`!imp` module deprecation. (Contributed by Berker Peksag in :issue:`18193`.) -:mod:`importlib.util` now has a :data:`~importlib.util.MAGIC_NUMBER` attribute +:mod:`importlib.util` now has a :const:`~importlib.util.MAGIC_NUMBER` attribute providing access to the bytecode version number. This replaces the :func:`!get_magic` function in the deprecated :mod:`!imp` module. (Contributed by Brett Cannon in :issue:`18192`.) diff --git a/Misc/NEWS.d/3.11.0a4.rst b/Misc/NEWS.d/3.11.0a4.rst index 64e2f39ad9db18..a2d36202045887 100644 --- a/Misc/NEWS.d/3.11.0a4.rst +++ b/Misc/NEWS.d/3.11.0a4.rst @@ -775,8 +775,8 @@ Ensure that :func:`math.expm1` does not raise on underflow. .. nonce: s9PuyF .. section: Library -Adding :attr:`F_DUP2FD` and :attr:`F_DUP2FD_CLOEXEC` constants from FreeBSD -into the fcntl module. +Adding :const:`!F_DUP2FD` and :const:`!F_DUP2FD_CLOEXEC` constants from FreeBSD +into the :mod:`fcntl` module. .. diff --git a/Misc/NEWS.d/3.14.0a1.rst b/Misc/NEWS.d/3.14.0a1.rst index dfc33812611185..efeba0ac1ffe2f 100644 --- a/Misc/NEWS.d/3.14.0a1.rst +++ b/Misc/NEWS.d/3.14.0a1.rst @@ -798,7 +798,7 @@ when used with ``happy_eyeballs_delay`` Fixed :exc:`AssertionError` when using :func:`!asyncio.staggered.staggered_race` with -:attr:`asyncio.eager_task_factory`. +:data:`asyncio.eager_task_factory`. .. @@ -1755,8 +1755,8 @@ Adjust ``cmath.tanh(nanj)`` and ``cmath.tanh(infj)`` for recent C standards. Remove internal frames from tracebacks shown in :class:`code.InteractiveInterpreter` with non-default :func:`sys.excepthook`. Save correct tracebacks in -:attr:`sys.last_traceback` and update ``__traceback__`` attribute of -:attr:`sys.last_value` and :attr:`sys.last_exc`. +:data:`sys.last_traceback` and update ``__traceback__`` attribute of +:data:`sys.last_value` and :data:`sys.last_exc`. .. diff --git a/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst b/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst index 9a6fce8647cc6b..8edd8dfb604878 100644 --- a/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst +++ b/Misc/NEWS.d/next/Library/2024-12-16-22-20-38.gh-issue-121604.m3Xn4G.rst @@ -1 +1 @@ -Add missing Deprecation warnings for :attr:`importlib.machinery.DEBUG_BYTECODE_SUFFIXES`, :attr:`importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES`, :class:`importlib.machinery.WindowsRegistryFinder`, :class:`importlib.abc.ResourceLoader`, :meth:`importlib.abc.SourceLoader.path_mtime`. +Add missing Deprecation warnings for :const:`importlib.machinery.DEBUG_BYTECODE_SUFFIXES`, :const:`importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES`, :class:`importlib.machinery.WindowsRegistryFinder`, :class:`importlib.abc.ResourceLoader`, :meth:`importlib.abc.SourceLoader.path_mtime`. From 8b5c8508c7f5d98b09792e159ef2396c73da68cd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 4 Feb 2025 16:24:22 +0200 Subject: [PATCH 065/311] gh-129350: Make tests for glob with trailing slash more strict (GH-129376) Test that the trailing pathname separator is preserved. Multiple trailing pathname separators are only preserved if the pattern does not contain metacharacters, otherwise only one trailing pathname separator is preserved. This is rather an implementation detail. --- Lib/test/test_glob.py | 65 ++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 1a836e34e8712f..a45b30599d5309 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -171,37 +171,45 @@ def test_glob_directory_names(self): self.norm('aab', 'F')]) def test_glob_directory_with_trailing_slash(self): - # Patterns ending with a slash shouldn't match non-dirs - res = glob.glob(self.norm('Z*Z') + os.sep) - self.assertEqual(res, []) - res = glob.glob(self.norm('ZZZ') + os.sep) - self.assertEqual(res, []) - # When there is a wildcard pattern which ends with os.sep, glob() - # doesn't blow up. - res = glob.glob(self.norm('aa*') + os.sep) - self.assertEqual(len(res), 2) - # either of these results is reasonable - self.assertIn(set(res), [ - {self.norm('aaa'), self.norm('aab')}, - {self.norm('aaa') + os.sep, self.norm('aab') + os.sep}, - ]) + seps = (os.sep, os.altsep) if os.altsep else (os.sep,) + for sep in seps: + # Patterns ending with a slash shouldn't match non-dirs + self.assertEqual(glob.glob(self.norm('Z*Z') + sep), []) + self.assertEqual(glob.glob(self.norm('ZZZ') + sep), []) + self.assertEqual(glob.glob(self.norm('aaa') + sep), + [self.norm('aaa') + sep]) + # Preserving the redundant separators is an implementation detail. + self.assertEqual(glob.glob(self.norm('aaa') + sep*2), + [self.norm('aaa') + sep*2]) + # When there is a wildcard pattern which ends with a pathname + # separator, glob() doesn't blow. + # The result should end with the pathname separator. + # Normalizing the trailing separator is an implementation detail. + eq = self.assertSequencesEqual_noorder + eq(glob.glob(self.norm('aa*') + sep), + [self.norm('aaa') + os.sep, self.norm('aab') + os.sep]) + # Stripping the redundant separators is an implementation detail. + eq(glob.glob(self.norm('aa*') + sep*2), + [self.norm('aaa') + os.sep, self.norm('aab') + os.sep]) def test_glob_bytes_directory_with_trailing_slash(self): # Same as test_glob_directory_with_trailing_slash, but with a # bytes argument. - res = glob.glob(os.fsencode(self.norm('Z*Z') + os.sep)) - self.assertEqual(res, []) - res = glob.glob(os.fsencode(self.norm('ZZZ') + os.sep)) - self.assertEqual(res, []) - res = glob.glob(os.fsencode(self.norm('aa*') + os.sep)) - self.assertEqual(len(res), 2) - # either of these results is reasonable - self.assertIn(set(res), [ - {os.fsencode(self.norm('aaa')), - os.fsencode(self.norm('aab'))}, - {os.fsencode(self.norm('aaa') + os.sep), - os.fsencode(self.norm('aab') + os.sep)}, - ]) + seps = (os.sep, os.altsep) if os.altsep else (os.sep,) + for sep in seps: + self.assertEqual(glob.glob(os.fsencode(self.norm('Z*Z') + sep)), []) + self.assertEqual(glob.glob(os.fsencode(self.norm('ZZZ') + sep)), []) + self.assertEqual(glob.glob(os.fsencode(self.norm('aaa') + sep)), + [os.fsencode(self.norm('aaa') + sep)]) + self.assertEqual(glob.glob(os.fsencode(self.norm('aaa') + sep*2)), + [os.fsencode(self.norm('aaa') + sep*2)]) + eq = self.assertSequencesEqual_noorder + eq(glob.glob(os.fsencode(self.norm('aa*') + sep)), + [os.fsencode(self.norm('aaa') + os.sep), + os.fsencode(self.norm('aab') + os.sep)]) + eq(glob.glob(os.fsencode(self.norm('aa*') + sep*2)), + [os.fsencode(self.norm('aaa') + os.sep), + os.fsencode(self.norm('aab') + os.sep)]) @skip_unless_symlink def test_glob_symlinks(self): @@ -209,8 +217,7 @@ def test_glob_symlinks(self): eq(self.glob('sym3'), [self.norm('sym3')]) eq(self.glob('sym3', '*'), [self.norm('sym3', 'EF'), self.norm('sym3', 'efg')]) - self.assertIn(self.glob('sym3' + os.sep), - [[self.norm('sym3')], [self.norm('sym3') + os.sep]]) + eq(self.glob('sym3' + os.sep), [self.norm('sym3') + os.sep]) eq(self.glob('*', '*F'), [self.norm('aaa', 'zzzF'), self.norm('aab', 'F'), self.norm('sym3', 'EF')]) From 979d76620990e6f8d68fa63e0ae0db1ec5b4d14c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 4 Feb 2025 16:25:49 +0200 Subject: [PATCH 066/311] gh-128772: Fix pydoc for methods with __module__ is None (GH-129177) --- Lib/pydoc.py | 2 +- Lib/test/test_pydoc/module_none.py | 8 ++++++++ Lib/test/test_pydoc/test_pydoc.py | 5 +++++ .../2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_pydoc/module_none.py create mode 100644 Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 922946e5fa7ddb..1839b88fec28b1 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -245,7 +245,7 @@ def parentname(object, modname): if necessary) or module.""" if '.' in object.__qualname__: name = object.__qualname__.rpartition('.')[0] - if object.__module__ != modname: + if object.__module__ != modname and object.__module__ is not None: return object.__module__ + '.' + name else: return name diff --git a/Lib/test/test_pydoc/module_none.py b/Lib/test/test_pydoc/module_none.py new file mode 100644 index 00000000000000..ebb50fc86e2cf7 --- /dev/null +++ b/Lib/test/test_pydoc/module_none.py @@ -0,0 +1,8 @@ +def func(): + pass +func.__module__ = None + +class A: + def method(self): + pass + method.__module__ = None diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index b02ba3aafd4d20..0abd36c5e076e2 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1903,6 +1903,11 @@ def a_fn_with_https_link(): html ) + def test_module_none(self): + # Issue #128772 + from test.test_pydoc import module_none + pydoc.render_doc(module_none) + class PydocFodderTest(unittest.TestCase): def tearDown(self): diff --git a/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst b/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst new file mode 100644 index 00000000000000..53d6b3ccaffda8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-22-13-29-06.gh-issue-128772.6YrxYM.rst @@ -0,0 +1,2 @@ +Fix :mod:`pydoc` for methods with the ``__module__`` attribute equal to +``None``. From 14489c1bb44dc2f4179278463fedd9a940b63f41 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 4 Feb 2025 16:21:10 +0100 Subject: [PATCH 067/311] gh-129354: Use PyErr_FormatUnraisable() function (#129656) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). --- Modules/_io/fileio.c | 6 ++++-- Modules/_io/iobase.c | 3 ++- Modules/socketmodule.c | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index cf0f1d671b507a..f27f2ed4843271 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -105,8 +105,10 @@ fileio_dealloc_warn(PyObject *op, PyObject *source) PyObject *exc = PyErr_GetRaisedException(); if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) { /* Spurious errors can appear at shutdown */ - if (PyErr_ExceptionMatches(PyExc_Warning)) - PyErr_WriteUnraisable((PyObject *) self); + if (PyErr_ExceptionMatches(PyExc_Warning)) { + PyErr_FormatUnraisable("Exception ignored " + "while finalizing file %R", self); + } } PyErr_SetRaisedException(exc); } diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 419e5516b5c11e..f87043df126895 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -314,7 +314,8 @@ iobase_finalize(PyObject *self) PyErr_Clear(); res = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(close)); if (res == NULL) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored " + "while finalizing file %R", self); } else { Py_DECREF(res); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index b178eb42ac8e6a..4b6d2dd1c5fc7b 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5359,7 +5359,8 @@ sock_finalize(PyObject *self) if (PyErr_ResourceWarning((PyObject *)s, 1, "unclosed %R", s)) { /* Spurious errors can appear at shutdown */ if (PyErr_ExceptionMatches(PyExc_Warning)) { - PyErr_WriteUnraisable((PyObject *)s); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing socket %R", s); } } From f61afca262d3a0aa6a8a501db0b1936c60858e35 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 4 Feb 2025 17:22:05 +0200 Subject: [PATCH 068/311] gh-129646: Update the locale alias mapping (#129647) * gh-129646: Update the locale alias mapping * Add a NEWS file. --- Lib/locale.py | 52 +++++++++++++++---- ...-02-04-15-16-33.gh-issue-129646.sapk1F.rst | 2 + 2 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-04-15-16-33.gh-issue-129646.sapk1F.rst diff --git a/Lib/locale.py b/Lib/locale.py index d8c09f1123d318..213d5e93418cfb 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -860,6 +860,24 @@ def getpreferredencoding(do_setlocale=True): # updated 'ca_es@valencia' -> 'ca_ES.ISO8859-15@valencia' to 'ca_ES.UTF-8@valencia' # updated 'kk_kz' -> 'kk_KZ.RK1048' to 'kk_KZ.ptcp154' # updated 'russian' -> 'ru_RU.ISO8859-5' to 'ru_RU.KOI8-R' +# +# SS 2025-02-04: +# Updated alias mapping with glibc 2.41 supported locales and the latest +# X lib alias mapping. +# +# These are the differences compared to the old mapping (Python 3.13.1 +# and older): +# +# updated 'c.utf8' -> 'C.UTF-8' to 'en_US.UTF-8' +# updated 'de_it' -> 'de_IT.ISO8859-1' to 'de_IT.UTF-8' +# removed 'de_li.utf8' +# updated 'en_il' -> 'en_IL.UTF-8' to 'en_IL.ISO8859-1' +# removed 'english.iso88591' +# updated 'es_cu' -> 'es_CU.UTF-8' to 'es_CU.ISO8859-1' +# updated 'russian' -> 'ru_RU.KOI8-R' to 'ru_RU.ISO8859-5' +# updated 'sr@latn' -> 'sr_CS.UTF-8@latin' to 'sr_RS.UTF-8@latin' +# removed 'univ' +# removed 'universal' locale_alias = { 'a3': 'az_AZ.KOI8-C', @@ -939,7 +957,7 @@ def getpreferredencoding(do_setlocale=True): 'c.ascii': 'C', 'c.en': 'C', 'c.iso88591': 'en_US.ISO8859-1', - 'c.utf8': 'C.UTF-8', + 'c.utf8': 'en_US.UTF-8', 'c_c': 'C', 'c_c.c': 'C', 'ca': 'ca_ES.ISO8859-1', @@ -956,6 +974,7 @@ def getpreferredencoding(do_setlocale=True): 'chr_us': 'chr_US.UTF-8', 'ckb_iq': 'ckb_IQ.UTF-8', 'cmn_tw': 'cmn_TW.UTF-8', + 'crh_ru': 'crh_RU.UTF-8', 'crh_ua': 'crh_UA.UTF-8', 'croatian': 'hr_HR.ISO8859-2', 'cs': 'cs_CZ.ISO8859-2', @@ -977,11 +996,12 @@ def getpreferredencoding(do_setlocale=True): 'de_be': 'de_BE.ISO8859-1', 'de_ch': 'de_CH.ISO8859-1', 'de_de': 'de_DE.ISO8859-1', - 'de_it': 'de_IT.ISO8859-1', - 'de_li.utf8': 'de_LI.UTF-8', + 'de_it': 'de_IT.UTF-8', + 'de_li': 'de_LI.ISO8859-1', 'de_lu': 'de_LU.ISO8859-1', 'deutsch': 'de_DE.ISO8859-1', 'doi_in': 'doi_IN.UTF-8', + 'dsb_de': 'dsb_DE.UTF-8', 'dutch': 'nl_NL.ISO8859-1', 'dutch.iso88591': 'nl_BE.ISO8859-1', 'dv_mv': 'dv_MV.UTF-8', @@ -1004,7 +1024,7 @@ def getpreferredencoding(do_setlocale=True): 'en_gb': 'en_GB.ISO8859-1', 'en_hk': 'en_HK.ISO8859-1', 'en_ie': 'en_IE.ISO8859-1', - 'en_il': 'en_IL.UTF-8', + 'en_il': 'en_IL.ISO8859-1', 'en_in': 'en_IN.ISO8859-1', 'en_ng': 'en_NG.UTF-8', 'en_nz': 'en_NZ.ISO8859-1', @@ -1020,7 +1040,6 @@ def getpreferredencoding(do_setlocale=True): 'en_zw.utf8': 'en_ZS.UTF-8', 'eng_gb': 'en_GB.ISO8859-1', 'english': 'en_EN.ISO8859-1', - 'english.iso88591': 'en_US.ISO8859-1', 'english_uk': 'en_GB.ISO8859-1', 'english_united-states': 'en_US.ISO8859-1', 'english_united-states.437': 'C', @@ -1036,7 +1055,7 @@ def getpreferredencoding(do_setlocale=True): 'es_cl': 'es_CL.ISO8859-1', 'es_co': 'es_CO.ISO8859-1', 'es_cr': 'es_CR.ISO8859-1', - 'es_cu': 'es_CU.UTF-8', + 'es_cu': 'es_CU.ISO8859-1', 'es_do': 'es_DO.ISO8859-1', 'es_ec': 'es_EC.ISO8859-1', 'es_es': 'es_ES.ISO8859-1', @@ -1086,6 +1105,7 @@ def getpreferredencoding(do_setlocale=True): 'ga_ie': 'ga_IE.ISO8859-1', 'galego': 'gl_ES.ISO8859-1', 'galician': 'gl_ES.ISO8859-1', + 'gbm_in': 'gbm_IN.UTF-8', 'gd': 'gd_GB.ISO8859-1', 'gd_gb': 'gd_GB.ISO8859-1', 'ger_de': 'de_DE.ISO8859-1', @@ -1126,6 +1146,7 @@ def getpreferredencoding(do_setlocale=True): 'icelandic': 'is_IS.ISO8859-1', 'id': 'id_ID.ISO8859-1', 'id_id': 'id_ID.ISO8859-1', + 'ie': 'ie.UTF-8', 'ig_ng': 'ig_NG.UTF-8', 'ik_ca': 'ik_CA.UTF-8', 'in': 'id_ID.ISO8859-1', @@ -1180,6 +1201,7 @@ def getpreferredencoding(do_setlocale=True): 'ks_in': 'ks_IN.UTF-8', 'ks_in@devanagari.utf8': 'ks_IN.UTF-8@devanagari', 'ku_tr': 'ku_TR.ISO8859-9', + 'kv_ru': 'kv_RU.UTF-8', 'kw': 'kw_GB.ISO8859-1', 'kw_gb': 'kw_GB.ISO8859-1', 'ky': 'ky_KG.UTF-8', @@ -1198,6 +1220,7 @@ def getpreferredencoding(do_setlocale=True): 'lo_la.mulelao1': 'lo_LA.MULELAO-1', 'lt': 'lt_LT.ISO8859-13', 'lt_lt': 'lt_LT.ISO8859-13', + 'ltg_lv.utf8': 'ltg_LV.UTF-8', 'lv': 'lv_LV.ISO8859-13', 'lv_lv': 'lv_LV.ISO8859-13', 'lzh_tw': 'lzh_TW.UTF-8', @@ -1205,6 +1228,7 @@ def getpreferredencoding(do_setlocale=True): 'mai': 'mai_IN.UTF-8', 'mai_in': 'mai_IN.UTF-8', 'mai_np': 'mai_NP.UTF-8', + 'mdf_ru': 'mdf_RU.UTF-8', 'mfe_mu': 'mfe_MU.UTF-8', 'mg_mg': 'mg_MG.ISO8859-15', 'mhr_ru': 'mhr_RU.UTF-8', @@ -1218,6 +1242,7 @@ def getpreferredencoding(do_setlocale=True): 'ml_in': 'ml_IN.UTF-8', 'mn_mn': 'mn_MN.UTF-8', 'mni_in': 'mni_IN.UTF-8', + 'mnw_mm': 'mnw_MM.UTF-8', 'mr': 'mr_IN.UTF-8', 'mr_in': 'mr_IN.UTF-8', 'ms': 'ms_MY.ISO8859-1', @@ -1286,6 +1311,7 @@ def getpreferredencoding(do_setlocale=True): 'pt_pt': 'pt_PT.ISO8859-1', 'quz_pe': 'quz_PE.UTF-8', 'raj_in': 'raj_IN.UTF-8', + 'rif_ma': 'rif_MA.UTF-8', 'ro': 'ro_RO.ISO8859-2', 'ro_ro': 'ro_RO.ISO8859-2', 'romanian': 'ro_RO.ISO8859-2', @@ -1293,12 +1319,14 @@ def getpreferredencoding(do_setlocale=True): 'ru_ru': 'ru_RU.UTF-8', 'ru_ua': 'ru_UA.KOI8-U', 'rumanian': 'ro_RO.ISO8859-2', - 'russian': 'ru_RU.KOI8-R', + 'russian': 'ru_RU.ISO8859-5', 'rw': 'rw_RW.ISO8859-1', 'rw_rw': 'rw_RW.ISO8859-1', 'sa_in': 'sa_IN.UTF-8', + 'sah_ru': 'sah_RU.UTF-8', 'sat_in': 'sat_IN.UTF-8', 'sc_it': 'sc_IT.UTF-8', + 'scn_it': 'scn_IT.UTF-8', 'sd': 'sd_IN.UTF-8', 'sd_in': 'sd_IN.UTF-8', 'sd_in@devanagari.utf8': 'sd_IN.UTF-8@devanagari', @@ -1340,7 +1368,7 @@ def getpreferredencoding(do_setlocale=True): 'sq_mk': 'sq_MK.UTF-8', 'sr': 'sr_RS.UTF-8', 'sr@cyrillic': 'sr_RS.UTF-8', - 'sr@latn': 'sr_CS.UTF-8@latin', + 'sr@latn': 'sr_RS.UTF-8@latin', 'sr_cs': 'sr_CS.UTF-8', 'sr_cs.iso88592@latn': 'sr_CS.ISO8859-2', 'sr_cs@latn': 'sr_CS.UTF-8@latin', @@ -1359,14 +1387,17 @@ def getpreferredencoding(do_setlocale=True): 'sr_yu@cyrillic': 'sr_RS.UTF-8', 'ss': 'ss_ZA.ISO8859-1', 'ss_za': 'ss_ZA.ISO8859-1', + 'ssy_er': 'ssy_ER.UTF-8', 'st': 'st_ZA.ISO8859-1', 'st_za': 'st_ZA.ISO8859-1', + 'su_id': 'su_ID.UTF-8', 'sv': 'sv_SE.ISO8859-1', 'sv_fi': 'sv_FI.ISO8859-1', 'sv_se': 'sv_SE.ISO8859-1', 'sw_ke': 'sw_KE.UTF-8', 'sw_tz': 'sw_TZ.UTF-8', 'swedish': 'sv_SE.ISO8859-1', + 'syr': 'syr.UTF-8', 'szl_pl': 'szl_PL.UTF-8', 'ta': 'ta_IN.TSCII-0', 'ta_in': 'ta_IN.TSCII-0', @@ -1393,6 +1424,7 @@ def getpreferredencoding(do_setlocale=True): 'tn': 'tn_ZA.ISO8859-15', 'tn_za': 'tn_ZA.ISO8859-15', 'to_to': 'to_TO.UTF-8', + 'tok': 'tok.UTF-8', 'tpi_pg': 'tpi_PG.UTF-8', 'tr': 'tr_TR.ISO8859-9', 'tr_cy': 'tr_CY.ISO8859-9', @@ -1407,8 +1439,7 @@ def getpreferredencoding(do_setlocale=True): 'ug_cn': 'ug_CN.UTF-8', 'uk': 'uk_UA.KOI8-U', 'uk_ua': 'uk_UA.KOI8-U', - 'univ': 'en_US.utf', - 'universal': 'en_US.utf', + 'univ.utf8': 'en_US.UTF-8', 'universal.utf8@ucs4': 'en_US.UTF-8', 'unm_us': 'unm_US.UTF-8', 'ur': 'ur_PK.CP1256', @@ -1437,6 +1468,7 @@ def getpreferredencoding(do_setlocale=True): 'yo_ng': 'yo_NG.UTF-8', 'yue_hk': 'yue_HK.UTF-8', 'yuw_pg': 'yuw_PG.UTF-8', + 'zgh_ma': 'zgh_MA.UTF-8', 'zh': 'zh_CN.eucCN', 'zh_cn': 'zh_CN.gb2312', 'zh_cn.big5': 'zh_TW.big5', diff --git a/Misc/NEWS.d/next/Library/2025-02-04-15-16-33.gh-issue-129646.sapk1F.rst b/Misc/NEWS.d/next/Library/2025-02-04-15-16-33.gh-issue-129646.sapk1F.rst new file mode 100644 index 00000000000000..742d1d60dfd1bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-04-15-16-33.gh-issue-129646.sapk1F.rst @@ -0,0 +1,2 @@ +Update the locale alias mapping in the :mod:`locale` module to match the +latest X Org locale alias mapping and support new locales in Glibc 2.41. From 285c1c4e9543299c8bf69ceb39a424782b8c632e Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 5 Feb 2025 04:33:07 +0900 Subject: [PATCH 069/311] gh-129660: Do not use test_embed in PGO profile builds (#129661) --- .../next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst diff --git a/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst new file mode 100644 index 00000000000000..945f91be63809a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-02-04-12-30-43.gh-issue-129660.SitXa7.rst @@ -0,0 +1,2 @@ +Drop ``test_embed`` from PGO training, whose contribution in recent +versions is considered to be ignorable. From e5f10a741408206e61cf793451cbd373bbe61594 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 4 Feb 2025 17:44:59 -0500 Subject: [PATCH 070/311] gh-127933: Add option to run regression tests in parallel (gh-128003) This adds a new command line argument, `--parallel-threads` to the regression test runner to allow it to run individual tests in multiple threads in parallel in order to find multithreading bugs. Some tests pass when run with `--parallel-threads`, but there's still more work before the entire suite passes. --- Doc/library/test.rst | 5 ++ Lib/test/libregrtest/cmdline.py | 5 ++ Lib/test/libregrtest/main.py | 3 + Lib/test/libregrtest/parallel_case.py | 79 +++++++++++++++++++ Lib/test/libregrtest/runtests.py | 3 + Lib/test/libregrtest/single.py | 30 ++++++- Lib/test/support/__init__.py | 17 +++- Lib/test/test_class.py | 2 + Lib/test/test_descr.py | 3 + Lib/test/test_operator.py | 1 + Lib/test/test_tokenize.py | 1 + ...-12-16-19-15-10.gh-issue-128003.GVBrfa.rst | 4 + 12 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 Lib/test/libregrtest/parallel_case.py create mode 100644 Misc/NEWS.d/next/Tests/2024-12-16-19-15-10.gh-issue-128003.GVBrfa.rst diff --git a/Doc/library/test.rst b/Doc/library/test.rst index b5b6e442e218fd..def22f8bb8ab2d 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -792,6 +792,11 @@ The :mod:`test.support` module defines the following functions: Decorator for invoking :func:`check_impl_detail` on *guards*. If that returns ``False``, then uses *msg* as the reason for skipping the test. +.. decorator:: thread_unsafe(reason=None) + + Decorator for marking tests as thread-unsafe. This test always runs in one + thread even when invoked with ``--parallel-threads``. + .. decorator:: no_tracing diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index bf9a71efbdbff9..1f3b2381c71d45 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -160,6 +160,7 @@ def __init__(self, **kwargs) -> None: self.print_slow = False self.random_seed = None self.use_mp = None + self.parallel_threads = None self.forever = False self.header = False self.failfast = False @@ -316,6 +317,10 @@ def _create_parser(): 'a single process, ignore -jN option, ' 'and failed tests are also rerun sequentially ' 'in the same process') + group.add_argument('--parallel-threads', metavar='PARALLEL_THREADS', + type=int, + help='run copies of each test in PARALLEL_THREADS at ' + 'once') group.add_argument('-T', '--coverage', action='store_true', dest='trace', help='turn on code coverage tracing using the trace ' diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index dcbcc6790c68d8..de377f185f7ed9 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -142,6 +142,8 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): else: self.random_seed = ns.random_seed + self.parallel_threads = ns.parallel_threads + # tests self.first_runtests: RunTests | None = None @@ -506,6 +508,7 @@ def create_run_tests(self, tests: TestTuple) -> RunTests: python_cmd=self.python_cmd, randomize=self.randomize, random_seed=self.random_seed, + parallel_threads=self.parallel_threads, ) def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: diff --git a/Lib/test/libregrtest/parallel_case.py b/Lib/test/libregrtest/parallel_case.py new file mode 100644 index 00000000000000..09d9d2831e86b8 --- /dev/null +++ b/Lib/test/libregrtest/parallel_case.py @@ -0,0 +1,79 @@ +"""Run a test case multiple times in parallel threads.""" + +import copy +import functools +import threading +import unittest + +from unittest import TestCase + + +class ParallelTestCase(TestCase): + def __init__(self, test_case: TestCase, num_threads: int): + self.test_case = test_case + self.num_threads = num_threads + self._testMethodName = test_case._testMethodName + self._testMethodDoc = test_case._testMethodDoc + + def __str__(self): + return f"{str(self.test_case)} [threads={self.num_threads}]" + + def run_worker(self, test_case: TestCase, result: unittest.TestResult, + barrier: threading.Barrier): + barrier.wait() + test_case.run(result) + + def run(self, result=None): + if result is None: + result = test_case.defaultTestResult() + startTestRun = getattr(result, 'startTestRun', None) + stopTestRun = getattr(result, 'stopTestRun', None) + if startTestRun is not None: + startTestRun() + else: + stopTestRun = None + + # Called at the beginning of each test. See TestCase.run. + result.startTest(self) + + cases = [copy.copy(self.test_case) for _ in range(self.num_threads)] + results = [unittest.TestResult() for _ in range(self.num_threads)] + + barrier = threading.Barrier(self.num_threads) + threads = [] + for i, (case, r) in enumerate(zip(cases, results)): + thread = threading.Thread(target=self.run_worker, + args=(case, r, barrier), + name=f"{str(self.test_case)}-{i}", + daemon=True) + threads.append(thread) + + for thread in threads: + thread.start() + + for threads in threads: + threads.join() + + # Aggregate test results + if all(r.wasSuccessful() for r in results): + result.addSuccess(self) + + # Note: We can't call result.addError, result.addFailure, etc. because + # we no longer have the original exception, just the string format. + for r in results: + if len(r.errors) > 0 or len(r.failures) > 0: + result._mirrorOutput = True + result.errors.extend(r.errors) + result.failures.extend(r.failures) + result.skipped.extend(r.skipped) + result.expectedFailures.extend(r.expectedFailures) + result.unexpectedSuccesses.extend(r.unexpectedSuccesses) + result.collectedDurations.extend(r.collectedDurations) + + if any(r.shouldStop for r in results): + result.stop() + + # Test has finished running + result.stopTest(self) + if stopTestRun is not None: + stopTestRun() diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 130c036a62eefb..759f24fc25e38c 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -100,6 +100,7 @@ class RunTests: python_cmd: tuple[str, ...] | None randomize: bool random_seed: int | str + parallel_threads: int | None def copy(self, **override) -> 'RunTests': state = dataclasses.asdict(self) @@ -184,6 +185,8 @@ def bisect_cmd_args(self) -> list[str]: args.extend(("--python", cmd)) if self.randomize: args.append(f"--randomize") + if self.parallel_threads: + args.append(f"--parallel-threads={self.parallel_threads}") args.append(f"--randseed={self.random_seed}") return args diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 54df688bbc470e..57d7b649d2ef63 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -17,6 +17,7 @@ from .save_env import saved_test_environment from .setup import setup_tests from .testresult import get_test_runner +from .parallel_case import ParallelTestCase from .utils import ( TestName, clear_caches, remove_testfn, abs_module_name, print_warning) @@ -27,14 +28,17 @@ PROGRESS_MIN_TIME = 30.0 # seconds -def run_unittest(test_mod): +def run_unittest(test_mod, runtests: RunTests): loader = unittest.TestLoader() tests = loader.loadTestsFromModule(test_mod) + for error in loader.errors: print(error, file=sys.stderr) if loader.errors: raise Exception("errors while loading tests") _filter_suite(tests, match_test) + if runtests.parallel_threads: + _parallelize_tests(tests, runtests.parallel_threads) return _run_suite(tests) def _filter_suite(suite, pred): @@ -49,6 +53,28 @@ def _filter_suite(suite, pred): newtests.append(test) suite._tests = newtests +def _parallelize_tests(suite, parallel_threads: int): + def is_thread_unsafe(test): + test_method = getattr(test, test._testMethodName) + instance = test_method.__self__ + return (getattr(test_method, "__unittest_thread_unsafe__", False) or + getattr(instance, "__unittest_thread_unsafe__", False)) + + newtests: list[object] = [] + for test in suite._tests: + if isinstance(test, unittest.TestSuite): + _parallelize_tests(test, parallel_threads) + newtests.append(test) + continue + + if is_thread_unsafe(test): + # Don't parallelize thread-unsafe tests + newtests.append(test) + continue + + newtests.append(ParallelTestCase(test, parallel_threads)) + suite._tests = newtests + def _run_suite(suite): """Run tests from a unittest.TestSuite-derived class.""" runner = get_test_runner(sys.stdout, @@ -133,7 +159,7 @@ def _load_run_test(result: TestResult, runtests: RunTests) -> None: raise Exception(f"Module {test_name} defines test_main() which " f"is no longer supported by regrtest") def test_func(): - return run_unittest(test_mod) + return run_unittest(test_mod, runtests) try: regrtest_runner(result, test_func, runtests) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 230bb240c89f77..f31d98bf731d67 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -40,7 +40,7 @@ "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", - "requires_limited_api", "requires_specialization", + "requires_limited_api", "requires_specialization", "thread_unsafe", # sys "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval", @@ -382,6 +382,21 @@ def wrapper(*args, **kw): return decorator +def thread_unsafe(reason): + """Mark a test as not thread safe. When the test runner is run with + --parallel-threads=N, the test will be run in a single thread.""" + def decorator(test_item): + test_item.__unittest_thread_unsafe__ = True + # the reason is not currently used + test_item.__unittest_thread_unsafe__why__ = reason + return test_item + if isinstance(reason, types.FunctionType): + test_item = reason + reason = '' + return decorator(test_item) + return decorator + + def skip_if_buildbot(reason=None): """Decorator raising SkipTest if running on a buildbot.""" import getpass diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index e20e59944e9ce9..017aca3c82850f 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -1,6 +1,7 @@ "Test the functionality of Python classes implementing operators." import unittest +from test import support from test.support import cpython_only, import_helper, script_helper, skip_emscripten_stack_overflow testmeths = [ @@ -134,6 +135,7 @@ def __%s__(self, *args): AllTests = type("AllTests", (object,), d) del d, statictests, method, method_template +@support.thread_unsafe("callLst is shared between threads") class ClassTests(unittest.TestCase): def setUp(self): callLst[:] = [] diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index a7ebc9e8be0294..f2f3d9469f8bab 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1103,6 +1103,7 @@ class MyFrozenSet(frozenset): with self.assertRaises(TypeError): frozenset().__class__ = MyFrozenSet + @support.thread_unsafe def test_slots(self): # Testing __slots__... class C0(object): @@ -5485,6 +5486,7 @@ def __repr__(self): {pickle.dumps, pickle._dumps}, {pickle.loads, pickle._loads})) + @support.thread_unsafe def test_pickle_slots(self): # Tests pickling of classes with __slots__. @@ -5552,6 +5554,7 @@ class E(C): y = pickle_copier.copy(x) self._assert_is_copy(x, y) + @support.thread_unsafe def test_reduce_copying(self): # Tests pickling and copying new-style classes and objects. global C1 diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index 82578a0ef1e6f2..1757824580e416 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -666,6 +666,7 @@ class COperatorTestCase(OperatorTestCase, unittest.TestCase): module = c_operator +@support.thread_unsafe("swaps global operator module") class OperatorPickleTestCase: def copy(self, obj, proto): with support.swap_item(sys.modules, 'operator', self.module): diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 480bff743a9f8a..52d3341975088b 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1538,6 +1538,7 @@ def test_false_encoding(self): self.assertEqual(encoding, 'utf-8') self.assertEqual(consumed_lines, [b'print("#coding=fake")']) + @support.thread_unsafe def test_open(self): filename = os_helper.TESTFN + '.py' self.addCleanup(os_helper.unlink, filename) diff --git a/Misc/NEWS.d/next/Tests/2024-12-16-19-15-10.gh-issue-128003.GVBrfa.rst b/Misc/NEWS.d/next/Tests/2024-12-16-19-15-10.gh-issue-128003.GVBrfa.rst new file mode 100644 index 00000000000000..05711c7e589551 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-12-16-19-15-10.gh-issue-128003.GVBrfa.rst @@ -0,0 +1,4 @@ +Add an option ``--parallel-threads=N`` to the regression test runner that +runs individual tests in multiple threads in parallel in order to find +concurrency bugs. Note that most of the test suite is not yet reviewed for +thread-safety or annotated with ``@thread_unsafe`` when necessary. From e41ec8e18b078024b02a742272e675ae39778536 Mon Sep 17 00:00:00 2001 From: "Tomas R." Date: Tue, 4 Feb 2025 23:59:23 +0100 Subject: [PATCH 071/311] gh-104400: pygettext: Prepare to replace TokenEater with a NodeVisitor (#129672) * Update the module docstring * Move ``key_for`` inside the class * Move ``write_pot_file`` outside the class --- Tools/i18n/pygettext.py | 137 ++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index 81d9fdbb36017b..d8a0e379ab82cb 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -7,15 +7,9 @@ the programming language and can be used from within Python programs. Martin von Loewis' work[1] helps considerably in this regard. -There's one problem though; xgettext is the program that scans source code -looking for message strings, but it groks only C (or C++). Python -introduces a few wrinkles, such as dual quoting characters, triple quoted -strings, and raw strings. xgettext understands none of this. - -Enter pygettext, which uses Python's standard tokenize module to scan -Python source code, generating .pot files identical to what GNU xgettext[2] -generates for C and C++ code. From there, the standard GNU tools can be -used. +pygettext uses Python's standard tokenize module to scan Python source +code, generating .pot files identical to what GNU xgettext[2] generates +for C and C++ code. From there, the standard GNU tools can be used. A word about marking Python strings as candidates for translation. GNU xgettext recognizes the following keywords: gettext, dgettext, dcgettext, @@ -41,6 +35,9 @@ option arguments is broken, and in these cases, pygettext just defines additional switches. +NOTE: The public interface of pygettext is limited to the command-line +interface only. The internal API is subject to change without notice. + Usage: pygettext [options] inputfile ... Options: @@ -328,12 +325,6 @@ def add_location(self, filename, lineno, msgid_plural=None, *, is_docstring=Fals self.is_docstring |= is_docstring -def key_for(msgid, msgctxt=None): - if msgctxt is not None: - return (msgctxt, msgid) - return msgid - - class TokenEater: def __init__(self, options): self.__options = options @@ -354,6 +345,10 @@ def __call__(self, ttype, tstring, stup, etup, line): ## file=sys.stderr) self.__state(ttype, tstring, stup[0]) + @property + def messages(self): + return self.__messages + def __waiting(self, ttype, tstring, lineno): opts = self.__options # Do docstring extractions, if enabled @@ -513,7 +508,7 @@ def __addentry(self, msg, lineno=None, *, is_docstring=False): lineno = self.__lineno msgctxt = msg.get('msgctxt') msgid_plural = msg.get('msgid_plural') - key = key_for(msgid, msgctxt) + key = self._key_for(msgid, msgctxt) if key in self.__messages: self.__messages[key].add_location( self.__curfile, @@ -530,6 +525,12 @@ def __addentry(self, msg, lineno=None, *, is_docstring=False): is_docstring=is_docstring, ) + @staticmethod + def _key_for(msgid, msgctxt=None): + if msgctxt is not None: + return (msgctxt, msgid) + return msgid + def warn_unexpected_token(self, token): print(( '*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"' @@ -543,58 +544,58 @@ def set_filename(self, filename): self.__curfile = filename self.__freshmodule = 1 - def write(self, fp): - options = self.__options - timestamp = time.strftime('%Y-%m-%d %H:%M%z') - encoding = fp.encoding if fp.encoding else 'UTF-8' - print(pot_header % {'time': timestamp, 'version': __version__, - 'charset': encoding, - 'encoding': '8bit'}, file=fp) - - # Sort locations within each message by filename and lineno - sorted_keys = [ - (key, sorted(msg.locations)) - for key, msg in self.__messages.items() - ] - # Sort messages by locations - # For example, a message with locations [('test.py', 1), ('test.py', 2)] will - # appear before a message with locations [('test.py', 1), ('test.py', 3)] - sorted_keys.sort(key=itemgetter(1)) - - for key, locations in sorted_keys: - msg = self.__messages[key] - if options.writelocations: - # location comments are different b/w Solaris and GNU: - if options.locationstyle == options.SOLARIS: - for location in locations: - print(f'# File: {location.filename}, line: {location.lineno}', file=fp) - elif options.locationstyle == options.GNU: - # fit as many locations on one line, as long as the - # resulting line length doesn't exceed 'options.width' - locline = '#:' - for location in locations: - s = f' {location.filename}:{location.lineno}' - if len(locline) + len(s) <= options.width: - locline = locline + s - else: - print(locline, file=fp) - locline = f'#:{s}' - if len(locline) > 2: + +def write_pot_file(messages, options, fp): + timestamp = time.strftime('%Y-%m-%d %H:%M%z') + encoding = fp.encoding if fp.encoding else 'UTF-8' + print(pot_header % {'time': timestamp, 'version': __version__, + 'charset': encoding, + 'encoding': '8bit'}, file=fp) + + # Sort locations within each message by filename and lineno + sorted_keys = [ + (key, sorted(msg.locations)) + for key, msg in messages.items() + ] + # Sort messages by locations + # For example, a message with locations [('test.py', 1), ('test.py', 2)] will + # appear before a message with locations [('test.py', 1), ('test.py', 3)] + sorted_keys.sort(key=itemgetter(1)) + + for key, locations in sorted_keys: + msg = messages[key] + if options.writelocations: + # location comments are different b/w Solaris and GNU: + if options.locationstyle == options.SOLARIS: + for location in locations: + print(f'# File: {location.filename}, line: {location.lineno}', file=fp) + elif options.locationstyle == options.GNU: + # fit as many locations on one line, as long as the + # resulting line length doesn't exceed 'options.width' + locline = '#:' + for location in locations: + s = f' {location.filename}:{location.lineno}' + if len(locline) + len(s) <= options.width: + locline = locline + s + else: print(locline, file=fp) - if msg.is_docstring: - # If the entry was gleaned out of a docstring, then add a - # comment stating so. This is to aid translators who may wish - # to skip translating some unimportant docstrings. - print('#, docstring', file=fp) - if msg.msgctxt is not None: - print('msgctxt', normalize(msg.msgctxt, encoding), file=fp) - print('msgid', normalize(msg.msgid, encoding), file=fp) - if msg.msgid_plural is not None: - print('msgid_plural', normalize(msg.msgid_plural, encoding), file=fp) - print('msgstr[0] ""', file=fp) - print('msgstr[1] ""\n', file=fp) - else: - print('msgstr ""\n', file=fp) + locline = f'#:{s}' + if len(locline) > 2: + print(locline, file=fp) + if msg.is_docstring: + # If the entry was gleaned out of a docstring, then add a + # comment stating so. This is to aid translators who may wish + # to skip translating some unimportant docstrings. + print('#, docstring', file=fp) + if msg.msgctxt is not None: + print('msgctxt', normalize(msg.msgctxt, encoding), file=fp) + print('msgid', normalize(msg.msgid, encoding), file=fp) + if msg.msgid_plural is not None: + print('msgid_plural', normalize(msg.msgid_plural, encoding), file=fp) + print('msgstr[0] ""', file=fp) + print('msgstr[1] ""\n', file=fp) + else: + print('msgstr ""\n', file=fp) def main(): @@ -752,7 +753,7 @@ class Options: fp = open(options.outfile, 'w') closep = 1 try: - eater.write(fp) + write_pot_file(eater.messages, options, fp) finally: if closep: fp.close() From fb5d1c923677e7982360bad934d70cf9ad3366ca Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 5 Feb 2025 13:08:02 +0530 Subject: [PATCH 072/311] gh-129643: fix thread safety of `PyList_SetItem` (#129644) --- .../2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst | 1 + Objects/listobject.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst new file mode 100644 index 00000000000000..27dd3b7f652aca --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-12-42-40.gh-issue-129643.K24Zow.rst @@ -0,0 +1 @@ +Fix thread safety of :c:func:`PyList_SetItem` in free-threading builds. Patch by Kumar Aditya. diff --git a/Objects/listobject.c b/Objects/listobject.c index f4a269e4d7b284..86fa2149556463 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -419,7 +419,6 @@ int PyList_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem) { - PyObject **p; if (!PyList_Check(op)) { Py_XDECREF(newitem); PyErr_BadInternalCall(); @@ -435,8 +434,9 @@ PyList_SetItem(PyObject *op, Py_ssize_t i, ret = -1; goto end; } - p = self->ob_item + i; - Py_XSETREF(*p, newitem); + PyObject *tmp = self->ob_item[i]; + FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[i], newitem); + Py_XDECREF(tmp); ret = 0; end:; Py_END_CRITICAL_SECTION(); From dc804ffb2f7cfaf60916b36f3d5cac9c00e4f1ea Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 5 Feb 2025 11:03:58 +0100 Subject: [PATCH 073/311] gh-128911: Use PyImport_ImportModuleAttr() function (#129657) * Replace PyImport_ImportModule() + PyObject_GetAttr() with PyImport_ImportModuleAttr(). * Replace PyImport_ImportModule() + PyObject_GetAttrString() with PyImport_ImportModuleAttrString(). --- Modules/_ctypes/callbacks.c | 20 ++++---------------- Modules/_ctypes/stgdict.c | 2 +- Modules/_testcapi/code.c | 9 ++------- Modules/_testcapimodule.c | 11 +++-------- Modules/main.c | 36 +++++++++++++----------------------- Python/crossinterp.c | 9 +++------ Python/pythonrun.c | 29 ++++++----------------------- 7 files changed, 32 insertions(+), 84 deletions(-) diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index b84bd25af8ec2c..6dd6f6ec56d008 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -551,31 +551,19 @@ STDAPI DllGetClassObject(REFCLSID rclsid, long Call_CanUnloadNow(void) { - PyObject *mod, *func, *result; - long retval; - - mod = PyImport_ImportModule("ctypes"); - if (!mod) { -/* OutputDebugString("Could not import ctypes"); */ - /* We assume that this error can only occur when shutting - down, so we silently ignore it */ - PyErr_Clear(); - return E_FAIL; - } - /* Other errors cannot be raised, but are printed to stderr */ - func = PyObject_GetAttrString(mod, "DllCanUnloadNow"); - Py_DECREF(mod); + PyObject *func = PyImport_ImportModuleAttrString("ctypes", + "DllCanUnloadNow"); if (!func) { goto error; } - result = _PyObject_CallNoArgs(func); + PyObject *result = _PyObject_CallNoArgs(func); Py_DECREF(func); if (!result) { goto error; } - retval = PyLong_AsLong(result); + long retval = PyLong_AsLong(result); if (PyErr_Occurred()) { Py_DECREF(result); goto error; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index d63a46a3bc23d2..05239d85c44d2c 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -258,7 +258,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct } PyObject *layout_func = PyImport_ImportModuleAttrString("ctypes._layout", - "get_layout"); + "get_layout"); if (!layout_func) { goto error; } diff --git a/Modules/_testcapi/code.c b/Modules/_testcapi/code.c index c0193489b6f340..94f752c9726189 100644 --- a/Modules/_testcapi/code.c +++ b/Modules/_testcapi/code.c @@ -47,7 +47,6 @@ static PyObject * test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) { PyObject *result = NULL; - PyObject *test_module = NULL; PyObject *test_func = NULL; // Get or initialize interpreter-specific code object storage index @@ -62,11 +61,8 @@ test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) // Get a function to test with // This can be any Python function. Use `test.test_misc.testfunction`. - test_module = PyImport_ImportModule("test.test_capi.test_misc"); - if (!test_module) { - goto finally; - } - test_func = PyObject_GetAttrString(test_module, "testfunction"); + test_func = PyImport_ImportModuleAttrString("test.test_capi.test_misc", + "testfunction"); if (!test_func) { goto finally; } @@ -102,7 +98,6 @@ test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable)) } result = Py_NewRef(Py_None); finally: - Py_XDECREF(test_module); Py_XDECREF(test_func); return result; } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 09e74fd3cf20af..c84646ccf03fa7 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1057,15 +1057,10 @@ test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) if (ret != -1 || match == 0) goto error; - PyObject *mod_io = PyImport_ImportModule("_io"); - if (mod_io == NULL) { - return NULL; - } - /* bytesiobuf_getbuffer() */ - PyTypeObject *type = (PyTypeObject *)PyObject_GetAttrString( - mod_io, "_BytesIOBuffer"); - Py_DECREF(mod_io); + PyTypeObject *type = (PyTypeObject *)PyImport_ImportModuleAttrString( + "_io", + "_BytesIOBuffer"); if (type == NULL) { return NULL; } diff --git a/Modules/main.c b/Modules/main.c index 5bb1de2d04d30c..f8a2438cdd0d93 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -314,25 +314,19 @@ pymain_start_pyrepl_no_main(void) static int pymain_run_module(const wchar_t *modname, int set_argv0) { - PyObject *module, *runpy, *runmodule, *runargs, *result; + PyObject *module, *runmodule, *runargs, *result; if (PySys_Audit("cpython.run_module", "u", modname) < 0) { return pymain_exit_err_print(); } - runpy = PyImport_ImportModule("runpy"); - if (runpy == NULL) { - fprintf(stderr, "Could not import runpy module\n"); - return pymain_exit_err_print(); - } - runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); + runmodule = PyImport_ImportModuleAttrString("runpy", + "_run_module_as_main"); if (runmodule == NULL) { - fprintf(stderr, "Could not access runpy._run_module_as_main\n"); - Py_DECREF(runpy); + fprintf(stderr, "Could not import runpy._run_module_as_main\n"); return pymain_exit_err_print(); } module = PyUnicode_FromWideChar(modname, wcslen(modname)); if (module == NULL) { fprintf(stderr, "Could not convert module name to unicode\n"); - Py_DECREF(runpy); Py_DECREF(runmodule); return pymain_exit_err_print(); } @@ -340,7 +334,6 @@ pymain_run_module(const wchar_t *modname, int set_argv0) if (runargs == NULL) { fprintf(stderr, "Could not create arguments for runpy._run_module_as_main\n"); - Py_DECREF(runpy); Py_DECREF(runmodule); Py_DECREF(module); return pymain_exit_err_print(); @@ -350,7 +343,6 @@ pymain_run_module(const wchar_t *modname, int set_argv0) if (!result && PyErr_Occurred() == PyExc_KeyboardInterrupt) { _PyRuntime.signals.unhandled_keyboard_interrupt = 1; } - Py_DECREF(runpy); Py_DECREF(runmodule); Py_DECREF(module); Py_DECREF(runargs); @@ -497,24 +489,22 @@ pymain_run_startup(PyConfig *config, int *exitcode) static int pymain_run_interactive_hook(int *exitcode) { - PyObject *sys, *hook, *result; - sys = PyImport_ImportModule("sys"); - if (sys == NULL) { - goto error; - } - - hook = PyObject_GetAttrString(sys, "__interactivehook__"); - Py_DECREF(sys); + PyObject *hook = PyImport_ImportModuleAttrString("sys", + "__interactivehook__"); if (hook == NULL) { - PyErr_Clear(); - return 0; + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + // no sys.__interactivehook__ attribute + PyErr_Clear(); + return 0; + } + goto error; } if (PySys_Audit("cpython.run_interactivehook", "O", hook) < 0) { goto error; } - result = _PyObject_CallNoArgs(hook); + PyObject *result = _PyObject_CallNoArgs(hook); Py_DECREF(hook); if (result == NULL) { goto error; diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 7eb5bc267487d1..aa2c1cb78bce06 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -368,12 +368,9 @@ _convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc) PyObject *create = NULL; // This is inspired by _PyErr_Display(). - PyObject *tbmod = PyImport_ImportModule("traceback"); - if (tbmod == NULL) { - return -1; - } - PyObject *tbexc_type = PyObject_GetAttrString(tbmod, "TracebackException"); - Py_DECREF(tbmod); + PyObject *tbexc_type = PyImport_ImportModuleAttrString( + "traceback", + "TracebackException"); if (tbexc_type == NULL) { return -1; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 0da26ad3f9b4bd..ae0df9685ac159 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1108,22 +1108,15 @@ _PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb) int unhandled_keyboard_interrupt = _PyRuntime.signals.unhandled_keyboard_interrupt; // Try first with the stdlib traceback module - PyObject *traceback_module = PyImport_ImportModule("traceback"); - - if (traceback_module == NULL) { - goto fallback; - } - - PyObject *print_exception_fn = PyObject_GetAttrString(traceback_module, "_print_exception_bltin"); - + PyObject *print_exception_fn = PyImport_ImportModuleAttrString( + "traceback", + "_print_exception_bltin"); if (print_exception_fn == NULL || !PyCallable_Check(print_exception_fn)) { - Py_DECREF(traceback_module); goto fallback; } PyObject* result = PyObject_CallOneArg(print_exception_fn, value); - Py_DECREF(traceback_module); Py_XDECREF(print_exception_fn); if (result) { Py_DECREF(result); @@ -1371,27 +1364,18 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, } if (interactive_src) { - PyObject *linecache_module = PyImport_ImportModule("linecache"); - - if (linecache_module == NULL) { - Py_DECREF(co); - Py_DECREF(interactive_filename); - return NULL; - } - - PyObject *print_tb_func = PyObject_GetAttrString(linecache_module, "_register_code"); - + PyObject *print_tb_func = PyImport_ImportModuleAttrString( + "linecache", + "_register_code"); if (print_tb_func == NULL) { Py_DECREF(co); Py_DECREF(interactive_filename); - Py_DECREF(linecache_module); return NULL; } if (!PyCallable_Check(print_tb_func)) { Py_DECREF(co); Py_DECREF(interactive_filename); - Py_DECREF(linecache_module); Py_DECREF(print_tb_func); PyErr_SetString(PyExc_ValueError, "linecache._register_code is not callable"); return NULL; @@ -1406,7 +1390,6 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, Py_DECREF(interactive_filename); - Py_DECREF(linecache_module); Py_XDECREF(print_tb_func); Py_XDECREF(result); if (!result) { From a25042e6d2b71651e93ff90c361ad763c0368872 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 5 Feb 2025 11:31:59 +0100 Subject: [PATCH 074/311] gh-129354: Use PyErr_FormatUnraisable() function (#129523) Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable(). Update tests: * test_coroutines * test_exceptions * test_generators * test_struct --- Lib/test/test_coroutines.py | 9 +++++++-- Lib/test/test_exceptions.py | 5 ++++- Lib/test/test_generators.py | 12 +++++++++--- Lib/test/test_struct.py | 2 +- Objects/genobject.c | 12 ++++++++---- Objects/typeobject.c | 9 ++++++--- Python/_warnings.c | 10 +++++++--- 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 840043d5271224..5566c9803d43ed 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2136,8 +2136,10 @@ async def func(): pass coro = None support.gc_collect() + self.assertEqual(cm.unraisable.err_msg, + f"Exception ignored while finalizing " + f"coroutine {coro_repr}") self.assertIn("was never awaited", str(cm.unraisable.exc_value)) - self.assertEqual(repr(cm.unraisable.object), coro_repr) def test_for_assign_raising_stop_async_iteration(self): class BadTarget: @@ -2411,10 +2413,13 @@ async def corofn(): coro_repr = repr(coro) # clear reference to the coroutine without awaiting for it + coro_repr = repr(coro) del coro support.gc_collect() - self.assertEqual(repr(cm.unraisable.object), coro_repr) + self.assertEqual(cm.unraisable.err_msg, + f"Exception ignored while finalizing " + f"coroutine {coro_repr}") self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError) del warnings._warn_unawaited_coroutine diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 2d324827451b54..3838eb5b27c9e6 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1678,10 +1678,13 @@ def __del__(self): obj = BrokenDel() with support.catch_unraisable_exception() as cm: + obj_repr = repr(type(obj).__del__) del obj gc_collect() # For PyPy or other GCs. - self.assertEqual(cm.unraisable.object, BrokenDel.__del__) + self.assertEqual(cm.unraisable.err_msg, + f"Exception ignored while calling " + f"deallocator {obj_repr}") self.assertIsNotNone(cm.unraisable.exc_traceback) def test_unhandled(self): diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index b6985054c33d10..bf4b88cd9c4450 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -2664,14 +2664,18 @@ def printsolution(self, x): >>> with support.catch_unraisable_exception() as cm: ... g = f() ... next(g) +... gen_repr = repr(g) ... del g ... +... cm.unraisable.err_msg == (f'Exception ignored while closing ' +... f'generator {gen_repr}') ... cm.unraisable.exc_type == RuntimeError ... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value) ... cm.unraisable.exc_traceback is not None True True True +True And errors thrown during closing should propagate: @@ -2776,10 +2780,12 @@ def printsolution(self, x): ... invoke("del failed") ... >>> with support.catch_unraisable_exception() as cm: -... l = Leaker() -... del l +... leaker = Leaker() +... del_repr = repr(type(leaker).__del__) +... del leaker ... -... cm.unraisable.object == Leaker.__del__ +... cm.unraisable.err_msg == (f'Exception ignored while ' +... f'calling deallocator {del_repr}') ... cm.unraisable.exc_type == RuntimeError ... str(cm.unraisable.exc_value) == "del failed" ... cm.unraisable.exc_traceback is not None diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 5fee9fbb92acf4..b99391e482ff70 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -694,7 +694,7 @@ def __del__(self): rc, stdout, stderr = assert_python_ok("-c", code) self.assertEqual(rc, 0) self.assertEqual(stdout.rstrip(), b"") - self.assertIn(b"Exception ignored in:", stderr) + self.assertIn(b"Exception ignored while calling deallocator", stderr) self.assertIn(b"C.__del__", stderr) def test__struct_reference_cycle_cleaned_up(self): diff --git a/Objects/genobject.c b/Objects/genobject.c index 73bbf86588c457..79aed8571c35e7 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -97,8 +97,10 @@ _PyGen_Finalize(PyObject *self) PyObject *res = PyObject_CallOneArg(finalizer, self); if (res == NULL) { - PyErr_WriteUnraisable(self); - } else { + PyErr_FormatUnraisable("Exception ignored while " + "finalizing generator %R", self); + } + else { Py_DECREF(res); } /* Restore the saved exception. */ @@ -122,7 +124,8 @@ _PyGen_Finalize(PyObject *self) PyObject *res = gen_close((PyObject*)gen, NULL); if (res == NULL) { if (PyErr_Occurred()) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored while " + "closing generator %R", self); } } else { @@ -338,7 +341,8 @@ gen_close_iter(PyObject *yf) else { PyObject *meth; if (PyObject_GetOptionalAttr(yf, &_Py_ID(close), &meth) < 0) { - PyErr_WriteUnraisable(yf); + PyErr_FormatUnraisable("Exception ignored while " + "closing generator %R", yf); } if (meth) { retval = _PyObject_CallNoArgs(meth); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 93920341a179e8..f3238da8a642e4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10288,10 +10288,13 @@ slot_tp_finalize(PyObject *self) del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound); if (del != NULL) { res = call_unbound_noarg(unbound, del, self); - if (res == NULL) - PyErr_WriteUnraisable(del); - else + if (res == NULL) { + PyErr_FormatUnraisable("Exception ignored while " + "calling deallocator %R", del); + } + else { Py_DECREF(res); + } Py_DECREF(del); } diff --git a/Python/_warnings.c b/Python/_warnings.c index 283f203c72c9bf..bb195da9512caf 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1445,7 +1445,8 @@ _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method) "coroutine method %R of %R was never awaited", method, agen->ag_qualname) < 0) { - PyErr_WriteUnraisable((PyObject *)agen); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing async generator %R", agen); } PyErr_SetRaisedException(exc); } @@ -1487,14 +1488,17 @@ _PyErr_WarnUnawaitedCoroutine(PyObject *coro) } if (PyErr_Occurred()) { - PyErr_WriteUnraisable(coro); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing coroutine %R", coro); } + if (!warned) { if (_PyErr_WarnFormat(coro, PyExc_RuntimeWarning, 1, "coroutine '%S' was never awaited", ((PyCoroObject *)coro)->cr_qualname) < 0) { - PyErr_WriteUnraisable(coro); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing coroutine %R", coro); } } } From e5c3b7e34974dcd6d7f6a1a50030bf7fbce38e74 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 5 Feb 2025 12:43:46 +0100 Subject: [PATCH 075/311] gh-129354: Cleanup test_coroutines (#129684) Remove unused variables. --- Lib/test/test_coroutines.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 5566c9803d43ed..ae3cd3555002ef 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -735,7 +735,7 @@ async def func(): pass def test_func_12(self): async def g(): - i = me.send(None) + me.send(None) await foo me = g() with self.assertRaisesRegex(ValueError, @@ -2283,7 +2283,7 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): buffer.append(exc_type.__name__) async def f(): - async with CM() as c: + async with CM(): await asyncio.sleep(0.01) raise MyException buffer.append('unreachable') @@ -2375,7 +2375,7 @@ def check(depth, msg): orig_depth = sys.get_coroutine_origin_tracking_depth() try: - msg = check(0, f"coroutine '{corofn.__qualname__}' was never awaited") + check(0, f"coroutine '{corofn.__qualname__}' was never awaited") check(1, "".join([ f"coroutine '{corofn.__qualname__}' was never awaited\n", "Coroutine created at (most recent call last)\n", @@ -2413,7 +2413,6 @@ async def corofn(): coro_repr = repr(coro) # clear reference to the coroutine without awaiting for it - coro_repr = repr(coro) del coro support.gc_collect() From 58a4357e29a15135e6fd99f320c60f8ea0472d27 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 5 Feb 2025 16:12:23 +0100 Subject: [PATCH 076/311] gh-127833: Docs: Add a `grammar-snippet` directive & replace `productionlist` (GH-127835) As a first step toward aligning the grammar documentation with Python's actual grammar, this overrides the ReST `productionlist` directive to: - use `:` instead of the `::=` symbol - add syntax highlighting for strings (using a Pygments highlighting class) All links and link targets should be preserved. (Unfortunately, this reaches into some Sphinx internals; I don't see a better way to do exactly what Sphinx does.) This also adds a new directive, `grammar-snippet`, which formats the snippet almost exactly like what's in the source, modulo syntax highlighting and keeping the backtick character to mark links to other rules. This will allow formatting the snippets as in the grammar file (file:///home/encukou/dev/cpython/Doc/build/html/reference/grammar.html). The new directive is applied to two simple rules in toplevel_components.rst --------- Co-authored-by: Blaise Pabon Co-authored-by: William Ferreira Co-authored-by: bswck Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- Doc/conf.py | 1 + Doc/reference/toplevel_components.rst | 8 +- Doc/tools/extensions/grammar_snippet.py | 219 ++++++++++++++++++++++++ 3 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 Doc/tools/extensions/grammar_snippet.py diff --git a/Doc/conf.py b/Doc/conf.py index 94af54084ee338..a4e0c628649018 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -27,6 +27,7 @@ 'c_annotations', 'changes', 'glossary_search', + 'grammar_snippet', 'lexers', 'misc_news', 'pydoc_topics', diff --git a/Doc/reference/toplevel_components.rst b/Doc/reference/toplevel_components.rst index dd3d3d6878e289..f155fafbe4d738 100644 --- a/Doc/reference/toplevel_components.rst +++ b/Doc/reference/toplevel_components.rst @@ -66,7 +66,9 @@ File input All input read from non-interactive files has the same form: -.. productionlist:: python-grammar +.. grammar-snippet:: + :group: python-grammar + file_input: (NEWLINE | `statement`)* This syntax is used in the following situations: @@ -85,7 +87,9 @@ Interactive input Input in interactive mode is parsed using the following grammar: -.. productionlist:: python-grammar +.. grammar-snippet:: + :group: python-grammar + interactive_input: [`stmt_list`] NEWLINE | `compound_stmt` NEWLINE Note that a (top-level) compound statement must be followed by a blank line in diff --git a/Doc/tools/extensions/grammar_snippet.py b/Doc/tools/extensions/grammar_snippet.py new file mode 100644 index 00000000000000..03c7e7ce2f4228 --- /dev/null +++ b/Doc/tools/extensions/grammar_snippet.py @@ -0,0 +1,219 @@ +"""Support for documenting Python's grammar.""" + +from __future__ import annotations + +import re +from typing import TYPE_CHECKING + +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx import addnodes +from sphinx.domains.std import token_xrefs +from sphinx.util.docutils import SphinxDirective +from sphinx.util.nodes import make_id + +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Any + + from docutils.nodes import Node + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata + + +class snippet_string_node(nodes.inline): # noqa: N801 (snake_case is fine) + """Node for a string literal in a grammar snippet.""" + + def __init__( + self, + rawsource: str = '', + text: str = '', + *children: Node, + **attributes: Any, + ) -> None: + super().__init__(rawsource, text, *children, **attributes) + # Use the Pygments highlight class for `Literal.String.Other` + self['classes'].append('sx') + + +class GrammarSnippetBase(SphinxDirective): + """Common functionality for GrammarSnippetDirective & CompatProductionList.""" + + # The option/argument handling is left to the individual classes. + + def make_grammar_snippet( + self, options: dict[str, Any], content: Sequence[str] + ) -> list[nodes.paragraph]: + """Create a literal block from options & content.""" + + group_name = options['group'] + + # Docutils elements have a `rawsource` attribute that is supposed to be + # set to the original ReST source. + # Sphinx does the following with it: + # - if it's empty, set it to `self.astext()` + # - if it matches `self.astext()` when generating the output, + # apply syntax highlighting (which is based on the plain-text content + # and thus discards internal formatting, like references). + # To get around this, we set it to this non-empty string: + rawsource = 'You should not see this.' + + literal = nodes.literal_block( + rawsource, + '', + classes=['highlight'], + ) + + grammar_re = re.compile( + r""" + (?P^[a-zA-Z0-9_]+) # identifier at start of line + (?=:) # ... followed by a colon + | + (?P`[^\s`]+`) # identifier in backquotes + | + (?P'[^']*') # string in 'quotes' + | + (?P"[^"]*") # string in "quotes" + """, + re.VERBOSE, + ) + + for line in content: + last_pos = 0 + for match in grammar_re.finditer(line): + # Handle text between matches + if match.start() > last_pos: + literal += nodes.Text(line[last_pos : match.start()]) + last_pos = match.end() + + # Handle matches + group_dict = { + name: content + for name, content in match.groupdict().items() + if content is not None + } + match group_dict: + case {'rule_name': name}: + literal += self.make_link_target_for_token( + group_name, name + ) + case {'rule_ref': ref_text}: + literal += token_xrefs(ref_text, group_name) + case {'single_quoted': name} | {'double_quoted': name}: + literal += snippet_string_node('', name) + case _: + raise ValueError('unhandled match') + literal += nodes.Text(line[last_pos:] + '\n') + + node = nodes.paragraph( + '', + '', + literal, + ) + + return [node] + + def make_link_target_for_token( + self, group_name: str, name: str + ) -> addnodes.literal_strong: + """Return a literal node which is a link target for the given token.""" + name_node = addnodes.literal_strong() + + # Cargo-culted magic to make `name_node` a link target + # similar to Sphinx `production`. + # This needs to be the same as what Sphinx does + # to avoid breaking existing links. + domain = self.env.domains['std'] + obj_name = f"{group_name}:{name}" + prefix = f'grammar-token-{group_name}' + node_id = make_id(self.env, self.state.document, prefix, name) + name_node['ids'].append(node_id) + self.state.document.note_implicit_target(name_node, name_node) + domain.note_object('token', obj_name, node_id, location=name_node) + + text_node = nodes.Text(name) + name_node += text_node + return name_node + + +class GrammarSnippetDirective(GrammarSnippetBase): + """Transform a grammar-snippet directive to a Sphinx literal_block + + That is, turn something like: + + .. grammar-snippet:: file + :group: python-grammar + + file: (NEWLINE | statement)* + + into something similar to Sphinx productionlist, but better suited + for our needs: + - Instead of `::=`, use a colon, as in `Grammar/python.gram` + - Show the listing almost as is, with no auto-aligment. + The only special character is the backtick, which marks tokens. + + Unlike Sphinx's productionlist, this directive supports options. + The "group" must be given as a named option. + The content must be preceded by a blank line (like with most ReST + directives). + """ + + has_content = True + option_spec = { + 'group': directives.unchanged_required, + } + + # We currently ignore arguments. + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + + def run(self) -> list[nodes.paragraph]: + return self.make_grammar_snippet(self.options, self.content) + + +class CompatProductionList(GrammarSnippetBase): + """Create grammar snippets from reST productionlist syntax + + This is intended to be a transitional directive, used while we switch + from productionlist to grammar-snippet. + It makes existing docs that use the ReST syntax look like grammar-snippet, + as much as possible. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self) -> list[nodes.paragraph]: + # The "content" of a productionlist is actually the first and only + # argument. The first line is the group; the rest is the content lines. + lines = self.arguments[0].splitlines() + group = lines[0].strip() + options = {'group': group} + # We assume there's a colon in each line; align on it. + align_column = max(line.index(':') for line in lines[1:]) + 1 + content = [] + for line in lines[1:]: + rule_name, _colon, text = line.partition(':') + rule_name = rule_name.strip() + if rule_name: + name_part = rule_name + ':' + else: + name_part = '' + content.append(f'{name_part:<{align_column}}{text}') + return self.make_grammar_snippet(options, content) + + +def setup(app: Sphinx) -> ExtensionMetadata: + app.add_directive('grammar-snippet', GrammarSnippetDirective) + app.add_directive_to_domain( + 'std', 'productionlist', CompatProductionList, override=True + ) + return { + 'version': '1.0', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } From 7d9a22f50923309955a2caf7d57013f224071e6e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:39:42 +0000 Subject: [PATCH 077/311] Convert change detection to a Python script (#129627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- .github/workflows/build.yml | 98 +++++----- .../workflows/reusable-change-detection.yml | 173 ----------------- .github/workflows/reusable-context.yml | 100 ++++++++++ Tools/build/compute-changes.py | 183 ++++++++++++++++++ 4 files changed, 329 insertions(+), 225 deletions(-) delete mode 100644 .github/workflows/reusable-change-detection.yml create mode 100644 .github/workflows/reusable-context.yml create mode 100644 Tools/build/compute-changes.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c10c5b4aa46ffb..dc2f4858be6e8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,25 +22,25 @@ env: FORCE_COLOR: 1 jobs: - check_source: + build-context: name: Change detection # To use boolean outputs from this job, parse them as JSON. # Here's some examples: # - # if: fromJSON(needs.check_source.outputs.run-docs) + # if: fromJSON(needs.build-context.outputs.run-docs) # # ${{ - # fromJSON(needs.check_source.outputs.run_tests) + # fromJSON(needs.build-context.outputs.run-tests) # && 'truthy-branch' # || 'falsy-branch' # }} # - uses: ./.github/workflows/reusable-change-detection.yml + uses: ./.github/workflows/reusable-context.yml check-docs: name: Docs - needs: check_source - if: fromJSON(needs.check_source.outputs.run-docs) + needs: build-context + if: fromJSON(needs.build-context.outputs.run-docs) uses: ./.github/workflows/reusable-docs.yml check_autoconf_regen: @@ -51,8 +51,8 @@ jobs: container: image: ghcr.io/python/autoconf:2025.01.02.12581854023 timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' steps: - name: Install Git run: | @@ -94,8 +94,8 @@ jobs: # reproducible: to get the same tools versions (autoconf, aclocal, ...) runs-on: ubuntu-24.04 timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' steps: - uses: actions/checkout@v4 with: @@ -110,7 +110,7 @@ jobs: with: path: config.cache # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }} + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.build-context.outputs.config-hash }}-${{ env.pythonLocation }} - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Add ccache to PATH @@ -153,8 +153,8 @@ jobs: name: >- Windows ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} - needs: check_source - if: fromJSON(needs.check_source.outputs.run_tests) + needs: build-context + if: fromJSON(needs.build-context.outputs.run-tests) strategy: fail-fast: false matrix: @@ -184,8 +184,8 @@ jobs: build_windows_msi: name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category Windows MSI${{ '' }} - needs: check_source - if: fromJSON(needs.check_source.outputs.run-win-msi) + needs: build-context + if: fromJSON(needs.build-context.outputs.run-windows-msi) strategy: matrix: arch: @@ -200,8 +200,8 @@ jobs: name: >- macOS ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' strategy: fail-fast: false matrix: @@ -226,7 +226,7 @@ jobs: free-threading: true uses: ./.github/workflows/reusable-macos.yml with: - config_hash: ${{ needs.check_source.outputs.config_hash }} + config_hash: ${{ needs.build-context.outputs.config-hash }} free-threading: ${{ matrix.free-threading }} os: ${{ matrix.os }} @@ -235,8 +235,8 @@ jobs: Ubuntu ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }} - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' strategy: matrix: bolt: @@ -257,7 +257,7 @@ jobs: bolt: true uses: ./.github/workflows/reusable-ubuntu.yml with: - config_hash: ${{ needs.check_source.outputs.config_hash }} + config_hash: ${{ needs.build-context.outputs.config-hash }} bolt-optimizations: ${{ matrix.bolt }} free-threading: ${{ matrix.free-threading }} os: ${{ matrix.os }} @@ -266,8 +266,8 @@ jobs: name: 'Ubuntu SSL tests with OpenSSL' runs-on: ${{ matrix.os }} timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' strategy: fail-fast: false matrix: @@ -289,7 +289,7 @@ jobs: uses: actions/cache@v4 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }} + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.build-context.outputs.config-hash }} - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install Dependencies @@ -326,18 +326,18 @@ jobs: build_wasi: name: 'WASI' - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' uses: ./.github/workflows/reusable-wasi.yml with: - config_hash: ${{ needs.check_source.outputs.config_hash }} + config_hash: ${{ needs.build-context.outputs.config-hash }} test_hypothesis: name: "Hypothesis tests on Ubuntu" runs-on: ubuntu-24.04 timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' env: OPENSSL_VER: 3.0.15 PYTHONSTRICTEXTENSIONBUILD: 1 @@ -384,7 +384,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.CPYTHON_BUILDDIR }}/config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }} + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.build-context.outputs.config-hash }} - name: Configure CPython out-of-tree working-directory: ${{ env.CPYTHON_BUILDDIR }} run: | @@ -452,8 +452,8 @@ jobs: name: 'Address sanitizer' runs-on: ${{ matrix.os }} timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' strategy: matrix: os: [ubuntu-24.04] @@ -471,7 +471,7 @@ jobs: uses: actions/cache@v4 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }} + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.build-context.outputs.config-hash }} - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Install Dependencies @@ -515,8 +515,8 @@ jobs: name: >- Thread sanitizer ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' strategy: matrix: free-threading: @@ -524,14 +524,14 @@ jobs: - true uses: ./.github/workflows/reusable-tsan.yml with: - config_hash: ${{ needs.check_source.outputs.config_hash }} + config_hash: ${{ needs.build-context.outputs.config-hash }} free-threading: ${{ matrix.free-threading }} cross-build-linux: name: Cross build Linux runs-on: ubuntu-latest - needs: check_source - if: needs.check_source.outputs.run_tests == 'true' + needs: build-context + if: needs.build-context.outputs.run-tests == 'true' steps: - uses: actions/checkout@v4 with: @@ -542,7 +542,7 @@ jobs: uses: actions/cache@v4 with: path: config.cache - key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }} + key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.build-context.outputs.config-hash }} - name: Register gcc problem matcher run: echo "::add-matcher::.github/problem-matchers/gcc.json" - name: Set build dir @@ -571,8 +571,8 @@ jobs: name: CIFuzz runs-on: ubuntu-latest timeout-minutes: 60 - needs: check_source - if: needs.check_source.outputs.run_cifuzz == 'true' + needs: build-context + if: needs.build-context.outputs.run-ci-fuzz == 'true' permissions: security-events: write strategy: @@ -611,7 +611,7 @@ jobs: if: always() needs: - - check_source # Transitive dependency, needed to access `run_tests` value + - build-context # Transitive dependency, needed to access `run-tests` value - check-docs - check_autoconf_regen - check_generated_files @@ -639,14 +639,14 @@ jobs: test_hypothesis, allowed-skips: >- ${{ - !fromJSON(needs.check_source.outputs.run-docs) + !fromJSON(needs.build-context.outputs.run-docs) && ' check-docs, ' || '' }} ${{ - needs.check_source.outputs.run_tests != 'true' + needs.build-context.outputs.run-tests != 'true' && ' check_autoconf_regen, check_generated_files, @@ -657,21 +657,15 @@ jobs: build_windows, build_asan, build_tsan, + test_hypothesis, ' || '' }} ${{ - !fromJSON(needs.check_source.outputs.run_cifuzz) + !fromJSON(needs.build-context.outputs.run-ci-fuzz) && ' cifuzz, ' || '' }} - ${{ - !fromJSON(needs.check_source.outputs.run_hypothesis) - && ' - test_hypothesis, - ' - || '' - }} jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/reusable-change-detection.yml b/.github/workflows/reusable-change-detection.yml deleted file mode 100644 index c08c0cb8873f12..00000000000000 --- a/.github/workflows/reusable-change-detection.yml +++ /dev/null @@ -1,173 +0,0 @@ -name: Reusable change detection - -on: # yamllint disable-line rule:truthy - workflow_call: - outputs: - # Some of the referenced steps set outputs conditionally and there may be - # cases when referencing them evaluates to empty strings. It is nice to - # work with proper booleans so they have to be evaluated through JSON - # conversion in the expressions. However, empty strings used like that - # may trigger all sorts of undefined and hard-to-debug behaviors in - # GitHub Actions CI/CD. To help with this, all of the outputs set here - # that are meant to be used as boolean flags (and not arbitrary strings), - # MUST have fallbacks with default values set. A common pattern would be - # to add ` || false` to all such expressions here, in the output - # definitions. They can then later be safely used through the following - # idiom in job conditionals and other expressions. Here's some examples: - # - # if: fromJSON(needs.change-detection.outputs.run-docs) - # - # ${{ - # fromJSON(needs.change-detection.outputs.run-tests) - # && 'truthy-branch' - # || 'falsy-branch' - # }} - # - config_hash: - description: Config hash value for use in cache keys - value: ${{ jobs.compute-changes.outputs.config-hash }} # str - run-docs: - description: Whether to build the docs - value: ${{ jobs.compute-changes.outputs.run-docs || false }} # bool - run_tests: - description: Whether to run the regular tests - value: ${{ jobs.compute-changes.outputs.run-tests || false }} # bool - run-win-msi: - description: Whether to run the MSI installer smoke tests - value: >- # bool - ${{ jobs.compute-changes.outputs.run-win-msi || false }} - run_hypothesis: - description: Whether to run the Hypothesis tests - value: >- # bool - ${{ jobs.compute-changes.outputs.run-hypothesis || false }} - run_cifuzz: - description: Whether to run the CIFuzz job - value: >- # bool - ${{ jobs.compute-changes.outputs.run-cifuzz || false }} - -jobs: - compute-changes: - name: Compute changed files - runs-on: ubuntu-latest - timeout-minutes: 10 - outputs: - config-hash: ${{ steps.config-hash.outputs.hash }} - run-cifuzz: ${{ steps.check.outputs.run-cifuzz }} - run-docs: ${{ steps.docs-changes.outputs.run-docs }} - run-hypothesis: ${{ steps.check.outputs.run-hypothesis }} - run-tests: ${{ steps.check.outputs.run-tests }} - run-win-msi: ${{ steps.win-msi-changes.outputs.run-win-msi }} - steps: - - run: >- - echo '${{ github.event_name }}' - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Check for source changes - id: check - run: | - if [ -z "$GITHUB_BASE_REF" ]; then - echo "run-tests=true" >> "$GITHUB_OUTPUT" - else - git fetch origin "$GITHUB_BASE_REF" --depth=1 - # git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more - # reliable than git diff "origin/$GITHUB_BASE_REF.." (2 dots), - # but it requires to download more commits (this job uses - # "git fetch --depth=1"). - # - # git diff "origin/$GITHUB_BASE_REF..." (3 dots) works with Git - # 2.26, but Git 2.28 is stricter and fails with "no merge base". - # - # git diff "origin/$GITHUB_BASE_REF.." (2 dots) should be enough on - # GitHub, since GitHub starts by merging origin/$GITHUB_BASE_REF - # into the PR branch anyway. - # - # https://github.com/python/core-workflow/issues/373 - grep_ignore_args=( - # file extensions - -e '\.md$' - -e '\.rst$' - # top-level folders - -e '^Doc/' - -e '^Misc/' - # configuration files - -e '^\.github/CODEOWNERS$' - -e '^\.pre-commit-config\.yaml$' - -e '\.ruff\.toml$' - -e 'mypy\.ini$' - ) - git diff --name-only "origin/$GITHUB_BASE_REF.." \ - | grep -qvE "${grep_ignore_args[@]}" \ - && echo "run-tests=true" >> "$GITHUB_OUTPUT" || true - fi - - # Check if we should run hypothesis tests - GIT_BRANCH=${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}} - echo "$GIT_BRANCH" - if $(echo "$GIT_BRANCH" | grep -q -w '3\.\(8\|9\|10\|11\)'); then - echo "Branch too old for hypothesis tests" - echo "run-hypothesis=false" >> "$GITHUB_OUTPUT" - else - echo "Run hypothesis tests" - echo "run-hypothesis=true" >> "$GITHUB_OUTPUT" - fi - - # oss-fuzz maintains a configuration for fuzzing the main branch of - # CPython, so CIFuzz should be run only for code that is likely to be - # merged into the main branch; compatibility with older branches may - # be broken. - FUZZ_RELEVANT_FILES='(\.c$|\.h$|\.cpp$|^configure$|^\.github/workflows/build\.yml$|^Modules/_xxtestfuzz)' - if [ "$GITHUB_BASE_REF" = "main" ] && [ "$(git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qE $FUZZ_RELEVANT_FILES; echo $?)" -eq 0 ]; then - # The tests are pretty slow so they are executed only for PRs - # changing relevant files. - echo "Run CIFuzz tests" - echo "run-cifuzz=true" >> "$GITHUB_OUTPUT" - else - echo "Branch too old for CIFuzz tests; or no C files were changed" - echo "run-cifuzz=false" >> "$GITHUB_OUTPUT" - fi - - name: Compute hash for config cache key - id: config-hash - run: | - echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> "$GITHUB_OUTPUT" - - name: Get a list of the changed documentation-related files - if: github.event_name == 'pull_request' - id: changed-docs-files - uses: Ana06/get-changed-files@v2.3.0 - with: - filter: | - Doc/** - Misc/** - .github/workflows/reusable-docs.yml - format: csv # works for paths with spaces - - name: Check for docs changes - # We only want to run this on PRs when related files are changed, - # or when user triggers manual workflow run. - if: >- - ( - github.event_name == 'pull_request' - && steps.changed-docs-files.outputs.added_modified_renamed != '' - ) || github.event_name == 'workflow_dispatch' - id: docs-changes - run: | - echo "run-docs=true" >> "${GITHUB_OUTPUT}" - - name: Get a list of the MSI installer-related files - if: github.event_name == 'pull_request' - id: changed-win-msi-files - uses: Ana06/get-changed-files@v2.3.0 - with: - filter: | - Tools/msi/** - .github/workflows/reusable-windows-msi.yml - format: csv # works for paths with spaces - - name: Check for changes in MSI installer-related files - # We only want to run this on PRs when related files are changed, - # or when user triggers manual workflow run. - if: >- - ( - github.event_name == 'pull_request' - && steps.changed-win-msi-files.outputs.added_modified_renamed != '' - ) || github.event_name == 'workflow_dispatch' - id: win-msi-changes - run: | - echo "run-win-msi=true" >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/reusable-context.yml b/.github/workflows/reusable-context.yml new file mode 100644 index 00000000000000..fa4df6f29711db --- /dev/null +++ b/.github/workflows/reusable-context.yml @@ -0,0 +1,100 @@ +name: Reusable build context + +on: # yamllint disable-line rule:truthy + workflow_call: + outputs: + # Every referenced step MUST always set its output variable, + # either via ``Tools/build/compute-changes.py`` or in this workflow file. + # Boolean outputs (generally prefixed ``run-``) can then later be used + # safely through the following idiom in job conditionals and other + # expressions. Here's some examples: + # + # if: fromJSON(needs.build-context.outputs.run-tests) + # + # ${{ + # fromJSON(needs.build-context.outputs.run-tests) + # && 'truthy-branch' + # || 'falsy-branch' + # }} + # + config-hash: + description: Config hash value for use in cache keys + value: ${{ jobs.compute-changes.outputs.config-hash }} # str + run-docs: + description: Whether to build the docs + value: ${{ jobs.compute-changes.outputs.run-docs }} # bool + run-tests: + description: Whether to run the regular tests + value: ${{ jobs.compute-changes.outputs.run-tests }} # bool + run-windows-msi: + description: Whether to run the MSI installer smoke tests + value: ${{ jobs.compute-changes.outputs.run-windows-msi }} # bool + run-ci-fuzz: + description: Whether to run the CIFuzz job + value: ${{ jobs.compute-changes.outputs.run-ci-fuzz }} # bool + +jobs: + compute-changes: + name: Create context from changed files + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + config-hash: ${{ steps.config-hash.outputs.hash }} + run-ci-fuzz: ${{ steps.changes.outputs.run-ci-fuzz }} + run-docs: ${{ steps.changes.outputs.run-docs }} + run-tests: ${{ steps.changes.outputs.run-tests }} + run-windows-msi: ${{ steps.changes.outputs.run-windows-msi }} + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3" + + - run: >- + echo '${{ github.event_name }}' + + - uses: actions/checkout@v4 + with: + persist-credentials: false + ref: >- + ${{ + github.event_name == 'pull_request' + && github.event.pull_request.head.sha + || '' + }} + + # Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721 + - name: Fetch commits to get branch diff + if: github.event_name == 'pull_request' + run: | + set -eux + + # Fetch enough history to find a common ancestor commit (aka merge-base): + git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \ + --no-tags --prune --no-recurse-submodules + + # This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from): + COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" ) + DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" ) + + # Get all commits since that commit date from the base branch (eg: main): + git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ + --no-tags --prune --no-recurse-submodules + env: + branch_pr: 'origin/${{ github.event.pull_request.head.ref }}' + commits: ${{ github.event.pull_request.commits }} + refspec_base: '+${{ github.event.pull_request.base.sha }}:remotes/origin/${{ github.event.pull_request.base.ref }}' + refspec_pr: '+${{ github.event.pull_request.head.sha }}:remotes/origin/${{ github.event.pull_request.head.ref }}' + + # We only want to run tests on PRs when related files are changed, + # or when someone triggers a manual workflow run. + - name: Compute changed files + id: changes + run: python Tools/build/compute-changes.py + env: + GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + + - name: Compute hash for config cache key + id: config-hash + run: | + echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> "$GITHUB_OUTPUT" diff --git a/Tools/build/compute-changes.py b/Tools/build/compute-changes.py new file mode 100644 index 00000000000000..105ba58cc9d941 --- /dev/null +++ b/Tools/build/compute-changes.py @@ -0,0 +1,183 @@ +"""Determine which GitHub Actions workflows to run. + +Called by ``.github/workflows/reusable-context.yml``. +We only want to run tests on PRs when related files are changed, +or when someone triggers a manual workflow run. +This improves developer experience by not doing (slow) +unnecessary work in GHA, and saves CI resources. +""" + +from __future__ import annotations + +import os +import subprocess +from dataclasses import dataclass +from pathlib import Path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Set + +GITHUB_DEFAULT_BRANCH = os.environ["GITHUB_DEFAULT_BRANCH"] +GITHUB_CODEOWNERS_PATH = Path(".github/CODEOWNERS") +GITHUB_WORKFLOWS_PATH = Path(".github/workflows") +CONFIGURATION_FILE_NAMES = frozenset({ + ".pre-commit-config.yaml", + ".ruff.toml", + "mypy.ini", +}) +SUFFIXES_C_OR_CPP = frozenset({".c", ".h", ".cpp"}) +SUFFIXES_DOCUMENTATION = frozenset({".rst", ".md"}) + + +@dataclass(kw_only=True, slots=True) +class Outputs: + run_ci_fuzz: bool = False + run_docs: bool = False + run_tests: bool = False + run_windows_msi: bool = False + + +def compute_changes() -> None: + target_branch, head_branch = git_branches() + if target_branch and head_branch: + # Getting changed files only makes sense on a pull request + files = get_changed_files( + f"origin/{target_branch}", f"origin/{head_branch}" + ) + outputs = process_changed_files(files) + else: + # Otherwise, just run the tests + outputs = Outputs(run_tests=True) + outputs = process_target_branch(outputs, target_branch) + + if outputs.run_tests: + print("Run tests") + + if outputs.run_ci_fuzz: + print("Run CIFuzz tests") + else: + print("Branch too old for CIFuzz tests; or no C files were changed") + + if outputs.run_docs: + print("Build documentation") + + if outputs.run_windows_msi: + print("Build Windows MSI") + + print(outputs) + + write_github_output(outputs) + + +def git_branches() -> tuple[str, str]: + target_branch = os.environ.get("GITHUB_BASE_REF", "") + target_branch = target_branch.removeprefix("refs/heads/") + print(f"target branch: {target_branch!r}") + + head_branch = os.environ.get("GITHUB_HEAD_REF", "") + head_branch = head_branch.removeprefix("refs/heads/") + print(f"head branch: {head_branch!r}") + return target_branch, head_branch + + +def get_changed_files( + ref_a: str = GITHUB_DEFAULT_BRANCH, ref_b: str = "HEAD" +) -> Set[Path]: + """List the files changed between two Git refs, filtered by change type.""" + args = ("git", "diff", "--name-only", f"{ref_a}...{ref_b}", "--") + print(*args) + changed_files_result = subprocess.run( + args, stdout=subprocess.PIPE, check=True, encoding="utf-8" + ) + changed_files = changed_files_result.stdout.strip().splitlines() + return frozenset(map(Path, filter(None, map(str.strip, changed_files)))) + + +def process_changed_files(changed_files: Set[Path]) -> Outputs: + run_tests = False + run_ci_fuzz = False + run_docs = False + run_windows_msi = False + + for file in changed_files: + # Documentation files + doc_or_misc = file.parts[0] in {"Doc", "Misc"} + doc_file = file.suffix in SUFFIXES_DOCUMENTATION or doc_or_misc + + if file.parent == GITHUB_WORKFLOWS_PATH: + if file.name == "build.yml": + run_tests = run_ci_fuzz = True + if file.name == "reusable-docs.yml": + run_docs = True + if file.name == "reusable-windows-msi.yml": + run_windows_msi = True + + if not ( + doc_file + or file == GITHUB_CODEOWNERS_PATH + or file.name in CONFIGURATION_FILE_NAMES + ): + run_tests = True + + # The fuzz tests are pretty slow so they are executed only for PRs + # changing relevant files. + if file.suffix in SUFFIXES_C_OR_CPP: + run_ci_fuzz = True + if file.parts[:2] in { + ("configure",), + ("Modules", "_xxtestfuzz"), + }: + run_ci_fuzz = True + + # Check for changed documentation-related files + if doc_file: + run_docs = True + + # Check for changed MSI installer-related files + if file.parts[:2] == ("Tools", "msi"): + run_windows_msi = True + + return Outputs( + run_ci_fuzz=run_ci_fuzz, + run_docs=run_docs, + run_tests=run_tests, + run_windows_msi=run_windows_msi, + ) + + +def process_target_branch(outputs: Outputs, git_branch: str) -> Outputs: + if not git_branch: + outputs.run_tests = True + + # CIFuzz / OSS-Fuzz compatibility with older branches may be broken. + if git_branch != GITHUB_DEFAULT_BRANCH: + outputs.run_ci_fuzz = False + + if os.environ.get("GITHUB_EVENT_NAME", "").lower() == "workflow_dispatch": + outputs.run_docs = True + outputs.run_windows_msi = True + + return outputs + + +def write_github_output(outputs: Outputs) -> None: + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-an-output-parameter + if "GITHUB_OUTPUT" not in os.environ: + print("GITHUB_OUTPUT not defined!") + return + + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f: + f.write(f"run-ci-fuzz={bool_lower(outputs.run_ci_fuzz)}\n") + f.write(f"run-docs={bool_lower(outputs.run_docs)}\n") + f.write(f"run-tests={bool_lower(outputs.run_tests)}\n") + f.write(f"run-windows-msi={bool_lower(outputs.run_windows_msi)}\n") + + +def bool_lower(value: bool, /) -> str: + return "true" if value else "false" + + +if __name__ == "__main__": + compute_changes() From 5fb019fc29a90e722aff20a9522bf588351358cd Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Wed, 5 Feb 2025 11:33:17 -0800 Subject: [PATCH 078/311] gh-129559: Add `bytearray.resize()` (GH-129560) Add bytearray.resize() which wraps PyByteArray_Resize. Make negative size passed to resize exception/error rather than crash in optimized builds. --- Doc/c-api/bytearray.rst | 5 ++ Doc/library/stdtypes.rst | 32 ++++++++++++ Lib/test/test_bytes.py | 51 ++++++++++++++++--- Lib/test/test_capi/test_bytearray.py | 3 +- ...-02-01-14-55-33.gh-issue-129559.hQCeAz.rst | 2 + Objects/bytearrayobject.c | 33 +++++++++++- Objects/clinic/bytearrayobject.c.h | 41 ++++++++++++++- 7 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst diff --git a/Doc/c-api/bytearray.rst b/Doc/c-api/bytearray.rst index 9045689a6be567..15295096a710c8 100644 --- a/Doc/c-api/bytearray.rst +++ b/Doc/c-api/bytearray.rst @@ -74,6 +74,11 @@ Direct API functions .. c:function:: int PyByteArray_Resize(PyObject *bytearray, Py_ssize_t len) Resize the internal buffer of *bytearray* to *len*. + Failure is a ``-1`` return with an exception set. + + .. versionchanged:: next + A negative *len* will now result in an exception being set and -1 returned. + Macros ^^^^^^ diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 6050784264707b..4a15e27f82a160 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2841,6 +2841,38 @@ objects. optional *sep* and *bytes_per_sep* parameters to insert separators between bytes in the hex output. + .. method:: resize(size) + + Resize the :class:`bytearray` to contain *size* bytes. *size* must be + greater than or equal to 0. + + If the :class:`bytearray` needs to shrink, bytes beyond *size* are truncated. + + If the :class:`bytearray` needs to grow, all new bytes, those beyond *size*, + will be set to null bytes. + + + This is equivalent to: + + >>> def resize(ba, size): + ... if len(ba) > size: + ... del ba[size:] + ... else: + ... ba += b'\0' * (size - len(ba)) + + Examples: + + >>> shrink = bytearray(b'abc') + >>> shrink.resize(1) + >>> (shrink, len(shrink)) + (bytearray(b'a'), 1) + >>> grow = bytearray(b'abc') + >>> grow.resize(5) + >>> (grow, len(grow)) + (bytearray(b'abc\x00\x00'), 5) + + .. versionadded:: next + Since bytearray objects are sequences of integers (akin to a list), for a bytearray object *b*, ``b[0]`` will be an integer, while ``b[0:1]`` will be a bytearray object of length 1. (This contrasts with text strings, where diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 7bb1ab38aa4fdf..18d619eb6239a1 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1359,6 +1359,44 @@ def by(s): b = by("Hello, world") self.assertEqual(re.findall(br"\w+", b), [by("Hello"), by("world")]) + def test_resize(self): + ba = bytearray(b'abcdef') + self.assertIsNone(ba.resize(3)) + self.assertEqual(ba, bytearray(b'abc')) + + self.assertIsNone(ba.resize(10)) + self.assertEqual(len(ba), 10) + # Bytes beyond set values must be cleared. + self.assertEqual(ba, bytearray(b'abc\0\0\0\0\0\0\0')) + + ba[3:10] = b'defghij' + self.assertEqual(ba, bytearray(b'abcdefghij')) + + self.assertIsNone(ba.resize(2 ** 20)) + self.assertEqual(len(ba), 2**20) + self.assertEqual(ba, bytearray(b'abcdefghij' + b'\0' * (2 ** 20 - 10))) + + self.assertIsNone(ba.resize(0)) + self.assertEqual(ba, bytearray()) + + self.assertIsNone(ba.resize(10)) + self.assertEqual(ba, bytearray(b'\0' * 10)) + + # Subclass + ba = ByteArraySubclass(b'abcdef') + self.assertIsNone(ba.resize(3)) + self.assertEqual(ba, bytearray(b'abc')) + + # Check arguments + self.assertRaises(TypeError, bytearray().resize) + self.assertRaises(TypeError, bytearray().resize, (10, 10)) + + self.assertRaises(ValueError, bytearray().resize, -1) + self.assertRaises(ValueError, bytearray().resize, -200) + self.assertRaises(MemoryError, bytearray().resize, sys.maxsize) + self.assertRaises(MemoryError, bytearray(1000).resize, sys.maxsize) + + def test_setitem(self): def setitem_as_mapping(b, i, val): b[i] = val @@ -1715,17 +1753,18 @@ def test_resize_forbidden(self): # if it wouldn't reallocate the underlying buffer. # Furthermore, no destructive changes to the buffer may be applied # before raising the error. - b = bytearray(range(10)) + b = bytearray(10) v = memoryview(b) - def resize(n): + def manual_resize(n): b[1:-1] = range(n + 1, 2*n - 1) - resize(10) + b.resize(10) orig = b[:] - self.assertRaises(BufferError, resize, 11) + self.assertRaises(BufferError, b.resize, 11) + self.assertRaises(BufferError, manual_resize, 11) self.assertEqual(b, orig) - self.assertRaises(BufferError, resize, 9) + self.assertRaises(BufferError, b.resize, 9) self.assertEqual(b, orig) - self.assertRaises(BufferError, resize, 0) + self.assertRaises(BufferError, b.resize, 0) self.assertEqual(b, orig) # Other operations implying resize self.assertRaises(BufferError, b.pop, 0) diff --git a/Lib/test/test_capi/test_bytearray.py b/Lib/test/test_capi/test_bytearray.py index 39099f6b82240f..323e0d2a5acdcb 100644 --- a/Lib/test/test_capi/test_bytearray.py +++ b/Lib/test/test_capi/test_bytearray.py @@ -151,10 +151,11 @@ def test_resize(self): self.assertEqual(resize(ba, 3), 0) self.assertEqual(ba, bytearray(b'abc')) + self.assertRaises(ValueError, resize, bytearray(), -1) + self.assertRaises(ValueError, resize, bytearray(), -200) self.assertRaises(MemoryError, resize, bytearray(), PY_SSIZE_T_MAX) self.assertRaises(MemoryError, resize, bytearray(1000), PY_SSIZE_T_MAX) - # CRASHES resize(bytearray(b'abc'), -1) # CRASHES resize(b'abc', 0) # CRASHES resize(object(), 0) # CRASHES resize(NULL, 0) diff --git a/Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst b/Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst new file mode 100644 index 00000000000000..f08d47b63a84b7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-01-14-55-33.gh-issue-129559.hQCeAz.rst @@ -0,0 +1,2 @@ +Add :meth:`bytearray.resize` method so :class:`bytearray` can be efficiently +resized in place. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 21584332e0e443..6133d30f49930a 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -184,7 +184,12 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size) assert(self != NULL); assert(PyByteArray_Check(self)); assert(logical_offset <= alloc); - assert(requested_size >= 0); + + if (requested_size < 0) { + PyErr_Format(PyExc_ValueError, + "Can only resize to positive sizes, got %zd", requested_size); + return -1; + } if (requested_size == Py_SIZE(self)) { return 0; @@ -1388,6 +1393,31 @@ bytearray_removesuffix_impl(PyByteArrayObject *self, Py_buffer *suffix) } +/*[clinic input] +bytearray.resize + size: Py_ssize_t + New size to resize to.. + / +Resize the internal buffer of bytearray to len. +[clinic start generated code]*/ + +static PyObject * +bytearray_resize_impl(PyByteArrayObject *self, Py_ssize_t size) +/*[clinic end generated code: output=f73524922990b2d9 input=75fd4d17c4aa47d3]*/ +{ + Py_ssize_t start_size = PyByteArray_GET_SIZE(self); + int result = PyByteArray_Resize((PyObject *)self, size); + if (result < 0) { + return NULL; + } + // Set new bytes to null bytes + if (size > start_size) { + memset(PyByteArray_AS_STRING(self) + start_size, 0, size - start_size); + } + Py_RETURN_NONE; +} + + /*[clinic input] bytearray.translate @@ -2361,6 +2391,7 @@ static PyMethodDef bytearray_methods[] = { BYTEARRAY_REPLACE_METHODDEF BYTEARRAY_REMOVEPREFIX_METHODDEF BYTEARRAY_REMOVESUFFIX_METHODDEF + BYTEARRAY_RESIZE_METHODDEF BYTEARRAY_REVERSE_METHODDEF BYTEARRAY_RFIND_METHODDEF BYTEARRAY_RINDEX_METHODDEF diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h index 91cf5363e639d1..03b5a8a516cc09 100644 --- a/Objects/clinic/bytearrayobject.c.h +++ b/Objects/clinic/bytearrayobject.c.h @@ -565,6 +565,45 @@ bytearray_removesuffix(PyObject *self, PyObject *arg) return return_value; } +PyDoc_STRVAR(bytearray_resize__doc__, +"resize($self, size, /)\n" +"--\n" +"\n" +"Resize the internal buffer of bytearray to len.\n" +"\n" +" size\n" +" New size to resize to.."); + +#define BYTEARRAY_RESIZE_METHODDEF \ + {"resize", (PyCFunction)bytearray_resize, METH_O, bytearray_resize__doc__}, + +static PyObject * +bytearray_resize_impl(PyByteArrayObject *self, Py_ssize_t size); + +static PyObject * +bytearray_resize(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t size; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } + return_value = bytearray_resize_impl((PyByteArrayObject *)self, size); + +exit: + return return_value; +} + PyDoc_STRVAR(bytearray_translate__doc__, "translate($self, table, /, delete=b\'\')\n" "--\n" @@ -1623,4 +1662,4 @@ bytearray_sizeof(PyObject *self, PyObject *Py_UNUSED(ignored)) { return bytearray_sizeof_impl((PyByteArrayObject *)self); } -/*[clinic end generated code: output=bc8bec8514102bf3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=41bb67a8a181e733 input=a9049054013a1b77]*/ From cdcacec79f7a216c3c988baa4dc31ce4e76c97ac Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 5 Feb 2025 11:38:30 -0800 Subject: [PATCH 079/311] gh-129201: Use prefetch in GC mark alive phase. (gh-129203) For the free-threaded version of the cyclic GC, restructure the "mark alive" phase to use software prefetch instructions. This gives a speedup in most cases when the number of objects is large enough. The prefetching is enabled conditionally based on the number of long-lived objects the GC finds. --- ...-01-22-14-22-34.gh-issue-129201.wiZzEb.rst | 5 + Python/gc_free_threading.c | 472 ++++++++++++++++-- 2 files changed, 435 insertions(+), 42 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst new file mode 100644 index 00000000000000..26737330716181 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-22-14-22-34.gh-issue-129201.wiZzEb.rst @@ -0,0 +1,5 @@ +The free-threaded version of the cyclic garbage collector has been optimized to +conditionally use CPU prefetch instructions during the collection. This can +reduce collection times by making it more likely that data is in the CPU cache +when it is needed. The prefetch instructions are enabled if the number of +long-lived objects (objects surviving a full collection) exceeds a threshold. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 5d264e407e1cc8..10c76a67979884 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -21,6 +21,9 @@ // enable the "mark alive" pass of GC #define GC_ENABLE_MARK_ALIVE 1 +// if true, enable the use of "prefetch" CPU instructions +#define GC_ENABLE_PREFETCH_INSTRUCTIONS 1 + // include additional roots in "mark alive" pass #define GC_MARK_ALIVE_EXTRA_ROOTS 1 @@ -472,13 +475,193 @@ gc_maybe_untrack(PyObject *op) } #ifdef GC_ENABLE_MARK_ALIVE + +// prefetch buffer and stack ////////////////////////////////// + +// The buffer is a circular FIFO queue of PyObject pointers. We take +// care to not dereference these pointers until they are taken out of +// the buffer. A prefetch CPU instruction is issued when a pointer is +// put into the buffer. If all is working as expected, there will be +// enough time between the enqueue and dequeue so that the needed memory +// for the object, most importantly ob_gc_bits and ob_type words, will +// already be in the CPU cache. +#define BUFFER_SIZE 256 +#define BUFFER_HI 16 +#define BUFFER_LO 8 +#define BUFFER_MASK (BUFFER_SIZE - 1) + +// the buffer size must be an exact power of two +static_assert(BUFFER_SIZE > 0 && !(BUFFER_SIZE & BUFFER_MASK), + "Invalid BUFFER_SIZE, must be power of 2"); +// the code below assumes these relationships are true +static_assert(BUFFER_HI < BUFFER_SIZE && + BUFFER_LO < BUFFER_HI && + BUFFER_LO > 0, + "Invalid prefetch buffer level settings."); + +// Prefetch intructions will fetch the line of data from memory that +// contains the byte specified with the source operand to a location in +// the cache hierarchy specified by a locality hint. The instruction +// is only a hint and the CPU is free to ignore it. Instructions and +// behaviour are CPU specific but the definitions of locality hints +// below are mostly consistent. +// +// * T0 (temporal data) prefetch data into all levels of the cache hierarchy. +// +// * T1 (temporal data with respect to first level cache) prefetch data into +// level 2 cache and higher. +// +// * T2 (temporal data with respect to second level cache) prefetch data into +// level 3 cache and higher, or an implementation-specific choice. +// +// * NTA (non-temporal data with respect to all cache levels) prefetch data into +// non-temporal cache structure and into a location close to the processor, +// minimizing cache pollution. + +#if defined(__GNUC__) || defined(__clang__) + #define PREFETCH_T0(ptr) __builtin_prefetch(ptr, 0, 3) + #define PREFETCH_T1(ptr) __builtin_prefetch(ptr, 0, 2) + #define PREFETCH_T2(ptr) __builtin_prefetch(ptr, 0, 1) + #define PREFETCH_NTA(ptr) __builtin_prefetch(ptr, 0, 0) +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) && !defined(_M_ARM64EC) + #include + #define PREFETCH_T0(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) + #define PREFETCH_T1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1) + #define PREFETCH_T2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T2) + #define PREFETCH_NTA(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_NTA) +#elif defined (__aarch64__) + #define PREFETCH_T0(ptr) \ + do { __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr))); } while (0) + #define PREFETCH_T1(ptr) \ + do { __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))); } while (0) + #define PREFETCH_T2(ptr) \ + do { __asm__ __volatile__("prfm pldl3keep, %0" ::"Q"(*(ptr))); } while (0) + #define PREFETCH_NTA(ptr) \ + do { __asm__ __volatile__("prfm pldl1strm, %0" ::"Q"(*(ptr))); } while (0) +#else + #define PREFETCH_T0(ptr) do { (void)(ptr); } while (0) /* disabled */ + #define PREFETCH_T1(ptr) do { (void)(ptr); } while (0) /* disabled */ + #define PREFETCH_T2(ptr) do { (void)(ptr); } while (0) /* disabled */ + #define PREFETCH_NTA(ptr) do { (void)(ptr); } while (0) /* disabled */ +#endif + +#ifdef GC_ENABLE_PREFETCH_INSTRUCTIONS + #define prefetch(ptr) PREFETCH_T1(ptr) +#else + #define prefetch(ptr) +#endif + +// a contigous sequence of PyObject pointers, can contain NULLs +typedef struct { + PyObject **start; + PyObject **end; +} gc_span_t; + +typedef struct { + Py_ssize_t size; + Py_ssize_t capacity; + gc_span_t *stack; +} gc_span_stack_t; + +typedef struct { + unsigned int in; + unsigned int out; + _PyObjectStack stack; + gc_span_stack_t spans; + PyObject *buffer[BUFFER_SIZE]; + bool use_prefetch; +} gc_mark_args_t; + + +// Returns number of entries in buffer +static inline unsigned int +gc_mark_buffer_len(gc_mark_args_t *args) +{ + return args->in - args->out; +} + +// Returns number of free entry slots in buffer +static inline unsigned int +gc_mark_buffer_avail(gc_mark_args_t *args) +{ + return BUFFER_SIZE - gc_mark_buffer_len(args); +} + +static inline bool +gc_mark_buffer_is_empty(gc_mark_args_t *args) +{ + return args->in == args->out; +} + +static inline bool +gc_mark_buffer_is_full(gc_mark_args_t *args) +{ + return gc_mark_buffer_len(args) == BUFFER_SIZE; +} + +static inline PyObject * +gc_mark_buffer_pop(gc_mark_args_t *args) +{ + assert(!gc_mark_buffer_is_empty(args)); + PyObject *op = args->buffer[args->out & BUFFER_MASK]; + args->out++; + return op; +} + +// Called when there is space in the buffer for the object. Issue the +// prefetch instruction and add it to the end of the buffer. +static inline void +gc_mark_buffer_push(PyObject *op, gc_mark_args_t *args) +{ + assert(!gc_mark_buffer_is_full(args)); + prefetch(op); + args->buffer[args->in & BUFFER_MASK] = op; + args->in++; +} + +// Called when we run out of space in the buffer or if the prefetching +// is disabled. The object will be pushed on the gc_mark_args.stack. +static int +gc_mark_stack_push(_PyObjectStack *ms, PyObject *op) +{ + if (_PyObjectStack_Push(ms, op) < 0) { + return -1; + } + return 0; +} + static int -mark_alive_stack_push(PyObject *op, _PyObjectStack *stack) +gc_mark_span_push(gc_span_stack_t *ss, PyObject **start, PyObject **end) +{ + if (start == end) { + return 0; + } + if (ss->size >= ss->capacity) { + if (ss->capacity == 0) { + ss->capacity = 256; + } + else { + ss->capacity *= 2; + } + ss->stack = (gc_span_t *)PyMem_Realloc(ss->stack, ss->capacity * sizeof(gc_span_t)); + if (ss->stack == NULL) { + return -1; + } + } + assert(end > start); + ss->stack[ss->size].start = start; + ss->stack[ss->size].end = end; + ss->size++; + return 0; +} + +static int +gc_mark_enqueue_no_buffer(PyObject *op, gc_mark_args_t *args) { if (op == NULL) { return 0; } - if (!_PyObject_GC_IS_TRACKED(op)) { + if (!gc_has_bit(op, _PyGC_BITS_TRACKED)) { return 0; } if (gc_is_alive(op)) { @@ -491,12 +674,68 @@ mark_alive_stack_push(PyObject *op, _PyObjectStack *stack) // Need to call tp_traverse on this object. Add to stack and mark it // alive so we don't traverse it a second time. gc_set_alive(op); - if (_PyObjectStack_Push(stack, op) < 0) { + if (_PyObjectStack_Push(&args->stack, op) < 0) { return -1; } return 0; } +static int +gc_mark_enqueue_buffer(PyObject *op, gc_mark_args_t *args) +{ + assert(op != NULL); + if (!gc_mark_buffer_is_full(args)) { + gc_mark_buffer_push(op, args); + return 0; + } + else { + return gc_mark_stack_push(&args->stack, op); + } +} + +// Called when we find an object that needs to be marked alive (either from a +// root or from calling tp_traverse). +static int +gc_mark_enqueue(PyObject *op, gc_mark_args_t *args) +{ + if (args->use_prefetch) { + return gc_mark_enqueue_buffer(op, args); + } + else { + return gc_mark_enqueue_no_buffer(op, args); + } +} + +// Called when we have a contigous sequence of PyObject pointers, either +// a tuple or list object. This will add the items to the buffer if there +// is space for them all otherwise push a new "span" on the span stack. Using +// spans has the advantage of not creating a deep _PyObjectStack stack when +// dealing with long sequences. Those sequences will be processed in smaller +// chunks by the gc_prime_from_spans() function. +static int +gc_mark_enqueue_span(PyObject **item, Py_ssize_t size, gc_mark_args_t *args) +{ + Py_ssize_t used = gc_mark_buffer_len(args); + Py_ssize_t free = BUFFER_SIZE - used; + if (free >= size) { + for (Py_ssize_t i = 0; i < size; i++) { + PyObject *op = item[i]; + if (op == NULL) { + continue; + } + gc_mark_buffer_push(op, args); + } + } + else { + assert(size > 0); + PyObject **end = &item[size]; + if (gc_mark_span_push(&args->spans, item, end) < 0) { + return -1; + } + } + return 0; +} + static bool gc_clear_alive_bits(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) @@ -511,25 +750,56 @@ gc_clear_alive_bits(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } +static int +gc_mark_traverse_list(PyObject *self, void *args) +{ + PyListObject *list = (PyListObject *)self; + if (list->ob_item == NULL) { + return 0; + } + if (gc_mark_enqueue_span(list->ob_item, PyList_GET_SIZE(list), args) < 0) { + return -1; + } + return 0; +} + +static int +gc_mark_traverse_tuple(PyObject *self, void *args) +{ + _PyTuple_MaybeUntrack(self); + if (!gc_has_bit(self, _PyGC_BITS_TRACKED)) { + gc_clear_alive(self); + return 0; + } + PyTupleObject *tuple = _PyTuple_CAST(self); + if (gc_mark_enqueue_span(tuple->ob_item, Py_SIZE(tuple), args) < 0) { + return -1; + } + return 0; +} + static void gc_abort_mark_alive(PyInterpreterState *interp, struct collection_state *state, - _PyObjectStack *stack) + gc_mark_args_t *args) { - // We failed to allocate memory for "stack" while doing the "mark - // alive" phase. In that case, free the object stack and make sure - // that no objects have the alive bit set. - _PyObjectStack_Clear(stack); + // We failed to allocate memory while doing the "mark alive" phase. + // In that case, free the memory used for marking state and make + // sure that no objects have the alive bit set. + _PyObjectStack_Clear(&args->stack); + if (args->spans.stack != NULL) { + PyMem_Free(args->spans.stack); + } gc_visit_heaps(interp, &gc_clear_alive_bits, &state->base); } #ifdef GC_MARK_ALIVE_STACKS static int -gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref) +gc_visit_stackref_mark_alive(gc_mark_args_t *args, _PyStackRef stackref) { if (!PyStackRef_IsNull(stackref)) { PyObject *op = PyStackRef_AsPyObjectBorrow(stackref); - if (mark_alive_stack_push(op, stack) < 0) { + if (gc_mark_enqueue(op, args) < 0) { return -1; } } @@ -537,7 +807,7 @@ gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref) } static int -gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, _PyObjectStack *stack) +gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *args) { int err = 0; _Py_FOR_EACH_TSTATE_BEGIN(interp, p) { @@ -554,13 +824,13 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, _PyObjectStack *st } _PyStackRef *top = f->stackpointer; - if (gc_visit_stackref_mark_alive(stack, f->f_executable) < 0) { + if (gc_visit_stackref_mark_alive(args, f->f_executable) < 0) { err = -1; goto exit; } while (top != f->localsplus) { --top; - if (gc_visit_stackref_mark_alive(stack, *top) < 0) { + if (gc_visit_stackref_mark_alive(args, *top) < 0) { err = -1; goto exit; } @@ -904,22 +1174,124 @@ static int move_legacy_finalizer_reachable(struct collection_state *state); #ifdef GC_ENABLE_MARK_ALIVE + +static void +gc_prime_from_spans(gc_mark_args_t *args) +{ + Py_ssize_t space = BUFFER_HI - gc_mark_buffer_len(args); + // there should always be at least this amount of space + assert(space <= gc_mark_buffer_avail(args)); + assert(space > 0); + gc_span_t entry = args->spans.stack[--args->spans.size]; + // spans on the stack should always have one or more elements + assert(entry.start < entry.end); + do { + PyObject *op = *entry.start; + entry.start++; + if (op != NULL) { + gc_mark_buffer_push(op, args); + space--; + if (space == 0) { + // buffer is as full as we want and not done with span + gc_mark_span_push(&args->spans, entry.start, entry.end); + return; + } + } + } while (entry.start < entry.end); +} + +static void +gc_prime_buffer(gc_mark_args_t *args) +{ + if (args->spans.size > 0) { + gc_prime_from_spans(args); + } + else { + // When priming, don't fill the buffer too full since that would + // likely cause the stack to be used shortly after when it + // fills. We want to use the buffer as much as possible and so + // we only fill to BUFFER_HI, not BUFFER_SIZE. + Py_ssize_t space = BUFFER_HI - gc_mark_buffer_len(args); + assert(space > 0); + do { + PyObject *op = _PyObjectStack_Pop(&args->stack); + if (op == NULL) { + return; + } + gc_mark_buffer_push(op, args); + space--; + } while (space > 0); + } +} + static int -propagate_alive_bits(_PyObjectStack *stack) +gc_propagate_alive_prefetch(gc_mark_args_t *args) { for (;;) { - PyObject *op = _PyObjectStack_Pop(stack); - if (op == NULL) { - break; + Py_ssize_t buf_used = gc_mark_buffer_len(args); + if (buf_used <= BUFFER_LO) { + // The mark buffer is getting empty. If it's too empty + // then there will not be enough delay between issuing + // the prefetch and when the object is actually accessed. + // Prime the buffer with object pointers from the stack or + // from the spans, if there are any available. + gc_prime_buffer(args); + if (gc_mark_buffer_is_empty(args)) { + return 0; + } + } + PyObject *op = gc_mark_buffer_pop(args); + + if (!gc_has_bit(op, _PyGC_BITS_TRACKED)) { + continue; } - assert(_PyObject_GC_IS_TRACKED(op)); - assert(gc_is_alive(op)); + + if (gc_is_alive(op)) { + continue; // already visited this object + } + + // Need to call tp_traverse on this object. Mark it alive so we + // don't traverse it a second time. + gc_set_alive(op); + traverseproc traverse = Py_TYPE(op)->tp_traverse; - if (traverse(op, (visitproc)&mark_alive_stack_push, stack) < 0) { + if (traverse == PyList_Type.tp_traverse) { + if (gc_mark_traverse_list(op, args) < 0) { + return -1; + } + } + else if (traverse == PyTuple_Type.tp_traverse) { + if (gc_mark_traverse_tuple(op, args) < 0) { + return -1; + } + } + else if (traverse(op, (visitproc)&gc_mark_enqueue_buffer, args) < 0) { return -1; } } - return 0; +} + +static int +gc_propagate_alive(gc_mark_args_t *args) +{ + if (args->use_prefetch) { + return gc_propagate_alive_prefetch(args); + } + else { + for (;;) { + PyObject *op = _PyObjectStack_Pop(&args->stack); + if (op == NULL) { + break; + } + assert(_PyObject_GC_IS_TRACKED(op)); + assert(gc_is_alive(op)); + traverseproc traverse = Py_TYPE(op)->tp_traverse; + if (traverse(op, (visitproc)&gc_mark_enqueue_no_buffer, args) < 0) { + return -1; + } + } + return 0; + } } // Using tp_traverse, mark everything reachable from known root objects @@ -939,48 +1311,64 @@ propagate_alive_bits(_PyObjectStack *stack) // // Returns -1 on failure (out of memory). static int -mark_alive_from_roots(PyInterpreterState *interp, - struct collection_state *state) +gc_mark_alive_from_roots(PyInterpreterState *interp, + struct collection_state *state) { #ifdef GC_DEBUG // Check that all objects don't have alive bit set gc_visit_heaps(interp, &validate_alive_bits, &state->base); #endif - _PyObjectStack stack = { NULL }; - - #define STACK_PUSH(op) \ - if (mark_alive_stack_push(op, &stack) < 0) { \ - gc_abort_mark_alive(interp, state, &stack); \ - return -1; \ + gc_mark_args_t mark_args = { 0 }; + + // Using prefetch instructions is only a win if the set of objects being + // examined by the GC does not fit into CPU caches. Otherwise, using the + // buffer and prefetch instructions is just overhead. Using the long lived + // object count seems a good estimate of if things will fit in the cache. + // On 64-bit platforms, the minimum object size is 32 bytes. A 4MB L2 cache + // would hold about 130k objects. + mark_args.use_prefetch = interp->gc.long_lived_total > 200000; + + #define MARK_ENQUEUE(op) \ + if (op != NULL ) { \ + if (gc_mark_enqueue(op, &mark_args) < 0) { \ + gc_abort_mark_alive(interp, state, &mark_args); \ + return -1; \ + } \ } - STACK_PUSH(interp->sysdict); + MARK_ENQUEUE(interp->sysdict); #ifdef GC_MARK_ALIVE_EXTRA_ROOTS - STACK_PUSH(interp->builtins); - STACK_PUSH(interp->dict); + MARK_ENQUEUE(interp->builtins); + MARK_ENQUEUE(interp->dict); struct types_state *types = &interp->types; for (int i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) { - STACK_PUSH(types->builtins.initialized[i].tp_dict); - STACK_PUSH(types->builtins.initialized[i].tp_subclasses); + MARK_ENQUEUE(types->builtins.initialized[i].tp_dict); + MARK_ENQUEUE(types->builtins.initialized[i].tp_subclasses); } for (int i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) { - STACK_PUSH(types->for_extensions.initialized[i].tp_dict); - STACK_PUSH(types->for_extensions.initialized[i].tp_subclasses); + MARK_ENQUEUE(types->for_extensions.initialized[i].tp_dict); + MARK_ENQUEUE(types->for_extensions.initialized[i].tp_subclasses); } #endif #ifdef GC_MARK_ALIVE_STACKS - if (gc_visit_thread_stacks_mark_alive(interp, &stack) < 0) { - gc_abort_mark_alive(interp, state, &stack); + if (gc_visit_thread_stacks_mark_alive(interp, &mark_args) < 0) { + gc_abort_mark_alive(interp, state, &mark_args); return -1; } #endif - #undef STACK_PUSH + #undef MARK_ENQUEUE // Use tp_traverse to find everything reachable from roots. - if (propagate_alive_bits(&stack) < 0) { - gc_abort_mark_alive(interp, state, &stack); + if (gc_propagate_alive(&mark_args) < 0) { + gc_abort_mark_alive(interp, state, &mark_args); return -1; } + assert(mark_args.spans.size == 0); + if (mark_args.spans.stack != NULL) { + PyMem_Free(mark_args.spans.stack); + } + assert(mark_args.stack.head == NULL); + return 0; } #endif // GC_ENABLE_MARK_ALIVE @@ -1559,7 +1947,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, if (!state->gcstate->freeze_active) { // Mark objects reachable from known roots as "alive". These will // be ignored for rest of the GC pass. - int err = mark_alive_from_roots(interp, state); + int err = gc_mark_alive_from_roots(interp, state); if (err < 0) { _PyEval_StartTheWorld(interp); PyErr_NoMemory(); From 76e018294801ab95f30756c702b63bf6b4c23310 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 6 Feb 2025 06:44:50 +0000 Subject: [PATCH 080/311] gh-69001: Replace maintainer email in IDLE credits (#129588) Instead, anyone requesting credit should submit a PR with contribution summary. (Also fix typo in existing name.) --- Lib/idlelib/CREDITS.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt index 4a42af586a4a9e..bea3ba7c20de22 100644 --- a/Lib/idlelib/CREDITS.txt +++ b/Lib/idlelib/CREDITS.txt @@ -33,15 +33,15 @@ Major contributors since 2005: - 2005: Tal Einat - 2010: Terry Jan Reedy (current maintainer) -- 2013: Roger Serwys +- 2013: Roger Serwy - 2014: Saimadhav Heblikar - 2015: Mark Roseman - 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka For additional details refer to NEWS.txt and Changelog. -Please contact the IDLE maintainer (kbk@shore.net) to have yourself included -here if you are one of those we missed! +If we missed you, feel free to submit a PR with a summary of +contributions (for instance, at least 5 merged PRs). From d83a8a26f5e321b26bec59f5fd47c9c46c16ab12 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 6 Feb 2025 07:55:25 +0000 Subject: [PATCH 081/311] gh-86206: Change IDLE splash line (#129698) Change splash line Co-authored-by: Terry Jan Reedy --- Lib/idlelib/pyshell.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 66fbbd4a97b7af..295d06e4a5f017 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1133,8 +1133,7 @@ def ispythonsource(self, filename): def short_title(self): return self.shell_title - COPYRIGHT = \ - 'Type "help", "copyright", "credits" or "license()" for more information.' + SPLASHLINE = 'Enter "help" below or click "Help" above for more information.' def begin(self): self.text.mark_set("iomark", "insert") @@ -1153,7 +1152,7 @@ def begin(self): sys.displayhook = rpc.displayhook self.write("Python %s on %s\n%s\n%s" % - (sys.version, sys.platform, self.COPYRIGHT, nosub)) + (sys.version, sys.platform, self.SPLASHLINE, nosub)) self.text.focus_force() self.showprompt() # User code should use separate default Tk root window From 052ca8ffe8c57afb9c270fcc4eb5f390cbcfb8ce Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Thu, 6 Feb 2025 02:18:08 -0800 Subject: [PATCH 082/311] gh-129005: Update _pyio.BytesIO to use bytearray.resize on write (#129702) Co-authored-by: Victor Stinner --- Lib/_pyio.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 023478aa78c6a0..b3a8f37d68acdb 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -937,10 +937,8 @@ def write(self, b): return 0 pos = self._pos if pos > len(self._buffer): - # Inserts null bytes between the current end of the file - # and the new write position. - padding = b'\x00' * (pos - len(self._buffer)) - self._buffer += padding + # Pad buffer to pos with null bytes. + self._buffer.resize(pos) self._buffer[pos:pos + n] = b self._pos += n return n From a64e0a553b7a3a7d6270414546400e6dd26dd95c Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:40:43 +0000 Subject: [PATCH 083/311] _markupbase.py: Use a permalink for the analysis of MS-Word extensions (GH-129017) Update _markupbase.py --- Lib/_markupbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_markupbase.py b/Lib/_markupbase.py index 3ad7e279960f7e..614f0cd16ddb44 100644 --- a/Lib/_markupbase.py +++ b/Lib/_markupbase.py @@ -13,7 +13,7 @@ _markedsectionclose = re.compile(r']\s*]\s*>') # An analysis of the MS-Word extensions is available at -# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf +# http://web.archive.org/web/20060321153828/http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf _msmarkedsectionclose = re.compile(r']\s*>') From 779d06945cbb59ec187a8c39300fb0ab6f9c0c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 6 Feb 2025 13:34:06 +0100 Subject: [PATCH 084/311] gh-118915: Fix bad link in documentation (GH-129691) --- Doc/c-api/init.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 8e3be97dfeefd1..f90af6a9ce7c26 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1501,7 +1501,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. c:function:: PyObject* PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *interp) - Return a :term:`strong reference` to the ``__main__`` `module object `_ + Return a :term:`strong reference` to the ``__main__`` :ref:`module object ` for the given interpreter. The caller must hold the GIL. From 63f0406d5ad25a55e49c3903b29407c50a17cfd5 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Feb 2025 15:54:40 +0300 Subject: [PATCH 085/311] gh-129643: Fix `PyList_Insert` in free-threading builds (#129680) --- .../2025-02-05-11-29-52.gh-issue-129643.4mGzvg.rst | 1 + Objects/listobject.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-11-29-52.gh-issue-129643.4mGzvg.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-11-29-52.gh-issue-129643.4mGzvg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-11-29-52.gh-issue-129643.4mGzvg.rst new file mode 100644 index 00000000000000..420e1fb9781ff3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-11-29-52.gh-issue-129643.4mGzvg.rst @@ -0,0 +1 @@ +Fix thread safety of :c:func:`PyList_Insert` in free-threading builds. diff --git a/Objects/listobject.c b/Objects/listobject.c index 86fa2149556463..120e353b709e7b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -466,8 +466,8 @@ ins1(PyListObject *self, Py_ssize_t where, PyObject *v) where = n; items = self->ob_item; for (i = n; --i >= where; ) - items[i+1] = items[i]; - items[where] = Py_NewRef(v); + FT_ATOMIC_STORE_PTR_RELAXED(items[i+1], items[i]); + FT_ATOMIC_STORE_PTR_RELEASE(items[where], Py_NewRef(v)); return 0; } From 55f17b77c305be877ac856d6426b13591cbc7fc8 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang <44627253+xuantengh@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:40:50 +0800 Subject: [PATCH 086/311] gh-128714: Fix function object races in `__annotate__`, `__annotations__` and `__type_params__` in free-threading build (#129016) --- .../test_func_annotations.py | 67 +++++++ ...-01-19-09-07-44.gh-issue-128714.m1fyCB.rst | 1 + Objects/clinic/funcobject.c.h | 174 +++++++++++++++++- Objects/funcobject.c | 131 ++++++++----- 4 files changed, 328 insertions(+), 45 deletions(-) create mode 100644 Lib/test/test_free_threading/test_func_annotations.py create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-19-09-07-44.gh-issue-128714.m1fyCB.rst diff --git a/Lib/test/test_free_threading/test_func_annotations.py b/Lib/test/test_free_threading/test_func_annotations.py new file mode 100644 index 00000000000000..1a6461953d4aec --- /dev/null +++ b/Lib/test/test_free_threading/test_func_annotations.py @@ -0,0 +1,67 @@ +import concurrent.futures +import unittest +import inspect +from threading import Thread, Barrier +from unittest import TestCase + +from test.support import threading_helper, Py_GIL_DISABLED + +threading_helper.requires_working_threading(module=True) + + +def get_func_annotation(f, b): + b.wait() + return inspect.get_annotations(f) + + +def get_func_annotation_dunder(f, b): + b.wait() + return f.__annotations__ + + +def set_func_annotation(f, b): + b.wait() + f.__annotations__ = {'x': int, 'y': int, 'return': int} + return f.__annotations__ + + +@unittest.skipUnless(Py_GIL_DISABLED, "Enable only in FT build") +class TestFTFuncAnnotations(TestCase): + NUM_THREADS = 8 + + def test_concurrent_read(self): + def f(x: int) -> int: + return x + 1 + + for _ in range(100): + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(get_func_annotation, f, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'return': int}) + + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(get_func_annotation_dunder, f, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'return': int}) + + def test_concurrent_write(self): + def bar(x: int, y: float) -> float: + return y ** x + + for _ in range(100): + with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: + b = Barrier(self.NUM_THREADS) + futures = {executor.submit(set_func_annotation, bar, b): i for i in range(self.NUM_THREADS)} + for fut in concurrent.futures.as_completed(futures): + annotate = fut.result() + self.assertIsNotNone(annotate) + self.assertEqual(annotate, {'x': int, 'y': int, 'return': int}) + + # func_get_annotations returns in-place dict, so bar.__annotations__ should be modified as well + self.assertEqual(bar.__annotations__, {'x': int, 'y': int, 'return': int}) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-19-09-07-44.gh-issue-128714.m1fyCB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-19-09-07-44.gh-issue-128714.m1fyCB.rst new file mode 100644 index 00000000000000..431032241e9157 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-19-09-07-44.gh-issue-128714.m1fyCB.rst @@ -0,0 +1 @@ +Fix the potential races in get/set dunder methods ``__annotations__``, ``__annotate__`` and ``__type_params__`` for function object, and add related tests. diff --git a/Objects/clinic/funcobject.c.h b/Objects/clinic/funcobject.c.h index 3f95a1db7b2788..efc579dc2597b7 100644 --- a/Objects/clinic/funcobject.c.h +++ b/Objects/clinic/funcobject.c.h @@ -6,8 +6,180 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +PyDoc_STRVAR(function___annotate____doc__, +"Get the code object for a function."); +#if defined(function___annotate___DOCSTR) +# undef function___annotate___DOCSTR +#endif +#define function___annotate___DOCSTR function___annotate____doc__ + +#if !defined(function___annotate___DOCSTR) +# define function___annotate___DOCSTR NULL +#endif +#if defined(FUNCTION___ANNOTATE___GETSETDEF) +# undef FUNCTION___ANNOTATE___GETSETDEF +# define FUNCTION___ANNOTATE___GETSETDEF {"__annotate__", (getter)function___annotate___get, (setter)function___annotate___set, function___annotate___DOCSTR}, +#else +# define FUNCTION___ANNOTATE___GETSETDEF {"__annotate__", (getter)function___annotate___get, NULL, function___annotate___DOCSTR}, +#endif + +static PyObject * +function___annotate___get_impl(PyFunctionObject *self); + +static PyObject * +function___annotate___get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___annotate___get_impl((PyFunctionObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(function___annotate___DOCSTR) +# define function___annotate___DOCSTR NULL +#endif +#if defined(FUNCTION___ANNOTATE___GETSETDEF) +# undef FUNCTION___ANNOTATE___GETSETDEF +# define FUNCTION___ANNOTATE___GETSETDEF {"__annotate__", (getter)function___annotate___get, (setter)function___annotate___set, function___annotate___DOCSTR}, +#else +# define FUNCTION___ANNOTATE___GETSETDEF {"__annotate__", NULL, (setter)function___annotate___set, NULL}, +#endif + +static int +function___annotate___set_impl(PyFunctionObject *self, PyObject *value); + +static int +function___annotate___set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___annotate___set_impl((PyFunctionObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(function___annotations____doc__, +"Dict of annotations in a function object."); +#if defined(function___annotations___DOCSTR) +# undef function___annotations___DOCSTR +#endif +#define function___annotations___DOCSTR function___annotations____doc__ + +#if !defined(function___annotations___DOCSTR) +# define function___annotations___DOCSTR NULL +#endif +#if defined(FUNCTION___ANNOTATIONS___GETSETDEF) +# undef FUNCTION___ANNOTATIONS___GETSETDEF +# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, (setter)function___annotations___set, function___annotations___DOCSTR}, +#else +# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, NULL, function___annotations___DOCSTR}, +#endif + +static PyObject * +function___annotations___get_impl(PyFunctionObject *self); + +static PyObject * +function___annotations___get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___annotations___get_impl((PyFunctionObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(function___annotations___DOCSTR) +# define function___annotations___DOCSTR NULL +#endif +#if defined(FUNCTION___ANNOTATIONS___GETSETDEF) +# undef FUNCTION___ANNOTATIONS___GETSETDEF +# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, (setter)function___annotations___set, function___annotations___DOCSTR}, +#else +# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", NULL, (setter)function___annotations___set, NULL}, +#endif + +static int +function___annotations___set_impl(PyFunctionObject *self, PyObject *value); + +static int +function___annotations___set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___annotations___set_impl((PyFunctionObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(function___type_params____doc__, +"Get the declared type parameters for a function."); +#if defined(function___type_params___DOCSTR) +# undef function___type_params___DOCSTR +#endif +#define function___type_params___DOCSTR function___type_params____doc__ + +#if !defined(function___type_params___DOCSTR) +# define function___type_params___DOCSTR NULL +#endif +#if defined(FUNCTION___TYPE_PARAMS___GETSETDEF) +# undef FUNCTION___TYPE_PARAMS___GETSETDEF +# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, (setter)function___type_params___set, function___type_params___DOCSTR}, +#else +# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, NULL, function___type_params___DOCSTR}, +#endif + +static PyObject * +function___type_params___get_impl(PyFunctionObject *self); + +static PyObject * +function___type_params___get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___type_params___get_impl((PyFunctionObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(function___type_params___DOCSTR) +# define function___type_params___DOCSTR NULL +#endif +#if defined(FUNCTION___TYPE_PARAMS___GETSETDEF) +# undef FUNCTION___TYPE_PARAMS___GETSETDEF +# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, (setter)function___type_params___set, function___type_params___DOCSTR}, +#else +# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", NULL, (setter)function___type_params___set, NULL}, +#endif + +static int +function___type_params___set_impl(PyFunctionObject *self, PyObject *value); + +static int +function___type_params___set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = function___type_params___set_impl((PyFunctionObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(func_new__doc__, "function(code, globals, name=None, argdefs=None, closure=None,\n" " kwdefaults=None)\n" @@ -116,4 +288,4 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=bad4e19757dd26c3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3cdce22867efe617 input=a9049054013a1b77]*/ diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 7b17a9ba31fac4..169db2048c6a74 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -2,11 +2,11 @@ /* Function object implementation */ #include "Python.h" -#include "pycore_dict.h" // _Py_INCREF_DICT() -#include "pycore_long.h" // _PyLong_GetOne() -#include "pycore_modsupport.h" // _PyArg_NoKeywords() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() -#include "pycore_pyerrors.h" // _PyErr_Occurred() +#include "pycore_dict.h" // _Py_INCREF_DICT() +#include "pycore_long.h" // _PyLong_GetOne() +#include "pycore_modsupport.h" // _PyArg_NoKeywords() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_Occurred() static const char * @@ -635,6 +635,13 @@ static PyMemberDef func_memberlist[] = { {NULL} /* Sentinel */ }; +/*[clinic input] +class function "PyFunctionObject *" "&PyFunction_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=70af9c90aa2e71b0]*/ + +#include "clinic/funcobject.c.h" + static PyObject * func_get_code(PyObject *self, void *Py_UNUSED(ignored)) { @@ -824,32 +831,46 @@ func_set_kwdefaults(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) return 0; } +/*[clinic input] +@critical_section +@getter +function.__annotate__ + +Get the code object for a function. +[clinic start generated code]*/ + static PyObject * -func_get_annotate(PyObject *self, void *Py_UNUSED(ignored)) +function___annotate___get_impl(PyFunctionObject *self) +/*[clinic end generated code: output=5ec7219ff2bda9e6 input=7f3db11e3c3329f3]*/ { - PyFunctionObject *op = _PyFunction_CAST(self); - if (op->func_annotate == NULL) { + if (self->func_annotate == NULL) { Py_RETURN_NONE; } - return Py_NewRef(op->func_annotate); + return Py_NewRef(self->func_annotate); } +/*[clinic input] +@critical_section +@setter +function.__annotate__ +[clinic start generated code]*/ + static int -func_set_annotate(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) +function___annotate___set_impl(PyFunctionObject *self, PyObject *value) +/*[clinic end generated code: output=05b7dfc07ada66cd input=eb6225e358d97448]*/ { - PyFunctionObject *op = _PyFunction_CAST(self); if (value == NULL) { PyErr_SetString(PyExc_TypeError, "__annotate__ cannot be deleted"); return -1; } if (Py_IsNone(value)) { - Py_XSETREF(op->func_annotate, value); + Py_XSETREF(self->func_annotate, value); return 0; } else if (PyCallable_Check(value)) { - Py_XSETREF(op->func_annotate, Py_XNewRef(value)); - Py_CLEAR(op->func_annotations); + Py_XSETREF(self->func_annotate, Py_XNewRef(value)); + Py_CLEAR(self->func_annotations); return 0; } else { @@ -859,24 +880,39 @@ func_set_annotate(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@critical_section +@getter +function.__annotations__ + +Dict of annotations in a function object. +[clinic start generated code]*/ + static PyObject * -func_get_annotations(PyObject *self, void *Py_UNUSED(ignored)) -{ - PyFunctionObject *op = _PyFunction_CAST(self); - if (op->func_annotations == NULL && - (op->func_annotate == NULL || !PyCallable_Check(op->func_annotate))) { - op->func_annotations = PyDict_New(); - if (op->func_annotations == NULL) +function___annotations___get_impl(PyFunctionObject *self) +/*[clinic end generated code: output=a4cf4c884c934cbb input=92643d7186c1ad0c]*/ +{ + PyObject *d = NULL; + if (self->func_annotations == NULL && + (self->func_annotate == NULL || !PyCallable_Check(self->func_annotate))) { + self->func_annotations = PyDict_New(); + if (self->func_annotations == NULL) return NULL; } - PyObject *d = func_get_annotation_dict(op); + d = func_get_annotation_dict(self); return Py_XNewRef(d); } +/*[clinic input] +@critical_section +@setter +function.__annotations__ +[clinic start generated code]*/ + static int -func_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) +function___annotations___set_impl(PyFunctionObject *self, PyObject *value) +/*[clinic end generated code: output=a61795d4a95eede4 input=5302641f686f0463]*/ { - PyFunctionObject *op = _PyFunction_CAST(self); if (value == Py_None) value = NULL; /* Legal to del f.func_annotations. @@ -887,35 +923,49 @@ func_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) "__annotations__ must be set to a dict object"); return -1; } - Py_XSETREF(op->func_annotations, Py_XNewRef(value)); - Py_CLEAR(op->func_annotate); + Py_XSETREF(self->func_annotations, Py_XNewRef(value)); + Py_CLEAR(self->func_annotate); return 0; } +/*[clinic input] +@critical_section +@getter +function.__type_params__ + +Get the declared type parameters for a function. +[clinic start generated code]*/ + static PyObject * -func_get_type_params(PyObject *self, void *Py_UNUSED(ignored)) +function___type_params___get_impl(PyFunctionObject *self) +/*[clinic end generated code: output=eb844d7ffca517a8 input=0864721484293724]*/ { - PyFunctionObject *op = _PyFunction_CAST(self); - if (op->func_typeparams == NULL) { + if (self->func_typeparams == NULL) { return PyTuple_New(0); } - assert(PyTuple_Check(op->func_typeparams)); - return Py_NewRef(op->func_typeparams); + assert(PyTuple_Check(self->func_typeparams)); + return Py_NewRef(self->func_typeparams); } +/*[clinic input] +@critical_section +@setter +function.__type_params__ +[clinic start generated code]*/ + static int -func_set_type_params(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) +function___type_params___set_impl(PyFunctionObject *self, PyObject *value) +/*[clinic end generated code: output=038b4cda220e56fb input=3862fbd4db2b70e8]*/ { /* Not legal to del f.__type_params__ or to set it to anything * other than a tuple object. */ - PyFunctionObject *op = _PyFunction_CAST(self); if (value == NULL || !PyTuple_Check(value)) { PyErr_SetString(PyExc_TypeError, "__type_params__ must be set to a tuple"); return -1; } - Py_XSETREF(op->func_typeparams, Py_NewRef(value)); + Py_XSETREF(self->func_typeparams, Py_NewRef(value)); return 0; } @@ -934,22 +984,15 @@ static PyGetSetDef func_getsetlist[] = { {"__code__", func_get_code, func_set_code}, {"__defaults__", func_get_defaults, func_set_defaults}, {"__kwdefaults__", func_get_kwdefaults, func_set_kwdefaults}, - {"__annotations__", func_get_annotations, func_set_annotations}, - {"__annotate__", func_get_annotate, func_set_annotate}, + FUNCTION___ANNOTATIONS___GETSETDEF + FUNCTION___ANNOTATE___GETSETDEF {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, {"__name__", func_get_name, func_set_name}, {"__qualname__", func_get_qualname, func_set_qualname}, - {"__type_params__", func_get_type_params, func_set_type_params}, + FUNCTION___TYPE_PARAMS___GETSETDEF {NULL} /* Sentinel */ }; -/*[clinic input] -class function "PyFunctionObject *" "&PyFunction_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=70af9c90aa2e71b0]*/ - -#include "clinic/funcobject.c.h" - /* function.__new__() maintains the following invariants for closures. The closure must correspond to the free variables of the code object. From 555dc50c811e3e9ebdc30a1d511cf48a32666d6f Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 6 Feb 2025 10:19:00 -0500 Subject: [PATCH 087/311] gh-129694: Add `--parallel-threads` TSAN job to CI (gh-129696) For now, this just adds a single test suite to the TSAN CI to be run with `--parallel-threads`. --- .github/workflows/reusable-tsan.yml | 3 +++ Lib/test/libregrtest/cmdline.py | 4 ++++ Lib/test/libregrtest/main.py | 6 +++++- Lib/test/libregrtest/tsan.py | 11 +++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-tsan.yml b/.github/workflows/reusable-tsan.yml index 1d2548565d50ef..8ac2f84099e563 100644 --- a/.github/workflows/reusable-tsan.yml +++ b/.github/workflows/reusable-tsan.yml @@ -74,6 +74,9 @@ jobs: run: make pythoninfo - name: Tests run: ./python -m test --tsan -j4 + - name: Parallel tests + if: fromJSON(inputs.free-threading) + run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 - name: Display TSAN logs if: always() run: find "${GITHUB_WORKSPACE}" -name 'tsan_log.*' | xargs head -n 1000 diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 1f3b2381c71d45..81c7106ac0c0f4 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -168,6 +168,7 @@ def __init__(self, **kwargs) -> None: self.pgo = False self.pgo_extended = False self.tsan = False + self.tsan_parallel = False self.worker_json = None self.start = None self.timeout = None @@ -351,6 +352,9 @@ def _create_parser(): help='enable extended PGO training (slower training)') group.add_argument('--tsan', dest='tsan', action='store_true', help='run a subset of test cases that are proper for the TSAN test') + group.add_argument('--tsan-parallel', action='store_true', + help='run a subset of test cases that are appropriate ' + 'for TSAN with `--parallel-threads=N`') group.add_argument('--fail-env-changed', action='store_true', help='if a test file alters the environment, mark ' 'the test as failed') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index de377f185f7ed9..2f8fd4c92c119d 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -20,7 +20,7 @@ from .runtests import RunTests, HuntRefleak from .setup import setup_process, setup_test_dir from .single import run_single_test, PROGRESS_MIN_TIME -from .tsan import setup_tsan_tests +from .tsan import setup_tsan_tests, setup_tsan_parallel_tests from .utils import ( StrPath, StrJSON, TestName, TestList, TestTuple, TestFilter, strip_py_suffix, count, format_duration, @@ -60,6 +60,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.pgo: bool = ns.pgo self.pgo_extended: bool = ns.pgo_extended self.tsan: bool = ns.tsan + self.tsan_parallel: bool = ns.tsan_parallel # Test results self.results: TestResults = TestResults() @@ -195,6 +196,9 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList if self.tsan: setup_tsan_tests(self.cmdline_args) + if self.tsan_parallel: + setup_tsan_parallel_tests(self.cmdline_args) + exclude_tests = set() if self.exclude: for arg in self.cmdline_args: diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index 00d5779d950e72..1b32deec12bd75 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -28,7 +28,18 @@ 'test_free_threading.test_slots', ] +# Tests that should be run with `--parallel-threads=N` under TSAN. These tests +# typically do not use threads, but are run multiple times in parallel by +# the regression test runner with the `--parallel-threads` option enabled. +TSAN_PARALLEL_TESTS = [ + 'test_abc', +] + def setup_tsan_tests(cmdline_args) -> None: if not cmdline_args: cmdline_args[:] = TSAN_TESTS[:] + +def setup_tsan_parallel_tests(cmdline_args) -> None: + if not cmdline_args: + cmdline_args[:] = TSAN_PARALLEL_TESTS[:] From cb640b659e14cb0a05767054f95a9d25787b472d Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Thu, 6 Feb 2025 23:21:57 +0800 Subject: [PATCH 088/311] gh-128563: A new tail-calling interpreter (GH-128718) Co-authored-by: Garrett Gu Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/workflows/tail-call.yml | 113 + Lib/test/test_generated_cases.py | 330 +- ...-01-10-18-56-20.gh-issue-128563.baDvls.rst | 1 + Python/bytecodes.c | 9 +- Python/ceval.c | 23 +- Python/ceval_macros.h | 47 +- Python/generated_cases.c.h | 3232 ++++++++++++++--- Python/opcode_targets.h | 505 +++ Tools/cases_generator/analyzer.py | 1 + Tools/cases_generator/generators_common.py | 24 +- Tools/cases_generator/target_generator.py | 40 + Tools/cases_generator/tier1_generator.py | 84 +- Tools/jit/template.c | 8 - configure | 48 + configure.ac | 38 + pyconfig.h.in | 3 + 16 files changed, 3883 insertions(+), 623 deletions(-) create mode 100644 .github/workflows/tail-call.yml create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-18-56-20.gh-issue-128563.baDvls.rst diff --git a/.github/workflows/tail-call.yml b/.github/workflows/tail-call.yml new file mode 100644 index 00000000000000..ad5d0aa55173c4 --- /dev/null +++ b/.github/workflows/tail-call.yml @@ -0,0 +1,113 @@ +name: Tail calling interpreter +on: + pull_request: + paths: + - 'Python/bytecodes.c' + - 'Python/ceval.c' + - 'Python/ceval_macros.h' + push: + paths: + - 'Python/bytecodes.c' + - 'Python/ceval.c' + - 'Python/ceval_macros.h' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + FORCE_COLOR: 1 + +jobs: + tail-call: + name: ${{ matrix.target }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + target: +# Un-comment as we add support for more platforms for tail-calling interpreters. +# - i686-pc-windows-msvc/msvc +# - x86_64-pc-windows-msvc/msvc +# - aarch64-pc-windows-msvc/msvc + - x86_64-apple-darwin/clang + - aarch64-apple-darwin/clang + - x86_64-unknown-linux-gnu/gcc + - aarch64-unknown-linux-gnu/gcc + llvm: + - 19 + include: +# - target: i686-pc-windows-msvc/msvc +# architecture: Win32 +# runner: windows-latest +# - target: x86_64-pc-windows-msvc/msvc +# architecture: x64 +# runner: windows-latest +# - target: aarch64-pc-windows-msvc/msvc +# architecture: ARM64 +# runner: windows-latest + - target: x86_64-apple-darwin/clang + architecture: x86_64 + runner: macos-13 + - target: aarch64-apple-darwin/clang + architecture: aarch64 + runner: macos-14 + - target: x86_64-unknown-linux-gnu/gcc + architecture: x86_64 + runner: ubuntu-24.04 + - target: aarch64-unknown-linux-gnu/gcc + architecture: aarch64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Native Windows (debug) + if: runner.os == 'Windows' && matrix.architecture != 'ARM64' + run: | + choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0 + ./PCbuild/build.bat --tail-call-interp -d -p ${{ matrix.architecture }} + ./PCbuild/rt.bat -d -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + + # No tests (yet): + - name: Emulated Windows (release) + if: runner.os == 'Windows' && matrix.architecture == 'ARM64' + run: | + choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0 + ./PCbuild/build.bat --tail-call-interp -p ${{ matrix.architecture }} + + # The `find` line is required as a result of https://github.com/actions/runner-images/issues/9966. + # This is a bug in the macOS runner image where the pre-installed Python is installed in the same + # directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes + # the symlink to the pre-installed Python so that the Homebrew Python is used instead. + - name: Native macOS (debug) + if: runner.os == 'macOS' + run: | + brew update + find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete + brew install llvm@${{ matrix.llvm }} + export SDKROOT="$(xcrun --show-sdk-path)" + export PATH="/opt/homebrew/opt/llvm/bin:$PATH" + export PATH="/usr/local/opt/llvm/bin:$PATH" + CC=clang-19 ./configure --with-tail-call-interp --with-pydebug + make all --jobs 4 + ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + + - name: Native Linux (release) + if: runner.os == 'Linux' + run: | + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }} + export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH" + CC=clang-19 ./configure --with-tail-call-interp + make all --jobs 4 + ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index d2b33706ea6b75..35ad9ebbe5a1a9 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -304,6 +304,11 @@ def test_inst_no_args(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -322,6 +327,11 @@ def test_inst_one_pop(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -343,6 +353,11 @@ def test_inst_one_push(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -365,6 +380,11 @@ def test_inst_one_push_one_pop(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -388,6 +408,11 @@ def test_binary_op(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -414,6 +439,11 @@ def test_overlap(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -442,6 +472,11 @@ def test_predictions(self): """ output = """ TARGET(OP1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -453,12 +488,23 @@ def test_predictions(self): } TARGET(OP3) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP3; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP3); static_assert(INLINE_CACHE_ENTRIES_OP1 == 0, "incorrect cache size"); _PyStackRef res; - DEOPT_IF(xxx, OP1); + if (xxx) { + UPDATE_MISS_STATS(OP1); + assert(_PyOpcode_Deopt[opcode] == (OP1)); + JUMP_TO_PREDICTED(OP1); + } res = Py_None; stack_pointer[-1] = res; DISPATCH(); @@ -481,6 +527,11 @@ def test_sync_sp(self): """ output = """ TARGET(A) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = A; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(A); @@ -498,6 +549,11 @@ def test_sync_sp(self): } TARGET(B) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = B; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(B); @@ -535,11 +591,16 @@ def test_error_if_plain(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); if (cond) { - goto label; + JUMP_TO_LABEL(label); } DISPATCH(); } @@ -554,11 +615,16 @@ def test_error_if_plain_with_comment(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); if (cond) { - goto label; + JUMP_TO_LABEL(label); } // Comment is ok DISPATCH(); @@ -577,6 +643,11 @@ def test_error_if_pop(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -587,7 +658,7 @@ def test_error_if_pop(self): left = stack_pointer[-2]; SPAM(left, right); if (cond) { - goto pop_2_label; + JUMP_TO_LABEL(pop_2_label); } res = 0; stack_pointer[-2] = res; @@ -608,6 +679,11 @@ def test_error_if_pop_with_result(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -618,7 +694,7 @@ def test_error_if_pop_with_result(self): left = stack_pointer[-2]; res = SPAM(left, right); if (cond) { - goto pop_2_label; + JUMP_TO_LABEL(pop_2_label); } stack_pointer[-2] = res; stack_pointer += -1; @@ -635,8 +711,14 @@ def test_cache_effect(self): """ output = """ TARGET(OP) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(OP); uint16_t counter = read_u16(&this_instr[1].cache); @@ -661,13 +743,18 @@ def test_suppress_dispatch(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); - goto somewhere; + JUMP_TO_LABEL(somewhere); } - somewhere: + LABEL(somewhere) { } @@ -692,6 +779,11 @@ def test_macro_instruction(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(OP); @@ -729,8 +821,14 @@ def test_macro_instruction(self): } TARGET(OP1) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(OP1); _PyStackRef left; @@ -746,6 +844,11 @@ def test_macro_instruction(self): } TARGET(OP3) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP3; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(OP3); @@ -777,6 +880,11 @@ def test_unused_caches(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(OP); @@ -799,6 +907,11 @@ def test_pseudo_instruction_no_flags(self): """ output = """ TARGET(OP1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -818,6 +931,11 @@ def test_pseudo_instruction_with_flags(self): """ output = """ TARGET(OP1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -840,6 +958,11 @@ def test_pseudo_instruction_as_sequence(self): """ output = """ TARGET(OP1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -847,6 +970,11 @@ def test_pseudo_instruction_as_sequence(self): } TARGET(OP2) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP2; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP2); @@ -864,6 +992,11 @@ def test_array_input(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -887,6 +1020,11 @@ def test_array_output(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -915,6 +1053,11 @@ def test_array_input_output(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -939,13 +1082,18 @@ def test_array_error_if(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); if (oparg == 0) { stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto somewhere; + JUMP_TO_LABEL(somewhere); } stack_pointer += -1 - oparg; assert(WITHIN_STACK_BOUNDS()); @@ -965,6 +1113,11 @@ def test_cond_effect(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1006,6 +1159,11 @@ def test_macro_cond_effect(self): """ output = """ TARGET(M) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = M; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1050,6 +1208,11 @@ def test_macro_push_push(self): """ output = """ TARGET(M) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = M; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1083,6 +1246,11 @@ def test_override_inst(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1104,6 +1272,11 @@ def test_override_op(self): """ output = """ TARGET(M) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = M; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1121,6 +1294,11 @@ def test_annotated_inst(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1139,6 +1317,11 @@ def test_annotated_op(self): """ output = """ TARGET(M) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = M; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1175,6 +1358,11 @@ def test_array_of_one(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1197,6 +1385,11 @@ def test_pointer_to_stackref(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1235,6 +1428,11 @@ def test_unused_named_values(self): """ output = """ TARGET(INST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INST); @@ -1261,6 +1459,11 @@ def test_used_unused_used(self): """ output = """ TARGET(TEST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TEST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1301,6 +1504,11 @@ def test_unused_used_used(self): """ output = """ TARGET(TEST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TEST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1340,6 +1548,11 @@ def test_flush(self): """ output = """ TARGET(TEST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TEST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1388,6 +1601,11 @@ def test_pop_on_error_peeks(self): """ output = """ TARGET(TEST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TEST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1409,7 +1627,7 @@ def test_pop_on_error_peeks(self): { // Mark j and k as used if (cond) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } } stack_pointer += -2; @@ -1436,6 +1654,11 @@ def test_push_then_error(self): output = """ TARGET(TEST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TEST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1453,7 +1676,7 @@ def test_push_then_error(self): stack_pointer[1] = b; stack_pointer += 2; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } } stack_pointer[0] = a; @@ -1477,17 +1700,27 @@ def test_error_if_true(self): """ output = """ TARGET(OP1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); - goto here; + JUMP_TO_LABEL(here); } TARGET(OP2) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP2; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP2); - goto there; + JUMP_TO_LABEL(there); } """ self.run_cases_test(input, output) @@ -1541,6 +1774,11 @@ def test_stack_save_reload(self): output = """ TARGET(BALANCED) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BALANCED; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BALANCED); @@ -1561,6 +1799,11 @@ def test_stack_reload_only(self): output = """ TARGET(BALANCED) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BALANCED; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BALANCED); @@ -1582,6 +1825,13 @@ def test_stack_save_only(self): output = """ TARGET(BALANCED) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BALANCED; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BALANCED); @@ -1602,6 +1852,11 @@ def test_instruction_size_macro(self): output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1637,6 +1892,11 @@ def test_escaping_call_next_to_cmacro(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1671,6 +1931,11 @@ def test_pystackref_frompyobject_new_next_to_cmacro(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1702,6 +1967,11 @@ def test_pop_input(self): """ output = """ TARGET(OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1793,26 +2063,26 @@ def test_complex_label(self): """ output = """ - other_label: + LABEL(other_label) { } - other_label2: + LABEL(other_label2) { } - my_label: + LABEL(my_label) { // Comment _PyFrame_SetStackPointer(frame, stack_pointer); do_thing(); stack_pointer = _PyFrame_GetStackPointer(frame); if (complex) { - goto other_label; + JUMP_TO_LABEL(other_label); } - goto other_label2; + JUMP_TO_LABEL(other_label2); } """ self.run_cases_test(input, output) @@ -1831,17 +2101,17 @@ def test_spilled_label(self): """ output = """ - one: + LABEL(one) { /* STACK SPILLED */ stack_pointer = _PyFrame_GetStackPointer(frame); - goto two; + JUMP_TO_LABEL(two); } - two: + LABEL(two) { _PyFrame_SetStackPointer(frame, stack_pointer); - goto one; + JUMP_TO_LABEL(one); } """ self.run_cases_test(input, output) @@ -1887,20 +2157,26 @@ def test_multiple_labels(self): """ output = """ - my_label_1: + LABEL(my_label_1) { // Comment + _PyFrame_SetStackPointer(frame, stack_pointer); do_thing1(); - goto my_label_2; + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(my_label_2); } - my_label_2: + LABEL(my_label_2) { // Comment + _PyFrame_SetStackPointer(frame, stack_pointer); do_thing2(); - goto my_label_1; + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(my_label_1); } """ + self.run_cases_test(input, output) + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-18-56-20.gh-issue-128563.baDvls.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-18-56-20.gh-issue-128563.baDvls.rst new file mode 100644 index 00000000000000..4d29f346cb6251 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-10-18-56-20.gh-issue-128563.baDvls.rst @@ -0,0 +1 @@ +A new type of interpreter has been added to CPython. This interpreter uses tail calls for its instruction handlers. Preliminary benchmark results suggest 7-11% geometric mean faster on pyperformance (depending on platform), and up to 30% faster on Python-intensive workloads. This interpreter currently only works on newer compilers, such as ``clang-19``. Other compilers will continue using the old interpreter. Patch by Ken Jin, with ideas on how to implement this in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b650613650cf36..c3024f7f98f28c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1387,7 +1387,9 @@ dummy_func( tier1 inst(CLEANUP_THROW, (sub_iter_st, last_sent_val_st, exc_value_st -- none, value)) { PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); + #ifndef Py_TAIL_CALL_INTERP assert(throwflag); + #endif assert(exc_value && PyExceptionInstance_Check(exc_value)); int matches = PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration); @@ -5305,6 +5307,9 @@ dummy_func( } #endif RELOAD_STACK(); +#ifdef Py_TAIL_CALL_INTERP + int opcode; +#endif DISPATCH(); } @@ -5351,8 +5356,10 @@ dummy_func( caller loses its exception */ assert(!_PyErr_Occurred(tstate)); #endif - RELOAD_STACK(); +#ifdef Py_TAIL_CALL_INTERP + int opcode; +#endif DISPATCH(); } diff --git a/Python/ceval.c b/Python/ceval.c index 4f628fdbabddbb..5e834883f355e1 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -768,13 +768,18 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch) #define PY_EVAL_C_STACK_UNITS 2 +#ifdef Py_TAIL_CALL_INTERP +#include "opcode_targets.h" +#include "generated_cases.c.h" +#endif + PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) { _Py_EnsureTstateNotNULL(tstate); CALL_STAT_INC(pyeval_calls); -#if USE_COMPUTED_GOTOS +#if USE_COMPUTED_GOTOS && !defined(Py_TAIL_CALL_INTERP) /* Import the static jump table */ #include "opcode_targets.h" #endif @@ -782,10 +787,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef Py_STATS int lastopcode = 0; #endif +#ifndef Py_TAIL_CALL_INTERP uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ - - _PyInterpreterFrame entry_frame; +#endif + _PyInterpreterFrame entry_frame; if (_Py_EnterRecursiveCallTstate(tstate, "")) { assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -845,7 +851,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); monitor_throw(tstate, frame, next_instr); +#ifdef Py_TAIL_CALL_INTERP + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0); +#else goto error; +#endif } #if defined(_Py_TIER2) && !defined(_Py_JIT) @@ -854,9 +864,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int const _PyUOpInstruction *next_uop = NULL; #endif +#ifdef Py_TAIL_CALL_INTERP + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0); +#else goto start_frame; - -#include "generated_cases.c.h" +# include "generated_cases.c.h" +#endif #ifdef _Py_TIER2 diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index b6d9df32953892..44bb52a09aab5f 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -70,12 +70,41 @@ #define INSTRUCTION_STATS(op) ((void)0) #endif -#if USE_COMPUTED_GOTOS +#define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, int oparg +#define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, oparg + +#ifdef Py_TAIL_CALL_INTERP + // Note: [[clang::musttail]] works for GCC 15, but not __attribute__((musttail)) at the moment. +# define Py_MUSTTAIL [[clang::musttail]] +# define Py_PRESERVE_NONE_CC __attribute__((preserve_none)) + Py_PRESERVE_NONE_CC typedef PyObject* (*py_tail_call_funcptr)(TAIL_CALL_PARAMS); + +# define TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_##op(TAIL_CALL_PARAMS) +# define DISPATCH_GOTO() \ + do { \ + Py_MUSTTAIL return (INSTRUCTION_TABLE[opcode])(TAIL_CALL_ARGS); \ + } while (0) +# define JUMP_TO_LABEL(name) \ + do { \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(TAIL_CALL_ARGS); \ + } while (0) +# define JUMP_TO_PREDICTED(name) \ + do { \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg); \ + } while (0) +# define LABEL(name) TARGET(name) +#elif USE_COMPUTED_GOTOS # define TARGET(op) TARGET_##op: # define DISPATCH_GOTO() goto *opcode_targets[opcode] +# define JUMP_TO_LABEL(name) goto name; +# define JUMP_TO_PREDICTED(name) goto PREDICTED_##name; +# define LABEL(name) name: #else # define TARGET(op) case op: TARGET_##op: # define DISPATCH_GOTO() goto dispatch_opcode +# define JUMP_TO_LABEL(name) goto name; +# define JUMP_TO_PREDICTED(name) goto PREDICTED_##name; +# define LABEL(name) name: #endif /* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ @@ -92,7 +121,7 @@ do { \ int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); \ frame->lltrace = lltrace; \ if (lltrace < 0) { \ - goto exit_unwind; \ + JUMP_TO_LABEL(exit_unwind); \ } \ } while (0) #else @@ -129,11 +158,11 @@ do { \ assert((NEW_FRAME)->previous == frame); \ frame = tstate->current_frame = (NEW_FRAME); \ CALL_STAT_INC(inlined_py_calls); \ - goto start_frame; \ + JUMP_TO_LABEL(start_frame); \ } while (0) // Use this instead of 'goto error' so Tier 2 can go to a different label -#define GOTO_ERROR(LABEL) goto LABEL +#define GOTO_ERROR(LABEL) JUMP_TO_LABEL(LABEL) /* Tuple access macros */ @@ -236,14 +265,6 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define UPDATE_MISS_STATS(INSTNAME) ((void)0) #endif -#define DEOPT_IF(COND, INSTNAME) \ - if ((COND)) { \ - /* This is only a single jump on release builds! */ \ - UPDATE_MISS_STATS((INSTNAME)); \ - assert(_PyOpcode_Deopt[opcode] == (INSTNAME)); \ - goto PREDICTED_##INSTNAME; \ - } - // Try to lock an object in the free threading build, if it's not already // locked. Use with a DEOPT_IF() to deopt if the object is already locked. @@ -328,7 +349,7 @@ do { \ stack_pointer = _PyFrame_GetStackPointer(frame); \ if (next_instr == NULL) { \ next_instr = (dest)+1; \ - goto error; \ + JUMP_TO_LABEL(error); \ } \ } \ } while (0); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5820147ff712a9..7927fa7db95c20 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8,15 +8,22 @@ #endif #define TIER_ONE 1 +#ifndef Py_TAIL_CALL_INTERP #if !USE_COMPUTED_GOTOS dispatch_opcode: switch (opcode) #endif { +#endif /* Py_TAIL_CALL_INTERP */ /* BEGIN INSTRUCTIONS */ TARGET(BINARY_OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP); @@ -58,7 +65,7 @@ PyStackRef_CLOSE(lhs); PyStackRef_CLOSE(rhs); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -69,6 +76,13 @@ } TARGET(BINARY_OP_ADD_FLOAT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_ADD_FLOAT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); @@ -82,8 +96,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyFloat_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_ADD_FLOAT @@ -98,7 +120,7 @@ ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -109,6 +131,13 @@ } TARGET(BINARY_OP_ADD_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_ADD_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_INT); @@ -122,8 +151,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); + if (!PyLong_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyLong_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_ADD_INT @@ -137,7 +174,7 @@ PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -148,6 +185,13 @@ } TARGET(BINARY_OP_ADD_UNICODE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_ADD_UNICODE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); @@ -161,8 +205,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyUnicode_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right_o), BINARY_OP); + if (!PyUnicode_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyUnicode_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_ADD_UNICODE @@ -176,7 +228,7 @@ PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -187,8 +239,14 @@ } TARGET(BINARY_OP_EXTEND) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_EXTEND; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_EXTEND); static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); @@ -209,7 +267,11 @@ _PyFrame_SetStackPointer(frame, stack_pointer); int res = d->guard(left_o, right_o); stack_pointer = _PyFrame_GetStackPointer(frame); - DEOPT_IF(!res, BINARY_OP); + if (!res) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip -4 cache entry */ // _BINARY_OP_EXTEND @@ -234,6 +296,13 @@ } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_INPLACE_ADD_UNICODE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); @@ -246,8 +315,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyUnicode_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyUnicode_CheckExact(right_o), BINARY_OP); + if (!PyUnicode_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyUnicode_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_INPLACE_ADD_UNICODE @@ -264,7 +341,11 @@ next_oparg = CURRENT_OPERAND0(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); - DEOPT_IF(PyStackRef_AsPyObjectBorrow(*target_local) != left_o, BINARY_OP); + if (PyStackRef_AsPyObjectBorrow(*target_local) != left_o) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. * @@ -284,7 +365,7 @@ *target_local = PyStackRef_FromPyObjectSteal(temp); PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); if (PyStackRef_IsNull(*target_local)) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } #if TIER_ONE // The STORE_FAST is already done. This is done here in tier one, @@ -299,6 +380,13 @@ } TARGET(BINARY_OP_MULTIPLY_FLOAT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_MULTIPLY_FLOAT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); @@ -312,8 +400,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyFloat_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_MULTIPLY_FLOAT @@ -328,7 +424,7 @@ ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -339,6 +435,13 @@ } TARGET(BINARY_OP_MULTIPLY_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_MULTIPLY_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); @@ -352,8 +455,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); + if (!PyLong_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyLong_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_MULTIPLY_INT @@ -367,7 +478,7 @@ PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -378,6 +489,13 @@ } TARGET(BINARY_OP_SUBTRACT_FLOAT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_SUBTRACT_FLOAT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); @@ -391,8 +509,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyFloat_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyFloat_CheckExact(right_o), BINARY_OP); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyFloat_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_SUBTRACT_FLOAT @@ -407,7 +533,7 @@ ((PyFloatObject *)right_o)->ob_fval; PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -418,6 +544,13 @@ } TARGET(BINARY_OP_SUBTRACT_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_OP_SUBTRACT_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); @@ -431,8 +564,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyLong_CheckExact(left_o), BINARY_OP); - DEOPT_IF(!PyLong_CheckExact(right_o), BINARY_OP); + if (!PyLong_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyLong_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_SUBTRACT_INT @@ -446,7 +587,7 @@ PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -457,6 +598,11 @@ } TARGET(BINARY_SLICE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SLICE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BINARY_SLICE); @@ -502,7 +648,7 @@ PyStackRef_CLOSE(container); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -513,6 +659,11 @@ } TARGET(BINARY_SUBSCR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR); @@ -551,7 +702,7 @@ PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -562,6 +713,13 @@ } TARGET(BINARY_SUBSCR_DICT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_DICT); @@ -574,7 +732,11 @@ dict_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -588,7 +750,7 @@ PyStackRef_CLOSE(dict_st); PyStackRef_CLOSE(sub_st); if (rc <= 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } // not found or error res = PyStackRef_FromPyObjectSteal(res_o); @@ -599,6 +761,13 @@ } TARGET(BINARY_SUBSCR_GETITEM) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR_GETITEM; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); @@ -610,22 +779,42 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, BINARY_SUBSCR); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } } // _BINARY_SUBSCR_CHECK_FUNC { container = stack_pointer[-2]; PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE), BINARY_SUBSCR); + if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); - DEOPT_IF(getitem_o == NULL, BINARY_SUBSCR); + if (getitem_o == NULL) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } assert(PyFunction_Check(getitem_o)); uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); - DEOPT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version, BINARY_SUBSCR); + if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } getitem = PyStackRef_FromPyObjectNew(getitem_o); STAT_INC(BINARY_SUBSCR, hit); } @@ -658,6 +847,13 @@ } TARGET(BINARY_SUBSCR_LIST_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR_LIST_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); @@ -670,19 +866,39 @@ list_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR); + if (!PyLong_CheckExact(sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } + if (!PyList_CheckExact(list)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } // Deopt unless 0 <= sub < PyList_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; #ifdef Py_GIL_DISABLED _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); stack_pointer = _PyFrame_GetStackPointer(frame); - DEOPT_IF(res_o == NULL, BINARY_SUBSCR); + if (res_o == NULL) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } STAT_INC(BINARY_SUBSCR, hit); #else - DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR); + if (index >= PyList_GET_SIZE(list)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); @@ -702,6 +918,13 @@ } TARGET(BINARY_SUBSCR_STR_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR_STR_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); @@ -714,14 +937,34 @@ str_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyUnicode_CheckExact(str), BINARY_SUBSCR); - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + if (!PyLong_CheckExact(sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } + if (!PyUnicode_CheckExact(str)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index, BINARY_SUBSCR); + if (PyUnicode_GET_LENGTH(str) <= index) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } // Specialize for reading an ASCII character from any string: Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c, BINARY_SUBSCR); + if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); @@ -738,6 +981,13 @@ } TARGET(BINARY_SUBSCR_TUPLE_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BINARY_SUBSCR_TUPLE_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); @@ -750,12 +1000,28 @@ tuple_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); - DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); - DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR); + if (!PyLong_CheckExact(sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } + if (!PyTuple_CheckExact(tuple)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } // Deopt unless 0 <= sub < PyTuple_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR); + if (index >= PyTuple_GET_SIZE(tuple)) { + UPDATE_MISS_STATS(BINARY_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); + JUMP_TO_PREDICTED(BINARY_SUBSCR); + } STAT_INC(BINARY_SUBSCR, hit); PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); @@ -774,6 +1040,11 @@ } TARGET(BUILD_LIST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_LIST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_LIST); @@ -782,7 +1053,7 @@ values = &stack_pointer[-oparg]; PyObject *list_o = _PyList_FromStackRefStealOnSuccess(values, oparg); if (list_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } list = PyStackRef_FromPyObjectSteal(list_o); stack_pointer[-oparg] = list; @@ -792,6 +1063,11 @@ } TARGET(BUILD_MAP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_MAP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_MAP); @@ -805,7 +1081,7 @@ } stack_pointer += -oparg*2; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *map_o = _PyDict_FromItems( @@ -820,7 +1096,7 @@ if (map_o == NULL) { stack_pointer += -oparg*2; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } map = PyStackRef_FromPyObjectSteal(map_o); stack_pointer[-oparg*2] = map; @@ -830,6 +1106,11 @@ } TARGET(BUILD_SET) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_SET; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_SET); @@ -845,7 +1126,7 @@ } stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } int err = 0; for (int i = 0; i < oparg; i++) { @@ -864,7 +1145,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } set = PyStackRef_FromPyObjectSteal(set_o); stack_pointer[-oparg] = set; @@ -874,6 +1155,11 @@ } TARGET(BUILD_SLICE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_SLICE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_SLICE); @@ -890,7 +1176,7 @@ if (slice_o == NULL) { stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } slice = PyStackRef_FromPyObjectSteal(slice_o); stack_pointer[-oparg] = slice; @@ -900,6 +1186,11 @@ } TARGET(BUILD_STRING) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_STRING; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_STRING); @@ -913,7 +1204,7 @@ } stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); @@ -923,7 +1214,7 @@ if (str_o == NULL) { stack_pointer += -oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } str = PyStackRef_FromPyObjectSteal(str_o); stack_pointer[-oparg] = str; @@ -933,6 +1224,11 @@ } TARGET(BUILD_TUPLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = BUILD_TUPLE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_TUPLE); @@ -941,7 +1237,7 @@ values = &stack_pointer[-oparg]; PyObject *tup_o = _PyTuple_FromStackRefStealOnSuccess(values, oparg); if (tup_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } tup = PyStackRef_FromPyObjectSteal(tup_o); stack_pointer[-oparg] = tup; @@ -951,6 +1247,11 @@ } TARGET(CACHE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CACHE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CACHE); @@ -960,13 +1261,17 @@ } TARGET(CALL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL); PREDICTED_CALL:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; - opcode = CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -1041,7 +1346,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } frame->return_offset = 4 ; DISPATCH_INLINED(new_frame); @@ -1056,7 +1361,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -1097,7 +1402,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -1113,7 +1418,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1126,8 +1431,14 @@ } TARGET(CALL_ALLOC_AND_ENTER_INIT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_ALLOC_AND_ENTER_INIT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); @@ -1141,7 +1452,11 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_AND_ALLOCATE_OBJECT { @@ -1152,23 +1467,39 @@ self = &stack_pointer[-1 - oparg]; uint32_t type_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyStackRef_IsNull(null[0]), CALL); - DEOPT_IF(!PyType_Check(callable_o), CALL); + if (!PyStackRef_IsNull(null[0])) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!PyType_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyTypeObject *tp = (PyTypeObject *)callable_o; - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version, CALL); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } assert(tp->tp_new == PyBaseObject_Type.tp_new); assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); assert(tp->tp_alloc == PyType_GenericAlloc); PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); PyCodeObject *code = (PyCodeObject *)init_func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize), CALL); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *self_o = PyType_GenericAlloc(tp, 0); stack_pointer = _PyFrame_GetStackPointer(frame); if (self_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } self[0] = PyStackRef_FromPyObjectSteal(self_o); _PyStackRef temp = callable[0]; @@ -1198,7 +1529,7 @@ assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { _PyEval_FrameClearAndPop(tstate, shim); - goto error; + JUMP_TO_LABEL(error); } init_frame = temp; frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL; @@ -1227,8 +1558,14 @@ } TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BOUND_METHOD_EXACT_ARGS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); @@ -1240,14 +1577,26 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS { null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - DEOPT_IF(!PyStackRef_IsNull(null[0]), CALL); - DEOPT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(callable[0])) != &PyMethod_Type, CALL); + if (!PyStackRef_IsNull(null[0])) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (Py_TYPE(PyStackRef_AsPyObjectBorrow(callable[0])) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _INIT_CALL_BOUND_METHOD_EXACT_ARGS { @@ -1267,9 +1616,17 @@ { uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyFunction_Check(callable_o), CALL); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyFunctionObject *func = (PyFunctionObject *)callable_o; - DEOPT_IF(func->func_version != func_version, CALL); + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_FUNCTION_EXACT_ARGS { @@ -1277,15 +1634,27 @@ assert(PyFunction_Check(callable_o)); PyFunctionObject *func = (PyFunctionObject *)callable_o; PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null[0])), CALL); + if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null[0]))) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_STACK_SPACE { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyFunctionObject *func = (PyFunctionObject *)callable_o; PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _INIT_CALL_PY_EXACT_ARGS { @@ -1329,8 +1698,14 @@ } TARGET(CALL_BOUND_METHOD_GENERAL) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BOUND_METHOD_GENERAL; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BOUND_METHOD_GENERAL); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); @@ -1342,7 +1717,11 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_METHOD_VERSION { @@ -1350,11 +1729,27 @@ callable = &stack_pointer[-2 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(Py_TYPE(callable_o) != &PyMethod_Type, CALL); + if (Py_TYPE(callable_o) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyObject *func = ((PyMethodObject *)callable_o)->im_func; - DEOPT_IF(!PyFunction_Check(func), CALL); - DEOPT_IF(((PyFunctionObject *)func)->func_version != func_version, CALL); - DEOPT_IF(!PyStackRef_IsNull(null[0]), CALL); + if (!PyFunction_Check(func)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!PyStackRef_IsNull(null[0])) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _EXPAND_METHOD { @@ -1394,7 +1789,7 @@ stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { - goto error; + JUMP_TO_LABEL(error); } new_frame = temp; } @@ -1426,6 +1821,13 @@ } TARGET(CALL_BUILTIN_CLASS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BUILTIN_CLASS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_CLASS); @@ -1442,7 +1844,11 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyType_Check(callable_o), CALL); + if (!PyType_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyTypeObject *tp = (PyTypeObject *)callable_o; int total_args = oparg; _PyStackRef *arguments = args; @@ -1450,7 +1856,11 @@ arguments--; total_args++; } - DEOPT_IF(tp->tp_vectorcall == NULL, CALL); + if (tp->tp_vectorcall == NULL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); if (CONVERSION_FAILED(args_o)) { @@ -1461,7 +1871,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); @@ -1475,7 +1885,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -1491,7 +1901,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1504,6 +1914,13 @@ } TARGET(CALL_BUILTIN_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BUILTIN_FAST; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_FAST); @@ -1527,8 +1944,16 @@ arguments--; total_args++; } - DEOPT_IF(!PyCFunction_CheckExact(callable_o), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL, CALL); + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); /* res = func(self, args, nargs) */ @@ -1541,7 +1966,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)( @@ -1559,7 +1984,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -1575,7 +2000,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1588,6 +2013,13 @@ } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BUILTIN_FAST_WITH_KEYWORDS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); @@ -1611,8 +2043,16 @@ arguments--; total_args++; } - DEOPT_IF(!PyCFunction_CheckExact(callable_o), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS), CALL); + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); /* res = func(self, arguments, nargs, kwnames) */ _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1629,7 +2069,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); @@ -1644,7 +2084,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -1660,7 +2100,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -1673,6 +2113,13 @@ } TARGET(CALL_BUILTIN_O) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_BUILTIN_O; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_BUILTIN_O); @@ -1695,11 +2142,27 @@ args--; total_args++; } - DEOPT_IF(total_args != 1, CALL); - DEOPT_IF(!PyCFunction_CheckExact(callable_o), CALL); - DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_O, CALL); + if (total_args != 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } // CPython promises to check all non-vectorcall function calls. - DEOPT_IF(tstate->c_recursion_remaining <= 0, CALL); + if (tstate->c_recursion_remaining <= 0) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); _PyStackRef arg = args[0]; @@ -1718,7 +2181,7 @@ PyStackRef_CLOSE(callable[0]); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -1734,7 +2197,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -1747,11 +2210,16 @@ } TARGET(CALL_FUNCTION_EX) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_FUNCTION_EX; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_FUNCTION_EX); - opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; _PyStackRef kwargs_in; @@ -1777,13 +2245,13 @@ int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *tuple_o = PySequence_Tuple(callargs_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (tuple_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } kwargs_out = kwargs_in; stack_pointer += -2; @@ -1824,7 +2292,7 @@ frame, this_instr, func, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); result_o = PyObject_Call(func, callargs, kwargs); @@ -1873,7 +2341,7 @@ stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } assert( 1 == 1); frame->return_offset = 1; @@ -1905,7 +2373,7 @@ PyStackRef_CLOSE(func_st); stack_pointer = _PyFrame_GetStackPointer(frame); if (result_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } result = PyStackRef_FromPyObjectSteal(result_o); } @@ -1921,7 +2389,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -1934,6 +2402,11 @@ } TARGET(CALL_INTRINSIC_1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_INTRINSIC_1; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_INTRINSIC_1); @@ -1946,7 +2419,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); if (res_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; @@ -1954,6 +2427,11 @@ } TARGET(CALL_INTRINSIC_2) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_INTRINSIC_2; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_INTRINSIC_2); @@ -1971,7 +2449,7 @@ PyStackRef_CLOSE(value2_st); PyStackRef_CLOSE(value1_st); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -1981,6 +2459,13 @@ } TARGET(CALL_ISINSTANCE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_ISINSTANCE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_ISINSTANCE); @@ -2002,9 +2487,17 @@ arguments--; total_args++; } - DEOPT_IF(total_args != 2, CALL); + if (total_args != 2) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.isinstance, CALL); + if (callable_o != interp->callable_cache.isinstance) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); _PyStackRef cls_stackref = arguments[1]; _PyStackRef inst_stackref = arguments[0]; @@ -2012,7 +2505,7 @@ int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); stack_pointer = _PyFrame_GetStackPointer(frame); if (retval < 0) { - goto error; + JUMP_TO_LABEL(error); } res = retval ? PyStackRef_True : PyStackRef_False; assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -2028,13 +2521,17 @@ } TARGET(CALL_KW) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_KW; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW); PREDICTED_CALL_KW:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; - opcode = CALL_KW; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -2123,7 +2620,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } assert( 4 == 1 + INLINE_CACHE_ENTRIES_CALL_KW); frame->return_offset = 4 ; @@ -2140,7 +2637,7 @@ PyStackRef_CLOSE(kwnames); stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } stack_pointer[-1] = kwnames; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2182,7 +2679,7 @@ if (res_o == NULL) { stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2193,8 +2690,14 @@ } TARGET(CALL_KW_BOUND_METHOD) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_KW_BOUND_METHOD; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW_BOUND_METHOD); static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); @@ -2207,7 +2710,11 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL_KW); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } } // _CHECK_METHOD_VERSION_KW { @@ -2215,11 +2722,27 @@ callable = &stack_pointer[-3 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(Py_TYPE(callable_o) != &PyMethod_Type, CALL_KW); + if (Py_TYPE(callable_o) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } PyObject *func = ((PyMethodObject *)callable_o)->im_func; - DEOPT_IF(!PyFunction_Check(func), CALL_KW); - DEOPT_IF(((PyFunctionObject *)func)->func_version != func_version, CALL_KW); - DEOPT_IF(!PyStackRef_IsNull(null[0]), CALL_KW); + if (!PyFunction_Check(func)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (!PyStackRef_IsNull(null[0])) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } } // _EXPAND_METHOD_KW { @@ -2269,7 +2792,7 @@ stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { - goto error; + JUMP_TO_LABEL(error); } new_frame = temp; } @@ -2301,10 +2824,16 @@ } TARGET(CALL_KW_NON_PY) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_KW_NON_PY; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW_NON_PY); - opcode = CALL_KW_NON_PY; static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef kwnames; @@ -2317,8 +2846,16 @@ { callable = &stack_pointer[-3 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(PyFunction_Check(callable_o), CALL_KW); - DEOPT_IF(Py_TYPE(callable_o) == &PyMethod_Type, CALL_KW); + if (PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } } // _CALL_KW_NON_PY { @@ -2346,7 +2883,7 @@ PyStackRef_CLOSE(kwnames); stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); @@ -2371,7 +2908,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2387,7 +2924,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2400,8 +2937,14 @@ } TARGET(CALL_KW_PY) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_KW_PY; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW_PY); static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); @@ -2413,16 +2956,28 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL_KW); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } } // _CHECK_FUNCTION_VERSION_KW { callable = &stack_pointer[-3 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyFunction_Check(callable_o), CALL_KW); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } PyFunctionObject *func = (PyFunctionObject *)callable_o; - DEOPT_IF(func->func_version != func_version, CALL_KW); + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } } // _PY_FRAME_KW { @@ -2458,7 +3013,7 @@ stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { - goto error; + JUMP_TO_LABEL(error); } new_frame = temp; } @@ -2490,6 +3045,13 @@ } TARGET(CALL_LEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_LEN; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_LEN); @@ -2510,9 +3072,17 @@ args--; total_args++; } - DEOPT_IF(total_args != 1, CALL); + if (total_args != 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.len, CALL); + if (callable_o != interp->callable_cache.len) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); _PyStackRef arg_stackref = args[0]; PyObject *arg = PyStackRef_AsPyObjectBorrow(arg_stackref); @@ -2520,7 +3090,7 @@ Py_ssize_t len_i = PyObject_Length(arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (len_i < 0) { - goto error; + JUMP_TO_LABEL(error); } PyObject *res_o = PyLong_FromSsize_t(len_i); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -2543,6 +3113,13 @@ } TARGET(CALL_LIST_APPEND) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_LIST_APPEND; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_LIST_APPEND); @@ -2559,10 +3136,22 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.list_append, CALL); + if (callable_o != interp->callable_cache.list_append) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } assert(self_o != NULL); - DEOPT_IF(!PyList_Check(self_o), CALL); - DEOPT_IF(!LOCK_OBJECT(self_o), CALL); + if (!PyList_Check(self_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!LOCK_OBJECT(self_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); UNLOCK_OBJECT(self_o); @@ -2577,7 +3166,7 @@ PyStackRef_CLOSE(callable); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } #if TIER_ONE // Skip the following POP_TOP. This is done here in tier one, and @@ -2589,6 +3178,13 @@ } TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_METHOD_DESCRIPTOR_FAST; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); @@ -2613,11 +3209,23 @@ } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; /* Builtin METH_FASTCALL methods, without keywords */ - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL); + if (meth->ml_flags != METH_FASTCALL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); int nargs = total_args - 1; STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); @@ -2629,7 +3237,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFast cfunc = @@ -2646,7 +3254,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2662,7 +3270,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2675,6 +3283,13 @@ } TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); @@ -2698,12 +3313,24 @@ total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL); + if (meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyTypeObject *d_type = method->d_common.d_type; PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); - DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); + if (!Py_IS_TYPE(self, d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); int nargs = total_args - 1; STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); @@ -2715,7 +3342,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = @@ -2732,7 +3359,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2748,7 +3375,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2761,6 +3388,13 @@ } TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_METHOD_DESCRIPTOR_NOARGS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); @@ -2783,16 +3417,36 @@ args--; total_args++; } - DEOPT_IF(total_args != 1, CALL); + if (total_args != 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyMethodDef *meth = method->d_method; _PyStackRef self_stackref = args[0]; PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); - DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); - DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (meth->ml_flags != METH_NOARGS) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } // CPython promises to check all non-vectorcall function calls. - DEOPT_IF(tstate->c_recursion_remaining <= 0, CALL); + if (tstate->c_recursion_remaining <= 0) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; _Py_EnterRecursiveCallTstateUnchecked(tstate); @@ -2810,7 +3464,7 @@ PyStackRef_CLOSE(callable[0]); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2826,7 +3480,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -2839,6 +3493,13 @@ } TARGET(CALL_METHOD_DESCRIPTOR_O) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_METHOD_DESCRIPTOR_O; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); @@ -2862,16 +3523,36 @@ total_args++; } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - DEOPT_IF(total_args != 2, CALL); - DEOPT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type), CALL); + if (total_args != 2) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyMethodDef *meth = method->d_method; - DEOPT_IF(meth->ml_flags != METH_O, CALL); + if (meth->ml_flags != METH_O) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } // CPython promises to check all non-vectorcall function calls. - DEOPT_IF(tstate->c_recursion_remaining <= 0, CALL); + if (tstate->c_recursion_remaining <= 0) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } _PyStackRef arg_stackref = arguments[1]; _PyStackRef self_stackref = arguments[0]; - DEOPT_IF(!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), - method->d_common.d_type), CALL); + if (!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), + method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); PyCFunction cfunc = meth->ml_meth; _Py_EnterRecursiveCallTstateUnchecked(tstate); @@ -2890,7 +3571,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2906,7 +3587,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -2919,10 +3600,16 @@ } TARGET(CALL_NON_PY_GENERAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_NON_PY_GENERAL; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_NON_PY_GENERAL); - opcode = CALL_NON_PY_GENERAL; static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef *self_or_null; @@ -2934,8 +3621,16 @@ { callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(PyFunction_Check(callable_o), CALL); - DEOPT_IF(Py_TYPE(callable_o) == &PyMethod_Type, CALL); + if (PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CALL_NON_PY_GENERAL { @@ -2961,7 +3656,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -2979,7 +3674,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2995,7 +3690,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -3008,8 +3703,14 @@ } TARGET(CALL_PY_EXACT_ARGS) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_PY_EXACT_ARGS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); @@ -3020,16 +3721,28 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_FUNCTION_VERSION { callable = &stack_pointer[-2 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyFunction_Check(callable_o), CALL); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyFunctionObject *func = (PyFunctionObject *)callable_o; - DEOPT_IF(func->func_version != func_version, CALL); + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_FUNCTION_EXACT_ARGS { @@ -3038,15 +3751,27 @@ assert(PyFunction_Check(callable_o)); PyFunctionObject *func = (PyFunctionObject *)callable_o; PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null[0])), CALL); + if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null[0]))) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_STACK_SPACE { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyFunctionObject *func = (PyFunctionObject *)callable_o; PyCodeObject *code = (PyCodeObject *)func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL); - DEOPT_IF(tstate->py_recursion_remaining <= 1, CALL); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _INIT_CALL_PY_EXACT_ARGS { @@ -3090,8 +3815,14 @@ } TARGET(CALL_PY_GENERAL) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_PY_GENERAL; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_PY_GENERAL); static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); @@ -3102,16 +3833,28 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, CALL); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _CHECK_FUNCTION_VERSION { callable = &stack_pointer[-2 - oparg]; uint32_t func_version = read_u32(&this_instr[2].cache); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - DEOPT_IF(!PyFunction_Check(callable_o), CALL); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } PyFunctionObject *func = (PyFunctionObject *)callable_o; - DEOPT_IF(func->func_version != func_version, CALL); + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } } // _PY_FRAME_GENERAL { @@ -3137,7 +3880,7 @@ stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); if (temp == NULL) { - goto error; + JUMP_TO_LABEL(error); } new_frame = temp; } @@ -3169,6 +3912,13 @@ } TARGET(CALL_STR_1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_STR_1; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_STR_1); @@ -3187,8 +3937,16 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null), CALL); - DEOPT_IF(callable_o != (PyObject *)&PyUnicode_Type, CALL); + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (callable_o != (PyObject *)&PyUnicode_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Str(arg_o); @@ -3199,7 +3957,7 @@ PyStackRef_CLOSE(arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3215,7 +3973,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -3228,6 +3986,13 @@ } TARGET(CALL_TUPLE_1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_TUPLE_1; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_TUPLE_1); @@ -3246,8 +4011,16 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null), CALL); - DEOPT_IF(callable_o != (PyObject *)&PyTuple_Type, CALL); + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (callable_o != (PyObject *)&PyTuple_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); @@ -3258,7 +4031,7 @@ PyStackRef_CLOSE(arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3274,7 +4047,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -3287,6 +4060,13 @@ } TARGET(CALL_TYPE_1) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CALL_TYPE_1; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_TYPE_1); @@ -3303,8 +4083,16 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null), CALL); - DEOPT_IF(callable_o != (PyObject *)&PyType_Type, CALL); + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (callable_o != (PyObject *)&PyType_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectSteal(Py_NewRef(Py_TYPE(arg_o))); stack_pointer[-3] = res; @@ -3317,6 +4105,11 @@ } TARGET(CHECK_EG_MATCH) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CHECK_EG_MATCH; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CHECK_EG_MATCH); @@ -3334,7 +4127,7 @@ if (err < 0) { PyStackRef_CLOSE(exc_value_st); PyStackRef_CLOSE(match_type_st); - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } PyObject *match_o = NULL; PyObject *rest_o = NULL; @@ -3345,11 +4138,11 @@ PyStackRef_CLOSE(exc_value_st); PyStackRef_CLOSE(match_type_st); if (res < 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } assert((match_o == NULL) == (rest_o == NULL)); if (match_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } if (!Py_IsNone(match_o)) { stack_pointer += -2; @@ -3368,6 +4161,11 @@ } TARGET(CHECK_EXC_MATCH) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CHECK_EXC_MATCH; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CHECK_EXC_MATCH); @@ -3384,7 +4182,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { PyStackRef_CLOSE(right); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyErr_GivenExceptionMatches(left_o, right_o); @@ -3396,8 +4194,14 @@ } TARGET(CLEANUP_THROW) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CLEANUP_THROW; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CLEANUP_THROW); _PyStackRef sub_iter_st; @@ -3409,7 +4213,9 @@ last_sent_val_st = stack_pointer[-2]; sub_iter_st = stack_pointer[-3]; PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); + #ifndef Py_TAIL_CALL_INTERP assert(throwflag); + #endif assert(exc_value && PyExceptionInstance_Check(exc_value)); _PyFrame_SetStackPointer(frame, stack_pointer); int matches = PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration); @@ -3427,7 +4233,7 @@ monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); _PyFrame_SetStackPointer(frame, stack_pointer); - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } stack_pointer[-3] = none; stack_pointer[-2] = value; @@ -3437,6 +4243,11 @@ } TARGET(COMPARE_OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COMPARE_OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP); @@ -3475,7 +4286,7 @@ PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } if (oparg & 16) { stack_pointer += -2; @@ -3485,7 +4296,7 @@ Py_DECREF(res_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_bool < 0) { - goto error; + JUMP_TO_LABEL(error); } res = res_bool ? PyStackRef_True : PyStackRef_False; } @@ -3502,6 +4313,13 @@ } TARGET(COMPARE_OP_FLOAT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COMPARE_OP_FLOAT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_FLOAT); @@ -3515,8 +4333,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyFloat_CheckExact(left_o), COMPARE_OP); - DEOPT_IF(!PyFloat_CheckExact(right_o), COMPARE_OP); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + if (!PyFloat_CheckExact(right_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } } /* Skip 1 cache entry */ // _COMPARE_OP_FLOAT @@ -3540,6 +4366,13 @@ } TARGET(COMPARE_OP_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COMPARE_OP_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_INT); @@ -3553,16 +4386,32 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyLong_CheckExact(left_o), COMPARE_OP); - DEOPT_IF(!PyLong_CheckExact(right_o), COMPARE_OP); + if (!PyLong_CheckExact(left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + if (!PyLong_CheckExact(right_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } } /* Skip 1 cache entry */ // _COMPARE_OP_INT { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left_o), COMPARE_OP); - DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right_o), COMPARE_OP); + if (!_PyLong_IsCompact((PyLongObject *)left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + if (!_PyLong_IsCompact((PyLongObject *)right_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } STAT_INC(COMPARE_OP, hit); assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 && _PyLong_DigitCount((PyLongObject *)right_o) <= 1); @@ -3582,6 +4431,13 @@ } TARGET(COMPARE_OP_STR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COMPARE_OP_STR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP_STR); @@ -3595,8 +4451,16 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyUnicode_CheckExact(left_o), COMPARE_OP); - DEOPT_IF(!PyUnicode_CheckExact(right_o), COMPARE_OP); + if (!PyUnicode_CheckExact(left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + if (!PyUnicode_CheckExact(right_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } } /* Skip 1 cache entry */ // _COMPARE_OP_STR @@ -3621,6 +4485,11 @@ } TARGET(CONTAINS_OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CONTAINS_OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(CONTAINS_OP); @@ -3658,7 +4527,7 @@ PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); if (res < 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; } @@ -3669,6 +4538,13 @@ } TARGET(CONTAINS_OP_DICT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CONTAINS_OP_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(CONTAINS_OP_DICT); @@ -3681,7 +4557,11 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!PyDict_CheckExact(right_o), CONTAINS_OP); + if (!PyDict_CheckExact(right_o)) { + UPDATE_MISS_STATS(CONTAINS_OP); + assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); + JUMP_TO_PREDICTED(CONTAINS_OP); + } STAT_INC(CONTAINS_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyDict_Contains(right_o, left_o); @@ -3689,7 +4569,7 @@ PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); if (res < 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; @@ -3699,6 +4579,13 @@ } TARGET(CONTAINS_OP_SET) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CONTAINS_OP_SET; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(CONTAINS_OP_SET); @@ -3711,7 +4598,11 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - DEOPT_IF(!(PySet_CheckExact(right_o) || PyFrozenSet_CheckExact(right_o)), CONTAINS_OP); + if (!(PySet_CheckExact(right_o) || PyFrozenSet_CheckExact(right_o))) { + UPDATE_MISS_STATS(CONTAINS_OP); + assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); + JUMP_TO_PREDICTED(CONTAINS_OP); + } STAT_INC(CONTAINS_OP, hit); // Note: both set and frozenset use the same seq_contains method! _PyFrame_SetStackPointer(frame, stack_pointer); @@ -3720,7 +4611,7 @@ PyStackRef_CLOSE(left); PyStackRef_CLOSE(right); if (res < 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; stack_pointer[-2] = b; @@ -3730,6 +4621,11 @@ } TARGET(CONVERT_VALUE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = CONVERT_VALUE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CONVERT_VALUE); @@ -3748,7 +4644,7 @@ PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); if (result_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } result = PyStackRef_FromPyObjectSteal(result_o); stack_pointer[0] = result; @@ -3758,6 +4654,11 @@ } TARGET(COPY) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COPY; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(COPY); @@ -3773,6 +4674,11 @@ } TARGET(COPY_FREE_VARS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = COPY_FREE_VARS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(COPY_FREE_VARS); @@ -3791,6 +4697,11 @@ } TARGET(DELETE_ATTR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_ATTR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_ATTR); @@ -3802,7 +4713,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); if (err) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -3810,6 +4721,11 @@ } TARGET(DELETE_DEREF) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_DEREF; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_DEREF); @@ -3821,7 +4737,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(oldobj); @@ -3830,6 +4746,11 @@ } TARGET(DELETE_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_FAST); @@ -3841,7 +4762,7 @@ PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } _PyStackRef tmp = GETLOCAL(oparg); GETLOCAL(oparg) = PyStackRef_NULL; @@ -3852,6 +4773,11 @@ } TARGET(DELETE_GLOBAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_GLOBAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_GLOBAL); @@ -3861,19 +4787,24 @@ stack_pointer = _PyFrame_GetStackPointer(frame); // Can't use ERROR_IF here. if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (err == 0) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } DISPATCH(); } TARGET(DELETE_NAME) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_NAME; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_NAME); @@ -3885,7 +4816,7 @@ _PyErr_Format(tstate, PyExc_SystemError, "no locals when deleting %R", name); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_DelItem(ns, name); @@ -3897,12 +4828,17 @@ NAME_ERROR_MSG, name); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } DISPATCH(); } TARGET(DELETE_SUBSCR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DELETE_SUBSCR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_SUBSCR); @@ -3918,7 +4854,7 @@ PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); if (err) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -3926,6 +4862,11 @@ } TARGET(DICT_MERGE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DICT_MERGE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DICT_MERGE); @@ -3946,7 +4887,7 @@ _PyEval_FormatKwargsError(tstate, callable_o, update_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(update); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } PyStackRef_CLOSE(update); stack_pointer += -1; @@ -3955,6 +4896,11 @@ } TARGET(DICT_UPDATE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = DICT_UPDATE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DICT_UPDATE); @@ -3979,7 +4925,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(update); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } PyStackRef_CLOSE(update); stack_pointer += -1; @@ -3988,8 +4934,14 @@ } TARGET(END_ASYNC_FOR) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = END_ASYNC_FOR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(END_ASYNC_FOR); _PyStackRef awaitable_st; @@ -4012,7 +4964,7 @@ monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); _PyFrame_SetStackPointer(frame, stack_pointer); - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -4020,6 +4972,11 @@ } TARGET(END_FOR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = END_FOR; + (void)(opcode); next_instr += 1; INSTRUCTION_STATS(END_FOR); _PyStackRef value; @@ -4038,6 +4995,11 @@ } TARGET(END_SEND) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = END_SEND; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(END_SEND); @@ -4056,11 +5018,16 @@ } TARGET(ENTER_EXECUTOR) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = ENTER_EXECUTOR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); - opcode = ENTER_EXECUTOR; #ifdef _Py_TIER2 PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; @@ -4090,6 +5057,11 @@ } TARGET(EXIT_INIT_CHECK) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = EXIT_INIT_CHECK; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(EXIT_INIT_CHECK); @@ -4102,7 +5074,7 @@ "__init__() should return None, not '%.200s'", Py_TYPE(PyStackRef_AsPyObjectBorrow(should_be_none))->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -4110,10 +5082,14 @@ } TARGET(EXTENDED_ARG) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = EXTENDED_ARG; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(EXTENDED_ARG); - opcode = EXTENDED_ARG; assert(oparg); opcode = next_instr->op.code; oparg = oparg << 8 | next_instr->op.arg; @@ -4122,6 +5098,11 @@ } TARGET(FORMAT_SIMPLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FORMAT_SIMPLE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(FORMAT_SIMPLE); @@ -4141,7 +5122,7 @@ PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4157,6 +5138,11 @@ } TARGET(FORMAT_WITH_SPEC) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FORMAT_WITH_SPEC; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(FORMAT_WITH_SPEC); @@ -4171,7 +5157,7 @@ PyStackRef_CLOSE(value); PyStackRef_CLOSE(fmt_spec); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -4181,6 +5167,11 @@ } TARGET(FOR_ITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FOR_ITER; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER); @@ -4219,7 +5210,7 @@ int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration); stack_pointer = _PyFrame_GetStackPointer(frame); if (!matches) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_MonitorRaise(tstate, frame, this_instr); @@ -4243,6 +5234,13 @@ } TARGET(FOR_ITER_GEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FOR_ITER_GEN; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_GEN); @@ -4253,14 +5251,26 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, FOR_ITER); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } } // _FOR_ITER_GEN_FRAME { iter = stack_pointer[-1]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); + if (Py_TYPE(gen) != &PyGen_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + if (gen->gi_frame_state >= FRAME_EXECUTING) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } STAT_INC(FOR_ITER, hit); gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, PyStackRef_None); @@ -4291,6 +5301,13 @@ } TARGET(FOR_ITER_LIST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FOR_ITER_LIST; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_LIST); @@ -4301,7 +5318,11 @@ // _ITER_CHECK_LIST { iter = stack_pointer[-1]; - DEOPT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyListIter_Type, FOR_ITER); + if (Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyListIter_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } } // _ITER_JUMP_LIST { @@ -4342,6 +5363,13 @@ } TARGET(FOR_ITER_RANGE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FOR_ITER_RANGE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_RANGE); @@ -4353,7 +5381,11 @@ { iter = stack_pointer[-1]; _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); - DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); + if (Py_TYPE(r) != &PyRangeIter_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } } // _ITER_JUMP_RANGE { @@ -4376,7 +5408,7 @@ r->len--; PyObject *res = PyLong_FromLong(value); if (res == NULL) { - goto error; + JUMP_TO_LABEL(error); } next = PyStackRef_FromPyObjectSteal(res); } @@ -4387,6 +5419,13 @@ } TARGET(FOR_ITER_TUPLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = FOR_ITER_TUPLE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER_TUPLE); @@ -4397,7 +5436,11 @@ // _ITER_CHECK_TUPLE { iter = stack_pointer[-1]; - DEOPT_IF(Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyTupleIter_Type, FOR_ITER); + if (Py_TYPE(PyStackRef_AsPyObjectBorrow(iter)) != &PyTupleIter_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } } // _ITER_JUMP_TUPLE { @@ -4435,6 +5478,11 @@ } TARGET(GET_AITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_AITER; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_AITER); @@ -4456,14 +5504,14 @@ type->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(obj); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } _PyFrame_SetStackPointer(frame, stack_pointer); iter_o = (*getter)(obj_o); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(obj); if (iter_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } if (Py_TYPE(iter_o)->tp_as_async == NULL || Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { @@ -4476,7 +5524,7 @@ Py_TYPE(iter_o)->tp_name); Py_DECREF(iter_o); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; @@ -4484,6 +5532,11 @@ } TARGET(GET_ANEXT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_ANEXT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_ANEXT); @@ -4494,7 +5547,7 @@ PyObject *awaitable_o = _PyEval_GetANext(PyStackRef_AsPyObjectBorrow(aiter)); stack_pointer = _PyFrame_GetStackPointer(frame); if (awaitable_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } awaitable = PyStackRef_FromPyObjectSteal(awaitable_o); stack_pointer[0] = awaitable; @@ -4504,6 +5557,11 @@ } TARGET(GET_AWAITABLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_AWAITABLE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_AWAITABLE); @@ -4515,7 +5573,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); if (iter_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; @@ -4523,6 +5581,11 @@ } TARGET(GET_ITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_ITER; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_ITER); @@ -4535,7 +5598,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); if (iter_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[-1] = iter; @@ -4543,6 +5606,11 @@ } TARGET(GET_LEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_LEN; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_LEN); @@ -4554,11 +5622,11 @@ Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); stack_pointer = _PyFrame_GetStackPointer(frame); if (len_i < 0) { - goto error; + JUMP_TO_LABEL(error); } PyObject *len_o = PyLong_FromSsize_t(len_i); if (len_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } len = PyStackRef_FromPyObjectSteal(len_o); stack_pointer[0] = len; @@ -4568,6 +5636,11 @@ } TARGET(GET_YIELD_FROM_ITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = GET_YIELD_FROM_ITER; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_YIELD_FROM_ITER); @@ -4586,7 +5659,7 @@ "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } iter = iterable; } @@ -4600,7 +5673,7 @@ PyObject *iter_o = PyObject_GetIter(iterable_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (iter_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } iter = PyStackRef_FromPyObjectSteal(iter_o); PyStackRef_CLOSE(iterable); @@ -4611,6 +5684,11 @@ } TARGET(IMPORT_FROM) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = IMPORT_FROM; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IMPORT_FROM); @@ -4622,7 +5700,7 @@ PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; @@ -4632,6 +5710,11 @@ } TARGET(IMPORT_NAME) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = IMPORT_NAME; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IMPORT_NAME); @@ -4649,7 +5732,7 @@ PyStackRef_CLOSE(level); PyStackRef_CLOSE(fromlist); if (res_o == NULL) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-2] = res; @@ -4659,11 +5742,16 @@ } TARGET(INSTRUMENTED_CALL) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_CALL; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL); - opcode = INSTRUMENTED_CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -4716,7 +5804,7 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } // _DO_CALL @@ -4750,7 +5838,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } frame->return_offset = 4 ; DISPATCH_INLINED(new_frame); @@ -4765,7 +5853,7 @@ } stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Vectorcall( @@ -4806,7 +5894,7 @@ if (res_o == NULL) { stack_pointer += -2 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4822,7 +5910,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1 + oparg; assert(WITHIN_STACK_BOUNDS()); @@ -4835,11 +5923,16 @@ } TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_CALL_FUNCTION_EX; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); - opcode = INSTRUMENTED_CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; _PyStackRef kwargs_in; @@ -4865,13 +5958,13 @@ int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *tuple_o = PySequence_Tuple(callargs_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (tuple_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } kwargs_out = kwargs_in; stack_pointer += -2; @@ -4912,7 +6005,7 @@ frame, this_instr, func, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); result_o = PyObject_Call(func, callargs, kwargs); @@ -4961,7 +6054,7 @@ stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } assert( 1 == 1); frame->return_offset = 1; @@ -4993,7 +6086,7 @@ PyStackRef_CLOSE(func_st); stack_pointer = _PyFrame_GetStackPointer(frame); if (result_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } result = PyStackRef_FromPyObjectSteal(result_o); } @@ -5009,7 +6102,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -5022,11 +6115,16 @@ } TARGET(INSTRUMENTED_CALL_KW) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_CALL_KW; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); - opcode = INSTRUMENTED_CALL_KW; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -5063,7 +6161,7 @@ frame, this_instr, function, arg); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } // _MAYBE_EXPAND_METHOD_KW @@ -5125,7 +6223,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + JUMP_TO_LABEL(error); } assert( 4 == 1 + INLINE_CACHE_ENTRIES_CALL_KW); frame->return_offset = 4 ; @@ -5142,7 +6240,7 @@ PyStackRef_CLOSE(kwnames); stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } stack_pointer[-1] = kwnames; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5184,7 +6282,7 @@ if (res_o == NULL) { stack_pointer += -3 - oparg; assert(WITHIN_STACK_BOUNDS()); - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } @@ -5195,6 +6293,11 @@ } TARGET(INSTRUMENTED_END_FOR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_END_FOR; + (void)(opcode); _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; next_instr += 1; @@ -5210,7 +6313,7 @@ int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } PyStackRef_CLOSE(value); @@ -5220,8 +6323,14 @@ } TARGET(INSTRUMENTED_END_SEND) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_END_SEND; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_END_SEND); _PyStackRef receiver; @@ -5235,7 +6344,7 @@ int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } val = value; @@ -5249,8 +6358,14 @@ } TARGET(INSTRUMENTED_FOR_ITER) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_FOR_ITER; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); /* Skip 1 cache entry */ @@ -5269,7 +6384,7 @@ int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration); stack_pointer = _PyFrame_GetStackPointer(frame); if (!matches) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_MonitorRaise(tstate, frame, this_instr); @@ -5286,17 +6401,22 @@ } TARGET(INSTRUMENTED_INSTRUCTION) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_INSTRUCTION; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); - opcode = INSTRUMENTED_INSTRUCTION; _PyFrame_SetStackPointer(frame, stack_pointer); int next_opcode = _Py_call_instrumentation_instruction( tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); if (next_opcode < 0) { - goto error; + JUMP_TO_LABEL(error); } next_instr = this_instr; if (_PyOpcode_Caches[next_opcode]) { @@ -5308,8 +6428,14 @@ } TARGET(INSTRUMENTED_JUMP_BACKWARD) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_JUMP_BACKWARD; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); /* Skip 1 cache entry */ @@ -5322,7 +6448,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -5334,8 +6460,14 @@ } TARGET(INSTRUMENTED_JUMP_FORWARD) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_JUMP_FORWARD; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); @@ -5343,12 +6475,17 @@ } TARGET(INSTRUMENTED_LINE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_LINE; + (void)(opcode); _Py_CODEUNIT* const prev_instr = frame->instr_ptr; - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_LINE); - opcode = INSTRUMENTED_LINE; int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); @@ -5364,7 +6501,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (original_opcode < 0) { next_instr = this_instr+1; - goto error; + JUMP_TO_LABEL(error); } next_instr = frame->instr_ptr; if (next_instr != this_instr) { @@ -5382,11 +6519,16 @@ } TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_LOAD_SUPER_ATTR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); - opcode = INSTRUMENTED_LOAD_SUPER_ATTR; _PyStackRef global_super_st; _PyStackRef class_st; _PyStackRef self_st; @@ -5412,7 +6554,7 @@ PyStackRef_CLOSE(global_super_st); PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } } // we make no attempt to optimize here; specializations should @@ -5447,7 +6589,7 @@ PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); if (super == NULL) { - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); stack_pointer += -3; @@ -5457,7 +6599,7 @@ Py_DECREF(super); stack_pointer = _PyFrame_GetStackPointer(frame); if (attr_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } attr = PyStackRef_FromPyObjectSteal(attr_o); } @@ -5473,9 +6615,15 @@ } TARGET(INSTRUMENTED_NOT_TAKEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_NOT_TAKEN; + (void)(opcode); _Py_CODEUNIT* const prev_instr = frame->instr_ptr; - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN); (void)this_instr; // INSTRUMENTED_JUMP requires this_instr @@ -5484,9 +6632,15 @@ } TARGET(INSTRUMENTED_POP_ITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_POP_ITER; + (void)(opcode); _Py_CODEUNIT* const prev_instr = frame->instr_ptr; - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_POP_ITER); _PyStackRef iter; @@ -5501,8 +6655,14 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_POP_JUMP_IF_FALSE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); /* Skip 1 cache entry */ @@ -5517,8 +6677,14 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_POP_JUMP_IF_NONE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); /* Skip 1 cache entry */ @@ -5537,8 +6703,14 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_POP_JUMP_IF_NOT_NONE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); /* Skip 1 cache entry */ @@ -5555,8 +6727,14 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_POP_JUMP_IF_TRUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); /* Skip 1 cache entry */ @@ -5571,8 +6749,14 @@ } TARGET(INSTRUMENTED_RESUME) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_RESUME; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_RESUME); // _LOAD_BYTECODE @@ -5585,7 +6769,7 @@ _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); stack_pointer = _PyFrame_GetStackPointer(frame); if (bytecode == NULL) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); @@ -5609,7 +6793,7 @@ int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } next_instr = this_instr; DISPATCH(); @@ -5626,7 +6810,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -5638,7 +6822,7 @@ tstate, oparg > 0, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } if (frame->instr_ptr != this_instr) { /* Instrumentation has jumped */ @@ -5649,8 +6833,14 @@ } TARGET(INSTRUMENTED_RETURN_VALUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_RETURN_VALUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); _PyStackRef val; @@ -5665,7 +6855,7 @@ frame, this_instr, PyStackRef_AsPyObjectBorrow(val)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } // _RETURN_VALUE @@ -5694,8 +6884,14 @@ } TARGET(INSTRUMENTED_YIELD_VALUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INSTRUMENTED_YIELD_VALUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); _PyStackRef val; @@ -5710,7 +6906,7 @@ frame, this_instr, PyStackRef_AsPyObjectBorrow(val)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } if (frame->instr_ptr != this_instr) { next_instr = frame->instr_ptr; @@ -5761,6 +6957,11 @@ } TARGET(INTERPRETER_EXIT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = INTERPRETER_EXIT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INTERPRETER_EXIT); @@ -5780,6 +6981,11 @@ } TARGET(IS_OP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = IS_OP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IS_OP); @@ -5799,6 +7005,11 @@ } TARGET(JUMP_BACKWARD) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = JUMP_BACKWARD; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(JUMP_BACKWARD); @@ -5826,7 +7037,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -5844,8 +7055,14 @@ } TARGET(JUMP_BACKWARD_JIT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = JUMP_BACKWARD_JIT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(JUMP_BACKWARD_JIT); static_assert(1 == 1, "incorrect cache size"); @@ -5859,7 +7076,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -5891,7 +7108,7 @@ if (optimized <= 0) { this_instr[1].counter = restart_backoff_counter(counter); if (optimized < 0) { - goto error; + JUMP_TO_LABEL(error); } } else { @@ -5912,6 +7129,11 @@ } TARGET(JUMP_BACKWARD_NO_INTERRUPT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = JUMP_BACKWARD_NO_INTERRUPT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); @@ -5926,6 +7148,11 @@ } TARGET(JUMP_BACKWARD_NO_JIT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = JUMP_BACKWARD_NO_JIT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(JUMP_BACKWARD_NO_JIT); @@ -5940,7 +7167,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -5958,6 +7185,11 @@ } TARGET(JUMP_FORWARD) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = JUMP_FORWARD; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(JUMP_FORWARD); @@ -5966,6 +7198,11 @@ } TARGET(LIST_APPEND) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LIST_APPEND; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LIST_APPEND); @@ -5976,7 +7213,7 @@ int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), PyStackRef_AsPyObjectSteal(v)); if (err < 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -5984,6 +7221,11 @@ } TARGET(LIST_EXTEND) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LIST_EXTEND; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LIST_EXTEND); @@ -6011,7 +7253,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } PyStackRef_CLOSE(iterable_st); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } assert(Py_IsNone(none_val)); PyStackRef_CLOSE(iterable_st); @@ -6021,6 +7263,11 @@ } TARGET(LOAD_ATTR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR); @@ -6077,7 +7324,7 @@ */ PyStackRef_CLOSE(owner); if (attr_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } self_or_null[0] = PyStackRef_NULL; } @@ -6089,7 +7336,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(owner); if (attr_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } } attr = PyStackRef_FromPyObjectSteal(attr_o); @@ -6101,8 +7348,14 @@ } TARGET(LOAD_ATTR_CLASS) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_CLASS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_CLASS); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6115,9 +7368,17 @@ owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(!PyType_Check(owner_o), LOAD_ATTR); + if (!PyType_Check(owner_o)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } /* Skip 2 cache entries */ // _LOAD_ATTR_CLASS @@ -6140,8 +7401,14 @@ } TARGET(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6154,16 +7421,28 @@ owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(!PyType_Check(owner_o), LOAD_ATTR); + if (!PyType_Check(owner_o)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_TYPE_VERSION { uint32_t type_version = read_u32(&this_instr[4].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _LOAD_ATTR_CLASS { @@ -6185,8 +7464,14 @@ } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6198,17 +7483,33 @@ PyObject *getattribute = read_obj(&this_instr[6].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } PyTypeObject *cls = Py_TYPE(owner_o); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; assert(func_version != 0); - DEOPT_IF(f->func_version != func_version, LOAD_ATTR); + if (f->func_version != func_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } PyCodeObject *code = (PyCodeObject *)f->func_code; assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } STAT_INC(LOAD_ATTR, hit); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked( @@ -6222,8 +7523,14 @@ } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_INSTANCE_VALUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6237,14 +7544,22 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _CHECK_MANAGED_OBJECT_HAS_VALUES { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid), LOAD_ATTR); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _LOAD_ATTR_INSTANCE_VALUE { @@ -6252,10 +7567,18 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); - DEOPT_IF(attr_o == NULL, LOAD_ATTR); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } #ifdef Py_GIL_DISABLED if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) { - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } #else attr = PyStackRef_FromPyObjectNew(attr_o); @@ -6278,8 +7601,14 @@ } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_METHOD_LAZY_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6293,7 +7622,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _CHECK_ATTR_METHOD_LAZY_DICT { @@ -6301,7 +7634,11 @@ char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); /* This object has a __dict__, just not yet created */ - DEOPT_IF(dict != NULL, LOAD_ATTR); + if (dict != NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } /* Skip 1 cache entry */ // _LOAD_ATTR_METHOD_LAZY_DICT @@ -6322,8 +7659,14 @@ } TARGET(LOAD_ATTR_METHOD_NO_DICT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_METHOD_NO_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6337,7 +7680,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_NO_DICT @@ -6359,8 +7706,14 @@ } TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_METHOD_WITH_VALUES; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6374,14 +7727,22 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyDictValues *ivs = _PyObject_InlineValues(owner_o); - DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid), LOAD_ATTR); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_KEYS_VERSION { @@ -6389,7 +7750,11 @@ PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _LOAD_ATTR_METHOD_WITH_VALUES { @@ -6410,8 +7775,14 @@ } TARGET(LOAD_ATTR_MODULE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_MODULE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_MODULE); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6425,11 +7796,19 @@ owner = stack_pointer[-1]; uint32_t dict_version = read_u32(&this_instr[2].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro, LOAD_ATTR); + if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } mod_keys = keys; } // _LOAD_ATTR_MODULE_FROM_KEYS @@ -6440,11 +7819,19 @@ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index; PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); // Clear mod_keys from stack in case we need to deopt - DEOPT_IF(attr_o == NULL, LOAD_ATTR); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); if (!increfed) { - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } #else Py_INCREF(attr_o); @@ -6468,8 +7855,14 @@ } TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_NONDESCRIPTOR_NO_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6482,7 +7875,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT @@ -6500,8 +7897,14 @@ } TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6514,14 +7917,22 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyDictValues *ivs = _PyObject_InlineValues(owner_o); - DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid), LOAD_ATTR); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_KEYS_VERSION { @@ -6529,7 +7940,11 @@ PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { @@ -6545,8 +7960,14 @@ } TARGET(LOAD_ATTR_PROPERTY) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_PROPERTY; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6555,7 +7976,11 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _GUARD_TYPE_VERSION { @@ -6563,7 +7988,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } /* Skip 2 cache entries */ // _LOAD_ATTR_PROPERTY_FRAME @@ -6573,10 +8002,26 @@ assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; PyCodeObject *code = (PyCodeObject *)f->func_code; - DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED, LOAD_ATTR); - DEOPT_IF(code->co_kwonlyargcount, LOAD_ATTR); - DEOPT_IF(code->co_argcount != 1, LOAD_ATTR); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + if (code->co_kwonlyargcount) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + if (code->co_argcount != 1) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } STAT_INC(LOAD_ATTR, hit); new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); new_frame->localsplus[0] = owner; @@ -6611,8 +8056,14 @@ } TARGET(LOAD_ATTR_SLOT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_SLOT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_SLOT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6626,7 +8077,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _LOAD_ATTR_SLOT { @@ -6634,10 +8089,18 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **addr = (PyObject **)((char *)owner_o + index); PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); - DEOPT_IF(attr_o == NULL, LOAD_ATTR); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); - DEOPT_IF(!increfed, LOAD_ATTR); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } #else attr = PyStackRef_FromPyObjectNew(attr_o); #endif @@ -6657,8 +8120,14 @@ } TARGET(LOAD_ATTR_WITH_HINT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_ATTR_WITH_HINT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); @@ -6673,14 +8142,22 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, LOAD_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } // _CHECK_ATTR_WITH_HINT { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(dict_o == NULL, LOAD_ATTR); + if (dict_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } assert(PyDict_CheckExact((PyObject *)dict_o)); dict = dict_o; } @@ -6689,26 +8166,46 @@ uint16_t hint = read_u16(&this_instr[4].cache); PyObject *attr_o; if (!LOCK_OBJECT(dict)) { - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } if (hint >= (size_t)dict->ma_keys->dk_nentries) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; if (ep->me_key != name) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } attr_o = ep->me_value; if (attr_o == NULL) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, LOAD_ATTR); + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } } STAT_INC(LOAD_ATTR, hit); attr = PyStackRef_FromPyObjectNew(attr_o); @@ -6728,6 +8225,11 @@ } TARGET(LOAD_BUILD_CLASS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_BUILD_CLASS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_BUILD_CLASS); @@ -6737,14 +8239,14 @@ int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (bc_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetString(tstate, PyExc_NameError, "__build_class__ not found"); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } bc = PyStackRef_FromPyObjectSteal(bc_o); stack_pointer[0] = bc; @@ -6754,6 +8256,11 @@ } TARGET(LOAD_COMMON_CONSTANT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_COMMON_CONSTANT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); @@ -6776,6 +8283,11 @@ } TARGET(LOAD_CONST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_CONST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST); @@ -6809,6 +8321,11 @@ } TARGET(LOAD_CONST_IMMORTAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_CONST_IMMORTAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST_IMMORTAL); @@ -6824,6 +8341,11 @@ } TARGET(LOAD_CONST_MORTAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_CONST_MORTAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST_MORTAL); @@ -6838,6 +8360,11 @@ } TARGET(LOAD_DEREF) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_DEREF; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_DEREF); @@ -6848,7 +8375,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } value = PyStackRef_FromPyObjectSteal(value_o); stack_pointer[0] = value; @@ -6858,6 +8385,11 @@ } TARGET(LOAD_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST); @@ -6871,6 +8403,11 @@ } TARGET(LOAD_FAST_AND_CLEAR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FAST_AND_CLEAR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); @@ -6884,6 +8421,11 @@ } TARGET(LOAD_FAST_CHECK) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FAST_CHECK; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_CHECK); @@ -6896,7 +8438,7 @@ PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } value = PyStackRef_DUP(value_s); stack_pointer[0] = value; @@ -6906,6 +8448,11 @@ } TARGET(LOAD_FAST_LOAD_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FAST_LOAD_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); @@ -6923,6 +8470,11 @@ } TARGET(LOAD_FROM_DICT_OR_DEREF) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FROM_DICT_OR_DEREF; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); @@ -6939,7 +8491,7 @@ int err = PyMapping_GetOptionalItem(class_dict, name, &value_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (!value_o) { PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); @@ -6948,7 +8500,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } } stack_pointer += -1; @@ -6964,6 +8516,11 @@ } TARGET(LOAD_FROM_DICT_OR_GLOBALS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_FROM_DICT_OR_GLOBALS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); @@ -6977,7 +8534,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(mod_or_class_dict); if (err < 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } if (v_o == NULL) { if (PyDict_CheckExact(GLOBALS()) @@ -6999,7 +8556,7 @@ NAME_ERROR_MSG, name); stack_pointer = _PyFrame_GetStackPointer(frame); } - goto error; + JUMP_TO_LABEL(error); } } else { @@ -7011,7 +8568,7 @@ int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (v_o == NULL) { /* namespace 2: builtins */ @@ -7019,7 +8576,7 @@ int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (v_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -7027,7 +8584,7 @@ tstate, PyExc_NameError, NAME_ERROR_MSG, name); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } } } @@ -7040,6 +8597,11 @@ } TARGET(LOAD_GLOBAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_GLOBAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL); @@ -7076,7 +8638,7 @@ _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); stack_pointer = _PyFrame_GetStackPointer(frame); if (PyStackRef_IsNull(*res)) { - goto error; + JUMP_TO_LABEL(error); } } // _PUSH_NULL_CONDITIONAL @@ -7090,8 +8652,14 @@ } TARGET(LOAD_GLOBAL_BUILTIN) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_GLOBAL_BUILTIN; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); @@ -7103,18 +8671,34 @@ { uint16_t version = read_u16(&this_instr[2].cache); PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version, LOAD_GLOBAL); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } assert(DK_IS_UNICODE(keys)); } // _GUARD_BUILTINS_VERSION_PUSH_KEYS { uint16_t version = read_u16(&this_instr[3].cache); PyDictObject *dict = (PyDictObject *)BUILTINS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version, LOAD_GLOBAL); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } builtins_keys = keys; assert(DK_IS_UNICODE(builtins_keys)); } @@ -7123,10 +8707,18 @@ uint16_t index = read_u16(&this_instr[4].cache); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(builtins_keys); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); - DEOPT_IF(res_o == NULL, LOAD_GLOBAL); + if (res_o == NULL) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } #if Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); - DEOPT_IF(!increfed, LOAD_GLOBAL); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } #else Py_INCREF(res_o); res = PyStackRef_FromPyObjectSteal(res_o); @@ -7145,8 +8737,14 @@ } TARGET(LOAD_GLOBAL_MODULE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_GLOBAL_MODULE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); @@ -7158,9 +8756,17 @@ { uint16_t version = read_u16(&this_instr[2].cache); PyDictObject *dict = (PyDictObject *)GLOBALS(); - DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version, LOAD_GLOBAL); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } globals_keys = keys; assert(DK_IS_UNICODE(globals_keys)); } @@ -7170,10 +8776,18 @@ uint16_t index = read_u16(&this_instr[4].cache); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(globals_keys); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); - DEOPT_IF(res_o == NULL, LOAD_GLOBAL); + if (res_o == NULL) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } #if Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); - DEOPT_IF(!increfed, LOAD_GLOBAL); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } #else Py_INCREF(res_o); res = PyStackRef_FromPyObjectSteal(res_o); @@ -7192,6 +8806,11 @@ } TARGET(LOAD_LOCALS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_LOCALS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_LOCALS); @@ -7202,7 +8821,7 @@ _PyErr_SetString(tstate, PyExc_SystemError, "no locals found"); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } locals = PyStackRef_FromPyObjectNew(l); stack_pointer[0] = locals; @@ -7212,6 +8831,11 @@ } TARGET(LOAD_NAME) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_NAME; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_NAME); @@ -7221,7 +8845,7 @@ PyObject *v_o = _PyEval_LoadName(tstate, frame, name); stack_pointer = _PyFrame_GetStackPointer(frame); if (v_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; @@ -7231,6 +8855,11 @@ } TARGET(LOAD_SMALL_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_SMALL_INT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_SMALL_INT); @@ -7245,6 +8874,11 @@ } TARGET(LOAD_SPECIAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_SPECIAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_SPECIAL); @@ -7269,7 +8903,7 @@ Py_TYPE(owner_o)->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); } - goto error; + JUMP_TO_LABEL(error); } attr = PyStackRef_FromPyObjectSteal(attr_o); self_or_null = self_or_null_o == NULL ? @@ -7282,13 +8916,17 @@ } TARGET(LOAD_SUPER_ATTR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_SUPER_ATTR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR); PREDICTED_LOAD_SUPER_ATTR:; _Py_CODEUNIT* const this_instr = next_instr - 2; (void)this_instr; - opcode = LOAD_SUPER_ATTR; _PyStackRef global_super_st; _PyStackRef class_st; _PyStackRef self_st; @@ -7330,7 +8968,7 @@ PyStackRef_CLOSE(global_super_st); PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } } // we make no attempt to optimize here; specializations should @@ -7365,7 +9003,7 @@ PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); if (super == NULL) { - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); stack_pointer += -3; @@ -7375,7 +9013,7 @@ Py_DECREF(super); stack_pointer = _PyFrame_GetStackPointer(frame); if (attr_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } attr = PyStackRef_FromPyObjectSteal(attr_o); } @@ -7391,6 +9029,13 @@ } TARGET(LOAD_SUPER_ATTR_ATTR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_SUPER_ATTR_ATTR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); @@ -7407,8 +9052,16 @@ PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); assert(!(oparg & 1)); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + if (global_super != (PyObject *)&PySuper_Type) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + if (!PyType_Check(class)) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } STAT_INC(LOAD_SUPER_ATTR, hit); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -7418,7 +9071,7 @@ PyStackRef_CLOSE(class_st); PyStackRef_CLOSE(self_st); if (attr == NULL) { - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } attr_st = PyStackRef_FromPyObjectSteal(attr); stack_pointer[-3] = attr_st; @@ -7428,6 +9081,13 @@ } TARGET(LOAD_SUPER_ATTR_METHOD) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = LOAD_SUPER_ATTR_METHOD; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); @@ -7445,8 +9105,16 @@ PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); assert(oparg & 1); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR); - DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR); + if (global_super != (PyObject *)&PySuper_Type) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + if (!PyType_Check(class)) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } STAT_INC(LOAD_SUPER_ATTR, hit); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); PyTypeObject *cls = (PyTypeObject *)class; @@ -7456,7 +9124,7 @@ Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); stack_pointer = _PyFrame_GetStackPointer(frame); if (attr_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } if (method_found) { self_or_null = self_st; // transfer ownership @@ -7481,6 +9149,11 @@ } TARGET(MAKE_CELL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MAKE_CELL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAKE_CELL); @@ -7489,7 +9162,7 @@ PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); PyObject *cell = PyCell_New(initial); if (cell == NULL) { - goto error; + JUMP_TO_LABEL(error); } _PyStackRef tmp = GETLOCAL(oparg); GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); @@ -7500,6 +9173,11 @@ } TARGET(MAKE_FUNCTION) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MAKE_FUNCTION; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAKE_FUNCTION); @@ -7517,7 +9195,7 @@ PyStackRef_CLOSE(codeobj_st); stack_pointer = _PyFrame_GetStackPointer(frame); if (func_obj == NULL) { - goto error; + JUMP_TO_LABEL(error); } _PyFunction_SetVersion( func_obj, ((PyCodeObject *)codeobj)->co_version); @@ -7529,6 +9207,11 @@ } TARGET(MAP_ADD) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MAP_ADD; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAP_ADD); @@ -7550,7 +9233,7 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -7558,6 +9241,11 @@ } TARGET(MATCH_CLASS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MATCH_CLASS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_CLASS); @@ -7586,7 +9274,7 @@ } else { if (_PyErr_Occurred(tstate)) { - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } // Error! attrs = PyStackRef_None; // Failure! @@ -7598,6 +9286,11 @@ } TARGET(MATCH_KEYS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MATCH_KEYS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_KEYS); @@ -7612,7 +9305,7 @@ PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); stack_pointer = _PyFrame_GetStackPointer(frame); if (values_or_none_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); stack_pointer[0] = values_or_none; @@ -7622,6 +9315,11 @@ } TARGET(MATCH_MAPPING) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MATCH_MAPPING; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_MAPPING); @@ -7637,6 +9335,11 @@ } TARGET(MATCH_SEQUENCE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = MATCH_SEQUENCE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_SEQUENCE); @@ -7652,6 +9355,11 @@ } TARGET(NOP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = NOP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(NOP); @@ -7659,6 +9367,11 @@ } TARGET(NOT_TAKEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = NOT_TAKEN; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(NOT_TAKEN); @@ -7666,6 +9379,11 @@ } TARGET(POP_EXCEPT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_EXCEPT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_EXCEPT); @@ -7683,6 +9401,11 @@ } TARGET(POP_ITER) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_ITER; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_ITER); @@ -7695,8 +9418,14 @@ } TARGET(POP_JUMP_IF_FALSE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_JUMP_IF_FALSE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(POP_JUMP_IF_FALSE); _PyStackRef cond; @@ -7712,8 +9441,14 @@ } TARGET(POP_JUMP_IF_NONE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_JUMP_IF_NONE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(POP_JUMP_IF_NONE); _PyStackRef value; @@ -7745,8 +9480,14 @@ } TARGET(POP_JUMP_IF_NOT_NONE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_JUMP_IF_NOT_NONE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); _PyStackRef value; @@ -7778,8 +9519,14 @@ } TARGET(POP_JUMP_IF_TRUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_JUMP_IF_TRUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(POP_JUMP_IF_TRUE); _PyStackRef cond; @@ -7795,6 +9542,11 @@ } TARGET(POP_TOP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = POP_TOP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_TOP); @@ -7807,6 +9559,11 @@ } TARGET(PUSH_EXC_INFO) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = PUSH_EXC_INFO; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(PUSH_EXC_INFO); @@ -7832,6 +9589,11 @@ } TARGET(PUSH_NULL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = PUSH_NULL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(PUSH_NULL); @@ -7844,8 +9606,14 @@ } TARGET(RAISE_VARARGS) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RAISE_VARARGS; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RAISE_VARARGS); _PyStackRef *args; @@ -7864,14 +9632,20 @@ monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); _PyFrame_SetStackPointer(frame, stack_pointer); - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } - goto error; + JUMP_TO_LABEL(error); } TARGET(RERAISE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RERAISE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RERAISE); _PyStackRef *values; @@ -7897,7 +9671,7 @@ _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); Py_DECREF(exc); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); @@ -7910,10 +9684,15 @@ monitor_reraise(tstate, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); _PyFrame_SetStackPointer(frame, stack_pointer); - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } TARGET(RESERVED) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RESERVED; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESERVED); @@ -7923,6 +9702,11 @@ } TARGET(RESUME) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RESUME; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESUME); @@ -7939,7 +9723,7 @@ _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); stack_pointer = _PyFrame_GetStackPointer(frame); if (bytecode == NULL) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); @@ -7963,7 +9747,7 @@ int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } next_instr = this_instr; DISPATCH(); @@ -7988,7 +9772,7 @@ int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { - goto error; + JUMP_TO_LABEL(error); } } } @@ -7997,26 +9781,50 @@ } TARGET(RESUME_CHECK) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RESUME_CHECK; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESUME_CHECK); static_assert(0 == 0, "incorrect cache size"); #if defined(__EMSCRIPTEN__) - DEOPT_IF(_Py_emscripten_signal_clock == 0, RESUME); + if (_Py_emscripten_signal_clock == 0) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; #endif uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); assert((version & _PY_EVAL_EVENTS_MASK) == 0); - DEOPT_IF(eval_breaker != version, RESUME); + if (eval_breaker != version) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } #ifdef Py_GIL_DISABLED - DEOPT_IF(frame->tlbc_index != - ((_PyThreadStateImpl *)tstate)->tlbc_index, RESUME); + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } #endif DISPATCH(); } TARGET(RETURN_GENERATOR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RETURN_GENERATOR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_GENERATOR); @@ -8027,7 +9835,7 @@ PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); stack_pointer = _PyFrame_GetStackPointer(frame); if (gen == NULL) { - goto error; + JUMP_TO_LABEL(error); } assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -8052,6 +9860,11 @@ } TARGET(RETURN_VALUE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = RETURN_VALUE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_VALUE); @@ -8080,6 +9893,11 @@ } TARGET(SEND) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SEND; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(SEND); @@ -8159,7 +9977,7 @@ } else { PyStackRef_CLOSE(v); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } } stack_pointer += -1; @@ -8176,6 +9994,13 @@ } TARGET(SEND_GEN) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SEND_GEN; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(SEND_GEN); @@ -8187,15 +10012,27 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - DEOPT_IF(tstate->interp->eval_frame, SEND); + if (tstate->interp->eval_frame) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } } // _SEND_GEN_FRAME { v = stack_pointer[-1]; receiver = stack_pointer[-2]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); + if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + if (gen->gi_frame_state >= FRAME_EXECUTING) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } STAT_INC(SEND, hit); gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, v); @@ -8228,6 +10065,11 @@ } TARGET(SETUP_ANNOTATIONS) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SETUP_ANNOTATIONS; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SETUP_ANNOTATIONS); @@ -8237,21 +10079,21 @@ _PyErr_Format(tstate, PyExc_SystemError, "no locals found when setting up annotations"); stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } /* check if __annotations__ in locals()... */ _PyFrame_SetStackPointer(frame, stack_pointer); int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { - goto error; + JUMP_TO_LABEL(error); } if (ann_dict == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); ann_dict = PyDict_New(); stack_pointer = _PyFrame_GetStackPointer(frame); if (ann_dict == NULL) { - goto error; + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), @@ -8259,7 +10101,7 @@ Py_DECREF(ann_dict); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } } else { @@ -8271,6 +10113,11 @@ } TARGET(SET_ADD) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SET_ADD; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_ADD); @@ -8284,7 +10131,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); if (err) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -8292,6 +10139,11 @@ } TARGET(SET_FUNCTION_ATTRIBUTE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SET_FUNCTION_ATTRIBUTE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); @@ -8316,6 +10168,11 @@ } TARGET(SET_UPDATE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SET_UPDATE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_UPDATE); @@ -8329,7 +10186,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(iterable); if (err < 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -8337,6 +10194,11 @@ } TARGET(STORE_ATTR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_ATTR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR); @@ -8375,7 +10237,7 @@ PyStackRef_CLOSE(v); PyStackRef_CLOSE(owner); if (err) { - goto pop_2_error; + JUMP_TO_LABEL(pop_2_error); } } stack_pointer += -2; @@ -8384,8 +10246,14 @@ } TARGET(STORE_ATTR_INSTANCE_VALUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_ATTR_INSTANCE_VALUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); @@ -8398,11 +10266,19 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(type_version != 0); - DEOPT_IF(!LOCK_OBJECT(owner_o), STORE_ATTR); + if (!LOCK_OBJECT(owner_o)) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } PyTypeObject *tp = Py_TYPE(owner_o); if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { UNLOCK_OBJECT(owner_o); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } } // _GUARD_DORV_NO_DICT @@ -8413,7 +10289,11 @@ if (_PyObject_GetManagedDict(owner_o) || !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { UNLOCK_OBJECT(owner_o); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } } // _STORE_ATTR_INSTANCE_VALUE @@ -8443,8 +10323,14 @@ } TARGET(STORE_ATTR_SLOT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_ATTR_SLOT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_SLOT); static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); @@ -8457,14 +10343,22 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, STORE_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } // _STORE_ATTR_SLOT { value = stack_pointer[-2]; uint16_t index = read_u16(&this_instr[4].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(!LOCK_OBJECT(owner_o), STORE_ATTR); + if (!LOCK_OBJECT(owner_o)) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } char *addr = (char *)owner_o + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; @@ -8481,8 +10375,14 @@ } TARGET(STORE_ATTR_WITH_HINT) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_ATTR_WITH_HINT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); @@ -8495,7 +10395,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, STORE_ATTR); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } // _STORE_ATTR_WITH_HINT { @@ -8504,12 +10408,24 @@ PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(dict == NULL, STORE_ATTR); - DEOPT_IF(!LOCK_OBJECT(dict), STORE_ATTR); + if (dict == NULL) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + if (!LOCK_OBJECT(dict)) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } #ifdef Py_GIL_DISABLED if (dict != _PyObject_GetManagedDict(owner_o)) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } #endif assert(PyDict_CheckExact((PyObject *)dict)); @@ -8517,17 +10433,29 @@ if (hint >= (size_t)dict->ma_keys->dk_nentries || !DK_IS_UNICODE(dict->ma_keys)) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; if (ep->me_key != name) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } PyObject *old_value = ep->me_value; if (old_value == NULL) { UNLOCK_OBJECT(dict); - DEOPT_IF(true, STORE_ATTR); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } } _PyFrame_SetStackPointer(frame, stack_pointer); _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); @@ -8548,6 +10476,11 @@ } TARGET(STORE_DEREF) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_DEREF; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_DEREF); @@ -8563,6 +10496,11 @@ } TARGET(STORE_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST); @@ -8579,6 +10517,11 @@ } TARGET(STORE_FAST_LOAD_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_FAST_LOAD_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); @@ -8598,6 +10541,11 @@ } TARGET(STORE_FAST_STORE_FAST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_FAST_STORE_FAST; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST_STORE_FAST); @@ -8625,6 +10573,11 @@ } TARGET(STORE_GLOBAL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_GLOBAL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_GLOBAL); @@ -8636,7 +10589,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); if (err) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -8644,6 +10597,11 @@ } TARGET(STORE_NAME) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_NAME; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_NAME); @@ -8658,7 +10616,7 @@ "no locals found when storing %R", name); stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(v); - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } if (PyDict_CheckExact(ns)) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -8672,7 +10630,7 @@ } PyStackRef_CLOSE(v); if (err) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -8680,6 +10638,11 @@ } TARGET(STORE_SLICE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_SLICE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_SLICE); @@ -8721,7 +10684,7 @@ PyStackRef_CLOSE(v); PyStackRef_CLOSE(container); if (err) { - goto pop_4_error; + JUMP_TO_LABEL(pop_4_error); } } stack_pointer += -4; @@ -8730,6 +10693,11 @@ } TARGET(STORE_SUBSCR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_SUBSCR; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR); @@ -8768,7 +10736,7 @@ PyStackRef_CLOSE(container); PyStackRef_CLOSE(sub); if (err) { - goto pop_3_error; + JUMP_TO_LABEL(pop_3_error); } } stack_pointer += -3; @@ -8777,6 +10745,13 @@ } TARGET(STORE_SUBSCR_DICT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_SUBSCR_DICT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR_DICT); @@ -8789,7 +10764,11 @@ dict_st = stack_pointer[-2]; value = stack_pointer[-3]; PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } STAT_INC(STORE_SUBSCR, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _PyDict_SetItem_Take2((PyDictObject *)dict, @@ -8802,12 +10781,19 @@ PyStackRef_CLOSE(dict_st); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { - goto error; + JUMP_TO_LABEL(error); } DISPATCH(); } TARGET(STORE_SUBSCR_LIST_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = STORE_SUBSCR_LIST_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); @@ -8821,16 +10807,36 @@ value = stack_pointer[-3]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR); - DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR); + if (!PyLong_CheckExact(sub)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + if (!PyList_CheckExact(list)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } // Ensure nonnegative, zero-or-one-digit ints. - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(!LOCK_OBJECT(list), STORE_SUBSCR); + if (!LOCK_OBJECT(list)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } // Ensure index < len(list) if (index >= PyList_GET_SIZE(list)) { UNLOCK_OBJECT(list); - DEOPT_IF(true, STORE_SUBSCR); + if (true) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } } STAT_INC(STORE_SUBSCR, hit); PyObject *old_value = PyList_GET_ITEM(list, index); @@ -8848,6 +10854,11 @@ } TARGET(SWAP) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = SWAP; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SWAP); @@ -8863,6 +10874,11 @@ } TARGET(TO_BOOL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL); @@ -8896,7 +10912,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); if (err < 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } res = err ? PyStackRef_True : PyStackRef_False; } @@ -8905,8 +10921,14 @@ } TARGET(TO_BOOL_ALWAYS_TRUE) { - _Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr; + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_ALWAYS_TRUE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; + frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); @@ -8920,7 +10942,11 @@ uint32_t type_version = read_u32(&this_instr[2].cache); PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version, TO_BOOL); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } } // _REPLACE_WITH_TRUE { @@ -8933,6 +10959,13 @@ } TARGET(TO_BOOL_BOOL) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_BOOL; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_BOOL); @@ -8941,12 +10974,23 @@ /* Skip 1 cache entry */ /* Skip 2 cache entries */ value = stack_pointer[-1]; - DEOPT_IF(!PyStackRef_BoolCheck(value), TO_BOOL); + if (!PyStackRef_BoolCheck(value)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } STAT_INC(TO_BOOL, hit); DISPATCH(); } TARGET(TO_BOOL_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_INT; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_INT); @@ -8957,7 +11001,11 @@ /* Skip 2 cache entries */ value = stack_pointer[-1]; PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - DEOPT_IF(!PyLong_CheckExact(value_o), TO_BOOL); + if (!PyLong_CheckExact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } STAT_INC(TO_BOOL, hit); if (_PyLong_IsZero((PyLongObject *)value_o)) { assert(_Py_IsImmortal(value_o)); @@ -8972,6 +11020,13 @@ } TARGET(TO_BOOL_LIST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_LIST; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_LIST); @@ -8982,7 +11037,11 @@ /* Skip 2 cache entries */ value = stack_pointer[-1]; PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - DEOPT_IF(!PyList_CheckExact(value_o), TO_BOOL); + if (!PyList_CheckExact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } STAT_INC(TO_BOOL, hit); res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; PyStackRef_CLOSE(value); @@ -8991,6 +11050,13 @@ } TARGET(TO_BOOL_NONE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_NONE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_NONE); @@ -9001,7 +11067,11 @@ /* Skip 2 cache entries */ value = stack_pointer[-1]; // This one is a bit weird, because we expect *some* failures: - DEOPT_IF(!PyStackRef_IsNone(value), TO_BOOL); + if (!PyStackRef_IsNone(value)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } STAT_INC(TO_BOOL, hit); res = PyStackRef_False; stack_pointer[-1] = res; @@ -9009,6 +11079,13 @@ } TARGET(TO_BOOL_STR) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = TO_BOOL_STR; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL_STR); @@ -9019,7 +11096,11 @@ /* Skip 2 cache entries */ value = stack_pointer[-1]; PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - DEOPT_IF(!PyUnicode_CheckExact(value_o), TO_BOOL); + if (!PyUnicode_CheckExact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } STAT_INC(TO_BOOL, hit); if (value_o == &_Py_STR(empty)) { assert(_Py_IsImmortal(value_o)); @@ -9035,6 +11116,11 @@ } TARGET(UNARY_INVERT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNARY_INVERT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_INVERT); @@ -9046,7 +11132,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); if (res_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; @@ -9054,6 +11140,11 @@ } TARGET(UNARY_NEGATIVE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNARY_NEGATIVE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_NEGATIVE); @@ -9065,7 +11156,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(value); if (res_o == NULL) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[-1] = res; @@ -9073,6 +11164,11 @@ } TARGET(UNARY_NOT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNARY_NOT; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_NOT); @@ -9087,6 +11183,11 @@ } TARGET(UNPACK_EX) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNPACK_EX; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNPACK_EX); @@ -9100,7 +11201,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); if (res == 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } stack_pointer += (oparg & 0xFF) + (oparg >> 8); assert(WITHIN_STACK_BOUNDS()); @@ -9108,6 +11209,11 @@ } TARGET(UNPACK_SEQUENCE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNPACK_SEQUENCE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE); @@ -9144,7 +11250,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); PyStackRef_CLOSE(seq); if (res == 0) { - goto pop_1_error; + JUMP_TO_LABEL(pop_1_error); } } stack_pointer += -1 + oparg; @@ -9153,6 +11259,13 @@ } TARGET(UNPACK_SEQUENCE_LIST) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNPACK_SEQUENCE_LIST; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); @@ -9163,11 +11276,23 @@ seq = stack_pointer[-1]; values = &stack_pointer[-1]; PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - DEOPT_IF(!PyList_CheckExact(seq_o), UNPACK_SEQUENCE); - DEOPT_IF(!LOCK_OBJECT(seq_o), UNPACK_SEQUENCE); + if (!PyList_CheckExact(seq_o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + if (!LOCK_OBJECT(seq_o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } if (PyList_GET_SIZE(seq_o) != oparg) { UNLOCK_OBJECT(seq_o); - DEOPT_IF(true, UNPACK_SEQUENCE); + if (true) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } } STAT_INC(UNPACK_SEQUENCE, hit); PyObject **items = _PyList_ITEMS(seq_o); @@ -9182,6 +11307,13 @@ } TARGET(UNPACK_SEQUENCE_TUPLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNPACK_SEQUENCE_TUPLE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); @@ -9192,8 +11324,16 @@ seq = stack_pointer[-1]; values = &stack_pointer[-1]; PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - DEOPT_IF(!PyTuple_CheckExact(seq_o), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq_o) != oparg, UNPACK_SEQUENCE); + if (!PyTuple_CheckExact(seq_o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + if (PyTuple_GET_SIZE(seq_o) != oparg) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } STAT_INC(UNPACK_SEQUENCE, hit); PyObject **items = _PyTuple_ITEMS(seq_o); for (int i = oparg; --i >= 0; ) { @@ -9206,6 +11346,13 @@ } TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = UNPACK_SEQUENCE_TWO_TUPLE; + (void)(opcode); + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); @@ -9217,8 +11364,16 @@ seq = stack_pointer[-1]; assert(oparg == 2); PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - DEOPT_IF(!PyTuple_CheckExact(seq_o), UNPACK_SEQUENCE); - DEOPT_IF(PyTuple_GET_SIZE(seq_o) != 2, UNPACK_SEQUENCE); + if (!PyTuple_CheckExact(seq_o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + if (PyTuple_GET_SIZE(seq_o) != 2) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } STAT_INC(UNPACK_SEQUENCE, hit); val0 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 0)); val1 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 1)); @@ -9231,6 +11386,11 @@ } TARGET(WITH_EXCEPT_START) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = WITH_EXCEPT_START; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(WITH_EXCEPT_START); @@ -9275,7 +11435,7 @@ (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - goto error; + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; @@ -9285,6 +11445,11 @@ } TARGET(YIELD_VALUE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode; + #endif + opcode = YIELD_VALUE; + (void)(opcode); frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(YIELD_VALUE); @@ -9331,6 +11496,7 @@ } /* END INSTRUCTIONS */ +#ifndef Py_TAIL_CALL_INTERP #if USE_COMPUTED_GOTOS _unknown_opcode: #else @@ -9340,44 +11506,46 @@ next_instr points the current instruction without TARGET(). */ opcode = next_instr->op.code; _PyErr_Format(tstate, PyExc_SystemError, - "%U:%d: unknown opcode %d", - _PyFrame_GetCode(frame)->co_filename, - PyUnstable_InterpreterFrame_GetLine(frame), - opcode); - goto error; + "%U:%d: unknown opcode %d", + _PyFrame_GetCode(frame)->co_filename, + PyUnstable_InterpreterFrame_GetLine(frame), + opcode); +JUMP_TO_LABEL(error); + } /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); +#endif /* Py_TAIL_CALL_INTERP */ /* BEGIN LABELS */ - pop_4_error: + LABEL(pop_4_error) { STACK_SHRINK(4); - goto error; + JUMP_TO_LABEL(error); } - pop_3_error: + LABEL(pop_3_error) { STACK_SHRINK(3); - goto error; + JUMP_TO_LABEL(error); } - pop_2_error: + LABEL(pop_2_error) { STACK_SHRINK(2); - goto error; + JUMP_TO_LABEL(error); } - pop_1_error: + LABEL(pop_1_error) { STACK_SHRINK(1); - goto error; + JUMP_TO_LABEL(error); } - error: + LABEL(error) { /* Double-check exception status. */ #ifdef NDEBUG @@ -9407,10 +11575,10 @@ _PyEval_MonitorRaise(tstate, frame, next_instr-1); stack_pointer = _PyFrame_GetStackPointer(frame); _PyFrame_SetStackPointer(frame, stack_pointer); - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } - exception_unwind: + LABEL(exception_unwind) { /* STACK SPILLED */ /* We can't use frame->instr_ptr here, as RERAISE may have set it */ @@ -9427,7 +11595,7 @@ PyStackRef_XCLOSE(ref); } monitor_unwind(tstate, frame, next_instr-1); - goto exit_unwind; + JUMP_TO_LABEL(exit_unwind); } assert(STACK_LEVEL() >= level); _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; @@ -9440,7 +11608,7 @@ int frame_lasti = _PyInterpreterFrame_LASTI(frame); PyObject *lasti = PyLong_FromLong(frame_lasti); if (lasti == NULL) { - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti)); } @@ -9453,7 +11621,7 @@ next_instr = _PyFrame_GetBytecode(frame) + handler; int err = monitor_handled(tstate, frame, next_instr, exc); if (err < 0) { - goto exception_unwind; + JUMP_TO_LABEL(exception_unwind); } /* Resume normal execution */ #ifdef LLTRACE @@ -9462,10 +11630,13 @@ } #endif stack_pointer = _PyFrame_GetStackPointer(frame); + #ifdef Py_TAIL_CALL_INTERP + int opcode; + #endif DISPATCH(); } - exit_unwind: + LABEL(exit_unwind) { /* STACK SPILLED */ assert(_PyErr_Occurred(tstate)); @@ -9484,15 +11655,15 @@ } next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); - goto error; + JUMP_TO_LABEL(error); } - start_frame: + LABEL(start_frame) { /* STACK SPILLED */ int too_deep = _Py_EnterRecursivePy(tstate); if (too_deep) { - goto exit_unwind; + JUMP_TO_LABEL(exit_unwind); } next_instr = frame->instr_ptr; #ifdef LLTRACE @@ -9500,7 +11671,7 @@ int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); frame->lltrace = lltrace; if (lltrace < 0) { - goto exit_unwind; + JUMP_TO_LABEL(exit_unwind); } } #endif @@ -9512,6 +11683,9 @@ assert(!_PyErr_Occurred(tstate)); #endif stack_pointer = _PyFrame_GetStackPointer(frame); + #ifdef Py_TAIL_CALL_INTERP + int opcode; + #endif DISPATCH(); } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 2b84f0281e5356..27c4d537b8079b 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -1,3 +1,4 @@ +#ifndef Py_TAIL_CALL_INTERP static void *opcode_targets[256] = { &&TARGET_CACHE, &&TARGET_BINARY_SLICE, @@ -256,3 +257,507 @@ static void *opcode_targets[256] = { &&TARGET_INSTRUMENTED_LINE, &&TARGET_ENTER_EXECUTOR, }; +#else /* Py_TAIL_CALL_INTERP */ +static py_tail_call_funcptr INSTRUCTION_TABLE[256]; + +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_4_error(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_3_error(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_start_frame(TAIL_CALL_PARAMS); + +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_FLOAT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_UNICODE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_EXTEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_GETITEM(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_STR_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SLICE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_STRING(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_TUPLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CACHE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BOUND_METHOD_GENERAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_CLASS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_O(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_INTRINSIC_1(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_INTRINSIC_2(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_ISINSTANCE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_BOUND_METHOD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_NON_PY(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_PY(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_LEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_O(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_NON_PY_GENERAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_PY_EXACT_ARGS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_PY_GENERAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_STR_1(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_TUPLE_1(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_TYPE_1(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CHECK_EG_MATCH(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CHECK_EXC_MATCH(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CLEANUP_THROW(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_FLOAT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_STR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP_SET(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONVERT_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COPY(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COPY_FREE_VARS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_DEREF(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_GLOBAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_NAME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_SUBSCR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DICT_MERGE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DICT_UPDATE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_ASYNC_FOR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_FOR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_SEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_ENTER_EXECUTOR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_EXIT_INIT_CHECK(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_EXTENDED_ARG(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FORMAT_SIMPLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FORMAT_WITH_SPEC(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_GEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_LIST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_RANGE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_TUPLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_AITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_ANEXT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_AWAITABLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_LEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_YIELD_FROM_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IMPORT_FROM(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IMPORT_NAME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL_KW(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_END_FOR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_END_SEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_FOR_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_INSTRUCTION(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_JUMP_FORWARD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_LINE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_NOT_TAKEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_RESUME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_RETURN_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_YIELD_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INTERPRETER_EXIT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IS_OP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_JIT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_NO_JIT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_FORWARD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LIST_EXTEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_CLASS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_MODULE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_PROPERTY(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_SLOT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_BUILD_CLASS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_COMMON_CONSTANT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_CONST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_CONST_IMMORTAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_CONST_MORTAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_DEREF(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_AND_CLEAR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_CHECK(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FROM_DICT_OR_DEREF(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL_BUILTIN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL_MODULE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_LOCALS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_NAME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SMALL_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SPECIAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR_METHOD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAKE_CELL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAKE_FUNCTION(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAP_ADD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_CLASS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_KEYS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_MAPPING(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_SEQUENCE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_NOP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_NOT_TAKEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_EXCEPT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_ITER(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_TOP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_PUSH_EXC_INFO(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_PUSH_NULL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RAISE_VARARGS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RERAISE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESERVED(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESUME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESUME_CHECK(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RETURN_GENERATOR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RETURN_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SEND(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SEND_GEN(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SETUP_ANNOTATIONS(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_ADD(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_FUNCTION_ATTRIBUTE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_UPDATE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_SLOT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_DEREF(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST_STORE_FAST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_GLOBAL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_NAME(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SLICE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SWAP(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_ALWAYS_TRUE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_BOOL(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_LIST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_NONE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_STR(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_INVERT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_NEGATIVE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_NOT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_EX(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_LIST(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_TUPLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_WITH_EXCEPT_START(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_YIELD_VALUE(TAIL_CALL_PARAMS); + +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) { + int opcode = next_instr->op.code; + _PyErr_Format(tstate, PyExc_SystemError, + "%U:%d: unknown opcode %d", + _PyFrame_GetCode(frame)->co_filename, + PyUnstable_InterpreterFrame_GetLine(frame), + opcode); +JUMP_TO_LABEL(error); +} + +static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { + [BINARY_OP] = _TAIL_CALL_BINARY_OP, + [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_BINARY_OP_ADD_FLOAT, + [BINARY_OP_ADD_INT] = _TAIL_CALL_BINARY_OP_ADD_INT, + [BINARY_OP_ADD_UNICODE] = _TAIL_CALL_BINARY_OP_ADD_UNICODE, + [BINARY_OP_EXTEND] = _TAIL_CALL_BINARY_OP_EXTEND, + [BINARY_OP_INPLACE_ADD_UNICODE] = _TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE, + [BINARY_OP_MULTIPLY_FLOAT] = _TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT, + [BINARY_OP_MULTIPLY_INT] = _TAIL_CALL_BINARY_OP_MULTIPLY_INT, + [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT, + [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_BINARY_OP_SUBTRACT_INT, + [BINARY_SLICE] = _TAIL_CALL_BINARY_SLICE, + [BINARY_SUBSCR] = _TAIL_CALL_BINARY_SUBSCR, + [BINARY_SUBSCR_DICT] = _TAIL_CALL_BINARY_SUBSCR_DICT, + [BINARY_SUBSCR_GETITEM] = _TAIL_CALL_BINARY_SUBSCR_GETITEM, + [BINARY_SUBSCR_LIST_INT] = _TAIL_CALL_BINARY_SUBSCR_LIST_INT, + [BINARY_SUBSCR_STR_INT] = _TAIL_CALL_BINARY_SUBSCR_STR_INT, + [BINARY_SUBSCR_TUPLE_INT] = _TAIL_CALL_BINARY_SUBSCR_TUPLE_INT, + [BUILD_LIST] = _TAIL_CALL_BUILD_LIST, + [BUILD_MAP] = _TAIL_CALL_BUILD_MAP, + [BUILD_SET] = _TAIL_CALL_BUILD_SET, + [BUILD_SLICE] = _TAIL_CALL_BUILD_SLICE, + [BUILD_STRING] = _TAIL_CALL_BUILD_STRING, + [BUILD_TUPLE] = _TAIL_CALL_BUILD_TUPLE, + [CACHE] = _TAIL_CALL_CACHE, + [CALL] = _TAIL_CALL_CALL, + [CALL_ALLOC_AND_ENTER_INIT] = _TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT, + [CALL_BOUND_METHOD_EXACT_ARGS] = _TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS, + [CALL_BOUND_METHOD_GENERAL] = _TAIL_CALL_CALL_BOUND_METHOD_GENERAL, + [CALL_BUILTIN_CLASS] = _TAIL_CALL_CALL_BUILTIN_CLASS, + [CALL_BUILTIN_FAST] = _TAIL_CALL_CALL_BUILTIN_FAST, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = _TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS, + [CALL_BUILTIN_O] = _TAIL_CALL_CALL_BUILTIN_O, + [CALL_FUNCTION_EX] = _TAIL_CALL_CALL_FUNCTION_EX, + [CALL_INTRINSIC_1] = _TAIL_CALL_CALL_INTRINSIC_1, + [CALL_INTRINSIC_2] = _TAIL_CALL_CALL_INTRINSIC_2, + [CALL_ISINSTANCE] = _TAIL_CALL_CALL_ISINSTANCE, + [CALL_KW] = _TAIL_CALL_CALL_KW, + [CALL_KW_BOUND_METHOD] = _TAIL_CALL_CALL_KW_BOUND_METHOD, + [CALL_KW_NON_PY] = _TAIL_CALL_CALL_KW_NON_PY, + [CALL_KW_PY] = _TAIL_CALL_CALL_KW_PY, + [CALL_LEN] = _TAIL_CALL_CALL_LEN, + [CALL_LIST_APPEND] = _TAIL_CALL_CALL_LIST_APPEND, + [CALL_METHOD_DESCRIPTOR_FAST] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + [CALL_METHOD_DESCRIPTOR_NOARGS] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS, + [CALL_METHOD_DESCRIPTOR_O] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_O, + [CALL_NON_PY_GENERAL] = _TAIL_CALL_CALL_NON_PY_GENERAL, + [CALL_PY_EXACT_ARGS] = _TAIL_CALL_CALL_PY_EXACT_ARGS, + [CALL_PY_GENERAL] = _TAIL_CALL_CALL_PY_GENERAL, + [CALL_STR_1] = _TAIL_CALL_CALL_STR_1, + [CALL_TUPLE_1] = _TAIL_CALL_CALL_TUPLE_1, + [CALL_TYPE_1] = _TAIL_CALL_CALL_TYPE_1, + [CHECK_EG_MATCH] = _TAIL_CALL_CHECK_EG_MATCH, + [CHECK_EXC_MATCH] = _TAIL_CALL_CHECK_EXC_MATCH, + [CLEANUP_THROW] = _TAIL_CALL_CLEANUP_THROW, + [COMPARE_OP] = _TAIL_CALL_COMPARE_OP, + [COMPARE_OP_FLOAT] = _TAIL_CALL_COMPARE_OP_FLOAT, + [COMPARE_OP_INT] = _TAIL_CALL_COMPARE_OP_INT, + [COMPARE_OP_STR] = _TAIL_CALL_COMPARE_OP_STR, + [CONTAINS_OP] = _TAIL_CALL_CONTAINS_OP, + [CONTAINS_OP_DICT] = _TAIL_CALL_CONTAINS_OP_DICT, + [CONTAINS_OP_SET] = _TAIL_CALL_CONTAINS_OP_SET, + [CONVERT_VALUE] = _TAIL_CALL_CONVERT_VALUE, + [COPY] = _TAIL_CALL_COPY, + [COPY_FREE_VARS] = _TAIL_CALL_COPY_FREE_VARS, + [DELETE_ATTR] = _TAIL_CALL_DELETE_ATTR, + [DELETE_DEREF] = _TAIL_CALL_DELETE_DEREF, + [DELETE_FAST] = _TAIL_CALL_DELETE_FAST, + [DELETE_GLOBAL] = _TAIL_CALL_DELETE_GLOBAL, + [DELETE_NAME] = _TAIL_CALL_DELETE_NAME, + [DELETE_SUBSCR] = _TAIL_CALL_DELETE_SUBSCR, + [DICT_MERGE] = _TAIL_CALL_DICT_MERGE, + [DICT_UPDATE] = _TAIL_CALL_DICT_UPDATE, + [END_ASYNC_FOR] = _TAIL_CALL_END_ASYNC_FOR, + [END_FOR] = _TAIL_CALL_END_FOR, + [END_SEND] = _TAIL_CALL_END_SEND, + [ENTER_EXECUTOR] = _TAIL_CALL_ENTER_EXECUTOR, + [EXIT_INIT_CHECK] = _TAIL_CALL_EXIT_INIT_CHECK, + [EXTENDED_ARG] = _TAIL_CALL_EXTENDED_ARG, + [FORMAT_SIMPLE] = _TAIL_CALL_FORMAT_SIMPLE, + [FORMAT_WITH_SPEC] = _TAIL_CALL_FORMAT_WITH_SPEC, + [FOR_ITER] = _TAIL_CALL_FOR_ITER, + [FOR_ITER_GEN] = _TAIL_CALL_FOR_ITER_GEN, + [FOR_ITER_LIST] = _TAIL_CALL_FOR_ITER_LIST, + [FOR_ITER_RANGE] = _TAIL_CALL_FOR_ITER_RANGE, + [FOR_ITER_TUPLE] = _TAIL_CALL_FOR_ITER_TUPLE, + [GET_AITER] = _TAIL_CALL_GET_AITER, + [GET_ANEXT] = _TAIL_CALL_GET_ANEXT, + [GET_AWAITABLE] = _TAIL_CALL_GET_AWAITABLE, + [GET_ITER] = _TAIL_CALL_GET_ITER, + [GET_LEN] = _TAIL_CALL_GET_LEN, + [GET_YIELD_FROM_ITER] = _TAIL_CALL_GET_YIELD_FROM_ITER, + [IMPORT_FROM] = _TAIL_CALL_IMPORT_FROM, + [IMPORT_NAME] = _TAIL_CALL_IMPORT_NAME, + [INSTRUMENTED_CALL] = _TAIL_CALL_INSTRUMENTED_CALL, + [INSTRUMENTED_CALL_FUNCTION_EX] = _TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX, + [INSTRUMENTED_CALL_KW] = _TAIL_CALL_INSTRUMENTED_CALL_KW, + [INSTRUMENTED_END_FOR] = _TAIL_CALL_INSTRUMENTED_END_FOR, + [INSTRUMENTED_END_SEND] = _TAIL_CALL_INSTRUMENTED_END_SEND, + [INSTRUMENTED_FOR_ITER] = _TAIL_CALL_INSTRUMENTED_FOR_ITER, + [INSTRUMENTED_INSTRUCTION] = _TAIL_CALL_INSTRUMENTED_INSTRUCTION, + [INSTRUMENTED_JUMP_BACKWARD] = _TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD, + [INSTRUMENTED_JUMP_FORWARD] = _TAIL_CALL_INSTRUMENTED_JUMP_FORWARD, + [INSTRUMENTED_LINE] = _TAIL_CALL_INSTRUMENTED_LINE, + [INSTRUMENTED_LOAD_SUPER_ATTR] = _TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR, + [INSTRUMENTED_NOT_TAKEN] = _TAIL_CALL_INSTRUMENTED_NOT_TAKEN, + [INSTRUMENTED_POP_ITER] = _TAIL_CALL_INSTRUMENTED_POP_ITER, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE, + [INSTRUMENTED_POP_JUMP_IF_NONE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE, + [INSTRUMENTED_RESUME] = _TAIL_CALL_INSTRUMENTED_RESUME, + [INSTRUMENTED_RETURN_VALUE] = _TAIL_CALL_INSTRUMENTED_RETURN_VALUE, + [INSTRUMENTED_YIELD_VALUE] = _TAIL_CALL_INSTRUMENTED_YIELD_VALUE, + [INTERPRETER_EXIT] = _TAIL_CALL_INTERPRETER_EXIT, + [IS_OP] = _TAIL_CALL_IS_OP, + [JUMP_BACKWARD] = _TAIL_CALL_JUMP_BACKWARD, + [JUMP_BACKWARD_JIT] = _TAIL_CALL_JUMP_BACKWARD_JIT, + [JUMP_BACKWARD_NO_INTERRUPT] = _TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT, + [JUMP_BACKWARD_NO_JIT] = _TAIL_CALL_JUMP_BACKWARD_NO_JIT, + [JUMP_FORWARD] = _TAIL_CALL_JUMP_FORWARD, + [LIST_APPEND] = _TAIL_CALL_LIST_APPEND, + [LIST_EXTEND] = _TAIL_CALL_LIST_EXTEND, + [LOAD_ATTR] = _TAIL_CALL_LOAD_ATTR, + [LOAD_ATTR_CLASS] = _TAIL_CALL_LOAD_ATTR_CLASS, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = _TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = _TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, + [LOAD_ATTR_INSTANCE_VALUE] = _TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE, + [LOAD_ATTR_METHOD_LAZY_DICT] = _TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT, + [LOAD_ATTR_METHOD_NO_DICT] = _TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT, + [LOAD_ATTR_METHOD_WITH_VALUES] = _TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES, + [LOAD_ATTR_MODULE] = _TAIL_CALL_LOAD_ATTR_MODULE, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, + [LOAD_ATTR_PROPERTY] = _TAIL_CALL_LOAD_ATTR_PROPERTY, + [LOAD_ATTR_SLOT] = _TAIL_CALL_LOAD_ATTR_SLOT, + [LOAD_ATTR_WITH_HINT] = _TAIL_CALL_LOAD_ATTR_WITH_HINT, + [LOAD_BUILD_CLASS] = _TAIL_CALL_LOAD_BUILD_CLASS, + [LOAD_COMMON_CONSTANT] = _TAIL_CALL_LOAD_COMMON_CONSTANT, + [LOAD_CONST] = _TAIL_CALL_LOAD_CONST, + [LOAD_CONST_IMMORTAL] = _TAIL_CALL_LOAD_CONST_IMMORTAL, + [LOAD_CONST_MORTAL] = _TAIL_CALL_LOAD_CONST_MORTAL, + [LOAD_DEREF] = _TAIL_CALL_LOAD_DEREF, + [LOAD_FAST] = _TAIL_CALL_LOAD_FAST, + [LOAD_FAST_AND_CLEAR] = _TAIL_CALL_LOAD_FAST_AND_CLEAR, + [LOAD_FAST_CHECK] = _TAIL_CALL_LOAD_FAST_CHECK, + [LOAD_FAST_LOAD_FAST] = _TAIL_CALL_LOAD_FAST_LOAD_FAST, + [LOAD_FROM_DICT_OR_DEREF] = _TAIL_CALL_LOAD_FROM_DICT_OR_DEREF, + [LOAD_FROM_DICT_OR_GLOBALS] = _TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS, + [LOAD_GLOBAL] = _TAIL_CALL_LOAD_GLOBAL, + [LOAD_GLOBAL_BUILTIN] = _TAIL_CALL_LOAD_GLOBAL_BUILTIN, + [LOAD_GLOBAL_MODULE] = _TAIL_CALL_LOAD_GLOBAL_MODULE, + [LOAD_LOCALS] = _TAIL_CALL_LOAD_LOCALS, + [LOAD_NAME] = _TAIL_CALL_LOAD_NAME, + [LOAD_SMALL_INT] = _TAIL_CALL_LOAD_SMALL_INT, + [LOAD_SPECIAL] = _TAIL_CALL_LOAD_SPECIAL, + [LOAD_SUPER_ATTR] = _TAIL_CALL_LOAD_SUPER_ATTR, + [LOAD_SUPER_ATTR_ATTR] = _TAIL_CALL_LOAD_SUPER_ATTR_ATTR, + [LOAD_SUPER_ATTR_METHOD] = _TAIL_CALL_LOAD_SUPER_ATTR_METHOD, + [MAKE_CELL] = _TAIL_CALL_MAKE_CELL, + [MAKE_FUNCTION] = _TAIL_CALL_MAKE_FUNCTION, + [MAP_ADD] = _TAIL_CALL_MAP_ADD, + [MATCH_CLASS] = _TAIL_CALL_MATCH_CLASS, + [MATCH_KEYS] = _TAIL_CALL_MATCH_KEYS, + [MATCH_MAPPING] = _TAIL_CALL_MATCH_MAPPING, + [MATCH_SEQUENCE] = _TAIL_CALL_MATCH_SEQUENCE, + [NOP] = _TAIL_CALL_NOP, + [NOT_TAKEN] = _TAIL_CALL_NOT_TAKEN, + [POP_EXCEPT] = _TAIL_CALL_POP_EXCEPT, + [POP_ITER] = _TAIL_CALL_POP_ITER, + [POP_JUMP_IF_FALSE] = _TAIL_CALL_POP_JUMP_IF_FALSE, + [POP_JUMP_IF_NONE] = _TAIL_CALL_POP_JUMP_IF_NONE, + [POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_POP_JUMP_IF_NOT_NONE, + [POP_JUMP_IF_TRUE] = _TAIL_CALL_POP_JUMP_IF_TRUE, + [POP_TOP] = _TAIL_CALL_POP_TOP, + [PUSH_EXC_INFO] = _TAIL_CALL_PUSH_EXC_INFO, + [PUSH_NULL] = _TAIL_CALL_PUSH_NULL, + [RAISE_VARARGS] = _TAIL_CALL_RAISE_VARARGS, + [RERAISE] = _TAIL_CALL_RERAISE, + [RESERVED] = _TAIL_CALL_RESERVED, + [RESUME] = _TAIL_CALL_RESUME, + [RESUME_CHECK] = _TAIL_CALL_RESUME_CHECK, + [RETURN_GENERATOR] = _TAIL_CALL_RETURN_GENERATOR, + [RETURN_VALUE] = _TAIL_CALL_RETURN_VALUE, + [SEND] = _TAIL_CALL_SEND, + [SEND_GEN] = _TAIL_CALL_SEND_GEN, + [SETUP_ANNOTATIONS] = _TAIL_CALL_SETUP_ANNOTATIONS, + [SET_ADD] = _TAIL_CALL_SET_ADD, + [SET_FUNCTION_ATTRIBUTE] = _TAIL_CALL_SET_FUNCTION_ATTRIBUTE, + [SET_UPDATE] = _TAIL_CALL_SET_UPDATE, + [STORE_ATTR] = _TAIL_CALL_STORE_ATTR, + [STORE_ATTR_INSTANCE_VALUE] = _TAIL_CALL_STORE_ATTR_INSTANCE_VALUE, + [STORE_ATTR_SLOT] = _TAIL_CALL_STORE_ATTR_SLOT, + [STORE_ATTR_WITH_HINT] = _TAIL_CALL_STORE_ATTR_WITH_HINT, + [STORE_DEREF] = _TAIL_CALL_STORE_DEREF, + [STORE_FAST] = _TAIL_CALL_STORE_FAST, + [STORE_FAST_LOAD_FAST] = _TAIL_CALL_STORE_FAST_LOAD_FAST, + [STORE_FAST_STORE_FAST] = _TAIL_CALL_STORE_FAST_STORE_FAST, + [STORE_GLOBAL] = _TAIL_CALL_STORE_GLOBAL, + [STORE_NAME] = _TAIL_CALL_STORE_NAME, + [STORE_SLICE] = _TAIL_CALL_STORE_SLICE, + [STORE_SUBSCR] = _TAIL_CALL_STORE_SUBSCR, + [STORE_SUBSCR_DICT] = _TAIL_CALL_STORE_SUBSCR_DICT, + [STORE_SUBSCR_LIST_INT] = _TAIL_CALL_STORE_SUBSCR_LIST_INT, + [SWAP] = _TAIL_CALL_SWAP, + [TO_BOOL] = _TAIL_CALL_TO_BOOL, + [TO_BOOL_ALWAYS_TRUE] = _TAIL_CALL_TO_BOOL_ALWAYS_TRUE, + [TO_BOOL_BOOL] = _TAIL_CALL_TO_BOOL_BOOL, + [TO_BOOL_INT] = _TAIL_CALL_TO_BOOL_INT, + [TO_BOOL_LIST] = _TAIL_CALL_TO_BOOL_LIST, + [TO_BOOL_NONE] = _TAIL_CALL_TO_BOOL_NONE, + [TO_BOOL_STR] = _TAIL_CALL_TO_BOOL_STR, + [UNARY_INVERT] = _TAIL_CALL_UNARY_INVERT, + [UNARY_NEGATIVE] = _TAIL_CALL_UNARY_NEGATIVE, + [UNARY_NOT] = _TAIL_CALL_UNARY_NOT, + [UNPACK_EX] = _TAIL_CALL_UNPACK_EX, + [UNPACK_SEQUENCE] = _TAIL_CALL_UNPACK_SEQUENCE, + [UNPACK_SEQUENCE_LIST] = _TAIL_CALL_UNPACK_SEQUENCE_LIST, + [UNPACK_SEQUENCE_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TUPLE, + [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE, + [WITH_EXCEPT_START] = _TAIL_CALL_WITH_EXCEPT_START, + [YIELD_VALUE] = _TAIL_CALL_YIELD_VALUE, + [118] = _TAIL_CALL_UNKNOWN_OPCODE, + [119] = _TAIL_CALL_UNKNOWN_OPCODE, + [120] = _TAIL_CALL_UNKNOWN_OPCODE, + [121] = _TAIL_CALL_UNKNOWN_OPCODE, + [122] = _TAIL_CALL_UNKNOWN_OPCODE, + [123] = _TAIL_CALL_UNKNOWN_OPCODE, + [124] = _TAIL_CALL_UNKNOWN_OPCODE, + [125] = _TAIL_CALL_UNKNOWN_OPCODE, + [126] = _TAIL_CALL_UNKNOWN_OPCODE, + [127] = _TAIL_CALL_UNKNOWN_OPCODE, + [128] = _TAIL_CALL_UNKNOWN_OPCODE, + [129] = _TAIL_CALL_UNKNOWN_OPCODE, + [130] = _TAIL_CALL_UNKNOWN_OPCODE, + [131] = _TAIL_CALL_UNKNOWN_OPCODE, + [132] = _TAIL_CALL_UNKNOWN_OPCODE, + [133] = _TAIL_CALL_UNKNOWN_OPCODE, + [134] = _TAIL_CALL_UNKNOWN_OPCODE, + [135] = _TAIL_CALL_UNKNOWN_OPCODE, + [136] = _TAIL_CALL_UNKNOWN_OPCODE, + [137] = _TAIL_CALL_UNKNOWN_OPCODE, + [138] = _TAIL_CALL_UNKNOWN_OPCODE, + [139] = _TAIL_CALL_UNKNOWN_OPCODE, + [140] = _TAIL_CALL_UNKNOWN_OPCODE, + [141] = _TAIL_CALL_UNKNOWN_OPCODE, + [142] = _TAIL_CALL_UNKNOWN_OPCODE, + [143] = _TAIL_CALL_UNKNOWN_OPCODE, + [144] = _TAIL_CALL_UNKNOWN_OPCODE, + [145] = _TAIL_CALL_UNKNOWN_OPCODE, + [146] = _TAIL_CALL_UNKNOWN_OPCODE, + [147] = _TAIL_CALL_UNKNOWN_OPCODE, + [148] = _TAIL_CALL_UNKNOWN_OPCODE, + [232] = _TAIL_CALL_UNKNOWN_OPCODE, + [233] = _TAIL_CALL_UNKNOWN_OPCODE, + [234] = _TAIL_CALL_UNKNOWN_OPCODE, +}; +#endif /* Py_TAIL_CALL_INTERP */ diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 11a559bca474b0..aa88608168c1be 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -675,6 +675,7 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "assert", "backoff_counter_triggers", "initial_temperature_backoff_counter", + "JUMP_TO_LABEL", "maybe_lltrace_resume_frame", "restart_backoff_counter", ) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 1c572ec0512b37..cd52f181b60e43 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import TextIO from analyzer import ( Instruction, @@ -154,28 +153,31 @@ def deopt_if( storage: Storage, inst: Instruction | None, ) -> bool: - self.out.emit_at("DEOPT_IF", tkn) + self.out.start_line() + self.out.emit("if (") lparen = next(tkn_iter) - self.emit(lparen) assert lparen.kind == "LPAREN" first_tkn = tkn_iter.peek() emit_to(self.out, tkn_iter, "RPAREN") + self.emit(") {\n") next(tkn_iter) # Semi colon - self.out.emit(", ") assert inst is not None assert inst.family is not None - self.out.emit(inst.family.name) - self.out.emit(");\n") + family_name = inst.family.name + self.emit(f"UPDATE_MISS_STATS({family_name});\n") + self.emit(f"assert(_PyOpcode_Deopt[opcode] == ({family_name}));\n") + self.emit(f"JUMP_TO_PREDICTED({family_name});\n") + self.emit("}\n") return not always_true(first_tkn) exit_if = deopt_if def goto_error(self, offset: int, label: str, storage: Storage) -> str: if offset > 0: - return f"goto pop_{offset}_{label};" + return f"JUMP_TO_LABEL(pop_{offset}_{label});" if offset < 0: storage.copy().flush(self.out) - return f"goto {label};" + return f"JUMP_TO_LABEL({label});" def error_if( self, @@ -410,8 +412,10 @@ def goto_label(self, goto: Token, label: Token, storage: Storage) -> None: self.emit_save(storage) elif storage.spilled: raise analysis_error("Cannot jump from spilled label without reloading the stack pointer", goto) - self.out.emit(goto) + self.out.start_line() + self.out.emit("JUMP_TO_LABEL(") self.out.emit(label) + self.out.emit(")") def emit_save(self, storage: Storage) -> None: storage.save(self.out) @@ -603,7 +607,7 @@ def _emit_block( elif tkn.kind == "GOTO": label_tkn = next(tkn_iter) self.goto_label(tkn, label_tkn, storage) - reachable = False; + reachable = False elif tkn.kind == "IDENTIFIER": if tkn.text in self._replacers: if not self._replacers[tkn.text](tkn, tkn_iter, uop, storage, inst): diff --git a/Tools/cases_generator/target_generator.py b/Tools/cases_generator/target_generator.py index c5097b7584724c..db028116db93d2 100644 --- a/Tools/cases_generator/target_generator.py +++ b/Tools/cases_generator/target_generator.py @@ -13,6 +13,7 @@ DEFAULT_INPUT, ROOT, ) +from tier1_generator import UNKNOWN_OPCODE_HANDLER from cwriter import CWriter @@ -25,11 +26,49 @@ def write_opcode_targets(analysis: Analysis, out: CWriter) -> None: for name, op in analysis.opmap.items(): if op < 256: targets[op] = f"&&TARGET_{name},\n" + out.emit("#ifndef Py_TAIL_CALL_INTERP\n") out.emit("static void *opcode_targets[256] = {\n") for target in targets: out.emit(target) out.emit("};\n") + out.emit("#else /* Py_TAIL_CALL_INTERP */\n") +def function_proto(name: str) -> str: + return f"Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_{name}(TAIL_CALL_PARAMS)" + + +def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: + out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256];\n") + out.emit("\n") + + # Emit function prototypes for labels. + for name in analysis.labels: + out.emit(f"{function_proto(name)};\n") + out.emit("\n") + + # Emit function prototypes for opcode handlers. + for name in sorted(analysis.instructions.keys()): + out.emit(f"{function_proto(name)};\n") + out.emit("\n") + + # Emit unknown opcode handler. + out.emit(function_proto("UNKNOWN_OPCODE")) + out.emit(" {\n") + out.emit("int opcode = next_instr->op.code;\n") + out.emit(UNKNOWN_OPCODE_HANDLER) + out.emit("}\n") + out.emit("\n") + + # Emit the dispatch table. + out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256] = {\n") + for name in sorted(analysis.instructions.keys()): + out.emit(f"[{name}] = _TAIL_CALL_{name},\n") + named_values = analysis.opmap.values() + for rest in range(256): + if rest not in named_values: + out.emit(f"[{rest}] = _TAIL_CALL_UNKNOWN_OPCODE,\n") + out.emit("};\n") + outfile.write("#endif /* Py_TAIL_CALL_INTERP */\n") arg_parser = argparse.ArgumentParser( description="Generate the file with dispatch targets.", @@ -52,3 +91,4 @@ def write_opcode_targets(analysis: Analysis, out: CWriter) -> None: with open(args.output, "w") as outfile: out = CWriter(outfile, 0, False) write_opcode_targets(data, out) + write_tailcall_dispatch_table(data, out) diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index c7cf09e2ec4ede..979646a7ca6525 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -24,10 +24,9 @@ Emitter, ) from cwriter import CWriter -from typing import TextIO +from typing import TextIO, Callable from stack import Local, Stack, StackError, get_stack_effect, Storage - DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" @@ -129,31 +128,52 @@ def uses_this(inst: Instruction) -> bool: for cache in uop.caches: if cache.name != "unused": return True + # Can't be merged into the loop above, because + # this must strictly be performed at the end. + for uop in inst.parts: + if not isinstance(uop, Uop): + continue + for tkn in uop.body: + if (tkn.kind == "IDENTIFIER" + and (tkn.text in {"DEOPT_IF", "EXIT_IF"})): + return True return False +UNKNOWN_OPCODE_HANDLER ="""\ +_PyErr_Format(tstate, PyExc_SystemError, + "%U:%d: unknown opcode %d", + _PyFrame_GetCode(frame)->co_filename, + PyUnstable_InterpreterFrame_GetLine(frame), + opcode); +JUMP_TO_LABEL(error); +""" + def generate_tier1( filenames: list[str], analysis: Analysis, outfile: TextIO, lines: bool ) -> None: write_header(__file__, filenames, outfile) - outfile.write( - f""" + outfile.write(""" #ifdef TIER_TWO #error "This file is for Tier 1 only" #endif #define TIER_ONE 1 - +""") + outfile.write(f""" +#ifndef Py_TAIL_CALL_INTERP #if !USE_COMPUTED_GOTOS dispatch_opcode: switch (opcode) #endif {{ +#endif /* Py_TAIL_CALL_INTERP */ {INSTRUCTION_START_MARKER} """ ) generate_tier1_cases(analysis, outfile, lines) outfile.write(f""" {INSTRUCTION_END_MARKER} +#ifndef Py_TAIL_CALL_INTERP #if USE_COMPUTED_GOTOS _unknown_opcode: #else @@ -162,41 +182,41 @@ def generate_tier1( /* Tell C compilers not to hold the opcode variable in the loop. next_instr points the current instruction without TARGET(). */ opcode = next_instr->op.code; - _PyErr_Format(tstate, PyExc_SystemError, - "%U:%d: unknown opcode %d", - _PyFrame_GetCode(frame)->co_filename, - PyUnstable_InterpreterFrame_GetLine(frame), - opcode); - goto error; + {UNKNOWN_OPCODE_HANDLER} }} /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); +#endif /* Py_TAIL_CALL_INTERP */ {LABEL_START_MARKER} """) - generate_tier1_labels(analysis, outfile, lines) + out = CWriter(outfile, 2, lines) + emitter = Emitter(out, analysis.labels) + generate_tier1_labels(analysis, emitter) outfile.write(f"{LABEL_END_MARKER}\n") outfile.write(FOOTER) + + def generate_tier1_labels( - analysis: Analysis, outfile: TextIO, lines: bool + analysis: Analysis, emitter: Emitter ) -> None: - out = CWriter(outfile, 2, lines) - emitter = Emitter(out, analysis.labels) - out.emit("\n") + emitter.emit("\n") + # Emit tail-callable labels as function defintions for name, label in analysis.labels.items(): - out.emit(f"{name}:\n") - out.emit("{\n") + emitter.emit(f"LABEL({name})\n") + emitter.emit("{\n") storage = Storage(Stack(), [], [], []) if label.spilled: storage.spilled = 1 - out.emit("/* STACK SPILLED */\n") + emitter.emit("/* STACK SPILLED */\n") emitter.emit_tokens(label, storage, None) - out.emit("\n") - out.emit("}\n") - out.emit("\n") + emitter.emit("\n") + emitter.emit("}\n") + emitter.emit("\n") + def generate_tier1_cases( analysis: Analysis, outfile: TextIO, lines: bool @@ -205,20 +225,26 @@ def generate_tier1_cases( emitter = Emitter(out, analysis.labels) out.emit("\n") for name, inst in sorted(analysis.instructions.items()): - needs_this = uses_this(inst) out.emit("\n") out.emit(f"TARGET({name}) {{\n") + # We need to ifdef it because this breaks platforms + # without computed gotos/tail calling. + out.emit(f"#if defined(Py_TAIL_CALL_INTERP)\n") + out.emit(f"int opcode;\n") + out.emit(f"#endif\n") + out.emit(f"opcode = {name};\n") + out.emit(f"(void)(opcode);\n") + needs_this = uses_this(inst) unused_guard = "(void)this_instr;\n" if inst.properties.needs_prev: out.emit(f"_Py_CODEUNIT* const prev_instr = frame->instr_ptr;\n") + if needs_this and not inst.is_target: - if inst.properties.no_save_ip: - out.emit(f"_Py_CODEUNIT* const this_instr = next_instr;\n") - else: - out.emit(f"_Py_CODEUNIT* const this_instr = frame->instr_ptr = next_instr;\n") + out.emit(f"_Py_CODEUNIT* const this_instr = next_instr;\n") out.emit(unused_guard) - elif not inst.properties.no_save_ip: + if not inst.properties.no_save_ip: out.emit(f"frame->instr_ptr = next_instr;\n") + out.emit(f"next_instr += {inst.size};\n") out.emit(f"INSTRUCTION_STATS({name});\n") if inst.is_target: @@ -226,8 +252,6 @@ def generate_tier1_cases( if needs_this: out.emit(f"_Py_CODEUNIT* const this_instr = next_instr - {inst.size};\n") out.emit(unused_guard) - if inst.properties.uses_opcode: - out.emit(f"opcode = {name};\n") if inst.family is not None: out.emit( f"static_assert({inst.family.size} == {inst.size-1}" diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 95c90bda70f352..929f37d2dddea1 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -32,14 +32,6 @@ #undef CURRENT_OPERAND1 #define CURRENT_OPERAND1() (_operand1) -#undef DEOPT_IF -#define DEOPT_IF(COND, INSTNAME) \ - do { \ - if ((COND)) { \ - goto deoptimize; \ - } \ - } while (0) - #undef ENABLE_SPECIALIZATION #define ENABLE_SPECIALIZATION (0) diff --git a/configure b/configure index 3eb787f788bfb9..22456959add174 100755 --- a/configure +++ b/configure @@ -1122,6 +1122,7 @@ with_platlibdir with_wheel_pkg_dir with_readline with_computed_gotos +with_tail_call_interp with_ensurepip with_openssl with_openssl_rpath @@ -1929,6 +1930,8 @@ Optional Packages: use libedit for backend or disable readline module --with-computed-gotos enable computed gotos in evaluation loop (enabled by default on supported compilers) + --tail-call-interp enable tail-calling interpreter in evaluation loop + and rest of CPython --with-ensurepip[=install|upgrade|no] "install" or "upgrade" using bundled pip (default is upgrade) @@ -29246,6 +29249,51 @@ printf "%s\n" "#define HAVE_COMPUTED_GOTOS 1" >>confdefs.h esac +# Check for --with-tail-call-interp +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-tail-call-interp" >&5 +printf %s "checking for --with-tail-call-interp... " >&6; } + +# Check whether --with-tail-call-interp was given. +if test ${with_tail_call_interp+y} +then : + withval=$with_tail_call_interp; +if test "$withval" = yes +then + +printf "%s\n" "#define Py_TAIL_CALL_INTERP 1" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +if test "$withval" = no +then + +printf "%s\n" "#define Py_TAIL_CALL_INTERP 0" >>confdefs.h + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no value specified" >&5 +printf "%s\n" "no value specified" >&6; } ;; +esac +fi + + +# Do not enable tail-calling interpreter if tier 2 is enabled. +if ${tier2_flags:+false} : +then : + + case "$ac_cv_tail_call" in yes*) + +printf "%s\n" "#define Py_TAIL_CALL_INTERP 1" >>confdefs.h + + esac + +fi + + case $ac_sys_system in AIX*) diff --git a/configure.ac b/configure.ac index c0130b8082cd8a..cf16e77f0a1503 100644 --- a/configure.ac +++ b/configure.ac @@ -7017,6 +7017,44 @@ case "$ac_cv_computed_gotos" in yes*) [Define if the C compiler supports computed gotos.]) esac +# Check for --with-tail-call-interp +AC_MSG_CHECKING([for --with-tail-call-interp]) +AC_ARG_WITH( + [tail-call-interp], + [AS_HELP_STRING( + [--tail-call-interp], + [enable tail-calling interpreter in evaluation loop and rest of CPython] + )], +[ +if test "$withval" = yes +then + AC_DEFINE([Py_TAIL_CALL_INTERP], [1], + [Define if you want to use tail-calling interpreters in CPython.]) + AC_MSG_RESULT([yes]) +fi +if test "$withval" = no +then + AC_DEFINE([Py_TAIL_CALL_INTERP], [0], + [Define if you want to use tail-calling interpreters in CPython.]) + AC_MSG_RESULT([no]) +fi +], +[AC_MSG_RESULT([no value specified])]) + +# Do not enable tail-calling interpreter if tier 2 is enabled. +AS_VAR_IF( + [tier2_flags], + [], + [ + case "$ac_cv_tail_call" in yes*) + AC_DEFINE([Py_TAIL_CALL_INTERP], [1], + [Define if the C compiler supports efficient proper tail calls.]) + esac + ], + [] +) + + case $ac_sys_system in AIX*) AC_DEFINE([HAVE_BROKEN_PIPE_BUF], [1], diff --git a/pyconfig.h.in b/pyconfig.h.in index 30e55158bad4d6..9ea01ad3fc0a31 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1718,6 +1718,9 @@ /* The version of SunOS/Solaris as reported by `uname -r' without the dot. */ #undef Py_SUNOS_VERSION +/* Define if the C compiler supports efficient proper tail calls. */ +#undef Py_TAIL_CALL_INTERP + /* Define if you want to enable tracing references for debugging purpose */ #undef Py_TRACE_REFS From 8b2fb629334613fa34a79f0a53d297f77121ed58 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Feb 2025 18:47:29 +0300 Subject: [PATCH 089/311] gh-129707: Check `Tools/build/compute-changes.py` with `mypy` (#129708) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Tools/build/mypy.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini index 0e5d6e874a72e5..06224163884a98 100644 --- a/Tools/build/mypy.ini +++ b/Tools/build/mypy.ini @@ -1,5 +1,7 @@ [mypy] -files = Tools/build/generate_sbom.py +files = + Tools/build/compute-changes.py, + Tools/build/generate_sbom.py pretty = True # Make sure Python can still be built From 75c551974f74f7656fbb479b278e69c8200b4603 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 6 Feb 2025 21:33:52 +0530 Subject: [PATCH 090/311] gh-91048: fix thread safety for asyncio stack introspection APIs (#129399) --- Modules/_asynciomodule.c | 171 +++++++++++++++++------------- Modules/clinic/_asynciomodule.c.h | 52 ++++----- 2 files changed, 124 insertions(+), 99 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index b488fd92aa6817..a20a18db3607f0 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -549,30 +549,28 @@ future_init(FutureObj *fut, PyObject *loop) } static int -future_awaited_by_add(asyncio_state *state, PyObject *fut, PyObject *thing) +future_awaited_by_add(asyncio_state *state, FutureObj *fut, PyObject *thing) { - if (!TaskOrFuture_Check(state, fut) || !TaskOrFuture_Check(state, thing)) { - // We only want to support native asyncio Futures. - // For further insight see the comment in the Python - // implementation of "future_add_to_awaited_by()". - return 0; - } - - FutureObj *_fut = (FutureObj *)fut; + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + // We only want to support native asyncio Futures. + // For further insight see the comment in the Python + // implementation of "future_add_to_awaited_by()". + assert(TaskOrFuture_Check(state, fut)); + assert(TaskOrFuture_Check(state, thing)); /* Most futures/task are only awaited by one entity, so we want to avoid always creating a set for `fut_awaited_by`. */ - if (_fut->fut_awaited_by == NULL) { - assert(!_fut->fut_awaited_by_is_set); + if (fut->fut_awaited_by == NULL) { + assert(!fut->fut_awaited_by_is_set); Py_INCREF(thing); - _fut->fut_awaited_by = thing; + fut->fut_awaited_by = thing; return 0; } - if (_fut->fut_awaited_by_is_set) { - assert(PySet_CheckExact(_fut->fut_awaited_by)); - return PySet_Add(_fut->fut_awaited_by, thing); + if (fut->fut_awaited_by_is_set) { + assert(PySet_CheckExact(fut->fut_awaited_by)); + return PySet_Add(fut->fut_awaited_by, thing); } PyObject *set = PySet_New(NULL); @@ -583,40 +581,38 @@ future_awaited_by_add(asyncio_state *state, PyObject *fut, PyObject *thing) Py_DECREF(set); return -1; } - if (PySet_Add(set, _fut->fut_awaited_by)) { + if (PySet_Add(set, fut->fut_awaited_by)) { Py_DECREF(set); return -1; } - Py_SETREF(_fut->fut_awaited_by, set); - _fut->fut_awaited_by_is_set = 1; + Py_SETREF(fut->fut_awaited_by, set); + fut->fut_awaited_by_is_set = 1; return 0; } static int -future_awaited_by_discard(asyncio_state *state, PyObject *fut, PyObject *thing) +future_awaited_by_discard(asyncio_state *state, FutureObj *fut, PyObject *thing) { - if (!TaskOrFuture_Check(state, fut) || !TaskOrFuture_Check(state, thing)) { - // We only want to support native asyncio Futures. - // For further insight see the comment in the Python - // implementation of "future_add_to_awaited_by()". - return 0; - } - - FutureObj *_fut = (FutureObj *)fut; + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(fut); + // We only want to support native asyncio Futures. + // For further insight see the comment in the Python + // implementation of "future_add_to_awaited_by()". + assert(TaskOrFuture_Check(state, fut)); + assert(TaskOrFuture_Check(state, thing)); /* Following the semantics of 'set.discard()' here in not raising an error if `thing` isn't in the `awaited_by` "set". */ - if (_fut->fut_awaited_by == NULL) { + if (fut->fut_awaited_by == NULL) { return 0; } - if (_fut->fut_awaited_by == thing) { - Py_CLEAR(_fut->fut_awaited_by); + if (fut->fut_awaited_by == thing) { + Py_CLEAR(fut->fut_awaited_by); return 0; } - if (_fut->fut_awaited_by_is_set) { - assert(PySet_CheckExact(_fut->fut_awaited_by)); - int err = PySet_Discard(_fut->fut_awaited_by, thing); + if (fut->fut_awaited_by_is_set) { + assert(PySet_CheckExact(fut->fut_awaited_by)); + int err = PySet_Discard(fut->fut_awaited_by, thing); if (err < 0) { return -1; } else { @@ -626,36 +622,6 @@ future_awaited_by_discard(asyncio_state *state, PyObject *fut, PyObject *thing) return 0; } -/*[clinic input] -@critical_section -@getter -_asyncio.Future._asyncio_awaited_by -[clinic start generated code]*/ - -static PyObject * -_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self) -/*[clinic end generated code: output=932af76d385d2e2a input=64c1783df2d44d2b]*/ -{ - /* Implementation of a Python getter. */ - if (self->fut_awaited_by == NULL) { - Py_RETURN_NONE; - } - if (self->fut_awaited_by_is_set) { - /* Already a set, just wrap it into a frozen set and return. */ - assert(PySet_CheckExact(self->fut_awaited_by)); - return PyFrozenSet_New(self->fut_awaited_by); - } - - PyObject *set = PyFrozenSet_New(NULL); - if (set == NULL) { - return NULL; - } - if (PySet_Add(set, self->fut_awaited_by)) { - Py_DECREF(set); - return NULL; - } - return set; -} static PyObject * future_set_result(asyncio_state *state, FutureObj *fut, PyObject *res) @@ -1362,6 +1328,38 @@ _asyncio_Future_get_loop_impl(FutureObj *self, PyTypeObject *cls) return Py_NewRef(self->fut_loop); } +/*[clinic input] +@critical_section +@getter +_asyncio.Future._asyncio_awaited_by +[clinic start generated code]*/ + +static PyObject * +_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self) +/*[clinic end generated code: output=932af76d385d2e2a input=64c1783df2d44d2b]*/ +{ + /* Implementation of a Python getter. */ + if (self->fut_awaited_by == NULL) { + Py_RETURN_NONE; + } + if (self->fut_awaited_by_is_set) { + /* Already a set, just wrap it into a frozen set and return. */ + assert(PySet_CheckExact(self->fut_awaited_by)); + return PyFrozenSet_New(self->fut_awaited_by); + } + + PyObject *set = PyFrozenSet_New(NULL); + if (set == NULL) { + return NULL; + } + if (PySet_Add(set, self->fut_awaited_by)) { + Py_DECREF(set); + return NULL; + } + return set; +} + + /*[clinic input] @critical_section @getter @@ -3298,8 +3296,11 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu if (!fut->fut_blocking) { goto yield_insteadof_yf; } - - if (future_awaited_by_add(state, result, (PyObject *)task)) { + int res; + Py_BEGIN_CRITICAL_SECTION(result); + res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task); + Py_END_CRITICAL_SECTION(); + if (res) { goto fail; } @@ -3392,8 +3393,14 @@ task_step_handle_result_impl(asyncio_state *state, TaskObj *task, PyObject *resu goto yield_insteadof_yf; } - if (future_awaited_by_add(state, result, (PyObject *)task)) { - goto fail; + if (TaskOrFuture_Check(state, result)) { + int res; + Py_BEGIN_CRITICAL_SECTION(result); + res = future_awaited_by_add(state, (FutureObj *)result, (PyObject *)task); + Py_END_CRITICAL_SECTION(); + if (res) { + goto fail; + } } /* result._asyncio_future_blocking = False */ @@ -3608,8 +3615,14 @@ task_wakeup_lock_held(TaskObj *task, PyObject *o) asyncio_state *state = get_asyncio_state_by_def((PyObject *)task); - if (future_awaited_by_discard(state, o, (PyObject *)task)) { - return NULL; + if (TaskOrFuture_Check(state, o)) { + int res; + Py_BEGIN_CRITICAL_SECTION(o); + res = future_awaited_by_discard(state, (FutureObj *)o, (PyObject *)task); + Py_END_CRITICAL_SECTION(); + if (res) { + return NULL; + } } if (Future_CheckExact(state, o) || Task_CheckExact(state, o)) { @@ -4112,8 +4125,14 @@ _asyncio_future_add_to_awaited_by_impl(PyObject *module, PyObject *fut, /*[clinic end generated code: output=0ab9a1a63389e4df input=06e6eaac51f532b9]*/ { asyncio_state *state = get_asyncio_state(module); - if (future_awaited_by_add(state, fut, waiter)) { - return NULL; + if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) { + int res; + Py_BEGIN_CRITICAL_SECTION(fut); + res = future_awaited_by_add(state, (FutureObj *)fut, waiter); + Py_END_CRITICAL_SECTION(); + if (res) { + return NULL; + } } Py_RETURN_NONE; } @@ -4133,8 +4152,14 @@ _asyncio_future_discard_from_awaited_by_impl(PyObject *module, PyObject *fut, /*[clinic end generated code: output=a03b0b4323b779de input=3833f7639e88e483]*/ { asyncio_state *state = get_asyncio_state(module); - if (future_awaited_by_discard(state, fut, waiter)) { - return NULL; + if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) { + int res; + Py_BEGIN_CRITICAL_SECTION(fut); + res = future_awaited_by_add(state, (FutureObj *)fut, waiter); + Py_END_CRITICAL_SECTION(); + if (res) { + return NULL; + } } Py_RETURN_NONE; } diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index c6b7e39788be71..d25411ee9958a1 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -9,31 +9,6 @@ preserve #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#if !defined(_asyncio_Future__asyncio_awaited_by_DOCSTR) -# define _asyncio_Future__asyncio_awaited_by_DOCSTR NULL -#endif -#if defined(_ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF) -# undef _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF -# define _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF {"_asyncio_awaited_by", (getter)_asyncio_Future__asyncio_awaited_by_get, (setter)_asyncio_Future__asyncio_awaited_by_set, _asyncio_Future__asyncio_awaited_by_DOCSTR}, -#else -# define _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF {"_asyncio_awaited_by", (getter)_asyncio_Future__asyncio_awaited_by_get, NULL, _asyncio_Future__asyncio_awaited_by_DOCSTR}, -#endif - -static PyObject * -_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self); - -static PyObject * -_asyncio_Future__asyncio_awaited_by_get(PyObject *self, void *Py_UNUSED(context)) -{ - PyObject *return_value = NULL; - - Py_BEGIN_CRITICAL_SECTION(self); - return_value = _asyncio_Future__asyncio_awaited_by_get_impl((FutureObj *)self); - Py_END_CRITICAL_SECTION(); - - return return_value; -} - PyDoc_STRVAR(_asyncio_Future___init____doc__, "Future(*, loop=None)\n" "--\n" @@ -534,6 +509,31 @@ _asyncio_Future_get_loop(PyObject *self, PyTypeObject *cls, PyObject *const *arg return return_value; } +#if !defined(_asyncio_Future__asyncio_awaited_by_DOCSTR) +# define _asyncio_Future__asyncio_awaited_by_DOCSTR NULL +#endif +#if defined(_ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF) +# undef _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF +# define _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF {"_asyncio_awaited_by", (getter)_asyncio_Future__asyncio_awaited_by_get, (setter)_asyncio_Future__asyncio_awaited_by_set, _asyncio_Future__asyncio_awaited_by_DOCSTR}, +#else +# define _ASYNCIO_FUTURE__ASYNCIO_AWAITED_BY_GETSETDEF {"_asyncio_awaited_by", (getter)_asyncio_Future__asyncio_awaited_by_get, NULL, _asyncio_Future__asyncio_awaited_by_DOCSTR}, +#endif + +static PyObject * +_asyncio_Future__asyncio_awaited_by_get_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__asyncio_awaited_by_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _asyncio_Future__asyncio_awaited_by_get_impl((FutureObj *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + #if !defined(_asyncio_Future__asyncio_future_blocking_DOCSTR) # define _asyncio_Future__asyncio_future_blocking_DOCSTR NULL #endif @@ -2174,4 +2174,4 @@ _asyncio_future_discard_from_awaited_by(PyObject *module, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=fe4ffe08404ad566 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f14ff14c29c691ec input=a9049054013a1b77]*/ From e7bc0cd182eaef021160c6c0614bfd86c63d516f Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 6 Feb 2025 22:05:33 +0530 Subject: [PATCH 091/311] gh-91048: fix `_asyncio.future_discard_from_awaited_by` (#129731) fix discard --- Modules/_asynciomodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index a20a18db3607f0..09ab8f13fe1f5d 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -4155,7 +4155,7 @@ _asyncio_future_discard_from_awaited_by_impl(PyObject *module, PyObject *fut, if (TaskOrFuture_Check(state, fut) && TaskOrFuture_Check(state, waiter)) { int res; Py_BEGIN_CRITICAL_SECTION(fut); - res = future_awaited_by_add(state, (FutureObj *)fut, waiter); + res = future_awaited_by_discard(state, (FutureObj *)fut, waiter); Py_END_CRITICAL_SECTION(); if (res) { return NULL; From ded54c3baa57f6727be809f13633b393241c164a Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 6 Feb 2025 08:39:37 -0800 Subject: [PATCH 092/311] Remove an inaccurate note from `socket.recv` (GH-129733) Remove an inaccurate note from socket.recv. --- Doc/library/socket.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index b36acad29ecb00..b936a502ca886e 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1668,11 +1668,6 @@ to sockets. See the Unix manual page :manpage:`recv(2)` for the meaning of the optional argument *flags*; it defaults to zero. - .. note:: - - For best match with hardware and network realities, the value of *bufsize* - should be a relatively small power of 2, for example, 4096. - .. versionchanged:: 3.5 If the system call is interrupted and the signal handler does not raise an exception, the method now retries the system call instead of raising From 4d56c40440c9fd4499d61d24977336d8cd8d8d83 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:53:27 +0000 Subject: [PATCH 093/311] GH-121970: Extract ``implementation_detail`` into a new extension (#129663) --- Doc/conf.py | 1 + Doc/tools/extensions/implementation_detail.py | 47 +++++++++++++++++++ Doc/tools/extensions/pyspecific.py | 26 ---------- Doc/tools/templates/dummy.html | 15 +++--- 4 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 Doc/tools/extensions/implementation_detail.py diff --git a/Doc/conf.py b/Doc/conf.py index a4e0c628649018..56594f32c6d02d 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -28,6 +28,7 @@ 'changes', 'glossary_search', 'grammar_snippet', + 'implementation_detail', 'lexers', 'misc_news', 'pydoc_topics', diff --git a/Doc/tools/extensions/implementation_detail.py b/Doc/tools/extensions/implementation_detail.py new file mode 100644 index 00000000000000..fce8375e40d565 --- /dev/null +++ b/Doc/tools/extensions/implementation_detail.py @@ -0,0 +1,47 @@ +"""Support for marking up implementation details.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from docutils import nodes +from sphinx.locale import _ as sphinx_gettext +from sphinx.util.docutils import SphinxDirective + +if TYPE_CHECKING: + from sphinx.application import Sphinx + from sphinx.util.typing import ExtensionMetadata + + +class ImplementationDetail(SphinxDirective): + has_content = True + final_argument_whitespace = True + + # This text is copied to templates/dummy.html + label_text = sphinx_gettext("CPython implementation detail:") + + def run(self): + self.assert_has_content() + content_nodes = self.parse_content_to_nodes() + + # insert our prefix at the start of the first paragraph + first_node = content_nodes[0] + first_node[:0] = [ + nodes.strong(self.label_text, self.label_text), + nodes.Text(" "), + ] + + # create a new compound container node + cnode = nodes.compound("", *content_nodes, classes=["impl-detail"]) + self.set_source_info(cnode) + return [cnode] + + +def setup(app: Sphinx) -> ExtensionMetadata: + app.add_directive("impl-detail", ImplementationDetail) + + return { + "version": "1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 57cf80a7e77324..c12d169c0cfe7a 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -65,31 +65,6 @@ def gh_issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [refnode], [] -# Support for marking up implementation details - -class ImplementationDetail(SphinxDirective): - - has_content = True - final_argument_whitespace = True - - # This text is copied to templates/dummy.html - label_text = sphinx_gettext('CPython implementation detail:') - - def run(self): - self.assert_has_content() - pnode = nodes.compound(classes=['impl-detail']) - content = self.content - add_text = nodes.strong(self.label_text, self.label_text) - self.state.nested_parse(content, self.content_offset, pnode) - content = nodes.inline(pnode[0].rawsource, translatable=True) - content.source = pnode[0].source - content.line = pnode[0].line - content += pnode[0].children - pnode[0].replace_self(nodes.paragraph( - '', '', add_text, nodes.Text(' '), content, translatable=False)) - return [pnode] - - class PyCoroutineMixin(object): def handle_signature(self, sig, signode): ret = super(PyCoroutineMixin, self).handle_signature(sig, signode) @@ -219,7 +194,6 @@ def patch_pairindextypes(app, _env) -> None: def setup(app): app.add_role('issue', issue_role) app.add_role('gh', gh_issue_role) - app.add_directive('impl-detail', ImplementationDetail) app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature) app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command) app.add_object_type('monitoring-event', 'monitoring-event', '%s (monitoring event)', parse_monitoring_event) diff --git a/Doc/tools/templates/dummy.html b/Doc/tools/templates/dummy.html index 4f0f6f91436a87..0fdbe2a58017ff 100644 --- a/Doc/tools/templates/dummy.html +++ b/Doc/tools/templates/dummy.html @@ -1,12 +1,6 @@ This file is not an actual template, but used to add some texts in extensions to sphinx.pot file. -In extensions/pyspecific.py: - -{% trans %}CPython implementation detail:{% endtrans %} -{% trans %}Deprecated since version {deprecated}, will be removed in version {removed}{% endtrans %} -{% trans %}Deprecated since version {deprecated}, removed in version {removed}{% endtrans %} - In extensions/availability.py: {% trans %}Availability{% endtrans %} @@ -27,6 +21,15 @@ {% trans %}Return value: New reference.{% endtrans %} {% trans %}Return value: Borrowed reference.{% endtrans %} +In extensions/implementation_detail.py: + +{% trans %}CPython implementation detail:{% endtrans %} + +In extensions/changes.py: + +{% trans %}Deprecated since version {deprecated}, will be removed in version {removed}{% endtrans %} +{% trans %}Deprecated since version {deprecated}, removed in version {removed}{% endtrans %} + In docsbuild-scripts, when rewriting indexsidebar.html with actual versions: {% trans %}in development{% endtrans %} From 51b4edb1a4092f60d84f7d14eb41c12085e39c31 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 6 Feb 2025 12:38:12 -0500 Subject: [PATCH 094/311] gh-129668: Fix thread-safety of MemoryError freelist in free threaded build (gh-129704) The MemoryError freelist was not thread-safe in the free threaded build. Use a mutex to protect accesses to the freelist. Unlike other freelists, the MemoryError freelist is not performance sensitive. --- Include/internal/pycore_exceptions.h | 3 + ...-02-04-21-26-05.gh-issue-129668.zDanyM.rst | 2 + Objects/exceptions.c | 62 +++++++++++-------- 3 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-21-26-05.gh-issue-129668.zDanyM.rst diff --git a/Include/internal/pycore_exceptions.h b/Include/internal/pycore_exceptions.h index 4a9df709131998..26456d1966bbb0 100644 --- a/Include/internal/pycore_exceptions.h +++ b/Include/internal/pycore_exceptions.h @@ -24,6 +24,9 @@ struct _Py_exc_state { PyObject *errnomap; PyBaseExceptionObject *memerrors_freelist; int memerrors_numfree; +#ifdef Py_GIL_DISABLED + PyMutex memerrors_lock; +#endif // The ExceptionGroup type PyObject *PyExc_ExceptionGroup; }; diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-21-26-05.gh-issue-129668.zDanyM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-21-26-05.gh-issue-129668.zDanyM.rst new file mode 100644 index 00000000000000..e42ef57c3164a1 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-21-26-05.gh-issue-129668.zDanyM.rst @@ -0,0 +1,2 @@ +Fix race condition when raising :exc:`MemoryError` in the free threaded +build. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index ea2733435fc3ec..154cde9316866e 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -3832,36 +3832,43 @@ SimpleExtendsException(PyExc_Exception, ReferenceError, #define MEMERRORS_SAVE 16 +#ifdef Py_GIL_DISABLED +# define MEMERRORS_LOCK(state) PyMutex_LockFlags(&state->memerrors_lock, _Py_LOCK_DONT_DETACH) +# define MEMERRORS_UNLOCK(state) PyMutex_Unlock(&state->memerrors_lock) +#else +# define MEMERRORS_LOCK(state) ((void)0) +# define MEMERRORS_UNLOCK(state) ((void)0) +#endif + static PyObject * get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds) { - PyBaseExceptionObject *self; + PyBaseExceptionObject *self = NULL; struct _Py_exc_state *state = get_exc_state(); - if (state->memerrors_freelist == NULL) { - if (!allow_allocation) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return Py_NewRef( - &_Py_INTERP_SINGLETON(interp, last_resort_memory_error)); - } - PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds); - return result; - } - /* Fetch object from freelist and revive it */ - self = state->memerrors_freelist; - self->args = PyTuple_New(0); - /* This shouldn't happen since the empty tuple is persistent */ + MEMERRORS_LOCK(state); + if (state->memerrors_freelist != NULL) { + /* Fetch MemoryError from freelist and initialize it */ + self = state->memerrors_freelist; + state->memerrors_freelist = (PyBaseExceptionObject *) self->dict; + state->memerrors_numfree--; + self->dict = NULL; + self->args = (PyObject *)&_Py_SINGLETON(tuple_empty); + _Py_NewReference((PyObject *)self); + _PyObject_GC_TRACK(self); + } + MEMERRORS_UNLOCK(state); - if (self->args == NULL) { - return NULL; + if (self != NULL) { + return (PyObject *)self; } - state->memerrors_freelist = (PyBaseExceptionObject *) self->dict; - state->memerrors_numfree--; - self->dict = NULL; - _Py_NewReference((PyObject *)self); - _PyObject_GC_TRACK(self); - return (PyObject *)self; + if (!allow_allocation) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + return Py_NewRef( + &_Py_INTERP_SINGLETON(interp, last_resort_memory_error)); + } + return BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds); } static PyObject * @@ -3907,14 +3914,17 @@ MemoryError_dealloc(PyObject *obj) } struct _Py_exc_state *state = get_exc_state(); - if (state->memerrors_numfree >= MEMERRORS_SAVE) { - Py_TYPE(self)->tp_free((PyObject *)self); - } - else { + MEMERRORS_LOCK(state); + if (state->memerrors_numfree < MEMERRORS_SAVE) { self->dict = (PyObject *) state->memerrors_freelist; state->memerrors_freelist = self; state->memerrors_numfree++; + MEMERRORS_UNLOCK(state); + return; } + MEMERRORS_UNLOCK(state); + + Py_TYPE(self)->tp_free((PyObject *)self); } static int From 78377c788e02e91bf43d290d69317198a2e563fd Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 6 Feb 2025 09:42:47 -0800 Subject: [PATCH 095/311] gh-112020: Rework socketserver examples to be correct (#129741) gh-112020: Rework socketserver examples to be correct. Outdated code updated, the BaseRequestHandler example is now much more illustrative instead of the bad idea of a single recv() call for TCP. tested, they now work. --- Doc/library/socketserver.rst | 45 ++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index 69f06e6cf4d923..a01ed17f9ec6d6 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -499,11 +499,17 @@ This is the server side:: def handle(self): # self.request is the TCP socket connected to the client - self.data = self.request.recv(1024).strip() - print("Received from {}:".format(self.client_address[0])) - print(self.data) + pieces = [b''] + total = 0 + while b'\n' not in pieces[-1] and total < 10_000: + pieces.append(self.request.recv(2000)) + total += len(pieces[-1]) + self.data = b''.join(pieces) + print(f"Received from {self.client_address[0]}:") + print(self.data.decode("utf-8")) # just send back the same data, but upper-cased self.request.sendall(self.data.upper()) + # after we return, the socket will be closed. if __name__ == "__main__": HOST, PORT = "localhost", 9999 @@ -520,20 +526,24 @@ objects that simplify communication by providing the standard file interface):: class MyTCPHandler(socketserver.StreamRequestHandler): def handle(self): - # self.rfile is a file-like object created by the handler; - # we can now use e.g. readline() instead of raw recv() calls - self.data = self.rfile.readline().strip() - print("{} wrote:".format(self.client_address[0])) - print(self.data) + # self.rfile is a file-like object created by the handler. + # We can now use e.g. readline() instead of raw recv() calls. + # We limit ourselves to 10000 bytes to avoid abuse by the sender. + self.data = self.rfile.readline(10000).rstrip() + print(f"{self.client_address[0]} wrote:") + print(self.data.decode("utf-8")) # Likewise, self.wfile is a file-like object used to write back # to the client self.wfile.write(self.data.upper()) The difference is that the ``readline()`` call in the second handler will call ``recv()`` multiple times until it encounters a newline character, while the -single ``recv()`` call in the first handler will just return what has been -received so far from the client's ``sendall()`` call (typically all of it, but -this is not guaranteed by the TCP protocol). +the first handler had to use a ``recv()`` loop to accumulate data until a +newline itself. If it had just used a single ``recv()`` without the loop it +would just have returned what has been received so far from the client. +TCP is stream based: data arrives in the order it was sent, but there no +correlation between client ``send()`` or ``sendall()`` calls and the number +of ``recv()`` calls on the server required to receive it. This is the client side:: @@ -548,13 +558,14 @@ This is the client side:: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: # Connect to server and send data sock.connect((HOST, PORT)) - sock.sendall(bytes(data + "\n", "utf-8")) + sock.sendall(bytes(data, "utf-8")) + sock.sendall(b"\n") # Receive data from the server and shut down received = str(sock.recv(1024), "utf-8") - print("Sent: {}".format(data)) - print("Received: {}".format(received)) + print("Sent: ", data) + print("Received:", received) The output of the example should look something like this: @@ -599,7 +610,7 @@ This is the server side:: def handle(self): data = self.request[0].strip() socket = self.request[1] - print("{} wrote:".format(self.client_address[0])) + print(f"{self.client_address[0]} wrote:") print(data) socket.sendto(data.upper(), self.client_address) @@ -624,8 +635,8 @@ This is the client side:: sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT)) received = str(sock.recv(1024), "utf-8") - print("Sent: {}".format(data)) - print("Received: {}".format(received)) + print("Sent: ", data) + print("Received:", received) The output of the example should look exactly like for the TCP server example. From b4ff8b22b3066b814c3758f87eaddfa923e657ed Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Thu, 6 Feb 2025 13:49:29 -0500 Subject: [PATCH 096/311] gh-129732: Fix race on `shared->array` in qsbr code under free-threading (gh-129738) The read of `shared->array` should happen under the lock to avoid a race. --- .../2025-02-06-17-57-33.gh-issue-129732.yl97oq.rst | 1 + Python/qsbr.c | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-57-33.gh-issue-129732.yl97oq.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-57-33.gh-issue-129732.yl97oq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-57-33.gh-issue-129732.yl97oq.rst new file mode 100644 index 00000000000000..a4b104af61692a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-57-33.gh-issue-129732.yl97oq.rst @@ -0,0 +1 @@ +Fixed a race in ``_Py_qsbr_reserve`` in the free threading build. diff --git a/Python/qsbr.c b/Python/qsbr.c index a40219acfe2c29..0df1285cc8e063 100644 --- a/Python/qsbr.c +++ b/Python/qsbr.c @@ -205,15 +205,15 @@ _Py_qsbr_reserve(PyInterpreterState *interp) } _PyEval_StartTheWorld(interp); } - PyMutex_Unlock(&shared->mutex); - - if (qsbr == NULL) { - return -1; - } // Return an index rather than the pointer because the array may be // resized and the pointer invalidated. - return (struct _qsbr_pad *)qsbr - shared->array; + Py_ssize_t index = -1; + if (qsbr != NULL) { + index = (struct _qsbr_pad *)qsbr - shared->array; + } + PyMutex_Unlock(&shared->mutex); + return index; } void From 0d68b14a0d8f493b2f403f64608bcfc055457053 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 7 Feb 2025 00:21:07 +0530 Subject: [PATCH 097/311] gh-128002: use per threads tasks linked list in asyncio (#128869) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Langa --- Include/internal/pycore_interp.h | 7 + Include/internal/pycore_lock.h | 2 +- Include/internal/pycore_pystate.h | 4 +- Include/internal/pycore_tstate.h | 5 + Lib/test/test_asyncio/test_free_threading.py | 19 ++- Modules/_asynciomodule.c | 165 +++++++++++++------ Python/pystate.c | 12 +- 7 files changed, 156 insertions(+), 58 deletions(-) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 6f00eca8de05af..7fdfc7903477de 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -227,6 +227,13 @@ struct _is { PyMutex weakref_locks[NUM_WEAKREF_LIST_LOCKS]; _PyIndexPool tlbc_indices; #endif + // Per-interpreter list of tasks, any lingering tasks from thread + // states gets added here and removed from the corresponding + // thread state's list. + struct llist_node asyncio_tasks_head; + // `asyncio_tasks_lock` is used when tasks are moved + // from thread's list to interpreter's list. + PyMutex asyncio_tasks_lock; // Per-interpreter state for the obmalloc allocator. For the main // interpreter and for all interpreters that don't have their diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index 8bcb23a6ce9f9d..7484b05d7f2446 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -52,7 +52,7 @@ typedef enum _PyLockFlags { // Lock a mutex with an optional timeout and additional options. See // _PyLockFlags for details. -extern PyLockStatus +extern PyAPI_FUNC(PyLockStatus) _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout_ns, _PyLockFlags flags); // Lock a mutex with additional options. See _PyLockFlags for details. diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index ff3b222b157810..9ec59e60f609ab 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -182,8 +182,8 @@ extern void _PyEval_StartTheWorldAll(_PyRuntimeState *runtime); // Perform a stop-the-world pause for threads in the specified interpreter. // // NOTE: This is a no-op outside of Py_GIL_DISABLED builds. -extern void _PyEval_StopTheWorld(PyInterpreterState *interp); -extern void _PyEval_StartTheWorld(PyInterpreterState *interp); +extern PyAPI_FUNC(void) _PyEval_StopTheWorld(PyInterpreterState *interp); +extern PyAPI_FUNC(void) _PyEval_StartTheWorld(PyInterpreterState *interp); static inline void diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 74e1452763e56c..932623f54c4260 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -24,9 +24,14 @@ typedef struct _PyThreadStateImpl { PyObject *asyncio_running_loop; // Strong reference PyObject *asyncio_running_task; // Strong reference + /* Head of circular linked-list of all tasks which are instances of `asyncio.Task` + or subclasses of it used in `asyncio.all_tasks`. + */ + struct llist_node asyncio_tasks_head; struct _qsbr_thread_state *qsbr; // only used by free-threaded build struct llist_node mem_free_queue; // delayed free queue + #ifdef Py_GIL_DISABLED struct _gc_thread_state gc; struct _mimalloc_thread_state mimalloc; diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py index 6da398e77e7797..d0221d87062c5b 100644 --- a/Lib/test/test_asyncio/test_free_threading.py +++ b/Lib/test/test_asyncio/test_free_threading.py @@ -3,7 +3,8 @@ import unittest from threading import Thread from unittest import TestCase - +import weakref +from test import support from test.support import threading_helper threading_helper.requires_working_threading(module=True) @@ -95,6 +96,22 @@ def check(): done.set() runner.join() + def test_task_different_thread_finalized(self) -> None: + task = None + async def func(): + nonlocal task + task = asyncio.current_task() + + thread = Thread(target=lambda: asyncio.run(func())) + thread.start() + thread.join() + wr = weakref.ref(task) + del thread + del task + # task finalization in different thread shouldn't crash + support.gc_collect() + self.assertIsNone(wr()) + def test_run_coroutine_threadsafe(self) -> None: results = [] diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 09ab8f13fe1f5d..656c03a98d73b2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -67,6 +67,10 @@ typedef struct TaskObj { PyObject *task_name; PyObject *task_context; struct llist_node task_node; +#ifdef Py_GIL_DISABLED + // thread id of the thread where this task was created + uintptr_t task_tid; +#endif } TaskObj; typedef struct { @@ -94,14 +98,6 @@ typedef struct { || PyObject_TypeCheck(obj, state->FutureType) \ || PyObject_TypeCheck(obj, state->TaskType)) -#ifdef Py_GIL_DISABLED -# define ASYNCIO_STATE_LOCK(state) Py_BEGIN_CRITICAL_SECTION_MUT(&state->mutex) -# define ASYNCIO_STATE_UNLOCK(state) Py_END_CRITICAL_SECTION() -#else -# define ASYNCIO_STATE_LOCK(state) ((void)state) -# define ASYNCIO_STATE_UNLOCK(state) ((void)state) -#endif - typedef struct _Py_AsyncioModuleDebugOffsets { struct _asyncio_task_object { uint64_t size; @@ -135,9 +131,6 @@ GENERATE_DEBUG_SECTION(AsyncioDebug, Py_AsyncioModuleDebugOffsets AsyncioDebug) /* State of the _asyncio module */ typedef struct { -#ifdef Py_GIL_DISABLED - PyMutex mutex; -#endif PyTypeObject *FutureIterType; PyTypeObject *TaskStepMethWrapper_Type; PyTypeObject *FutureType; @@ -184,11 +177,6 @@ typedef struct { /* Counter for autogenerated Task names */ uint64_t task_name_counter; - /* Head of circular linked-list of all tasks which are instances of `asyncio.Task` - or subclasses of it. Third party tasks implementations which don't inherit from - `asyncio.Task` are tracked separately using the `non_asyncio_tasks` WeakSet. - */ - struct llist_node asyncio_tasks_head; } asyncio_state; static inline asyncio_state * @@ -2179,16 +2167,15 @@ static PyMethodDef TaskWakeupDef = { static void register_task(asyncio_state *state, TaskObj *task) { - ASYNCIO_STATE_LOCK(state); assert(Task_Check(state, task)); if (task->task_node.next != NULL) { // already registered assert(task->task_node.prev != NULL); - goto exit; + return; } - llist_insert_tail(&state->asyncio_tasks_head, &task->task_node); -exit: - ASYNCIO_STATE_UNLOCK(state); + _PyThreadStateImpl *tstate = (_PyThreadStateImpl *) _PyThreadState_GET(); + struct llist_node *head = &tstate->asyncio_tasks_head; + llist_insert_tail(head, &task->task_node); } static int @@ -2197,19 +2184,38 @@ register_eager_task(asyncio_state *state, PyObject *task) return PySet_Add(state->eager_tasks, task); } -static void -unregister_task(asyncio_state *state, TaskObj *task) +static inline void +unregister_task_safe(TaskObj *task) { - ASYNCIO_STATE_LOCK(state); - assert(Task_Check(state, task)); if (task->task_node.next == NULL) { // not registered assert(task->task_node.prev == NULL); - goto exit; + return; } llist_remove(&task->task_node); -exit: - ASYNCIO_STATE_UNLOCK(state); +} + +static void +unregister_task(asyncio_state *state, TaskObj *task) +{ + assert(Task_Check(state, task)); +#ifdef Py_GIL_DISABLED + // check if we are in the same thread + // if so, we can avoid locking + if (task->task_tid == _Py_ThreadId()) { + unregister_task_safe(task); + } + else { + // we are in a different thread + // stop the world then check and remove the task + PyThreadState *tstate = _PyThreadState_GET(); + _PyEval_StopTheWorld(tstate->interp); + unregister_task_safe(task); + _PyEval_StartTheWorld(tstate->interp); + } +#else + unregister_task_safe(task); +#endif } static int @@ -2423,6 +2429,9 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, } Py_CLEAR(self->task_fut_waiter); +#ifdef Py_GIL_DISABLED + self->task_tid = _Py_ThreadId(); +#endif self->task_must_cancel = 0; self->task_log_destroy_pending = 1; self->task_num_cancels_requested = 0; @@ -3981,6 +3990,7 @@ _asyncio_current_task_impl(PyObject *module, PyObject *loop) static inline int add_one_task(asyncio_state *state, PyObject *tasks, PyObject *task, PyObject *loop) { + assert(PySet_CheckExact(tasks)); PyObject *done = PyObject_CallMethodNoArgs(task, &_Py_ID(done)); if (done == NULL) { return -1; @@ -4003,6 +4013,57 @@ add_one_task(asyncio_state *state, PyObject *tasks, PyObject *task, PyObject *lo return 0; } +static inline int +add_tasks_llist(struct llist_node *head, PyListObject *tasks) +{ + struct llist_node *node; + llist_for_each_safe(node, head) { + TaskObj *task = llist_data(node, TaskObj, task_node); + // The linked list holds borrowed references to task + // as such it is possible that the task is concurrently + // deallocated while added to this list. + // To protect against concurrent deallocations, + // we first try to incref the task which would fail + // if it is concurrently getting deallocated in another thread, + // otherwise it gets added to the list. + if (_Py_TryIncref((PyObject *)task)) { + if (_PyList_AppendTakeRef(tasks, (PyObject *)task) < 0) { + // do not call any escaping calls here while the world is stopped. + return -1; + } + } + } + return 0; +} + +static inline int +add_tasks_interp(PyInterpreterState *interp, PyListObject *tasks) +{ +#ifdef Py_GIL_DISABLED + assert(interp->stoptheworld.world_stopped); +#endif + // Start traversing from interpreter's linked list + struct llist_node *head = &interp->asyncio_tasks_head; + + if (add_tasks_llist(head, tasks) < 0) { + return -1; + } + + int ret = 0; + // traverse the task lists of thread states + _Py_FOR_EACH_TSTATE_BEGIN(interp, p) { + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)p; + head = &ts->asyncio_tasks_head; + if (add_tasks_llist(head, tasks) < 0) { + ret = -1; + goto exit; + } + } +exit: + _Py_FOR_EACH_TSTATE_END(interp); + return ret; +} + /*********************** Module **************************/ /*[clinic input] @@ -4041,30 +4102,29 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) Py_DECREF(loop); return NULL; } - int err = 0; - ASYNCIO_STATE_LOCK(state); - struct llist_node *node; - - llist_for_each_safe(node, &state->asyncio_tasks_head) { - TaskObj *task = llist_data(node, TaskObj, task_node); - // The linked list holds borrowed references to task - // as such it is possible that the task is concurrently - // deallocated while added to this list. - // To protect against concurrent deallocations, - // we first try to incref the task which would fail - // if it is concurrently getting deallocated in another thread, - // otherwise it gets added to the list. - if (_Py_TryIncref((PyObject *)task)) { - if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) { - Py_DECREF(tasks); - Py_DECREF(loop); - err = 1; - break; - } - } - } - ASYNCIO_STATE_UNLOCK(state); - if (err) { + PyInterpreterState *interp = PyInterpreterState_Get(); + // Stop the world and traverse the per-thread linked list + // of asyncio tasks for every thread, as well as the + // interpreter's linked list, and add them to `tasks`. + // The interpreter linked list is used for any lingering tasks + // whose thread state has been deallocated while the task was + // still alive. This can happen if a task is referenced by + // a different thread, in which case the task is moved to + // the interpreter's linked list from the thread's linked + // list before deallocation. See PyThreadState_Clear. + // + // The stop-the-world pause is required so that no thread + // modifies its linked list while being iterated here + // in parallel. This design allows for lock-free + // register_task/unregister_task for loops running in parallel + // in different threads (the general case). + _PyEval_StopTheWorld(interp); + int ret = add_tasks_interp(interp, (PyListObject *)tasks); + _PyEval_StartTheWorld(interp); + if (ret < 0) { + // call any escaping calls after starting the world to avoid any deadlocks. + Py_DECREF(tasks); + Py_DECREF(loop); return NULL; } PyObject *scheduled_iter = PyObject_GetIter(state->non_asyncio_tasks); @@ -4348,7 +4408,6 @@ module_exec(PyObject *mod) { asyncio_state *state = get_asyncio_state(mod); - llist_init(&state->asyncio_tasks_head); #define CREATE_TYPE(m, tp, spec, base) \ do { \ diff --git a/Python/pystate.c b/Python/pystate.c index e6770ef40df740..89a652850e9363 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -643,6 +643,8 @@ init_interpreter(PyInterpreterState *interp, _Py_brc_init_state(interp); #endif llist_init(&interp->mem_free_queue.head); + llist_init(&interp->asyncio_tasks_head); + interp->asyncio_tasks_lock = (PyMutex){0}; for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { interp->monitors.tools[i] = 0; } @@ -1512,7 +1514,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, tstate->delete_later = NULL; llist_init(&_tstate->mem_free_queue); - + llist_init(&_tstate->asyncio_tasks_head); if (interp->stoptheworld.requested || _PyRuntime.stoptheworld.requested) { // Start in the suspended state if there is an ongoing stop-the-world. tstate->state = _Py_THREAD_SUSPENDED; @@ -1692,6 +1694,14 @@ PyThreadState_Clear(PyThreadState *tstate) Py_CLEAR(((_PyThreadStateImpl *)tstate)->asyncio_running_loop); Py_CLEAR(((_PyThreadStateImpl *)tstate)->asyncio_running_task); + + PyMutex_Lock(&tstate->interp->asyncio_tasks_lock); + // merge any lingering tasks from thread state to interpreter's + // tasks list + llist_concat(&tstate->interp->asyncio_tasks_head, + &((_PyThreadStateImpl *)tstate)->asyncio_tasks_head); + PyMutex_Unlock(&tstate->interp->asyncio_tasks_lock); + Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); From 365cf5fc23835fa6dc8608396109085f31d2d5f0 Mon Sep 17 00:00:00 2001 From: Bogdan Romanyuk <65823030+wrongnull@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:35:37 +0300 Subject: [PATCH 098/311] gh-117657: Fix data race in `new_reference` for free threaded build (gh-129665) --- Objects/object.c | 9 ++++++++- Tools/tsan/suppressions_free_threading.txt | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Objects/object.c b/Objects/object.c index fdff16138201a0..f3c7fa6d906ad6 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2485,13 +2485,20 @@ new_reference(PyObject *op) op->ob_refcnt = 1; #endif #else - op->ob_tid = _Py_ThreadId(); op->ob_flags = 0; op->ob_mutex = (PyMutex){ 0 }; +#ifdef _Py_THREAD_SANITIZER + _Py_atomic_store_uintptr_relaxed(&op->ob_tid, _Py_ThreadId()); + _Py_atomic_store_uint8_relaxed(&op->ob_gc_bits, 0); + _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 1); + _Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, 0); +#else + op->ob_tid = _Py_ThreadId(); op->ob_gc_bits = 0; op->ob_ref_local = 1; op->ob_ref_shared = 0; #endif +#endif #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op); #endif diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index e5eb665ae212de..b25b3700b35613 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -22,7 +22,6 @@ race:free_threadstate # These warnings trigger directly in a CPython function. race_top:assign_version_tag -race_top:new_reference race_top:_multiprocessing_SemLock_acquire_impl race_top:list_get_item_ref race_top:_Py_slot_tp_getattr_hook From 4e3330f054b91049c7260eb02b1e2c3808958e11 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 6 Feb 2025 17:03:01 -0500 Subject: [PATCH 099/311] gh-117657: Skip tests that may cause stack overflows under TSan (#129751) These tests crash under TSan due to stack overflows. Just skip them if TSan is enabled. --- Lib/test/test_call.py | 4 +++- Lib/test/test_functools.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index a4115d54ce5995..9d73d0e690ed58 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1,6 +1,7 @@ import unittest from test.support import (cpython_only, is_wasi, requires_limited_api, Py_DEBUG, - set_recursion_limit, skip_on_s390x, skip_emscripten_stack_overflow, import_helper) + set_recursion_limit, skip_on_s390x, skip_emscripten_stack_overflow, + skip_if_sanitizer, import_helper) try: import _testcapi except ImportError: @@ -1036,6 +1037,7 @@ class TestRecursion(unittest.TestCase): @skip_on_s390x @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") + @skip_if_sanitizer("requires deep stack", thread=True) @unittest.skipIf(_testcapi is None, "requires _testcapi") @skip_emscripten_stack_overflow() def test_super_deep(self): diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 4beb4380c3ad6b..1b7a76bec839bf 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2067,6 +2067,7 @@ def orig(a, /, b, c=True): ... @support.skip_on_s390x @unittest.skipIf(support.is_wasi, "WASI has limited C stack") + @support.skip_if_sanitizer("requires deep stack", thread=True) @support.skip_emscripten_stack_overflow() def test_lru_recursion(self): From b184abf074c0e1f379a238f07da5616460f36b93 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 7 Feb 2025 07:41:13 +0900 Subject: [PATCH 100/311] gh-129533: Update PyGC_Enable/Disable/IsEnabled to use atomic operation (gh-129563) --- ...5-02-02-12-58-21.gh-issue-129533.dFfqkT.rst | 3 +++ Python/gc_free_threading.c | 18 +++++++----------- 2 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-02-02-12-58-21.gh-issue-129533.dFfqkT.rst diff --git a/Misc/NEWS.d/next/C_API/2025-02-02-12-58-21.gh-issue-129533.dFfqkT.rst b/Misc/NEWS.d/next/C_API/2025-02-02-12-58-21.gh-issue-129533.dFfqkT.rst new file mode 100644 index 00000000000000..20e93e2bcc1f47 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-02-02-12-58-21.gh-issue-129533.dFfqkT.rst @@ -0,0 +1,3 @@ +Update :c:func:`PyGC_Enable()`, :c:func:`PyGC_Disable()`, +:c:func:`PyGC_IsEnabled()` to use atomic operation for thread-safety +at free-threading build. Patch by Donghee Na. diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 10c76a67979884..9e459da3a44370 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -1864,7 +1864,8 @@ gc_should_collect(GCState *gcstate) { int count = _Py_atomic_load_int_relaxed(&gcstate->young.count); int threshold = gcstate->young.threshold; - if (count <= threshold || threshold == 0 || !gcstate->enabled) { + int gc_enabled = _Py_atomic_load_int_relaxed(&gcstate->enabled); + if (count <= threshold || threshold == 0 || !gc_enabled) { return false; } // Avoid quadratic behavior by scaling threshold to the number of live @@ -2340,25 +2341,21 @@ int PyGC_Enable(void) { GCState *gcstate = get_gc_state(); - int old_state = gcstate->enabled; - gcstate->enabled = 1; - return old_state; + return _Py_atomic_exchange_int(&gcstate->enabled, 1); } int PyGC_Disable(void) { GCState *gcstate = get_gc_state(); - int old_state = gcstate->enabled; - gcstate->enabled = 0; - return old_state; + return _Py_atomic_exchange_int(&gcstate->enabled, 0); } int PyGC_IsEnabled(void) { GCState *gcstate = get_gc_state(); - return gcstate->enabled; + return _Py_atomic_load_int_relaxed(&gcstate->enabled); } /* Public API to invoke gc.collect() from C */ @@ -2368,7 +2365,7 @@ PyGC_Collect(void) PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = &tstate->interp->gc; - if (!gcstate->enabled) { + if (!_Py_atomic_load_int_relaxed(&gcstate->enabled)) { return 0; } @@ -2527,8 +2524,7 @@ _PyObject_GC_Link(PyObject *op) void _Py_RunGC(PyThreadState *tstate) { - GCState *gcstate = get_gc_state(); - if (!gcstate->enabled) { + if (!PyGC_IsEnabled()) { return; } gc_collect_main(tstate, 0, _Py_GC_REASON_HEAP); From a191d6f78e10268845b24483b3ac65856d7af868 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 6 Feb 2025 18:37:05 -0500 Subject: [PATCH 101/311] gh-117657: Include all of test_free_threading in TSAN tests (#129749) --- Lib/test/libregrtest/tsan.py | 2 +- Tools/tsan/suppressions_free_threading.txt | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index 1b32deec12bd75..90c9f0db0af2bf 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -25,7 +25,7 @@ 'test_threading_local', 'test_threadsignals', 'test_weakref', - 'test_free_threading.test_slots', + 'test_free_threading', ] # Tests that should be run with `--parallel-threads=N` under TSAN. These tests diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index b25b3700b35613..c2509cae7b9e9d 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -43,5 +43,11 @@ race_top:PyThreadState_Clear # Only seen on macOS, sample: https://gist.github.com/aisk/dda53f5d494a4556c35dde1fce03259c race_top:set_default_allocator_unlocked +# gh-129068: race on shared range iterators (test_free_threading.test_zip.ZipThreading.test_threading) +race_top:rangeiter_next + +# gh-129748: test.test_free_threading.test_slots.TestSlots.test_object +race_top:mi_block_set_nextx + # https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 thread:pthread_create From e1e85204edbf8c0c9ba1e50c74ac8708553585d8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 6 Feb 2025 18:35:55 -0600 Subject: [PATCH 102/311] Add multinomial to the itertools recipes docs (gh-129760) --- Doc/library/itertools.rst | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index eb61453718bd3c..fbbec42ce9dae7 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -838,10 +838,10 @@ and :term:`generators ` which incur interpreter overhead. .. testcode:: - from collections import deque + from collections import Counter, deque from contextlib import suppress from functools import reduce - from math import sumprod, isqrt + from math import comb, prod, sumprod, isqrt from operator import itemgetter, getitem, mul, neg def take(n, iterable): @@ -1127,6 +1127,12 @@ The following recipes have a more mathematical flavor: n -= n // prime return n + def multinomial(*counts): + "Number of distinct arrangements of a multiset." + # Counter('abracadabra').values() -> 5 2 1 1 2 + # multinomial(5, 2, 1, 1, 2) → 83160 + return prod(map(comb, accumulate(counts), counts)) + .. doctest:: :hide: @@ -1730,6 +1736,12 @@ The following recipes have a more mathematical flavor: >>> ''.join(it) 'DEF1' + >>> multinomial(5, 2, 1, 1, 2) + 83160 + >>> word = 'coffee' + >>> multinomial(*Counter(word).values()) == len(set(permutations(word))) + True + .. testcode:: :hide: From 43e024021392c8c70e5a56cdf7428ced45d73688 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 7 Feb 2025 01:47:13 +0100 Subject: [PATCH 103/311] gh-129737: Fix help message for tail calling interpreter configuration (GH-129754) --- configure | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 22456959add174..65b8e711cdccae 100755 --- a/configure +++ b/configure @@ -1930,7 +1930,7 @@ Optional Packages: use libedit for backend or disable readline module --with-computed-gotos enable computed gotos in evaluation loop (enabled by default on supported compilers) - --tail-call-interp enable tail-calling interpreter in evaluation loop + --with-tail-call-interp enable tail-calling interpreter in evaluation loop and rest of CPython --with-ensurepip[=install|upgrade|no] "install" or "upgrade" using bundled pip (default is diff --git a/configure.ac b/configure.ac index cf16e77f0a1503..0c6063d87d654a 100644 --- a/configure.ac +++ b/configure.ac @@ -7022,7 +7022,7 @@ AC_MSG_CHECKING([for --with-tail-call-interp]) AC_ARG_WITH( [tail-call-interp], [AS_HELP_STRING( - [--tail-call-interp], + [--with-tail-call-interp], [enable tail-calling interpreter in evaluation loop and rest of CPython] )], [ From 0fef47e5bbd167c21eb4f3cbd885cf61270014e7 Mon Sep 17 00:00:00 2001 From: Forest Date: Fri, 7 Feb 2025 03:15:11 +0000 Subject: [PATCH 104/311] gh-55454: Add IMAP4 IDLE support to imaplib (#122542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gh-55454: Add IMAP4 IDLE support to imaplib This extends imaplib with support for the rfc2177 IMAP IDLE command, as requested in #55454. It allows events to be pushed to a client as they occur, rather than having to continually poll for mailbox changes. The interface is a new idle() method, which returns an iterable context manager. Entering the context starts IDLE mode, during which events (untagged responses) can be retrieved using the iteration protocol. Exiting the context sends DONE to the server, ending IDLE mode. An optional time limit for the IDLE session is supported, for use with servers that impose an inactivity timeout. The context manager also offers a burst() method, designed for programs wishing to process events in batch rather than one at a time. Notable differences from other implementations: - It's an extension to imaplib, rather than a replacement. - It doesn't introduce additional threads. - It doesn't impose new requirements on the use of imaplib's existing methods. - It passes the unit tests in CPython's test/test_imaplib.py module (and adds new ones). - It works on Windows, Linux, and other unix-like systems. - It makes IDLE available on all of imaplib's client variants (including IMAP4_stream). - The interface is pythonic and easy to use. Caveats: - Due to a Windows limitation, the special case of IMAP4_stream running on Windows lacks a duration/timeout feature. (This is the stdin/stdout pipe connection variant; timeouts work fine for socket-based connections, even on Windows.) I have documented it where appropriate. - The file-like imaplib instance attributes are changed from buffered to unbuffered mode. This could potentially break any client code that uses those objects directly without expecting partial reads/writes. However, these attributes are undocumented. As such, I think (and PEP 8 confirms) that they are fair game for changes. https://peps.python.org/pep-0008/#public-and-internal-interfaces Usage examples: https://github.com/python/cpython/issues/55454#issuecomment-2227543041 Original discussion: https://discuss.python.org/t/gauging-interest-in-my-imap4-idle-implementation-for-imaplib/59272 Earlier requests and suggestions: https://github.com/python/cpython/issues/55454 https://mail.python.org/archives/list/python-ideas@python.org/thread/C4TVEYL5IBESQQPPS5GBR7WFBXCLQMZ2/ * gh-55454: Clarify imaplib idle() docs - Add example idle response tuples, to make the minor difference from other imaplib response tuples more obvious. - Merge the idle context manager's burst() method docs with the IMAP object's idle() method docs, for easier understanding. - Upgrade the Windows note regarding lack of pipe timeouts to a warning. - Rephrase various things for clarity. * docs: words instead of <= Co-authored-by: Peter Bierma * docs: improve style in an example Co-authored-by: Peter Bierma * docs: grammatical edit Co-authored-by: Peter Bierma * docs consistency Co-authored-by: Peter Bierma * comment -> docstring Co-authored-by: Peter Bierma * docs: refer to imaplib as "this module" Co-authored-by: Peter Bierma * imaplib: simplify & clarify idle debug message Co-authored-by: Peter Bierma * imaplib: elaborate in idle context manager comment * imaplib: re-raise BaseException instead of bare except Co-authored-by: Peter Bierma * imaplib: convert private doc string to comment * docs: correct mistake in imaplib example This is a correction to 8077f2eab287703b77350f1bfc9db2bd236dd9a7, which changed a variable name in only one place and broke the subsequent reference to it, departed from the naming convention used in the rest of the module, and shadowed the type() builtin along the way. * imaplib: simplify example code in doc string This is for consistency with the documentation change in 8077f2eab287 and subsequent correction in 013bbf18fc42. * imaplib: rename _Idler to Idler, update its docs * imaplib: add comment in Idler._pop() Co-authored-by: Peter Bierma * imaplib: remove unnecessary blank line Co-authored-by: Peter Bierma * imaplib: comment on use of unbuffered pipes * docs: imaplib: use the reStructuredText :class: role Co-authored-by: Peter Bierma * Revert "docs: imaplib: use the reStructuredText :class: role" This reverts commit f385e441df15d962d1f22e9bab2f15a39e5363d5, because it triggers CI failures in the docs by referencing a class that is (deliberately) undocumented. * docs: imaplib: use the reST :class: role, escaped This is a different approach to f385e441df15, which was reverted for creating dangling link references. By prefixing the reStructuredText role target with a ! we disable conversion to a link, thereby passing continuous integration checks even though the referenced class is deliberately absent from the documentation. * docs: refer to IMAP4 IDLE instead of just IDLE This clarifies that we are referring to the email protocol, not the editor with the same name. Co-authored-by: Guido van Rossum * imaplib: IDLE -> IMAP4 IDLE in exception message Co-authored-by: Peter Bierma * docs: imaplib idle() phrasing and linking tweaks * docs: imaplib: avoid linking to an invalid target This reverts and rephrases part of a3f21cd75b4d7c97c0d7e46b0a0cc0875e29f6cc which created links to a method on a deliberately undocumented class. The links didn't work consistently, and caused sphinx warnings that broke cpython's continuous integration tests. * imaplib: update test after recent exception change This fixes a test that was broken by changing an exception in b01de95171d6124f8acc7b907c1842472ea5f5fb * imaplib: rename idle() dur argument to duration * imaplib: bytes.index() -> bytes.find() This makes it more obvious which statement triggers the branch. * imaplib: remove no-longer-necessary statement Co-authored-by: Martin Panter * docs: imaplib: concise & valid method links The burst() method is a little tricky to link in restructuredText, due to quirks of its parent class. This syntax allows sphinx to generate working links without generating warnings (which break continuous integration) and without burdening the reader with unimportant namespace qualifications. It makes the reST source ugly, but few people read the reST source, so it's a tolerable tradeoff. * imaplib: note data types present in IDLE responses * docs: imaplib: add comma to reST changes header Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> * imaplib: sync doc strings with reST docs * docs: imaplib: minor Idler clarifications * imaplib: idle: emit (type, [data, ...]) tuples This allows our iterator to emit untagged responses that contain literal strings in the same way that imaplib's existing methods do, while still emitting exactly one whole response per iteration. * imaplib: while/yield instead of yield from iter() * imaplib: idle: use deadline idiom when iterating This simplifies the code, and avoids idle duration drift from time spent processing each iteration. * docs: imaplib: state duration/interval arg types * docs: imaplib: minor rephrasing of a sentence * docs: imaplib: reposition a paragraph This might improve readability, especially when encountering Idler.burst() for the first time. * docs: imaplib: wrap long lines in idle() section * docs: imaplib: note: Idler objects require 'with' * docs: imaplib: say that 29 minutes is 1740 seconds * docs: imaplib: mark a paragraph as a 'tip' * docs: imaplib: rephrase reference to MS Windows * imaplib: end doc string titles with a period * imaplib: idle: socket timeouts instead of select() IDLE timeouts were originally implemented using select() after checking for the presence of already-buffered data. That allowed timeouts on pipe connetions like IMAP4_stream. However, it seemed possible that SSL data arriving without any IMAP data afterward could cause select() to indicate available application data when there was none, leading to a read() call that would block with no timeout. It was unclear under what conditions this would happen in practice. This change switches to socket timeouts instead of select(), just to be safe. This also reverts IMAP4_stream changes that were made to support IDLE timeouts, since our new implementation only supports socket connections. * imaplib: Idler: rename private state attributes * imaplib: rephrase a comment in example code * docs: imaplib: idle: use Sphinx code-block:: pycon * docs: whatsnew: imaplib: reformat IMAP4.idle entry * imaplib: idle: make doc strings brief Since we generally rely on the reST/html documentation for details, we can keep these doc strings short. This matches the module's existing doc string style and avoids having to sync small changes between two files. * imaplib: Idler: split assert into two statements * imaplib: Idler: move assignment out of try: block * imaplib: Idler: move __exit__() for readability * imaplib: Idler: move __next__() for readability * imaplib: test: make IdleCmdHandler a global class * docs: imaplib: idle: collapse double-spaces * imaplib: warn on use of undocumented 'file' attr * imaplib: revert import reformatting Since we no longer import platform or selectors, the original import statement style can be restored, reducing the footprint of PR #122542. * imaplib: restore original exception msg formatting This reduces the footprint of PR #122542. * docs: imaplib: idle: versionadded:: next * imaplib: move import statement to where it's used This import is only needed if external code tries to use an attribute that it shouldn't be using. Making it a local import reduces module loading time in supported cases. * imaplib test: RuntimeWarning on IMAP4.file access * imaplib: use stacklevel=2 in warnings.warn() * imaplib test: simplify IMAP4.file warning test * imaplib test: pre-idle-continuation response * imaplib test: post-done untagged response * imaplib: downgrade idle-denied exception to error This makes it easier for client code to distinguish a temporary rejection of the IDLE command from a server responding incorrectly to IDLE. * imaplib: simplify check for socket object * imaplib: narrow the scope of IDLE socket timeouts If an IDLE duration or burst() was in use, and an unsolicited response contained a literal string, and crossed a packet boundary, and the subsequent packet was delayed beyond the IDLE feature's time limit, the timeout would leave the incoming protocol stream in a bad state (with the tail of that response appearing where the start of a response is expected). This change moves the IDLE socket timeout to cover only the start of a response, so it can no longer cause that problem. * imaplib: preserve partial reads on exception This ensures that short IDLE durations / burst() intervals won't risk corrupting response lines that span multiple packets. * imaplib: read/readline: save multipart buffer tail For resilience if read() or readline() ever complete with more than one bytes object remaining in the buffer. This is not expected to happen, but it seems wise to be prepared for a future change making it possible. * imaplib: use TimeoutError subclass only if needed * doc: imaplib: elaborate on IDLE response delivery * doc: imaplib: elaborate in note re: IMAP4.response * imaplib: comment on benefit of reading in chunks Our read() implementation designed to support IDLE replaces the one from PR #119514, fixing the same problem it was addressing. The tests that it added are preserved. * imaplib: readline(): treat ConnectionError as EOF --------- Co-authored-by: Gregory P. Smith Co-authored-by: Peter Bierma Co-authored-by: Guido van Rossum Co-authored-by: Guido van Rossum Co-authored-by: Martin Panter Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/imaplib.rst | 90 ++++- Doc/whatsnew/3.14.rst | 6 + Lib/imaplib.py | 354 +++++++++++++++++- Lib/test/test_imaplib.py | 123 ++++++ Misc/ACKS | 1 + ...4-08-01-01-00-00.gh-issue-55454.wy0vGw.rst | 1 + 6 files changed, 555 insertions(+), 20 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-08-01-01-00-00.gh-issue-55454.wy0vGw.rst diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index a2dad58b00b9fa..2c5a1f1fbc1213 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -10,6 +10,7 @@ .. changes for IMAP4_SSL by Tino Lange , March 2002 .. changes for IMAP4_stream by Piers Lauder , November 2002 +.. changes for IMAP4 IDLE by Forest , August 2024 **Source code:** :source:`Lib/imaplib.py` @@ -187,7 +188,7 @@ However, the *password* argument to the ``LOGIN`` command is always quoted. If you want to avoid having an argument string quoted (eg: the *flags* argument to ``STORE``) then enclose the string in parentheses (eg: ``r'(\Deleted)'``). -Each command returns a tuple: ``(type, [data, ...])`` where *type* is usually +Most commands return a tuple: ``(type, [data, ...])`` where *type* is usually ``'OK'`` or ``'NO'``, and *data* is either the text from the command response, or mandated results from the command. Each *data* is either a ``bytes``, or a tuple. If a tuple, then the first part is the header of the response, and the @@ -307,6 +308,93 @@ An :class:`IMAP4` instance has the following methods: of the IMAP4 QUOTA extension defined in rfc2087. +.. method:: IMAP4.idle(duration=None) + + Return an :class:`!Idler`: an iterable context manager implementing the + IMAP4 ``IDLE`` command as defined in :rfc:`2177`. + + The returned object sends the ``IDLE`` command when activated by the + :keyword:`with` statement, produces IMAP untagged responses via the + :term:`iterator` protocol, and sends ``DONE`` upon context exit. + + All untagged responses that arrive after sending the ``IDLE`` command + (including any that arrive before the server acknowledges the command) will + be available via iteration. Any leftover responses (those not iterated in + the :keyword:`with` context) can be retrieved in the usual way after + ``IDLE`` ends, using :meth:`IMAP4.response`. + + Responses are represented as ``(type, [data, ...])`` tuples, as described + in :ref:`IMAP4 Objects `. + + The *duration* argument sets a maximum duration (in seconds) to keep idling, + after which any ongoing iteration will stop. It can be an :class:`int` or + :class:`float`, or ``None`` for no time limit. + Callers wishing to avoid inactivity timeouts on servers that impose them + should keep this at most 29 minutes (1740 seconds). + Requires a socket connection; *duration* must be ``None`` on + :class:`IMAP4_stream` connections. + + .. code-block:: pycon + + >>> with M.idle(duration=29 * 60) as idler: + ... for typ, data in idler: + ... print(typ, data) + ... + EXISTS [b'1'] + RECENT [b'1'] + + + .. method:: Idler.burst(interval=0.1) + + Yield a burst of responses no more than *interval* seconds apart + (expressed as an :class:`int` or :class:`float`). + + This :term:`generator` is an alternative to iterating one response at a + time, intended to aid in efficient batch processing. It retrieves the + next response along with any immediately available subsequent responses. + (For example, a rapid series of ``EXPUNGE`` responses after a bulk + delete.) + + Requires a socket connection; does not work on :class:`IMAP4_stream` + connections. + + .. code-block:: pycon + + >>> with M.idle() as idler: + ... # get a response and any others following by < 0.1 seconds + ... batch = list(idler.burst()) + ... print(f'processing {len(batch)} responses...') + ... print(batch) + ... + processing 3 responses... + [('EXPUNGE', [b'2']), ('EXPUNGE', [b'1']), ('RECENT', [b'0'])] + + .. tip:: + + The ``IDLE`` context's maximum duration, as passed to + :meth:`IMAP4.idle`, is respected when waiting for the first response + in a burst. Therefore, an expired :class:`!Idler` will cause this + generator to return immediately without producing anything. Callers + should consider this if using it in a loop. + + + .. note:: + + The iterator returned by :meth:`IMAP4.idle` is usable only within a + :keyword:`with` statement. Before or after that context, unsolicited + responses are collected internally whenever a command finishes, and can + be retrieved with :meth:`IMAP4.response`. + + .. note:: + + The :class:`!Idler` class name and structure are internal interfaces, + subject to change. Calling code can rely on its context management, + iteration, and public method to remain stable, but should not subclass, + instantiate, compare, or otherwise directly reference the class. + + .. versionadded:: next + + .. method:: IMAP4.list([directory[, pattern]]) List mailbox names in *directory* matching *pattern*. *directory* defaults to diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 59c432d30a342b..a95fc7a4b6a9fa 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -456,6 +456,12 @@ inspect :term:`package` or not. (Contributed by Zhikang Yan in :gh:`125634`.) +imaplib +------- + +* Add :meth:`IMAP4.idle() `, implementing the IMAP4 + ``IDLE`` command as defined in :rfc:`2177`. + (Contributed by Forest in :gh:`55454`.) io diff --git a/Lib/imaplib.py b/Lib/imaplib.py index db708580a0abf6..2c3925958d011b 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -19,8 +19,9 @@ # GET/SETQUOTA contributed by Andreas Zeidler June 2002. # PROXYAUTH contributed by Rick Holbert November 2002. # GET/SETANNOTATION contributed by Tomas Lindroos June 2005. +# IDLE contributed by Forest August 2024. -__version__ = "2.58" +__version__ = "2.59" import binascii, errno, random, re, socket, subprocess, sys, time, calendar from datetime import datetime, timezone, timedelta @@ -52,9 +53,6 @@ # search command can be quite large, so we now use 1M. _MAXLINE = 1000000 -# Data larger than this will be read in chunks, to prevent extreme -# overallocation. -_SAFE_BUF_SIZE = 1 << 20 # Commands @@ -77,6 +75,7 @@ 'GETANNOTATION':('AUTH', 'SELECTED'), 'GETQUOTA': ('AUTH', 'SELECTED'), 'GETQUOTAROOT': ('AUTH', 'SELECTED'), + 'IDLE': ('AUTH', 'SELECTED'), 'MYRIGHTS': ('AUTH', 'SELECTED'), 'LIST': ('AUTH', 'SELECTED'), 'LOGIN': ('NONAUTH',), @@ -187,6 +186,7 @@ class IMAP4: class error(Exception): pass # Logical errors - debug required class abort(error): pass # Service errors - close and retry class readonly(abort): pass # Mailbox status changed to READ-ONLY + class _responsetimeout(TimeoutError): pass # No response during IDLE def __init__(self, host='', port=IMAP4_PORT, timeout=None): self.debug = Debug @@ -195,10 +195,13 @@ def __init__(self, host='', port=IMAP4_PORT, timeout=None): self.tagged_commands = {} # Tagged commands awaiting response self.untagged_responses = {} # {typ: [data, ...], ...} self.continuation_response = '' # Last continuation response + self._idle_responses = [] # Response queue for idle iteration + self._idle_capture = False # Whether to queue responses for idle self.is_readonly = False # READ-ONLY desired state self.tagnum = 0 self._tls_established = False self._mode_ascii() + self._readbuf = [] # Open socket to server. @@ -313,23 +316,97 @@ def open(self, host='', port=IMAP4_PORT, timeout=None): self.host = host self.port = port self.sock = self._create_socket(timeout) - self.file = self.sock.makefile('rb') + self._file = self.sock.makefile('rb') + + + @property + def file(self): + # The old 'file' attribute is no longer used now that we do our own + # read() and readline() buffering, with which it conflicts. + # As an undocumented interface, it should never have been accessed by + # external code, and therefore does not warrant deprecation. + # Nevertheless, we provide this property for now, to avoid suddenly + # breaking any code in the wild that might have been using it in a + # harmless way. + import warnings + warnings.warn( + 'IMAP4.file is unsupported, can cause errors, and may be removed.', + RuntimeWarning, + stacklevel=2) + return self._file def read(self, size): """Read 'size' bytes from remote.""" - cursize = min(size, _SAFE_BUF_SIZE) - data = self.file.read(cursize) - while cursize < size and len(data) == cursize: - delta = min(cursize, size - cursize) - data += self.file.read(delta) - cursize += delta - return data + # We need buffered read() to continue working after socket timeouts, + # since we use them during IDLE. Unfortunately, the standard library's + # SocketIO implementation makes this impossible, by setting a permanent + # error condition instead of letting the caller decide how to handle a + # timeout. We therefore implement our own buffered read(). + # https://github.com/python/cpython/issues/51571 + # + # Reading in chunks instead of delegating to a single + # BufferedReader.read() call also means we avoid its preallocation + # of an unreasonably large memory block if a malicious server claims + # it will send a huge literal without actually sending one. + # https://github.com/python/cpython/issues/119511 + + parts = [] + + while size > 0: + + if len(parts) < len(self._readbuf): + buf = self._readbuf[len(parts)] + else: + try: + buf = self.sock.recv(DEFAULT_BUFFER_SIZE) + except ConnectionError: + break + if not buf: + break + self._readbuf.append(buf) + + if len(buf) >= size: + parts.append(buf[:size]) + self._readbuf = [buf[size:]] + self._readbuf[len(parts):] + break + parts.append(buf) + size -= len(buf) + + return b''.join(parts) def readline(self): """Read line from remote.""" - line = self.file.readline(_MAXLINE + 1) + # The comment in read() explains why we implement our own readline(). + + LF = b'\n' + parts = [] + length = 0 + + while length < _MAXLINE: + + if len(parts) < len(self._readbuf): + buf = self._readbuf[len(parts)] + else: + try: + buf = self.sock.recv(DEFAULT_BUFFER_SIZE) + except ConnectionError: + break + if not buf: + break + self._readbuf.append(buf) + + pos = buf.find(LF) + if pos != -1: + pos += 1 + parts.append(buf[:pos]) + self._readbuf = [buf[pos:]] + self._readbuf[len(parts):] + break + parts.append(buf) + length += len(buf) + + line = b''.join(parts) if len(line) > _MAXLINE: raise self.error("got more than %d bytes" % _MAXLINE) return line @@ -343,7 +420,7 @@ def send(self, data): def shutdown(self): """Close I/O established in "open".""" - self.file.close() + self._file.close() try: self.sock.shutdown(socket.SHUT_RDWR) except OSError as exc: @@ -597,6 +674,19 @@ def getquotaroot(self, mailbox): return typ, [quotaroot, quota] + def idle(self, duration=None): + """Return an iterable IDLE context manager producing untagged responses. + If the argument is not None, limit iteration to 'duration' seconds. + + with M.idle(duration=29 * 60) as idler: + for typ, data in idler: + print(typ, data) + + Note: 'duration' requires a socket connection (not IMAP4_stream). + """ + return Idler(self, duration) + + def list(self, directory='""', pattern='*'): """List mailbox names in directory matching pattern. @@ -830,7 +920,7 @@ def starttls(self, ssl_context=None): if typ == 'OK': self.sock = ssl_context.wrap_socket(self.sock, server_hostname=self.host) - self.file = self.sock.makefile('rb') + self._file = self.sock.makefile('rb') self._tls_established = True self._get_capabilities() else: @@ -953,6 +1043,24 @@ def xatom(self, name, *args): def _append_untagged(self, typ, dat): if dat is None: dat = b'' + + # During idle, queue untagged responses for delivery via iteration + if self._idle_capture: + # Responses containing literal strings are passed to us one data + # fragment at a time, while others arrive in a single call. + if (not self._idle_responses or + isinstance(self._idle_responses[-1][1][-1], bytes)): + # We are not continuing a fragmented response; start a new one + self._idle_responses.append((typ, [dat])) + else: + # We are continuing a fragmented response; append the fragment + response = self._idle_responses[-1] + assert response[0] == typ + response[1].append(dat) + if __debug__ and self.debug >= 5: + self._mesg(f'idle: queue untagged {typ} {dat!r}') + return + ur = self.untagged_responses if __debug__: if self.debug >= 5: @@ -1074,14 +1182,29 @@ def _get_capabilities(self): self.capabilities = tuple(dat.split()) - def _get_response(self): + def _get_response(self, start_timeout=False): # Read response and store. # # Returns None for continuation responses, # otherwise first response line received. - - resp = self._get_line() + # + # If start_timeout is given, temporarily uses it as a socket + # timeout while waiting for the start of a response, raising + # _responsetimeout if one doesn't arrive. (Used by Idler.) + + if start_timeout is not False and self.sock: + assert start_timeout is None or start_timeout > 0 + saved_timeout = self.sock.gettimeout() + self.sock.settimeout(start_timeout) + try: + resp = self._get_line() + except TimeoutError as err: + raise self._responsetimeout from err + finally: + self.sock.settimeout(saved_timeout) + else: + resp = self._get_line() # Command completion response? @@ -1288,6 +1411,199 @@ def print_log(self): n -= 1 +class Idler: + """Iterable IDLE context manager: start IDLE & produce untagged responses. + + An object of this type is returned by the IMAP4.idle() method. + + Note: The name and structure of this class are subject to change. + """ + + def __init__(self, imap, duration=None): + if 'IDLE' not in imap.capabilities: + raise imap.error("Server does not support IMAP4 IDLE") + if duration is not None and not imap.sock: + # IMAP4_stream pipes don't support timeouts + raise imap.error('duration requires a socket connection') + self._duration = duration + self._deadline = None + self._imap = imap + self._tag = None + self._saved_state = None + + def __enter__(self): + imap = self._imap + assert not imap._idle_responses + assert not imap._idle_capture + + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle start duration={self._duration}') + + # Start capturing untagged responses before sending IDLE, + # so we can deliver via iteration any that arrive while + # the IDLE command continuation request is still pending. + imap._idle_capture = True + + try: + self._tag = imap._command('IDLE') + # As with any command, the server is allowed to send us unrelated, + # untagged responses before acting on IDLE. These lines will be + # returned by _get_response(). When the server is ready, it will + # send an IDLE continuation request, indicated by _get_response() + # returning None. We therefore process responses in a loop until + # this occurs. + while resp := imap._get_response(): + if imap.tagged_commands[self._tag]: + typ, data = imap.tagged_commands.pop(self._tag) + if typ == 'NO': + raise imap.error(f'idle denied: {data}') + raise imap.abort(f'unexpected status response: {resp}') + + if __debug__ and imap.debug >= 4: + prompt = imap.continuation_response + imap._mesg(f'idle continuation prompt: {prompt}') + except BaseException: + imap._idle_capture = False + raise + + if self._duration is not None: + self._deadline = time.monotonic() + self._duration + + self._saved_state = imap.state + imap.state = 'IDLING' + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + imap = self._imap + + if __debug__ and imap.debug >= 4: + imap._mesg('idle done') + imap.state = self._saved_state + + # Stop intercepting untagged responses before sending DONE, + # since we can no longer deliver them via iteration. + imap._idle_capture = False + + # If we captured untagged responses while the IDLE command + # continuation request was still pending, but the user did not + # iterate over them before exiting IDLE, we must put them + # someplace where the user can retrieve them. The only + # sensible place for this is the untagged_responses dict, + # despite its unfortunate inability to preserve the relative + # order of different response types. + if leftovers := len(imap._idle_responses): + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle quit with {leftovers} leftover responses') + while imap._idle_responses: + typ, data = imap._idle_responses.pop(0) + # Append one fragment at a time, just as _get_response() does + for datum in data: + imap._append_untagged(typ, datum) + + try: + imap.send(b'DONE' + CRLF) + status, [msg] = imap._command_complete('IDLE', self._tag) + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle status: {status} {msg!r}') + except OSError: + if not exc_type: + raise + + return False # Do not suppress context body exceptions + + def __iter__(self): + return self + + def _pop(self, timeout, default=('', None)): + # Get the next response, or a default value on timeout. + # The timeout arg can be an int or float, or None for no timeout. + # Timeouts require a socket connection (not IMAP4_stream). + # This method ignores self._duration. + + # Historical Note: + # The timeout was originally implemented using select() after + # checking for the presence of already-buffered data. + # That allowed timeouts on pipe connetions like IMAP4_stream. + # However, it seemed possible that SSL data arriving without any + # IMAP data afterward could cause select() to indicate available + # application data when there was none, leading to a read() call + # that would block with no timeout. It was unclear under what + # conditions this would happen in practice. Our implementation was + # changed to use socket timeouts instead of select(), just to be + # safe. + + imap = self._imap + if imap.state != 'IDLING': + raise imap.error('_pop() only works during IDLE') + + if imap._idle_responses: + # Response is ready to return to the user + resp = imap._idle_responses.pop(0) + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle _pop({timeout}) de-queued {resp[0]}') + return resp + + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle _pop({timeout}) reading') + + if timeout is not None: + if timeout <= 0: + return default + timeout = float(timeout) # Required by socket.settimeout() + + try: + imap._get_response(timeout) # Reads line, calls _append_untagged() + except IMAP4._responsetimeout: + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle _pop({timeout}) done') + return default + + resp = imap._idle_responses.pop(0) + + if __debug__ and imap.debug >= 4: + imap._mesg(f'idle _pop({timeout}) read {resp[0]}') + return resp + + def __next__(self): + imap = self._imap + + if self._duration is None: + timeout = None + else: + timeout = self._deadline - time.monotonic() + typ, data = self._pop(timeout) + + if not typ: + if __debug__ and imap.debug >= 4: + imap._mesg('idle iterator exhausted') + raise StopIteration + + return typ, data + + def burst(self, interval=0.1): + """Yield a burst of responses no more than 'interval' seconds apart. + + with M.idle() as idler: + # get a response and any others following by < 0.1 seconds + batch = list(idler.burst()) + print(f'processing {len(batch)} responses...') + print(batch) + + Note: This generator requires a socket connection (not IMAP4_stream). + """ + if not self._imap.sock: + raise self._imap.error('burst() requires a socket connection') + + try: + yield next(self) + except StopIteration: + return + + while response := self._pop(interval, None): + yield response + + if HAVE_SSL: class IMAP4_SSL(IMAP4): @@ -1355,7 +1671,7 @@ def open(self, host=None, port=None, timeout=None): self.host = None # For compatibility with parent class self.port = None self.sock = None - self.file = None + self._file = None self.process = subprocess.Popen(self.command, bufsize=DEFAULT_BUFFER_SIZE, stdin=subprocess.PIPE, stdout=subprocess.PIPE, diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 2fbf83b264d5b4..a13ee58d650e1b 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -208,6 +208,54 @@ def cmd_UNSELECT(self, tag, args): self._send_tagged(tag, 'BAD', 'No mailbox selected') +class IdleCmdDenyHandler(SimpleIMAPHandler): + capabilities = 'IDLE' + def cmd_IDLE(self, tag, args): + self._send_tagged(tag, 'NO', 'IDLE is not allowed at this time') + + +class IdleCmdHandler(SimpleIMAPHandler): + capabilities = 'IDLE' + def cmd_IDLE(self, tag, args): + # pre-idle-continuation response + self._send_line(b'* 0 EXISTS') + self._send_textline('+ idling') + # simple response + self._send_line(b'* 2 EXISTS') + # complex response: fragmented data due to literal string + self._send_line(b'* 1 FETCH (BODY[HEADER.FIELDS (DATE)] {41}') + self._send(b'Date: Fri, 06 Dec 2024 06:00:00 +0000\r\n\r\n') + self._send_line(b')') + # simple response following a fragmented one + self._send_line(b'* 3 EXISTS') + # response arriving later + time.sleep(1) + self._send_line(b'* 1 RECENT') + r = yield + if r == b'DONE\r\n': + self._send_line(b'* 9 RECENT') + self._send_tagged(tag, 'OK', 'Idle completed') + else: + self._send_tagged(tag, 'BAD', 'Expected DONE') + + +class IdleCmdDelayedPacketHandler(SimpleIMAPHandler): + capabilities = 'IDLE' + def cmd_IDLE(self, tag, args): + self._send_textline('+ idling') + # response line spanning multiple packets, the last one delayed + self._send(b'* 1 EX') + time.sleep(0.2) + self._send(b'IS') + time.sleep(1) + self._send(b'TS\r\n') + r = yield + if r == b'DONE\r\n': + self._send_tagged(tag, 'OK', 'Idle completed') + else: + self._send_tagged(tag, 'BAD', 'Expected DONE') + + class NewIMAPTestsMixin(): client = None @@ -497,6 +545,73 @@ def test_with_statement_logout(self): # command tests + def test_idle_capability(self): + client, _ = self._setup(SimpleIMAPHandler) + with self.assertRaisesRegex(imaplib.IMAP4.error, + 'does not support IMAP4 IDLE'): + with client.idle(): + pass + + def test_idle_denied(self): + client, _ = self._setup(IdleCmdDenyHandler) + client.login('user', 'pass') + with self.assertRaises(imaplib.IMAP4.error): + with client.idle() as idler: + pass + + def test_idle_iter(self): + client, _ = self._setup(IdleCmdHandler) + client.login('user', 'pass') + with client.idle() as idler: + # iteration should include response between 'IDLE' & '+ idling' + response = next(idler) + self.assertEqual(response, ('EXISTS', [b'0'])) + # iteration should produce responses + response = next(idler) + self.assertEqual(response, ('EXISTS', [b'2'])) + # fragmented response (with literal string) should arrive whole + expected_fetch_data = [ + (b'1 (BODY[HEADER.FIELDS (DATE)] {41}', + b'Date: Fri, 06 Dec 2024 06:00:00 +0000\r\n\r\n'), + b')'] + typ, data = next(idler) + self.assertEqual(typ, 'FETCH') + self.assertEqual(data, expected_fetch_data) + # response after a fragmented one should arrive separately + response = next(idler) + self.assertEqual(response, ('EXISTS', [b'3'])) + # iteration should have consumed untagged responses + _, data = client.response('EXISTS') + self.assertEqual(data, [None]) + # responses not iterated should be available after idle + _, data = client.response('RECENT') + self.assertEqual(data[0], b'1') + # responses received after 'DONE' should be available after idle + self.assertEqual(data[1], b'9') + + def test_idle_burst(self): + client, _ = self._setup(IdleCmdHandler) + client.login('user', 'pass') + # burst() should yield immediately available responses + with client.idle() as idler: + batch = list(idler.burst()) + self.assertEqual(len(batch), 4) + # burst() should not have consumed later responses + _, data = client.response('RECENT') + self.assertEqual(data, [b'1', b'9']) + + def test_idle_delayed_packet(self): + client, _ = self._setup(IdleCmdDelayedPacketHandler) + client.login('user', 'pass') + # If our readline() implementation fails to preserve line fragments + # when idle timeouts trigger, a response spanning delayed packets + # can be corrupted, leaving the protocol stream in a bad state. + try: + with client.idle(0.5) as idler: + self.assertRaises(StopIteration, next, idler) + except client.abort as err: + self.fail('multi-packet response was corrupted by idle timeout') + def test_login(self): client, _ = self._setup(SimpleIMAPHandler) typ, data = client.login('user', 'pass') @@ -537,6 +652,14 @@ def test_unselect(self): self.assertEqual(data[0], b'Returned to authenticated state. (Success)') self.assertEqual(client.state, 'AUTH') + # property tests + + def test_file_property_should_not_be_accessed(self): + client, _ = self._setup(SimpleIMAPHandler) + # the 'file' property replaced a private attribute that is now unsafe + with self.assertWarns(RuntimeWarning): + client.file + class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase): imap_class = imaplib.IMAP4 diff --git a/Misc/ACKS b/Misc/ACKS index 47c8d2b40aafb7..27480a1f3131bd 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -576,6 +576,7 @@ Benjamin Fogle Artem Fokin Arnaud Fontaine Michael Foord +Forest Amaury Forgeot d'Arc Doug Fort Daniel Fortunov diff --git a/Misc/NEWS.d/next/Library/2024-08-01-01-00-00.gh-issue-55454.wy0vGw.rst b/Misc/NEWS.d/next/Library/2024-08-01-01-00-00.gh-issue-55454.wy0vGw.rst new file mode 100644 index 00000000000000..58fc85963217c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-01-01-00-00.gh-issue-55454.wy0vGw.rst @@ -0,0 +1 @@ +Add IMAP4 ``IDLE`` support to the :mod:`imaplib` module. Patch by Forest. From e2064d67504bc360c20e03eeea8b360d605cb439 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Fri, 7 Feb 2025 04:55:27 +0000 Subject: [PATCH 105/311] Emscripten: use better `_Py_Version` computation for worker module (#129757) Use integer bit shifting instead of conversion to strings to compute Python version. --- Tools/wasm/emscripten/web_example/python.worker.mjs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Tools/wasm/emscripten/web_example/python.worker.mjs b/Tools/wasm/emscripten/web_example/python.worker.mjs index 8043e419966743..5f9012a492a399 100644 --- a/Tools/wasm/emscripten/web_example/python.worker.mjs +++ b/Tools/wasm/emscripten/web_example/python.worker.mjs @@ -70,12 +70,9 @@ const emscriptenSettings = { postMessage({ type: "ready", stdinBuffer: stdinBuffer.sab }); }, async preRun(Module) { - const versionHex = Module.HEAPU32[Module._Py_Version / 4].toString(16); - const versionTuple = versionHex - .padStart(8, "0") - .match(/.{1,2}/g) - .map((x) => parseInt(x, 16)); - const [major, minor, ..._] = versionTuple; + const versionInt = Module.HEAPU32[Module._Py_Version >>> 2]; + const major = (versionInt >>> 24) & 0xff; + const minor = (versionInt >>> 16) & 0xff; // Prevent complaints about not finding exec-prefix by making a lib-dynload directory Module.FS.mkdirTree(`/lib/python${major}.${minor}/lib-dynload/`); Module.addRunDependency("install-stdlib"); From ae132edc296d27c6ed04fe4d400c67e3cfb622e8 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 7 Feb 2025 12:59:52 +0300 Subject: [PATCH 106/311] gh-129766: Fix crash on calling `warnings._release_lock` with no lock (#129771) --- Lib/test/test_warnings/__init__.py | 11 +++++++++++ .../2025-02-07-10-34-09.gh-issue-129766.6n5fQZ.rst | 2 ++ Python/_warnings.c | 9 ++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-07-10-34-09.gh-issue-129766.6n5fQZ.rst diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 4bd164b8a9a82b..6f4c569d247601 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1432,6 +1432,17 @@ class PyEnvironmentVariableTests(EnvironmentVariableTests, unittest.TestCase): module = py_warnings +class LocksTest(unittest.TestCase): + @support.cpython_only + @unittest.skipUnless(c_warnings, 'C module is required') + def test_release_lock_no_lock(self): + with self.assertRaisesRegex( + RuntimeError, + 'cannot release un-acquired lock', + ): + c_warnings._release_lock() + + class _DeprecatedTest(BaseTest, unittest.TestCase): """Test _deprecated().""" diff --git a/Misc/NEWS.d/next/Library/2025-02-07-10-34-09.gh-issue-129766.6n5fQZ.rst b/Misc/NEWS.d/next/Library/2025-02-07-10-34-09.gh-issue-129766.6n5fQZ.rst new file mode 100644 index 00000000000000..76e908300a858d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-07-10-34-09.gh-issue-129766.6n5fQZ.rst @@ -0,0 +1,2 @@ +Fix crash in :mod:`warnings`, when calling ``_release_lock()`` with no +existing lock. diff --git a/Python/_warnings.c b/Python/_warnings.c index bb195da9512caf..891db2743a28fc 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -240,12 +240,12 @@ warnings_lock(PyInterpreterState *interp) _PyRecursiveMutex_Lock(&st->lock); } -static inline void +static inline int warnings_unlock(PyInterpreterState *interp) { WarningsState *st = warnings_get_state(interp); assert(st != NULL); - _PyRecursiveMutex_Unlock(&st->lock); + return _PyRecursiveMutex_TryUnlock(&st->lock); } static inline bool @@ -284,7 +284,10 @@ warnings_release_lock_impl(PyObject *module) if (interp == NULL) { return NULL; } - warnings_unlock(interp); + if (warnings_unlock(interp) < 0) { + PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); + return NULL; + } Py_RETURN_NONE; } From a3d5aab9a89e311cded9c724ce7d5a873e4d680d Mon Sep 17 00:00:00 2001 From: Cody Maloney Date: Fri, 7 Feb 2025 03:06:11 -0800 Subject: [PATCH 107/311] gh-129005: Align FileIO.readall between _pyio and _io (#129705) Utilize `bytearray.resize()` and `os.readinto()` to reduce copies and match behavior of `_io.FileIO.readall()`. There is still an extra copy which means twice the memory required compared to FileIO because there isn't a zero-copy path from `bytearray` -> `bytes` currently. On my system reading a 2 GB file: `./python -m test -M8g -uall test_largefile -m test.test_largefile.PyLargeFileTest.test_large_read -v` Goes from ~2.7 seconds -> ~2.2 seconds Co-authored-by: Victor Stinner --- Lib/_pyio.py | 37 ++++++++++++------- ...-02-05-13-19-15.gh-issue-129005.Sb69L_.rst | 2 + 2 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-05-13-19-15.gh-issue-129005.Sb69L_.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index b3a8f37d68acdb..f7370dff19efc8 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1454,6 +1454,17 @@ def write(self, b): return BufferedWriter.write(self, b) +def _new_buffersize(bytes_read): + # Parallels _io/fileio.c new_buffersize + if bytes_read > 65536: + addend = bytes_read >> 3 + else: + addend = 256 + bytes_read + if addend < DEFAULT_BUFFER_SIZE: + addend = DEFAULT_BUFFER_SIZE + return bytes_read + addend + + class FileIO(RawIOBase): _fd = -1 _created = False @@ -1672,22 +1683,20 @@ def readall(self): except OSError: pass - result = bytearray() - while True: - if len(result) >= bufsize: - bufsize = len(result) - bufsize += max(bufsize, DEFAULT_BUFFER_SIZE) - n = bufsize - len(result) - try: - chunk = os.read(self._fd, n) - except BlockingIOError: - if result: - break + result = bytearray(bufsize) + bytes_read = 0 + try: + while n := os.readinto(self._fd, memoryview(result)[bytes_read:]): + bytes_read += n + if bytes_read >= len(result): + result.resize(_new_buffersize(bytes_read)) + except BlockingIOError: + if not bytes_read: return None - if not chunk: # reached the end of the file - break - result += chunk + assert len(result) - bytes_read >= 1, \ + "os.readinto buffer size 0 will result in erroneous EOF / returns 0" + result.resize(bytes_read) return bytes(result) def readinto(self, buffer): diff --git a/Misc/NEWS.d/next/Library/2025-02-05-13-19-15.gh-issue-129005.Sb69L_.rst b/Misc/NEWS.d/next/Library/2025-02-05-13-19-15.gh-issue-129005.Sb69L_.rst new file mode 100644 index 00000000000000..236d7766b92cc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-05-13-19-15.gh-issue-129005.Sb69L_.rst @@ -0,0 +1,2 @@ +``_pyio.FileIO.readall()`` now allocates, resizes, and fills a data buffer +using the same algorithm ``_io.FileIO.readall()`` uses. From 3d3a4beefed46df7aa3a477f2e511459ce06c4b0 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Fri, 7 Feb 2025 20:18:15 +0800 Subject: [PATCH 108/311] gh-128563: Document the tail-calling interpreter (GH-129728) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/using/configure.rst | 10 ++++++++++ Doc/whatsnew/3.14.rst | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 629859e36cb654..101c43576b0314 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -618,6 +618,16 @@ also be used to improve performance. Enable computed gotos in evaluation loop (enabled by default on supported compilers). +.. option:: --with-tail-call-interp + + Enable interpreters using tail calls in CPython. If enabled, enabling PGO + (:option:`--enable-optimizations`) is highly recommended. This option specifically + requires a C compiler with proper tail call support, and the + `preserve_none `_ + calling convention. For example, Clang 19 and newer supports this feature. + + .. versionadded:: next + .. option:: --without-mimalloc Disable the fast :ref:`mimalloc ` allocator diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index a95fc7a4b6a9fa..c788db31fc27ed 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -68,6 +68,7 @@ Summary -- release highlights * :ref:`PEP 649: deferred evaluation of annotations ` * :ref:`PEP 741: Python Configuration C API ` * :ref:`PEP 761: Discontinuation of PGP signatures ` +* :ref:`A new tail-calling interpreter ` New features @@ -208,6 +209,30 @@ configuration mechanisms). .. seealso:: :pep:`741`. +.. _whatsnew314-tail-call: + +A new tail-calling interpreter +------------------------------ + +A new type of interpreter based on tail calls has been added to CPython. +For certain newer compilers, this interpreter provides +significantly better performance. Preliminary numbers on our machines suggest +anywhere from -3% to 30% faster Python code, and a geometric mean of 9-15% +faster on ``pyperformance`` depending on platform and architecture. + +This interpreter currently only works with Clang 19 and newer +on x86-64 and AArch64 architectures. However, we expect +that a future release of GCC will support this as well. + +This feature is opt-in for now. We highly recommend enabling profile-guided +optimization with the new interpreter as it is the only configuration we have +tested and can validate its improved performance. +For further information on how to build Python, see +:option:`--with-tail-call-interp`. + +(Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this +in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.) + Other language changes ====================== From 175844713af383c9e4dd60166d1d7407c80a1949 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Fri, 7 Feb 2025 21:11:57 +0800 Subject: [PATCH 109/311] gh-128563: Move assignment of opcode into ifdef (GH-129803) --- Lib/test/test_generated_cases.py | 260 ++--- Python/generated_cases.c.h | 1124 +++++++++------------- Tools/cases_generator/tier1_generator.py | 7 +- 3 files changed, 566 insertions(+), 825 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 35ad9ebbe5a1a9..0e0f28be6b2af0 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -305,10 +305,9 @@ def test_inst_no_args(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -328,10 +327,9 @@ def test_inst_one_pop(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -354,10 +352,9 @@ def test_inst_one_push(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -381,10 +378,9 @@ def test_inst_one_push_one_pop(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -409,10 +405,9 @@ def test_binary_op(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -440,10 +435,9 @@ def test_overlap(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -473,10 +467,9 @@ def test_predictions(self): output = """ TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -489,10 +482,9 @@ def test_predictions(self): TARGET(OP3) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP3; + int opcode = OP3; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -528,10 +520,9 @@ def test_sync_sp(self): output = """ TARGET(A) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = A; + int opcode = A; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(A); @@ -550,10 +541,9 @@ def test_sync_sp(self): TARGET(B) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = B; + int opcode = B; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(B); @@ -592,10 +582,9 @@ def test_error_if_plain(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -616,10 +605,9 @@ def test_error_if_plain_with_comment(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -644,10 +632,9 @@ def test_error_if_pop(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -680,10 +667,9 @@ def test_error_if_pop_with_result(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -712,10 +698,9 @@ def test_cache_effect(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -744,10 +729,9 @@ def test_suppress_dispatch(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -780,10 +764,9 @@ def test_macro_instruction(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(OP); @@ -822,10 +805,9 @@ def test_macro_instruction(self): TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -845,10 +827,9 @@ def test_macro_instruction(self): TARGET(OP3) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP3; + int opcode = OP3; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(OP3); @@ -881,10 +862,9 @@ def test_unused_caches(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(OP); @@ -908,10 +888,9 @@ def test_pseudo_instruction_no_flags(self): output = """ TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -932,10 +911,9 @@ def test_pseudo_instruction_with_flags(self): output = """ TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -959,10 +937,9 @@ def test_pseudo_instruction_as_sequence(self): output = """ TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -971,10 +948,9 @@ def test_pseudo_instruction_as_sequence(self): TARGET(OP2) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP2; + int opcode = OP2; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP2); @@ -993,10 +969,9 @@ def test_array_input(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1021,10 +996,9 @@ def test_array_output(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1054,10 +1028,9 @@ def test_array_input_output(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1083,10 +1056,9 @@ def test_array_error_if(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1114,10 +1086,9 @@ def test_cond_effect(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1160,10 +1131,9 @@ def test_macro_cond_effect(self): output = """ TARGET(M) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = M; + int opcode = M; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1209,10 +1179,9 @@ def test_macro_push_push(self): output = """ TARGET(M) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = M; + int opcode = M; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1247,10 +1216,9 @@ def test_override_inst(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1273,10 +1241,9 @@ def test_override_op(self): output = """ TARGET(M) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = M; + int opcode = M; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1295,10 +1262,9 @@ def test_annotated_inst(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1318,10 +1284,9 @@ def test_annotated_op(self): output = """ TARGET(M) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = M; + int opcode = M; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(M); @@ -1359,10 +1324,9 @@ def test_array_of_one(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1386,10 +1350,9 @@ def test_pointer_to_stackref(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1429,10 +1392,9 @@ def test_unused_named_values(self): output = """ TARGET(INST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INST; + int opcode = INST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INST); @@ -1460,10 +1422,9 @@ def test_used_unused_used(self): output = """ TARGET(TEST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TEST; + int opcode = TEST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1505,10 +1466,9 @@ def test_unused_used_used(self): output = """ TARGET(TEST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TEST; + int opcode = TEST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1549,10 +1509,9 @@ def test_flush(self): output = """ TARGET(TEST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TEST; + int opcode = TEST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1602,10 +1561,9 @@ def test_pop_on_error_peeks(self): output = """ TARGET(TEST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TEST; + int opcode = TEST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1655,10 +1613,9 @@ def test_push_then_error(self): output = """ TARGET(TEST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TEST; + int opcode = TEST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(TEST); @@ -1701,10 +1658,9 @@ def test_error_if_true(self): output = """ TARGET(OP1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP1; + int opcode = OP1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP1); @@ -1713,10 +1669,9 @@ def test_error_if_true(self): TARGET(OP2) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP2; + int opcode = OP2; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP2); @@ -1775,10 +1730,9 @@ def test_stack_save_reload(self): output = """ TARGET(BALANCED) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BALANCED; + int opcode = BALANCED; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BALANCED); @@ -1800,10 +1754,9 @@ def test_stack_reload_only(self): output = """ TARGET(BALANCED) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BALANCED; + int opcode = BALANCED; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BALANCED); @@ -1826,10 +1779,9 @@ def test_stack_save_only(self): output = """ TARGET(BALANCED) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BALANCED; + int opcode = BALANCED; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1853,10 +1805,9 @@ def test_instruction_size_macro(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1893,10 +1844,9 @@ def test_escaping_call_next_to_cmacro(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1932,10 +1882,9 @@ def test_pystackref_frompyobject_new_next_to_cmacro(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); @@ -1968,10 +1917,9 @@ def test_pop_input(self): output = """ TARGET(OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = OP; + int opcode = OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7927fa7db95c20..facdf2f78d28ea 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -20,10 +20,9 @@ TARGET(BINARY_OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP; + int opcode = BINARY_OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 6; INSTRUCTION_STATS(BINARY_OP); @@ -77,10 +76,9 @@ TARGET(BINARY_OP_ADD_FLOAT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_ADD_FLOAT; + int opcode = BINARY_OP_ADD_FLOAT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -132,10 +130,9 @@ TARGET(BINARY_OP_ADD_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_ADD_INT; + int opcode = BINARY_OP_ADD_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -186,10 +183,9 @@ TARGET(BINARY_OP_ADD_UNICODE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_ADD_UNICODE; + int opcode = BINARY_OP_ADD_UNICODE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -240,10 +236,9 @@ TARGET(BINARY_OP_EXTEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_EXTEND; + int opcode = BINARY_OP_EXTEND; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -297,10 +292,9 @@ TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_INPLACE_ADD_UNICODE; + int opcode = BINARY_OP_INPLACE_ADD_UNICODE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -381,10 +375,9 @@ TARGET(BINARY_OP_MULTIPLY_FLOAT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_MULTIPLY_FLOAT; + int opcode = BINARY_OP_MULTIPLY_FLOAT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -436,10 +429,9 @@ TARGET(BINARY_OP_MULTIPLY_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_MULTIPLY_INT; + int opcode = BINARY_OP_MULTIPLY_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -490,10 +482,9 @@ TARGET(BINARY_OP_SUBTRACT_FLOAT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_SUBTRACT_FLOAT; + int opcode = BINARY_OP_SUBTRACT_FLOAT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -545,10 +536,9 @@ TARGET(BINARY_OP_SUBTRACT_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_OP_SUBTRACT_INT; + int opcode = BINARY_OP_SUBTRACT_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -599,10 +589,9 @@ TARGET(BINARY_SLICE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SLICE; + int opcode = BINARY_SLICE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BINARY_SLICE); @@ -660,10 +649,9 @@ TARGET(BINARY_SUBSCR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR; + int opcode = BINARY_SUBSCR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(BINARY_SUBSCR); @@ -714,10 +702,9 @@ TARGET(BINARY_SUBSCR_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR_DICT; + int opcode = BINARY_SUBSCR_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -762,10 +749,9 @@ TARGET(BINARY_SUBSCR_GETITEM) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR_GETITEM; + int opcode = BINARY_SUBSCR_GETITEM; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -848,10 +834,9 @@ TARGET(BINARY_SUBSCR_LIST_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR_LIST_INT; + int opcode = BINARY_SUBSCR_LIST_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -919,10 +904,9 @@ TARGET(BINARY_SUBSCR_STR_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR_STR_INT; + int opcode = BINARY_SUBSCR_STR_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -982,10 +966,9 @@ TARGET(BINARY_SUBSCR_TUPLE_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BINARY_SUBSCR_TUPLE_INT; + int opcode = BINARY_SUBSCR_TUPLE_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1041,10 +1024,9 @@ TARGET(BUILD_LIST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_LIST; + int opcode = BUILD_LIST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_LIST); @@ -1064,10 +1046,9 @@ TARGET(BUILD_MAP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_MAP; + int opcode = BUILD_MAP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_MAP); @@ -1107,10 +1088,9 @@ TARGET(BUILD_SET) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_SET; + int opcode = BUILD_SET; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_SET); @@ -1156,10 +1136,9 @@ TARGET(BUILD_SLICE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_SLICE; + int opcode = BUILD_SLICE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_SLICE); @@ -1187,10 +1166,9 @@ TARGET(BUILD_STRING) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_STRING; + int opcode = BUILD_STRING; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_STRING); @@ -1225,10 +1203,9 @@ TARGET(BUILD_TUPLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = BUILD_TUPLE; + int opcode = BUILD_TUPLE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(BUILD_TUPLE); @@ -1248,10 +1225,9 @@ TARGET(CACHE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CACHE; + int opcode = CACHE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CACHE); @@ -1262,16 +1238,16 @@ TARGET(CALL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL; + int opcode = CALL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL); PREDICTED_CALL:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; + opcode = CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -1432,10 +1408,9 @@ TARGET(CALL_ALLOC_AND_ENTER_INIT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_ALLOC_AND_ENTER_INIT; + int opcode = CALL_ALLOC_AND_ENTER_INIT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1559,10 +1534,9 @@ TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BOUND_METHOD_EXACT_ARGS; + int opcode = CALL_BOUND_METHOD_EXACT_ARGS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1699,10 +1673,9 @@ TARGET(CALL_BOUND_METHOD_GENERAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BOUND_METHOD_GENERAL; + int opcode = CALL_BOUND_METHOD_GENERAL; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1822,10 +1795,9 @@ TARGET(CALL_BUILTIN_CLASS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BUILTIN_CLASS; + int opcode = CALL_BUILTIN_CLASS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -1915,10 +1887,9 @@ TARGET(CALL_BUILTIN_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BUILTIN_FAST; + int opcode = CALL_BUILTIN_FAST; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -2014,10 +1985,9 @@ TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BUILTIN_FAST_WITH_KEYWORDS; + int opcode = CALL_BUILTIN_FAST_WITH_KEYWORDS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -2114,10 +2084,9 @@ TARGET(CALL_BUILTIN_O) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_BUILTIN_O; + int opcode = CALL_BUILTIN_O; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -2211,15 +2180,15 @@ TARGET(CALL_FUNCTION_EX) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_FUNCTION_EX; + int opcode = CALL_FUNCTION_EX; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_FUNCTION_EX); + opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; _PyStackRef kwargs_in; @@ -2403,10 +2372,9 @@ TARGET(CALL_INTRINSIC_1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_INTRINSIC_1; + int opcode = CALL_INTRINSIC_1; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_INTRINSIC_1); @@ -2428,10 +2396,9 @@ TARGET(CALL_INTRINSIC_2) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_INTRINSIC_2; + int opcode = CALL_INTRINSIC_2; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_INTRINSIC_2); @@ -2460,10 +2427,9 @@ TARGET(CALL_ISINSTANCE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_ISINSTANCE; + int opcode = CALL_ISINSTANCE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -2522,16 +2488,16 @@ TARGET(CALL_KW) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_KW; + int opcode = CALL_KW; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW); PREDICTED_CALL_KW:; _Py_CODEUNIT* const this_instr = next_instr - 4; (void)this_instr; + opcode = CALL_KW; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -2691,10 +2657,9 @@ TARGET(CALL_KW_BOUND_METHOD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_KW_BOUND_METHOD; + int opcode = CALL_KW_BOUND_METHOD; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -2825,15 +2790,15 @@ TARGET(CALL_KW_NON_PY) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_KW_NON_PY; + int opcode = CALL_KW_NON_PY; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_KW_NON_PY); + opcode = CALL_KW_NON_PY; static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef kwnames; @@ -2938,10 +2903,9 @@ TARGET(CALL_KW_PY) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_KW_PY; + int opcode = CALL_KW_PY; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3046,10 +3010,9 @@ TARGET(CALL_LEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_LEN; + int opcode = CALL_LEN; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3114,10 +3077,9 @@ TARGET(CALL_LIST_APPEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_LIST_APPEND; + int opcode = CALL_LIST_APPEND; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3179,10 +3141,9 @@ TARGET(CALL_METHOD_DESCRIPTOR_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_METHOD_DESCRIPTOR_FAST; + int opcode = CALL_METHOD_DESCRIPTOR_FAST; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3284,10 +3245,9 @@ TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; + int opcode = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3389,10 +3349,9 @@ TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_METHOD_DESCRIPTOR_NOARGS; + int opcode = CALL_METHOD_DESCRIPTOR_NOARGS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3494,10 +3453,9 @@ TARGET(CALL_METHOD_DESCRIPTOR_O) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_METHOD_DESCRIPTOR_O; + int opcode = CALL_METHOD_DESCRIPTOR_O; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3601,15 +3559,15 @@ TARGET(CALL_NON_PY_GENERAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_NON_PY_GENERAL; + int opcode = CALL_NON_PY_GENERAL; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(CALL_NON_PY_GENERAL); + opcode = CALL_NON_PY_GENERAL; static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); _PyStackRef *callable; _PyStackRef *self_or_null; @@ -3704,10 +3662,9 @@ TARGET(CALL_PY_EXACT_ARGS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_PY_EXACT_ARGS; + int opcode = CALL_PY_EXACT_ARGS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3816,10 +3773,9 @@ TARGET(CALL_PY_GENERAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_PY_GENERAL; + int opcode = CALL_PY_GENERAL; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3913,10 +3869,9 @@ TARGET(CALL_STR_1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_STR_1; + int opcode = CALL_STR_1; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -3987,10 +3942,9 @@ TARGET(CALL_TUPLE_1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_TUPLE_1; + int opcode = CALL_TUPLE_1; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4061,10 +4015,9 @@ TARGET(CALL_TYPE_1) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CALL_TYPE_1; + int opcode = CALL_TYPE_1; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4106,10 +4059,9 @@ TARGET(CHECK_EG_MATCH) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CHECK_EG_MATCH; + int opcode = CHECK_EG_MATCH; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CHECK_EG_MATCH); @@ -4162,10 +4114,9 @@ TARGET(CHECK_EXC_MATCH) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CHECK_EXC_MATCH; + int opcode = CHECK_EXC_MATCH; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CHECK_EXC_MATCH); @@ -4195,10 +4146,9 @@ TARGET(CLEANUP_THROW) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CLEANUP_THROW; + int opcode = CLEANUP_THROW; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4244,10 +4194,9 @@ TARGET(COMPARE_OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COMPARE_OP; + int opcode = COMPARE_OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(COMPARE_OP); @@ -4314,10 +4263,9 @@ TARGET(COMPARE_OP_FLOAT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COMPARE_OP_FLOAT; + int opcode = COMPARE_OP_FLOAT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4367,10 +4315,9 @@ TARGET(COMPARE_OP_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COMPARE_OP_INT; + int opcode = COMPARE_OP_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4432,10 +4379,9 @@ TARGET(COMPARE_OP_STR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COMPARE_OP_STR; + int opcode = COMPARE_OP_STR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4486,10 +4432,9 @@ TARGET(CONTAINS_OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CONTAINS_OP; + int opcode = CONTAINS_OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(CONTAINS_OP); @@ -4539,10 +4484,9 @@ TARGET(CONTAINS_OP_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CONTAINS_OP_DICT; + int opcode = CONTAINS_OP_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4580,10 +4524,9 @@ TARGET(CONTAINS_OP_SET) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CONTAINS_OP_SET; + int opcode = CONTAINS_OP_SET; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4622,10 +4565,9 @@ TARGET(CONVERT_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = CONVERT_VALUE; + int opcode = CONVERT_VALUE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CONVERT_VALUE); @@ -4655,10 +4597,9 @@ TARGET(COPY) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COPY; + int opcode = COPY; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(COPY); @@ -4675,10 +4616,9 @@ TARGET(COPY_FREE_VARS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = COPY_FREE_VARS; + int opcode = COPY_FREE_VARS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(COPY_FREE_VARS); @@ -4698,10 +4638,9 @@ TARGET(DELETE_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_ATTR; + int opcode = DELETE_ATTR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_ATTR); @@ -4722,10 +4661,9 @@ TARGET(DELETE_DEREF) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_DEREF; + int opcode = DELETE_DEREF; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_DEREF); @@ -4747,10 +4685,9 @@ TARGET(DELETE_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_FAST; + int opcode = DELETE_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_FAST); @@ -4774,10 +4711,9 @@ TARGET(DELETE_GLOBAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_GLOBAL; + int opcode = DELETE_GLOBAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_GLOBAL); @@ -4801,10 +4737,9 @@ TARGET(DELETE_NAME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_NAME; + int opcode = DELETE_NAME; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_NAME); @@ -4835,10 +4770,9 @@ TARGET(DELETE_SUBSCR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DELETE_SUBSCR; + int opcode = DELETE_SUBSCR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_SUBSCR); @@ -4863,10 +4797,9 @@ TARGET(DICT_MERGE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DICT_MERGE; + int opcode = DICT_MERGE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DICT_MERGE); @@ -4897,10 +4830,9 @@ TARGET(DICT_UPDATE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = DICT_UPDATE; + int opcode = DICT_UPDATE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DICT_UPDATE); @@ -4935,10 +4867,9 @@ TARGET(END_ASYNC_FOR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = END_ASYNC_FOR; + int opcode = END_ASYNC_FOR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -4973,10 +4904,9 @@ TARGET(END_FOR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = END_FOR; + int opcode = END_FOR; (void)(opcode); + #endif next_instr += 1; INSTRUCTION_STATS(END_FOR); _PyStackRef value; @@ -4996,10 +4926,9 @@ TARGET(END_SEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = END_SEND; + int opcode = END_SEND; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(END_SEND); @@ -5019,15 +4948,15 @@ TARGET(ENTER_EXECUTOR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = ENTER_EXECUTOR; + int opcode = ENTER_EXECUTOR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); + opcode = ENTER_EXECUTOR; #ifdef _Py_TIER2 PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; @@ -5058,10 +4987,9 @@ TARGET(EXIT_INIT_CHECK) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = EXIT_INIT_CHECK; + int opcode = EXIT_INIT_CHECK; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(EXIT_INIT_CHECK); @@ -5083,13 +5011,13 @@ TARGET(EXTENDED_ARG) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = EXTENDED_ARG; + int opcode = EXTENDED_ARG; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(EXTENDED_ARG); + opcode = EXTENDED_ARG; assert(oparg); opcode = next_instr->op.code; oparg = oparg << 8 | next_instr->op.arg; @@ -5099,10 +5027,9 @@ TARGET(FORMAT_SIMPLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FORMAT_SIMPLE; + int opcode = FORMAT_SIMPLE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(FORMAT_SIMPLE); @@ -5139,10 +5066,9 @@ TARGET(FORMAT_WITH_SPEC) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FORMAT_WITH_SPEC; + int opcode = FORMAT_WITH_SPEC; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(FORMAT_WITH_SPEC); @@ -5168,10 +5094,9 @@ TARGET(FOR_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FOR_ITER; + int opcode = FOR_ITER; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(FOR_ITER); @@ -5235,10 +5160,9 @@ TARGET(FOR_ITER_GEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FOR_ITER_GEN; + int opcode = FOR_ITER_GEN; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -5302,10 +5226,9 @@ TARGET(FOR_ITER_LIST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FOR_ITER_LIST; + int opcode = FOR_ITER_LIST; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -5364,10 +5287,9 @@ TARGET(FOR_ITER_RANGE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FOR_ITER_RANGE; + int opcode = FOR_ITER_RANGE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -5420,10 +5342,9 @@ TARGET(FOR_ITER_TUPLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = FOR_ITER_TUPLE; + int opcode = FOR_ITER_TUPLE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -5479,10 +5400,9 @@ TARGET(GET_AITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_AITER; + int opcode = GET_AITER; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_AITER); @@ -5533,10 +5453,9 @@ TARGET(GET_ANEXT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_ANEXT; + int opcode = GET_ANEXT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_ANEXT); @@ -5558,10 +5477,9 @@ TARGET(GET_AWAITABLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_AWAITABLE; + int opcode = GET_AWAITABLE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_AWAITABLE); @@ -5582,10 +5500,9 @@ TARGET(GET_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_ITER; + int opcode = GET_ITER; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_ITER); @@ -5607,10 +5524,9 @@ TARGET(GET_LEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_LEN; + int opcode = GET_LEN; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_LEN); @@ -5637,10 +5553,9 @@ TARGET(GET_YIELD_FROM_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = GET_YIELD_FROM_ITER; + int opcode = GET_YIELD_FROM_ITER; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(GET_YIELD_FROM_ITER); @@ -5685,10 +5600,9 @@ TARGET(IMPORT_FROM) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = IMPORT_FROM; + int opcode = IMPORT_FROM; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IMPORT_FROM); @@ -5711,10 +5625,9 @@ TARGET(IMPORT_NAME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = IMPORT_NAME; + int opcode = IMPORT_NAME; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IMPORT_NAME); @@ -5743,15 +5656,15 @@ TARGET(INSTRUMENTED_CALL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_CALL; + int opcode = INSTRUMENTED_CALL; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL); + opcode = INSTRUMENTED_CALL; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -5924,15 +5837,15 @@ TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_CALL_FUNCTION_EX; + int opcode = INSTRUMENTED_CALL_FUNCTION_EX; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); + opcode = INSTRUMENTED_CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; _PyStackRef kwargs_in; @@ -6116,15 +6029,15 @@ TARGET(INSTRUMENTED_CALL_KW) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_CALL_KW; + int opcode = INSTRUMENTED_CALL_KW; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); + opcode = INSTRUMENTED_CALL_KW; _PyStackRef *callable; _PyStackRef *self_or_null; _PyStackRef *args; @@ -6294,10 +6207,9 @@ TARGET(INSTRUMENTED_END_FOR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_END_FOR; + int opcode = INSTRUMENTED_END_FOR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; next_instr += 1; @@ -6324,10 +6236,9 @@ TARGET(INSTRUMENTED_END_SEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_END_SEND; + int opcode = INSTRUMENTED_END_SEND; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6359,10 +6270,9 @@ TARGET(INSTRUMENTED_FOR_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_FOR_ITER; + int opcode = INSTRUMENTED_FOR_ITER; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6402,15 +6312,15 @@ TARGET(INSTRUMENTED_INSTRUCTION) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_INSTRUCTION; + int opcode = INSTRUMENTED_INSTRUCTION; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); + opcode = INSTRUMENTED_INSTRUCTION; _PyFrame_SetStackPointer(frame, stack_pointer); int next_opcode = _Py_call_instrumentation_instruction( tstate, frame, this_instr); @@ -6429,10 +6339,9 @@ TARGET(INSTRUMENTED_JUMP_BACKWARD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_JUMP_BACKWARD; + int opcode = INSTRUMENTED_JUMP_BACKWARD; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6461,10 +6370,9 @@ TARGET(INSTRUMENTED_JUMP_FORWARD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_JUMP_FORWARD; + int opcode = INSTRUMENTED_JUMP_FORWARD; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6476,16 +6384,16 @@ TARGET(INSTRUMENTED_LINE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_LINE; + int opcode = INSTRUMENTED_LINE; (void)(opcode); + #endif _Py_CODEUNIT* const prev_instr = frame->instr_ptr; _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_LINE); + opcode = INSTRUMENTED_LINE; int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); @@ -6520,15 +6428,15 @@ TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_LOAD_SUPER_ATTR; + int opcode = INSTRUMENTED_LOAD_SUPER_ATTR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); + opcode = INSTRUMENTED_LOAD_SUPER_ATTR; _PyStackRef global_super_st; _PyStackRef class_st; _PyStackRef self_st; @@ -6616,10 +6524,9 @@ TARGET(INSTRUMENTED_NOT_TAKEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_NOT_TAKEN; + int opcode = INSTRUMENTED_NOT_TAKEN; (void)(opcode); + #endif _Py_CODEUNIT* const prev_instr = frame->instr_ptr; _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; @@ -6633,10 +6540,9 @@ TARGET(INSTRUMENTED_POP_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_POP_ITER; + int opcode = INSTRUMENTED_POP_ITER; (void)(opcode); + #endif _Py_CODEUNIT* const prev_instr = frame->instr_ptr; _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; @@ -6656,10 +6562,9 @@ TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_POP_JUMP_IF_FALSE; + int opcode = INSTRUMENTED_POP_JUMP_IF_FALSE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6678,10 +6583,9 @@ TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_POP_JUMP_IF_NONE; + int opcode = INSTRUMENTED_POP_JUMP_IF_NONE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6704,10 +6608,9 @@ TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_POP_JUMP_IF_NOT_NONE; + int opcode = INSTRUMENTED_POP_JUMP_IF_NOT_NONE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6728,10 +6631,9 @@ TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_POP_JUMP_IF_TRUE; + int opcode = INSTRUMENTED_POP_JUMP_IF_TRUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6750,10 +6652,9 @@ TARGET(INSTRUMENTED_RESUME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_RESUME; + int opcode = INSTRUMENTED_RESUME; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6834,10 +6735,9 @@ TARGET(INSTRUMENTED_RETURN_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_RETURN_VALUE; + int opcode = INSTRUMENTED_RETURN_VALUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6885,10 +6785,9 @@ TARGET(INSTRUMENTED_YIELD_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INSTRUMENTED_YIELD_VALUE; + int opcode = INSTRUMENTED_YIELD_VALUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -6958,10 +6857,9 @@ TARGET(INTERPRETER_EXIT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = INTERPRETER_EXIT; + int opcode = INTERPRETER_EXIT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INTERPRETER_EXIT); @@ -6982,10 +6880,9 @@ TARGET(IS_OP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = IS_OP; + int opcode = IS_OP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(IS_OP); @@ -7006,10 +6903,9 @@ TARGET(JUMP_BACKWARD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = JUMP_BACKWARD; + int opcode = JUMP_BACKWARD; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(JUMP_BACKWARD); @@ -7056,10 +6952,9 @@ TARGET(JUMP_BACKWARD_JIT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = JUMP_BACKWARD_JIT; + int opcode = JUMP_BACKWARD_JIT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7130,10 +7025,9 @@ TARGET(JUMP_BACKWARD_NO_INTERRUPT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = JUMP_BACKWARD_NO_INTERRUPT; + int opcode = JUMP_BACKWARD_NO_INTERRUPT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); @@ -7149,10 +7043,9 @@ TARGET(JUMP_BACKWARD_NO_JIT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = JUMP_BACKWARD_NO_JIT; + int opcode = JUMP_BACKWARD_NO_JIT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(JUMP_BACKWARD_NO_JIT); @@ -7186,10 +7079,9 @@ TARGET(JUMP_FORWARD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = JUMP_FORWARD; + int opcode = JUMP_FORWARD; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(JUMP_FORWARD); @@ -7199,10 +7091,9 @@ TARGET(LIST_APPEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LIST_APPEND; + int opcode = LIST_APPEND; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LIST_APPEND); @@ -7222,10 +7113,9 @@ TARGET(LIST_EXTEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LIST_EXTEND; + int opcode = LIST_EXTEND; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LIST_EXTEND); @@ -7264,10 +7154,9 @@ TARGET(LOAD_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR; + int opcode = LOAD_ATTR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR); @@ -7349,10 +7238,9 @@ TARGET(LOAD_ATTR_CLASS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_CLASS; + int opcode = LOAD_ATTR_CLASS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7402,10 +7290,9 @@ TARGET(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; + int opcode = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7465,10 +7352,9 @@ TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; + int opcode = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7524,10 +7410,9 @@ TARGET(LOAD_ATTR_INSTANCE_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_INSTANCE_VALUE; + int opcode = LOAD_ATTR_INSTANCE_VALUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7602,10 +7487,9 @@ TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_METHOD_LAZY_DICT; + int opcode = LOAD_ATTR_METHOD_LAZY_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7660,10 +7544,9 @@ TARGET(LOAD_ATTR_METHOD_NO_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_METHOD_NO_DICT; + int opcode = LOAD_ATTR_METHOD_NO_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7707,10 +7590,9 @@ TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_METHOD_WITH_VALUES; + int opcode = LOAD_ATTR_METHOD_WITH_VALUES; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7776,10 +7658,9 @@ TARGET(LOAD_ATTR_MODULE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_MODULE; + int opcode = LOAD_ATTR_MODULE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7856,10 +7737,9 @@ TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_NONDESCRIPTOR_NO_DICT; + int opcode = LOAD_ATTR_NONDESCRIPTOR_NO_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7898,10 +7778,9 @@ TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; + int opcode = LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -7961,10 +7840,9 @@ TARGET(LOAD_ATTR_PROPERTY) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_PROPERTY; + int opcode = LOAD_ATTR_PROPERTY; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -8057,10 +7935,9 @@ TARGET(LOAD_ATTR_SLOT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_SLOT; + int opcode = LOAD_ATTR_SLOT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -8121,10 +7998,9 @@ TARGET(LOAD_ATTR_WITH_HINT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_ATTR_WITH_HINT; + int opcode = LOAD_ATTR_WITH_HINT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -8226,10 +8102,9 @@ TARGET(LOAD_BUILD_CLASS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_BUILD_CLASS; + int opcode = LOAD_BUILD_CLASS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_BUILD_CLASS); @@ -8257,10 +8132,9 @@ TARGET(LOAD_COMMON_CONSTANT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_COMMON_CONSTANT; + int opcode = LOAD_COMMON_CONSTANT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); @@ -8284,10 +8158,9 @@ TARGET(LOAD_CONST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_CONST; + int opcode = LOAD_CONST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST); @@ -8322,10 +8195,9 @@ TARGET(LOAD_CONST_IMMORTAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_CONST_IMMORTAL; + int opcode = LOAD_CONST_IMMORTAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST_IMMORTAL); @@ -8342,10 +8214,9 @@ TARGET(LOAD_CONST_MORTAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_CONST_MORTAL; + int opcode = LOAD_CONST_MORTAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_CONST_MORTAL); @@ -8361,10 +8232,9 @@ TARGET(LOAD_DEREF) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_DEREF; + int opcode = LOAD_DEREF; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_DEREF); @@ -8386,10 +8256,9 @@ TARGET(LOAD_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FAST; + int opcode = LOAD_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST); @@ -8404,10 +8273,9 @@ TARGET(LOAD_FAST_AND_CLEAR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FAST_AND_CLEAR; + int opcode = LOAD_FAST_AND_CLEAR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); @@ -8422,10 +8290,9 @@ TARGET(LOAD_FAST_CHECK) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FAST_CHECK; + int opcode = LOAD_FAST_CHECK; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_CHECK); @@ -8449,10 +8316,9 @@ TARGET(LOAD_FAST_LOAD_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FAST_LOAD_FAST; + int opcode = LOAD_FAST_LOAD_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); @@ -8471,10 +8337,9 @@ TARGET(LOAD_FROM_DICT_OR_DEREF) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FROM_DICT_OR_DEREF; + int opcode = LOAD_FROM_DICT_OR_DEREF; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); @@ -8517,10 +8382,9 @@ TARGET(LOAD_FROM_DICT_OR_GLOBALS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_FROM_DICT_OR_GLOBALS; + int opcode = LOAD_FROM_DICT_OR_GLOBALS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); @@ -8598,10 +8462,9 @@ TARGET(LOAD_GLOBAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_GLOBAL; + int opcode = LOAD_GLOBAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(LOAD_GLOBAL); @@ -8653,10 +8516,9 @@ TARGET(LOAD_GLOBAL_BUILTIN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_GLOBAL_BUILTIN; + int opcode = LOAD_GLOBAL_BUILTIN; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -8738,10 +8600,9 @@ TARGET(LOAD_GLOBAL_MODULE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_GLOBAL_MODULE; + int opcode = LOAD_GLOBAL_MODULE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -8807,10 +8668,9 @@ TARGET(LOAD_LOCALS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_LOCALS; + int opcode = LOAD_LOCALS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_LOCALS); @@ -8832,10 +8692,9 @@ TARGET(LOAD_NAME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_NAME; + int opcode = LOAD_NAME; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_NAME); @@ -8856,10 +8715,9 @@ TARGET(LOAD_SMALL_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_SMALL_INT; + int opcode = LOAD_SMALL_INT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_SMALL_INT); @@ -8875,10 +8733,9 @@ TARGET(LOAD_SPECIAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_SPECIAL; + int opcode = LOAD_SPECIAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(LOAD_SPECIAL); @@ -8917,16 +8774,16 @@ TARGET(LOAD_SUPER_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_SUPER_ATTR; + int opcode = LOAD_SUPER_ATTR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(LOAD_SUPER_ATTR); PREDICTED_LOAD_SUPER_ATTR:; _Py_CODEUNIT* const this_instr = next_instr - 2; (void)this_instr; + opcode = LOAD_SUPER_ATTR; _PyStackRef global_super_st; _PyStackRef class_st; _PyStackRef self_st; @@ -9030,10 +8887,9 @@ TARGET(LOAD_SUPER_ATTR_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_SUPER_ATTR_ATTR; + int opcode = LOAD_SUPER_ATTR_ATTR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9082,10 +8938,9 @@ TARGET(LOAD_SUPER_ATTR_METHOD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = LOAD_SUPER_ATTR_METHOD; + int opcode = LOAD_SUPER_ATTR_METHOD; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9150,10 +9005,9 @@ TARGET(MAKE_CELL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MAKE_CELL; + int opcode = MAKE_CELL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAKE_CELL); @@ -9174,10 +9028,9 @@ TARGET(MAKE_FUNCTION) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MAKE_FUNCTION; + int opcode = MAKE_FUNCTION; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAKE_FUNCTION); @@ -9208,10 +9061,9 @@ TARGET(MAP_ADD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MAP_ADD; + int opcode = MAP_ADD; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MAP_ADD); @@ -9242,10 +9094,9 @@ TARGET(MATCH_CLASS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MATCH_CLASS; + int opcode = MATCH_CLASS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_CLASS); @@ -9287,10 +9138,9 @@ TARGET(MATCH_KEYS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MATCH_KEYS; + int opcode = MATCH_KEYS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_KEYS); @@ -9316,10 +9166,9 @@ TARGET(MATCH_MAPPING) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MATCH_MAPPING; + int opcode = MATCH_MAPPING; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_MAPPING); @@ -9336,10 +9185,9 @@ TARGET(MATCH_SEQUENCE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = MATCH_SEQUENCE; + int opcode = MATCH_SEQUENCE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(MATCH_SEQUENCE); @@ -9356,10 +9204,9 @@ TARGET(NOP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = NOP; + int opcode = NOP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(NOP); @@ -9368,10 +9215,9 @@ TARGET(NOT_TAKEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = NOT_TAKEN; + int opcode = NOT_TAKEN; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(NOT_TAKEN); @@ -9380,10 +9226,9 @@ TARGET(POP_EXCEPT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_EXCEPT; + int opcode = POP_EXCEPT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_EXCEPT); @@ -9402,10 +9247,9 @@ TARGET(POP_ITER) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_ITER; + int opcode = POP_ITER; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_ITER); @@ -9419,10 +9263,9 @@ TARGET(POP_JUMP_IF_FALSE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_JUMP_IF_FALSE; + int opcode = POP_JUMP_IF_FALSE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9442,10 +9285,9 @@ TARGET(POP_JUMP_IF_NONE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_JUMP_IF_NONE; + int opcode = POP_JUMP_IF_NONE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9481,10 +9323,9 @@ TARGET(POP_JUMP_IF_NOT_NONE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_JUMP_IF_NOT_NONE; + int opcode = POP_JUMP_IF_NOT_NONE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9520,10 +9361,9 @@ TARGET(POP_JUMP_IF_TRUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_JUMP_IF_TRUE; + int opcode = POP_JUMP_IF_TRUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9543,10 +9383,9 @@ TARGET(POP_TOP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = POP_TOP; + int opcode = POP_TOP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(POP_TOP); @@ -9560,10 +9399,9 @@ TARGET(PUSH_EXC_INFO) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = PUSH_EXC_INFO; + int opcode = PUSH_EXC_INFO; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(PUSH_EXC_INFO); @@ -9590,10 +9428,9 @@ TARGET(PUSH_NULL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = PUSH_NULL; + int opcode = PUSH_NULL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(PUSH_NULL); @@ -9607,10 +9444,9 @@ TARGET(RAISE_VARARGS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RAISE_VARARGS; + int opcode = RAISE_VARARGS; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9639,10 +9475,9 @@ TARGET(RERAISE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RERAISE; + int opcode = RERAISE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9689,10 +9524,9 @@ TARGET(RESERVED) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RESERVED; + int opcode = RESERVED; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESERVED); @@ -9703,10 +9537,9 @@ TARGET(RESUME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RESUME; + int opcode = RESUME; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RESUME); @@ -9782,10 +9615,9 @@ TARGET(RESUME_CHECK) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RESUME_CHECK; + int opcode = RESUME_CHECK; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -9821,10 +9653,9 @@ TARGET(RETURN_GENERATOR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RETURN_GENERATOR; + int opcode = RETURN_GENERATOR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_GENERATOR); @@ -9861,10 +9692,9 @@ TARGET(RETURN_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = RETURN_VALUE; + int opcode = RETURN_VALUE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_VALUE); @@ -9894,10 +9724,9 @@ TARGET(SEND) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SEND; + int opcode = SEND; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(SEND); @@ -9995,10 +9824,9 @@ TARGET(SEND_GEN) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SEND_GEN; + int opcode = SEND_GEN; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10066,10 +9894,9 @@ TARGET(SETUP_ANNOTATIONS) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SETUP_ANNOTATIONS; + int opcode = SETUP_ANNOTATIONS; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SETUP_ANNOTATIONS); @@ -10114,10 +9941,9 @@ TARGET(SET_ADD) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SET_ADD; + int opcode = SET_ADD; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_ADD); @@ -10140,10 +9966,9 @@ TARGET(SET_FUNCTION_ATTRIBUTE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SET_FUNCTION_ATTRIBUTE; + int opcode = SET_FUNCTION_ATTRIBUTE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); @@ -10169,10 +9994,9 @@ TARGET(SET_UPDATE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SET_UPDATE; + int opcode = SET_UPDATE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SET_UPDATE); @@ -10195,10 +10019,9 @@ TARGET(STORE_ATTR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_ATTR; + int opcode = STORE_ATTR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 5; INSTRUCTION_STATS(STORE_ATTR); @@ -10247,10 +10070,9 @@ TARGET(STORE_ATTR_INSTANCE_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_ATTR_INSTANCE_VALUE; + int opcode = STORE_ATTR_INSTANCE_VALUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10324,10 +10146,9 @@ TARGET(STORE_ATTR_SLOT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_ATTR_SLOT; + int opcode = STORE_ATTR_SLOT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10376,10 +10197,9 @@ TARGET(STORE_ATTR_WITH_HINT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_ATTR_WITH_HINT; + int opcode = STORE_ATTR_WITH_HINT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10477,10 +10297,9 @@ TARGET(STORE_DEREF) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_DEREF; + int opcode = STORE_DEREF; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_DEREF); @@ -10497,10 +10316,9 @@ TARGET(STORE_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_FAST; + int opcode = STORE_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST); @@ -10518,10 +10336,9 @@ TARGET(STORE_FAST_LOAD_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_FAST_LOAD_FAST; + int opcode = STORE_FAST_LOAD_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); @@ -10542,10 +10359,9 @@ TARGET(STORE_FAST_STORE_FAST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_FAST_STORE_FAST; + int opcode = STORE_FAST_STORE_FAST; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST_STORE_FAST); @@ -10574,10 +10390,9 @@ TARGET(STORE_GLOBAL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_GLOBAL; + int opcode = STORE_GLOBAL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_GLOBAL); @@ -10598,10 +10413,9 @@ TARGET(STORE_NAME) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_NAME; + int opcode = STORE_NAME; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_NAME); @@ -10639,10 +10453,9 @@ TARGET(STORE_SLICE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_SLICE; + int opcode = STORE_SLICE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_SLICE); @@ -10694,10 +10507,9 @@ TARGET(STORE_SUBSCR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_SUBSCR; + int opcode = STORE_SUBSCR; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(STORE_SUBSCR); @@ -10746,10 +10558,9 @@ TARGET(STORE_SUBSCR_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_SUBSCR_DICT; + int opcode = STORE_SUBSCR_DICT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10788,10 +10599,9 @@ TARGET(STORE_SUBSCR_LIST_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = STORE_SUBSCR_LIST_INT; + int opcode = STORE_SUBSCR_LIST_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10855,10 +10665,9 @@ TARGET(SWAP) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = SWAP; + int opcode = SWAP; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SWAP); @@ -10875,10 +10684,9 @@ TARGET(TO_BOOL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL; + int opcode = TO_BOOL; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 4; INSTRUCTION_STATS(TO_BOOL); @@ -10922,10 +10730,9 @@ TARGET(TO_BOOL_ALWAYS_TRUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_ALWAYS_TRUE; + int opcode = TO_BOOL_ALWAYS_TRUE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10960,10 +10767,9 @@ TARGET(TO_BOOL_BOOL) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_BOOL; + int opcode = TO_BOOL_BOOL; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -10985,10 +10791,9 @@ TARGET(TO_BOOL_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_INT; + int opcode = TO_BOOL_INT; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11021,10 +10826,9 @@ TARGET(TO_BOOL_LIST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_LIST; + int opcode = TO_BOOL_LIST; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11051,10 +10855,9 @@ TARGET(TO_BOOL_NONE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_NONE; + int opcode = TO_BOOL_NONE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11080,10 +10883,9 @@ TARGET(TO_BOOL_STR) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = TO_BOOL_STR; + int opcode = TO_BOOL_STR; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11117,10 +10919,9 @@ TARGET(UNARY_INVERT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNARY_INVERT; + int opcode = UNARY_INVERT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_INVERT); @@ -11141,10 +10942,9 @@ TARGET(UNARY_NEGATIVE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNARY_NEGATIVE; + int opcode = UNARY_NEGATIVE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_NEGATIVE); @@ -11165,10 +10965,9 @@ TARGET(UNARY_NOT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNARY_NOT; + int opcode = UNARY_NOT; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNARY_NOT); @@ -11184,10 +10983,9 @@ TARGET(UNPACK_EX) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNPACK_EX; + int opcode = UNPACK_EX; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(UNPACK_EX); @@ -11210,10 +11008,9 @@ TARGET(UNPACK_SEQUENCE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNPACK_SEQUENCE; + int opcode = UNPACK_SEQUENCE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 2; INSTRUCTION_STATS(UNPACK_SEQUENCE); @@ -11260,10 +11057,9 @@ TARGET(UNPACK_SEQUENCE_LIST) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNPACK_SEQUENCE_LIST; + int opcode = UNPACK_SEQUENCE_LIST; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11308,10 +11104,9 @@ TARGET(UNPACK_SEQUENCE_TUPLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNPACK_SEQUENCE_TUPLE; + int opcode = UNPACK_SEQUENCE_TUPLE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11347,10 +11142,9 @@ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = UNPACK_SEQUENCE_TWO_TUPLE; + int opcode = UNPACK_SEQUENCE_TWO_TUPLE; (void)(opcode); + #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; @@ -11387,10 +11181,9 @@ TARGET(WITH_EXCEPT_START) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = WITH_EXCEPT_START; + int opcode = WITH_EXCEPT_START; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(WITH_EXCEPT_START); @@ -11446,10 +11239,9 @@ TARGET(YIELD_VALUE) { #if defined(Py_TAIL_CALL_INTERP) - int opcode; - #endif - opcode = YIELD_VALUE; + int opcode = YIELD_VALUE; (void)(opcode); + #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(YIELD_VALUE); diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index 979646a7ca6525..02012fb4172260 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -230,10 +230,9 @@ def generate_tier1_cases( # We need to ifdef it because this breaks platforms # without computed gotos/tail calling. out.emit(f"#if defined(Py_TAIL_CALL_INTERP)\n") - out.emit(f"int opcode;\n") - out.emit(f"#endif\n") - out.emit(f"opcode = {name};\n") + out.emit(f"int opcode = {name};\n") out.emit(f"(void)(opcode);\n") + out.emit(f"#endif\n") needs_this = uses_this(inst) unused_guard = "(void)this_instr;\n" if inst.properties.needs_prev: @@ -252,6 +251,8 @@ def generate_tier1_cases( if needs_this: out.emit(f"_Py_CODEUNIT* const this_instr = next_instr - {inst.size};\n") out.emit(unused_guard) + if inst.properties.uses_opcode: + out.emit(f"opcode = {name};\n") if inst.family is not None: out.emit( f"static_assert({inst.family.size} == {inst.size-1}" From 34379d0a593e5a76e2f754cdd7fccb79f25a4613 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 7 Feb 2025 09:44:24 -0500 Subject: [PATCH 110/311] gh-117657: Fix data race in `dict_dict_merge` (gh-129755) Found while running `test_load_attr_module` from `test_opcache` under TSan. --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 91cf013a1dc24b..d979cd72b48e69 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3793,7 +3793,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe ensure_shared_on_resize(mp); dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp)); - mp->ma_keys = keys; + set_keys(mp, keys); STORE_USED(mp, other->ma_used); ASSERT_CONSISTENT(mp); From 476a78fdd62ad72b279ac692831619b22ad56201 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Fri, 7 Feb 2025 22:59:34 +0800 Subject: [PATCH 111/311] gh-128563: Clarify tail calling interpreter is not TCO (#129809) Clarify tail calling interpreter is not TCO --- Doc/whatsnew/3.14.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index c788db31fc27ed..e5e9474d1e5d85 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -233,6 +233,11 @@ For further information on how to build Python, see (Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.) +.. note:: + + This is not to be confused with tail call optimization of Python code. + Python functions do not currently have tail call optimization. + Other language changes ====================== From a93a5a39fb3c4d15082d2464ef10161aa9f2c46e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:12:33 +0000 Subject: [PATCH 112/311] gh-128563: Clarify clarificatory tail calling wording in What's New (#129812) --- Doc/whatsnew/3.14.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index e5e9474d1e5d85..1e469e8738bfcb 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -230,13 +230,15 @@ tested and can validate its improved performance. For further information on how to build Python, see :option:`--with-tail-call-interp`. -(Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this -in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.) - .. note:: - This is not to be confused with tail call optimization of Python code. - Python functions do not currently have tail call optimization. + This is not to be confused with `tail call optimization`__ of Python + functions, which is currently not implemented in CPython. + + __ https://en.wikipedia.org/wiki/Tail_call + +(Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this +in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.) Other language changes From f52a3a51eb711e16445307ff1ce28e94ff4b1535 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 7 Feb 2025 21:46:26 +0530 Subject: [PATCH 113/311] fix tests in `test_asyncio.test_tasks` to use correct `all_tasks` (#129815) --- Lib/test/test_asyncio/test_tasks.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 7a052817766a07..cd4a48499e8f11 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2757,7 +2757,6 @@ async def func(): # Add patched Task & Future back to the test case cls.Task = Task cls.Future = Future - cls.all_tasks = tasks.all_tasks # Add an extra unit-test cls.test_subclasses_ctask_cfuture = test_subclasses_ctask_cfuture @@ -2883,7 +2882,7 @@ class PyTask_CFutureSubclass_Tests(BaseTaskTests, test_utils.TestCase): Future = getattr(futures, '_CFuture', None) Task = tasks._PyTask - all_tasks = tasks._py_all_tasks + all_tasks = staticmethod(tasks._py_all_tasks) @unittest.skipUnless(hasattr(tasks, '_CTask'), @@ -2916,7 +2915,7 @@ class PyTask_PyFuture_Tests(BaseTaskTests, SetMethodsTest, class PyTask_PyFuture_SubclassTests(BaseTaskTests, test_utils.TestCase): Task = tasks._PyTask Future = futures._PyFuture - + all_tasks = staticmethod(tasks._py_all_tasks) @unittest.skipUnless(hasattr(tasks, '_CTask'), 'requires the C _asyncio module') From fbaa6c8ff06cf885d9b8c8ea6cf25bab3781a2bd Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 7 Feb 2025 08:49:51 -0800 Subject: [PATCH 114/311] GH-129763: Remove the LLTRACE macro (GH-129764) --- ...-02-06-17-05-09.gh-issue-129763.6ZxQ8W.rst | 1 + Misc/SpecialBuilds.txt | 26 +++++++------------ Python/bytecodes.c | 12 ++------- Python/ceval.c | 9 ++----- Python/ceval_macros.h | 6 ++--- Python/generated_cases.c.h | 13 ++-------- Tools/c-analyzer/TODO | 1 - Tools/cases_generator/analyzer.py | 1 - Tools/jit/template.c | 5 ++++ 9 files changed, 25 insertions(+), 49 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-05-09.gh-issue-129763.6ZxQ8W.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-05-09.gh-issue-129763.6ZxQ8W.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-05-09.gh-issue-129763.6ZxQ8W.rst new file mode 100644 index 00000000000000..ffb96c20fcad7e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-06-17-05-09.gh-issue-129763.6ZxQ8W.rst @@ -0,0 +1 @@ +Remove the internal ``LLTRACE`` macro (use :c:macro:`Py_DEBUG` instead). diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt index 78201bfbd6741d..23fa36af78cc52 100644 --- a/Misc/SpecialBuilds.txt +++ b/Misc/SpecialBuilds.txt @@ -78,22 +78,16 @@ Py_DEBUG This is what is generally meant by "a debug build" of Python. -Py_DEBUG implies LLTRACE and Py_REF_DEBUG. In addition, C assert()s are enabled +Py_DEBUG implies Py_REF_DEBUG. In addition, C assert()s are enabled (via the C way: by not defining NDEBUG), and some routines do additional sanity checks inside "#ifdef Py_DEBUG" blocks. - -LLTRACE -------- - -Compile in support for Low Level TRACE-ing of the main interpreter loop. - -When this preprocessor symbol is defined, before PyEval_EvalFrame executes a -frame's code it checks the frame's global namespace for a variable -"__lltrace__". If such a variable is found, mounds of information about what -the interpreter is doing are sprayed to stdout, such as every opcode and opcode -argument and values pushed onto and popped off the value stack. - -Not useful very often, but very useful when needed. - -Py_DEBUG implies LLTRACE. +Also, compile in support for "lltrace" (Low Level TRACE-ing) of the main +interpreter loop. Before _PyEval_EvalFrameDefault executes a frame's code, it +checks the frame's global namespace for a variable "__lltrace__" (as well as for +the environment variable PYTHON_LLTRACE"). If such a variable is found, mounds +of information about what the interpreter is doing are sprayed to stdout, such +as every opcode and opcode argument and values pushed onto and popped off the +value stack. Higher integer values for the environment variable result in more +and more detail being printed (the global __lltrace__ always enables the maximum +output). Not useful very often, but *very* useful when needed. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c3024f7f98f28c..07022e9932d203 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5301,7 +5301,7 @@ dummy_func( goto exception_unwind; } /* Resume normal execution */ -#ifdef LLTRACE +#ifdef Py_DEBUG if (frame->lltrace >= 5) { lltrace_resume_frame(frame); } @@ -5340,15 +5340,7 @@ dummy_func( } next_instr = frame->instr_ptr; - #ifdef LLTRACE - { - int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); - frame->lltrace = lltrace; - if (lltrace < 0) { - goto exit_unwind; - } - } - #endif + LLTRACE_RESUME_FRAME(); #ifdef Py_DEBUG /* _PyEval_EvalFrameDefault() must not be called with an exception set, diff --git a/Python/ceval.c b/Python/ceval.c index 5e834883f355e1..b2e2b540783880 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -43,11 +43,6 @@ #include // bool -#ifdef Py_DEBUG - /* For debugging the interpreter: */ -# define LLTRACE 1 /* Low-level trace feature */ -#endif - #if !defined(Py_BUILD_CORE) # error "ceval.c must be build with Py_BUILD_CORE define for best performance" #endif @@ -136,7 +131,7 @@ #endif -#ifdef LLTRACE +#ifdef Py_DEBUG static void dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer) { @@ -818,7 +813,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int entry_frame.owner = FRAME_OWNED_BY_INTERPRETER; entry_frame.visited = 0; entry_frame.return_offset = 0; -#ifdef LLTRACE +#ifdef Py_DEBUG entry_frame.lltrace = 0; #endif /* Push frame */ diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 44bb52a09aab5f..fde8e5efa983b2 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -108,21 +108,21 @@ #endif /* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ -#ifdef LLTRACE +#ifdef Py_DEBUG #define PRE_DISPATCH_GOTO() if (frame->lltrace >= 5) { \ lltrace_instruction(frame, stack_pointer, next_instr, opcode, oparg); } #else #define PRE_DISPATCH_GOTO() ((void)0) #endif -#if LLTRACE +#ifdef Py_DEBUG #define LLTRACE_RESUME_FRAME() \ do { \ int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); \ - frame->lltrace = lltrace; \ if (lltrace < 0) { \ JUMP_TO_LABEL(exit_unwind); \ } \ + frame->lltrace = lltrace; \ } while (0) #else #define LLTRACE_RESUME_FRAME() ((void)0) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index facdf2f78d28ea..ec9b287541f016 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -11416,7 +11416,7 @@ JUMP_TO_LABEL(error); JUMP_TO_LABEL(exception_unwind); } /* Resume normal execution */ - #ifdef LLTRACE + #ifdef Py_DEBUG if (frame->lltrace >= 5) { lltrace_resume_frame(frame); } @@ -11458,16 +11458,7 @@ JUMP_TO_LABEL(error); JUMP_TO_LABEL(exit_unwind); } next_instr = frame->instr_ptr; - #ifdef LLTRACE - { - int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); - frame->lltrace = lltrace; - if (lltrace < 0) { - JUMP_TO_LABEL(exit_unwind); - } - } - #endif - + LLTRACE_RESUME_FRAME(); #ifdef Py_DEBUG /* _PyEval_EvalFrameDefault() must not be called with an exception set, because it can clear it (directly or indirectly) and so the diff --git a/Tools/c-analyzer/TODO b/Tools/c-analyzer/TODO index e81ceb29c64bf0..edd0c4bc7fdaa6 100644 --- a/Tools/c-analyzer/TODO +++ b/Tools/c-analyzer/TODO @@ -69,7 +69,6 @@ Objects/tupleobject.c:_Py_tuple_zero_allocs Py_ssize_t _Py_ Objects/typeobject.c:next_version_tag static unsigned int next_version_tag Python/Python-ast.c:init_types():initialized static int initialized Python/bootstrap_hash.c:urandom_cache static struct { int fd; dev_t st_dev; ino_t st_ino; } urandom_cache -Python/ceval.c:lltrace static int lltrace Python/ceval.c:make_pending_calls():busy static int busy Python/dynload_shlib.c:handles static struct { dev_t dev; ino_t ino; void *handle; } handles[128] Python/dynload_shlib.c:nhandles static int nhandles diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index aa88608168c1be..57cc587428c2b9 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -676,7 +676,6 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "backoff_counter_triggers", "initial_temperature_backoff_counter", "JUMP_TO_LABEL", - "maybe_lltrace_resume_frame", "restart_backoff_counter", ) diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 929f37d2dddea1..6ea04f5640ef05 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -81,6 +81,11 @@ do { \ #undef WITHIN_STACK_BOUNDS #define WITHIN_STACK_BOUNDS() 1 +#undef LLTRACE_RESUME_FRAME +#define LLTRACE_RESUME_FRAME() \ + do { \ + } while (0) + #define TIER_TWO 2 __attribute__((preserve_none)) _Py_CODEUNIT * From 70e387c990c98d0fba9f2518061713809cff2f53 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 7 Feb 2025 09:52:49 -0800 Subject: [PATCH 115/311] GH-129709: Clean up tier two (GH-129710) --- Include/internal/pycore_uop_metadata.h | 2 +- Python/bytecodes.c | 40 +++----- Python/ceval.c | 34 +------ Python/ceval_macros.h | 38 ++++---- Python/executor_cases.c.h | 49 ++++------ Python/generated_cases.c.h | 26 +++-- Python/optimizer_bytecodes.c | 45 +-------- Python/optimizer_cases.c.h | 129 +------------------------ Tools/cases_generator/analyzer.py | 1 + Tools/jit/jit.h | 4 + Tools/jit/shim.c | 13 +-- Tools/jit/template.c | 64 ++++-------- 12 files changed, 97 insertions(+), 348 deletions(-) diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 0ed4c7c3a35436..2f957afb259b0f 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -287,7 +287,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_FATAL_ERROR] = 0, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, [_DEOPT] = 0, - [_ERROR_POP_N] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, + [_ERROR_POP_N] = HAS_ARG_FLAG, [_TIER2_RESUME_CHECK] = HAS_DEOPT_FLAG, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 07022e9932d203..9fb58b9cdcc022 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3526,6 +3526,7 @@ dummy_func( } op(_MAYBE_EXPAND_METHOD, (callable[1], self_or_null[1], args[oparg] -- func[1], maybe_self[1], args[oparg])) { + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -3892,6 +3893,7 @@ dummy_func( _CHECK_PERIODIC; op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable[1], null[1], args[oparg] -- init[1], self[1], args[oparg])) { + (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); DEOPT_IF(!PyStackRef_IsNull(null[0])); DEOPT_IF(!PyType_Check(callable_o)); @@ -4119,7 +4121,7 @@ dummy_func( PyObject *res_o = PyLong_FromSsize_t(len_i); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); if (res_o == NULL) { - GOTO_ERROR(error); + ERROR_NO_POP(); } PyStackRef_CLOSE(arg_stackref); DEAD(args); @@ -4364,6 +4366,7 @@ dummy_func( } op(_MAYBE_EXPAND_METHOD_KW, (callable[1], self_or_null[1], args[oparg], kwnames_in -- func[1], maybe_self[1], args[oparg], kwnames_out)) { + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -5022,7 +5025,7 @@ dummy_func( if (frame->lltrace >= 2) { printf("SIDE EXIT: [UOp "); _PyUOpPrint(&next_uop[-1]); - printf(", exit %u, temp %d, target %d -> %s]\n", + printf(", exit %lu, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); @@ -5032,11 +5035,11 @@ dummy_func( exit->temperature = initial_temperature_backoff_counter(); Py_CLEAR(exit->executor); } + tstate->previous_executor = (PyObject *)current_executor; if (exit->executor == NULL) { _Py_BackoffCounter temperature = exit->temperature; if (!backoff_counter_triggers(temperature)) { exit->temperature = advance_backoff_counter(temperature); - tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_ONE(target); } _PyExecutorObject *executor; @@ -5049,20 +5052,13 @@ dummy_func( int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth); if (optimized <= 0) { exit->temperature = restart_backoff_counter(temperature); - if (optimized < 0) { - GOTO_UNWIND(); - } - tstate->previous_executor = (PyObject *)current_executor; - GOTO_TIER_ONE(target); - } - else { - exit->temperature = initial_temperature_backoff_counter(); + GOTO_TIER_ONE(optimized < 0 ? NULL : target); } + exit->temperature = initial_temperature_backoff_counter(); } exit->executor = executor; } Py_INCREF(exit->executor); - tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_TWO(exit->executor); } @@ -5130,7 +5126,7 @@ dummy_func( if (frame->lltrace >= 2) { printf("DYNAMIC EXIT: [UOp "); _PyUOpPrint(&next_uop[-1]); - printf(", exit %u, temp %d, target %d -> %s]\n", + printf(", exit %lu, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); @@ -5150,21 +5146,15 @@ dummy_func( int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0); if (optimized <= 0) { exit->temperature = restart_backoff_counter(exit->temperature); - if (optimized < 0) { - GOTO_UNWIND(); - } - GOTO_TIER_ONE(target); - } - else { - exit->temperature = initial_temperature_backoff_counter(); + GOTO_TIER_ONE(optimized < 0 ? NULL : target); } + exit->temperature = initial_temperature_backoff_counter(); } GOTO_TIER_TWO(executor); } tier2 op(_START_EXECUTOR, (executor/4 --)) { - Py_DECREF(tstate->previous_executor); - tstate->previous_executor = NULL; + Py_CLEAR(tstate->previous_executor); #ifndef _Py_JIT current_executor = (_PyExecutorObject*)executor; #endif @@ -5190,14 +5180,16 @@ dummy_func( } tier2 op(_DEOPT, (--)) { - EXIT_TO_TIER1(); + tstate->previous_executor = (PyObject *)current_executor; + GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET()); } tier2 op(_ERROR_POP_N, (target/2 --)) { + tstate->previous_executor = (PyObject *)current_executor; assert(oparg == 0); frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; SYNC_SP(); - GOTO_UNWIND(); + GOTO_TIER_ONE(NULL); } /* Progress is guaranteed if we DEOPT on the eval breaker, because diff --git a/Python/ceval.c b/Python/ceval.c index b2e2b540783880..6c8e39a09c255b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -879,9 +879,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #undef LOAD_IP #define LOAD_IP(UNUSED) (void)0 -#undef GOTO_ERROR -#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two - #ifdef Py_STATS // Disable these macros that apply to Tier 1 stats when we are in Tier 2 #undef STAT_INC @@ -957,46 +954,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyOpcode_OpName[frame->instr_ptr->op.code]); } #endif - assert (next_uop[-1].format == UOP_FORMAT_JUMP); + assert(next_uop[-1].format == UOP_FORMAT_JUMP); uint16_t target = uop_get_error_target(&next_uop[-1]); next_uop = current_executor->trace + target; goto tier2_dispatch; -error_tier_two: - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - assert(next_uop[-1].format == UOP_FORMAT_TARGET); - frame->return_offset = 0; // Don't leave this random - Py_DECREF(current_executor); - tstate->previous_executor = NULL; - next_instr = frame->instr_ptr; - goto error; - jump_to_jump_target: assert(next_uop[-1].format == UOP_FORMAT_JUMP); target = uop_get_jump_target(&next_uop[-1]); next_uop = current_executor->trace + target; goto tier2_dispatch; -exit_to_tier1_dynamic: - next_instr = frame->instr_ptr; - goto goto_to_tier1; -exit_to_tier1: - assert(next_uop[-1].format == UOP_FORMAT_TARGET); - next_instr = next_uop[-1].target + _PyFrame_GetBytecode(frame); -goto_to_tier1: -#ifdef Py_DEBUG - if (frame->lltrace >= 2) { - printf("DEOPT: [UOp "); - _PyUOpPrint(&next_uop[-1]); - printf(" -> %s]\n", - _PyOpcode_OpName[next_instr->op.code]); - } -#endif - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - Py_DECREF(current_executor); - tstate->previous_executor = NULL; - DISPATCH(); - #endif // _Py_JIT #endif // _Py_TIER2 diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index fde8e5efa983b2..0a4f65feb3b512 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -161,9 +161,6 @@ do { \ JUMP_TO_LABEL(start_frame); \ } while (0) -// Use this instead of 'goto error' so Tier 2 can go to a different label -#define GOTO_ERROR(LABEL) JUMP_TO_LABEL(LABEL) - /* Tuple access macros */ #ifndef Py_DEBUG @@ -387,17 +384,19 @@ _PyFrame_SetStackPointer(frame, stack_pointer) #define GOTO_TIER_TWO(EXECUTOR) \ do { \ OPT_STAT_INC(traces_executed); \ - jit_func jitted = (EXECUTOR)->jit_code; \ + _PyExecutorObject *_executor = (EXECUTOR); \ + jit_func jitted = _executor->jit_code; \ + /* Keep the shim frame alive via the executor: */ \ + Py_INCREF(_executor); \ next_instr = jitted(frame, stack_pointer, tstate); \ - Py_DECREF(tstate->previous_executor); \ - tstate->previous_executor = NULL; \ + Py_DECREF(_executor); \ + Py_CLEAR(tstate->previous_executor); \ frame = tstate->current_frame; \ + stack_pointer = _PyFrame_GetStackPointer(frame); \ if (next_instr == NULL) { \ next_instr = frame->instr_ptr; \ - stack_pointer = _PyFrame_GetStackPointer(frame); \ goto error; \ } \ - stack_pointer = _PyFrame_GetStackPointer(frame); \ DISPATCH(); \ } while (0) #else @@ -410,24 +409,25 @@ do { \ } while (0) #endif -#define GOTO_TIER_ONE(TARGET) \ -do { \ - Py_DECREF(tstate->previous_executor); \ - tstate->previous_executor = NULL; \ - next_instr = target; \ - DISPATCH(); \ +#define GOTO_TIER_ONE(TARGET) \ +do { \ + next_instr = (TARGET); \ + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \ + Py_CLEAR(tstate->previous_executor); \ + if (next_instr == NULL) { \ + next_instr = frame->instr_ptr; \ + goto error; \ + } \ + DISPATCH(); \ } while (0) -#define CURRENT_OPARG() (next_uop[-1].oparg) - +#define CURRENT_OPARG() (next_uop[-1].oparg) #define CURRENT_OPERAND0() (next_uop[-1].operand0) #define CURRENT_OPERAND1() (next_uop[-1].operand1) +#define CURRENT_TARGET() (next_uop[-1].target) #define JUMP_TO_JUMP_TARGET() goto jump_to_jump_target #define JUMP_TO_ERROR() goto jump_to_error_target -#define GOTO_UNWIND() goto error_tier_two -#define EXIT_TO_TIER1() goto exit_to_tier1 -#define EXIT_TO_TIER1_DYNAMIC() goto exit_to_tier1_dynamic; /* Stackref macros */ diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 22d11059fcadb8..2e7f5ecc7aa4da 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4369,6 +4369,8 @@ callable = &stack_pointer[-2 - oparg]; func = &stack_pointer[-2 - oparg]; maybe_self = &stack_pointer[-1 - oparg]; + args = &stack_pointer[-oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -4936,7 +4938,9 @@ callable = &stack_pointer[-2 - oparg]; init = &stack_pointer[-2 - oparg]; self = &stack_pointer[-1 - oparg]; + args = &stack_pointer[-oparg]; uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); + (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (!PyStackRef_IsNull(null[0])) { UOP_STAT_INC(uopcode, miss); @@ -4989,9 +4993,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); - stack_pointer = _PyFrame_GetStackPointer(frame); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5317,7 +5321,7 @@ PyObject *res_o = PyLong_FromSsize_t(len_i); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); if (res_o == NULL) { - GOTO_ERROR(error); + JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg_stackref); @@ -5723,6 +5727,8 @@ callable = &stack_pointer[-3 - oparg]; func = &stack_pointer[-3 - oparg]; maybe_self = &stack_pointer[-2 - oparg]; + args = &stack_pointer[-1 - oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -6339,16 +6345,14 @@ PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); _PyExitData *exit = (_PyExitData *)exit_p; PyCodeObject *code = _PyFrame_GetCode(frame); - _PyFrame_SetStackPointer(frame, stack_pointer); _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; - stack_pointer = _PyFrame_GetStackPointer(frame); #if defined(Py_DEBUG) && !defined(_Py_JIT) OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); if (frame->lltrace >= 2) { _PyFrame_SetStackPointer(frame, stack_pointer); printf("SIDE EXIT: [UOp "); _PyUOpPrint(&next_uop[-1]); - printf(", exit %u, temp %d, target %d -> %s]\n", + printf(", exit %lu, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); @@ -6361,11 +6365,11 @@ Py_CLEAR(exit->executor); stack_pointer = _PyFrame_GetStackPointer(frame); } + tstate->previous_executor = (PyObject *)current_executor; if (exit->executor == NULL) { _Py_BackoffCounter temperature = exit->temperature; if (!backoff_counter_triggers(temperature)) { exit->temperature = advance_backoff_counter(temperature); - tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_ONE(target); } _PyExecutorObject *executor; @@ -6380,20 +6384,13 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { exit->temperature = restart_backoff_counter(temperature); - if (optimized < 0) { - GOTO_UNWIND(); - } - tstate->previous_executor = (PyObject *)current_executor; - GOTO_TIER_ONE(target); - } - else { - exit->temperature = initial_temperature_backoff_counter(); + GOTO_TIER_ONE(optimized < 0 ? NULL : target); } + exit->temperature = initial_temperature_backoff_counter(); } exit->executor = executor; } Py_INCREF(exit->executor); - tstate->previous_executor = (PyObject *)current_executor; GOTO_TIER_TWO(exit->executor); break; } @@ -6524,7 +6521,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); printf("DYNAMIC EXIT: [UOp "); _PyUOpPrint(&next_uop[-1]); - printf(", exit %u, temp %d, target %d -> %s]\n", + printf(", exit %lu, temp %d, target %d -> %s]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, (int)(target - _PyFrame_GetBytecode(frame)), _PyOpcode_OpName[target->op.code]); @@ -6547,14 +6544,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (optimized <= 0) { exit->temperature = restart_backoff_counter(exit->temperature); - if (optimized < 0) { - GOTO_UNWIND(); - } - GOTO_TIER_ONE(target); - } - else { - exit->temperature = initial_temperature_backoff_counter(); + GOTO_TIER_ONE(optimized < 0 ? NULL : target); } + exit->temperature = initial_temperature_backoff_counter(); } GOTO_TIER_TWO(executor); break; @@ -6563,9 +6555,8 @@ case _START_EXECUTOR: { PyObject *executor = (PyObject *)CURRENT_OPERAND0(); _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(tstate->previous_executor); + Py_CLEAR(tstate->previous_executor); stack_pointer = _PyFrame_GetStackPointer(frame); - tstate->previous_executor = NULL; #ifndef _Py_JIT current_executor = (_PyExecutorObject*)executor; #endif @@ -6599,18 +6590,18 @@ } case _DEOPT: { - EXIT_TO_TIER1(); + tstate->previous_executor = (PyObject *)current_executor; + GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET()); break; } case _ERROR_POP_N: { oparg = CURRENT_OPARG(); uint32_t target = (uint32_t)CURRENT_OPERAND0(); + tstate->previous_executor = (PyObject *)current_executor; assert(oparg == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; - stack_pointer = _PyFrame_GetStackPointer(frame); - GOTO_UNWIND(); + GOTO_TIER_ONE(NULL); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ec9b287541f016..914b06987ed64e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1278,6 +1278,8 @@ args = &stack_pointer[-oparg]; func = &stack_pointer[-2 - oparg]; maybe_self = &stack_pointer[-1 - oparg]; + args = &stack_pointer[-oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -1440,7 +1442,9 @@ callable = &stack_pointer[-2 - oparg]; init = &stack_pointer[-2 - oparg]; self = &stack_pointer[-1 - oparg]; + args = &stack_pointer[-oparg]; uint32_t type_version = read_u32(&this_instr[2].cache); + (void)args; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); if (!PyStackRef_IsNull(null[0])) { UPDATE_MISS_STATS(CALL); @@ -1491,9 +1495,9 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); - stack_pointer = _PyFrame_GetStackPointer(frame); /* Push self onto stack of shim */ shim->localsplus[0] = PyStackRef_DUP(self[0]); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2532,6 +2536,8 @@ args = &stack_pointer[-1 - oparg]; func = &stack_pointer[-3 - oparg]; maybe_self = &stack_pointer[-2 - oparg]; + args = &stack_pointer[-1 - oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -3058,7 +3064,7 @@ PyObject *res_o = PyLong_FromSsize_t(len_i); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); if (res_o == NULL) { - GOTO_ERROR(error); + JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(arg_stackref); @@ -5679,6 +5685,8 @@ callable = &stack_pointer[-2 - oparg]; func = &stack_pointer[-2 - oparg]; maybe_self = &stack_pointer[-1 - oparg]; + args = &stack_pointer[-oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -6082,6 +6090,8 @@ kwnames_in = stack_pointer[-1]; func = &stack_pointer[-3 - oparg]; maybe_self = &stack_pointer[-2 - oparg]; + args = &stack_pointer[-1 - oparg]; + (void)args; if (PyStackRef_TYPE(callable[0]) == &PyMethod_Type && PyStackRef_IsNull(self_or_null[0])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); PyObject *self = ((PyMethodObject *)callable_o)->im_self; @@ -6397,9 +6407,7 @@ int original_opcode = 0; if (tstate->tracing) { PyCodeObject *code = _PyFrame_GetCode(frame); - _PyFrame_SetStackPointer(frame, stack_pointer); int index = (int)(this_instr - _PyFrame_GetBytecode(frame)); - stack_pointer = _PyFrame_GetStackPointer(frame); original_opcode = code->_co_monitoring->lines->data[index*code->_co_monitoring->lines->bytes_per_entry]; next_instr = this_instr; } else { @@ -6672,9 +6680,7 @@ if (bytecode == NULL) { JUMP_TO_LABEL(error); } - _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); - stack_pointer = _PyFrame_GetStackPointer(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; // Make sure this_instr gets reset correctley for any uops that @@ -9492,11 +9498,7 @@ if (oparg) { PyObject *lasti = PyStackRef_AsPyObjectBorrow(values[0]); if (PyLong_Check(lasti)) { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyLong_AsLong(lasti); - stack_pointer = _PyFrame_GetStackPointer(frame); assert(!_PyErr_Occurred(tstate)); } else { @@ -9508,8 +9510,6 @@ stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_LABEL(error); } - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); } assert(exc && PyExceptionInstance_Check(exc)); stack_pointer += -1; @@ -9558,9 +9558,7 @@ if (bytecode == NULL) { JUMP_TO_LABEL(error); } - _PyFrame_SetStackPointer(frame, stack_pointer); ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); - stack_pointer = _PyFrame_GetStackPointer(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; // Make sure this_instr gets reset correctley for any uops that diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 91573e82841cc9..09f1915bb3a5e0 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -385,9 +385,6 @@ dummy_func(void) { } op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) { - (void)container; - (void)sub; - (void)getitem; new_frame = NULL; ctx->done = true; } @@ -434,8 +431,6 @@ dummy_func(void) { } op(_COMPARE_OP, (left, right -- res)) { - (void)left; - (void)right; if (oparg & 16) { res = sym_new_type(ctx, &PyBool_Type); } @@ -445,32 +440,22 @@ dummy_func(void) { } op(_COMPARE_OP_INT, (left, right -- res)) { - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); } op(_COMPARE_OP_FLOAT, (left, right -- res)) { - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); } op(_COMPARE_OP_STR, (left, right -- res)) { - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); } op(_IS_OP, (left, right -- res)) { - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); } op(_CONTAINS_OP, (left, right -- res)) { - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); } @@ -522,7 +507,6 @@ dummy_func(void) { op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { attr = sym_new_not_null(ctx); (void)offset; - (void)owner; } op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys)) { @@ -583,26 +567,21 @@ dummy_func(void) { op(_CHECK_ATTR_WITH_HINT, (owner -- owner, dict)) { dict = sym_new_not_null(ctx); - (void)owner; } op(_LOAD_ATTR_WITH_HINT, (hint/1, owner, dict -- attr)) { attr = sym_new_not_null(ctx); (void)hint; - (void)owner; - (void)dict; } op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) { attr = sym_new_not_null(ctx); (void)index; - (void)owner; } op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) { attr = sym_new_not_null(ctx); (void)descr; - (void)owner; } op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) { @@ -625,19 +604,16 @@ dummy_func(void) { op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame: _Py_UOpsAbstractFrame *)) { (void)fget; - (void)owner; new_frame = NULL; ctx->done = true; } op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) { - (void)callable; callable[0] = sym_new_not_null(ctx); self_or_null[0] = sym_new_not_null(ctx); } op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - (void)self_or_null; if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) { assert(PyFunction_Check(sym_get_const(callable))); REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); @@ -657,7 +633,6 @@ dummy_func(void) { } } } - (void)self_or_null; } op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { @@ -667,7 +642,6 @@ dummy_func(void) { op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) { int argcount = oparg; - (void)callable; PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); @@ -695,16 +669,12 @@ dummy_func(void) { } op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, args[oparg] -- func, maybe_self, args[oparg])) { - (void)callable; - (void)self_or_null; (void)args; func = sym_new_not_null(ctx); maybe_self = sym_new_not_null(ctx); } op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) { - (void)(self_or_null); - (void)(callable); PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); co = get_code_with_logging((this_instr + 2)); @@ -717,27 +687,18 @@ dummy_func(void) { } op(_PY_FRAME_KW, (callable, self_or_null, args[oparg], kwnames -- new_frame: _Py_UOpsAbstractFrame *)) { - (void)callable; - (void)self_or_null; - (void)args; - (void)kwnames; new_frame = NULL; ctx->done = true; } op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable, null, args[oparg] -- self, init, args[oparg])) { (void)type_version; - (void)callable; - (void)null; (void)args; self = sym_new_not_null(ctx); init = sym_new_not_null(ctx); } op(_CREATE_INIT_FRAME, (self, init, args[oparg] -- init_frame: _Py_UOpsAbstractFrame *)) { - (void)self; - (void)init; - (void)args; init_frame = NULL; ctx->done = true; } @@ -806,7 +767,7 @@ dummy_func(void) { corresponding_check_stack = this_instr; } - op (_CHECK_STACK_SPACE_OPERAND, ( -- )) { + op (_CHECK_STACK_SPACE_OPERAND, (framesize/2 -- )) { (void)framesize; /* We should never see _CHECK_STACK_SPACE_OPERANDs. * They are only created at the end of this pass. */ @@ -848,7 +809,6 @@ dummy_func(void) { op(_UNPACK_SEQUENCE, (seq -- values[oparg])) { /* This has to be done manually */ - (void)seq; for (int i = 0; i < oparg; i++) { values[i] = sym_new_unknown(ctx); } @@ -856,7 +816,6 @@ dummy_func(void) { op(_UNPACK_EX, (seq -- values[oparg & 0xFF], unused, unused[oparg >> 8])) { /* This has to be done manually */ - (void)seq; int totalargs = (oparg & 0xFF) + (oparg >> 8) + 1; for (int i = 0; i < totalargs; i++) { values[i] = sym_new_unknown(ctx); @@ -865,7 +824,6 @@ dummy_func(void) { op(_ITER_NEXT_RANGE, (iter -- iter, next)) { next = sym_new_type(ctx, &PyLong_Type); - (void)iter; } op(_GUARD_IS_TRUE_POP, (flag -- )) { @@ -917,7 +875,6 @@ dummy_func(void) { } op(_LOAD_SPECIAL, (owner -- attr, self_or_null)) { - (void)owner; attr = sym_new_not_null(ctx); self_or_null = sym_new_unknown(ctx); } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 11f1815a977798..dde41d9407ee33 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -645,16 +645,7 @@ } case _BINARY_SUBSCR_INIT_CALL: { - JitOptSymbol *getitem; - JitOptSymbol *sub; - JitOptSymbol *container; _Py_UOpsAbstractFrame *new_frame; - getitem = stack_pointer[-1]; - sub = stack_pointer[-2]; - container = stack_pointer[-3]; - (void)container; - (void)sub; - (void)getitem; new_frame = NULL; ctx->done = true; stack_pointer[-3] = (JitOptSymbol *)new_frame; @@ -816,12 +807,9 @@ } case _UNPACK_SEQUENCE: { - JitOptSymbol *seq; JitOptSymbol **values; - seq = stack_pointer[-1]; values = &stack_pointer[-1]; /* This has to be done manually */ - (void)seq; for (int i = 0; i < oparg; i++) { values[i] = sym_new_unknown(ctx); } @@ -869,12 +857,9 @@ } case _UNPACK_EX: { - JitOptSymbol *seq; JitOptSymbol **values; - seq = stack_pointer[-1]; values = &stack_pointer[-1]; /* This has to be done manually */ - (void)seq; int totalargs = (oparg & 0xFF) + (oparg >> 8) + 1; for (int i = 0; i < totalargs; i++) { values[i] = sym_new_unknown(ctx); @@ -1178,13 +1163,10 @@ } case _LOAD_ATTR_INSTANCE_VALUE: { - JitOptSymbol *owner; JitOptSymbol *attr; - owner = stack_pointer[-1]; uint16_t offset = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)offset; - (void)owner; stack_pointer[-1] = attr; break; } @@ -1258,11 +1240,8 @@ } case _CHECK_ATTR_WITH_HINT: { - JitOptSymbol *owner; JitOptSymbol *dict; - owner = stack_pointer[-1]; dict = sym_new_not_null(ctx); - (void)owner; stack_pointer[0] = dict; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); @@ -1270,16 +1249,10 @@ } case _LOAD_ATTR_WITH_HINT: { - JitOptSymbol *dict; - JitOptSymbol *owner; JitOptSymbol *attr; - dict = stack_pointer[-1]; - owner = stack_pointer[-2]; uint16_t hint = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)hint; - (void)owner; - (void)dict; stack_pointer[-2] = attr; stack_pointer += -1; assert(WITHIN_STACK_BOUNDS()); @@ -1287,13 +1260,10 @@ } case _LOAD_ATTR_SLOT: { - JitOptSymbol *owner; JitOptSymbol *attr; - owner = stack_pointer[-1]; uint16_t index = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)index; - (void)owner; stack_pointer[-1] = attr; break; } @@ -1303,24 +1273,18 @@ } case _LOAD_ATTR_CLASS: { - JitOptSymbol *owner; JitOptSymbol *attr; - owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand0; attr = sym_new_not_null(ctx); (void)descr; - (void)owner; stack_pointer[-1] = attr; break; } case _LOAD_ATTR_PROPERTY_FRAME: { - JitOptSymbol *owner; _Py_UOpsAbstractFrame *new_frame; - owner = stack_pointer[-1]; PyObject *fget = (PyObject *)this_instr->operand0; (void)fget; - (void)owner; new_frame = NULL; ctx->done = true; stack_pointer[-1] = (JitOptSymbol *)new_frame; @@ -1352,13 +1316,7 @@ } case _COMPARE_OP: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; if (oparg & 16) { res = sym_new_type(ctx, &PyBool_Type); } @@ -1376,13 +1334,7 @@ } case _COMPARE_OP_FLOAT: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); stack_pointer[-2] = res; stack_pointer += -1; @@ -1391,13 +1343,7 @@ } case _COMPARE_OP_INT: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); stack_pointer[-2] = res; stack_pointer += -1; @@ -1406,13 +1352,7 @@ } case _COMPARE_OP_STR: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); stack_pointer[-2] = res; stack_pointer += -1; @@ -1421,13 +1361,7 @@ } case _IS_OP: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); stack_pointer[-2] = res; stack_pointer += -1; @@ -1436,13 +1370,7 @@ } case _CONTAINS_OP: { - JitOptSymbol *right; - JitOptSymbol *left; JitOptSymbol *res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - (void)left; - (void)right; res = sym_new_type(ctx, &PyBool_Type); stack_pointer[-2] = res; stack_pointer += -1; @@ -1635,11 +1563,8 @@ } case _ITER_NEXT_RANGE: { - JitOptSymbol *iter; JitOptSymbol *next; - iter = stack_pointer[-1]; next = sym_new_type(ctx, &PyLong_Type); - (void)iter; stack_pointer[0] = next; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); @@ -1653,11 +1578,8 @@ } case _LOAD_SPECIAL: { - JitOptSymbol *owner; JitOptSymbol *attr; JitOptSymbol *self_or_null; - owner = stack_pointer[-1]; - (void)owner; attr = sym_new_not_null(ctx); self_or_null = sym_new_unknown(ctx); stack_pointer[-1] = attr; @@ -1764,16 +1686,10 @@ case _MAYBE_EXPAND_METHOD: { JitOptSymbol **args; - JitOptSymbol *self_or_null; - JitOptSymbol *callable; JitOptSymbol *func; JitOptSymbol *maybe_self; args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; args = &stack_pointer[-oparg]; - (void)callable; - (void)self_or_null; (void)args; func = sym_new_not_null(ctx); maybe_self = sym_new_not_null(ctx); @@ -1787,17 +1703,11 @@ /* _MONITOR_CALL is not a viable micro-op for tier 2 */ case _PY_FRAME_GENERAL: { - JitOptSymbol *self_or_null; - JitOptSymbol *callable; _Py_UOpsAbstractFrame *new_frame; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - (void)(self_or_null); - (void)(callable); PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); + stack_pointer += -2 - oparg; + assert(WITHIN_STACK_BOUNDS()); co = get_code_with_logging((this_instr + 2)); if (co == NULL) { ctx->done = true; @@ -1811,12 +1721,9 @@ } case _CHECK_FUNCTION_VERSION: { - JitOptSymbol *self_or_null; JitOptSymbol *callable; - self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; uint32_t func_version = (uint32_t)this_instr->operand0; - (void)self_or_null; if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) { assert(PyFunction_Check(sym_get_const(callable))); REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); @@ -1866,7 +1773,6 @@ JitOptSymbol **callable; self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; - (void)callable; callable[0] = sym_new_not_null(ctx); self_or_null[0] = sym_new_not_null(ctx); break; @@ -1896,7 +1802,6 @@ } } } - (void)self_or_null; break; } @@ -1909,13 +1814,10 @@ case _INIT_CALL_PY_EXACT_ARGS: { JitOptSymbol **args; JitOptSymbol *self_or_null; - JitOptSymbol *callable; _Py_UOpsAbstractFrame *new_frame; args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; int argcount = oparg; - (void)callable; PyCodeObject *co = NULL; assert((this_instr + 2)->opcode == _PUSH_FRAME); stack_pointer += -2 - oparg; @@ -2010,18 +1912,12 @@ case _CHECK_AND_ALLOCATE_OBJECT: { JitOptSymbol **args; - JitOptSymbol *null; - JitOptSymbol *callable; JitOptSymbol *self; JitOptSymbol *init; args = &stack_pointer[-oparg]; - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; args = &stack_pointer[-oparg]; uint32_t type_version = (uint32_t)this_instr->operand0; (void)type_version; - (void)callable; - (void)null; (void)args; self = sym_new_not_null(ctx); init = sym_new_not_null(ctx); @@ -2031,16 +1927,7 @@ } case _CREATE_INIT_FRAME: { - JitOptSymbol **args; - JitOptSymbol *init; - JitOptSymbol *self; _Py_UOpsAbstractFrame *init_frame; - args = &stack_pointer[-oparg]; - init = stack_pointer[-1 - oparg]; - self = stack_pointer[-2 - oparg]; - (void)self; - (void)init; - (void)args; init_frame = NULL; ctx->done = true; stack_pointer[-2 - oparg] = (JitOptSymbol *)init_frame; @@ -2174,19 +2061,7 @@ /* _DO_CALL_KW is not a viable micro-op for tier 2 */ case _PY_FRAME_KW: { - JitOptSymbol *kwnames; - JitOptSymbol **args; - JitOptSymbol *self_or_null; - JitOptSymbol *callable; _Py_UOpsAbstractFrame *new_frame; - kwnames = stack_pointer[-1]; - args = &stack_pointer[-1 - oparg]; - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - (void)callable; - (void)self_or_null; - (void)args; - (void)kwnames; new_frame = NULL; ctx->done = true; stack_pointer[-3 - oparg] = (JitOptSymbol *)new_frame; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 57cc587428c2b9..c127d0334d9cf8 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -621,6 +621,7 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_PyErr_Occurred", "_PyEval_FrameClearAndPop", "_PyFloat_FromDouble_ConsumeInputs", + "_PyFrame_GetBytecode", "_PyFrame_GetCode", "_PyFrame_IsIncomplete", "_PyFrame_PushUnchecked", diff --git a/Tools/jit/jit.h b/Tools/jit/jit.h index 47da64cb12bd24..f767ef68127eb7 100644 --- a/Tools/jit/jit.h +++ b/Tools/jit/jit.h @@ -2,3 +2,7 @@ // pointer with __attribute__((preserve_none)), since this attribute may not be // supported by the compiler used to build the rest of the interpreter. typedef jit_func __attribute__((preserve_none)) jit_func_preserve_none; + +#define PATCH_VALUE(TYPE, NAME, ALIAS) \ + PyAPI_DATA(void) ALIAS; \ + TYPE NAME = (TYPE)(uintptr_t)&ALIAS; diff --git a/Tools/jit/shim.c b/Tools/jit/shim.c index f0cffa2f049d26..ebd4e9bc858b73 100644 --- a/Tools/jit/shim.c +++ b/Tools/jit/shim.c @@ -9,16 +9,7 @@ _Py_CODEUNIT * _JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate) { - // This is subtle. The actual trace will return to us once it exits, so we - // need to make sure that we stay alive until then. If our trace side-exits - // into another trace, and this trace is then invalidated, the code for - // *this function* will be freed and we'll crash upon return: - PyAPI_DATA(void) _JIT_EXECUTOR; - PyObject *executor = (PyObject *)(uintptr_t)&_JIT_EXECUTOR; - Py_INCREF(executor); // Note that this is *not* a tail call: - PyAPI_DATA(void) _JIT_CONTINUE; - _Py_CODEUNIT *target = ((jit_func_preserve_none)&_JIT_CONTINUE)(frame, stack_pointer, tstate); - Py_SETREF(tstate->previous_executor, executor); - return target; + PATCH_VALUE(jit_func_preserve_none, call, _JIT_CONTINUE); + return call(frame, stack_pointer, tstate); } diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 6ea04f5640ef05..0b7d077d78ce7d 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -32,28 +32,22 @@ #undef CURRENT_OPERAND1 #define CURRENT_OPERAND1() (_operand1) -#undef ENABLE_SPECIALIZATION -#define ENABLE_SPECIALIZATION (0) - -#undef GOTO_ERROR -#define GOTO_ERROR(LABEL) \ - do { \ - goto LABEL ## _tier_two; \ - } while (0) +#undef CURRENT_TARGET +#define CURRENT_TARGET() (_target) #undef GOTO_TIER_TWO -#define GOTO_TIER_TWO(EXECUTOR) \ -do { \ - OPT_STAT_INC(traces_executed); \ - __attribute__((musttail)) \ - return ((jit_func_preserve_none)((EXECUTOR)->jit_side_entry))(frame, stack_pointer, tstate); \ +#define GOTO_TIER_TWO(EXECUTOR) \ +do { \ + OPT_STAT_INC(traces_executed); \ + jit_func_preserve_none jitted = (EXECUTOR)->jit_side_entry; \ + __attribute__((musttail)) return jitted(frame, stack_pointer, tstate); \ } while (0) #undef GOTO_TIER_ONE -#define GOTO_TIER_ONE(TARGET) \ -do { \ +#define GOTO_TIER_ONE(TARGET) \ +do { \ _PyFrame_SetStackPointer(frame, stack_pointer); \ - return TARGET; \ + return TARGET; \ } while (0) #undef LOAD_IP @@ -61,15 +55,15 @@ do { \ do { \ } while (0) -#define PATCH_VALUE(TYPE, NAME, ALIAS) \ - PyAPI_DATA(void) ALIAS; \ - TYPE NAME = (TYPE)(uintptr_t)&ALIAS; +#undef LLTRACE_RESUME_FRAME +#define LLTRACE_RESUME_FRAME() \ + do { \ + } while (0) -#define PATCH_JUMP(ALIAS) \ -do { \ - PyAPI_DATA(void) ALIAS; \ - __attribute__((musttail)) \ - return ((jit_func_preserve_none)&ALIAS)(frame, stack_pointer, tstate); \ +#define PATCH_JUMP(ALIAS) \ +do { \ + PATCH_VALUE(jit_func_preserve_none, jump, ALIAS); \ + __attribute__((musttail)) return jump(frame, stack_pointer, tstate); \ } while (0) #undef JUMP_TO_JUMP_TARGET @@ -78,14 +72,6 @@ do { \ #undef JUMP_TO_ERROR #define JUMP_TO_ERROR() PATCH_JUMP(_JIT_ERROR_TARGET) -#undef WITHIN_STACK_BOUNDS -#define WITHIN_STACK_BOUNDS() 1 - -#undef LLTRACE_RESUME_FRAME -#define LLTRACE_RESUME_FRAME() \ - do { \ - } while (0) - #define TIER_TWO 2 __attribute__((preserve_none)) _Py_CODEUNIT * @@ -106,16 +92,13 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState PATCH_VALUE(uint32_t, _operand0_hi, _JIT_OPERAND0_HI) PATCH_VALUE(uint32_t, _operand0_lo, _JIT_OPERAND0_LO) uint64_t _operand0 = ((uint64_t)_operand0_hi << 32) | _operand0_lo; - PATCH_VALUE(uint32_t, _operand1_hi, _JIT_OPERAND1_HI) PATCH_VALUE(uint32_t, _operand1_lo, _JIT_OPERAND1_LO) uint64_t _operand1 = ((uint64_t)_operand1_hi << 32) | _operand1_lo; #endif PATCH_VALUE(uint32_t, _target, _JIT_TARGET) - OPT_STAT_INC(uops_executed); UOP_STAT_INC(uopcode, execution_count); - switch (uopcode) { // The actual instruction definition gets inserted here: CASE @@ -123,15 +106,4 @@ _JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState Py_UNREACHABLE(); } PATCH_JUMP(_JIT_CONTINUE); - // Labels that the instruction implementations expect to exist: - -error_tier_two: - tstate->previous_executor = (PyObject *)current_executor; - GOTO_TIER_ONE(NULL); -exit_to_tier1: - tstate->previous_executor = (PyObject *)current_executor; - GOTO_TIER_ONE(_PyCode_CODE(_PyFrame_GetCode(frame)) + _target); -exit_to_tier1_dynamic: - tstate->previous_executor = (PyObject *)current_executor; - GOTO_TIER_ONE(frame->instr_ptr); } From 662e88db642899bcbc28ef142361d5f315a46901 Mon Sep 17 00:00:00 2001 From: Garrett Gu Date: Fri, 7 Feb 2025 11:56:46 -0600 Subject: [PATCH 116/311] Fix Garrett Gu name misspelling in 3.14 whatsnew (GH-129822) Update 3.14.rst to fix typo --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 1e469e8738bfcb..52d6e963a4a835 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -238,7 +238,7 @@ For further information on how to build Python, see __ https://en.wikipedia.org/wiki/Tail_call (Contributed by Ken Jin in :gh:`128718`, with ideas on how to implement this -in CPython by Mark Shannon, Garret Gu, Haoran Xu, and Josh Haberman.) +in CPython by Mark Shannon, Garrett Gu, Haoran Xu, and Josh Haberman.) Other language changes From 49bd47d5f14993d37b97aa2bbf257f5df16b96a9 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 7 Feb 2025 23:30:59 +0530 Subject: [PATCH 117/311] improve `test_log_destroyed_pending_task` in asyncio (#129821) --- Lib/test/test_asyncio/test_tasks.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index cd4a48499e8f11..881a7aeeb2fbe5 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2254,7 +2254,6 @@ def test_wait_invalid_args(self): asyncio.wait([])) def test_log_destroyed_pending_task(self): - Task = self.__class__.Task async def kill_me(loop): future = self.new_future(loop) @@ -2269,7 +2268,7 @@ async def kill_me(loop): # schedule the task coro = kill_me(self.loop) - task = asyncio.ensure_future(coro, loop=self.loop) + task = self.new_task(self.loop, coro) self.assertEqual(self.all_tasks(loop=self.loop), {task}) @@ -2286,14 +2285,17 @@ async def kill_me(loop): # no more reference to kill_me() task: the task is destroyed by the GC support.gc_collect() - self.assertEqual(self.all_tasks(loop=self.loop), set()) - mock_handler.assert_called_with(self.loop, { 'message': 'Task was destroyed but it is pending!', 'task': mock.ANY, 'source_traceback': source_traceback, }) mock_handler.reset_mock() + # task got resurrected by the exception handler + support.gc_collect() + + self.assertEqual(self.all_tasks(loop=self.loop), set()) + @mock.patch('asyncio.base_events.logger') def test_tb_logger_not_called_after_cancel(self, m_log): From 7b2e01bb555359913939d7ff168363f1760d3f8e Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 8 Feb 2025 02:49:28 +0800 Subject: [PATCH 118/311] Remove tail-calling wording as it is confusing (GH-129823) --- Doc/whatsnew/3.14.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 52d6e963a4a835..2bdc3cabcfbc03 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -211,8 +211,8 @@ configuration mechanisms). .. _whatsnew314-tail-call: -A new tail-calling interpreter ------------------------------- +A new type of interpreter +------------------------- A new type of interpreter based on tail calls has been added to CPython. For certain newer compilers, this interpreter provides From e4a00f70b176c3182aad3904f94c69a7fb733cd9 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 8 Feb 2025 03:30:23 +0800 Subject: [PATCH 119/311] Fix link in 3.14 whatsnew (#129828) --- Doc/whatsnew/3.14.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 2bdc3cabcfbc03..6eb256586e71d8 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -68,7 +68,7 @@ Summary -- release highlights * :ref:`PEP 649: deferred evaluation of annotations ` * :ref:`PEP 741: Python Configuration C API ` * :ref:`PEP 761: Discontinuation of PGP signatures ` -* :ref:`A new tail-calling interpreter ` +* :ref:`A new type of interpreter ` New features From 5fa7e1b7fd57e8c6297e9eb79d79cede42e5ce0f Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 7 Feb 2025 11:41:17 -0800 Subject: [PATCH 120/311] GH-129715: Remove _DYNAMIC_EXIT (GH-129716) --- Include/cpython/pystats.h | 1 + Include/internal/pycore_optimizer.h | 3 +- Include/internal/pycore_uop_ids.h | 247 +++++++++--------- Include/internal/pycore_uop_metadata.h | 4 - Lib/test/test_capi/test_opt.py | 1 + ...-02-05-22-58-18.gh-issue-129715.mLlpIO.rst | 1 + Python/bytecodes.c | 36 --- Python/executor_cases.c.h | 42 --- Python/optimizer.c | 18 +- Python/optimizer_analysis.c | 1 - Python/optimizer_cases.c.h | 4 - Python/specialize.c | 1 + Tools/scripts/summarize_stats.py | 5 + 13 files changed, 138 insertions(+), 226 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-22-58-18.gh-issue-129715.mLlpIO.rst diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h index f52348e42b1330..4421b4d6e91dad 100644 --- a/Include/cpython/pystats.h +++ b/Include/cpython/pystats.h @@ -129,6 +129,7 @@ typedef struct _optimization_stats { uint64_t inner_loop; uint64_t recursive_call; uint64_t low_confidence; + uint64_t unknown_callee; uint64_t executors_invalidated; UOpStats opcode[PYSTATS_MAX_UOP_ID + 1]; uint64_t unsupported_opcode[256]; diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 00fc4338b0a412..25c3d3e5a22442 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -289,8 +289,7 @@ static inline int is_terminator(const _PyUOpInstruction *uop) int opcode = uop->opcode; return ( opcode == _EXIT_TRACE || - opcode == _JUMP_TO_TOP || - opcode == _DYNAMIC_EXIT + opcode == _JUMP_TO_TOP ); } diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index ca40af55406089..554e3439e0b1da 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -101,57 +101,56 @@ extern "C" { #define _DO_CALL 358 #define _DO_CALL_FUNCTION_EX 359 #define _DO_CALL_KW 360 -#define _DYNAMIC_EXIT 361 #define _END_FOR END_FOR #define _END_SEND END_SEND -#define _ERROR_POP_N 362 +#define _ERROR_POP_N 361 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _EXPAND_METHOD 363 -#define _EXPAND_METHOD_KW 364 -#define _FATAL_ERROR 365 +#define _EXPAND_METHOD 362 +#define _EXPAND_METHOD_KW 363 +#define _FATAL_ERROR 364 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 366 -#define _FOR_ITER_GEN_FRAME 367 -#define _FOR_ITER_TIER_TWO 368 +#define _FOR_ITER 365 +#define _FOR_ITER_GEN_FRAME 366 +#define _FOR_ITER_TIER_TWO 367 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BINARY_OP_EXTEND 369 -#define _GUARD_BOTH_FLOAT 370 -#define _GUARD_BOTH_INT 371 -#define _GUARD_BOTH_UNICODE 372 -#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 373 -#define _GUARD_DORV_NO_DICT 374 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 375 -#define _GUARD_GLOBALS_VERSION 376 -#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 377 -#define _GUARD_IS_FALSE_POP 378 -#define _GUARD_IS_NONE_POP 379 -#define _GUARD_IS_NOT_NONE_POP 380 -#define _GUARD_IS_TRUE_POP 381 -#define _GUARD_KEYS_VERSION 382 -#define _GUARD_NOS_FLOAT 383 -#define _GUARD_NOS_INT 384 -#define _GUARD_NOT_EXHAUSTED_LIST 385 -#define _GUARD_NOT_EXHAUSTED_RANGE 386 -#define _GUARD_NOT_EXHAUSTED_TUPLE 387 -#define _GUARD_TOS_FLOAT 388 -#define _GUARD_TOS_INT 389 -#define _GUARD_TYPE_VERSION 390 -#define _GUARD_TYPE_VERSION_AND_LOCK 391 +#define _GUARD_BINARY_OP_EXTEND 368 +#define _GUARD_BOTH_FLOAT 369 +#define _GUARD_BOTH_INT 370 +#define _GUARD_BOTH_UNICODE 371 +#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 372 +#define _GUARD_DORV_NO_DICT 373 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 374 +#define _GUARD_GLOBALS_VERSION 375 +#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 376 +#define _GUARD_IS_FALSE_POP 377 +#define _GUARD_IS_NONE_POP 378 +#define _GUARD_IS_NOT_NONE_POP 379 +#define _GUARD_IS_TRUE_POP 380 +#define _GUARD_KEYS_VERSION 381 +#define _GUARD_NOS_FLOAT 382 +#define _GUARD_NOS_INT 383 +#define _GUARD_NOT_EXHAUSTED_LIST 384 +#define _GUARD_NOT_EXHAUSTED_RANGE 385 +#define _GUARD_NOT_EXHAUSTED_TUPLE 386 +#define _GUARD_TOS_FLOAT 387 +#define _GUARD_TOS_INT 388 +#define _GUARD_TYPE_VERSION 389 +#define _GUARD_TYPE_VERSION_AND_LOCK 390 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 392 -#define _INIT_CALL_PY_EXACT_ARGS 393 -#define _INIT_CALL_PY_EXACT_ARGS_0 394 -#define _INIT_CALL_PY_EXACT_ARGS_1 395 -#define _INIT_CALL_PY_EXACT_ARGS_2 396 -#define _INIT_CALL_PY_EXACT_ARGS_3 397 -#define _INIT_CALL_PY_EXACT_ARGS_4 398 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 391 +#define _INIT_CALL_PY_EXACT_ARGS 392 +#define _INIT_CALL_PY_EXACT_ARGS_0 393 +#define _INIT_CALL_PY_EXACT_ARGS_1 394 +#define _INIT_CALL_PY_EXACT_ARGS_2 395 +#define _INIT_CALL_PY_EXACT_ARGS_3 396 +#define _INIT_CALL_PY_EXACT_ARGS_4 397 #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER #define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD @@ -161,137 +160,137 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _IS_NONE 399 +#define _IS_NONE 398 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 400 -#define _ITER_CHECK_RANGE 401 -#define _ITER_CHECK_TUPLE 402 -#define _ITER_JUMP_LIST 403 -#define _ITER_JUMP_RANGE 404 -#define _ITER_JUMP_TUPLE 405 -#define _ITER_NEXT_LIST 406 -#define _ITER_NEXT_RANGE 407 -#define _ITER_NEXT_TUPLE 408 -#define _JUMP_TO_TOP 409 +#define _ITER_CHECK_LIST 399 +#define _ITER_CHECK_RANGE 400 +#define _ITER_CHECK_TUPLE 401 +#define _ITER_JUMP_LIST 402 +#define _ITER_JUMP_RANGE 403 +#define _ITER_JUMP_TUPLE 404 +#define _ITER_NEXT_LIST 405 +#define _ITER_NEXT_RANGE 406 +#define _ITER_NEXT_TUPLE 407 +#define _JUMP_TO_TOP 408 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 410 -#define _LOAD_ATTR_CLASS 411 +#define _LOAD_ATTR 409 +#define _LOAD_ATTR_CLASS 410 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 412 -#define _LOAD_ATTR_METHOD_LAZY_DICT 413 -#define _LOAD_ATTR_METHOD_NO_DICT 414 -#define _LOAD_ATTR_METHOD_WITH_VALUES 415 -#define _LOAD_ATTR_MODULE 416 -#define _LOAD_ATTR_MODULE_FROM_KEYS 417 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 418 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 419 -#define _LOAD_ATTR_PROPERTY_FRAME 420 -#define _LOAD_ATTR_SLOT 421 -#define _LOAD_ATTR_WITH_HINT 422 +#define _LOAD_ATTR_INSTANCE_VALUE 411 +#define _LOAD_ATTR_METHOD_LAZY_DICT 412 +#define _LOAD_ATTR_METHOD_NO_DICT 413 +#define _LOAD_ATTR_METHOD_WITH_VALUES 414 +#define _LOAD_ATTR_MODULE 415 +#define _LOAD_ATTR_MODULE_FROM_KEYS 416 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 417 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 418 +#define _LOAD_ATTR_PROPERTY_FRAME 419 +#define _LOAD_ATTR_SLOT 420 +#define _LOAD_ATTR_WITH_HINT 421 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 423 +#define _LOAD_BYTECODE 422 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 424 -#define _LOAD_CONST_INLINE_BORROW 425 +#define _LOAD_CONST_INLINE 423 +#define _LOAD_CONST_INLINE_BORROW 424 #define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 426 -#define _LOAD_FAST_0 427 -#define _LOAD_FAST_1 428 -#define _LOAD_FAST_2 429 -#define _LOAD_FAST_3 430 -#define _LOAD_FAST_4 431 -#define _LOAD_FAST_5 432 -#define _LOAD_FAST_6 433 -#define _LOAD_FAST_7 434 +#define _LOAD_FAST 425 +#define _LOAD_FAST_0 426 +#define _LOAD_FAST_1 427 +#define _LOAD_FAST_2 428 +#define _LOAD_FAST_3 429 +#define _LOAD_FAST_4 430 +#define _LOAD_FAST_5 431 +#define _LOAD_FAST_6 432 +#define _LOAD_FAST_7 433 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 435 -#define _LOAD_GLOBAL_BUILTINS 436 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 437 -#define _LOAD_GLOBAL_MODULE 438 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 439 +#define _LOAD_GLOBAL 434 +#define _LOAD_GLOBAL_BUILTINS 435 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 436 +#define _LOAD_GLOBAL_MODULE 437 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 438 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 440 -#define _LOAD_SMALL_INT_0 441 -#define _LOAD_SMALL_INT_1 442 -#define _LOAD_SMALL_INT_2 443 -#define _LOAD_SMALL_INT_3 444 +#define _LOAD_SMALL_INT 439 +#define _LOAD_SMALL_INT_0 440 +#define _LOAD_SMALL_INT_1 441 +#define _LOAD_SMALL_INT_2 442 +#define _LOAD_SMALL_INT_3 443 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 445 +#define _MAKE_CALLARGS_A_TUPLE 444 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 446 +#define _MAKE_WARM 445 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 447 -#define _MAYBE_EXPAND_METHOD_KW 448 -#define _MONITOR_CALL 449 -#define _MONITOR_CALL_KW 450 -#define _MONITOR_JUMP_BACKWARD 451 -#define _MONITOR_RESUME 452 +#define _MAYBE_EXPAND_METHOD 446 +#define _MAYBE_EXPAND_METHOD_KW 447 +#define _MONITOR_CALL 448 +#define _MONITOR_CALL_KW 449 +#define _MONITOR_JUMP_BACKWARD 450 +#define _MONITOR_RESUME 451 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 453 -#define _POP_JUMP_IF_TRUE 454 +#define _POP_JUMP_IF_FALSE 452 +#define _POP_JUMP_IF_TRUE 453 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 455 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 454 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 456 +#define _PUSH_FRAME 455 #define _PUSH_NULL PUSH_NULL -#define _PUSH_NULL_CONDITIONAL 457 -#define _PY_FRAME_GENERAL 458 -#define _PY_FRAME_KW 459 -#define _QUICKEN_RESUME 460 -#define _REPLACE_WITH_TRUE 461 +#define _PUSH_NULL_CONDITIONAL 456 +#define _PY_FRAME_GENERAL 457 +#define _PY_FRAME_KW 458 +#define _QUICKEN_RESUME 459 +#define _REPLACE_WITH_TRUE 460 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 462 -#define _SEND 463 -#define _SEND_GEN_FRAME 464 +#define _SAVE_RETURN_OFFSET 461 +#define _SEND 462 +#define _SEND_GEN_FRAME 463 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 465 -#define _STORE_ATTR 466 -#define _STORE_ATTR_INSTANCE_VALUE 467 -#define _STORE_ATTR_SLOT 468 -#define _STORE_ATTR_WITH_HINT 469 +#define _START_EXECUTOR 464 +#define _STORE_ATTR 465 +#define _STORE_ATTR_INSTANCE_VALUE 466 +#define _STORE_ATTR_SLOT 467 +#define _STORE_ATTR_WITH_HINT 468 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 470 -#define _STORE_FAST_0 471 -#define _STORE_FAST_1 472 -#define _STORE_FAST_2 473 -#define _STORE_FAST_3 474 -#define _STORE_FAST_4 475 -#define _STORE_FAST_5 476 -#define _STORE_FAST_6 477 -#define _STORE_FAST_7 478 +#define _STORE_FAST 469 +#define _STORE_FAST_0 470 +#define _STORE_FAST_1 471 +#define _STORE_FAST_2 472 +#define _STORE_FAST_3 473 +#define _STORE_FAST_4 474 +#define _STORE_FAST_5 475 +#define _STORE_FAST_6 476 +#define _STORE_FAST_7 477 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 479 -#define _STORE_SUBSCR 480 +#define _STORE_SLICE 478 +#define _STORE_SUBSCR 479 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 481 -#define _TO_BOOL 482 +#define _TIER2_RESUME_CHECK 480 +#define _TO_BOOL 481 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -301,13 +300,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 483 +#define _UNPACK_SEQUENCE 482 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 483 +#define MAX_UOP_ID 482 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 2f957afb259b0f..2e126b57aa7dbd 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -281,7 +281,6 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_GLOBAL_MODULE] = HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE] = HAS_DEOPT_FLAG, - [_DYNAMIC_EXIT] = HAS_ESCAPES_FLAG, [_START_EXECUTOR] = HAS_ESCAPES_FLAG, [_MAKE_WARM] = 0, [_FATAL_ERROR] = 0, @@ -386,7 +385,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_DEOPT] = "_DEOPT", [_DICT_MERGE] = "_DICT_MERGE", [_DICT_UPDATE] = "_DICT_UPDATE", - [_DYNAMIC_EXIT] = "_DYNAMIC_EXIT", [_END_FOR] = "_END_FOR", [_END_SEND] = "_END_SEND", [_ERROR_POP_N] = "_ERROR_POP_N", @@ -1097,8 +1095,6 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _LOAD_ATTR_MODULE: return 1; - case _DYNAMIC_EXIT: - return 0; case _START_EXECUTOR: return 0; case _MAKE_WARM: diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 02e534caec1162..2a9b777862c84a 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1160,6 +1160,7 @@ def testfunc(n): self.assertIsNotNone(ex) self.assertIn("_RETURN_GENERATOR", get_opnames(ex)) + @unittest.skip("Tracing into generators currently isn't supported.") def test_for_iter_gen(self): def gen(n): for i in range(n): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-22-58-18.gh-issue-129715.mLlpIO.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-22-58-18.gh-issue-129715.mLlpIO.rst new file mode 100644 index 00000000000000..f8eb7481aea350 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-05-22-58-18.gh-issue-129715.mLlpIO.rst @@ -0,0 +1 @@ +Improve JIT performance for generators. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9fb58b9cdcc022..bef120b64779d9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5117,42 +5117,6 @@ dummy_func( DECREF_INPUTS(); } - tier2 op(_DYNAMIC_EXIT, (exit_p/4 --)) { - tstate->previous_executor = (PyObject *)current_executor; - _PyExitData *exit = (_PyExitData *)exit_p; - _Py_CODEUNIT *target = frame->instr_ptr; - #if defined(Py_DEBUG) && !defined(_Py_JIT) - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - if (frame->lltrace >= 2) { - printf("DYNAMIC EXIT: [UOp "); - _PyUOpPrint(&next_uop[-1]); - printf(", exit %lu, temp %d, target %d -> %s]\n", - exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyFrame_GetBytecode(frame)), - _PyOpcode_OpName[target->op.code]); - } - #endif - _PyExecutorObject *executor; - if (target->op.code == ENTER_EXECUTOR) { - PyCodeObject *code = _PyFrame_GetCode(frame); - executor = code->co_executors->executors[target->op.arg]; - Py_INCREF(executor); - } - else { - if (!backoff_counter_triggers(exit->temperature)) { - exit->temperature = advance_backoff_counter(exit->temperature); - GOTO_TIER_ONE(target); - } - int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0); - if (optimized <= 0) { - exit->temperature = restart_backoff_counter(exit->temperature); - GOTO_TIER_ONE(optimized < 0 ? NULL : target); - } - exit->temperature = initial_temperature_backoff_counter(); - } - GOTO_TIER_TWO(executor); - } - tier2 op(_START_EXECUTOR, (executor/4 --)) { Py_CLEAR(tstate->previous_executor); #ifndef _Py_JIT diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 2e7f5ecc7aa4da..dc6572813212aa 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -6510,48 +6510,6 @@ break; } - case _DYNAMIC_EXIT: { - PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); - tstate->previous_executor = (PyObject *)current_executor; - _PyExitData *exit = (_PyExitData *)exit_p; - _Py_CODEUNIT *target = frame->instr_ptr; - #if defined(Py_DEBUG) && !defined(_Py_JIT) - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - if (frame->lltrace >= 2) { - _PyFrame_SetStackPointer(frame, stack_pointer); - printf("DYNAMIC EXIT: [UOp "); - _PyUOpPrint(&next_uop[-1]); - printf(", exit %lu, temp %d, target %d -> %s]\n", - exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyFrame_GetBytecode(frame)), - _PyOpcode_OpName[target->op.code]); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - #endif - _PyExecutorObject *executor; - if (target->op.code == ENTER_EXECUTOR) { - PyCodeObject *code = _PyFrame_GetCode(frame); - executor = code->co_executors->executors[target->op.arg]; - Py_INCREF(executor); - } - else { - if (!backoff_counter_triggers(exit->temperature)) { - exit->temperature = advance_backoff_counter(exit->temperature); - GOTO_TIER_ONE(target); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, target, &executor, 0); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (optimized <= 0) { - exit->temperature = restart_backoff_counter(exit->temperature); - GOTO_TIER_ONE(optimized < 0 ? NULL : target); - } - exit->temperature = initial_temperature_backoff_counter(); - } - GOTO_TIER_TWO(executor); - break; - } - case _START_EXECUTOR: { PyObject *executor = (PyObject *)CURRENT_OPERAND0(); _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/optimizer.c b/Python/optimizer.c index 97831f58098c95..340770ae55ec57 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -757,9 +757,8 @@ translate_bytecode_to_trace( opcode == SEND_GEN) { DPRINTF(2, "Bailing due to dynamic target\n"); - ADD_TO_TRACE(uop, oparg, 0, target); - ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0); - goto done; + OPT_STAT_INC(unknown_callee); + return 0; } assert(_PyOpcode_Deopt[opcode] == CALL || _PyOpcode_Deopt[opcode] == CALL_KW); int func_version_offset = @@ -825,9 +824,8 @@ translate_bytecode_to_trace( goto top; } DPRINTF(2, "Bail, new_code == NULL\n"); - ADD_TO_TRACE(uop, oparg, 0, target); - ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0); - goto done; + OPT_STAT_INC(unknown_callee); + return 0; } if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) { @@ -914,7 +912,7 @@ count_exits(_PyUOpInstruction *buffer, int length) int exit_count = 0; for (int i = 0; i < length; i++) { int opcode = buffer[i].opcode; - if (opcode == _EXIT_TRACE || opcode == _DYNAMIC_EXIT) { + if (opcode == _EXIT_TRACE) { exit_count++; } } @@ -1119,12 +1117,6 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil dest->operand0 = (uint64_t)exit; next_exit--; } - if (opcode == _DYNAMIC_EXIT) { - _PyExitData *exit = &executor->exits[next_exit]; - exit->target = 0; - dest->operand0 = (uint64_t)exit; - next_exit--; - } } assert(next_exit == -1); assert(dest == executor->trace); diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 5dd7725f398cd1..6c0aadb87e6741 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -616,7 +616,6 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) } case _JUMP_TO_TOP: case _EXIT_TRACE: - case _DYNAMIC_EXIT: return pc + 1; default: { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index dde41d9407ee33..2383be8ea3086e 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2469,10 +2469,6 @@ break; } - case _DYNAMIC_EXIT: { - break; - } - case _START_EXECUTOR: { break; } diff --git a/Python/specialize.c b/Python/specialize.c index 8831cfaa82be9b..4f84b2970ba98a 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -260,6 +260,7 @@ print_optimization_stats(FILE *out, OptimizationStats *stats) fprintf(out, "Optimization inner loop: %" PRIu64 "\n", stats->inner_loop); fprintf(out, "Optimization recursive call: %" PRIu64 "\n", stats->recursive_call); fprintf(out, "Optimization low confidence: %" PRIu64 "\n", stats->low_confidence); + fprintf(out, "Optimization unknown callee: %" PRIu64 "\n", stats->unknown_callee); fprintf(out, "Executors invalidated: %" PRIu64 "\n", stats->executors_invalidated); print_histogram(out, "Trace length", stats->trace_length_hist); diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 161af09183a282..4243b53850b3de 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -457,6 +457,7 @@ def get_optimization_stats(self) -> dict[str, tuple[int, int | None]]: inner_loop = self._data["Optimization inner loop"] recursive_call = self._data["Optimization recursive call"] low_confidence = self._data["Optimization low confidence"] + unknown_callee = self._data["Optimization unknown callee"] executors_invalidated = self._data["Executors invalidated"] return { @@ -497,6 +498,10 @@ def get_optimization_stats(self) -> dict[str, tuple[int, int | None]]: "A trace is abandoned because the likelihood of the jump to top being taken " "is too low.", ): (low_confidence, attempts), + Doc( + "Unknown callee", + "A trace is abandoned because the target of a call is unknown.", + ): (unknown_callee, attempts), Doc( "Executors invalidated", "The number of executors that were invalidated due to watched " From 2248a9c153092b920ff68b0eee009c04dbe19f61 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 7 Feb 2025 17:35:59 -0500 Subject: [PATCH 121/311] gh-129825: Skip test_faulthandler.test_register_chain under TSAN (gh-129827) The test hangs when run under TSAN due to an interaction between TSAN's signal interception and our attempt to call the previous signal handler. --- Lib/test/test_faulthandler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 75d303cd212c82..bcebaef0a5101a 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -795,6 +795,7 @@ def test_register_fd(self): def test_register_threads(self): self.check_register(all_threads=True) + @support.skip_if_sanitizer("gh-129825: hangs under TSAN", thread=True) def test_register_chain(self): self.check_register(chain=True) From a1417b211f0bb9582b00f7b82d0a43a3bcc9ed05 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 7 Feb 2025 22:39:54 +0000 Subject: [PATCH 122/311] gh-100239: replace BINARY_SUBSCR & family by BINARY_OP with oparg NB_SUBSCR (#129700) --- Doc/library/dis.rst | 11 +- Doc/whatsnew/3.14.rst | 6 + Include/internal/pycore_code.h | 3 +- Include/internal/pycore_magic_number.h | 3 +- Include/internal/pycore_opcode_metadata.h | 122 ++-- Include/internal/pycore_uop_ids.h | 355 ++++++----- Include/internal/pycore_uop_metadata.h | 40 +- Include/opcode.h | 3 +- Include/opcode_ids.h | 243 ++++---- Lib/_opcode_metadata.py | 255 ++++---- Lib/test/test__opcode.py | 2 +- Lib/test/test_compile.py | 18 +- Lib/test/test_dis.py | 372 +++++------ Lib/test/test_opcache.py | 16 +- Lib/test/test_peepholer.py | 15 +- ...-02-07-17-06-39.gh-issue-100239.WvBTPL.rst | 1 + Modules/_opcode.c | 1 + Programs/test_frozenmain.h | 29 +- Python/bytecodes.c | 72 +-- Python/ceval.c | 1 + Python/codegen.c | 21 +- Python/executor_cases.c.h | 51 +- Python/flowgraph.c | 13 +- Python/generated_cases.c.h | 583 ++++++++---------- Python/opcode_targets.h | 39 +- Python/optimizer.c | 2 +- Python/optimizer_bytecodes.c | 2 +- Python/optimizer_cases.c.h | 21 +- Python/specialize.c | 298 ++++----- Tools/c-analyzer/cpython/ignored.tsv | 4 +- 30 files changed, 1218 insertions(+), 1384 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-17-06-39.gh-issue-100239.WvBTPL.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index f8f4188d27b472..d914acbbc67076 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -703,15 +703,8 @@ not have to be) the original ``STACK[-2]``. STACK.append(lhs op rhs) .. versionadded:: 3.11 - - -.. opcode:: BINARY_SUBSCR - - Implements:: - - key = STACK.pop() - container = STACK.pop() - STACK.append(container[key]) + .. versionchanged:: 3.14 + With oparg :``NB_SUBSCR``, implements binary subscript (replaces opcode ``BINARY_SUBSCR``) .. opcode:: STORE_SUBSCR diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 6eb256586e71d8..5cef8999944005 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1199,6 +1199,12 @@ Others :meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.) +CPython Bytecode Changes +======================== + +* Replaced the opcode ``BINARY_SUBSCR`` by :opcode:`BINARY_OP` with oparg ``NB_SUBSCR``. + (Contributed by Irit Katriel in :gh:`100239`.) + Porting to Python 3.14 ====================== diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 65c3d142458577..6d45d5f0c4071f 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -338,8 +338,6 @@ extern void _Py_Specialize_StoreAttr(_PyStackRef owner, _Py_CODEUNIT *instr, PyObject *name); extern void _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name); -extern void _Py_Specialize_BinarySubscr(_PyStackRef sub, _PyStackRef container, - _Py_CODEUNIT *instr); extern void _Py_Specialize_StoreSubscr(_PyStackRef container, _PyStackRef sub, _Py_CODEUNIT *instr); extern void _Py_Specialize_Call(_PyStackRef callable, _Py_CODEUNIT *instr, @@ -586,6 +584,7 @@ typedef int (*binaryopguardfunc)(PyObject *lhs, PyObject *rhs); typedef PyObject *(*binaryopactionfunc)(PyObject *lhs, PyObject *rhs); typedef struct { + int oparg; binaryopguardfunc guard; binaryopactionfunc action; } _PyBinaryOpSpecializationDescr; diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 5a0b6dae8a5ad6..4803213e84bd17 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -268,6 +268,7 @@ Known values: Python 3.14a4 3613 (Add LOAD_CONST_MORTAL instruction) Python 3.14a5 3614 (Add BINARY_OP_EXTEND) Python 3.14a5 3615 (CALL_FUNCTION_EX always take a kwargs argument) + Python 3.14a5 3616 (Remove BINARY_SUBSCR and family. Make them BINARY_OPs) Python 3.15 will start with 3650 @@ -280,7 +281,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3615 +#define PYC_MAGIC_NUMBER 3616 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index beb0baa7bb69a6..24c698adb31562 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -51,24 +51,22 @@ int _PyOpcode_num_popped(int opcode, int oparg) { return 2; case BINARY_OP_MULTIPLY_INT: return 2; - case BINARY_OP_SUBTRACT_FLOAT: - return 2; - case BINARY_OP_SUBTRACT_INT: + case BINARY_OP_SUBSCR_DICT: return 2; - case BINARY_SLICE: - return 3; - case BINARY_SUBSCR: + case BINARY_OP_SUBSCR_GETITEM: return 2; - case BINARY_SUBSCR_DICT: + case BINARY_OP_SUBSCR_LIST_INT: return 2; - case BINARY_SUBSCR_GETITEM: + case BINARY_OP_SUBSCR_STR_INT: return 2; - case BINARY_SUBSCR_LIST_INT: + case BINARY_OP_SUBSCR_TUPLE_INT: return 2; - case BINARY_SUBSCR_STR_INT: + case BINARY_OP_SUBTRACT_FLOAT: return 2; - case BINARY_SUBSCR_TUPLE_INT: + case BINARY_OP_SUBTRACT_INT: return 2; + case BINARY_SLICE: + return 3; case BUILD_LIST: return oparg; case BUILD_MAP: @@ -526,23 +524,21 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { return 1; case BINARY_OP_MULTIPLY_INT: return 1; - case BINARY_OP_SUBTRACT_FLOAT: - return 1; - case BINARY_OP_SUBTRACT_INT: + case BINARY_OP_SUBSCR_DICT: return 1; - case BINARY_SLICE: + case BINARY_OP_SUBSCR_GETITEM: + return 0; + case BINARY_OP_SUBSCR_LIST_INT: return 1; - case BINARY_SUBSCR: + case BINARY_OP_SUBSCR_STR_INT: return 1; - case BINARY_SUBSCR_DICT: + case BINARY_OP_SUBSCR_TUPLE_INT: return 1; - case BINARY_SUBSCR_GETITEM: - return 0; - case BINARY_SUBSCR_LIST_INT: + case BINARY_OP_SUBTRACT_FLOAT: return 1; - case BINARY_SUBSCR_STR_INT: + case BINARY_OP_SUBTRACT_INT: return 1; - case BINARY_SUBSCR_TUPLE_INT: + case BINARY_SLICE: return 1; case BUILD_LIST: return 1; @@ -986,7 +982,7 @@ extern int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect); int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { switch(opcode) { case BINARY_OP: { - *effect = 0; + *effect = 1; return 0; } case BINARY_OP_ADD_FLOAT: { @@ -1017,40 +1013,36 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) { *effect = 0; return 0; } - case BINARY_OP_SUBTRACT_FLOAT: { - *effect = 0; - return 0; - } - case BINARY_OP_SUBTRACT_INT: { - *effect = 0; - return 0; - } - case BINARY_SLICE: { - *effect = 0; + case BINARY_OP_SUBSCR_DICT: { + *effect = -1; return 0; } - case BINARY_SUBSCR: { + case BINARY_OP_SUBSCR_GETITEM: { *effect = 1; return 0; } - case BINARY_SUBSCR_DICT: { + case BINARY_OP_SUBSCR_LIST_INT: { *effect = -1; return 0; } - case BINARY_SUBSCR_GETITEM: { - *effect = 1; + case BINARY_OP_SUBSCR_STR_INT: { + *effect = -1; return 0; } - case BINARY_SUBSCR_LIST_INT: { + case BINARY_OP_SUBSCR_TUPLE_INT: { *effect = -1; return 0; } - case BINARY_SUBSCR_STR_INT: { - *effect = -1; + case BINARY_OP_SUBTRACT_FLOAT: { + *effect = 0; return 0; } - case BINARY_SUBSCR_TUPLE_INT: { - *effect = -1; + case BINARY_OP_SUBTRACT_INT: { + *effect = 0; + return 0; + } + case BINARY_SLICE: { + *effect = 0; return 0; } case BUILD_LIST: { @@ -2017,15 +2009,14 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, + [BINARY_OP_SUBSCR_DICT] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_SUBSCR_GETITEM] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG }, + [BINARY_OP_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_SUBSCR_STR_INT] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [BINARY_SLICE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR_GETITEM] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG }, - [BINARY_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR_STR_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [BUILD_LIST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, [BUILD_MAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BUILD_SET] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -2262,15 +2253,14 @@ _PyOpcode_macro_expansion[256] = { [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, 0, 0 } } }, [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, 0, 0 } } }, [BINARY_OP_MULTIPLY_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_MULTIPLY_INT, 0, 0 } } }, + [BINARY_OP_SUBSCR_DICT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_DICT, 0, 0 } } }, + [BINARY_OP_SUBSCR_GETITEM] = { .nuops = 4, .uops = { { _CHECK_PEP_523, 0, 0 }, { _BINARY_OP_SUBSCR_CHECK_FUNC, 0, 0 }, { _BINARY_OP_SUBSCR_INIT_CALL, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, + [BINARY_OP_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_LIST_INT, 0, 0 } } }, + [BINARY_OP_SUBSCR_STR_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_STR_INT, 0, 0 } } }, + [BINARY_OP_SUBSCR_TUPLE_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_TUPLE_INT, 0, 0 } } }, [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } }, [BINARY_OP_SUBTRACT_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_SUBTRACT_INT, 0, 0 } } }, [BINARY_SLICE] = { .nuops = 1, .uops = { { _BINARY_SLICE, 0, 0 } } }, - [BINARY_SUBSCR] = { .nuops = 1, .uops = { { _BINARY_SUBSCR, 0, 0 } } }, - [BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_DICT, 0, 0 } } }, - [BINARY_SUBSCR_GETITEM] = { .nuops = 4, .uops = { { _CHECK_PEP_523, 0, 0 }, { _BINARY_SUBSCR_CHECK_FUNC, 0, 0 }, { _BINARY_SUBSCR_INIT_CALL, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, - [BINARY_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_LIST_INT, 0, 0 } } }, - [BINARY_SUBSCR_STR_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_STR_INT, 0, 0 } } }, - [BINARY_SUBSCR_TUPLE_INT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_TUPLE_INT, 0, 0 } } }, [BUILD_LIST] = { .nuops = 1, .uops = { { _BUILD_LIST, 0, 0 } } }, [BUILD_MAP] = { .nuops = 1, .uops = { { _BUILD_MAP, 0, 0 } } }, [BUILD_SET] = { .nuops = 1, .uops = { { _BUILD_SET, 0, 0 } } }, @@ -2447,15 +2437,14 @@ const char *_PyOpcode_OpName[266] = { [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE", [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT", + [BINARY_OP_SUBSCR_DICT] = "BINARY_OP_SUBSCR_DICT", + [BINARY_OP_SUBSCR_GETITEM] = "BINARY_OP_SUBSCR_GETITEM", + [BINARY_OP_SUBSCR_LIST_INT] = "BINARY_OP_SUBSCR_LIST_INT", + [BINARY_OP_SUBSCR_STR_INT] = "BINARY_OP_SUBSCR_STR_INT", + [BINARY_OP_SUBSCR_TUPLE_INT] = "BINARY_OP_SUBSCR_TUPLE_INT", [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT", [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT", [BINARY_SLICE] = "BINARY_SLICE", - [BINARY_SUBSCR] = "BINARY_SUBSCR", - [BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT", - [BINARY_SUBSCR_GETITEM] = "BINARY_SUBSCR_GETITEM", - [BINARY_SUBSCR_LIST_INT] = "BINARY_SUBSCR_LIST_INT", - [BINARY_SUBSCR_STR_INT] = "BINARY_SUBSCR_STR_INT", - [BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT", [BUILD_LIST] = "BUILD_LIST", [BUILD_MAP] = "BUILD_MAP", [BUILD_SET] = "BUILD_SET", @@ -2678,7 +2667,6 @@ extern const uint8_t _PyOpcode_Caches[256]; #ifdef NEED_OPCODE_METADATA const uint8_t _PyOpcode_Caches[256] = { [TO_BOOL] = 3, - [BINARY_SUBSCR] = 1, [STORE_SUBSCR] = 1, [SEND] = 1, [UNPACK_SEQUENCE] = 1, @@ -2711,15 +2699,14 @@ const uint8_t _PyOpcode_Deopt[256] = { [BINARY_OP_INPLACE_ADD_UNICODE] = BINARY_OP, [BINARY_OP_MULTIPLY_FLOAT] = BINARY_OP, [BINARY_OP_MULTIPLY_INT] = BINARY_OP, + [BINARY_OP_SUBSCR_DICT] = BINARY_OP, + [BINARY_OP_SUBSCR_GETITEM] = BINARY_OP, + [BINARY_OP_SUBSCR_LIST_INT] = BINARY_OP, + [BINARY_OP_SUBSCR_STR_INT] = BINARY_OP, + [BINARY_OP_SUBSCR_TUPLE_INT] = BINARY_OP, [BINARY_OP_SUBTRACT_FLOAT] = BINARY_OP, [BINARY_OP_SUBTRACT_INT] = BINARY_OP, [BINARY_SLICE] = BINARY_SLICE, - [BINARY_SUBSCR] = BINARY_SUBSCR, - [BINARY_SUBSCR_DICT] = BINARY_SUBSCR, - [BINARY_SUBSCR_GETITEM] = BINARY_SUBSCR, - [BINARY_SUBSCR_LIST_INT] = BINARY_SUBSCR, - [BINARY_SUBSCR_STR_INT] = BINARY_SUBSCR, - [BINARY_SUBSCR_TUPLE_INT] = BINARY_SUBSCR, [BUILD_LIST] = BUILD_LIST, [BUILD_MAP] = BUILD_MAP, [BUILD_SET] = BUILD_SET, @@ -2930,6 +2917,7 @@ const uint8_t _PyOpcode_Deopt[256] = { #endif // NEED_OPCODE_METADATA #define EXTRA_CASES \ + case 117: \ case 118: \ case 119: \ case 120: \ diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 554e3439e0b1da..4e04dd69542648 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -19,138 +19,137 @@ extern "C" { #define _BINARY_OP_INPLACE_ADD_UNICODE 307 #define _BINARY_OP_MULTIPLY_FLOAT 308 #define _BINARY_OP_MULTIPLY_INT 309 -#define _BINARY_OP_SUBTRACT_FLOAT 310 -#define _BINARY_OP_SUBTRACT_INT 311 -#define _BINARY_SLICE 312 -#define _BINARY_SUBSCR 313 -#define _BINARY_SUBSCR_CHECK_FUNC 314 -#define _BINARY_SUBSCR_DICT BINARY_SUBSCR_DICT -#define _BINARY_SUBSCR_INIT_CALL 315 -#define _BINARY_SUBSCR_LIST_INT BINARY_SUBSCR_LIST_INT -#define _BINARY_SUBSCR_STR_INT BINARY_SUBSCR_STR_INT -#define _BINARY_SUBSCR_TUPLE_INT BINARY_SUBSCR_TUPLE_INT +#define _BINARY_OP_SUBSCR_CHECK_FUNC 310 +#define _BINARY_OP_SUBSCR_DICT BINARY_OP_SUBSCR_DICT +#define _BINARY_OP_SUBSCR_INIT_CALL 311 +#define _BINARY_OP_SUBSCR_LIST_INT BINARY_OP_SUBSCR_LIST_INT +#define _BINARY_OP_SUBSCR_STR_INT BINARY_OP_SUBSCR_STR_INT +#define _BINARY_OP_SUBSCR_TUPLE_INT BINARY_OP_SUBSCR_TUPLE_INT +#define _BINARY_OP_SUBTRACT_FLOAT 312 +#define _BINARY_OP_SUBTRACT_INT 313 +#define _BINARY_SLICE 314 #define _BUILD_LIST BUILD_LIST #define _BUILD_MAP BUILD_MAP #define _BUILD_SET BUILD_SET #define _BUILD_SLICE BUILD_SLICE #define _BUILD_STRING BUILD_STRING #define _BUILD_TUPLE BUILD_TUPLE -#define _CALL_BUILTIN_CLASS 316 -#define _CALL_BUILTIN_FAST 317 -#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 318 -#define _CALL_BUILTIN_O 319 +#define _CALL_BUILTIN_CLASS 315 +#define _CALL_BUILTIN_FAST 316 +#define _CALL_BUILTIN_FAST_WITH_KEYWORDS 317 +#define _CALL_BUILTIN_O 318 #define _CALL_INTRINSIC_1 CALL_INTRINSIC_1 #define _CALL_INTRINSIC_2 CALL_INTRINSIC_2 #define _CALL_ISINSTANCE CALL_ISINSTANCE -#define _CALL_KW_NON_PY 320 +#define _CALL_KW_NON_PY 319 #define _CALL_LEN CALL_LEN #define _CALL_LIST_APPEND CALL_LIST_APPEND -#define _CALL_METHOD_DESCRIPTOR_FAST 321 -#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 322 -#define _CALL_METHOD_DESCRIPTOR_NOARGS 323 -#define _CALL_METHOD_DESCRIPTOR_O 324 -#define _CALL_NON_PY_GENERAL 325 -#define _CALL_STR_1 326 -#define _CALL_TUPLE_1 327 +#define _CALL_METHOD_DESCRIPTOR_FAST 320 +#define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 321 +#define _CALL_METHOD_DESCRIPTOR_NOARGS 322 +#define _CALL_METHOD_DESCRIPTOR_O 323 +#define _CALL_NON_PY_GENERAL 324 +#define _CALL_STR_1 325 +#define _CALL_TUPLE_1 326 #define _CALL_TYPE_1 CALL_TYPE_1 -#define _CHECK_AND_ALLOCATE_OBJECT 328 -#define _CHECK_ATTR_CLASS 329 -#define _CHECK_ATTR_METHOD_LAZY_DICT 330 -#define _CHECK_ATTR_MODULE_PUSH_KEYS 331 -#define _CHECK_ATTR_WITH_HINT 332 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 333 +#define _CHECK_AND_ALLOCATE_OBJECT 327 +#define _CHECK_ATTR_CLASS 328 +#define _CHECK_ATTR_METHOD_LAZY_DICT 329 +#define _CHECK_ATTR_MODULE_PUSH_KEYS 330 +#define _CHECK_ATTR_WITH_HINT 331 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 332 #define _CHECK_EG_MATCH CHECK_EG_MATCH #define _CHECK_EXC_MATCH CHECK_EXC_MATCH -#define _CHECK_FUNCTION 334 -#define _CHECK_FUNCTION_EXACT_ARGS 335 -#define _CHECK_FUNCTION_VERSION 336 -#define _CHECK_FUNCTION_VERSION_INLINE 337 -#define _CHECK_FUNCTION_VERSION_KW 338 -#define _CHECK_IS_NOT_PY_CALLABLE 339 -#define _CHECK_IS_NOT_PY_CALLABLE_KW 340 -#define _CHECK_MANAGED_OBJECT_HAS_VALUES 341 -#define _CHECK_METHOD_VERSION 342 -#define _CHECK_METHOD_VERSION_KW 343 -#define _CHECK_PEP_523 344 -#define _CHECK_PERIODIC 345 -#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 346 -#define _CHECK_STACK_SPACE 347 -#define _CHECK_STACK_SPACE_OPERAND 348 -#define _CHECK_VALIDITY 349 -#define _CHECK_VALIDITY_AND_SET_IP 350 -#define _COMPARE_OP 351 -#define _COMPARE_OP_FLOAT 352 -#define _COMPARE_OP_INT 353 -#define _COMPARE_OP_STR 354 -#define _CONTAINS_OP 355 +#define _CHECK_FUNCTION 333 +#define _CHECK_FUNCTION_EXACT_ARGS 334 +#define _CHECK_FUNCTION_VERSION 335 +#define _CHECK_FUNCTION_VERSION_INLINE 336 +#define _CHECK_FUNCTION_VERSION_KW 337 +#define _CHECK_IS_NOT_PY_CALLABLE 338 +#define _CHECK_IS_NOT_PY_CALLABLE_KW 339 +#define _CHECK_MANAGED_OBJECT_HAS_VALUES 340 +#define _CHECK_METHOD_VERSION 341 +#define _CHECK_METHOD_VERSION_KW 342 +#define _CHECK_PEP_523 343 +#define _CHECK_PERIODIC 344 +#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 345 +#define _CHECK_STACK_SPACE 346 +#define _CHECK_STACK_SPACE_OPERAND 347 +#define _CHECK_VALIDITY 348 +#define _CHECK_VALIDITY_AND_SET_IP 349 +#define _COMPARE_OP 350 +#define _COMPARE_OP_FLOAT 351 +#define _COMPARE_OP_INT 352 +#define _COMPARE_OP_STR 353 +#define _CONTAINS_OP 354 #define _CONTAINS_OP_DICT CONTAINS_OP_DICT #define _CONTAINS_OP_SET CONTAINS_OP_SET #define _CONVERT_VALUE CONVERT_VALUE #define _COPY COPY #define _COPY_FREE_VARS COPY_FREE_VARS -#define _CREATE_INIT_FRAME 356 +#define _CREATE_INIT_FRAME 355 #define _DELETE_ATTR DELETE_ATTR #define _DELETE_DEREF DELETE_DEREF #define _DELETE_FAST DELETE_FAST #define _DELETE_GLOBAL DELETE_GLOBAL #define _DELETE_NAME DELETE_NAME #define _DELETE_SUBSCR DELETE_SUBSCR -#define _DEOPT 357 +#define _DEOPT 356 #define _DICT_MERGE DICT_MERGE #define _DICT_UPDATE DICT_UPDATE -#define _DO_CALL 358 -#define _DO_CALL_FUNCTION_EX 359 -#define _DO_CALL_KW 360 +#define _DO_CALL 357 +#define _DO_CALL_FUNCTION_EX 358 +#define _DO_CALL_KW 359 #define _END_FOR END_FOR #define _END_SEND END_SEND -#define _ERROR_POP_N 361 +#define _ERROR_POP_N 360 #define _EXIT_INIT_CHECK EXIT_INIT_CHECK -#define _EXPAND_METHOD 362 -#define _EXPAND_METHOD_KW 363 -#define _FATAL_ERROR 364 +#define _EXPAND_METHOD 361 +#define _EXPAND_METHOD_KW 362 +#define _FATAL_ERROR 363 #define _FORMAT_SIMPLE FORMAT_SIMPLE #define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC -#define _FOR_ITER 365 -#define _FOR_ITER_GEN_FRAME 366 -#define _FOR_ITER_TIER_TWO 367 +#define _FOR_ITER 364 +#define _FOR_ITER_GEN_FRAME 365 +#define _FOR_ITER_TIER_TWO 366 #define _GET_AITER GET_AITER #define _GET_ANEXT GET_ANEXT #define _GET_AWAITABLE GET_AWAITABLE #define _GET_ITER GET_ITER #define _GET_LEN GET_LEN #define _GET_YIELD_FROM_ITER GET_YIELD_FROM_ITER -#define _GUARD_BINARY_OP_EXTEND 368 -#define _GUARD_BOTH_FLOAT 369 -#define _GUARD_BOTH_INT 370 -#define _GUARD_BOTH_UNICODE 371 -#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 372 -#define _GUARD_DORV_NO_DICT 373 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 374 -#define _GUARD_GLOBALS_VERSION 375 -#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 376 -#define _GUARD_IS_FALSE_POP 377 -#define _GUARD_IS_NONE_POP 378 -#define _GUARD_IS_NOT_NONE_POP 379 -#define _GUARD_IS_TRUE_POP 380 -#define _GUARD_KEYS_VERSION 381 -#define _GUARD_NOS_FLOAT 382 -#define _GUARD_NOS_INT 383 -#define _GUARD_NOT_EXHAUSTED_LIST 384 -#define _GUARD_NOT_EXHAUSTED_RANGE 385 -#define _GUARD_NOT_EXHAUSTED_TUPLE 386 -#define _GUARD_TOS_FLOAT 387 -#define _GUARD_TOS_INT 388 -#define _GUARD_TYPE_VERSION 389 -#define _GUARD_TYPE_VERSION_AND_LOCK 390 +#define _GUARD_BINARY_OP_EXTEND 367 +#define _GUARD_BOTH_FLOAT 368 +#define _GUARD_BOTH_INT 369 +#define _GUARD_BOTH_UNICODE 370 +#define _GUARD_BUILTINS_VERSION_PUSH_KEYS 371 +#define _GUARD_DORV_NO_DICT 372 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 373 +#define _GUARD_GLOBALS_VERSION 374 +#define _GUARD_GLOBALS_VERSION_PUSH_KEYS 375 +#define _GUARD_IS_FALSE_POP 376 +#define _GUARD_IS_NONE_POP 377 +#define _GUARD_IS_NOT_NONE_POP 378 +#define _GUARD_IS_TRUE_POP 379 +#define _GUARD_KEYS_VERSION 380 +#define _GUARD_NOS_FLOAT 381 +#define _GUARD_NOS_INT 382 +#define _GUARD_NOT_EXHAUSTED_LIST 383 +#define _GUARD_NOT_EXHAUSTED_RANGE 384 +#define _GUARD_NOT_EXHAUSTED_TUPLE 385 +#define _GUARD_TOS_FLOAT 386 +#define _GUARD_TOS_INT 387 +#define _GUARD_TYPE_VERSION 388 +#define _GUARD_TYPE_VERSION_AND_LOCK 389 #define _IMPORT_FROM IMPORT_FROM #define _IMPORT_NAME IMPORT_NAME -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 391 -#define _INIT_CALL_PY_EXACT_ARGS 392 -#define _INIT_CALL_PY_EXACT_ARGS_0 393 -#define _INIT_CALL_PY_EXACT_ARGS_1 394 -#define _INIT_CALL_PY_EXACT_ARGS_2 395 -#define _INIT_CALL_PY_EXACT_ARGS_3 396 -#define _INIT_CALL_PY_EXACT_ARGS_4 397 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 390 +#define _INIT_CALL_PY_EXACT_ARGS 391 +#define _INIT_CALL_PY_EXACT_ARGS_0 392 +#define _INIT_CALL_PY_EXACT_ARGS_1 393 +#define _INIT_CALL_PY_EXACT_ARGS_2 394 +#define _INIT_CALL_PY_EXACT_ARGS_3 395 +#define _INIT_CALL_PY_EXACT_ARGS_4 396 #define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER #define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION #define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD @@ -160,137 +159,137 @@ extern "C" { #define _INSTRUMENTED_POP_JUMP_IF_NONE INSTRUMENTED_POP_JUMP_IF_NONE #define _INSTRUMENTED_POP_JUMP_IF_NOT_NONE INSTRUMENTED_POP_JUMP_IF_NOT_NONE #define _INSTRUMENTED_POP_JUMP_IF_TRUE INSTRUMENTED_POP_JUMP_IF_TRUE -#define _IS_NONE 398 +#define _IS_NONE 397 #define _IS_OP IS_OP -#define _ITER_CHECK_LIST 399 -#define _ITER_CHECK_RANGE 400 -#define _ITER_CHECK_TUPLE 401 -#define _ITER_JUMP_LIST 402 -#define _ITER_JUMP_RANGE 403 -#define _ITER_JUMP_TUPLE 404 -#define _ITER_NEXT_LIST 405 -#define _ITER_NEXT_RANGE 406 -#define _ITER_NEXT_TUPLE 407 -#define _JUMP_TO_TOP 408 +#define _ITER_CHECK_LIST 398 +#define _ITER_CHECK_RANGE 399 +#define _ITER_CHECK_TUPLE 400 +#define _ITER_JUMP_LIST 401 +#define _ITER_JUMP_RANGE 402 +#define _ITER_JUMP_TUPLE 403 +#define _ITER_NEXT_LIST 404 +#define _ITER_NEXT_RANGE 405 +#define _ITER_NEXT_TUPLE 406 +#define _JUMP_TO_TOP 407 #define _LIST_APPEND LIST_APPEND #define _LIST_EXTEND LIST_EXTEND -#define _LOAD_ATTR 409 -#define _LOAD_ATTR_CLASS 410 +#define _LOAD_ATTR 408 +#define _LOAD_ATTR_CLASS 409 #define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN -#define _LOAD_ATTR_INSTANCE_VALUE 411 -#define _LOAD_ATTR_METHOD_LAZY_DICT 412 -#define _LOAD_ATTR_METHOD_NO_DICT 413 -#define _LOAD_ATTR_METHOD_WITH_VALUES 414 -#define _LOAD_ATTR_MODULE 415 -#define _LOAD_ATTR_MODULE_FROM_KEYS 416 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 417 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 418 -#define _LOAD_ATTR_PROPERTY_FRAME 419 -#define _LOAD_ATTR_SLOT 420 -#define _LOAD_ATTR_WITH_HINT 421 +#define _LOAD_ATTR_INSTANCE_VALUE 410 +#define _LOAD_ATTR_METHOD_LAZY_DICT 411 +#define _LOAD_ATTR_METHOD_NO_DICT 412 +#define _LOAD_ATTR_METHOD_WITH_VALUES 413 +#define _LOAD_ATTR_MODULE 414 +#define _LOAD_ATTR_MODULE_FROM_KEYS 415 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 416 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 417 +#define _LOAD_ATTR_PROPERTY_FRAME 418 +#define _LOAD_ATTR_SLOT 419 +#define _LOAD_ATTR_WITH_HINT 420 #define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS -#define _LOAD_BYTECODE 422 +#define _LOAD_BYTECODE 421 #define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT #define _LOAD_CONST LOAD_CONST #define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL -#define _LOAD_CONST_INLINE 423 -#define _LOAD_CONST_INLINE_BORROW 424 +#define _LOAD_CONST_INLINE 422 +#define _LOAD_CONST_INLINE_BORROW 423 #define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL #define _LOAD_DEREF LOAD_DEREF -#define _LOAD_FAST 425 -#define _LOAD_FAST_0 426 -#define _LOAD_FAST_1 427 -#define _LOAD_FAST_2 428 -#define _LOAD_FAST_3 429 -#define _LOAD_FAST_4 430 -#define _LOAD_FAST_5 431 -#define _LOAD_FAST_6 432 -#define _LOAD_FAST_7 433 +#define _LOAD_FAST 424 +#define _LOAD_FAST_0 425 +#define _LOAD_FAST_1 426 +#define _LOAD_FAST_2 427 +#define _LOAD_FAST_3 428 +#define _LOAD_FAST_4 429 +#define _LOAD_FAST_5 430 +#define _LOAD_FAST_6 431 +#define _LOAD_FAST_7 432 #define _LOAD_FAST_AND_CLEAR LOAD_FAST_AND_CLEAR #define _LOAD_FAST_CHECK LOAD_FAST_CHECK #define _LOAD_FAST_LOAD_FAST LOAD_FAST_LOAD_FAST #define _LOAD_FROM_DICT_OR_DEREF LOAD_FROM_DICT_OR_DEREF #define _LOAD_FROM_DICT_OR_GLOBALS LOAD_FROM_DICT_OR_GLOBALS -#define _LOAD_GLOBAL 434 -#define _LOAD_GLOBAL_BUILTINS 435 -#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 436 -#define _LOAD_GLOBAL_MODULE 437 -#define _LOAD_GLOBAL_MODULE_FROM_KEYS 438 +#define _LOAD_GLOBAL 433 +#define _LOAD_GLOBAL_BUILTINS 434 +#define _LOAD_GLOBAL_BUILTINS_FROM_KEYS 435 +#define _LOAD_GLOBAL_MODULE 436 +#define _LOAD_GLOBAL_MODULE_FROM_KEYS 437 #define _LOAD_LOCALS LOAD_LOCALS #define _LOAD_NAME LOAD_NAME -#define _LOAD_SMALL_INT 439 -#define _LOAD_SMALL_INT_0 440 -#define _LOAD_SMALL_INT_1 441 -#define _LOAD_SMALL_INT_2 442 -#define _LOAD_SMALL_INT_3 443 +#define _LOAD_SMALL_INT 438 +#define _LOAD_SMALL_INT_0 439 +#define _LOAD_SMALL_INT_1 440 +#define _LOAD_SMALL_INT_2 441 +#define _LOAD_SMALL_INT_3 442 #define _LOAD_SPECIAL LOAD_SPECIAL #define _LOAD_SUPER_ATTR_ATTR LOAD_SUPER_ATTR_ATTR #define _LOAD_SUPER_ATTR_METHOD LOAD_SUPER_ATTR_METHOD -#define _MAKE_CALLARGS_A_TUPLE 444 +#define _MAKE_CALLARGS_A_TUPLE 443 #define _MAKE_CELL MAKE_CELL #define _MAKE_FUNCTION MAKE_FUNCTION -#define _MAKE_WARM 445 +#define _MAKE_WARM 444 #define _MAP_ADD MAP_ADD #define _MATCH_CLASS MATCH_CLASS #define _MATCH_KEYS MATCH_KEYS #define _MATCH_MAPPING MATCH_MAPPING #define _MATCH_SEQUENCE MATCH_SEQUENCE -#define _MAYBE_EXPAND_METHOD 446 -#define _MAYBE_EXPAND_METHOD_KW 447 -#define _MONITOR_CALL 448 -#define _MONITOR_CALL_KW 449 -#define _MONITOR_JUMP_BACKWARD 450 -#define _MONITOR_RESUME 451 +#define _MAYBE_EXPAND_METHOD 445 +#define _MAYBE_EXPAND_METHOD_KW 446 +#define _MONITOR_CALL 447 +#define _MONITOR_CALL_KW 448 +#define _MONITOR_JUMP_BACKWARD 449 +#define _MONITOR_RESUME 450 #define _NOP NOP #define _POP_EXCEPT POP_EXCEPT -#define _POP_JUMP_IF_FALSE 452 -#define _POP_JUMP_IF_TRUE 453 +#define _POP_JUMP_IF_FALSE 451 +#define _POP_JUMP_IF_TRUE 452 #define _POP_TOP POP_TOP -#define _POP_TOP_LOAD_CONST_INLINE_BORROW 454 +#define _POP_TOP_LOAD_CONST_INLINE_BORROW 453 #define _PUSH_EXC_INFO PUSH_EXC_INFO -#define _PUSH_FRAME 455 +#define _PUSH_FRAME 454 #define _PUSH_NULL PUSH_NULL -#define _PUSH_NULL_CONDITIONAL 456 -#define _PY_FRAME_GENERAL 457 -#define _PY_FRAME_KW 458 -#define _QUICKEN_RESUME 459 -#define _REPLACE_WITH_TRUE 460 +#define _PUSH_NULL_CONDITIONAL 455 +#define _PY_FRAME_GENERAL 456 +#define _PY_FRAME_KW 457 +#define _QUICKEN_RESUME 458 +#define _REPLACE_WITH_TRUE 459 #define _RESUME_CHECK RESUME_CHECK #define _RETURN_GENERATOR RETURN_GENERATOR #define _RETURN_VALUE RETURN_VALUE -#define _SAVE_RETURN_OFFSET 461 -#define _SEND 462 -#define _SEND_GEN_FRAME 463 +#define _SAVE_RETURN_OFFSET 460 +#define _SEND 461 +#define _SEND_GEN_FRAME 462 #define _SETUP_ANNOTATIONS SETUP_ANNOTATIONS #define _SET_ADD SET_ADD #define _SET_FUNCTION_ATTRIBUTE SET_FUNCTION_ATTRIBUTE #define _SET_UPDATE SET_UPDATE -#define _START_EXECUTOR 464 -#define _STORE_ATTR 465 -#define _STORE_ATTR_INSTANCE_VALUE 466 -#define _STORE_ATTR_SLOT 467 -#define _STORE_ATTR_WITH_HINT 468 +#define _START_EXECUTOR 463 +#define _STORE_ATTR 464 +#define _STORE_ATTR_INSTANCE_VALUE 465 +#define _STORE_ATTR_SLOT 466 +#define _STORE_ATTR_WITH_HINT 467 #define _STORE_DEREF STORE_DEREF -#define _STORE_FAST 469 -#define _STORE_FAST_0 470 -#define _STORE_FAST_1 471 -#define _STORE_FAST_2 472 -#define _STORE_FAST_3 473 -#define _STORE_FAST_4 474 -#define _STORE_FAST_5 475 -#define _STORE_FAST_6 476 -#define _STORE_FAST_7 477 +#define _STORE_FAST 468 +#define _STORE_FAST_0 469 +#define _STORE_FAST_1 470 +#define _STORE_FAST_2 471 +#define _STORE_FAST_3 472 +#define _STORE_FAST_4 473 +#define _STORE_FAST_5 474 +#define _STORE_FAST_6 475 +#define _STORE_FAST_7 476 #define _STORE_FAST_LOAD_FAST STORE_FAST_LOAD_FAST #define _STORE_FAST_STORE_FAST STORE_FAST_STORE_FAST #define _STORE_GLOBAL STORE_GLOBAL #define _STORE_NAME STORE_NAME -#define _STORE_SLICE 478 -#define _STORE_SUBSCR 479 +#define _STORE_SLICE 477 +#define _STORE_SUBSCR 478 #define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT #define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT #define _SWAP SWAP -#define _TIER2_RESUME_CHECK 480 -#define _TO_BOOL 481 +#define _TIER2_RESUME_CHECK 479 +#define _TO_BOOL 480 #define _TO_BOOL_BOOL TO_BOOL_BOOL #define _TO_BOOL_INT TO_BOOL_INT #define _TO_BOOL_LIST TO_BOOL_LIST @@ -300,13 +299,13 @@ extern "C" { #define _UNARY_NEGATIVE UNARY_NEGATIVE #define _UNARY_NOT UNARY_NOT #define _UNPACK_EX UNPACK_EX -#define _UNPACK_SEQUENCE 482 +#define _UNPACK_SEQUENCE 481 #define _UNPACK_SEQUENCE_LIST UNPACK_SEQUENCE_LIST #define _UNPACK_SEQUENCE_TUPLE UNPACK_SEQUENCE_TUPLE #define _UNPACK_SEQUENCE_TWO_TUPLE UNPACK_SEQUENCE_TWO_TUPLE #define _WITH_EXCEPT_START WITH_EXCEPT_START #define _YIELD_VALUE YIELD_VALUE -#define MAX_UOP_ID 482 +#define MAX_UOP_ID 481 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 2e126b57aa7dbd..86a4843ea05db9 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -84,15 +84,14 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG, [_GUARD_BINARY_OP_EXTEND] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_BINARY_OP_EXTEND] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, - [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_STR_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_TUPLE_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_DICT] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_BINARY_SUBSCR_CHECK_FUNC] = HAS_DEOPT_FLAG, - [_BINARY_SUBSCR_INIT_CALL] = 0, + [_BINARY_OP_SUBSCR_LIST_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_SUBSCR_STR_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_SUBSCR_TUPLE_INT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_SUBSCR_DICT] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_SUBSCR_CHECK_FUNC] = HAS_DEOPT_FLAG, + [_BINARY_OP_SUBSCR_INIT_CALL] = 0, [_LIST_APPEND] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_SET_ADD] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -306,16 +305,15 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_BINARY_OP_INPLACE_ADD_UNICODE] = "_BINARY_OP_INPLACE_ADD_UNICODE", [_BINARY_OP_MULTIPLY_FLOAT] = "_BINARY_OP_MULTIPLY_FLOAT", [_BINARY_OP_MULTIPLY_INT] = "_BINARY_OP_MULTIPLY_INT", + [_BINARY_OP_SUBSCR_CHECK_FUNC] = "_BINARY_OP_SUBSCR_CHECK_FUNC", + [_BINARY_OP_SUBSCR_DICT] = "_BINARY_OP_SUBSCR_DICT", + [_BINARY_OP_SUBSCR_INIT_CALL] = "_BINARY_OP_SUBSCR_INIT_CALL", + [_BINARY_OP_SUBSCR_LIST_INT] = "_BINARY_OP_SUBSCR_LIST_INT", + [_BINARY_OP_SUBSCR_STR_INT] = "_BINARY_OP_SUBSCR_STR_INT", + [_BINARY_OP_SUBSCR_TUPLE_INT] = "_BINARY_OP_SUBSCR_TUPLE_INT", [_BINARY_OP_SUBTRACT_FLOAT] = "_BINARY_OP_SUBTRACT_FLOAT", [_BINARY_OP_SUBTRACT_INT] = "_BINARY_OP_SUBTRACT_INT", [_BINARY_SLICE] = "_BINARY_SLICE", - [_BINARY_SUBSCR] = "_BINARY_SUBSCR", - [_BINARY_SUBSCR_CHECK_FUNC] = "_BINARY_SUBSCR_CHECK_FUNC", - [_BINARY_SUBSCR_DICT] = "_BINARY_SUBSCR_DICT", - [_BINARY_SUBSCR_INIT_CALL] = "_BINARY_SUBSCR_INIT_CALL", - [_BINARY_SUBSCR_LIST_INT] = "_BINARY_SUBSCR_LIST_INT", - [_BINARY_SUBSCR_STR_INT] = "_BINARY_SUBSCR_STR_INT", - [_BINARY_SUBSCR_TUPLE_INT] = "_BINARY_SUBSCR_TUPLE_INT", [_BUILD_LIST] = "_BUILD_LIST", [_BUILD_MAP] = "_BUILD_MAP", [_BUILD_SET] = "_BUILD_SET", @@ -701,23 +699,21 @@ int _PyUop_num_popped(int opcode, int oparg) return 0; case _BINARY_OP_EXTEND: return 2; - case _BINARY_SUBSCR: - return 2; case _BINARY_SLICE: return 3; case _STORE_SLICE: return 4; - case _BINARY_SUBSCR_LIST_INT: + case _BINARY_OP_SUBSCR_LIST_INT: return 2; - case _BINARY_SUBSCR_STR_INT: + case _BINARY_OP_SUBSCR_STR_INT: return 2; - case _BINARY_SUBSCR_TUPLE_INT: + case _BINARY_OP_SUBSCR_TUPLE_INT: return 2; - case _BINARY_SUBSCR_DICT: + case _BINARY_OP_SUBSCR_DICT: return 2; - case _BINARY_SUBSCR_CHECK_FUNC: + case _BINARY_OP_SUBSCR_CHECK_FUNC: return 0; - case _BINARY_SUBSCR_INIT_CALL: + case _BINARY_OP_SUBSCR_INIT_CALL: return 3; case _LIST_APPEND: return 1; diff --git a/Include/opcode.h b/Include/opcode.h index 2619b690019acc..fcb972f2ec2acb 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -33,8 +33,9 @@ extern "C" { #define NB_INPLACE_SUBTRACT 23 #define NB_INPLACE_TRUE_DIVIDE 24 #define NB_INPLACE_XOR 25 +#define NB_SUBSCR 26 -#define NB_OPARG_LAST 25 +#define NB_OPARG_LAST 26 #ifdef __cplusplus } diff --git a/Include/opcode_ids.h b/Include/opcode_ids.h index dfe7fa36cccd31..54f543f4a8a505 100644 --- a/Include/opcode_ids.h +++ b/Include/opcode_ids.h @@ -12,122 +12,121 @@ extern "C" { /* Instruction opcodes for compiled code */ #define CACHE 0 #define BINARY_SLICE 1 -#define BINARY_SUBSCR 2 +#define CALL_FUNCTION_EX 2 #define BINARY_OP_INPLACE_ADD_UNICODE 3 -#define CALL_FUNCTION_EX 4 -#define CHECK_EG_MATCH 5 -#define CHECK_EXC_MATCH 6 -#define CLEANUP_THROW 7 -#define DELETE_SUBSCR 8 -#define END_ASYNC_FOR 9 -#define END_FOR 10 -#define END_SEND 11 -#define EXIT_INIT_CHECK 12 -#define FORMAT_SIMPLE 13 -#define FORMAT_WITH_SPEC 14 -#define GET_AITER 15 -#define GET_ANEXT 16 +#define CHECK_EG_MATCH 4 +#define CHECK_EXC_MATCH 5 +#define CLEANUP_THROW 6 +#define DELETE_SUBSCR 7 +#define END_ASYNC_FOR 8 +#define END_FOR 9 +#define END_SEND 10 +#define EXIT_INIT_CHECK 11 +#define FORMAT_SIMPLE 12 +#define FORMAT_WITH_SPEC 13 +#define GET_AITER 14 +#define GET_ANEXT 15 +#define GET_ITER 16 #define RESERVED 17 -#define GET_ITER 18 -#define GET_LEN 19 -#define GET_YIELD_FROM_ITER 20 -#define INTERPRETER_EXIT 21 -#define LOAD_BUILD_CLASS 22 -#define LOAD_LOCALS 23 -#define MAKE_FUNCTION 24 -#define MATCH_KEYS 25 -#define MATCH_MAPPING 26 -#define MATCH_SEQUENCE 27 -#define NOP 28 -#define NOT_TAKEN 29 -#define POP_EXCEPT 30 -#define POP_ITER 31 -#define POP_TOP 32 -#define PUSH_EXC_INFO 33 -#define PUSH_NULL 34 -#define RETURN_GENERATOR 35 -#define RETURN_VALUE 36 -#define SETUP_ANNOTATIONS 37 -#define STORE_SLICE 38 -#define STORE_SUBSCR 39 -#define TO_BOOL 40 -#define UNARY_INVERT 41 -#define UNARY_NEGATIVE 42 -#define UNARY_NOT 43 -#define WITH_EXCEPT_START 44 -#define BINARY_OP 45 -#define BUILD_LIST 46 -#define BUILD_MAP 47 -#define BUILD_SET 48 -#define BUILD_SLICE 49 -#define BUILD_STRING 50 -#define BUILD_TUPLE 51 -#define CALL 52 -#define CALL_INTRINSIC_1 53 -#define CALL_INTRINSIC_2 54 -#define CALL_KW 55 -#define COMPARE_OP 56 -#define CONTAINS_OP 57 -#define CONVERT_VALUE 58 -#define COPY 59 -#define COPY_FREE_VARS 60 -#define DELETE_ATTR 61 -#define DELETE_DEREF 62 -#define DELETE_FAST 63 -#define DELETE_GLOBAL 64 -#define DELETE_NAME 65 -#define DICT_MERGE 66 -#define DICT_UPDATE 67 -#define EXTENDED_ARG 68 -#define FOR_ITER 69 -#define GET_AWAITABLE 70 -#define IMPORT_FROM 71 -#define IMPORT_NAME 72 -#define IS_OP 73 -#define JUMP_BACKWARD 74 -#define JUMP_BACKWARD_NO_INTERRUPT 75 -#define JUMP_FORWARD 76 -#define LIST_APPEND 77 -#define LIST_EXTEND 78 -#define LOAD_ATTR 79 -#define LOAD_COMMON_CONSTANT 80 -#define LOAD_CONST 81 -#define LOAD_DEREF 82 -#define LOAD_FAST 83 -#define LOAD_FAST_AND_CLEAR 84 -#define LOAD_FAST_CHECK 85 -#define LOAD_FAST_LOAD_FAST 86 -#define LOAD_FROM_DICT_OR_DEREF 87 -#define LOAD_FROM_DICT_OR_GLOBALS 88 -#define LOAD_GLOBAL 89 -#define LOAD_NAME 90 -#define LOAD_SMALL_INT 91 -#define LOAD_SPECIAL 92 -#define LOAD_SUPER_ATTR 93 -#define MAKE_CELL 94 -#define MAP_ADD 95 -#define MATCH_CLASS 96 -#define POP_JUMP_IF_FALSE 97 -#define POP_JUMP_IF_NONE 98 -#define POP_JUMP_IF_NOT_NONE 99 -#define POP_JUMP_IF_TRUE 100 -#define RAISE_VARARGS 101 -#define RERAISE 102 -#define SEND 103 -#define SET_ADD 104 -#define SET_FUNCTION_ATTRIBUTE 105 -#define SET_UPDATE 106 -#define STORE_ATTR 107 -#define STORE_DEREF 108 -#define STORE_FAST 109 -#define STORE_FAST_LOAD_FAST 110 -#define STORE_FAST_STORE_FAST 111 -#define STORE_GLOBAL 112 -#define STORE_NAME 113 -#define SWAP 114 -#define UNPACK_EX 115 -#define UNPACK_SEQUENCE 116 -#define YIELD_VALUE 117 +#define GET_LEN 18 +#define GET_YIELD_FROM_ITER 19 +#define INTERPRETER_EXIT 20 +#define LOAD_BUILD_CLASS 21 +#define LOAD_LOCALS 22 +#define MAKE_FUNCTION 23 +#define MATCH_KEYS 24 +#define MATCH_MAPPING 25 +#define MATCH_SEQUENCE 26 +#define NOP 27 +#define NOT_TAKEN 28 +#define POP_EXCEPT 29 +#define POP_ITER 30 +#define POP_TOP 31 +#define PUSH_EXC_INFO 32 +#define PUSH_NULL 33 +#define RETURN_GENERATOR 34 +#define RETURN_VALUE 35 +#define SETUP_ANNOTATIONS 36 +#define STORE_SLICE 37 +#define STORE_SUBSCR 38 +#define TO_BOOL 39 +#define UNARY_INVERT 40 +#define UNARY_NEGATIVE 41 +#define UNARY_NOT 42 +#define WITH_EXCEPT_START 43 +#define BINARY_OP 44 +#define BUILD_LIST 45 +#define BUILD_MAP 46 +#define BUILD_SET 47 +#define BUILD_SLICE 48 +#define BUILD_STRING 49 +#define BUILD_TUPLE 50 +#define CALL 51 +#define CALL_INTRINSIC_1 52 +#define CALL_INTRINSIC_2 53 +#define CALL_KW 54 +#define COMPARE_OP 55 +#define CONTAINS_OP 56 +#define CONVERT_VALUE 57 +#define COPY 58 +#define COPY_FREE_VARS 59 +#define DELETE_ATTR 60 +#define DELETE_DEREF 61 +#define DELETE_FAST 62 +#define DELETE_GLOBAL 63 +#define DELETE_NAME 64 +#define DICT_MERGE 65 +#define DICT_UPDATE 66 +#define EXTENDED_ARG 67 +#define FOR_ITER 68 +#define GET_AWAITABLE 69 +#define IMPORT_FROM 70 +#define IMPORT_NAME 71 +#define IS_OP 72 +#define JUMP_BACKWARD 73 +#define JUMP_BACKWARD_NO_INTERRUPT 74 +#define JUMP_FORWARD 75 +#define LIST_APPEND 76 +#define LIST_EXTEND 77 +#define LOAD_ATTR 78 +#define LOAD_COMMON_CONSTANT 79 +#define LOAD_CONST 80 +#define LOAD_DEREF 81 +#define LOAD_FAST 82 +#define LOAD_FAST_AND_CLEAR 83 +#define LOAD_FAST_CHECK 84 +#define LOAD_FAST_LOAD_FAST 85 +#define LOAD_FROM_DICT_OR_DEREF 86 +#define LOAD_FROM_DICT_OR_GLOBALS 87 +#define LOAD_GLOBAL 88 +#define LOAD_NAME 89 +#define LOAD_SMALL_INT 90 +#define LOAD_SPECIAL 91 +#define LOAD_SUPER_ATTR 92 +#define MAKE_CELL 93 +#define MAP_ADD 94 +#define MATCH_CLASS 95 +#define POP_JUMP_IF_FALSE 96 +#define POP_JUMP_IF_NONE 97 +#define POP_JUMP_IF_NOT_NONE 98 +#define POP_JUMP_IF_TRUE 99 +#define RAISE_VARARGS 100 +#define RERAISE 101 +#define SEND 102 +#define SET_ADD 103 +#define SET_FUNCTION_ATTRIBUTE 104 +#define SET_UPDATE 105 +#define STORE_ATTR 106 +#define STORE_DEREF 107 +#define STORE_FAST 108 +#define STORE_FAST_LOAD_FAST 109 +#define STORE_FAST_STORE_FAST 110 +#define STORE_GLOBAL 111 +#define STORE_NAME 112 +#define SWAP 113 +#define UNPACK_EX 114 +#define UNPACK_SEQUENCE 115 +#define YIELD_VALUE 116 #define RESUME 149 #define BINARY_OP_ADD_FLOAT 150 #define BINARY_OP_ADD_INT 151 @@ -135,13 +134,13 @@ extern "C" { #define BINARY_OP_EXTEND 153 #define BINARY_OP_MULTIPLY_FLOAT 154 #define BINARY_OP_MULTIPLY_INT 155 -#define BINARY_OP_SUBTRACT_FLOAT 156 -#define BINARY_OP_SUBTRACT_INT 157 -#define BINARY_SUBSCR_DICT 158 -#define BINARY_SUBSCR_GETITEM 159 -#define BINARY_SUBSCR_LIST_INT 160 -#define BINARY_SUBSCR_STR_INT 161 -#define BINARY_SUBSCR_TUPLE_INT 162 +#define BINARY_OP_SUBSCR_DICT 156 +#define BINARY_OP_SUBSCR_GETITEM 157 +#define BINARY_OP_SUBSCR_LIST_INT 158 +#define BINARY_OP_SUBSCR_STR_INT 159 +#define BINARY_OP_SUBSCR_TUPLE_INT 160 +#define BINARY_OP_SUBTRACT_FLOAT 161 +#define BINARY_OP_SUBTRACT_INT 162 #define CALL_ALLOC_AND_ENTER_INIT 163 #define CALL_BOUND_METHOD_EXACT_ARGS 164 #define CALL_BOUND_METHOD_GENERAL 165 @@ -243,7 +242,7 @@ extern "C" { #define SETUP_WITH 264 #define STORE_FAST_MAYBE_NULL 265 -#define HAVE_ARGUMENT 44 +#define HAVE_ARGUMENT 43 #define MIN_SPECIALIZED_OPCODE 150 #define MIN_INSTRUMENTED_OPCODE 235 diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index ae3e9bd0ab4940..0e18792402df6c 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -26,16 +26,14 @@ "BINARY_OP_ADD_FLOAT", "BINARY_OP_SUBTRACT_FLOAT", "BINARY_OP_ADD_UNICODE", + "BINARY_OP_SUBSCR_LIST_INT", + "BINARY_OP_SUBSCR_TUPLE_INT", + "BINARY_OP_SUBSCR_STR_INT", + "BINARY_OP_SUBSCR_DICT", + "BINARY_OP_SUBSCR_GETITEM", "BINARY_OP_EXTEND", "BINARY_OP_INPLACE_ADD_UNICODE", ], - "BINARY_SUBSCR": [ - "BINARY_SUBSCR_DICT", - "BINARY_SUBSCR_GETITEM", - "BINARY_SUBSCR_LIST_INT", - "BINARY_SUBSCR_STR_INT", - "BINARY_SUBSCR_TUPLE_INT", - ], "STORE_SUBSCR": [ "STORE_SUBSCR_DICT", "STORE_SUBSCR_LIST_INT", @@ -132,13 +130,13 @@ 'BINARY_OP_INPLACE_ADD_UNICODE': 3, 'BINARY_OP_MULTIPLY_FLOAT': 154, 'BINARY_OP_MULTIPLY_INT': 155, - 'BINARY_OP_SUBTRACT_FLOAT': 156, - 'BINARY_OP_SUBTRACT_INT': 157, - 'BINARY_SUBSCR_DICT': 158, - 'BINARY_SUBSCR_GETITEM': 159, - 'BINARY_SUBSCR_LIST_INT': 160, - 'BINARY_SUBSCR_STR_INT': 161, - 'BINARY_SUBSCR_TUPLE_INT': 162, + 'BINARY_OP_SUBSCR_DICT': 156, + 'BINARY_OP_SUBSCR_GETITEM': 157, + 'BINARY_OP_SUBSCR_LIST_INT': 158, + 'BINARY_OP_SUBSCR_STR_INT': 159, + 'BINARY_OP_SUBSCR_TUPLE_INT': 160, + 'BINARY_OP_SUBTRACT_FLOAT': 161, + 'BINARY_OP_SUBTRACT_INT': 162, 'CALL_ALLOC_AND_ENTER_INIT': 163, 'CALL_BOUND_METHOD_EXACT_ARGS': 164, 'CALL_BOUND_METHOD_GENERAL': 165, @@ -217,120 +215,119 @@ 'INSTRUMENTED_LINE': 254, 'ENTER_EXECUTOR': 255, 'BINARY_SLICE': 1, - 'BINARY_SUBSCR': 2, - 'CALL_FUNCTION_EX': 4, - 'CHECK_EG_MATCH': 5, - 'CHECK_EXC_MATCH': 6, - 'CLEANUP_THROW': 7, - 'DELETE_SUBSCR': 8, - 'END_ASYNC_FOR': 9, - 'END_FOR': 10, - 'END_SEND': 11, - 'EXIT_INIT_CHECK': 12, - 'FORMAT_SIMPLE': 13, - 'FORMAT_WITH_SPEC': 14, - 'GET_AITER': 15, - 'GET_ANEXT': 16, - 'GET_ITER': 18, - 'GET_LEN': 19, - 'GET_YIELD_FROM_ITER': 20, - 'INTERPRETER_EXIT': 21, - 'LOAD_BUILD_CLASS': 22, - 'LOAD_LOCALS': 23, - 'MAKE_FUNCTION': 24, - 'MATCH_KEYS': 25, - 'MATCH_MAPPING': 26, - 'MATCH_SEQUENCE': 27, - 'NOP': 28, - 'NOT_TAKEN': 29, - 'POP_EXCEPT': 30, - 'POP_ITER': 31, - 'POP_TOP': 32, - 'PUSH_EXC_INFO': 33, - 'PUSH_NULL': 34, - 'RETURN_GENERATOR': 35, - 'RETURN_VALUE': 36, - 'SETUP_ANNOTATIONS': 37, - 'STORE_SLICE': 38, - 'STORE_SUBSCR': 39, - 'TO_BOOL': 40, - 'UNARY_INVERT': 41, - 'UNARY_NEGATIVE': 42, - 'UNARY_NOT': 43, - 'WITH_EXCEPT_START': 44, - 'BINARY_OP': 45, - 'BUILD_LIST': 46, - 'BUILD_MAP': 47, - 'BUILD_SET': 48, - 'BUILD_SLICE': 49, - 'BUILD_STRING': 50, - 'BUILD_TUPLE': 51, - 'CALL': 52, - 'CALL_INTRINSIC_1': 53, - 'CALL_INTRINSIC_2': 54, - 'CALL_KW': 55, - 'COMPARE_OP': 56, - 'CONTAINS_OP': 57, - 'CONVERT_VALUE': 58, - 'COPY': 59, - 'COPY_FREE_VARS': 60, - 'DELETE_ATTR': 61, - 'DELETE_DEREF': 62, - 'DELETE_FAST': 63, - 'DELETE_GLOBAL': 64, - 'DELETE_NAME': 65, - 'DICT_MERGE': 66, - 'DICT_UPDATE': 67, - 'EXTENDED_ARG': 68, - 'FOR_ITER': 69, - 'GET_AWAITABLE': 70, - 'IMPORT_FROM': 71, - 'IMPORT_NAME': 72, - 'IS_OP': 73, - 'JUMP_BACKWARD': 74, - 'JUMP_BACKWARD_NO_INTERRUPT': 75, - 'JUMP_FORWARD': 76, - 'LIST_APPEND': 77, - 'LIST_EXTEND': 78, - 'LOAD_ATTR': 79, - 'LOAD_COMMON_CONSTANT': 80, - 'LOAD_CONST': 81, - 'LOAD_DEREF': 82, - 'LOAD_FAST': 83, - 'LOAD_FAST_AND_CLEAR': 84, - 'LOAD_FAST_CHECK': 85, - 'LOAD_FAST_LOAD_FAST': 86, - 'LOAD_FROM_DICT_OR_DEREF': 87, - 'LOAD_FROM_DICT_OR_GLOBALS': 88, - 'LOAD_GLOBAL': 89, - 'LOAD_NAME': 90, - 'LOAD_SMALL_INT': 91, - 'LOAD_SPECIAL': 92, - 'LOAD_SUPER_ATTR': 93, - 'MAKE_CELL': 94, - 'MAP_ADD': 95, - 'MATCH_CLASS': 96, - 'POP_JUMP_IF_FALSE': 97, - 'POP_JUMP_IF_NONE': 98, - 'POP_JUMP_IF_NOT_NONE': 99, - 'POP_JUMP_IF_TRUE': 100, - 'RAISE_VARARGS': 101, - 'RERAISE': 102, - 'SEND': 103, - 'SET_ADD': 104, - 'SET_FUNCTION_ATTRIBUTE': 105, - 'SET_UPDATE': 106, - 'STORE_ATTR': 107, - 'STORE_DEREF': 108, - 'STORE_FAST': 109, - 'STORE_FAST_LOAD_FAST': 110, - 'STORE_FAST_STORE_FAST': 111, - 'STORE_GLOBAL': 112, - 'STORE_NAME': 113, - 'SWAP': 114, - 'UNPACK_EX': 115, - 'UNPACK_SEQUENCE': 116, - 'YIELD_VALUE': 117, + 'CALL_FUNCTION_EX': 2, + 'CHECK_EG_MATCH': 4, + 'CHECK_EXC_MATCH': 5, + 'CLEANUP_THROW': 6, + 'DELETE_SUBSCR': 7, + 'END_ASYNC_FOR': 8, + 'END_FOR': 9, + 'END_SEND': 10, + 'EXIT_INIT_CHECK': 11, + 'FORMAT_SIMPLE': 12, + 'FORMAT_WITH_SPEC': 13, + 'GET_AITER': 14, + 'GET_ANEXT': 15, + 'GET_ITER': 16, + 'GET_LEN': 18, + 'GET_YIELD_FROM_ITER': 19, + 'INTERPRETER_EXIT': 20, + 'LOAD_BUILD_CLASS': 21, + 'LOAD_LOCALS': 22, + 'MAKE_FUNCTION': 23, + 'MATCH_KEYS': 24, + 'MATCH_MAPPING': 25, + 'MATCH_SEQUENCE': 26, + 'NOP': 27, + 'NOT_TAKEN': 28, + 'POP_EXCEPT': 29, + 'POP_ITER': 30, + 'POP_TOP': 31, + 'PUSH_EXC_INFO': 32, + 'PUSH_NULL': 33, + 'RETURN_GENERATOR': 34, + 'RETURN_VALUE': 35, + 'SETUP_ANNOTATIONS': 36, + 'STORE_SLICE': 37, + 'STORE_SUBSCR': 38, + 'TO_BOOL': 39, + 'UNARY_INVERT': 40, + 'UNARY_NEGATIVE': 41, + 'UNARY_NOT': 42, + 'WITH_EXCEPT_START': 43, + 'BINARY_OP': 44, + 'BUILD_LIST': 45, + 'BUILD_MAP': 46, + 'BUILD_SET': 47, + 'BUILD_SLICE': 48, + 'BUILD_STRING': 49, + 'BUILD_TUPLE': 50, + 'CALL': 51, + 'CALL_INTRINSIC_1': 52, + 'CALL_INTRINSIC_2': 53, + 'CALL_KW': 54, + 'COMPARE_OP': 55, + 'CONTAINS_OP': 56, + 'CONVERT_VALUE': 57, + 'COPY': 58, + 'COPY_FREE_VARS': 59, + 'DELETE_ATTR': 60, + 'DELETE_DEREF': 61, + 'DELETE_FAST': 62, + 'DELETE_GLOBAL': 63, + 'DELETE_NAME': 64, + 'DICT_MERGE': 65, + 'DICT_UPDATE': 66, + 'EXTENDED_ARG': 67, + 'FOR_ITER': 68, + 'GET_AWAITABLE': 69, + 'IMPORT_FROM': 70, + 'IMPORT_NAME': 71, + 'IS_OP': 72, + 'JUMP_BACKWARD': 73, + 'JUMP_BACKWARD_NO_INTERRUPT': 74, + 'JUMP_FORWARD': 75, + 'LIST_APPEND': 76, + 'LIST_EXTEND': 77, + 'LOAD_ATTR': 78, + 'LOAD_COMMON_CONSTANT': 79, + 'LOAD_CONST': 80, + 'LOAD_DEREF': 81, + 'LOAD_FAST': 82, + 'LOAD_FAST_AND_CLEAR': 83, + 'LOAD_FAST_CHECK': 84, + 'LOAD_FAST_LOAD_FAST': 85, + 'LOAD_FROM_DICT_OR_DEREF': 86, + 'LOAD_FROM_DICT_OR_GLOBALS': 87, + 'LOAD_GLOBAL': 88, + 'LOAD_NAME': 89, + 'LOAD_SMALL_INT': 90, + 'LOAD_SPECIAL': 91, + 'LOAD_SUPER_ATTR': 92, + 'MAKE_CELL': 93, + 'MAP_ADD': 94, + 'MATCH_CLASS': 95, + 'POP_JUMP_IF_FALSE': 96, + 'POP_JUMP_IF_NONE': 97, + 'POP_JUMP_IF_NOT_NONE': 98, + 'POP_JUMP_IF_TRUE': 99, + 'RAISE_VARARGS': 100, + 'RERAISE': 101, + 'SEND': 102, + 'SET_ADD': 103, + 'SET_FUNCTION_ATTRIBUTE': 104, + 'SET_UPDATE': 105, + 'STORE_ATTR': 106, + 'STORE_DEREF': 107, + 'STORE_FAST': 108, + 'STORE_FAST_LOAD_FAST': 109, + 'STORE_FAST_STORE_FAST': 110, + 'STORE_GLOBAL': 111, + 'STORE_NAME': 112, + 'SWAP': 113, + 'UNPACK_EX': 114, + 'UNPACK_SEQUENCE': 115, + 'YIELD_VALUE': 116, 'INSTRUMENTED_END_FOR': 235, 'INSTRUMENTED_POP_ITER': 236, 'INSTRUMENTED_END_SEND': 237, @@ -362,5 +359,5 @@ 'STORE_FAST_MAYBE_NULL': 265, } -HAVE_ARGUMENT = 44 +HAVE_ARGUMENT = 43 MIN_INSTRUMENTED_OPCODE = 235 diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py index 4b11e83ae59a78..c253bc2be025a0 100644 --- a/Lib/test/test__opcode.py +++ b/Lib/test/test__opcode.py @@ -123,7 +123,7 @@ def test_specialization_stats(self): if opcode._inline_cache_entries.get(op, 0) ] self.assertIn('load_attr', specialized_opcodes) - self.assertIn('binary_subscr', specialized_opcodes) + self.assertIn('binary_op', specialized_opcodes) stats = _opcode.get_specialization_stats() if stats is not None: diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index b5cf2ad18fe60b..6c3de2091c451d 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1410,7 +1410,7 @@ def load(): check_op_count(load, "BINARY_SLICE", 3) check_op_count(load, "BUILD_SLICE", 0) check_consts(load, slice, [slice(None, None, None)]) - check_op_count(load, "BINARY_SUBSCR", 1) + check_op_count(load, "BINARY_OP", 4) def store(): x[a:b] = y @@ -1429,7 +1429,7 @@ def long_slice(): check_op_count(long_slice, "BUILD_SLICE", 1) check_op_count(long_slice, "BINARY_SLICE", 0) check_consts(long_slice, slice, []) - check_op_count(long_slice, "BINARY_SUBSCR", 1) + check_op_count(long_slice, "BINARY_OP", 1) def aug(): x[a:b] += y @@ -1437,7 +1437,7 @@ def aug(): check_op_count(aug, "BINARY_SLICE", 1) check_op_count(aug, "STORE_SLICE", 1) check_op_count(aug, "BUILD_SLICE", 0) - check_op_count(aug, "BINARY_SUBSCR", 0) + check_op_count(aug, "BINARY_OP", 1) check_op_count(aug, "STORE_SUBSCR", 0) check_consts(aug, slice, []) @@ -1446,7 +1446,7 @@ def aug_const(): check_op_count(aug_const, "BINARY_SLICE", 0) check_op_count(aug_const, "STORE_SLICE", 0) - check_op_count(aug_const, "BINARY_SUBSCR", 1) + check_op_count(aug_const, "BINARY_OP", 2) check_op_count(aug_const, "STORE_SUBSCR", 1) check_consts(aug_const, slice, [slice(1, 2)]) @@ -2050,16 +2050,16 @@ def test_complex_single_line_expression(self): snippet = "a - b @ (c * x['key'] + 23)" compiled_code, _ = self.check_positions_against_ast(snippet) - self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_SUBSCR', + self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', line=1, end_line=1, column=13, end_column=21) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=9, end_column=21, occurrence=1) + line=1, end_line=1, column=9, end_column=21, occurrence=2) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=9, end_column=26, occurrence=2) + line=1, end_line=1, column=9, end_column=26, occurrence=3) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=4, end_column=27, occurrence=3) + line=1, end_line=1, column=4, end_column=27, occurrence=4) self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP', - line=1, end_line=1, column=0, end_column=27, occurrence=4) + line=1, end_line=1, column=0, end_column=27, occurrence=5) def test_multiline_assert_rewritten_as_method_call(self): # GH-94694: Don't crash if pytest rewrites a multiline assert as a diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 0b273cbd63e21e..27350120d667c2 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1706,211 +1706,211 @@ def _prepare_test_cases(): Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=51, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_FUNCTION', opcode=24, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_LIST', opcode=46, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_MAP', opcode=47, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=50, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_LIST', opcode=45, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_MAP', opcode=46, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), ] expected_opinfo_f = [ - Instruction(opname='COPY_FREE_VARS', opcode=60, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_CELL', opcode=94, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=59, arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_CELL', opcode=93, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='BUILD_TUPLE', opcode=51, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='MAKE_FUNCTION', opcode=24, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=105, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='STORE_FAST', opcode=109, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='BUILD_TUPLE', opcode=50, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=104, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='STORE_FAST', opcode=108, arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6, label=None, positions=None, cache_info=None), ] expected_opinfo_inner = [ - Instruction(opname='COPY_FREE_VARS', opcode=60, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='COPY_FREE_VARS', opcode=59, arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_DEREF', opcode=82, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_DEREF', opcode=82, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=86, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_DEREF', opcode=81, arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_DEREF', opcode=81, arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_LOAD_FAST', opcode=85, arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), ] expected_opinfo_jumpy = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='GET_ITER', opcode=18, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='FOR_ITER', opcode=69, arg=32, argval=92, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=70, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=64, start_offset=64, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=23, argval=24, argrepr='to L1', offset=66, start_offset=66, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=70, start_offset=70, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=74, start_offset=74, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=88, argrepr='to L3', offset=78, start_offset=78, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=82, start_offset=82, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=32, argval=24, argrepr='to L1', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=8, label=3, positions=None, cache_info=None), - Instruction(opname='JUMP_FORWARD', opcode=76, arg=13, argval=118, argrepr='to L5', offset=90, start_offset=90, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), - Instruction(opname='END_FOR', opcode=10, arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), - Instruction(opname='POP_ITER', opcode=31, arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=96, start_offset=96, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=106, start_offset=106, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=108, start_offset=108, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=85, arg=0, argval='i', argrepr='i', offset=118, start_offset=118, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=40, argval=212, argrepr='to L8', offset=128, start_offset=128, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=134, start_offset=134, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=144, start_offset=144, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=154, start_offset=154, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=45, arg=23, argval=23, argrepr='-=', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=0, argval='i', argrepr='i', offset=172, start_offset=172, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=6, argval=6, argrepr='', offset=176, start_offset=176, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=148, argval='>', argrepr='bool(>)', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=3, argval=192, argrepr='to L6', offset=182, start_offset=182, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=186, start_offset=186, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=37, argval=118, argrepr='to L5', offset=188, start_offset=188, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=192, start_offset=192, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=4, argval=4, argrepr='', offset=194, start_offset=194, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='COMPARE_OP', opcode=56, arg=18, argval='<', argrepr='bool(<)', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=3, argval=210, argrepr='to L7', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=204, start_offset=204, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD', opcode=74, arg=46, argval=118, argrepr='to L5', offset=206, start_offset=206, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='JUMP_FORWARD', opcode=76, arg=11, argval=234, argrepr='to L9', offset=210, start_offset=210, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=212, start_offset=212, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=222, start_offset=222, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=224, start_offset=224, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=232, start_offset=232, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), - Instruction(opname='NOP', opcode=28, arg=None, argval=None, argrepr='', offset=234, start_offset=234, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=1, argval=1, argrepr='', offset=236, start_offset=236, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SMALL_INT', opcode=91, arg=0, argval=0, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='BINARY_OP', opcode=45, arg=11, argval=11, argrepr='/', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=252, start_offset=252, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_FAST', opcode=83, arg=0, argval='i', argrepr='i', offset=254, start_offset=254, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=1, argval=1, argrepr='', offset=256, start_offset=256, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=1, argval=1, argrepr='__exit__', offset=258, start_offset=258, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=2, argval=2, argrepr='', offset=260, start_offset=260, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='SWAP', opcode=114, arg=3, argval=3, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_SPECIAL', opcode=92, arg=0, argval=0, argrepr='__enter__', offset=264, start_offset=264, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=0, argval=0, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='STORE_FAST', opcode=109, arg=1, argval='dodgy', argrepr='dodgy', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=276, start_offset=276, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=286, start_offset=286, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=288, start_offset=288, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=298, start_offset=298, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=302, start_offset=302, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=3, argval=3, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=314, start_offset=314, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=3, argval=None, argrepr='None', offset=336, start_offset=336, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=338, start_offset=338, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='WITH_EXCEPT_START', opcode=44, arg=None, argval=None, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='TO_BOOL', opcode=40, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=100, arg=2, argval=360, argrepr='to L11', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=2, argval=2, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=30, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=29, argval=314, argrepr='to L10', offset=370, start_offset=370, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=372, start_offset=372, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=30, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=380, start_offset=380, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='CHECK_EXC_MATCH', opcode=6, arg=None, argval=None, argrepr='', offset=390, start_offset=390, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=97, arg=15, argval=426, argrepr='to L12', offset=392, start_offset=392, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), - Instruction(opname='NOT_TAKEN', opcode=29, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=400, start_offset=400, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=30, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=75, arg=56, argval=314, argrepr='to L10', offset=424, start_offset=424, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=428, start_offset=428, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=30, arg=None, argval=None, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='PUSH_EXC_INFO', opcode=33, arg=None, argval=None, argrepr='', offset=434, start_offset=434, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='LOAD_GLOBAL', opcode=89, arg=3, argval='print', argrepr='print + NULL', offset=436, start_offset=436, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - Instruction(opname='LOAD_CONST', opcode=81, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='CALL', opcode=52, arg=1, argval=1, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - Instruction(opname='POP_TOP', opcode=32, arg=None, argval=None, argrepr='', offset=456, start_offset=456, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=0, argval=0, argrepr='', offset=458, start_offset=458, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), - Instruction(opname='COPY', opcode=59, arg=3, argval=3, argrepr='', offset=460, start_offset=460, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='POP_EXCEPT', opcode=30, arg=None, argval=None, argrepr='', offset=462, start_offset=462, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), - Instruction(opname='RERAISE', opcode=102, arg=1, argval=1, argrepr='', offset=464, start_offset=464, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='GET_ITER', opcode=16, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='FOR_ITER', opcode=68, arg=32, argval=92, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=70, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=64, start_offset=64, starts_line=False, line_number=5, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=23, argval=24, argrepr='to L1', offset=66, start_offset=66, starts_line=True, line_number=6, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=70, start_offset=70, starts_line=True, line_number=7, label=2, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=72, start_offset=72, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=74, start_offset=74, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=88, argrepr='to L3', offset=78, start_offset=78, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=82, start_offset=82, starts_line=False, line_number=7, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=32, argval=24, argrepr='to L1', offset=84, start_offset=84, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=8, label=3, positions=None, cache_info=None), + Instruction(opname='JUMP_FORWARD', opcode=75, arg=13, argval=118, argrepr='to L5', offset=90, start_offset=90, starts_line=False, line_number=8, label=None, positions=None, cache_info=None), + Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=3, label=4, positions=None, cache_info=None), + Instruction(opname='POP_ITER', opcode=30, arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=False, line_number=3, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=96, start_offset=96, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=106, start_offset=106, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=108, start_offset=108, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=116, start_offset=116, starts_line=False, line_number=10, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=84, arg=0, argval='i', argrepr='i', offset=118, start_offset=118, starts_line=True, line_number=11, label=5, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=120, start_offset=120, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=40, argval=212, argrepr='to L8', offset=128, start_offset=128, starts_line=False, line_number=11, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=132, start_offset=132, starts_line=False, line_number=11, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=134, start_offset=134, starts_line=True, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=144, start_offset=144, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=154, start_offset=154, starts_line=False, line_number=12, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=True, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=158, start_offset=158, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=23, argval=23, argrepr='-=', offset=160, start_offset=160, starts_line=False, line_number=13, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=108, arg=0, argval='i', argrepr='i', offset=172, start_offset=172, starts_line=False, line_number=13, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=6, argval=6, argrepr='', offset=176, start_offset=176, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=148, argval='>', argrepr='bool(>)', offset=178, start_offset=178, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=3, argval=192, argrepr='to L6', offset=182, start_offset=182, starts_line=False, line_number=14, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=186, start_offset=186, starts_line=False, line_number=14, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=37, argval=118, argrepr='to L5', offset=188, start_offset=188, starts_line=True, line_number=15, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=192, start_offset=192, starts_line=True, line_number=16, label=6, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=4, argval=4, argrepr='', offset=194, start_offset=194, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='COMPARE_OP', opcode=55, arg=18, argval='<', argrepr='bool(<)', offset=196, start_offset=196, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=3, argval=210, argrepr='to L7', offset=200, start_offset=200, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=204, start_offset=204, starts_line=False, line_number=16, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD', opcode=73, arg=46, argval=118, argrepr='to L5', offset=206, start_offset=206, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='JUMP_FORWARD', opcode=75, arg=11, argval=234, argrepr='to L9', offset=210, start_offset=210, starts_line=True, line_number=17, label=7, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=212, start_offset=212, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=222, start_offset=222, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=224, start_offset=224, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=232, start_offset=232, starts_line=False, line_number=19, label=None, positions=None, cache_info=None), + Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=234, start_offset=234, starts_line=True, line_number=20, label=9, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=1, argval=1, argrepr='', offset=236, start_offset=236, starts_line=True, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SMALL_INT', opcode=90, arg=0, argval=0, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='BINARY_OP', opcode=44, arg=11, argval=11, argrepr='/', offset=240, start_offset=240, starts_line=False, line_number=21, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=252, start_offset=252, starts_line=False, line_number=21, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_FAST', opcode=82, arg=0, argval='i', argrepr='i', offset=254, start_offset=254, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=1, argval=1, argrepr='', offset=256, start_offset=256, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=91, arg=1, argval=1, argrepr='__exit__', offset=258, start_offset=258, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=113, arg=2, argval=2, argrepr='', offset=260, start_offset=260, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='SWAP', opcode=113, arg=3, argval=3, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_SPECIAL', opcode=91, arg=0, argval=0, argrepr='__enter__', offset=264, start_offset=264, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=0, argval=0, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='STORE_FAST', opcode=108, arg=1, argval='dodgy', argrepr='dodgy', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=276, start_offset=276, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=286, start_offset=286, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=288, start_offset=288, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=26, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=298, start_offset=298, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=300, start_offset=300, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=302, start_offset=302, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=3, argval=3, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=314, start_offset=314, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=324, start_offset=324, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=334, start_offset=334, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=3, argval=None, argrepr='None', offset=336, start_offset=336, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=338, start_offset=338, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=True, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='WITH_EXCEPT_START', opcode=43, arg=None, argval=None, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='TO_BOOL', opcode=39, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=99, arg=2, argval=360, argrepr='to L11', offset=352, start_offset=352, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=2, argval=2, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=25, label=11, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=29, argval=314, argrepr='to L10', offset=370, start_offset=370, starts_line=False, line_number=25, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=372, start_offset=372, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=380, start_offset=380, starts_line=True, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='CHECK_EXC_MATCH', opcode=5, arg=None, argval=None, argrepr='', offset=390, start_offset=390, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=96, arg=15, argval=426, argrepr='to L12', offset=392, start_offset=392, starts_line=False, line_number=22, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]), + Instruction(opname='NOT_TAKEN', opcode=28, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=22, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=400, start_offset=400, starts_line=True, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=410, start_offset=410, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=23, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=420, start_offset=420, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='JUMP_BACKWARD_NO_INTERRUPT', opcode=74, arg=56, argval=314, argrepr='to L10', offset=424, start_offset=424, starts_line=False, line_number=23, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=426, start_offset=426, starts_line=True, line_number=22, label=12, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=428, start_offset=428, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='PUSH_EXC_INFO', opcode=32, arg=None, argval=None, argrepr='', offset=434, start_offset=434, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='LOAD_GLOBAL', opcode=88, arg=3, argval='print', argrepr='print + NULL', offset=436, start_offset=436, starts_line=True, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + Instruction(opname='LOAD_CONST', opcode=80, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=446, start_offset=446, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='CALL', opcode=51, arg=1, argval=1, argrepr='', offset=448, start_offset=448, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + Instruction(opname='POP_TOP', opcode=31, arg=None, argval=None, argrepr='', offset=456, start_offset=456, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=0, argval=0, argrepr='', offset=458, start_offset=458, starts_line=False, line_number=28, label=None, positions=None, cache_info=None), + Instruction(opname='COPY', opcode=58, arg=3, argval=3, argrepr='', offset=460, start_offset=460, starts_line=True, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='POP_EXCEPT', opcode=29, arg=None, argval=None, argrepr='', offset=462, start_offset=462, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), + Instruction(opname='RERAISE', opcode=101, arg=1, argval=1, argrepr='', offset=464, start_offset=464, starts_line=False, line_number=None, label=None, positions=None, cache_info=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno, label=None, positions=None), - Instruction(opname='LOAD_CONST', opcode=81, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), - Instruction(opname='RETURN_VALUE', opcode=36, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='LOAD_CONST', opcode=80, arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), + Instruction(opname='RETURN_VALUE', opcode=35, arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno, label=None), ] diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 87de4c94ba26fb..e4224b843b2e00 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -629,7 +629,7 @@ def write(items): pass type(item).__getitem__ = lambda self, item: None - opname = "BINARY_SUBSCR_GETITEM" + opname = "BINARY_OP_SUBSCR_GETITEM" self.assert_races_do_not_crash(opname, get_items, read, write) @requires_specialization_ft @@ -653,7 +653,7 @@ def write(items): item.clear() item.append(None) - opname = "BINARY_SUBSCR_LIST_INT" + opname = "BINARY_OP_SUBSCR_LIST_INT" self.assert_races_do_not_crash(opname, get_items, read, write) @requires_specialization @@ -1705,7 +1705,7 @@ def binary_subscr_list_int(): binary_subscr_list_int() self.assert_specialized(binary_subscr_list_int, - "BINARY_SUBSCR_LIST_INT") + "BINARY_OP_SUBSCR_LIST_INT") self.assert_no_opcode(binary_subscr_list_int, "BINARY_SUBSCR") def binary_subscr_tuple_int(): @@ -1716,7 +1716,7 @@ def binary_subscr_tuple_int(): binary_subscr_tuple_int() self.assert_specialized(binary_subscr_tuple_int, - "BINARY_SUBSCR_TUPLE_INT") + "BINARY_OP_SUBSCR_TUPLE_INT") self.assert_no_opcode(binary_subscr_tuple_int, "BINARY_SUBSCR") def binary_subscr_dict(): @@ -1726,8 +1726,8 @@ def binary_subscr_dict(): self.assertEqual(a[2], 3) binary_subscr_dict() - self.assert_specialized(binary_subscr_dict, "BINARY_SUBSCR_DICT") - self.assert_no_opcode(binary_subscr_dict, "BINARY_SUBSCR") + self.assert_specialized(binary_subscr_dict, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_dict, "BINARY_OP") def binary_subscr_str_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1736,7 +1736,7 @@ def binary_subscr_str_int(): self.assertEqual(a[idx], expected) binary_subscr_str_int() - self.assert_specialized(binary_subscr_str_int, "BINARY_SUBSCR_STR_INT") + self.assert_specialized(binary_subscr_str_int, "BINARY_OP_SUBSCR_STR_INT") self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR") def binary_subscr_getitems(): @@ -1751,7 +1751,7 @@ def __getitem__(self, item): self.assertEqual(items[i][i], i) binary_subscr_getitems() - self.assert_specialized(binary_subscr_getitems, "BINARY_SUBSCR_GETITEM") + self.assert_specialized(binary_subscr_getitems, "BINARY_OP_SUBSCR_GETITEM") self.assert_no_opcode(binary_subscr_getitems, "BINARY_SUBSCR") @cpython_only diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 9f2f9350d74661..a9e55a1af51648 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,5 +1,6 @@ import dis from itertools import combinations, product +import opcode import sys import textwrap import unittest @@ -280,23 +281,23 @@ def test_binary_subscr_on_unicode(self): # valid code get optimized code = compile('"foo"[0]', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 'f') - self.assertNotInBytecode(code, 'BINARY_SUBSCR') + self.assertNotInBytecode(code, 'BINARY_OP') self.check_lnotab(code) code = compile('"\u0061\uffff"[1]', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', '\uffff') - self.assertNotInBytecode(code,'BINARY_SUBSCR') + self.assertNotInBytecode(code,'BINARY_OP') self.check_lnotab(code) # With PEP 393, non-BMP char get optimized code = compile('"\U00012345"[0]', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', '\U00012345') - self.assertNotInBytecode(code, 'BINARY_SUBSCR') + self.assertNotInBytecode(code, 'BINARY_OP') self.check_lnotab(code) # invalid code doesn't get optimized # out of range code = compile('"fuu"[10]', '', 'single') - self.assertInBytecode(code, 'BINARY_SUBSCR') + self.assertInBytecode(code, 'BINARY_OP') self.check_lnotab(code) def test_folding_of_unaryops_on_constants(self): @@ -517,13 +518,15 @@ def test_folding_subscript(self): ('("a" * 10)[10]', True), ('(1, (1, 2))[2:6][0][2-1]', True), ] + subscr_argval = 26 + assert opcode._nb_ops[subscr_argval][0] == 'NB_SUBSCR' for expr, has_error in tests: with self.subTest(expr=expr, has_error=has_error): code = compile(expr, '', 'single') if not has_error: - self.assertNotInBytecode(code, 'BINARY_SUBSCR') + self.assertNotInBytecode(code, 'BINARY_OP', argval=subscr_argval) else: - self.assertInBytecode(code, 'BINARY_SUBSCR') + self.assertInBytecode(code, 'BINARY_OP', argval=subscr_argval) self.check_lnotab(code) def test_in_literal_list(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-17-06-39.gh-issue-100239.WvBTPL.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-17-06-39.gh-issue-100239.WvBTPL.rst new file mode 100644 index 00000000000000..428111cff1bc79 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-07-17-06-39.gh-issue-100239.WvBTPL.rst @@ -0,0 +1 @@ +Replace the opcode BINARY_SUBSCR and its family by BINARY_OP with oparg NB_SUBSCR. diff --git a/Modules/_opcode.c b/Modules/_opcode.c index 7ccf7af6bf908f..c295f7b3152577 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -274,6 +274,7 @@ _opcode_get_nb_ops_impl(PyObject *module) ADD_NB_OP(NB_INPLACE_SUBTRACT, "-="); ADD_NB_OP(NB_INPLACE_TRUE_DIVIDE, "/="); ADD_NB_OP(NB_INPLACE_XOR, "^="); + ADD_NB_OP(NB_SUBSCR, "[]"); #undef ADD_NB_OP diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 1442434f9eba26..0fe8d3d3f7d8c6 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,18 +1,19 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, - 0,0,0,0,0,243,168,0,0,0,149,0,91,0,81,0, - 72,0,113,0,91,0,81,0,72,1,113,1,90,2,34,0, - 81,1,52,1,0,0,0,0,0,0,32,0,90,2,34,0, - 81,2,90,0,79,6,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,52,2,0,0,0,0,0,0, - 32,0,90,1,79,8,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,34,0,52,0,0,0,0,0, - 0,0,81,3,2,0,0,0,113,5,81,4,18,0,69,20, - 0,0,113,6,90,2,34,0,81,5,90,6,13,0,81,6, - 90,5,90,6,2,0,0,0,13,0,50,4,52,1,0,0, - 0,0,0,0,32,0,74,22,0,0,10,0,31,0,81,0, - 36,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101, + 0,0,0,0,0,243,184,0,0,0,149,0,90,0,80,0, + 71,0,112,0,90,0,80,0,71,1,112,1,89,2,33,0, + 80,1,51,1,0,0,0,0,0,0,31,0,89,2,33,0, + 80,2,89,0,78,6,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,51,2,0,0,0,0,0,0, + 31,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,33,0,51,0,0,0,0,0, + 0,0,80,3,44,26,0,0,0,0,0,0,0,0,0,0, + 112,5,80,4,16,0,68,24,0,0,112,6,89,2,33,0, + 80,5,89,6,12,0,80,6,89,5,89,6,44,26,0,0, + 0,0,0,0,0,0,0,0,12,0,49,4,51,1,0,0, + 0,0,0,0,31,0,73,26,0,0,9,0,30,0,80,0, + 35,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101, 108,108,111,32,87,111,114,108,100,122,8,115,121,115,46,97, 114,103,118,218,6,99,111,110,102,105,103,41,5,218,12,112, 114,111,103,114,97,109,95,110,97,109,101,218,10,101,120,101, @@ -30,8 +31,8 @@ unsigned char M_test_frozenmain[] = { 1,0,0,0,115,94,0,0,0,240,3,1,1,1,243,8, 0,1,11,219,0,24,225,0,5,208,6,26,212,0,27,217, 0,5,128,106,144,35,151,40,145,40,212,0,27,216,9,26, - 215,9,38,210,9,38,211,9,40,168,24,209,9,50,128,6, + 215,9,38,210,9,38,211,9,40,168,24,213,9,50,128,6, 243,2,6,12,2,128,67,241,14,0,5,10,136,71,144,67, - 144,53,152,2,152,54,160,35,153,59,152,45,208,10,40,214, + 144,53,152,2,152,54,160,35,157,59,152,45,208,10,40,214, 4,41,243,15,6,12,2,114,15,0,0,0, }; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index bef120b64779d9..703d7ec61ebab3 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -537,6 +537,11 @@ dummy_func( BINARY_OP_ADD_FLOAT, BINARY_OP_SUBTRACT_FLOAT, BINARY_OP_ADD_UNICODE, + BINARY_OP_SUBSCR_LIST_INT, + BINARY_OP_SUBSCR_TUPLE_INT, + BINARY_OP_SUBSCR_STR_INT, + BINARY_OP_SUBSCR_DICT, + BINARY_OP_SUBSCR_GETITEM, // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. BINARY_OP_EXTEND, }; @@ -787,39 +792,6 @@ dummy_func( macro(BINARY_OP_INPLACE_ADD_UNICODE) = _GUARD_BOTH_UNICODE + unused/5 + _BINARY_OP_INPLACE_ADD_UNICODE; - family(BINARY_SUBSCR, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = { - BINARY_SUBSCR_DICT, - BINARY_SUBSCR_GETITEM, - BINARY_SUBSCR_LIST_INT, - BINARY_SUBSCR_STR_INT, - BINARY_SUBSCR_TUPLE_INT, - }; - - specializing op(_SPECIALIZE_BINARY_SUBSCR, (counter/1, container, sub -- container, sub)) { - #if ENABLE_SPECIALIZATION_FT - assert(frame->stackpointer == NULL); - if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { - next_instr = this_instr; - _Py_Specialize_BinarySubscr(container, sub, next_instr); - DISPATCH_SAME_OPARG(); - } - OPCODE_DEFERRED_INC(BINARY_SUBSCR); - ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ - } - - op(_BINARY_SUBSCR, (container, sub -- res)) { - PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); - PyObject *sub_o = PyStackRef_AsPyObjectBorrow(sub); - - PyObject *res_o = PyObject_GetItem(container_o, sub_o); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL, error); - res = PyStackRef_FromPyObjectSteal(res_o); - } - - macro(BINARY_SUBSCR) = _SPECIALIZE_BINARY_SUBSCR + _BINARY_SUBSCR; - specializing op(_SPECIALIZE_BINARY_SLICE, (container, start, stop -- container, start, stop)) { // Placeholder until we implement BINARY_SLICE specialization #if ENABLE_SPECIALIZATION @@ -871,7 +843,7 @@ dummy_func( macro(STORE_SLICE) = _SPECIALIZE_STORE_SLICE + _STORE_SLICE; - inst(BINARY_SUBSCR_LIST_INT, (unused/1, list_st, sub_st -- res)) { + inst(BINARY_OP_SUBSCR_LIST_INT, (unused/5, list_st, sub_st -- res)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); @@ -884,10 +856,10 @@ dummy_func( #ifdef Py_GIL_DISABLED PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); DEOPT_IF(res_o == NULL); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); #else DEOPT_IF(index >= PyList_GET_SIZE(list)); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -898,7 +870,7 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - inst(BINARY_SUBSCR_STR_INT, (unused/1, str_st, sub_st -- res)) { + inst(BINARY_OP_SUBSCR_STR_INT, (unused/5, str_st, sub_st -- res)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); @@ -910,7 +882,7 @@ dummy_func( // Specialize for reading an ASCII character from any string: Py_UCS4 c = PyUnicode_READ_CHAR(str, index); DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); DEAD(sub_st); @@ -918,7 +890,7 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - inst(BINARY_SUBSCR_TUPLE_INT, (unused/1, tuple_st, sub_st -- res)) { + inst(BINARY_OP_SUBSCR_TUPLE_INT, (unused/5, tuple_st, sub_st -- res)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); @@ -929,7 +901,7 @@ dummy_func( DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; DEOPT_IF(index >= PyTuple_GET_SIZE(tuple)); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -939,12 +911,12 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - inst(BINARY_SUBSCR_DICT, (unused/1, dict_st, sub_st -- res)) { + inst(BINARY_OP_SUBSCR_DICT, (unused/5, dict_st, sub_st -- res)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); DEOPT_IF(!PyDict_CheckExact(dict)); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o; int rc = PyDict_GetItemRef(dict, sub, &res_o); if (rc == 0) { @@ -955,7 +927,7 @@ dummy_func( res = PyStackRef_FromPyObjectSteal(res_o); } - op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) { + op(_BINARY_OP_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) { PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)); PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; @@ -968,10 +940,10 @@ dummy_func( assert(code->co_argcount == 2); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); } - op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _PyInterpreterFrame* )) { + op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _PyInterpreterFrame* )) { new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; @@ -979,11 +951,11 @@ dummy_func( frame->return_offset = INSTRUCTION_SIZE; } - macro(BINARY_SUBSCR_GETITEM) = - unused/1 + // Skip over the counter + macro(BINARY_OP_SUBSCR_GETITEM) = + unused/5 + // Skip over the counter and cache _CHECK_PEP_523 + - _BINARY_SUBSCR_CHECK_FUNC + - _BINARY_SUBSCR_INIT_CALL + + _BINARY_OP_SUBSCR_CHECK_FUNC + + _BINARY_OP_SUBSCR_INIT_CALL + _PUSH_FRAME; inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) { @@ -4816,7 +4788,7 @@ dummy_func( ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); #endif /* ENABLE_SPECIALIZATION_FT */ assert(NB_ADD <= oparg); - assert(oparg <= NB_INPLACE_XOR); + assert(oparg <= NB_OPARG_LAST); } op(_BINARY_OP, (lhs, rhs -- res)) { diff --git a/Python/ceval.c b/Python/ceval.c index 6c8e39a09c255b..1e4f1f3af20aca 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -362,6 +362,7 @@ const binaryfunc _PyEval_BinaryOps[] = { [NB_INPLACE_SUBTRACT] = PyNumber_InPlaceSubtract, [NB_INPLACE_TRUE_DIVIDE] = PyNumber_InPlaceTrueDivide, [NB_INPLACE_XOR] = PyNumber_InPlaceXor, + [NB_SUBSCR] = PyObject_GetItem, }; const conversion_func _PyEval_ConversionFuncs[4] = { diff --git a/Python/codegen.c b/Python/codegen.c index e9853d7302f67f..cd77b34c06296b 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -5076,7 +5076,7 @@ codegen_augassign(compiler *c, stmt_ty s) VISIT(c, expr, e->v.Subscript.slice); ADDOP_I(c, loc, COPY, 2); ADDOP_I(c, loc, COPY, 2); - ADDOP(c, loc, BINARY_SUBSCR); + ADDOP_I(c, loc, BINARY_OP, NB_SUBSCR); } break; case Name_kind: @@ -5242,7 +5242,6 @@ codegen_subscript(compiler *c, expr_ty e) { location loc = LOC(e); expr_context_ty ctx = e->v.Subscript.ctx; - int op = 0; if (ctx == Load) { RETURN_IF_ERROR(check_subscripter(c, e->v.Subscript.value)); @@ -5265,12 +5264,16 @@ codegen_subscript(compiler *c, expr_ty e) else { VISIT(c, expr, e->v.Subscript.slice); switch (ctx) { - case Load: op = BINARY_SUBSCR; break; - case Store: op = STORE_SUBSCR; break; - case Del: op = DELETE_SUBSCR; break; + case Load: + ADDOP_I(c, loc, BINARY_OP, NB_SUBSCR); + break; + case Store: + ADDOP(c, loc, STORE_SUBSCR); + break; + case Del: + ADDOP(c, loc, DELETE_SUBSCR); + break; } - assert(op); - ADDOP(c, loc, op); } return SUCCESS; } @@ -5502,7 +5505,7 @@ pattern_helper_sequence_unpack(compiler *c, location loc, return SUCCESS; } -// Like pattern_helper_sequence_unpack, but uses BINARY_SUBSCR instead of +// Like pattern_helper_sequence_unpack, but uses BINARY_OP/NB_SUBSCR instead of // UNPACK_SEQUENCE / UNPACK_EX. This is more efficient for patterns with a // starred wildcard like [first, *_] / [first, *_, last] / [*_, last] / etc. static int @@ -5533,7 +5536,7 @@ pattern_helper_sequence_subscr(compiler *c, location loc, ADDOP_LOAD_CONST_NEW(c, loc, PyLong_FromSsize_t(size - i)); ADDOP_BINARY(c, loc, Sub); } - ADDOP(c, loc, BINARY_SUBSCR); + ADDOP_I(c, loc, BINARY_OP, NB_SUBSCR); RETURN_IF_ERROR(codegen_pattern_subpattern(c, pattern, pc)); } // Pop the subject, we're done with it: diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index dc6572813212aa..96b7386bd24846 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1029,31 +1029,6 @@ break; } - case _BINARY_SUBSCR: { - _PyStackRef sub; - _PyStackRef container; - _PyStackRef res; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); - PyObject *sub_o = PyStackRef_AsPyObjectBorrow(sub); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_GetItem(container_o, sub_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(container); - PyStackRef_CLOSE(sub); - if (res_o == NULL) { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - case _BINARY_SLICE: { _PyStackRef stop; _PyStackRef start; @@ -1136,7 +1111,7 @@ break; } - case _BINARY_SUBSCR_LIST_INT: { + case _BINARY_OP_SUBSCR_LIST_INT: { _PyStackRef sub_st; _PyStackRef list_st; _PyStackRef res; @@ -1166,13 +1141,13 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); #else if (index >= PyList_GET_SIZE(list)) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -1190,7 +1165,7 @@ break; } - case _BINARY_SUBSCR_STR_INT: { + case _BINARY_OP_SUBSCR_STR_INT: { _PyStackRef sub_st; _PyStackRef str_st; _PyStackRef res; @@ -1221,7 +1196,7 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); stack_pointer += -2; @@ -1236,7 +1211,7 @@ break; } - case _BINARY_SUBSCR_TUPLE_INT: { + case _BINARY_OP_SUBSCR_TUPLE_INT: { _PyStackRef sub_st; _PyStackRef tuple_st; _PyStackRef res; @@ -1262,7 +1237,7 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -1279,7 +1254,7 @@ break; } - case _BINARY_SUBSCR_DICT: { + case _BINARY_OP_SUBSCR_DICT: { _PyStackRef sub_st; _PyStackRef dict_st; _PyStackRef res; @@ -1291,7 +1266,7 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o; _PyFrame_SetStackPointer(frame, stack_pointer); int rc = PyDict_GetItemRef(dict, sub, &res_o); @@ -1316,7 +1291,7 @@ break; } - case _BINARY_SUBSCR_CHECK_FUNC: { + case _BINARY_OP_SUBSCR_CHECK_FUNC: { _PyStackRef container; _PyStackRef getitem; container = stack_pointer[-2]; @@ -1344,14 +1319,14 @@ JUMP_TO_JUMP_TARGET(); } getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); stack_pointer[0] = getitem; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); break; } - case _BINARY_SUBSCR_INIT_CALL: { + case _BINARY_OP_SUBSCR_INIT_CALL: { _PyStackRef getitem; _PyStackRef sub; _PyStackRef container; @@ -1362,7 +1337,7 @@ new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; - frame->return_offset = 2 ; + frame->return_offset = 6 ; stack_pointer[-3].bits = (uintptr_t)new_frame; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 95ab53ce64301c..12eedc33e42621 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -2,6 +2,7 @@ #include #include "Python.h" +#include "opcode.h" #include "pycore_flowgraph.h" #include "pycore_compile.h" #include "pycore_intrinsics.h" @@ -1492,10 +1493,14 @@ newop_from_folded(PyObject *newconst, PyObject *consts, } static int -optimize_if_const_subscr(basicblock *bb, int n, PyObject *consts, PyObject *const_cache) +optimize_if_const_op(basicblock *bb, int n, PyObject *consts, PyObject *const_cache) { cfg_instr *subscr = &bb->b_instr[n]; - assert(subscr->i_opcode == BINARY_SUBSCR); + assert(subscr->i_opcode == BINARY_OP); + if (subscr->i_oparg != NB_SUBSCR) { + /* TODO: support other binary ops */ + return SUCCESS; + } cfg_instr *arg, *idx; if (!find_load_const_pair(bb, n-1, &arg, &idx)) { return SUCCESS; @@ -2033,8 +2038,8 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); } break; - case BINARY_SUBSCR: - RETURN_IF_ERROR(optimize_if_const_subscr(bb, i, consts, const_cache)); + case BINARY_OP: + RETURN_IF_ERROR(optimize_if_const_op(bb, i, consts, const_cache)); break; } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 914b06987ed64e..f02e13f5e3fbce 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -50,7 +50,7 @@ ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); #endif /* ENABLE_SPECIALIZATION_FT */ assert(NB_ADD <= oparg); - assert(oparg <= NB_INPLACE_XOR); + assert(oparg <= NB_OPARG_LAST); } /* Skip 4 cache entries */ // _BINARY_OP @@ -480,251 +480,31 @@ DISPATCH(); } - TARGET(BINARY_OP_SUBTRACT_FLOAT) { + TARGET(BINARY_OP_SUBSCR_DICT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_OP_SUBTRACT_FLOAT; + int opcode = BINARY_OP_SUBSCR_DICT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + INSTRUCTION_STATS(BINARY_OP_SUBSCR_DICT); static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); - _PyStackRef left; - _PyStackRef right; - _PyStackRef res; - // _GUARD_BOTH_FLOAT - { - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - if (!PyFloat_CheckExact(left_o)) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } - if (!PyFloat_CheckExact(right_o)) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } - } - /* Skip 5 cache entries */ - // _BINARY_OP_SUBTRACT_FLOAT - { - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (res_o == NULL) { - JUMP_TO_LABEL(pop_2_error); - } - res = PyStackRef_FromPyObjectSteal(res_o); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - - TARGET(BINARY_OP_SUBTRACT_INT) { - #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_OP_SUBTRACT_INT; - (void)(opcode); - #endif - _Py_CODEUNIT* const this_instr = next_instr; - (void)this_instr; - frame->instr_ptr = next_instr; - next_instr += 6; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); - _PyStackRef left; - _PyStackRef right; - _PyStackRef res; - // _GUARD_BOTH_INT - { - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - if (!PyLong_CheckExact(left_o)) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } - if (!PyLong_CheckExact(right_o)) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } - } - /* Skip 5 cache entries */ - // _BINARY_OP_SUBTRACT_INT - { - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyLong_CheckExact(left_o)); - assert(PyLong_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - if (res_o == NULL) { - JUMP_TO_LABEL(pop_2_error); - } - res = PyStackRef_FromPyObjectSteal(res_o); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - - TARGET(BINARY_SLICE) { - #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SLICE; - (void)(opcode); - #endif - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(BINARY_SLICE); - _PyStackRef container; - _PyStackRef start; - _PyStackRef stop; - _PyStackRef res; - // _SPECIALIZE_BINARY_SLICE - { - // Placeholder until we implement BINARY_SLICE specialization - #if ENABLE_SPECIALIZATION - OPCODE_DEFERRED_INC(BINARY_SLICE); - #endif /* ENABLE_SPECIALIZATION */ - } - // _BINARY_SLICE - { - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); - PyObject *res_o; - // Can't use ERROR_IF() here, because we haven't - // DECREF'ed container yet, and we still own slice. - if (slice == NULL) { - res_o = NULL; - } - else { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); - } - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); - } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - - TARGET(BINARY_SUBSCR) { - #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR; - (void)(opcode); - #endif - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR); - PREDICTED_BINARY_SUBSCR:; - _Py_CODEUNIT* const this_instr = next_instr - 2; - (void)this_instr; - _PyStackRef container; - _PyStackRef sub; - _PyStackRef res; - // _SPECIALIZE_BINARY_SUBSCR - { - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - uint16_t counter = read_u16(&this_instr[1].cache); - (void)counter; - #if ENABLE_SPECIALIZATION_FT - assert(frame->stackpointer == NULL); - if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { - next_instr = this_instr; - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_Specialize_BinarySubscr(container, sub, next_instr); - stack_pointer = _PyFrame_GetStackPointer(frame); - DISPATCH_SAME_OPARG(); - } - OPCODE_DEFERRED_INC(BINARY_SUBSCR); - ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ - } - // _BINARY_SUBSCR - { - PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); - PyObject *sub_o = PyStackRef_AsPyObjectBorrow(sub); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_GetItem(container_o, sub_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - PyStackRef_CLOSE(container); - PyStackRef_CLOSE(sub); - if (res_o == NULL) { - JUMP_TO_LABEL(pop_2_error); - } - res = PyStackRef_FromPyObjectSteal(res_o); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - - TARGET(BINARY_SUBSCR_DICT) { - #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR_DICT; - (void)(opcode); - #endif - _Py_CODEUNIT* const this_instr = next_instr; - (void)this_instr; - frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_DICT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); _PyStackRef dict_st; _PyStackRef sub_st; _PyStackRef res; - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ sub_st = stack_pointer[-1]; dict_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); if (!PyDict_CheckExact(dict)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o; _PyFrame_SetStackPointer(frame, stack_pointer); int rc = PyDict_GetItemRef(dict, sub, &res_o); @@ -747,70 +527,70 @@ DISPATCH(); } - TARGET(BINARY_SUBSCR_GETITEM) { + TARGET(BINARY_OP_SUBSCR_GETITEM) { #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR_GETITEM; + int opcode = BINARY_OP_SUBSCR_GETITEM; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_GETITEM); - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_GETITEM); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef container; _PyStackRef getitem; _PyStackRef sub; _PyInterpreterFrame *new_frame; - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ // _CHECK_PEP_523 { if (tstate->interp->eval_frame) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } } - // _BINARY_SUBSCR_CHECK_FUNC + // _BINARY_OP_SUBSCR_CHECK_FUNC { container = stack_pointer[-2]; PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); if (getitem_o == NULL) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } assert(PyFunction_Check(getitem_o)); uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); } - // _BINARY_SUBSCR_INIT_CALL + // _BINARY_OP_SUBSCR_INIT_CALL { sub = stack_pointer[-1]; new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); new_frame->localsplus[0] = container; new_frame->localsplus[1] = sub; - frame->return_offset = 2 ; + frame->return_offset = 6 ; } // _PUSH_FRAME { @@ -832,40 +612,40 @@ DISPATCH(); } - TARGET(BINARY_SUBSCR_LIST_INT) { + TARGET(BINARY_OP_SUBSCR_LIST_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR_LIST_INT; + int opcode = BINARY_OP_SUBSCR_LIST_INT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_LIST_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef list_st; _PyStackRef sub_st; _PyStackRef res; - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ sub_st = stack_pointer[-1]; list_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); if (!PyLong_CheckExact(sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } if (!PyList_CheckExact(list)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } // Deopt unless 0 <= sub < PyList_Size(list) if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; #ifdef Py_GIL_DISABLED @@ -873,18 +653,18 @@ PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); #else if (index >= PyList_GET_SIZE(list)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -902,54 +682,54 @@ DISPATCH(); } - TARGET(BINARY_SUBSCR_STR_INT) { + TARGET(BINARY_OP_SUBSCR_STR_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR_STR_INT; + int opcode = BINARY_OP_SUBSCR_STR_INT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_STR_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_STR_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef str_st; _PyStackRef sub_st; _PyStackRef res; - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ sub_st = stack_pointer[-1]; str_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); if (!PyLong_CheckExact(sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } if (!PyUnicode_CheckExact(str)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; if (PyUnicode_GET_LENGTH(str) <= index) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } // Specialize for reading an ASCII character from any string: Py_UCS4 c = PyUnicode_READ_CHAR(str, index); if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); stack_pointer += -2; @@ -964,48 +744,48 @@ DISPATCH(); } - TARGET(BINARY_SUBSCR_TUPLE_INT) { + TARGET(BINARY_OP_SUBSCR_TUPLE_INT) { #if defined(Py_TAIL_CALL_INTERP) - int opcode = BINARY_SUBSCR_TUPLE_INT; + int opcode = BINARY_OP_SUBSCR_TUPLE_INT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 2; - INSTRUCTION_STATS(BINARY_SUBSCR_TUPLE_INT); - static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 1, "incorrect cache size"); + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_TUPLE_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef tuple_st; _PyStackRef sub_st; _PyStackRef res; - /* Skip 1 cache entry */ + /* Skip 5 cache entries */ sub_st = stack_pointer[-1]; tuple_st = stack_pointer[-2]; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); if (!PyLong_CheckExact(sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } if (!PyTuple_CheckExact(tuple)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } // Deopt unless 0 <= sub < PyTuple_Size(list) if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; if (index >= PyTuple_GET_SIZE(tuple)) { - UPDATE_MISS_STATS(BINARY_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (BINARY_SUBSCR)); - JUMP_TO_PREDICTED(BINARY_SUBSCR); + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_SUBSCR, hit); + STAT_INC(BINARY_OP, hit); PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); Py_INCREF(res_o); @@ -1022,6 +802,173 @@ DISPATCH(); } + TARGET(BINARY_OP_SUBTRACT_FLOAT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode = BINARY_OP_SUBTRACT_FLOAT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + // _GUARD_BOTH_FLOAT + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyFloat_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBTRACT_FLOAT + { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *res_o = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + if (res_o == NULL) { + JUMP_TO_LABEL(pop_2_error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBTRACT_INT) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode = BINARY_OP_SUBTRACT_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + // _GUARD_BOTH_INT + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + if (!PyLong_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyLong_CheckExact(right_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBTRACT_INT + { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); + PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + if (res_o == NULL) { + JUMP_TO_LABEL(pop_2_error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-2] = res; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + + TARGET(BINARY_SLICE) { + #if defined(Py_TAIL_CALL_INTERP) + int opcode = BINARY_SLICE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BINARY_SLICE); + _PyStackRef container; + _PyStackRef start; + _PyStackRef stop; + _PyStackRef res; + // _SPECIALIZE_BINARY_SLICE + { + // Placeholder until we implement BINARY_SLICE specialization + #if ENABLE_SPECIALIZATION + OPCODE_DEFERRED_INC(BINARY_SLICE); + #endif /* ENABLE_SPECIALIZATION */ + } + // _BINARY_SLICE + { + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), + PyStackRef_AsPyObjectSteal(stop)); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *res_o; + // Can't use ERROR_IF() here, because we haven't + // DECREF'ed container yet, and we still own slice. + if (slice == NULL) { + res_o = NULL; + } + else { + stack_pointer += -2; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += 2; + assert(WITHIN_STACK_BOUNDS()); + } + stack_pointer += -3; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(container); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[0] = res; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + DISPATCH(); + } + TARGET(BUILD_LIST) { #if defined(Py_TAIL_CALL_INTERP) int opcode = BUILD_LIST; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 27c4d537b8079b..039a6eee37939a 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -2,9 +2,8 @@ static void *opcode_targets[256] = { &&TARGET_CACHE, &&TARGET_BINARY_SLICE, - &&TARGET_BINARY_SUBSCR, - &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, &&TARGET_CALL_FUNCTION_EX, + &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, &&TARGET_CHECK_EG_MATCH, &&TARGET_CHECK_EXC_MATCH, &&TARGET_CLEANUP_THROW, @@ -17,8 +16,8 @@ static void *opcode_targets[256] = { &&TARGET_FORMAT_WITH_SPEC, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, - &&TARGET_RESERVED, &&TARGET_GET_ITER, + &&TARGET_RESERVED, &&TARGET_GET_LEN, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_INTERPRETER_EXIT, @@ -149,6 +148,7 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_RESUME, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, @@ -156,13 +156,13 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_OP_EXTEND, &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_BINARY_OP_MULTIPLY_INT, + &&TARGET_BINARY_OP_SUBSCR_DICT, + &&TARGET_BINARY_OP_SUBSCR_GETITEM, + &&TARGET_BINARY_OP_SUBSCR_LIST_INT, + &&TARGET_BINARY_OP_SUBSCR_STR_INT, + &&TARGET_BINARY_OP_SUBSCR_TUPLE_INT, &&TARGET_BINARY_OP_SUBTRACT_FLOAT, &&TARGET_BINARY_OP_SUBTRACT_INT, - &&TARGET_BINARY_SUBSCR_DICT, - &&TARGET_BINARY_SUBSCR_GETITEM, - &&TARGET_BINARY_SUBSCR_LIST_INT, - &&TARGET_BINARY_SUBSCR_STR_INT, - &&TARGET_BINARY_SUBSCR_TUPLE_INT, &&TARGET_CALL_ALLOC_AND_ENTER_INIT, &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, &&TARGET_CALL_BOUND_METHOD_GENERAL, @@ -277,15 +277,14 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_EXTEND(TAIL_CALL_PARAM Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_DICT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_GETITEM(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_STR_INT(TAIL_CALL_PARAMS); +Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_GETITEM(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_STR_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS); @@ -511,15 +510,14 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [BINARY_OP_INPLACE_ADD_UNICODE] = _TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE, [BINARY_OP_MULTIPLY_FLOAT] = _TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT, [BINARY_OP_MULTIPLY_INT] = _TAIL_CALL_BINARY_OP_MULTIPLY_INT, + [BINARY_OP_SUBSCR_DICT] = _TAIL_CALL_BINARY_OP_SUBSCR_DICT, + [BINARY_OP_SUBSCR_GETITEM] = _TAIL_CALL_BINARY_OP_SUBSCR_GETITEM, + [BINARY_OP_SUBSCR_LIST_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT, + [BINARY_OP_SUBSCR_STR_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_STR_INT, + [BINARY_OP_SUBSCR_TUPLE_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT, [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT, [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_BINARY_OP_SUBTRACT_INT, [BINARY_SLICE] = _TAIL_CALL_BINARY_SLICE, - [BINARY_SUBSCR] = _TAIL_CALL_BINARY_SUBSCR, - [BINARY_SUBSCR_DICT] = _TAIL_CALL_BINARY_SUBSCR_DICT, - [BINARY_SUBSCR_GETITEM] = _TAIL_CALL_BINARY_SUBSCR_GETITEM, - [BINARY_SUBSCR_LIST_INT] = _TAIL_CALL_BINARY_SUBSCR_LIST_INT, - [BINARY_SUBSCR_STR_INT] = _TAIL_CALL_BINARY_SUBSCR_STR_INT, - [BINARY_SUBSCR_TUPLE_INT] = _TAIL_CALL_BINARY_SUBSCR_TUPLE_INT, [BUILD_LIST] = _TAIL_CALL_BUILD_LIST, [BUILD_MAP] = _TAIL_CALL_BUILD_MAP, [BUILD_SET] = _TAIL_CALL_BUILD_SET, @@ -725,6 +723,7 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE, [WITH_EXCEPT_START] = _TAIL_CALL_WITH_EXCEPT_START, [YIELD_VALUE] = _TAIL_CALL_YIELD_VALUE, + [117] = _TAIL_CALL_UNKNOWN_OPCODE, [118] = _TAIL_CALL_UNKNOWN_OPCODE, [119] = _TAIL_CALL_UNKNOWN_OPCODE, [120] = _TAIL_CALL_UNKNOWN_OPCODE, diff --git a/Python/optimizer.c b/Python/optimizer.c index 340770ae55ec57..bef5728349a612 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -753,7 +753,7 @@ translate_bytecode_to_trace( assert(i + 1 == nuops); if (opcode == FOR_ITER_GEN || opcode == LOAD_ATTR_PROPERTY || - opcode == BINARY_SUBSCR_GETITEM || + opcode == BINARY_OP_SUBSCR_GETITEM || opcode == SEND_GEN) { DPRINTF(2, "Bailing due to dynamic target\n"); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 09f1915bb3a5e0..41eb59c931aaa7 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -384,7 +384,7 @@ dummy_func(void) { GETLOCAL(this_instr->operand0) = res; } - op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) { + op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) { new_frame = NULL; ctx->done = true; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 2383be8ea3086e..fd8486785ed8db 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -575,15 +575,6 @@ break; } - case _BINARY_SUBSCR: { - JitOptSymbol *res; - res = sym_new_not_null(ctx); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - case _BINARY_SLICE: { JitOptSymbol *res; res = sym_new_not_null(ctx); @@ -599,7 +590,7 @@ break; } - case _BINARY_SUBSCR_LIST_INT: { + case _BINARY_OP_SUBSCR_LIST_INT: { JitOptSymbol *res; res = sym_new_not_null(ctx); stack_pointer[-2] = res; @@ -608,7 +599,7 @@ break; } - case _BINARY_SUBSCR_STR_INT: { + case _BINARY_OP_SUBSCR_STR_INT: { JitOptSymbol *res; res = sym_new_not_null(ctx); stack_pointer[-2] = res; @@ -617,7 +608,7 @@ break; } - case _BINARY_SUBSCR_TUPLE_INT: { + case _BINARY_OP_SUBSCR_TUPLE_INT: { JitOptSymbol *res; res = sym_new_not_null(ctx); stack_pointer[-2] = res; @@ -626,7 +617,7 @@ break; } - case _BINARY_SUBSCR_DICT: { + case _BINARY_OP_SUBSCR_DICT: { JitOptSymbol *res; res = sym_new_not_null(ctx); stack_pointer[-2] = res; @@ -635,7 +626,7 @@ break; } - case _BINARY_SUBSCR_CHECK_FUNC: { + case _BINARY_OP_SUBSCR_CHECK_FUNC: { JitOptSymbol *getitem; getitem = sym_new_not_null(ctx); stack_pointer[0] = getitem; @@ -644,7 +635,7 @@ break; } - case _BINARY_SUBSCR_INIT_CALL: { + case _BINARY_OP_SUBSCR_INIT_CALL: { _Py_UOpsAbstractFrame *new_frame; new_frame = NULL; ctx->done = true; diff --git a/Python/specialize.c b/Python/specialize.c index 4f84b2970ba98a..c741c4f93f3138 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -113,7 +113,6 @@ _Py_GetSpecializationStats(void) { err += add_stat_dict(stats, LOAD_SUPER_ATTR, "load_super_attr"); err += add_stat_dict(stats, LOAD_ATTR, "load_attr"); err += add_stat_dict(stats, LOAD_GLOBAL, "load_global"); - err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr"); err += add_stat_dict(stats, STORE_SUBSCR, "store_subscr"); err += add_stat_dict(stats, STORE_ATTR, "store_attr"); err += add_stat_dict(stats, CALL, "call"); @@ -553,11 +552,8 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_SUBSCR_ARRAY_INT 9 #define SPEC_FAIL_SUBSCR_ARRAY_SLICE 10 #define SPEC_FAIL_SUBSCR_LIST_SLICE 11 -#define SPEC_FAIL_SUBSCR_TUPLE_SLICE 12 -#define SPEC_FAIL_SUBSCR_STRING_SLICE 14 -#define SPEC_FAIL_SUBSCR_BUFFER_INT 15 -#define SPEC_FAIL_SUBSCR_BUFFER_SLICE 16 -#define SPEC_FAIL_SUBSCR_SEQUENCE_INT 17 +#define SPEC_FAIL_SUBSCR_BUFFER_INT 12 +#define SPEC_FAIL_SUBSCR_BUFFER_SLICE 13 /* Store subscr */ #define SPEC_FAIL_SUBSCR_BYTEARRAY_INT 18 @@ -593,6 +589,11 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_BINARY_OP_OR_DIFFERENT_TYPES 30 #define SPEC_FAIL_BINARY_OP_XOR_INT 31 #define SPEC_FAIL_BINARY_OP_XOR_DIFFERENT_TYPES 32 +#define SPEC_FAIL_BINARY_OP_SUBSCR 33 +#define SPEC_FAIL_BINARY_OP_SUBSCR_LIST_SLICE 34 +#define SPEC_FAIL_BINARY_OP_SUBSCR_TUPLE_SLICE 35 +#define SPEC_FAIL_BINARY_OP_SUBSCR_STRING_SLICE 36 +#define SPEC_FAIL_BINARY_OP_SUBSCR_NOT_HEAP_TYPE 37 /* Calls */ @@ -1761,37 +1762,6 @@ _Py_Specialize_LoadGlobal( Py_END_CRITICAL_SECTION2(); } -#ifdef Py_STATS -static int -binary_subscr_fail_kind(PyTypeObject *container_type, PyObject *sub) -{ - if (strcmp(container_type->tp_name, "array.array") == 0) { - if (PyLong_CheckExact(sub)) { - return SPEC_FAIL_SUBSCR_ARRAY_INT; - } - if (PySlice_Check(sub)) { - return SPEC_FAIL_SUBSCR_ARRAY_SLICE; - } - return SPEC_FAIL_OTHER; - } - else if (container_type->tp_as_buffer) { - if (PyLong_CheckExact(sub)) { - return SPEC_FAIL_SUBSCR_BUFFER_INT; - } - if (PySlice_Check(sub)) { - return SPEC_FAIL_SUBSCR_BUFFER_SLICE; - } - return SPEC_FAIL_OTHER; - } - else if (container_type->tp_as_sequence) { - if (PyLong_CheckExact(sub) && container_type->tp_as_sequence->sq_item) { - return SPEC_FAIL_SUBSCR_SEQUENCE_INT; - } - } - return SPEC_FAIL_OTHER; -} -#endif // Py_STATS - static int function_kind(PyCodeObject *code) { int flags = code->co_flags; @@ -1837,107 +1807,6 @@ function_get_version(PyObject *o, int opcode) return version; } -void -_Py_Specialize_BinarySubscr( - _PyStackRef container_st, _PyStackRef sub_st, _Py_CODEUNIT *instr) -{ - PyObject *container = PyStackRef_AsPyObjectBorrow(container_st); - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - - assert(ENABLE_SPECIALIZATION_FT); - assert(_PyOpcode_Caches[BINARY_SUBSCR] == - INLINE_CACHE_ENTRIES_BINARY_SUBSCR); - PyTypeObject *container_type = Py_TYPE(container); - uint8_t specialized_op; - if (container_type == &PyList_Type) { - if (PyLong_CheckExact(sub)) { - if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - specialized_op = BINARY_SUBSCR_LIST_INT; - goto success; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); - goto fail; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, - PySlice_Check(sub) ? SPEC_FAIL_SUBSCR_LIST_SLICE : SPEC_FAIL_OTHER); - goto fail; - } - if (container_type == &PyTuple_Type) { - if (PyLong_CheckExact(sub)) { - if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - specialized_op = BINARY_SUBSCR_TUPLE_INT; - goto success; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); - goto fail; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, - PySlice_Check(sub) ? SPEC_FAIL_SUBSCR_TUPLE_SLICE : SPEC_FAIL_OTHER); - goto fail; - } - if (container_type == &PyUnicode_Type) { - if (PyLong_CheckExact(sub)) { - if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - specialized_op = BINARY_SUBSCR_STR_INT; - goto success; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_RANGE); - goto fail; - } - SPECIALIZATION_FAIL(BINARY_SUBSCR, - PySlice_Check(sub) ? SPEC_FAIL_SUBSCR_STRING_SLICE : SPEC_FAIL_OTHER); - goto fail; - } - if (container_type == &PyDict_Type) { - specialized_op = BINARY_SUBSCR_DICT; - goto success; - } - unsigned int tp_version; - PyObject *descriptor = _PyType_LookupRefAndVersion(container_type, &_Py_ID(__getitem__), &tp_version); - if (descriptor && Py_TYPE(descriptor) == &PyFunction_Type) { - if (!(container_type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_SUBSCR_NOT_HEAP_TYPE); - Py_DECREF(descriptor); - goto fail; - } - PyFunctionObject *func = (PyFunctionObject *)descriptor; - PyCodeObject *fcode = (PyCodeObject *)func->func_code; - int kind = function_kind(fcode); - if (kind != SIMPLE_FUNCTION) { - SPECIALIZATION_FAIL(BINARY_SUBSCR, kind); - Py_DECREF(descriptor); - goto fail; - } - if (fcode->co_argcount != 2) { - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS); - Py_DECREF(descriptor); - goto fail; - } - - PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; - /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { - SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OTHER); - Py_DECREF(descriptor); - goto fail; - } - if (_PyType_CacheGetItemForSpecialization(ht, descriptor, (uint32_t)tp_version)) { - specialized_op = BINARY_SUBSCR_GETITEM; - Py_DECREF(descriptor); - goto success; - } - } - Py_XDECREF(descriptor); - SPECIALIZATION_FAIL(BINARY_SUBSCR, - binary_subscr_fail_kind(container_type, sub)); -fail: - unspecialize(instr); - return; -success: - specialize(instr, specialized_op); -} - - #ifdef Py_STATS static int store_subscr_fail_kind(PyObject *container, PyObject *sub) @@ -2431,6 +2300,59 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) return SPEC_FAIL_BINARY_OP_XOR_INT; } return SPEC_FAIL_BINARY_OP_XOR; + case NB_SUBSCR: + if (PyList_CheckExact(lhs)) { + if (PyLong_CheckExact(rhs) && !_PyLong_IsNonNegativeCompact((PyLongObject *)rhs)) { + return SPEC_FAIL_OUT_OF_RANGE; + } + if (PySlice_Check(rhs)) { + return SPEC_FAIL_BINARY_OP_SUBSCR_LIST_SLICE; + } + } + if (PyTuple_CheckExact(lhs)) { + if (PyLong_CheckExact(rhs) && !_PyLong_IsNonNegativeCompact((PyLongObject *)rhs)) { + return SPEC_FAIL_OUT_OF_RANGE; + } + if (PySlice_Check(rhs)) { + return SPEC_FAIL_BINARY_OP_SUBSCR_TUPLE_SLICE; + } + } + if (PyUnicode_CheckExact(lhs)) { + if (PyLong_CheckExact(rhs) && !_PyLong_IsNonNegativeCompact((PyLongObject *)rhs)) { + return SPEC_FAIL_OUT_OF_RANGE; + } + if (PySlice_Check(rhs)) { + return SPEC_FAIL_BINARY_OP_SUBSCR_STRING_SLICE; + } + } + unsigned int tp_version; + PyTypeObject *container_type = Py_TYPE(lhs); + PyObject *descriptor = _PyType_LookupRefAndVersion(container_type, &_Py_ID(__getitem__), &tp_version); + if (descriptor && Py_TYPE(descriptor) == &PyFunction_Type) { + if (!(container_type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + Py_DECREF(descriptor); + return SPEC_FAIL_BINARY_OP_SUBSCR_NOT_HEAP_TYPE; + } + PyFunctionObject *func = (PyFunctionObject *)descriptor; + PyCodeObject *fcode = (PyCodeObject *)func->func_code; + int kind = function_kind(fcode); + if (kind != SIMPLE_FUNCTION) { + Py_DECREF(descriptor); + return kind; + } + if (fcode->co_argcount != 2) { + Py_DECREF(descriptor); + return SPEC_FAIL_WRONG_NUMBER_ARGUMENTS; + } + + if (_PyInterpreterState_GET()->eval_frame) { + /* Don't specialize if PEP 523 is active */ + Py_DECREF(descriptor); + return SPEC_FAIL_OTHER; + } + } + Py_XDECREF(descriptor); + return SPEC_FAIL_BINARY_OP_SUBSCR; } Py_UNREACHABLE(); } @@ -2536,45 +2458,40 @@ LONG_FLOAT_ACTION(compactlong_float_multiply, *) LONG_FLOAT_ACTION(compactlong_float_true_div, /) #undef LONG_FLOAT_ACTION -static _PyBinaryOpSpecializationDescr compactlongs_specs[NB_OPARG_LAST+1] = { - [NB_OR] = {compactlongs_guard, compactlongs_or}, - [NB_AND] = {compactlongs_guard, compactlongs_and}, - [NB_XOR] = {compactlongs_guard, compactlongs_xor}, - [NB_INPLACE_OR] = {compactlongs_guard, compactlongs_or}, - [NB_INPLACE_AND] = {compactlongs_guard, compactlongs_and}, - [NB_INPLACE_XOR] = {compactlongs_guard, compactlongs_xor}, -}; - -static _PyBinaryOpSpecializationDescr float_compactlong_specs[NB_OPARG_LAST+1] = { - [NB_ADD] = {float_compactlong_guard, float_compactlong_add}, - [NB_SUBTRACT] = {float_compactlong_guard, float_compactlong_subtract}, - [NB_TRUE_DIVIDE] = {nonzero_float_compactlong_guard, float_compactlong_true_div}, - [NB_MULTIPLY] = {float_compactlong_guard, float_compactlong_multiply}, -}; - -static _PyBinaryOpSpecializationDescr compactlong_float_specs[NB_OPARG_LAST+1] = { - [NB_ADD] = {compactlong_float_guard, compactlong_float_add}, - [NB_SUBTRACT] = {compactlong_float_guard, compactlong_float_subtract}, - [NB_TRUE_DIVIDE] = {nonzero_compactlong_float_guard, compactlong_float_true_div}, - [NB_MULTIPLY] = {compactlong_float_guard, compactlong_float_multiply}, +static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = { + /* long-long arithmetic */ + {NB_OR, compactlongs_guard, compactlongs_or}, + {NB_AND, compactlongs_guard, compactlongs_and}, + {NB_XOR, compactlongs_guard, compactlongs_xor}, + {NB_INPLACE_OR, compactlongs_guard, compactlongs_or}, + {NB_INPLACE_AND, compactlongs_guard, compactlongs_and}, + {NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor}, + + /* float-long arithemetic */ + {NB_ADD, float_compactlong_guard, float_compactlong_add}, + {NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract}, + {NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div}, + {NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply}, + + /* float-float arithmetic */ + {NB_ADD, compactlong_float_guard, compactlong_float_add}, + {NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract}, + {NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div}, + {NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply}, }; static int binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg, _PyBinaryOpSpecializationDescr **descr) { -#define LOOKUP_SPEC(TABLE, OPARG) \ - if ((TABLE)[(OPARG)].action) { \ - if ((TABLE)[(OPARG)].guard(lhs, rhs)) { \ - *descr = &((TABLE)[OPARG]); \ - return 1; \ - } \ + size_t n = sizeof(binaryop_extend_descrs)/sizeof(_PyBinaryOpSpecializationDescr); + for (size_t i = 0; i < n; i++) { + _PyBinaryOpSpecializationDescr *d = &binaryop_extend_descrs[i]; + if (d->oparg == oparg && d->guard(lhs, rhs)) { + *descr = d; + return 1; + } } - - LOOKUP_SPEC(compactlong_float_specs, oparg); - LOOKUP_SPEC(float_compactlong_specs, oparg); - LOOKUP_SPEC(compactlongs_specs, oparg); -#undef LOOKUP_SPEC return 0; } @@ -2645,6 +2562,47 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in return; } break; + case NB_SUBSCR: + if (PyLong_CheckExact(rhs) && _PyLong_IsNonNegativeCompact((PyLongObject *)rhs)) { + if (PyList_CheckExact(lhs)) { + specialize(instr, BINARY_OP_SUBSCR_LIST_INT); + return; + } + if (PyTuple_CheckExact(lhs)) { + specialize(instr, BINARY_OP_SUBSCR_TUPLE_INT); + return; + } + if (PyUnicode_CheckExact(lhs)) { + specialize(instr, BINARY_OP_SUBSCR_STR_INT); + return; + } + } + if (PyDict_CheckExact(lhs)) { + specialize(instr, BINARY_OP_SUBSCR_DICT); + return; + } + unsigned int tp_version; + PyTypeObject *container_type = Py_TYPE(lhs); + PyObject *descriptor = _PyType_LookupRefAndVersion(container_type, &_Py_ID(__getitem__), &tp_version); + if (descriptor && Py_TYPE(descriptor) == &PyFunction_Type && + container_type->tp_flags & Py_TPFLAGS_HEAPTYPE) + { + PyFunctionObject *func = (PyFunctionObject *)descriptor; + PyCodeObject *fcode = (PyCodeObject *)func->func_code; + int kind = function_kind(fcode); + PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; + if (kind == SIMPLE_FUNCTION && + fcode->co_argcount == 2 && + !_PyInterpreterState_GET()->eval_frame && /* Don't specialize if PEP 523 is active */ + _PyType_CacheGetItemForSpecialization(ht, descriptor, (uint32_t)tp_version)) + { + specialize(instr, BINARY_OP_SUBSCR_GETITEM); + Py_DECREF(descriptor); + return; + } + } + Py_XDECREF(descriptor); + break; } _PyBinaryOpSpecializationDescr *descr; diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index be3ded9f07ef8a..df0262f9c84148 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -381,9 +381,7 @@ Python/pylifecycle.c - INTERPRETER_TRAMPOLINE_CODEDEF - Python/pystate.c - initial - Python/specialize.c - adaptive_opcodes - Python/specialize.c - cache_requirements - -Python/specialize.c - compactlongs_specs - -Python/specialize.c - float_compactlong_specs - -Python/specialize.c - compactlong_float_specs - +Python/specialize.c - binaryop_extend_descrs - Python/stdlib_module_names.h - _Py_stdlib_module_names - Python/sysmodule.c - perf_map_state - Python/sysmodule.c - _PySys_ImplCacheTag - From 718ab662991214039626db432d60310e0e19a0ac Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 8 Feb 2025 01:16:45 +0000 Subject: [PATCH 123/311] GH-125413: Add `pathlib.Path.info` attribute (#127730) Add `pathlib.Path.info` attribute, which stores an object implementing the `pathlib.types.PathInfo` protocol (also new). The object supports querying the file type and internally caching `os.stat()` results. Path objects generated by `Path.iterdir()` are initialised with status information from `os.DirEntry` objects, which is gleaned from scanning the parent directory. The `PathInfo` protocol has four methods: `exists()`, `is_dir()`, `is_file()` and `is_symlink()`. --- Doc/library/pathlib.rst | 85 ++++++++ Doc/whatsnew/3.14.rst | 9 + Lib/glob.py | 47 +++-- Lib/pathlib/_abc.py | 73 +++---- Lib/pathlib/_local.py | 35 ++-- Lib/pathlib/_os.py | 160 +++++++++++++++ Lib/pathlib/{_types.py => types.py} | 13 +- Lib/test/test_pathlib/test_pathlib.py | 13 ++ Lib/test/test_pathlib/test_pathlib_abc.py | 186 +++++++++++++++--- ...-12-10-19-39-35.gh-issue-125413.wOb4yr.rst | 6 + 10 files changed, 526 insertions(+), 101 deletions(-) rename Lib/pathlib/{_types.py => types.py} (57%) create mode 100644 Misc/NEWS.d/next/Library/2024-12-10-19-39-35.gh-issue-125413.wOb4yr.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 4b48880d6d9a18..8977ccfe6e4124 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1177,6 +1177,38 @@ Querying file type and status .. versionadded:: 3.5 +.. attribute:: Path.info + + A :class:`~pathlib.types.PathInfo` object that supports querying file type + information. The object exposes methods that cache their results, which can + help reduce the number of system calls needed when switching on file type. + For example:: + + >>> p = Path('src') + >>> if p.info.is_symlink(): + ... print('symlink') + ... elif p.info.is_dir(): + ... print('directory') + ... elif p.info.exists(): + ... print('something else') + ... else: + ... print('not found') + ... + directory + + If the path was generated from :meth:`Path.iterdir` then this attribute is + initialized with some information about the file type gleaned from scanning + the parent directory. Merely accessing :attr:`Path.info` does not perform + any filesystem queries. + + To fetch up-to-date information, it's best to call :meth:`Path.is_dir`, + :meth:`~Path.is_file` and :meth:`~Path.is_symlink` rather than methods of + this attribute. There is no way to reset the cache; instead you can create + a new path object with an empty info cache via ``p = Path(p)``. + + .. versionadded:: 3.14 + + Reading and writing files ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1903,3 +1935,56 @@ Below is a table mapping various :mod:`os` functions to their corresponding .. [4] :func:`os.walk` always follows symlinks when categorizing paths into *dirnames* and *filenames*, whereas :meth:`Path.walk` categorizes all symlinks into *filenames* when *follow_symlinks* is false (the default.) + + +Protocols +--------- + +.. module:: pathlib.types + :synopsis: pathlib types for static type checking + + +The :mod:`pathlib.types` module provides types for static type checking. + +.. versionadded:: 3.14 + + +.. class:: PathInfo() + + A :class:`typing.Protocol` describing the + :attr:`Path.info ` attribute. Implementations may + return cached results from their methods. + + .. method:: exists(*, follow_symlinks=True) + + Return ``True`` if the path is an existing file or directory, or any + other kind of file; return ``False`` if the path doesn't exist. + + If *follow_symlinks* is ``False``, return ``True`` for symlinks without + checking if their targets exist. + + .. method:: is_dir(*, follow_symlinks=True) + + Return ``True`` if the path is a directory, or a symbolic link pointing + to a directory; return ``False`` if the path is (or points to) any other + kind of file, or if it doesn't exist. + + If *follow_symlinks* is ``False``, return ``True`` only if the path + is a directory (without following symlinks); return ``False`` if the + path is any other kind of file, or if it doesn't exist. + + .. method:: is_file(*, follow_symlinks=True) + + Return ``True`` if the path is a file, or a symbolic link pointing to + a file; return ``False`` if the path is (or points to) a directory or + other non-file, or if it doesn't exist. + + If *follow_symlinks* is ``False``, return ``True`` only if the path + is a file (without following symlinks); return ``False`` if the path + is a directory or other other non-file, or if it doesn't exist. + + .. method:: is_symlink() + + Return ``True`` if the path is a symbolic link (even if broken); return + ``False`` if the path is a directory or any kind of file, or if it + doesn't exist. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 5cef8999944005..0f119d10819d26 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -617,6 +617,15 @@ pathlib (Contributed by Barney Gale in :gh:`73991`.) +* Add :attr:`pathlib.Path.info` attribute, which stores an object + implementing the :class:`pathlib.types.PathInfo` protocol (also new). The + object supports querying the file type and internally caching + :func:`~os.stat` results. Path objects generated by + :meth:`~pathlib.Path.iterdir` are initialized with file type information + gleaned from scanning the parent directory. + + (Contributed by Barney Gale in :gh:`125413`.) + pdb --- diff --git a/Lib/glob.py b/Lib/glob.py index 690ab1b8b9fb1d..a834ea7f7ce556 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -348,7 +348,7 @@ def lexists(path): @staticmethod def scandir(path): - """Implements os.scandir(). + """Like os.scandir(), but generates (entry, name, path) tuples. """ raise NotImplementedError @@ -425,23 +425,18 @@ def wildcard_selector(self, part, parts): def select_wildcard(path, exists=False): try: - # We must close the scandir() object before proceeding to - # avoid exhausting file descriptors when globbing deep trees. - with self.scandir(path) as scandir_it: - entries = list(scandir_it) + entries = self.scandir(path) except OSError: pass else: - prefix = self.add_slash(path) - for entry in entries: - if match is None or match(entry.name): + for entry, entry_name, entry_path in entries: + if match is None or match(entry_name): if dir_only: try: if not entry.is_dir(): continue except OSError: continue - entry_path = self.concat_path(prefix, entry.name) if dir_only: yield from select_next(entry_path, exists=True) else: @@ -483,15 +478,11 @@ def select_recursive(path, exists=False): def select_recursive_step(stack, match_pos): path = stack.pop() try: - # We must close the scandir() object before proceeding to - # avoid exhausting file descriptors when globbing deep trees. - with self.scandir(path) as scandir_it: - entries = list(scandir_it) + entries = self.scandir(path) except OSError: pass else: - prefix = self.add_slash(path) - for entry in entries: + for entry, _entry_name, entry_path in entries: is_dir = False try: if entry.is_dir(follow_symlinks=follow_symlinks): @@ -500,7 +491,6 @@ def select_recursive_step(stack, match_pos): pass if is_dir or not dir_only: - entry_path = self.concat_path(prefix, entry.name) if match is None or match(str(entry_path), match_pos): if dir_only: yield from select_next(entry_path, exists=True) @@ -528,9 +518,16 @@ class _StringGlobber(_GlobberBase): """Provides shell-style pattern matching and globbing for string paths. """ lexists = staticmethod(os.path.lexists) - scandir = staticmethod(os.scandir) concat_path = operator.add + @staticmethod + def scandir(path): + # We must close the scandir() object before proceeding to + # avoid exhausting file descriptors when globbing deep trees. + with os.scandir(path) as scandir_it: + entries = list(scandir_it) + return ((entry, entry.name, entry.path) for entry in entries) + if os.name == 'nt': @staticmethod def add_slash(pathname): @@ -544,3 +541,19 @@ def add_slash(pathname): if not pathname or pathname[-1] == '/': return pathname return f'{pathname}/' + + +class _PathGlobber(_GlobberBase): + """Provides shell-style pattern matching and globbing for pathlib paths. + """ + + lexists = operator.methodcaller('exists', follow_symlinks=False) + add_slash = operator.methodcaller('joinpath', '') + + @staticmethod + def scandir(path): + return ((child.info, child.name, child) for child in path.iterdir()) + + @staticmethod + def concat_path(path, text): + return path.with_segments(str(path) + text) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index e498dc78e83b5e..d20f04fc5b6dc3 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -13,10 +13,9 @@ import functools import io -import operator import posixpath from errno import EINVAL -from glob import _GlobberBase, _no_recurse_symlinks +from glob import _PathGlobber, _no_recurse_symlinks from pathlib._os import copyfileobj @@ -76,21 +75,6 @@ def magic_open(path, mode='r', buffering=-1, encoding=None, errors=None, raise TypeError(f"{cls.__name__} can't be opened with mode {mode!r}") -class PathGlobber(_GlobberBase): - """ - Class providing shell-style globbing for path objects. - """ - - lexists = operator.methodcaller('exists', follow_symlinks=False) - add_slash = operator.methodcaller('joinpath', '') - scandir = operator.methodcaller('_scandir') - - @staticmethod - def concat_path(path, text): - """Appends text to the given path.""" - return path.with_segments(str(path) + text) - - class CopyReader: """ Class that implements the "read" part of copying between path objects. @@ -367,7 +351,7 @@ def full_match(self, pattern, *, case_sensitive=None): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = _is_case_sensitive(self.parser) - globber = PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) + globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) match = globber.compile(str(pattern)) return match(str(self)) is not None @@ -388,6 +372,14 @@ class ReadablePath(JoinablePath): """ __slots__ = () + @property + def info(self): + """ + A PathInfo object that exposes the file type and other file attributes + of this path. + """ + raise NotImplementedError + def exists(self, *, follow_symlinks=True): """ Whether this path exists. @@ -395,26 +387,30 @@ def exists(self, *, follow_symlinks=True): This method normally follows symlinks; to check whether a symlink exists, add the argument follow_symlinks=False. """ - raise NotImplementedError + info = self.joinpath().info + return info.exists(follow_symlinks=follow_symlinks) def is_dir(self, *, follow_symlinks=True): """ Whether this path is a directory. """ - raise NotImplementedError + info = self.joinpath().info + return info.is_dir(follow_symlinks=follow_symlinks) def is_file(self, *, follow_symlinks=True): """ Whether this path is a regular file (also True for symlinks pointing to regular files). """ - raise NotImplementedError + info = self.joinpath().info + return info.is_file(follow_symlinks=follow_symlinks) def is_symlink(self): """ Whether this path is a symbolic link. """ - raise NotImplementedError + info = self.joinpath().info + return info.is_symlink() def __open_rb__(self, buffering=-1): """ @@ -437,15 +433,6 @@ def read_text(self, encoding=None, errors=None, newline=None): with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: return f.read() - def _scandir(self): - """Yield os.DirEntry-like objects of the directory contents. - - The children are yielded in arbitrary order, and the - special entries '.' and '..' are not included. - """ - import contextlib - return contextlib.nullcontext(self.iterdir()) - def iterdir(self): """Yield path objects of the directory contents. @@ -471,7 +458,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): else: case_pedantic = True recursive = True if recurse_symlinks else _no_recurse_symlinks - globber = PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) + globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) select = globber.selector(parts) return select(self) @@ -498,18 +485,16 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False): if not top_down: paths.append((path, dirnames, filenames)) try: - with path._scandir() as entries: - for entry in entries: - name = entry.name - try: - if entry.is_dir(follow_symlinks=follow_symlinks): - if not top_down: - paths.append(path.joinpath(name)) - dirnames.append(name) - else: - filenames.append(name) - except OSError: - filenames.append(name) + for child in path.iterdir(): + try: + if child.info.is_dir(follow_symlinks=follow_symlinks): + if not top_down: + paths.append(child) + dirnames.append(child.name) + else: + filenames.append(child.name) + except OSError: + filenames.append(child.name) except OSError as error: if on_error is not None: on_error(error) diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index b3ec934f7510de..07d361d7b1352c 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -19,7 +19,7 @@ except ImportError: grp = None -from pathlib._os import copyfile +from pathlib._os import copyfile, PathInfo, DirEntryInfo from pathlib._abc import CopyReader, CopyWriter, JoinablePath, ReadablePath, WritablePath @@ -728,13 +728,25 @@ class Path(WritablePath, ReadablePath, PurePath): object. You can also instantiate a PosixPath or WindowsPath directly, but cannot instantiate a WindowsPath on a POSIX system or vice versa. """ - __slots__ = () + __slots__ = ('_info',) def __new__(cls, *args, **kwargs): if cls is Path: cls = WindowsPath if os.name == 'nt' else PosixPath return object.__new__(cls) + @property + def info(self): + """ + A PathInfo object that exposes the file type and other file attributes + of this path. + """ + try: + return self._info + except AttributeError: + self._info = PathInfo(self) + return self._info + def stat(self, *, follow_symlinks=True): """ Return the result of the stat() system call on this path, like @@ -909,13 +921,11 @@ def _filter_trailing_slash(self, paths): path_str = path_str[:-1] yield path_str - def _scandir(self): - """Yield os.DirEntry-like objects of the directory contents. - - The children are yielded in arbitrary order, and the - special entries '.' and '..' are not included. - """ - return os.scandir(self) + def _from_dir_entry(self, dir_entry, path_str): + path = self.with_segments(path_str) + path._str = path_str + path._info = DirEntryInfo(dir_entry) + return path def iterdir(self): """Yield path objects of the directory contents. @@ -925,10 +935,11 @@ def iterdir(self): """ root_dir = str(self) with os.scandir(root_dir) as scandir_it: - paths = [entry.path for entry in scandir_it] + entries = list(scandir_it) if root_dir == '.': - paths = map(self._remove_leading_dot, paths) - return map(self._from_parsed_string, paths) + return (self._from_dir_entry(e, e.name) for e in entries) + else: + return (self._from_dir_entry(e, e.path) for e in entries) def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): """Iterate over this subtree and yield all existing files (of any diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py index 57bcaf3d680138..c2febb773cd83a 100644 --- a/Lib/pathlib/_os.py +++ b/Lib/pathlib/_os.py @@ -3,6 +3,7 @@ """ from errno import * +from stat import S_ISDIR, S_ISREG, S_ISLNK import os import sys try: @@ -162,3 +163,162 @@ def copyfileobj(source_f, target_f): write_target = target_f.write while buf := read_source(1024 * 1024): write_target(buf) + + +class _PathInfoBase: + __slots__ = () + + def __repr__(self): + path_type = "WindowsPath" if os.name == "nt" else "PosixPath" + return f"<{path_type}.info>" + + +class _WindowsPathInfo(_PathInfoBase): + """Implementation of pathlib.types.PathInfo that provides status + information for Windows paths. Don't try to construct it yourself.""" + __slots__ = ('_path', '_exists', '_is_dir', '_is_file', '_is_symlink') + + def __init__(self, path): + self._path = str(path) + + def exists(self, *, follow_symlinks=True): + """Whether this path exists.""" + if not follow_symlinks and self.is_symlink(): + return True + try: + return self._exists + except AttributeError: + if os.path.exists(self._path): + self._exists = True + return True + else: + self._exists = self._is_dir = self._is_file = False + return False + + def is_dir(self, *, follow_symlinks=True): + """Whether this path is a directory.""" + if not follow_symlinks and self.is_symlink(): + return False + try: + return self._is_dir + except AttributeError: + if os.path.isdir(self._path): + self._is_dir = self._exists = True + return True + else: + self._is_dir = False + return False + + def is_file(self, *, follow_symlinks=True): + """Whether this path is a regular file.""" + if not follow_symlinks and self.is_symlink(): + return False + try: + return self._is_file + except AttributeError: + if os.path.isfile(self._path): + self._is_file = self._exists = True + return True + else: + self._is_file = False + return False + + def is_symlink(self): + """Whether this path is a symbolic link.""" + try: + return self._is_symlink + except AttributeError: + self._is_symlink = os.path.islink(self._path) + return self._is_symlink + + +class _PosixPathInfo(_PathInfoBase): + """Implementation of pathlib.types.PathInfo that provides status + information for POSIX paths. Don't try to construct it yourself.""" + __slots__ = ('_path', '_mode') + + def __init__(self, path): + self._path = str(path) + self._mode = [None, None] + + def _get_mode(self, *, follow_symlinks=True): + idx = bool(follow_symlinks) + mode = self._mode[idx] + if mode is None: + try: + st = os.stat(self._path, follow_symlinks=follow_symlinks) + except (OSError, ValueError): + mode = 0 + else: + mode = st.st_mode + if follow_symlinks or S_ISLNK(mode): + self._mode[idx] = mode + else: + # Not a symlink, so stat() will give the same result + self._mode = [mode, mode] + return mode + + def exists(self, *, follow_symlinks=True): + """Whether this path exists.""" + return self._get_mode(follow_symlinks=follow_symlinks) > 0 + + def is_dir(self, *, follow_symlinks=True): + """Whether this path is a directory.""" + return S_ISDIR(self._get_mode(follow_symlinks=follow_symlinks)) + + def is_file(self, *, follow_symlinks=True): + """Whether this path is a regular file.""" + return S_ISREG(self._get_mode(follow_symlinks=follow_symlinks)) + + def is_symlink(self): + """Whether this path is a symbolic link.""" + return S_ISLNK(self._get_mode(follow_symlinks=False)) + + +PathInfo = _WindowsPathInfo if os.name == 'nt' else _PosixPathInfo + + +class DirEntryInfo(_PathInfoBase): + """Implementation of pathlib.types.PathInfo that provides status + information by querying a wrapped os.DirEntry object. Don't try to + construct it yourself.""" + __slots__ = ('_entry', '_exists') + + def __init__(self, entry): + self._entry = entry + + def exists(self, *, follow_symlinks=True): + """Whether this path exists.""" + if not follow_symlinks: + return True + try: + return self._exists + except AttributeError: + try: + self._entry.stat() + except OSError: + self._exists = False + else: + self._exists = True + return self._exists + + def is_dir(self, *, follow_symlinks=True): + """Whether this path is a directory.""" + try: + return self._entry.is_dir(follow_symlinks=follow_symlinks) + except OSError: + return False + + def is_file(self, *, follow_symlinks=True): + """Whether this path is a regular file.""" + try: + return self._entry.is_file(follow_symlinks=follow_symlinks) + except OSError: + return False + + def is_symlink(self): + """Whether this path is a symbolic link.""" + try: + return self._entry.is_symlink() + except OSError: + return False diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/types.py similarity index 57% rename from Lib/pathlib/_types.py rename to Lib/pathlib/types.py index 84032bb5b4ff1a..b781264796bf67 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/types.py @@ -5,7 +5,7 @@ @runtime_checkable -class Parser(Protocol): +class _PathParser(Protocol): """Protocol for path parsers, which do low-level path manipulation. Path parsers provide a subset of the os.path API, specifically those @@ -17,3 +17,14 @@ class Parser(Protocol): def split(self, path: str) -> tuple[str, str]: ... def splitext(self, path: str) -> tuple[str, str]: ... def normcase(self, path: str) -> str: ... + + +@runtime_checkable +class PathInfo(Protocol): + """Protocol for path info objects, which support querying the file type. + Methods may return cached results. + """ + def exists(self, *, follow_symlinks: bool = True) -> bool: ... + def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... + def is_file(self, *, follow_symlinks: bool = True) -> bool: ... + def is_symlink(self) -> bool: ... diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index d64092b710a4d6..31e5306ae60538 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -2396,6 +2396,19 @@ def test_symlink_to_unsupported(self): with self.assertRaises(pathlib.UnsupportedOperation): q.symlink_to(p) + @needs_symlinks + def test_info_is_symlink_caching(self): + p = self.cls(self.base) + q = p / 'mylink' + self.assertFalse(q.info.is_symlink()) + q.symlink_to('blah') + self.assertFalse(q.info.is_symlink()) + + q = p / 'mylink' # same path, new instance. + self.assertTrue(q.info.is_symlink()) + q.unlink() + self.assertTrue(q.info.is_symlink()) + def test_stat(self): statA = self.cls(self.base).joinpath('fileA').stat() statB = self.cls(self.base).joinpath('dirB', 'fileB').stat() diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index e67bead4297829..696874273a21fd 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -5,7 +5,7 @@ import unittest from pathlib._abc import JoinablePath, ReadablePath, WritablePath, magic_open -from pathlib._types import Parser +from pathlib.types import _PathParser, PathInfo import posixpath from test.support.os_helper import TESTFN @@ -95,7 +95,7 @@ def setUp(self): self.altsep = self.parser.altsep def test_parser(self): - self.assertIsInstance(self.cls.parser, Parser) + self.assertIsInstance(self.cls.parser, _PathParser) def test_constructor_common(self): P = self.cls @@ -849,28 +849,49 @@ def close(self): super().close() -class DummyReadablePath(ReadablePath, DummyJoinablePath): - """ - Simple implementation of DummyReadablePath that keeps files and - directories in memory. - """ - __slots__ = () +class DummyReadablePathInfo: + __slots__ = ('_is_dir', '_is_file') - _files = {} - _directories = {} + def __init__(self, is_dir, is_file): + self._is_dir = is_dir + self._is_file = is_file def exists(self, *, follow_symlinks=True): - return self.is_dir() or self.is_file() + return self._is_dir or self._is_file def is_dir(self, *, follow_symlinks=True): - return str(self).rstrip('/') in self._directories + return self._is_dir def is_file(self, *, follow_symlinks=True): - return str(self) in self._files + return self._is_file def is_symlink(self): return False + +class DummyReadablePath(ReadablePath, DummyJoinablePath): + """ + Simple implementation of DummyReadablePath that keeps files and + directories in memory. + """ + __slots__ = ('_info') + + _files = {} + _directories = {} + + def __init__(self, *segments): + super().__init__(*segments) + self._info = None + + @property + def info(self): + if self._info is None: + path_str = str(self) + self._info = DummyReadablePathInfo( + is_dir=path_str.rstrip('/') in self._directories, + is_file=path_str in self._files) + return self._info + def __open_rb__(self, buffering=-1): path = str(self) if path in self._directories: @@ -1037,21 +1058,20 @@ def test_iterdir_nodir(self): self.assertIn(cm.exception.errno, (errno.ENOTDIR, errno.ENOENT, errno.EINVAL)) - def test_scandir(self): + def test_iterdir_info(self): p = self.cls(self.base) - with p._scandir() as entries: - self.assertTrue(list(entries)) - with p._scandir() as entries: - for entry in entries: - child = p / entry.name - self.assertIsNotNone(entry) - self.assertEqual(entry.name, child.name) - self.assertEqual(entry.is_symlink(), - child.is_symlink()) - self.assertEqual(entry.is_dir(follow_symlinks=False), - child.is_dir(follow_symlinks=False)) - if entry.name != 'brokenLinkLoop': - self.assertEqual(entry.is_dir(), child.is_dir()) + for child in p.iterdir(): + info = child.info + self.assertIsInstance(info, PathInfo) + self.assertEqual(info.exists(), child.exists()) + self.assertEqual(info.is_dir(), child.is_dir()) + self.assertEqual(info.is_file(), child.is_file()) + self.assertEqual(info.is_symlink(), child.is_symlink()) + self.assertTrue(info.exists(follow_symlinks=False)) + self.assertEqual(info.is_dir(follow_symlinks=False), + child.is_dir(follow_symlinks=False)) + self.assertEqual(info.is_file(follow_symlinks=False), + child.is_file(follow_symlinks=False)) def test_glob_common(self): def _check(glob, expected): @@ -1177,6 +1197,118 @@ def test_rglob_windows(self): self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") }) self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") }) + def test_info_exists(self): + p = self.cls(self.base) + self.assertTrue(p.info.exists()) + self.assertTrue((p / 'dirA').info.exists()) + self.assertTrue((p / 'dirA').info.exists(follow_symlinks=False)) + self.assertTrue((p / 'fileA').info.exists()) + self.assertTrue((p / 'fileA').info.exists(follow_symlinks=False)) + self.assertFalse((p / 'non-existing').info.exists()) + self.assertFalse((p / 'non-existing').info.exists(follow_symlinks=False)) + if self.can_symlink: + self.assertTrue((p / 'linkA').info.exists()) + self.assertTrue((p / 'linkA').info.exists(follow_symlinks=False)) + self.assertTrue((p / 'linkB').info.exists()) + self.assertTrue((p / 'linkB').info.exists(follow_symlinks=True)) + self.assertFalse((p / 'brokenLink').info.exists()) + self.assertTrue((p / 'brokenLink').info.exists(follow_symlinks=False)) + self.assertFalse((p / 'brokenLinkLoop').info.exists()) + self.assertTrue((p / 'brokenLinkLoop').info.exists(follow_symlinks=False)) + self.assertFalse((p / 'fileA\udfff').info.exists()) + self.assertFalse((p / 'fileA\udfff').info.exists(follow_symlinks=False)) + self.assertFalse((p / 'fileA\x00').info.exists()) + self.assertFalse((p / 'fileA\x00').info.exists(follow_symlinks=False)) + + def test_info_exists_caching(self): + p = self.cls(self.base) + q = p / 'myfile' + self.assertFalse(q.info.exists()) + self.assertFalse(q.info.exists(follow_symlinks=False)) + if isinstance(self.cls, WritablePath): + q.write_text('hullo') + self.assertFalse(q.info.exists()) + self.assertFalse(q.info.exists(follow_symlinks=False)) + + def test_info_is_dir(self): + p = self.cls(self.base) + self.assertTrue((p / 'dirA').info.is_dir()) + self.assertTrue((p / 'dirA').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'fileA').info.is_dir()) + self.assertFalse((p / 'fileA').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'non-existing').info.is_dir()) + self.assertFalse((p / 'non-existing').info.is_dir(follow_symlinks=False)) + if self.can_symlink: + self.assertFalse((p / 'linkA').info.is_dir()) + self.assertFalse((p / 'linkA').info.is_dir(follow_symlinks=False)) + self.assertTrue((p / 'linkB').info.is_dir()) + self.assertFalse((p / 'linkB').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'brokenLink').info.is_dir()) + self.assertFalse((p / 'brokenLink').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'brokenLinkLoop').info.is_dir()) + self.assertFalse((p / 'brokenLinkLoop').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'dirA\udfff').info.is_dir()) + self.assertFalse((p / 'dirA\udfff').info.is_dir(follow_symlinks=False)) + self.assertFalse((p / 'dirA\x00').info.is_dir()) + self.assertFalse((p / 'dirA\x00').info.is_dir(follow_symlinks=False)) + + def test_info_is_dir_caching(self): + p = self.cls(self.base) + q = p / 'mydir' + self.assertFalse(q.info.is_dir()) + self.assertFalse(q.info.is_dir(follow_symlinks=False)) + if isinstance(self.cls, WritablePath): + q.mkdir() + self.assertFalse(q.info.is_dir()) + self.assertFalse(q.info.is_dir(follow_symlinks=False)) + + def test_info_is_file(self): + p = self.cls(self.base) + self.assertTrue((p / 'fileA').info.is_file()) + self.assertTrue((p / 'fileA').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'dirA').info.is_file()) + self.assertFalse((p / 'dirA').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'non-existing').info.is_file()) + self.assertFalse((p / 'non-existing').info.is_file(follow_symlinks=False)) + if self.can_symlink: + self.assertTrue((p / 'linkA').info.is_file()) + self.assertFalse((p / 'linkA').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'linkB').info.is_file()) + self.assertFalse((p / 'linkB').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'brokenLink').info.is_file()) + self.assertFalse((p / 'brokenLink').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'brokenLinkLoop').info.is_file()) + self.assertFalse((p / 'brokenLinkLoop').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'fileA\udfff').info.is_file()) + self.assertFalse((p / 'fileA\udfff').info.is_file(follow_symlinks=False)) + self.assertFalse((p / 'fileA\x00').info.is_file()) + self.assertFalse((p / 'fileA\x00').info.is_file(follow_symlinks=False)) + + def test_info_is_file_caching(self): + p = self.cls(self.base) + q = p / 'myfile' + self.assertFalse(q.info.is_file()) + self.assertFalse(q.info.is_file(follow_symlinks=False)) + if isinstance(self.cls, WritablePath): + q.write_text('hullo') + self.assertFalse(q.info.is_file()) + self.assertFalse(q.info.is_file(follow_symlinks=False)) + + def test_info_is_symlink(self): + p = self.cls(self.base) + self.assertFalse((p / 'fileA').info.is_symlink()) + self.assertFalse((p / 'dirA').info.is_symlink()) + self.assertFalse((p / 'non-existing').info.is_symlink()) + if self.can_symlink: + self.assertTrue((p / 'linkA').info.is_symlink()) + self.assertTrue((p / 'linkB').info.is_symlink()) + self.assertTrue((p / 'brokenLink').info.is_symlink()) + self.assertFalse((p / 'linkA\udfff').info.is_symlink()) + self.assertFalse((p / 'linkA\x00').info.is_symlink()) + self.assertTrue((p / 'brokenLinkLoop').info.is_symlink()) + self.assertFalse((p / 'fileA\udfff').info.is_symlink()) + self.assertFalse((p / 'fileA\x00').info.is_symlink()) + def test_is_dir(self): P = self.cls(self.base) self.assertTrue((P / 'dirA').is_dir()) diff --git a/Misc/NEWS.d/next/Library/2024-12-10-19-39-35.gh-issue-125413.wOb4yr.rst b/Misc/NEWS.d/next/Library/2024-12-10-19-39-35.gh-issue-125413.wOb4yr.rst new file mode 100644 index 00000000000000..9ac96179a88367 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-10-19-39-35.gh-issue-125413.wOb4yr.rst @@ -0,0 +1,6 @@ +Add :attr:`pathlib.Path.info` attribute, which stores an object +implementing the :class:`pathlib.types.PathInfo` protocol (also new). The +object supports querying the file type and internally caching +:func:`~os.stat` results. Path objects generated by +:meth:`~pathlib.Path.iterdir` are initialized with file type information +gleaned from scanning the parent directory. From d3b60fff584d65dd5487bac056e4c0ad7ebc43b4 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Fri, 7 Feb 2025 21:02:46 -0500 Subject: [PATCH 124/311] gh-124703: Add documentation and whatsnew entry for pdb exit change (#129818) --- Doc/library/pdb.rst | 11 +++++++++++ Doc/whatsnew/3.14.rst | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index f9d1213fb6d29d..bdd89d127491a5 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -697,6 +697,17 @@ can be overridden by the local file. .. pdbcommand:: q(uit) Quit from the debugger. The program being executed is aborted. + An end-of-file input is equivalent to :pdbcmd:`quit`. + + A confirmation prompt will be shown if the debugger is invoked in + ``'inline'`` mode. Either ``y``, ``Y``, ```` or ``EOF`` + will confirm the quit. + + .. versionchanged:: 3.14 + A confirmation prompt will be shown if the debugger is invoked in + ``'inline'`` mode. After the confirmation, the debugger will call + :func:`sys.exit` immediately, instead of raising :exc:`bdb.BdbQuit` + in the next trace event. .. pdbcommand:: debug code diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 0f119d10819d26..9ac0e6ed2a6d40 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -641,6 +641,11 @@ pdb command when :mod:`pdb` is in ``inline`` mode. (Contributed by Tian Gao in :gh:`123757`.) +* A confirmation prompt will be shown when the user tries to quit :mod:`pdb` + in ``inline`` mode. ``y``, ``Y``, ```` or ``EOF`` will confirm + the quit and call :func:`sys.exit`, instead of raising :exc:`bdb.BdbQuit`. + (Contributed by Tian Gao in :gh:`124704`.) + pickle ------ From 6c67904e793828d84716a8c83436c9495235f3a1 Mon Sep 17 00:00:00 2001 From: Tomasz Pytel Date: Fri, 7 Feb 2025 21:47:45 -0500 Subject: [PATCH 125/311] gh-128657: fix _hashopenssl ref/data race (GH-128886) --- ...-01-15-15-45-21.gh-issue-128657.P5LNQA.rst | 1 + Modules/_hashopenssl.c | 36 ++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-15-15-45-21.gh-issue-128657.P5LNQA.rst diff --git a/Misc/NEWS.d/next/Library/2025-01-15-15-45-21.gh-issue-128657.P5LNQA.rst b/Misc/NEWS.d/next/Library/2025-01-15-15-45-21.gh-issue-128657.P5LNQA.rst new file mode 100644 index 00000000000000..3b08a9fba59620 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-15-15-45-21.gh-issue-128657.P5LNQA.rst @@ -0,0 +1 @@ +Fix possible extra reference when using objects returned by :func:`hashlib.sha256` under :term:`free threading`. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 082929be3c77b7..d7586feea3efcd 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -25,13 +25,14 @@ #include #include "Python.h" #include "pycore_hashtable.h" -#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_RELAXED #include "hashlib.h" /* EVP is the preferred interface to hashing in OpenSSL */ #include #include -#include // FIPS_mode() +#include // FIPS_mode() /* We use the object interface to discover what hashes OpenSSL supports. */ #include #include @@ -369,6 +370,7 @@ static PY_EVP_MD* py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht) { PY_EVP_MD *digest = NULL; + PY_EVP_MD *other_digest = NULL; _hashlibstate *state = get_hashlib_state(module); py_hashentry_t *entry = (py_hashentry_t *)_Py_hashtable_get( state->hashtable, (const void*)name @@ -379,20 +381,36 @@ py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht) case Py_ht_evp: case Py_ht_mac: case Py_ht_pbkdf2: - if (entry->evp == NULL) { - entry->evp = PY_EVP_MD_fetch(entry->ossl_name, NULL); + digest = FT_ATOMIC_LOAD_PTR_RELAXED(entry->evp); + if (digest == NULL) { + digest = PY_EVP_MD_fetch(entry->ossl_name, NULL); +#ifdef Py_GIL_DISABLED + // exchange just in case another thread did same thing at same time + other_digest = _Py_atomic_exchange_ptr(&entry->evp, digest); +#else + entry->evp = digest; +#endif } - digest = entry->evp; break; case Py_ht_evp_nosecurity: - if (entry->evp_nosecurity == NULL) { - entry->evp_nosecurity = PY_EVP_MD_fetch(entry->ossl_name, "-fips"); + digest = FT_ATOMIC_LOAD_PTR_RELAXED(entry->evp_nosecurity); + if (digest == NULL) { + digest = PY_EVP_MD_fetch(entry->ossl_name, "-fips"); +#ifdef Py_GIL_DISABLED + // exchange just in case another thread did same thing at same time + other_digest = _Py_atomic_exchange_ptr(&entry->evp_nosecurity, digest); +#else + entry->evp_nosecurity = digest; +#endif } - digest = entry->evp_nosecurity; break; } + // if another thread same thing at same time make sure we got same ptr + assert(other_digest == NULL || other_digest == digest); if (digest != NULL) { - PY_EVP_MD_up_ref(digest); + if (other_digest == NULL) { + PY_EVP_MD_up_ref(digest); + } } } else { // Fall back for looking up an unindexed OpenSSL specific name. From 707d066193c26ab66c8e5e45e72c3a37f48daf45 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 8 Feb 2025 06:47:09 +0000 Subject: [PATCH 126/311] GH-129835: Yield path with trailing slash from `ReadablePath.glob('')` (#129836) In the private pathlib ABCs, make `ReadablePath.glob('')` yield a path with a trailing slash (if it yields anything at all). As a result, `glob()` works similarly to `joinpath()` when given a non-magic pattern. In the globbing implementation, we preemptively add trailing slashes to intermediate paths if there are pattern parts remaining; this removes the need to check for existing trailing slashes (in the removed `add_slash()` method) at subsequent steps. --- Lib/glob.py | 39 +++++++---------------- Lib/pathlib/_abc.py | 2 +- Lib/pathlib/_local.py | 2 +- Lib/test/test_pathlib/test_pathlib_abc.py | 2 +- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/Lib/glob.py b/Lib/glob.py index a834ea7f7ce556..cd8859e63318f3 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -352,12 +352,6 @@ def scandir(path): """ raise NotImplementedError - @staticmethod - def add_slash(path): - """Returns a path with a trailing slash added. - """ - raise NotImplementedError - @staticmethod def concat_path(path, text): """Implements path concatenation. @@ -389,10 +383,12 @@ def selector(self, parts): def special_selector(self, part, parts): """Returns a function that selects special children of the given path. """ + if parts: + part += self.sep select_next = self.selector(parts) def select_special(path, exists=False): - path = self.concat_path(self.add_slash(path), part) + path = self.concat_path(path, part) return select_next(path, exists) return select_special @@ -402,14 +398,16 @@ def literal_selector(self, part, parts): # Optimization: consume and join any subsequent literal parts here, # rather than leaving them for the next selector. This reduces the - # number of string concatenation operations and calls to add_slash(). + # number of string concatenation operations. while parts and magic_check.search(parts[-1]) is None: part += self.sep + parts.pop() + if parts: + part += self.sep select_next = self.selector(parts) def select_literal(path, exists=False): - path = self.concat_path(self.add_slash(path), part) + path = self.concat_path(path, part) return select_next(path, exists=False) return select_literal @@ -437,7 +435,7 @@ def select_wildcard(path, exists=False): continue except OSError: continue - if dir_only: + entry_path = self.concat_path(entry_path, self.sep) yield from select_next(entry_path, exists=True) else: yield entry_path @@ -467,7 +465,6 @@ def recursive_selector(self, part, parts): select_next = self.selector(parts) def select_recursive(path, exists=False): - path = self.add_slash(path) match_pos = len(str(path)) if match is None or match(str(path), match_pos): yield from select_next(path, exists) @@ -491,7 +488,10 @@ def select_recursive_step(stack, match_pos): pass if is_dir or not dir_only: - if match is None or match(str(entry_path), match_pos): + entry_path_str = str(entry_path) + if dir_only: + entry_path = self.concat_path(entry_path, self.sep) + if match is None or match(entry_path_str, match_pos): if dir_only: yield from select_next(entry_path, exists=True) else: @@ -528,27 +528,12 @@ def scandir(path): entries = list(scandir_it) return ((entry, entry.name, entry.path) for entry in entries) - if os.name == 'nt': - @staticmethod - def add_slash(pathname): - tail = os.path.splitroot(pathname)[2] - if not tail or tail[-1] in '\\/': - return pathname - return f'{pathname}\\' - else: - @staticmethod - def add_slash(pathname): - if not pathname or pathname[-1] == '/': - return pathname - return f'{pathname}/' - class _PathGlobber(_GlobberBase): """Provides shell-style pattern matching and globbing for pathlib paths. """ lexists = operator.methodcaller('exists', follow_symlinks=False) - add_slash = operator.methodcaller('joinpath', '') @staticmethod def scandir(path): diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index d20f04fc5b6dc3..65d91e4d67b463 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -460,7 +460,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): recursive = True if recurse_symlinks else _no_recurse_symlinks globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) select = globber.selector(parts) - return select(self) + return select(self.joinpath('')) def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): """Recursively yield all existing files (of any kind, including diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 07d361d7b1352c..6cdcd448991c8c 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -959,7 +959,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False): globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) select = globber.selector(parts[::-1]) root = str(self) - paths = select(root) + paths = select(self.parser.join(root, '')) # Normalize results if root == '.': diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 696874273a21fd..836d8387bdc433 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1125,7 +1125,7 @@ def test_glob_windows(self): def test_glob_empty_pattern(self): P = self.cls p = P(self.base) - self.assertEqual(list(p.glob("")), [p]) + self.assertEqual(list(p.glob("")), [p.joinpath("")]) def test_glob_case_sensitive(self): P = self.cls From 80b9e79d84e835ecdb5a15c9ba73e44803ca9d32 Mon Sep 17 00:00:00 2001 From: Diego Russo Date: Sat, 8 Feb 2025 06:56:19 +0000 Subject: [PATCH 127/311] Use ubuntu-22.04-arm image for Arm runners. (#129834) GitHub suggested us to try the 22.04 images for the Arm runners while they are invetigating the failures we've been having using 24.04. --- .github/workflows/build.yml | 4 ++-- .github/workflows/jit.yml | 2 +- .github/workflows/tail-call.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc2f4858be6e8c..63417972ba6bec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -247,13 +247,13 @@ jobs: - true os: - ubuntu-24.04 - - ubuntu-24.04-arm + - ubuntu-22.04-arm exclude: # Do not test BOLT with free-threading, to conserve resources - bolt: true free-threading: true # BOLT currently crashes during instrumentation on aarch64 - - os: ubuntu-24.04-arm + - os: ubuntu-22.04-arm bolt: true uses: ./.github/workflows/reusable-ubuntu.yml with: diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 806a8524112d76..f8b0c98f052ab9 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -86,7 +86,7 @@ jobs: runner: ubuntu-24.04 - target: aarch64-unknown-linux-gnu/gcc architecture: aarch64 - runner: ubuntu-24.04-arm + runner: ubuntu-22.04-arm steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/tail-call.yml b/.github/workflows/tail-call.yml index ad5d0aa55173c4..37df21af02e919 100644 --- a/.github/workflows/tail-call.yml +++ b/.github/workflows/tail-call.yml @@ -62,7 +62,7 @@ jobs: runner: ubuntu-24.04 - target: aarch64-unknown-linux-gnu/gcc architecture: aarch64 - runner: ubuntu-24.04-arm + runner: ubuntu-22.04-arm steps: - uses: actions/checkout@v4 with: From 0f128b9435fccb296714f3ea2466c3fdda77d91d Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 8 Feb 2025 12:57:17 +0000 Subject: [PATCH 128/311] gh-129842: warnings.py: Remove obsolete requirement reference (GH-129845) --- Lib/warnings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lib/warnings.py b/Lib/warnings.py index f20b01372dd7a4..13ad6c8aacbb7f 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -473,9 +473,6 @@ def __init__(self, *, record=False, module=None, """Specify whether to record warnings and if an alternative module should be used other than sys.modules['warnings']. - For compatibility with Python 3.0, please consider all arguments to be - keyword-only. - """ self._record = record self._module = sys.modules['warnings'] if module is None else module From 421ea1291d9b8ebfe5eaa72ab041338073fb67d0 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Sat, 8 Feb 2025 08:02:36 -0500 Subject: [PATCH 129/311] gh-119349: Add ctypes.util.dllist -- list loaded shared libraries (GH-122946) Add function to list the currently loaded libraries to ctypes.util The dllist() function calls platform-specific APIs in order to list the runtime libraries loaded by Python and any imported modules. On unsupported platforms the function may be missing. Co-authored-by: Eryk Sun Co-authored-by: Peter Bierma --- Doc/library/ctypes.rst | 36 +++++ Doc/whatsnew/3.14.rst | 3 + Lib/ctypes/util.py | 131 ++++++++++++++++++ Lib/test/test_ctypes/test_dllist.py | 59 ++++++++ Misc/ACKS | 1 + ...-08-12-11-58-15.gh-issue-119349.-xTnHl.rst | 2 + 6 files changed, 232 insertions(+) create mode 100644 Lib/test/test_ctypes/test_dllist.py create mode 100644 Misc/NEWS.d/next/Library/2024-08-12-11-58-15.gh-issue-119349.-xTnHl.rst diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 615138302e1379..cb02fd33a6e741 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -1406,6 +1406,28 @@ the shared library name at development time, and hardcode that into the wrapper module instead of using :func:`~ctypes.util.find_library` to locate the library at runtime. +.. _ctypes-listing-loaded-shared-libraries: + +Listing loaded shared libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When writing code that relies on code loaded from shared libraries, it can be +useful to know which shared libraries have already been loaded into the current +process. + +The :mod:`!ctypes.util` module provides the :func:`~ctypes.util.dllist` function, +which calls the different APIs provided by the various platforms to help determine +which shared libraries have already been loaded into the current process. + +The exact output of this function will be system dependent. On most platforms, +the first entry of this list represents the current process itself, which may +be an empty string. +For example, on glibc-based Linux, the return may look like:: + + >>> from ctypes.util import dllist + >>> dllist() + ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ] + .. _ctypes-loading-shared-libraries: Loading shared libraries @@ -2083,6 +2105,20 @@ Utility functions .. availability:: Windows +.. function:: dllist() + :module: ctypes.util + + Try to provide a list of paths of the shared libraries loaded into the current + process. These paths are not normalized or processed in any way. The function + can raise :exc:`OSError` if the underlying platform APIs fail. + The exact functionality is system dependent. + + On most platforms, the first element of the list represents the current + executable file. It may be an empty string. + + .. availability:: Windows, macOS, iOS, glibc, BSD libc, musl + .. versionadded:: next + .. function:: FormatError([code]) Returns a textual description of the error code *code*. If no error code is diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 9ac0e6ed2a6d40..9c4922308b7f2d 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -389,6 +389,9 @@ ctypes complex C types. (Contributed by Sergey B Kirpichev in :gh:`61103`). +* Add :func:`ctypes.util.dllist` for listing the shared libraries + loaded by the current process. + (Contributed by Brian Ward in :gh:`119349`.) datetime -------- diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 117bf06cb01013..99504911a3dbe0 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -67,6 +67,65 @@ def find_library(name): return fname return None + # Listing loaded DLLs on Windows relies on the following APIs: + # https://learn.microsoft.com/windows/win32/api/psapi/nf-psapi-enumprocessmodules + # https://learn.microsoft.com/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew + import ctypes + from ctypes import wintypes + + _kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + _get_current_process = _kernel32["GetCurrentProcess"] + _get_current_process.restype = wintypes.HANDLE + + _k32_get_module_file_name = _kernel32["GetModuleFileNameW"] + _k32_get_module_file_name.restype = wintypes.DWORD + _k32_get_module_file_name.argtypes = ( + wintypes.HMODULE, + wintypes.LPWSTR, + wintypes.DWORD, + ) + + _psapi = ctypes.WinDLL('psapi', use_last_error=True) + _enum_process_modules = _psapi["EnumProcessModules"] + _enum_process_modules.restype = wintypes.BOOL + _enum_process_modules.argtypes = ( + wintypes.HANDLE, + ctypes.POINTER(wintypes.HMODULE), + wintypes.DWORD, + wintypes.LPDWORD, + ) + + def _get_module_filename(module: wintypes.HMODULE): + name = (wintypes.WCHAR * 32767)() # UNICODE_STRING_MAX_CHARS + if _k32_get_module_file_name(module, name, len(name)): + return name.value + return None + + + def _get_module_handles(): + process = _get_current_process() + space_needed = wintypes.DWORD() + n = 1024 + while True: + modules = (wintypes.HMODULE * n)() + if not _enum_process_modules(process, + modules, + ctypes.sizeof(modules), + ctypes.byref(space_needed)): + err = ctypes.get_last_error() + msg = ctypes.FormatError(err).strip() + raise ctypes.WinError(err, f"EnumProcessModules failed: {msg}") + n = space_needed.value // ctypes.sizeof(wintypes.HMODULE) + if n <= len(modules): + return modules[:n] + + def dllist(): + """Return a list of loaded shared libraries in the current process.""" + modules = _get_module_handles() + libraries = [name for h in modules + if (name := _get_module_filename(h)) is not None] + return libraries + elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos"}: from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): @@ -80,6 +139,22 @@ def find_library(name): continue return None + # Listing loaded libraries on Apple systems relies on the following API: + # https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html + import ctypes + + _libc = ctypes.CDLL(find_library("c")) + _dyld_get_image_name = _libc["_dyld_get_image_name"] + _dyld_get_image_name.restype = ctypes.c_char_p + + def dllist(): + """Return a list of loaded shared libraries in the current process.""" + num_images = _libc._dyld_image_count() + libraries = [os.fsdecode(name) for i in range(num_images) + if (name := _dyld_get_image_name(i)) is not None] + + return libraries + elif sys.platform.startswith("aix"): # AIX has two styles of storing shared libraries # GNU auto_tools refer to these as svr4 and aix @@ -341,6 +416,55 @@ def find_library(name): return _findSoname_ldconfig(name) or \ _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name)) + +# Listing loaded libraries on other systems will try to use +# functions common to Linux and a few other Unix-like systems. +# See the following for several platforms' documentation of the same API: +# https://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html +# https://man.freebsd.org/cgi/man.cgi?query=dl_iterate_phdr +# https://man.openbsd.org/dl_iterate_phdr +# https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html +if (os.name == "posix" and + sys.platform not in {"darwin", "ios", "tvos", "watchos"}): + import ctypes + if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"): + + class _dl_phdr_info(ctypes.Structure): + _fields_ = [ + ("dlpi_addr", ctypes.c_void_p), + ("dlpi_name", ctypes.c_char_p), + ("dlpi_phdr", ctypes.c_void_p), + ("dlpi_phnum", ctypes.c_ushort), + ] + + _dl_phdr_callback = ctypes.CFUNCTYPE( + ctypes.c_int, + ctypes.POINTER(_dl_phdr_info), + ctypes.c_size_t, + ctypes.POINTER(ctypes.py_object), + ) + + @_dl_phdr_callback + def _info_callback(info, _size, data): + libraries = data.contents.value + name = os.fsdecode(info.contents.dlpi_name) + libraries.append(name) + return 0 + + _dl_iterate_phdr = _libc["dl_iterate_phdr"] + _dl_iterate_phdr.argtypes = [ + _dl_phdr_callback, + ctypes.POINTER(ctypes.py_object), + ] + _dl_iterate_phdr.restype = ctypes.c_int + + def dllist(): + """Return a list of loaded shared libraries in the current process.""" + libraries = [] + _dl_iterate_phdr(_info_callback, + ctypes.byref(ctypes.py_object(libraries))) + return libraries + ################################################################ # test code @@ -384,5 +508,12 @@ def test(): print(cdll.LoadLibrary("libcrypt.so")) print(find_library("crypt")) + try: + dllist + except NameError: + print('dllist() not available') + else: + print(dllist()) + if __name__ == "__main__": test() diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py new file mode 100644 index 00000000000000..15603dc3d77972 --- /dev/null +++ b/Lib/test/test_ctypes/test_dllist.py @@ -0,0 +1,59 @@ +import os +import sys +import unittest +from ctypes import CDLL +import ctypes.util +from test.support import import_helper + + +WINDOWS = os.name == "nt" +APPLE = sys.platform in {"darwin", "ios", "tvos", "watchos"} + +if WINDOWS: + KNOWN_LIBRARIES = ["KERNEL32.DLL"] +elif APPLE: + KNOWN_LIBRARIES = ["libSystem.B.dylib"] +else: + # trickier than it seems, because libc may not be present + # on musl systems, and sometimes goes by different names. + # However, ctypes itself loads libffi + KNOWN_LIBRARIES = ["libc.so", "libffi.so"] + + +@unittest.skipUnless( + hasattr(ctypes.util, "dllist"), + "ctypes.util.dllist is not available on this platform", +) +class ListSharedLibraries(unittest.TestCase): + + def test_lists_system(self): + dlls = ctypes.util.dllist() + + self.assertGreater(len(dlls), 0, f"loaded={dlls}") + self.assertTrue( + any(lib in dll for dll in dlls for lib in KNOWN_LIBRARIES), f"loaded={dlls}" + ) + + def test_lists_updates(self): + dlls = ctypes.util.dllist() + + # this test relies on being able to import a library which is + # not already loaded. + # If it is (e.g. by a previous test in the same process), we skip + if any("_ctypes_test" in dll for dll in dlls): + self.skipTest("Test library is already loaded") + + _ctypes_test = import_helper.import_module("_ctypes_test") + test_module = CDLL(_ctypes_test.__file__) + dlls2 = ctypes.util.dllist() + self.assertIsNotNone(dlls2) + + dlls1 = set(dlls) + dlls2 = set(dlls2) + + self.assertGreater(dlls2, dlls1, f"newly loaded libraries: {dlls2 - dlls1}") + self.assertTrue(any("_ctypes_test" in dll for dll in dlls2), f"loaded={dlls2}") + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index 27480a1f3131bd..2a68b69f161041 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1993,6 +1993,7 @@ Edward C Wang Jiahua Wang Ke Wang Liang-Bo Wang +Brian Ward Greg Ward Tom Wardill Zachary Ware diff --git a/Misc/NEWS.d/next/Library/2024-08-12-11-58-15.gh-issue-119349.-xTnHl.rst b/Misc/NEWS.d/next/Library/2024-08-12-11-58-15.gh-issue-119349.-xTnHl.rst new file mode 100644 index 00000000000000..5dd8264a608dfa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-12-11-58-15.gh-issue-119349.-xTnHl.rst @@ -0,0 +1,2 @@ +Add the :func:`ctypes.util.dllist` function to list the loaded shared +libraries for the current process. From 1988003625409e4d6e3a1affab14b846063a4f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:21:32 +0100 Subject: [PATCH 130/311] gh-111178: fix UBSan failures in `Modules/_io/*.c` (GH-129083) * fix UBSan failures for `buffered`, `rwpair`, `bytesio`, `bytesiobuf`, `iobase`, `stringio`, `nldecoder_object`, `textio`, `winconsoleio` * arg names: use 'dummy' for NOARGS method and 'args' for others --- Modules/_io/bufferedio.c | 93 +++++++++++++++---------- Modules/_io/bytesio.c | 58 ++++++++++------ Modules/_io/fileio.c | 26 +++---- Modules/_io/iobase.c | 19 +++-- Modules/_io/stringio.c | 21 +++--- Modules/_io/textio.c | 139 ++++++++++++++++++++----------------- Modules/_io/winconsoleio.c | 37 ++++++---- 7 files changed, 230 insertions(+), 163 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index bc5fff54a62b6d..53c4702f673786 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -261,6 +261,8 @@ typedef struct { PyObject *weakreflist; } buffered; +#define buffered_CAST(op) ((buffered *)(op)) + /* Implementation notes: @@ -399,8 +401,9 @@ _enter_buffered_busy(buffered *self) static int -buffered_clear(buffered *self) +buffered_clear(PyObject *op) { + buffered *self = buffered_CAST(op); self->ok = 0; Py_CLEAR(self->raw); Py_CLEAR(self->dict); @@ -408,16 +411,17 @@ buffered_clear(buffered *self) } static void -buffered_dealloc(buffered *self) +buffered_dealloc(PyObject *op) { + buffered *self = buffered_CAST(op); PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; - if (_PyIOBase_finalize((PyObject *) self) < 0) + if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); self->ok = 0; if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)self); + PyObject_ClearWeakRefs(op); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; @@ -426,8 +430,8 @@ buffered_dealloc(buffered *self) PyThread_free_lock(self->lock); self->lock = NULL; } - (void)buffered_clear(self); - tp->tp_free((PyObject *)self); + (void)buffered_clear(op); + tp->tp_free(self); Py_DECREF(tp); } @@ -2227,6 +2231,8 @@ typedef struct { PyObject *weakreflist; } rwpair; +#define rwpair_CAST(op) ((rwpair *)(op)) + /*[clinic input] _io.BufferedRWPair.__init__ reader: object @@ -2276,8 +2282,9 @@ _io_BufferedRWPair___init___impl(rwpair *self, PyObject *reader, } static int -bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg) +bufferedrwpair_traverse(PyObject *op, visitproc visit, void *arg) { + rwpair *self = rwpair_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); Py_VISIT(self->reader); @@ -2286,8 +2293,9 @@ bufferedrwpair_traverse(rwpair *self, visitproc visit, void *arg) } static int -bufferedrwpair_clear(rwpair *self) +bufferedrwpair_clear(PyObject *op) { + rwpair *self = rwpair_CAST(op); Py_CLEAR(self->reader); Py_CLEAR(self->writer); Py_CLEAR(self->dict); @@ -2295,14 +2303,15 @@ bufferedrwpair_clear(rwpair *self) } static void -bufferedrwpair_dealloc(rwpair *self) +bufferedrwpair_dealloc(PyObject *op) { + rwpair *self = rwpair_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)self); - (void)bufferedrwpair_clear(self); - tp->tp_free((PyObject *) self); + PyObject_ClearWeakRefs(op); + (void)bufferedrwpair_clear(op); + tp->tp_free(self); Py_DECREF(tp); } @@ -2328,62 +2337,72 @@ _forward_call(buffered *self, PyObject *name, PyObject *args) } static PyObject * -bufferedrwpair_read(rwpair *self, PyObject *args) +bufferedrwpair_read(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(read), args); } static PyObject * -bufferedrwpair_peek(rwpair *self, PyObject *args) +bufferedrwpair_peek(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(peek), args); } static PyObject * -bufferedrwpair_read1(rwpair *self, PyObject *args) +bufferedrwpair_read1(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(read1), args); } static PyObject * -bufferedrwpair_readinto(rwpair *self, PyObject *args) +bufferedrwpair_readinto(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(readinto), args); } static PyObject * -bufferedrwpair_readinto1(rwpair *self, PyObject *args) +bufferedrwpair_readinto1(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(readinto1), args); } static PyObject * -bufferedrwpair_write(rwpair *self, PyObject *args) +bufferedrwpair_write(PyObject *op, PyObject *args) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->writer, &_Py_ID(write), args); } static PyObject * -bufferedrwpair_flush(rwpair *self, PyObject *Py_UNUSED(ignored)) +bufferedrwpair_flush(PyObject *op, PyObject *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->writer, &_Py_ID(flush), NULL); } static PyObject * -bufferedrwpair_readable(rwpair *self, PyObject *Py_UNUSED(ignored)) +bufferedrwpair_readable(PyObject *op, PyObject *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->reader, &_Py_ID(readable), NULL); } static PyObject * -bufferedrwpair_writable(rwpair *self, PyObject *Py_UNUSED(ignored)) +bufferedrwpair_writable(PyObject *op, PyObject *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); return _forward_call(self->writer, &_Py_ID(writable), NULL); } static PyObject * -bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored)) +bufferedrwpair_close(PyObject *op, PyObject *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); PyObject *exc = NULL; PyObject *ret = _forward_call(self->writer, &_Py_ID(close), NULL); if (ret == NULL) { @@ -2401,8 +2420,9 @@ bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored)) } static PyObject * -bufferedrwpair_isatty(rwpair *self, PyObject *Py_UNUSED(ignored)) +bufferedrwpair_isatty(PyObject *op, PyObject *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); PyObject *ret = _forward_call(self->writer, &_Py_ID(isatty), NULL); if (ret != Py_False) { @@ -2415,8 +2435,9 @@ bufferedrwpair_isatty(rwpair *self, PyObject *Py_UNUSED(ignored)) } static PyObject * -bufferedrwpair_closed_get(rwpair *self, void *context) +bufferedrwpair_closed_get(PyObject *op, void *Py_UNUSED(dummy)) { + rwpair *self = rwpair_CAST(op); if (self->writer == NULL) { PyErr_SetString(PyExc_RuntimeError, "the BufferedRWPair object is being garbage-collected"); @@ -2633,20 +2654,20 @@ PyType_Spec bufferedwriter_spec = { }; static PyMethodDef bufferedrwpair_methods[] = { - {"read", (PyCFunction)bufferedrwpair_read, METH_VARARGS}, - {"peek", (PyCFunction)bufferedrwpair_peek, METH_VARARGS}, - {"read1", (PyCFunction)bufferedrwpair_read1, METH_VARARGS}, - {"readinto", (PyCFunction)bufferedrwpair_readinto, METH_VARARGS}, - {"readinto1", (PyCFunction)bufferedrwpair_readinto1, METH_VARARGS}, + {"read", bufferedrwpair_read, METH_VARARGS}, + {"peek", bufferedrwpair_peek, METH_VARARGS}, + {"read1", bufferedrwpair_read1, METH_VARARGS}, + {"readinto", bufferedrwpair_readinto, METH_VARARGS}, + {"readinto1", bufferedrwpair_readinto1, METH_VARARGS}, - {"write", (PyCFunction)bufferedrwpair_write, METH_VARARGS}, - {"flush", (PyCFunction)bufferedrwpair_flush, METH_NOARGS}, + {"write", bufferedrwpair_write, METH_VARARGS}, + {"flush", bufferedrwpair_flush, METH_NOARGS}, - {"readable", (PyCFunction)bufferedrwpair_readable, METH_NOARGS}, - {"writable", (PyCFunction)bufferedrwpair_writable, METH_NOARGS}, + {"readable", bufferedrwpair_readable, METH_NOARGS}, + {"writable", bufferedrwpair_writable, METH_NOARGS}, - {"close", (PyCFunction)bufferedrwpair_close, METH_NOARGS}, - {"isatty", (PyCFunction)bufferedrwpair_isatty, METH_NOARGS}, + {"close", bufferedrwpair_close, METH_NOARGS}, + {"isatty", bufferedrwpair_isatty, METH_NOARGS}, {NULL, NULL} }; @@ -2658,7 +2679,7 @@ static PyMemberDef bufferedrwpair_members[] = { }; static PyGetSetDef bufferedrwpair_getset[] = { - {"closed", (getter)bufferedrwpair_closed_get, NULL, NULL}, + {"closed", bufferedrwpair_closed_get, NULL, NULL}, {NULL} }; diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 16095333db6638..dc4e40b9f09a1d 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -21,11 +21,15 @@ typedef struct { Py_ssize_t exports; } bytesio; +#define bytesio_CAST(op) ((bytesio *)(op)) + typedef struct { PyObject_HEAD bytesio *source; } bytesiobuf; +#define bytesiobuf_CAST(op) ((bytesiobuf *)(op)) + /* The bytesio object can be in three states: * Py_REFCNT(buf) == 1, exports == 0. * Py_REFCNT(buf) > 1. exports == 0, @@ -239,8 +243,9 @@ write_bytes(bytesio *self, PyObject *b) } static PyObject * -bytesio_get_closed(bytesio *self, void *Py_UNUSED(ignored)) +bytesio_get_closed(PyObject *op, void *Py_UNUSED(closure)) { + bytesio *self = bytesio_CAST(op); if (self->buf == NULL) { Py_RETURN_TRUE; } @@ -620,9 +625,10 @@ _io_BytesIO_truncate_impl(bytesio *self, Py_ssize_t size) } static PyObject * -bytesio_iternext(bytesio *self) +bytesio_iternext(PyObject *op) { Py_ssize_t n; + bytesio *self = bytesio_CAST(op); CHECK_CLOSED(self); @@ -783,8 +789,9 @@ _io_BytesIO_close_impl(bytesio *self) */ static PyObject * -bytesio_getstate(bytesio *self, PyObject *Py_UNUSED(ignored)) +bytesio_getstate(PyObject *op, PyObject *Py_UNUSED(dummy)) { + bytesio *self = bytesio_CAST(op); PyObject *initvalue = _io_BytesIO_getvalue_impl(self); PyObject *dict; PyObject *state; @@ -808,12 +815,13 @@ bytesio_getstate(bytesio *self, PyObject *Py_UNUSED(ignored)) } static PyObject * -bytesio_setstate(bytesio *self, PyObject *state) +bytesio_setstate(PyObject *op, PyObject *state) { PyObject *result; PyObject *position_obj; PyObject *dict; Py_ssize_t pos; + bytesio *self = bytesio_CAST(op); assert(state != NULL); @@ -883,8 +891,9 @@ bytesio_setstate(bytesio *self, PyObject *state) } static void -bytesio_dealloc(bytesio *self) +bytesio_dealloc(PyObject *op) { + bytesio *self = bytesio_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->exports > 0) { @@ -895,7 +904,7 @@ bytesio_dealloc(bytesio *self) Py_CLEAR(self->buf); Py_CLEAR(self->dict); if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); tp->tp_free(self); Py_DECREF(tp); } @@ -961,8 +970,9 @@ _io_BytesIO___init___impl(bytesio *self, PyObject *initvalue) } static PyObject * -bytesio_sizeof(bytesio *self, void *unused) +bytesio_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy)) { + bytesio *self = bytesio_CAST(op); size_t res = _PyObject_SIZE(Py_TYPE(self)); if (self->buf && !SHARED_BUF(self)) { size_t s = _PySys_GetSizeOf(self->buf); @@ -975,8 +985,9 @@ bytesio_sizeof(bytesio *self, void *unused) } static int -bytesio_traverse(bytesio *self, visitproc visit, void *arg) +bytesio_traverse(PyObject *op, visitproc visit, void *arg) { + bytesio *self = bytesio_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); Py_VISIT(self->buf); @@ -984,8 +995,9 @@ bytesio_traverse(bytesio *self, visitproc visit, void *arg) } static int -bytesio_clear(bytesio *self) +bytesio_clear(PyObject *op) { + bytesio *self = bytesio_CAST(op); Py_CLEAR(self->dict); if (self->exports == 0) { Py_CLEAR(self->buf); @@ -999,7 +1011,7 @@ bytesio_clear(bytesio *self) #undef clinic_state static PyGetSetDef bytesio_getsetlist[] = { - {"closed", (getter)bytesio_get_closed, NULL, + {"closed", bytesio_get_closed, NULL, "True if the file is closed."}, {NULL}, /* sentinel */ }; @@ -1023,9 +1035,9 @@ static struct PyMethodDef bytesio_methods[] = { _IO_BYTESIO_GETVALUE_METHODDEF _IO_BYTESIO_SEEK_METHODDEF _IO_BYTESIO_TRUNCATE_METHODDEF - {"__getstate__", (PyCFunction)bytesio_getstate, METH_NOARGS, NULL}, - {"__setstate__", (PyCFunction)bytesio_setstate, METH_O, NULL}, - {"__sizeof__", (PyCFunction)bytesio_sizeof, METH_NOARGS, NULL}, + {"__getstate__", bytesio_getstate, METH_NOARGS, NULL}, + {"__setstate__", bytesio_setstate, METH_O, NULL}, + {"__sizeof__", bytesio_sizeof, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; @@ -1065,9 +1077,10 @@ PyType_Spec bytesio_spec = { */ static int -bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) +bytesiobuf_getbuffer(PyObject *op, Py_buffer *view, int flags) { - bytesio *b = (bytesio *) obj->source; + bytesiobuf *obj = bytesiobuf_CAST(op); + bytesio *b = bytesio_CAST(obj->source); if (view == NULL) { PyErr_SetString(PyExc_BufferError, @@ -1080,7 +1093,7 @@ bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) } /* cannot fail if view != NULL and readonly == 0 */ - (void)PyBuffer_FillInfo(view, (PyObject*)obj, + (void)PyBuffer_FillInfo(view, op, PyBytes_AS_STRING(b->buf), b->string_size, 0, flags); b->exports++; @@ -1088,26 +1101,29 @@ bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) } static void -bytesiobuf_releasebuffer(bytesiobuf *obj, Py_buffer *view) +bytesiobuf_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view)) { - bytesio *b = (bytesio *) obj->source; + bytesiobuf *obj = bytesiobuf_CAST(op); + bytesio *b = bytesio_CAST(obj->source); b->exports--; } static int -bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg) +bytesiobuf_traverse(PyObject *op, visitproc visit, void *arg) { + bytesiobuf *self = bytesiobuf_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->source); return 0; } static void -bytesiobuf_dealloc(bytesiobuf *self) +bytesiobuf_dealloc(PyObject *op) { + bytesiobuf *self = bytesiobuf_CAST(op); PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ - PyObject_GC_UnTrack(self); + PyObject_GC_UnTrack(op); Py_CLEAR(self->source); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index f27f2ed4843271..89f1cfe6b20935 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -83,7 +83,7 @@ typedef struct { } fileio; #define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type)) -#define _PyFileIO_CAST(op) _Py_CAST(fileio*, (op)) +#define PyFileIO_CAST(op) ((fileio *)(op)) /* Forward declarations */ static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error); @@ -91,7 +91,7 @@ static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool int _PyFileIO_closed(PyObject *self) { - return (_PyFileIO_CAST(self)->fd < 0); + return (PyFileIO_CAST(self)->fd < 0); } /* Because this can call arbitrary code, it shouldn't be called when @@ -100,7 +100,7 @@ _PyFileIO_closed(PyObject *self) static PyObject * fileio_dealloc_warn(PyObject *op, PyObject *source) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); if (self->fd >= 0 && self->closefd) { PyObject *exc = PyErr_GetRaisedException(); if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) { @@ -542,7 +542,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, static int fileio_traverse(PyObject *op, visitproc visit, void *arg) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; @@ -551,7 +551,7 @@ fileio_traverse(PyObject *op, visitproc visit, void *arg) static int fileio_clear(PyObject *op) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); Py_CLEAR(self->dict); return 0; } @@ -559,7 +559,7 @@ fileio_clear(PyObject *op) static void fileio_dealloc(PyObject *op) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); self->finalizing = 1; if (_PyIOBase_finalize(op) < 0) { return; @@ -1161,7 +1161,7 @@ mode_string(fileio *self) static PyObject * fileio_repr(PyObject *op) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); const char *type_name = Py_TYPE(self)->tp_name; if (self->fd < 0) { @@ -1227,9 +1227,9 @@ _io_FileIO_isatty_impl(fileio *self) context TOCTOU issues (the fd could be arbitrarily modified by surrounding code). */ static PyObject * -_io_FileIO_isatty_open_only(PyObject *op, PyObject *Py_UNUSED(ignored)) +_io_FileIO_isatty_open_only(PyObject *op, PyObject *Py_UNUSED(dummy)) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) { Py_RETURN_FALSE; } @@ -1264,28 +1264,28 @@ static PyMethodDef fileio_methods[] = { static PyObject * fileio_get_closed(PyObject *op, void *closure) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); return PyBool_FromLong((long)(self->fd < 0)); } static PyObject * fileio_get_closefd(PyObject *op, void *closure) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); return PyBool_FromLong((long)(self->closefd)); } static PyObject * fileio_get_mode(PyObject *op, void *closure) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); return PyUnicode_FromString(mode_string(self)); } static PyObject * fileio_get_blksize(PyObject *op, void *closure) { - fileio *self = _PyFileIO_CAST(op); + fileio *self = PyFileIO_CAST(op); #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE if (self->stat_atopen != NULL && self->stat_atopen->st_blksize > 1) { return PyLong_FromLong(self->stat_atopen->st_blksize); diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index f87043df126895..7e0822e3350eeb 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -35,6 +35,8 @@ typedef struct { PyObject *weakreflist; } iobase; +#define iobase_CAST(op) ((iobase *)(op)) + PyDoc_STRVAR(iobase_doc, "The abstract base class for all I/O classes.\n" "\n" @@ -343,16 +345,18 @@ _PyIOBase_finalize(PyObject *self) } static int -iobase_traverse(iobase *self, visitproc visit, void *arg) +iobase_traverse(PyObject *op, visitproc visit, void *arg) { + iobase *self = iobase_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } static int -iobase_clear(iobase *self) +iobase_clear(PyObject *op) { + iobase *self = iobase_CAST(op); Py_CLEAR(self->dict); return 0; } @@ -360,14 +364,15 @@ iobase_clear(iobase *self) /* Destructor */ static void -iobase_dealloc(iobase *self) +iobase_dealloc(PyObject *op) { /* NOTE: since IOBaseObject has its own dict, Python-defined attributes are still available here for close() to use. However, if the derived class declares a __slots__, those slots are already gone. */ - if (_PyIOBase_finalize((PyObject *) self) < 0) { + iobase *self = iobase_CAST(op); + if (_PyIOBase_finalize(op) < 0) { /* When called from a heap type's dealloc, the type will be decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ if (_PyType_HasFeature(Py_TYPE(self), Py_TPFLAGS_HEAPTYPE)) { @@ -378,9 +383,9 @@ iobase_dealloc(iobase *self) PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); - tp->tp_free((PyObject *)self); + tp->tp_free(self); Py_DECREF(tp); } @@ -853,7 +858,7 @@ static PyMethodDef iobase_methods[] = { static PyGetSetDef iobase_getset[] = { {"__dict__", PyObject_GenericGetDict, NULL, NULL}, - {"closed", (getter)iobase_closed_get, NULL, NULL}, + {"closed", iobase_closed_get, NULL, NULL}, {NULL} }; diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index d1a71298a9087f..9d1bfa3ea05cea 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -45,6 +45,8 @@ typedef struct { _PyIO_State *module_state; } stringio; +#define stringio_CAST(op) ((stringio *)(op)) + #define clinic_state() (find_io_state_by_def(Py_TYPE(self))) #include "clinic/stringio.c.h" #undef clinic_state @@ -402,9 +404,10 @@ _io_StringIO_readline_impl(stringio *self, Py_ssize_t size) } static PyObject * -stringio_iternext(stringio *self) +stringio_iternext(PyObject *op) { PyObject *line; + stringio *self = stringio_CAST(op); CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -416,8 +419,7 @@ stringio_iternext(stringio *self) } else { /* XXX is subclassing StringIO really supported? */ - line = PyObject_CallMethodNoArgs((PyObject *)self, - &_Py_ID(readline)); + line = PyObject_CallMethodNoArgs(op, &_Py_ID(readline)); if (line && !PyUnicode_Check(line)) { PyErr_Format(PyExc_OSError, "readline() should have returned a str object, " @@ -591,8 +593,9 @@ _io_StringIO_close_impl(stringio *self) } static int -stringio_traverse(stringio *self, visitproc visit, void *arg) +stringio_traverse(PyObject *op, visitproc visit, void *arg) { + stringio *self = stringio_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->readnl); Py_VISIT(self->writenl); @@ -602,8 +605,9 @@ stringio_traverse(stringio *self, visitproc visit, void *arg) } static int -stringio_clear(stringio *self) +stringio_clear(PyObject *op) { + stringio *self = stringio_CAST(op); Py_CLEAR(self->readnl); Py_CLEAR(self->writenl); Py_CLEAR(self->decoder); @@ -612,8 +616,9 @@ stringio_clear(stringio *self) } static void -stringio_dealloc(stringio *self) +stringio_dealloc(PyObject *op) { + stringio *self = stringio_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); self->ok = 0; @@ -622,9 +627,9 @@ stringio_dealloc(stringio *self) self->buf = NULL; } PyUnicodeWriter_Discard(self->writer); - (void)stringio_clear(self); + (void)stringio_clear(op); if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); } tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 791ee070401fe5..935aaab20a031f 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -223,6 +223,8 @@ struct nldecoder_object { unsigned int seennl: 3; }; +#define nldecoder_object_CAST(op) ((nldecoder_object *)(op)) + /*[clinic input] _io.IncrementalNewlineDecoder.__init__ decoder: object @@ -263,9 +265,9 @@ _io_IncrementalNewlineDecoder___init___impl(nldecoder_object *self, } static int -incrementalnewlinedecoder_traverse(nldecoder_object *self, visitproc visit, - void *arg) +incrementalnewlinedecoder_traverse(PyObject *op, visitproc visit, void *arg) { + nldecoder_object *self = nldecoder_object_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->decoder); Py_VISIT(self->errors); @@ -273,20 +275,22 @@ incrementalnewlinedecoder_traverse(nldecoder_object *self, visitproc visit, } static int -incrementalnewlinedecoder_clear(nldecoder_object *self) +incrementalnewlinedecoder_clear(PyObject *op) { + nldecoder_object *self = nldecoder_object_CAST(op); Py_CLEAR(self->decoder); Py_CLEAR(self->errors); return 0; } static void -incrementalnewlinedecoder_dealloc(nldecoder_object *self) +incrementalnewlinedecoder_dealloc(PyObject *op) { + nldecoder_object *self = nldecoder_object_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - (void)incrementalnewlinedecoder_clear(self); - tp->tp_free((PyObject *)self); + (void)incrementalnewlinedecoder_clear(op); + tp->tp_free(self); Py_DECREF(tp); } @@ -323,7 +327,7 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself, { PyObject *output; Py_ssize_t output_len; - nldecoder_object *self = (nldecoder_object *) myself; + nldecoder_object *self = nldecoder_object_CAST(myself); CHECK_INITIALIZED_DECODER(self); @@ -625,8 +629,9 @@ _io_IncrementalNewlineDecoder_reset_impl(nldecoder_object *self) } static PyObject * -incrementalnewlinedecoder_newlines_get(nldecoder_object *self, void *context) +incrementalnewlinedecoder_newlines_get(PyObject *op, void *Py_UNUSED(context)) { + nldecoder_object *self = nldecoder_object_CAST(op); CHECK_INITIALIZED_DECODER(self); switch (self->seennl) { @@ -652,8 +657,7 @@ incrementalnewlinedecoder_newlines_get(nldecoder_object *self, void *context) /* TextIOWrapper */ -typedef PyObject * - (*encodefunc_t)(PyObject *, PyObject *); +typedef PyObject *(*encodefunc_t)(PyObject *, PyObject *); struct textio { @@ -716,6 +720,8 @@ struct textio _PyIO_State *state; }; +#define textio_CAST(op) ((textio *)(op)) + static void textiowrapper_set_decoded_chars(textio *self, PyObject *chars); @@ -723,78 +729,81 @@ textiowrapper_set_decoded_chars(textio *self, PyObject *chars); encoding methods for the most popular encodings. */ static PyObject * -ascii_encode(textio *self, PyObject *text) +ascii_encode(PyObject *op, PyObject *text) { + textio *self = textio_CAST(op); return _PyUnicode_AsASCIIString(text, PyUnicode_AsUTF8(self->errors)); } static PyObject * -utf16be_encode(textio *self, PyObject *text) +utf16be_encode(PyObject *op, PyObject *text) { - return _PyUnicode_EncodeUTF16(text, - PyUnicode_AsUTF8(self->errors), 1); + textio *self = textio_CAST(op); + return _PyUnicode_EncodeUTF16(text, PyUnicode_AsUTF8(self->errors), 1); } static PyObject * -utf16le_encode(textio *self, PyObject *text) +utf16le_encode(PyObject *op, PyObject *text) { - return _PyUnicode_EncodeUTF16(text, - PyUnicode_AsUTF8(self->errors), -1); + textio *self = textio_CAST(op); + return _PyUnicode_EncodeUTF16(text, PyUnicode_AsUTF8(self->errors), -1); } static PyObject * -utf16_encode(textio *self, PyObject *text) +utf16_encode(PyObject *op, PyObject *text) { + textio *self = textio_CAST(op); if (!self->encoding_start_of_stream) { /* Skip the BOM and use native byte ordering */ #if PY_BIG_ENDIAN - return utf16be_encode(self, text); + return utf16be_encode(op, text); #else - return utf16le_encode(self, text); + return utf16le_encode(op, text); #endif } - return _PyUnicode_EncodeUTF16(text, - PyUnicode_AsUTF8(self->errors), 0); + return _PyUnicode_EncodeUTF16(text, PyUnicode_AsUTF8(self->errors), 0); } static PyObject * -utf32be_encode(textio *self, PyObject *text) +utf32be_encode(PyObject *op, PyObject *text) { - return _PyUnicode_EncodeUTF32(text, - PyUnicode_AsUTF8(self->errors), 1); + textio *self = textio_CAST(op); + return _PyUnicode_EncodeUTF32(text, PyUnicode_AsUTF8(self->errors), 1); } static PyObject * -utf32le_encode(textio *self, PyObject *text) +utf32le_encode(PyObject *op, PyObject *text) { - return _PyUnicode_EncodeUTF32(text, - PyUnicode_AsUTF8(self->errors), -1); + textio *self = textio_CAST(op); + return _PyUnicode_EncodeUTF32(text, PyUnicode_AsUTF8(self->errors), -1); } static PyObject * -utf32_encode(textio *self, PyObject *text) +utf32_encode(PyObject *op, PyObject *text) { + textio *self = textio_CAST(op); if (!self->encoding_start_of_stream) { /* Skip the BOM and use native byte ordering */ #if PY_BIG_ENDIAN - return utf32be_encode(self, text); + return utf32be_encode(op, text); #else - return utf32le_encode(self, text); + return utf32le_encode(op, text); #endif } - return _PyUnicode_EncodeUTF32(text, - PyUnicode_AsUTF8(self->errors), 0); + return _PyUnicode_EncodeUTF32(text, PyUnicode_AsUTF8(self->errors), 0); } static PyObject * -utf8_encode(textio *self, PyObject *text) +utf8_encode(PyObject *op, PyObject *text) { + textio *self = textio_CAST(op); return _PyUnicode_AsUTF8String(text, PyUnicode_AsUTF8(self->errors)); } static PyObject * -latin1_encode(textio *self, PyObject *text) +latin1_encode(PyObject *op, PyObject *text) { + textio *self = textio_CAST(op); return _PyUnicode_AsLatin1String(text, PyUnicode_AsUTF8(self->errors)); } @@ -802,9 +811,7 @@ latin1_encode(textio *self, PyObject *text) static inline int is_asciicompat_encoding(encodefunc_t f) { - return f == (encodefunc_t) ascii_encode - || f == (encodefunc_t) latin1_encode - || f == (encodefunc_t) utf8_encode; + return f == ascii_encode || f == latin1_encode || f == utf8_encode; } /* Map normalized encoding names onto the specialized encoding funcs */ @@ -815,15 +822,15 @@ typedef struct { } encodefuncentry; static const encodefuncentry encodefuncs[] = { - {"ascii", (encodefunc_t) ascii_encode}, - {"iso8859-1", (encodefunc_t) latin1_encode}, - {"utf-8", (encodefunc_t) utf8_encode}, - {"utf-16-be", (encodefunc_t) utf16be_encode}, - {"utf-16-le", (encodefunc_t) utf16le_encode}, - {"utf-16", (encodefunc_t) utf16_encode}, - {"utf-32-be", (encodefunc_t) utf32be_encode}, - {"utf-32-le", (encodefunc_t) utf32le_encode}, - {"utf-32", (encodefunc_t) utf32_encode}, + {"ascii", ascii_encode}, + {"iso8859-1", latin1_encode}, + {"utf-8", utf8_encode}, + {"utf-16-be", utf16be_encode}, + {"utf-16-le", utf16le_encode}, + {"utf-16", utf16_encode}, + {"utf-32-be", utf32be_encode}, + {"utf-32-le", utf32le_encode}, + {"utf-32", utf32_encode}, {NULL, NULL} }; @@ -1433,8 +1440,9 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding, } static int -textiowrapper_clear(textio *self) +textiowrapper_clear(PyObject *op) { + textio *self = textio_CAST(op); self->ok = 0; Py_CLEAR(self->buffer); Py_CLEAR(self->encoding); @@ -1452,24 +1460,26 @@ textiowrapper_clear(textio *self) } static void -textiowrapper_dealloc(textio *self) +textiowrapper_dealloc(PyObject *op) { + textio *self = textio_CAST(op); PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; - if (_PyIOBase_finalize((PyObject *) self) < 0) + if (_PyIOBase_finalize(op) < 0) return; self->ok = 0; _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)self); - (void)textiowrapper_clear(self); - tp->tp_free((PyObject *)self); + PyObject_ClearWeakRefs(op); + (void)textiowrapper_clear(op); + tp->tp_free(self); Py_DECREF(tp); } static int -textiowrapper_traverse(textio *self, visitproc visit, void *arg) +textiowrapper_traverse(PyObject *op, visitproc visit, void *arg) { + textio *self = textio_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->buffer); Py_VISIT(self->encoding); @@ -2963,10 +2973,11 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) } static PyObject * -textiowrapper_repr(textio *self) +textiowrapper_repr(PyObject *op) { PyObject *nameobj, *modeobj, *res, *s; int status; + textio *self = textio_CAST(op); const char *type_name = Py_TYPE(self)->tp_name; CHECK_INITIALIZED(self); @@ -2975,7 +2986,7 @@ textiowrapper_repr(textio *self) if (res == NULL) return NULL; - status = Py_ReprEnter((PyObject *)self); + status = Py_ReprEnter(op); if (status != 0) { if (status > 0) { PyErr_Format(PyExc_RuntimeError, @@ -2984,7 +2995,7 @@ textiowrapper_repr(textio *self) } goto error; } - if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) { + if (PyObject_GetOptionalAttr(op, &_Py_ID(name), &nameobj) < 0) { if (!PyErr_ExceptionMatches(PyExc_ValueError)) { goto error; } @@ -3000,7 +3011,7 @@ textiowrapper_repr(textio *self) if (res == NULL) goto error; } - if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(mode), &modeobj) < 0) { + if (PyObject_GetOptionalAttr(op, &_Py_ID(mode), &modeobj) < 0) { goto error; } if (modeobj != NULL) { @@ -3016,14 +3027,14 @@ textiowrapper_repr(textio *self) res, self->encoding); Py_DECREF(res); if (status == 0) { - Py_ReprLeave((PyObject *)self); + Py_ReprLeave(op); } return s; error: Py_XDECREF(res); if (status == 0) { - Py_ReprLeave((PyObject *)self); + Py_ReprLeave(op); } return NULL; } @@ -3163,9 +3174,10 @@ _io_TextIOWrapper_close_impl(textio *self) } static PyObject * -textiowrapper_iternext(textio *self) +textiowrapper_iternext(PyObject *op) { PyObject *line; + textio *self = textio_CAST(op); CHECK_ATTACHED(self); @@ -3175,8 +3187,7 @@ textiowrapper_iternext(textio *self) line = _textiowrapper_readline(self, -1); } else { - line = PyObject_CallMethodNoArgs((PyObject *)self, - &_Py_ID(readline)); + line = PyObject_CallMethodNoArgs(op, &_Py_ID(readline)); if (line && !PyUnicode_Check(line)) { PyErr_Format(PyExc_OSError, "readline() should have returned a str object, " @@ -3313,7 +3324,7 @@ static PyMethodDef incrementalnewlinedecoder_methods[] = { }; static PyGetSetDef incrementalnewlinedecoder_getset[] = { - {"newlines", (getter)incrementalnewlinedecoder_newlines_get, NULL, NULL}, + {"newlines", incrementalnewlinedecoder_newlines_get, NULL, NULL}, {NULL} }; diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 3fa0301e337991..27c320ed073103 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -221,6 +221,8 @@ typedef struct { wchar_t wbuf; } winconsoleio; +#define winconsoleio_CAST(op) ((winconsoleio *)(op)) + int _PyWindowsConsoleIO_closed(PyObject *self) { @@ -492,32 +494,35 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, } static int -winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg) +winconsoleio_traverse(PyObject *op, visitproc visit, void *arg) { + winconsoleio *self = winconsoleio_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->dict); return 0; } static int -winconsoleio_clear(winconsoleio *self) +winconsoleio_clear(PyObject *op) { + winconsoleio *self = winconsoleio_CAST(op); Py_CLEAR(self->dict); return 0; } static void -winconsoleio_dealloc(winconsoleio *self) +winconsoleio_dealloc(PyObject *op) { + winconsoleio *self = winconsoleio_CAST(op); PyTypeObject *tp = Py_TYPE(self); self->finalizing = 1; - if (_PyIOBase_finalize((PyObject *) self) < 0) + if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); - tp->tp_free((PyObject *)self); + tp->tp_free(self); Py_DECREF(tp); } @@ -1137,9 +1142,10 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, PyTypeObject *cls, } static PyObject * -winconsoleio_repr(winconsoleio *self) +winconsoleio_repr(PyObject *op) { - const char *type_name = (Py_TYPE((PyObject *)self)->tp_name); + winconsoleio *self = winconsoleio_CAST(op); + const char *type_name = Py_TYPE(self)->tp_name; if (self->fd == -1) { return PyUnicode_FromFormat("<%.100s [closed]>", type_name); @@ -1197,28 +1203,31 @@ static PyMethodDef winconsoleio_methods[] = { /* 'closed' and 'mode' are attributes for compatibility with FileIO. */ static PyObject * -get_closed(winconsoleio *self, void *closure) +get_closed(PyObject *op, void *Py_UNUSED(closure)) { + winconsoleio *self = winconsoleio_CAST(op); return PyBool_FromLong((long)(self->fd == -1)); } static PyObject * -get_closefd(winconsoleio *self, void *closure) +get_closefd(PyObject *op, void *Py_UNUSED(closure)) { + winconsoleio *self = winconsoleio_CAST(op); return PyBool_FromLong((long)(self->closefd)); } static PyObject * -get_mode(winconsoleio *self, void *closure) +get_mode(PyObject *op, void *Py_UNUSED(closure)) { + winconsoleio *self = winconsoleio_CAST(op); return PyUnicode_FromString(self->readable ? "rb" : "wb"); } static PyGetSetDef winconsoleio_getsetlist[] = { - {"closed", (getter)get_closed, NULL, "True if the file is closed"}, - {"closefd", (getter)get_closefd, NULL, + {"closed", get_closed, NULL, "True if the file is closed"}, + {"closefd", get_closefd, NULL, "True if the file descriptor will be closed by close()."}, - {"mode", (getter)get_mode, NULL, "String giving the file mode"}, + {"mode", get_mode, NULL, "String giving the file mode"}, {NULL}, }; From 624b93ed67332bbaf758bf963351052acc6ccf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:26:14 +0100 Subject: [PATCH 131/311] gh-111178: fix UBSan failures in `Modules/arraymodule.c` (GH-129772) * fix UBSan failures for `arrayobject`, `arrayiterobject` * suppress unused return values --- Modules/arraymodule.c | 99 ++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index dc1729a7a3a558..5b86ec98393e48 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -79,6 +79,9 @@ get_array_state(PyObject *module) #define get_array_state_by_class(cls) \ (get_array_state(PyType_GetModule(cls))) +#define arrayobject_CAST(op) ((arrayobject *)(op)) +#define arrayiterobject_CAST(op) ((arrayiterobject *)(op)) + enum machine_format_code { UNKNOWN_FORMAT = -1, /* UNKNOWN_FORMAT is used to indicate that the machine format for an @@ -712,22 +715,25 @@ ins1(arrayobject *self, Py_ssize_t where, PyObject *v) /* Methods */ static int -array_tp_traverse(arrayobject *op, visitproc visit, void *arg) +array_tp_traverse(PyObject *op, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(op)); return 0; } static void -array_dealloc(arrayobject *op) +array_dealloc(PyObject *op) { PyTypeObject *tp = Py_TYPE(op); PyObject_GC_UnTrack(op); - if (op->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) op); - if (op->ob_item != NULL) - PyMem_Free(op->ob_item); + arrayobject *self = arrayobject_CAST(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } + if (self->ob_item != NULL) { + PyMem_Free(self->ob_item); + } tp->tp_free(op); Py_DECREF(tp); } @@ -843,19 +849,19 @@ array_richcompare(PyObject *v, PyObject *w, int op) } static Py_ssize_t -array_length(arrayobject *a) +array_length(PyObject *op) { - return Py_SIZE(a); + return Py_SIZE(op); } static PyObject * -array_item(arrayobject *a, Py_ssize_t i) +array_item(PyObject *op, Py_ssize_t i) { - if (i < 0 || i >= Py_SIZE(a)) { + if (i < 0 || i >= Py_SIZE(op)) { PyErr_SetString(PyExc_IndexError, "array index out of range"); return NULL; } - return getarrayitem((PyObject *)a, i); + return getarrayitem(op, i); } static PyObject * @@ -930,8 +936,9 @@ array_array___deepcopy__(arrayobject *self, PyObject *unused) } static PyObject * -array_concat(arrayobject *a, PyObject *bb) +array_concat(PyObject *op, PyObject *bb) { + arrayobject *a = arrayobject_CAST(op); array_state *state = find_array_state_by_type(Py_TYPE(a)); Py_ssize_t size; arrayobject *np; @@ -966,8 +973,9 @@ array_concat(arrayobject *a, PyObject *bb) } static PyObject * -array_repeat(arrayobject *a, Py_ssize_t n) +array_repeat(PyObject *op, Py_ssize_t n) { + arrayobject *a = arrayobject_CAST(op); array_state *state = find_array_state_by_type(Py_TYPE(a)); if (n < 0) @@ -1026,8 +1034,9 @@ array_del_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh) } static int -array_ass_item(arrayobject *a, Py_ssize_t i, PyObject *v) +array_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) { + arrayobject *a = arrayobject_CAST(op); if (i < 0 || i >= Py_SIZE(a)) { PyErr_SetString(PyExc_IndexError, "array assignment index out of range"); @@ -1045,7 +1054,7 @@ setarrayitem(PyObject *a, Py_ssize_t i, PyObject *v) array_state *state = find_array_state_by_type(Py_TYPE(a)); assert(array_Check(a, state)); #endif - return array_ass_item((arrayobject *)a, i, v); + return array_ass_item(a, i, v); } static int @@ -1105,8 +1114,9 @@ array_do_extend(array_state *state, arrayobject *self, PyObject *bb) } static PyObject * -array_inplace_concat(arrayobject *self, PyObject *bb) +array_inplace_concat(PyObject *op, PyObject *bb) { + arrayobject *self = arrayobject_CAST(op); array_state *state = find_array_state_by_type(Py_TYPE(self)); if (!array_Check(bb, state)) { @@ -1121,8 +1131,9 @@ array_inplace_concat(arrayobject *self, PyObject *bb) } static PyObject * -array_inplace_repeat(arrayobject *self, Py_ssize_t n) +array_inplace_repeat(PyObject *op, Py_ssize_t n) { + arrayobject *self = arrayobject_CAST(op); const Py_ssize_t array_size = Py_SIZE(self); if (array_size > 0 && n != 1 ) { @@ -1236,13 +1247,13 @@ array_array_index_impl(arrayobject *self, PyObject *v, Py_ssize_t start, } static int -array_contains(arrayobject *self, PyObject *v) +array_contains(PyObject *self, PyObject *v) { Py_ssize_t i; int cmp; for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(self); i++) { - PyObject *selfi = getarrayitem((PyObject *)self, i); + PyObject *selfi = getarrayitem(self, i); if (selfi == NULL) return -1; cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); @@ -2349,22 +2360,24 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, } static PyObject * -array_get_typecode(arrayobject *a, void *closure) +array_get_typecode(PyObject *op, void *Py_UNUSED(closure)) { + arrayobject *a = arrayobject_CAST(op); char typecode = a->ob_descr->typecode; return PyUnicode_FromOrdinal(typecode); } static PyObject * -array_get_itemsize(arrayobject *a, void *closure) +array_get_itemsize(PyObject *op, void *Py_UNUSED(closure)) { + arrayobject *a = arrayobject_CAST(op); return PyLong_FromLong((long)a->ob_descr->itemsize); } static PyGetSetDef array_getsets [] = { - {"typecode", (getter) array_get_typecode, NULL, + {"typecode", array_get_typecode, NULL, "the typecode character used to create the array"}, - {"itemsize", (getter) array_get_itemsize, NULL, + {"itemsize", array_get_itemsize, NULL, "the size, in bytes, of one array item"}, {NULL} }; @@ -2398,11 +2411,12 @@ static PyMethodDef array_methods[] = { }; static PyObject * -array_repr(arrayobject *a) +array_repr(PyObject *op) { char typecode; PyObject *s, *v = NULL; Py_ssize_t len; + arrayobject *a = arrayobject_CAST(op); len = Py_SIZE(a); typecode = a->ob_descr->typecode; @@ -2425,8 +2439,9 @@ array_repr(arrayobject *a) } static PyObject* -array_subscr(arrayobject* self, PyObject* item) +array_subscr(PyObject *op, PyObject *item) { + arrayobject *self = arrayobject_CAST(op); array_state *state = find_array_state_by_type(Py_TYPE(self)); if (PyIndex_Check(item)) { @@ -2436,7 +2451,7 @@ array_subscr(arrayobject* self, PyObject* item) } if (i < 0) i += Py_SIZE(self); - return array_item(self, i); + return array_item(op, i); } else if (PySlice_Check(item)) { Py_ssize_t start, stop, step, slicelength, i; @@ -2488,9 +2503,10 @@ array_subscr(arrayobject* self, PyObject* item) } static int -array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) +array_ass_subscr(PyObject *op, PyObject *item, PyObject *value) { Py_ssize_t start, stop, step, slicelength, needed; + arrayobject *self = arrayobject_CAST(op); array_state* state = find_array_state_by_type(Py_TYPE(self)); arrayobject* other; int itemsize; @@ -2542,7 +2558,7 @@ array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) value = array_slice(other, 0, needed); if (value == NULL) return -1; - ret = array_ass_subscr(self, item, value); + ret = array_ass_subscr(op, item, value); Py_DECREF(value); return ret; } @@ -2649,7 +2665,7 @@ static const void *emptybuf = ""; static int -array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags) +array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) { if (view == NULL) { PyErr_SetString(PyExc_BufferError, @@ -2657,6 +2673,7 @@ array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags) return -1; } + arrayobject *self = arrayobject_CAST(op); view->buf = (void *)self->ob_item; view->obj = Py_NewRef(self); if (view->buf == NULL) @@ -2689,8 +2706,9 @@ array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags) } static void -array_buffer_relbuf(arrayobject *self, Py_buffer *view) +array_buffer_relbuf(PyObject *op, Py_buffer *Py_UNUSED(view)) { + arrayobject *self = arrayobject_CAST(op); self->ob_exports--; } @@ -2925,7 +2943,7 @@ typecode -- the typecode character used to create the array\n\ itemsize -- the length in bytes of one array item\n\ "); -static PyObject *array_iter(arrayobject *ao); +static PyObject *array_iter(PyObject *op); static struct PyMemberDef array_members[] = { {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(arrayobject, weakreflist), Py_READONLY}, @@ -2985,8 +3003,9 @@ class array.arrayiterator "arrayiterobject *" "find_array_state_by_type(type)->A /*[clinic end generated code: output=da39a3ee5e6b4b0d input=fb46d5ef98dd95ff]*/ static PyObject * -array_iter(arrayobject *ao) +array_iter(PyObject *op) { + arrayobject *ao = arrayobject_CAST(op); array_state *state = find_array_state_by_type(Py_TYPE(ao)); arrayiterobject *it; @@ -3007,16 +3026,15 @@ array_iter(arrayobject *ao) } static PyObject * -arrayiter_next(arrayiterobject *it) +arrayiter_next(PyObject *op) { - arrayobject *ao; - + arrayiterobject *it = arrayiterobject_CAST(op); assert(it != NULL); #ifndef NDEBUG array_state *state = find_array_state_by_type(Py_TYPE(it)); assert(PyObject_TypeCheck(it, state->ArrayIterType)); #endif - ao = it->ao; + arrayobject *ao = it->ao; if (ao == NULL) { return NULL; } @@ -3032,10 +3050,10 @@ arrayiter_next(arrayiterobject *it) } static void -arrayiter_dealloc(arrayiterobject *it) +arrayiter_dealloc(PyObject *op) { + arrayiterobject *it = arrayiterobject_CAST(op); PyTypeObject *tp = Py_TYPE(it); - PyObject_GC_UnTrack(it); Py_XDECREF(it->ao); PyObject_GC_Del(it); @@ -3043,8 +3061,9 @@ arrayiter_dealloc(arrayiterobject *it) } static int -arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg) +arrayiter_traverse(PyObject *op, visitproc visit, void *arg) { + arrayiterobject *it = arrayiterobject_CAST(op); Py_VISIT(Py_TYPE(it)); Py_VISIT(it->ao); return 0; @@ -3156,7 +3175,7 @@ array_clear(PyObject *module) static void array_free(void *module) { - array_clear((PyObject *)module); + (void)array_clear((PyObject *)module); } /* No functions in array module. */ From 12e1d3011b0ff427b7be36d5f5d2d63014c6a54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:47:19 +0100 Subject: [PATCH 132/311] gh-111178: fix UBSan failures in `Objects/floatobject.c` (GH-129776) fix UBSan failures for `PyFloatObject` --- Objects/floatobject.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 7ca43033d722ab..3b72a1e7c37b7f 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -369,8 +369,9 @@ _Py_convert_int_to_double(PyObject **v, double *dbl) } static PyObject * -float_repr(PyFloatObject *v) +float_repr(PyObject *op) { + PyFloatObject *v = _PyFloat_CAST(op); PyObject *result; char *buf; @@ -579,9 +580,10 @@ float_richcompare(PyObject *v, PyObject *w, int op) } static Py_hash_t -float_hash(PyFloatObject *v) +float_hash(PyObject *op) { - return _Py_HashDouble((PyObject *)v, v->ob_fval); + PyFloatObject *v = _PyFloat_CAST(op); + return _Py_HashDouble(op, v->ob_fval); } static PyObject * @@ -851,20 +853,23 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) #undef DOUBLE_IS_ODD_INTEGER static PyObject * -float_neg(PyFloatObject *v) +float_neg(PyObject *op) { + PyFloatObject *v = _PyFloat_CAST(op); return PyFloat_FromDouble(-v->ob_fval); } static PyObject * -float_abs(PyFloatObject *v) +float_abs(PyObject *op) { + PyFloatObject *v = _PyFloat_CAST(op); return PyFloat_FromDouble(fabs(v->ob_fval)); } static int -float_bool(PyFloatObject *v) +float_bool(PyObject *op) { + PyFloatObject *v = _PyFloat_CAST(op); return v->ob_fval != 0.0; } @@ -1206,7 +1211,7 @@ float_hex_impl(PyObject *self) CONVERT_TO_DOUBLE(self, x); if (isnan(x) || isinf(x)) - return float_repr((PyFloatObject *)self); + return float_repr(self); if (x == 0.0) { if (copysign(1.0, x) == -1.0) @@ -1651,7 +1656,7 @@ float_subtype_new(PyTypeObject *type, PyObject *x) } static PyObject * -float_vectorcall(PyObject *type, PyObject * const*args, +float_vectorcall(PyObject *type, PyObject *const *args, size_t nargsf, PyObject *kwnames) { if (!_PyArg_NoKwnames("float", kwnames)) { @@ -1771,13 +1776,13 @@ float___getformat___impl(PyTypeObject *type, const char *typestr) static PyObject * -float_getreal(PyObject *v, void *closure) +float_getreal(PyObject *v, void *Py_UNUSED(closure)) { return float_float(v); } static PyObject * -float_getimag(PyObject *v, void *closure) +float_getimag(PyObject *Py_UNUSED(v), void *Py_UNUSED(closure)) { return PyFloat_FromDouble(0.0); } @@ -1829,11 +1834,11 @@ static PyMethodDef float_methods[] = { static PyGetSetDef float_getset[] = { {"real", - float_getreal, (setter)NULL, + float_getreal, NULL, "the real part of a complex number", NULL}, {"imag", - float_getimag, (setter)NULL, + float_getimag, NULL, "the imaginary part of a complex number", NULL}, {NULL} /* Sentinel */ @@ -1847,10 +1852,10 @@ static PyNumberMethods float_as_number = { float_rem, /* nb_remainder */ float_divmod, /* nb_divmod */ float_pow, /* nb_power */ - (unaryfunc)float_neg, /* nb_negative */ + float_neg, /* nb_negative */ float_float, /* nb_positive */ - (unaryfunc)float_abs, /* nb_absolute */ - (inquiry)float_bool, /* nb_bool */ + float_abs, /* nb_absolute */ + float_bool, /* nb_bool */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ @@ -1881,16 +1886,16 @@ PyTypeObject PyFloat_Type = { "float", sizeof(PyFloatObject), 0, - (destructor)float_dealloc, /* tp_dealloc */ + float_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ - (reprfunc)float_repr, /* tp_repr */ + float_repr, /* tp_repr */ &float_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - (hashfunc)float_hash, /* tp_hash */ + float_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ @@ -1916,7 +1921,7 @@ PyTypeObject PyFloat_Type = { 0, /* tp_init */ 0, /* tp_alloc */ float_new, /* tp_new */ - .tp_vectorcall = (vectorcallfunc)float_vectorcall, + .tp_vectorcall = float_vectorcall, .tp_version_tag = _Py_TYPE_VERSION_FLOAT, }; From d046421f4edacbf64f9436750a727cb0a4bfbd04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:54:34 +0100 Subject: [PATCH 133/311] gh-111178: fix UBSan failures in `Objects/frameobject.c` (GH-129777) fix UBSan failures for `PyFrameObject`, `PyFrameLocalsProxyObject` --- Objects/frameobject.c | 214 +++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 96 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 15ec4b78235992..8ebcc1a4b5e892 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -16,6 +16,14 @@ #include "pycore_frame.h" #include "opcode.h" // EXTENDED_ARG +#define PyFrameObject_CAST(op) \ + (assert(PyObject_TypeCheck((op), &PyFrame_Type)), (PyFrameObject *)(op)) + +#define PyFrameLocalsProxyObject_CAST(op) \ + ( \ + assert(PyObject_TypeCheck((op), &PyFrameLocalsProxy_Type)), \ + (PyFrameLocalsProxyObject *)(op) \ + ) #define OFF(x) offsetof(PyFrameObject, x) @@ -126,8 +134,8 @@ framelocalsproxy_getkeyindex(PyFrameObject *frame, PyObject* key, bool read) static PyObject * framelocalsproxy_getitem(PyObject *self, PyObject *key) { - PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame; - PyCodeObject* co = _PyFrame_GetCode(frame->f_frame); + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; + PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); int i = framelocalsproxy_getkeyindex(frame, key, true); if (i == -2) { @@ -157,7 +165,7 @@ static int framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value) { /* Merge locals into fast locals */ - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; _PyStackRef *fast = _PyFrame_GetLocalsArray(frame->f_frame); PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); @@ -272,9 +280,9 @@ framelocalsproxy_merge(PyObject* self, PyObject* other) } static PyObject * -framelocalsproxy_keys(PyObject *self, void *Py_UNUSED(ignored)) +framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); PyObject *names = PyList_New(0); if (names == NULL) { @@ -314,8 +322,9 @@ framelocalsproxy_keys(PyObject *self, void *Py_UNUSED(ignored)) static void framelocalsproxy_dealloc(PyObject *self) { + PyFrameLocalsProxyObject *proxy = PyFrameLocalsProxyObject_CAST(self); PyObject_GC_UnTrack(self); - Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame); + Py_CLEAR(proxy->frame); Py_TYPE(self)->tp_free(self); } @@ -355,14 +364,16 @@ framelocalsproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static int framelocalsproxy_tp_clear(PyObject *self) { - Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame); + PyFrameLocalsProxyObject *proxy = PyFrameLocalsProxyObject_CAST(self); + Py_CLEAR(proxy->frame); return 0; } static int framelocalsproxy_visit(PyObject *self, visitproc visit, void *arg) { - Py_VISIT(((PyFrameLocalsProxyObject*)self)->frame); + PyFrameLocalsProxyObject *proxy = PyFrameLocalsProxyObject_CAST(self); + Py_VISIT(proxy->frame); return 0; } @@ -381,27 +392,29 @@ framelocalsproxy_iter(PyObject *self) } static PyObject * -framelocalsproxy_richcompare(PyObject *self, PyObject *other, int op) +framelocalsproxy_richcompare(PyObject *lhs, PyObject *rhs, int op) { - if (PyFrameLocalsProxy_Check(other)) { - bool result = ((PyFrameLocalsProxyObject*)self)->frame == ((PyFrameLocalsProxyObject*)other)->frame; + PyFrameLocalsProxyObject *self = PyFrameLocalsProxyObject_CAST(lhs); + if (PyFrameLocalsProxy_Check(rhs)) { + PyFrameLocalsProxyObject *other = (PyFrameLocalsProxyObject *)rhs; + bool result = self->frame == other->frame; if (op == Py_EQ) { return PyBool_FromLong(result); } else if (op == Py_NE) { return PyBool_FromLong(!result); } - } else if (PyDict_Check(other)) { + } else if (PyDict_Check(rhs)) { PyObject *dct = PyDict_New(); if (dct == NULL) { return NULL; } - if (PyDict_Update(dct, self) < 0) { + if (PyDict_Update(dct, lhs) < 0) { Py_DECREF(dct); return NULL; } - PyObject *result = PyObject_RichCompare(dct, other, op); + PyObject *result = PyObject_RichCompare(dct, rhs, op); Py_DECREF(dct); return result; } @@ -476,10 +489,10 @@ framelocalsproxy_inplace_or(PyObject *self, PyObject *other) return Py_NewRef(self); } -static PyObject* -framelocalsproxy_values(PyObject *self, void *Py_UNUSED(ignored)) +static PyObject * +framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); PyObject *values = PyList_New(0); if (values == NULL) { @@ -513,9 +526,9 @@ framelocalsproxy_values(PyObject *self, void *Py_UNUSED(ignored)) } static PyObject * -framelocalsproxy_items(PyObject *self, void *Py_UNUSED(ignored)) +framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); PyObject *items = PyList_New(0); if (items == NULL) { @@ -571,7 +584,7 @@ framelocalsproxy_items(PyObject *self, void *Py_UNUSED(ignored)) static Py_ssize_t framelocalsproxy_length(PyObject *self) { - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; PyCodeObject *co = _PyFrame_GetCode(frame->f_frame); Py_ssize_t size = 0; @@ -591,7 +604,7 @@ framelocalsproxy_length(PyObject *self) static int framelocalsproxy_contains(PyObject *self, PyObject *key) { - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; int i = framelocalsproxy_getkeyindex(frame, key, true); if (i == -2) { @@ -601,7 +614,7 @@ framelocalsproxy_contains(PyObject *self, PyObject *key) return 1; } - PyObject *extra = ((PyFrameObject*)frame)->f_extra_locals; + PyObject *extra = frame->f_extra_locals; if (extra != NULL) { return PyDict_Contains(extra, key); } @@ -702,7 +715,7 @@ framelocalsproxy_pop(PyObject* self, PyObject *const *args, Py_ssize_t nargs) default_value = args[1]; } - PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame; + PyFrameObject *frame = PyFrameLocalsProxyObject_CAST(self)->frame; int i = framelocalsproxy_getkeyindex(frame, key, false); if (i == -2) { @@ -759,7 +772,7 @@ framelocalsproxy_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) } static PyObject* -framelocalsproxy_reversed(PyObject *self, void *Py_UNUSED(ignored)) +framelocalsproxy_reversed(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *result = framelocalsproxy_keys(self, NULL); @@ -784,42 +797,36 @@ static PySequenceMethods framelocalsproxy_as_sequence = { }; static PyMappingMethods framelocalsproxy_as_mapping = { - framelocalsproxy_length, // mp_length - framelocalsproxy_getitem, // mp_subscript - framelocalsproxy_setitem, // mp_ass_subscript + .mp_length = framelocalsproxy_length, + .mp_subscript = framelocalsproxy_getitem, + .mp_ass_subscript = framelocalsproxy_setitem, }; static PyMethodDef framelocalsproxy_methods[] = { - {"__contains__", framelocalsproxy___contains__, METH_O | METH_COEXIST, - NULL}, - {"__getitem__", framelocalsproxy_getitem, METH_O | METH_COEXIST, - NULL}, - {"update", framelocalsproxy_update, METH_O, - NULL}, - {"__reversed__", _PyCFunction_CAST(framelocalsproxy_reversed), METH_NOARGS, - NULL}, - {"copy", _PyCFunction_CAST(framelocalsproxy_copy), METH_NOARGS, - NULL}, - {"keys", _PyCFunction_CAST(framelocalsproxy_keys), METH_NOARGS, - NULL}, - {"values", _PyCFunction_CAST(framelocalsproxy_values), METH_NOARGS, - NULL}, - {"items", _PyCFunction_CAST(framelocalsproxy_items), METH_NOARGS, - NULL}, - {"get", _PyCFunction_CAST(framelocalsproxy_get), METH_FASTCALL, - NULL}, - {"pop", _PyCFunction_CAST(framelocalsproxy_pop), METH_FASTCALL, - NULL}, - {"setdefault", _PyCFunction_CAST(framelocalsproxy_setdefault), METH_FASTCALL, - NULL}, - {NULL, NULL} /* sentinel */ + {"__contains__", framelocalsproxy___contains__, METH_O | METH_COEXIST, NULL}, + {"__getitem__", framelocalsproxy_getitem, METH_O | METH_COEXIST, NULL}, + {"update", framelocalsproxy_update, METH_O, NULL}, + {"__reversed__", framelocalsproxy_reversed, METH_NOARGS, NULL}, + {"copy", framelocalsproxy_copy, METH_NOARGS, NULL}, + {"keys", framelocalsproxy_keys, METH_NOARGS, NULL}, + {"values", framelocalsproxy_values, METH_NOARGS, NULL}, + {"items", _PyCFunction_CAST(framelocalsproxy_items), METH_NOARGS, NULL}, + {"get", _PyCFunction_CAST(framelocalsproxy_get), METH_FASTCALL, NULL}, + {"pop", _PyCFunction_CAST(framelocalsproxy_pop), METH_FASTCALL, NULL}, + { + "setdefault", + _PyCFunction_CAST(framelocalsproxy_setdefault), + METH_FASTCALL, + NULL + }, + {NULL, NULL} /* sentinel */ }; PyTypeObject PyFrameLocalsProxy_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "FrameLocalsProxy", .tp_basicsize = sizeof(PyFrameLocalsProxyObject), - .tp_dealloc = (destructor)framelocalsproxy_dealloc, + .tp_dealloc = framelocalsproxy_dealloc, .tp_repr = &framelocalsproxy_repr, .tp_as_number = &framelocalsproxy_as_number, .tp_as_sequence = &framelocalsproxy_as_sequence, @@ -845,7 +852,7 @@ _PyFrameLocalsProxy_New(PyFrameObject *frame) return NULL; } - PyObject* proxy = (PyObject*)framelocalsproxy_new(&PyFrameLocalsProxy_Type, args, NULL); + PyObject* proxy = framelocalsproxy_new(&PyFrameLocalsProxy_Type, args, NULL); Py_DECREF(args); return proxy; } @@ -856,8 +863,9 @@ static PyMemberDef frame_memberlist[] = { }; static PyObject * -frame_getlocals(PyFrameObject *f, void *closure) +frame_getlocals(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (f == NULL) { PyErr_BadInternalCall(); return NULL; @@ -903,8 +911,9 @@ PyFrame_GetLineNumber(PyFrameObject *f) } static PyObject * -frame_getlineno(PyFrameObject *f, void *closure) +frame_getlineno(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); int lineno = PyFrame_GetLineNumber(f); if (lineno < 0) { Py_RETURN_NONE; @@ -915,8 +924,9 @@ frame_getlineno(PyFrameObject *f, void *closure) } static PyObject * -frame_getlasti(PyFrameObject *f, void *closure) +frame_getlasti(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); int lasti = _PyInterpreterFrame_LASTI(f->f_frame); if (lasti < 0) { return PyLong_FromLong(-1); @@ -925,8 +935,9 @@ frame_getlasti(PyFrameObject *f, void *closure) } static PyObject * -frame_getglobals(PyFrameObject *f, void *closure) +frame_getglobals(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyObject *globals = f->f_frame->f_globals; if (globals == NULL) { globals = Py_None; @@ -935,8 +946,9 @@ frame_getglobals(PyFrameObject *f, void *closure) } static PyObject * -frame_getbuiltins(PyFrameObject *f, void *closure) +frame_getbuiltins(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyObject *builtins = f->f_frame->f_builtins; if (builtins == NULL) { builtins = Py_None; @@ -945,8 +957,9 @@ frame_getbuiltins(PyFrameObject *f, void *closure) } static PyObject * -frame_getcode(PyFrameObject *f, void *closure) +frame_getcode(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (PySys_Audit("object.__getattr__", "Os", f, "f_code") < 0) { return NULL; } @@ -954,8 +967,9 @@ frame_getcode(PyFrameObject *f, void *closure) } static PyObject * -frame_getback(PyFrameObject *f, void *closure) +frame_getback(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyObject *res = (PyObject *)PyFrame_GetBack(f); if (res == NULL) { Py_RETURN_NONE; @@ -964,15 +978,17 @@ frame_getback(PyFrameObject *f, void *closure) } static PyObject * -frame_gettrace_opcodes(PyFrameObject *f, void *closure) +frame_gettrace_opcodes(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyObject *result = f->f_trace_opcodes ? Py_True : Py_False; return Py_NewRef(result); } static int -frame_settrace_opcodes(PyFrameObject *f, PyObject* value, void *Py_UNUSED(ignored)) +frame_settrace_opcodes(PyObject *op, PyObject* value, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (!PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "attribute value type must be bool"); @@ -1464,8 +1480,9 @@ static bool frame_is_suspended(PyFrameObject *frame) * that time. */ static int -frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored)) +frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyCodeObject *code = _PyFrame_GetCode(f->f_frame); if (p_new_lineno == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); @@ -1658,8 +1675,9 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore } static PyObject * -frame_gettrace(PyFrameObject *f, void *closure) +frame_gettrace(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); PyObject* trace = f->f_trace; if (trace == NULL) trace = Py_None; @@ -1667,8 +1685,9 @@ frame_gettrace(PyFrameObject *f, void *closure) } static int -frame_settrace(PyFrameObject *f, PyObject* v, void *closure) +frame_settrace(PyObject *op, PyObject* v, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (v == Py_None) { v = NULL; } @@ -1682,7 +1701,8 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure) } static PyObject * -frame_getgenerator(PyFrameObject *f, void *arg) { +frame_getgenerator(PyObject *op, void *Py_UNUSED(closure)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { PyObject *gen = (PyObject *)_PyGen_GetGeneratorFromFrame(f->f_frame); return Py_NewRef(gen); @@ -1692,26 +1712,25 @@ frame_getgenerator(PyFrameObject *f, void *arg) { static PyGetSetDef frame_getsetlist[] = { - {"f_back", (getter)frame_getback, NULL, NULL}, - {"f_locals", (getter)frame_getlocals, NULL, NULL}, - {"f_lineno", (getter)frame_getlineno, - (setter)frame_setlineno, NULL}, - {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, - {"f_lasti", (getter)frame_getlasti, NULL, NULL}, - {"f_globals", (getter)frame_getglobals, NULL, NULL}, - {"f_builtins", (getter)frame_getbuiltins, NULL, NULL}, - {"f_code", (getter)frame_getcode, NULL, NULL}, - {"f_trace_opcodes", (getter)frame_gettrace_opcodes, (setter)frame_settrace_opcodes, NULL}, - {"f_generator", (getter)frame_getgenerator, NULL, NULL}, + {"f_back", frame_getback, NULL, NULL}, + {"f_locals", frame_getlocals, NULL, NULL}, + {"f_lineno", frame_getlineno, frame_setlineno, NULL}, + {"f_trace", frame_gettrace, frame_settrace, NULL}, + {"f_lasti", frame_getlasti, NULL, NULL}, + {"f_globals", frame_getglobals, NULL, NULL}, + {"f_builtins", frame_getbuiltins, NULL, NULL}, + {"f_code", frame_getcode, NULL, NULL}, + {"f_trace_opcodes", frame_gettrace_opcodes, frame_settrace_opcodes, NULL}, + {"f_generator", frame_getgenerator, NULL, NULL}, {0} }; static void -frame_dealloc(PyFrameObject *f) +frame_dealloc(PyObject *op) { /* It is the responsibility of the owning generator/coroutine * to have cleared the generator pointer */ - + PyFrameObject *f = PyFrameObject_CAST(op); if (_PyObject_GC_IS_TRACKED(f)) { _PyObject_GC_UNTRACK(f); } @@ -1744,8 +1763,9 @@ frame_dealloc(PyFrameObject *f) } static int -frame_traverse(PyFrameObject *f, visitproc visit, void *arg) +frame_traverse(PyObject *op, visitproc visit, void *arg) { + PyFrameObject *f = PyFrameObject_CAST(op); Py_VISIT(f->f_back); Py_VISIT(f->f_trace); Py_VISIT(f->f_extra_locals); @@ -1758,8 +1778,9 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) } static int -frame_tp_clear(PyFrameObject *f) +frame_tp_clear(PyObject *op) { + PyFrameObject *f = PyFrameObject_CAST(op); Py_CLEAR(f->f_trace); Py_CLEAR(f->f_extra_locals); Py_CLEAR(f->f_locals_cache); @@ -1778,8 +1799,9 @@ frame_tp_clear(PyFrameObject *f) } static PyObject * -frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) +frame_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) { + PyFrameObject *f = PyFrameObject_CAST(op); if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { PyGenObject *gen = _PyGen_GetGeneratorFromFrame(f->f_frame); if (gen->gi_frame_state == FRAME_EXECUTING) { @@ -1795,7 +1817,7 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) } else { assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); - (void)frame_tp_clear(f); + (void)frame_tp_clear(op); } Py_RETURN_NONE; running: @@ -1812,8 +1834,9 @@ PyDoc_STRVAR(clear__doc__, "F.clear(): clear all references held by the frame"); static PyObject * -frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) +frame_sizeof(PyObject *op, PyObject *Py_UNUSED(ignored)) { + PyFrameObject *f = PyFrameObject_CAST(op); Py_ssize_t res; res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus); PyCodeObject *code = _PyFrame_GetCode(f->f_frame); @@ -1825,8 +1848,9 @@ PyDoc_STRVAR(sizeof__doc__, "F.__sizeof__() -> size of F in memory, in bytes"); static PyObject * -frame_repr(PyFrameObject *f) +frame_repr(PyObject *op) { + PyFrameObject *f = PyFrameObject_CAST(op); int lineno = PyFrame_GetLineNumber(f); PyCodeObject *code = _PyFrame_GetCode(f->f_frame); return PyUnicode_FromFormat( @@ -1835,11 +1859,9 @@ frame_repr(PyFrameObject *f) } static PyMethodDef frame_methods[] = { - {"clear", (PyCFunction)frame_clear, METH_NOARGS, - clear__doc__}, - {"__sizeof__", (PyCFunction)frame_sizeof, METH_NOARGS, - sizeof__doc__}, - {NULL, NULL} /* sentinel */ + {"clear", frame_clear, METH_NOARGS, clear__doc__}, + {"__sizeof__", frame_sizeof, METH_NOARGS, sizeof__doc__}, + {NULL, NULL} /* sentinel */ }; PyTypeObject PyFrame_Type = { @@ -1848,12 +1870,12 @@ PyTypeObject PyFrame_Type = { offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus), sizeof(PyObject *), - (destructor)frame_dealloc, /* tp_dealloc */ + frame_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ - (reprfunc)frame_repr, /* tp_repr */ + frame_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -1865,8 +1887,8 @@ PyTypeObject PyFrame_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - (traverseproc)frame_traverse, /* tp_traverse */ - (inquiry)frame_tp_clear, /* tp_clear */ + frame_traverse, /* tp_traverse */ + frame_tp_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ @@ -2186,21 +2208,21 @@ PyObject* PyFrame_GetLocals(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getlocals(frame, NULL); + return frame_getlocals((PyObject *)frame, NULL); } PyObject* PyFrame_GetGlobals(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getglobals(frame, NULL); + return frame_getglobals((PyObject *)frame, NULL); } PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getbuiltins(frame, NULL); + return frame_getbuiltins((PyObject *)frame, NULL); } int From 167cf3ace0db553dce50b2068dfae1f9eae4da6e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 8 Feb 2025 15:38:11 +0100 Subject: [PATCH 134/311] gh-115806: Don't mangle gdbm header dependency detection output (#129390) Replace AC_CACHE_VAL/AC_CHECK_HEADER with a cleaner variant using AC_CACHE_CHECK/AC_PREPROC_IFELSE. The former would produce garbled output when config.cache was reused. It also required directly manipulating GNU Autoconf cache variables. --- configure | 41 ++++++++++++++++++++++------------------- configure.ac | 24 ++++++++---------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/configure b/configure index 65b8e711cdccae..d46bc563a67245 100755 --- a/configure +++ b/configure @@ -17162,26 +17162,28 @@ esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $have_ndbm ($dbm_ndbm)" >&5 printf "%s\n" "$have_ndbm ($dbm_ndbm)" >&6; } -{ ac_cv_header_gdbm_ndbm_h=; unset ac_cv_header_gdbm_ndbm_h;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gdbm/ndbm.h" >&5 +printf %s "checking for gdbm/ndbm.h... " >&6; } if test ${ac_cv_header_gdbm_slash_ndbm_h+y} then : printf %s "(cached) " >&6 else case e in #( - e) - ac_fn_c_check_header_compile "$LINENO" "gdbm/ndbm.h" "ac_cv_header_gdbm_ndbm_h" "$ac_includes_default" -if test "x$ac_cv_header_gdbm_ndbm_h" = xyes + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : ac_cv_header_gdbm_slash_ndbm_h=yes else case e in #( - e) ac_cv_header_gdbm_slash_ndbm_h=no - ;; + e) ac_cv_header_gdbm_slash_ndbm_h=no ;; esac fi - - ;; +rm -f conftest.err conftest.i conftest.$ac_ext ;; esac fi - +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_gdbm_slash_ndbm_h" >&5 +printf "%s\n" "$ac_cv_header_gdbm_slash_ndbm_h" >&6; } if test "x$ac_cv_header_gdbm_slash_ndbm_h" = xyes then : @@ -17191,26 +17193,28 @@ printf "%s\n" "#define HAVE_GDBM_NDBM_H 1" >>confdefs.h fi -{ ac_cv_header_gdbm_ndbm_h=; unset ac_cv_header_gdbm_ndbm_h;} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gdbm-ndbm.h" >&5 +printf %s "checking for gdbm-ndbm.h... " >&6; } if test ${ac_cv_header_gdbm_dash_ndbm_h+y} then : printf %s "(cached) " >&6 else case e in #( - e) - ac_fn_c_check_header_compile "$LINENO" "gdbm-ndbm.h" "ac_cv_header_gdbm_ndbm_h" "$ac_includes_default" -if test "x$ac_cv_header_gdbm_ndbm_h" = xyes + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : ac_cv_header_gdbm_dash_ndbm_h=yes else case e in #( - e) ac_cv_header_gdbm_dash_ndbm_h=no - ;; + e) ac_cv_header_gdbm_dash_ndbm_h=no ;; esac fi - - ;; +rm -f conftest.err conftest.i conftest.$ac_ext ;; esac fi - +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_gdbm_dash_ndbm_h" >&5 +printf "%s\n" "$ac_cv_header_gdbm_dash_ndbm_h" >&6; } if test "x$ac_cv_header_gdbm_dash_ndbm_h" = xyes then : @@ -17219,7 +17223,6 @@ printf "%s\n" "#define HAVE_GDBM_DASH_NDBM_H 1" >>confdefs.h fi -{ ac_cv_header_gdbm_ndbm_h=; unset ac_cv_header_gdbm_ndbm_h;} if test "$ac_cv_header_gdbm_slash_ndbm_h" = yes -o "$ac_cv_header_gdbm_dash_ndbm_h" = yes; then { ac_cv_search_dbm_open=; unset ac_cv_search_dbm_open;} diff --git a/configure.ac b/configure.ac index 0c6063d87d654a..faa8909530389d 100644 --- a/configure.ac +++ b/configure.ac @@ -4430,29 +4430,21 @@ AS_CASE([$ac_cv_search_dbm_open], AC_MSG_RESULT([$have_ndbm ($dbm_ndbm)]) dnl "gdbm-ndbm.h" and "gdbm/ndbm.h" are both normalized to "gdbm_ndbm_h" -dnl unset ac_cv_header_gdbm_ndbm_h to prevent false positive cache hits. -AS_UNSET([ac_cv_header_gdbm_ndbm_h]) -AC_CACHE_VAL([ac_cv_header_gdbm_slash_ndbm_h], [ - AC_CHECK_HEADER( - [gdbm/ndbm.h], - [ac_cv_header_gdbm_slash_ndbm_h=yes], [ac_cv_header_gdbm_slash_ndbm_h=no] - ) -]) +AC_CACHE_CHECK([for gdbm/ndbm.h], [ac_cv_header_gdbm_slash_ndbm_h], + [AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include ])], + [ac_cv_header_gdbm_slash_ndbm_h=yes], + [ac_cv_header_gdbm_slash_ndbm_h=no])]) AS_VAR_IF([ac_cv_header_gdbm_slash_ndbm_h], [yes], [ AC_DEFINE([HAVE_GDBM_NDBM_H], [1], [Define to 1 if you have the header file.]) ]) -AS_UNSET([ac_cv_header_gdbm_ndbm_h]) -AC_CACHE_VAL([ac_cv_header_gdbm_dash_ndbm_h], [ - AC_CHECK_HEADER( - [gdbm-ndbm.h], - [ac_cv_header_gdbm_dash_ndbm_h=yes], [ac_cv_header_gdbm_dash_ndbm_h=no] - ) -]) +AC_CACHE_CHECK([for gdbm-ndbm.h], [ac_cv_header_gdbm_dash_ndbm_h], + [AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include ])], + [ac_cv_header_gdbm_dash_ndbm_h=yes], + [ac_cv_header_gdbm_dash_ndbm_h=no])]) AS_VAR_IF([ac_cv_header_gdbm_dash_ndbm_h], [yes], [ AC_DEFINE([HAVE_GDBM_DASH_NDBM_H], [1], [Define to 1 if you have the header file.]) ]) -AS_UNSET([ac_cv_header_gdbm_ndbm_h]) if test "$ac_cv_header_gdbm_slash_ndbm_h" = yes -o "$ac_cv_header_gdbm_dash_ndbm_h" = yes; then AS_UNSET([ac_cv_search_dbm_open]) From a56ead089caf4e975d7e36036ad98e932822d7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 8 Feb 2025 16:01:57 +0100 Subject: [PATCH 135/311] gh-129173: Use `_PyUnicodeError_GetParams` in `PyCodec_NameReplaceErrors` (GH-129135) --- Python/codecs.c | 234 +++++++++++++++++++++++++----------------------- 1 file changed, 123 insertions(+), 111 deletions(-) diff --git a/Python/codecs.c b/Python/codecs.c index 53680a79082634..6c9f8222079ec8 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -676,6 +676,60 @@ wrong_exception_type(PyObject *exc) PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeTranslateError) +// --- codecs handlers: utilities --------------------------------------------- + +/* + * Return the number of characters (including special prefixes) + * needed to represent 'ch' by codec_handler_write_unicode_hex(). + */ +static inline Py_ssize_t +codec_handler_unicode_hex_width(Py_UCS4 ch) +{ + if (ch >= 0x10000) { + // format: '\\' + 'U' + 8 hex digits + return 1 + 1 + 8; + } + else if (ch >= 0x100) { + // format: '\\' + 'u' + 4 hex digits + return 1 + 1 + 4; + } + else { + // format: '\\' + 'x' + 2 hex digits + return 1 + 1 + 2; + } +} + + +/* + * Write the hexadecimal representation of 'ch' to the buffer pointed by 'p' + * using 2, 4, or 8 characters prefixed by '\x', '\u', or '\U' respectively. + */ +static inline void +codec_handler_write_unicode_hex(Py_UCS1 **p, Py_UCS4 ch) +{ + *(*p)++ = '\\'; + if (ch >= 0x10000) { + *(*p)++ = 'U'; + *(*p)++ = Py_hexdigits[(ch >> 28) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 24) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 20) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 16) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 12) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 8) & 0xf]; + } + else if (ch >= 0x100) { + *(*p)++ = 'u'; + *(*p)++ = Py_hexdigits[(ch >> 12) & 0xf]; + *(*p)++ = Py_hexdigits[(ch >> 8) & 0xf]; + } + else { + *(*p)++ = 'x'; + } + *(*p)++ = Py_hexdigits[(ch >> 4) & 0xf]; + *(*p)++ = Py_hexdigits[ch & 0xf]; +} + + // --- handler: 'strict' ------------------------------------------------------ PyObject *PyCodec_StrictErrors(PyObject *exc) @@ -942,17 +996,8 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) Py_ssize_t ressize = 0; for (Py_ssize_t i = start; i < end; ++i) { - /* object is guaranteed to be "ready" */ Py_UCS4 c = PyUnicode_READ_CHAR(obj, i); - if (c >= 0x10000) { - ressize += 1 + 1 + 8; - } - else if (c >= 0x100) { - ressize += 1 + 1 + 4; - } - else { - ressize += 1 + 1 + 2; - } + ressize += codec_handler_unicode_hex_width(c); } PyObject *res = PyUnicode_New(ressize, 127); if (res == NULL) { @@ -962,122 +1007,86 @@ PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) Py_UCS1 *outp = PyUnicode_1BYTE_DATA(res); for (Py_ssize_t i = start; i < end; ++i) { Py_UCS4 c = PyUnicode_READ_CHAR(obj, i); - *outp++ = '\\'; - if (c >= 0x00010000) { - *outp++ = 'U'; - *outp++ = Py_hexdigits[(c >> 28) & 0xf]; - *outp++ = Py_hexdigits[(c >> 24) & 0xf]; - *outp++ = Py_hexdigits[(c >> 20) & 0xf]; - *outp++ = Py_hexdigits[(c >> 16) & 0xf]; - *outp++ = Py_hexdigits[(c >> 12) & 0xf]; - *outp++ = Py_hexdigits[(c >> 8) & 0xf]; - } - else if (c >= 0x100) { - *outp++ = 'u'; - *outp++ = Py_hexdigits[(c >> 12) & 0xf]; - *outp++ = Py_hexdigits[(c >> 8) & 0xf]; - } - else { - *outp++ = 'x'; - } - *outp++ = Py_hexdigits[(c >> 4) & 0xf]; - *outp++ = Py_hexdigits[c & 0xf]; + codec_handler_write_unicode_hex(&outp, c); } assert(_PyUnicode_CheckConsistency(res, 1)); Py_DECREF(obj); return Py_BuildValue("(Nn)", res, end); } + +// --- handler: 'namereplace' ------------------------------------------------- + PyObject *PyCodec_NameReplaceErrors(PyObject *exc) { - if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeEncodeError)) { - PyObject *restuple; - PyObject *object; - Py_ssize_t i; - Py_ssize_t start; - Py_ssize_t end; - PyObject *res; - Py_UCS1 *outp; - Py_ssize_t ressize; - int replsize; - Py_UCS4 c; - char buffer[256]; /* NAME_MAXLEN */ - if (PyUnicodeEncodeError_GetStart(exc, &start)) - return NULL; - if (PyUnicodeEncodeError_GetEnd(exc, &end)) - return NULL; - if (!(object = PyUnicodeEncodeError_GetObject(exc))) - return NULL; - _PyUnicode_Name_CAPI *ucnhash_capi = _PyUnicode_GetNameCAPI(); - if (ucnhash_capi == NULL) { - return NULL; + if (!_PyIsUnicodeEncodeError(exc)) { + wrong_exception_type(exc); + return NULL; + } + + _PyUnicode_Name_CAPI *ucnhash_capi = _PyUnicode_GetNameCAPI(); + if (ucnhash_capi == NULL) { + return NULL; + } + + PyObject *obj; + Py_ssize_t start, end; + if (_PyUnicodeError_GetParams(exc, + &obj, NULL, + &start, &end, NULL, false) < 0) + { + return NULL; + } + + char buffer[256]; /* NAME_MAXLEN in unicodename_db.h */ + Py_ssize_t imax = start, ressize = 0, replsize; + for (; imax < end; ++imax) { + Py_UCS4 c = PyUnicode_READ_CHAR(obj, imax); + if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { + // If 'c' is recognized by getname(), the corresponding replacement + // is '\\' + 'N' + '{' + NAME + '}', i.e. 1 + 1 + 1 + len(NAME) + 1 + // characters. Failures of getname() are ignored by the handler. + replsize = 1 + 1 + 1 + strlen(buffer) + 1; } - for (i = start, ressize = 0; i < end; ++i) { - /* object is guaranteed to be "ready" */ - c = PyUnicode_READ_CHAR(object, i); - if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { - replsize = 1+1+1+(int)strlen(buffer)+1; - } - else if (c >= 0x10000) { - replsize = 1+1+8; - } - else if (c >= 0x100) { - replsize = 1+1+4; - } - else - replsize = 1+1+2; - if (ressize > PY_SSIZE_T_MAX - replsize) - break; - ressize += replsize; + else { + replsize = codec_handler_unicode_hex_width(c); } - end = i; - res = PyUnicode_New(ressize, 127); - if (res==NULL) - return NULL; - for (i = start, outp = PyUnicode_1BYTE_DATA(res); - i < end; ++i) { - c = PyUnicode_READ_CHAR(object, i); - *outp++ = '\\'; - if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { - *outp++ = 'N'; - *outp++ = '{'; - strcpy((char *)outp, buffer); - outp += strlen(buffer); - *outp++ = '}'; - continue; - } - if (c >= 0x00010000) { - *outp++ = 'U'; - *outp++ = Py_hexdigits[(c>>28)&0xf]; - *outp++ = Py_hexdigits[(c>>24)&0xf]; - *outp++ = Py_hexdigits[(c>>20)&0xf]; - *outp++ = Py_hexdigits[(c>>16)&0xf]; - *outp++ = Py_hexdigits[(c>>12)&0xf]; - *outp++ = Py_hexdigits[(c>>8)&0xf]; - } - else if (c >= 0x100) { - *outp++ = 'u'; - *outp++ = Py_hexdigits[(c>>12)&0xf]; - *outp++ = Py_hexdigits[(c>>8)&0xf]; - } - else - *outp++ = 'x'; - *outp++ = Py_hexdigits[(c>>4)&0xf]; - *outp++ = Py_hexdigits[c&0xf]; + if (ressize > PY_SSIZE_T_MAX - replsize) { + break; } - - assert(outp == PyUnicode_1BYTE_DATA(res) + ressize); - assert(_PyUnicode_CheckConsistency(res, 1)); - restuple = Py_BuildValue("(Nn)", res, end); - Py_DECREF(object); - return restuple; + ressize += replsize; } - else { - wrong_exception_type(exc); + + PyObject *res = PyUnicode_New(ressize, 127); + if (res == NULL) { + Py_DECREF(obj); return NULL; } + + Py_UCS1 *outp = PyUnicode_1BYTE_DATA(res); + for (Py_ssize_t i = start; i < imax; ++i) { + Py_UCS4 c = PyUnicode_READ_CHAR(obj, i); + if (ucnhash_capi->getname(c, buffer, sizeof(buffer), 1)) { + *outp++ = '\\'; + *outp++ = 'N'; + *outp++ = '{'; + (void)strcpy((char *)outp, buffer); + outp += strlen(buffer); + *outp++ = '}'; + } + else { + codec_handler_write_unicode_hex(&outp, c); + } + } + + assert(outp == PyUnicode_1BYTE_DATA(res) + ressize); + assert(_PyUnicode_CheckConsistency(res, 1)); + PyObject *restuple = Py_BuildValue("(Nn)", res, imax); + Py_DECREF(obj); + return restuple; } + #define ENC_UNKNOWN -1 #define ENC_UTF8 0 #define ENC_UTF16BE 1 @@ -1421,11 +1430,14 @@ static PyObject *backslashreplace_errors(PyObject *self, PyObject *exc) return PyCodec_BackslashReplaceErrors(exc); } -static PyObject *namereplace_errors(PyObject *self, PyObject *exc) + +static inline PyObject * +namereplace_errors(PyObject *Py_UNUSED(self), PyObject *exc) { return PyCodec_NameReplaceErrors(exc); } + static PyObject *surrogatepass_errors(PyObject *self, PyObject *exc) { return PyCodec_SurrogatePassErrors(exc); From 1bccd6c34f82f8373c320792323bfd7f7a328bc7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 8 Feb 2025 17:56:57 +0200 Subject: [PATCH 136/311] gh-128317: Move CLI calendar highlighting to private class (#129625) Co-authored-by: Petr Viktorin --- Doc/library/calendar.rst | 21 +-- Lib/calendar.py | 155 +++++++++++++----- ...-02-03-22-31-43.gh-issue-128317.n2Swnh.rst | 2 + 3 files changed, 120 insertions(+), 58 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index 1c6b5e03af3560..39090e36ed9c0d 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -166,18 +166,13 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is the specified width, representing an empty day. The *weekday* parameter is unused. - .. method:: formatweek(theweek, w=0, highlight_day=None) + .. method:: formatweek(theweek, w=0) Return a single week in a string with no newline. If *w* is provided, it specifies the width of the date columns, which are centered. Depends on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. - .. versionchanged:: 3.14 - If *highlight_day* is given, this date is highlighted in color. - This can be :ref:`controlled using environment variables - `. - .. method:: formatweekday(weekday, width) @@ -193,7 +188,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is settings and are padded to the specified width. - .. method:: formatmonth(theyear, themonth, w=0, l=0, highlight_day=None) + .. method:: formatmonth(theyear, themonth, w=0, l=0) Return a month's calendar in a multi-line string. If *w* is provided, it specifies the width of the date columns, which are centered. If *l* is @@ -201,11 +196,6 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is on the first weekday as specified in the constructor or set by the :meth:`setfirstweekday` method. - .. versionchanged:: 3.14 - If *highlight_day* is given, this date is highlighted in color. - This can be :ref:`controlled using environment variables - `. - .. method:: formatmonthname(theyear, themonth, width=0, withyear=True) @@ -220,7 +210,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is Print a month's calendar as returned by :meth:`formatmonth`. - .. method:: formatyear(theyear, w=2, l=1, c=6, m=3, highlight_day=None) + .. method:: formatyear(theyear, w=2, l=1, c=6, m=3) Return a *m*-column calendar for an entire year as a multi-line string. Optional parameters *w*, *l*, and *c* are for date column width, lines per @@ -229,11 +219,6 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :meth:`setfirstweekday` method. The earliest year for which a calendar can be generated is platform-dependent. - .. versionchanged:: 3.14 - If *highlight_day* is given, this date is highlighted in color. - This can be :ref:`controlled using environment variables - `. - .. method:: pryear(theyear, w=2, l=1, c=6, m=3) diff --git a/Lib/calendar.py b/Lib/calendar.py index d2e5e08d02dbb9..1105a705a8036a 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -349,27 +349,11 @@ def formatday(self, day, weekday, width): s = '%2i' % day # right-align single-digit days return s.center(width) - def formatweek(self, theweek, width, *, highlight_day=None): + def formatweek(self, theweek, width): """ Returns a single week in a string (no newline). """ - if highlight_day: - from _colorize import get_colors - - ansi = get_colors() - highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" - reset = ansi.RESET - else: - highlight = reset = "" - - return ' '.join( - ( - f"{highlight}{self.formatday(d, wd, width)}{reset}" - if d == highlight_day - else self.formatday(d, wd, width) - ) - for (d, wd) in theweek - ) + return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) def formatweekday(self, day, width): """ @@ -404,11 +388,10 @@ def prmonth(self, theyear, themonth, w=0, l=0): """ print(self.formatmonth(theyear, themonth, w, l), end='') - def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None): + def formatmonth(self, theyear, themonth, w=0, l=0): """ Return a month's calendar string (multi-line). """ - highlight_day = highlight_day.day if highlight_day else None w = max(2, w) l = max(1, l) s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) @@ -417,11 +400,11 @@ def formatmonth(self, theyear, themonth, w=0, l=0, *, highlight_day=None): s += self.formatweekheader(w).rstrip() s += '\n' * l for week in self.monthdays2calendar(theyear, themonth): - s += self.formatweek(week, w, highlight_day=highlight_day).rstrip() + s += self.formatweek(week, w).rstrip() s += '\n' * l return s - def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None): + def formatyear(self, theyear, w=2, l=1, c=6, m=3): """ Returns a year's calendar as a multi-line string. """ @@ -446,23 +429,15 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3, *, highlight_day=None): a(formatstring(headers, colwidth, c).rstrip()) a('\n'*l) - if highlight_day and highlight_day.month in months: - month_pos = months.index(highlight_day.month) - else: - month_pos = None - # max number of weeks for this row height = max(len(cal) for cal in row) for j in range(height): weeks = [] - for k, cal in enumerate(row): + for cal in row: if j >= len(cal): weeks.append('') else: - day = highlight_day.day if k == month_pos else None - weeks.append( - self.formatweek(cal[j], w, highlight_day=day) - ) + weeks.append(self.formatweek(cal[j], w)) a(formatstring(weeks, colwidth, c).rstrip()) a('\n' * l) return ''.join(v) @@ -672,6 +647,111 @@ def formatmonthname(self, theyear, themonth, withyear=True): with different_locale(self.locale): return super().formatmonthname(theyear, themonth, withyear) + +class _CLIDemoCalendar(LocaleTextCalendar): + def __init__(self, highlight_day=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.highlight_day = highlight_day + + def formatweek(self, theweek, width, *, highlight_day=None): + """ + Returns a single week in a string (no newline). + """ + if highlight_day: + from _colorize import get_colors + + ansi = get_colors() + highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" + reset = ansi.RESET + else: + highlight = reset = "" + + return ' '.join( + ( + f"{highlight}{self.formatday(d, wd, width)}{reset}" + if d == highlight_day + else self.formatday(d, wd, width) + ) + for (d, wd) in theweek + ) + + def formatmonth(self, theyear, themonth, w=0, l=0): + """ + Return a month's calendar string (multi-line). + """ + if ( + self.highlight_day + and self.highlight_day.year == theyear + and self.highlight_day.month == themonth + ): + highlight_day = self.highlight_day.day + else: + highlight_day = None + w = max(2, w) + l = max(1, l) + s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) + s = s.rstrip() + s += '\n' * l + s += self.formatweekheader(w).rstrip() + s += '\n' * l + for week in self.monthdays2calendar(theyear, themonth): + s += self.formatweek(week, w, highlight_day=highlight_day).rstrip() + s += '\n' * l + return s + + def formatyear(self, theyear, w=2, l=1, c=6, m=3): + """ + Returns a year's calendar as a multi-line string. + """ + w = max(2, w) + l = max(1, l) + c = max(2, c) + colwidth = (w + 1) * 7 - 1 + v = [] + a = v.append + a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) + a('\n'*l) + header = self.formatweekheader(w) + for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): + # months in this row + months = range(m*i+1, min(m*(i+1)+1, 13)) + a('\n'*l) + names = (self.formatmonthname(theyear, k, colwidth, False) + for k in months) + a(formatstring(names, colwidth, c).rstrip()) + a('\n'*l) + headers = (header for k in months) + a(formatstring(headers, colwidth, c).rstrip()) + a('\n'*l) + + if ( + self.highlight_day + and self.highlight_day.year == theyear + and self.highlight_day.month in months + ): + month_pos = months.index(self.highlight_day.month) + else: + month_pos = None + + # max number of weeks for this row + height = max(len(cal) for cal in row) + for j in range(height): + weeks = [] + for k, cal in enumerate(row): + if j >= len(cal): + weeks.append('') + else: + day = ( + self.highlight_day.day if k == month_pos else None + ) + weeks.append( + self.formatweek(cal[j], w, highlight_day=day) + ) + a(formatstring(weeks, colwidth, c).rstrip()) + a('\n' * l) + return ''.join(v) + + # Support for old module level interface c = TextCalendar() @@ -813,26 +893,21 @@ def main(args=None): write(cal.formatyearpage(options.year, **optdict)) else: if options.locale: - cal = LocaleTextCalendar(locale=locale) + cal = _CLIDemoCalendar(highlight_day=today, locale=locale) else: - cal = TextCalendar() + cal = _CLIDemoCalendar(highlight_day=today) cal.setfirstweekday(options.first_weekday) optdict = dict(w=options.width, l=options.lines) if options.month is None: optdict["c"] = options.spacing optdict["m"] = options.months - if options.month is not None: + else: _validate_month(options.month) if options.year is None: - optdict["highlight_day"] = today result = cal.formatyear(today.year, **optdict) elif options.month is None: - if options.year == today.year: - optdict["highlight_day"] = today result = cal.formatyear(options.year, **optdict) else: - if options.year == today.year and options.month == today.month: - optdict["highlight_day"] = today result = cal.formatmonth(options.year, options.month, **optdict) write = sys.stdout.write if options.encoding: diff --git a/Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst b/Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst new file mode 100644 index 00000000000000..eaff03384a976a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-03-22-31-43.gh-issue-128317.n2Swnh.rst @@ -0,0 +1,2 @@ +Put CLI calendar highlighting in private class, removing ``highlight_day`` +from public :class:`calendar.TextCalendar` API. Patch by Hugo van Kemenade. From 33a7094aa680bca66582fec4dcda7d150eb90cd8 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sat, 8 Feb 2025 19:26:07 +0000 Subject: [PATCH 137/311] gh-129699: Add description to IDLE doc title (#129727) Also extend the 'idlelib' section header. These additions affect both the displayed idle.html file and the contents.html file displayed by clicking the Complete table of contents link on the main docs.python.org page. (The module index entries are generated from the module name and synopsis within module files.) --------- Co-authored-by: Terry Jan Reedy --- Doc/library/idle.rst | 8 +- Lib/idlelib/help.html | 120 ++++++++++++++--------------- Lib/idlelib/help.py | 4 +- Lib/idlelib/idle_test/test_help.py | 2 +- 4 files changed, 68 insertions(+), 66 deletions(-) diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 59b181aab3e484..830bc33922d5dd 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -1,7 +1,7 @@ .. _idle: -IDLE -==== +IDLE --- Python editor and shell +================================ .. moduleauthor:: Guido van Rossum @@ -971,8 +971,8 @@ information. The only current default extension is zzdummy, an example also used for testing. -idlelib -------- +idlelib --- implementation of IDLE application +---------------------------------------------- .. module:: idlelib :synopsis: Implementation package for the IDLE shell/editor. diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 2a4adc6a4d395f..2200bf29abea66 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -1,24 +1,24 @@ - + - IDLE — Python 3.14.0a0 documentation + IDLE — Python editor and shell — Python 3.14.0a4 documentation - - - + + + - - - + + + @@ -87,7 +87,7 @@ -