Python-checkins
Threads by month
- ----- 2025 -----
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
February 2025
- 1 participants
- 684 discussions

Feb. 28, 2025
https://github.com/python/cpython/commit/75f38af7810af1c3ca567d6224a975f85a…
commit: 75f38af7810af1c3ca567d6224a975f85aef970f
branch: main
author: Sam Gross <colesbury(a)gmail.com>
committer: colesbury <colesbury(a)gmail.com>
date: 2025-02-28T16:57:48-05:00
summary:
Revert "gh-128942: make `array` module thread safe (#128943)" (#130707)
The change regressed performance on scimark benchmarks from the
pyperformance benchmark suite.
This reverts commit 8ba0d7bbc295781bf27902380521db97a272c442.
files:
D Misc/NEWS.d/next/Library/2025-01-17-13-53-32.gh-issue-128942.DxzaIg.rst
M Lib/test/test_array.py
M Modules/arraymodule.c
M Modules/clinic/arraymodule.c.h
diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py
index bc3eeef8000190..58ea89c4fac833 100755
--- a/Lib/test/test_array.py
+++ b/Lib/test/test_array.py
@@ -3,21 +3,16 @@
"""
import collections.abc
-import io
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 import _2G
import weakref
import pickle
import operator
-import random
import struct
import sys
-import sysconfig
-import threading
import warnings
import array
@@ -1678,266 +1673,5 @@ def test_gh_128961(self):
self.assertRaises(StopIteration, next, it)
-class FreeThreadingTest(unittest.TestCase):
- # Test pretty much everything that can break under free-threading.
- # Non-deterministic, but at least one of these things will fail if
- # array module is not free-thread safe.
-
- @unittest.skipUnless(support.Py_GIL_DISABLED, 'this test can only possibly fail with GIL disabled')
- @threading_helper.reap_threads
- @threading_helper.requires_working_threading()
- def test_free_threading(self):
- def pop1(b, a): # MODIFIES!
- b.wait()
- try: a.pop()
- except IndexError: pass
-
- def append1(b, a): # MODIFIES!
- b.wait()
- a.append(2)
-
- def insert1(b, a): # MODIFIES!
- b.wait()
- a.insert(0, 2)
-
- def extend(b, a): # MODIFIES!
- c = array.array('i', [2])
- b.wait()
- a.extend(c)
-
- def extend2(b, a, c): # MODIFIES!
- b.wait()
- a.extend(c)
-
- def inplace_concat(b, a): # MODIFIES!
- c = array.array('i', [2])
- b.wait()
- a += c
-
- def inplace_concat2(b, a, c): # MODIFIES!
- b.wait()
- a += c
-
- def inplace_repeat2(b, a): # MODIFIES!
- b.wait()
- a *= 2
-
- def clear(b, a, *args): # MODIFIES!
- b.wait()
- a.clear()
-
- def clear2(b, a, c): # MODIFIES c!
- b.wait()
- try: c.clear()
- except BufferError: pass
-
- def remove1(b, a): # MODIFIES!
- b.wait()
- try: a.remove(1)
- except ValueError: pass
-
- def fromunicode(b, a): # MODIFIES!
- b.wait()
- a.fromunicode('test')
-
- def frombytes(b, a): # MODIFIES!
- b.wait()
- a.frombytes(b'0000')
-
- def frombytes2(b, a, c): # MODIFIES!
- b.wait()
- a.frombytes(c)
-
- def fromlist(b, a): # MODIFIES!
- n = random.randint(0, 100)
- b.wait()
- a.fromlist([2] * n)
-
- def ass_subscr2(b, a, c): # MODIFIES!
- b.wait()
- a[:] = c
-
- def ass0(b, a): # modifies inplace
- b.wait()
- try: a[0] = 0
- except IndexError: pass
-
- def byteswap(b, a): # modifies inplace
- b.wait()
- a.byteswap()
-
- def tounicode(b, a):
- b.wait()
- a.tounicode()
-
- def tobytes(b, a):
- b.wait()
- a.tobytes()
-
- def tolist(b, a):
- b.wait()
- a.tolist()
-
- def tofile(b, a):
- f = io.BytesIO()
- b.wait()
- a.tofile(f)
-
- def reduce_ex2(b, a):
- b.wait()
- a.__reduce_ex__(2)
-
- def reduce_ex3(b, a):
- b.wait()
- c = a.__reduce_ex__(3)
- assert not c[1] or 0xdd not in c[1][3]
-
- def copy(b, a):
- b.wait()
- c = a.__copy__()
- assert not c or 0xdd not in c
-
- def repr1(b, a):
- b.wait()
- repr(a)
-
- def repeat2(b, a):
- b.wait()
- a * 2
-
- def count1(b, a):
- b.wait()
- a.count(1)
-
- def index1(b, a):
- b.wait()
- try: a.index(1)
- except ValueError: pass
-
- def contains1(b, a):
- b.wait()
- try: 1 in a
- except ValueError: pass
-
- def subscr0(b, a):
- b.wait()
- try: a[0]
- except IndexError: pass
-
- def concat(b, a):
- b.wait()
- a + a
-
- def concat2(b, a, c):
- b.wait()
- a + c
-
- def richcmplhs(b, a):
- c = a[:]
- b.wait()
- a == c
-
- def richcmprhs(b, a):
- c = a[:]
- b.wait()
- c == a
-
- def new(b, a):
- tc = a.typecode
- b.wait()
- array.array(tc, a)
-
- def repr_(b, a):
- b.wait()
- repr(a)
-
- def irepeat(b, a): # MODIFIES!
- b.wait()
- a *= 2
-
- def newi(b, l):
- b.wait()
- array.array('i', l)
-
- def fromlistl(b, a, l): # MODIFIES!
- b.wait()
- a.fromlist(l)
-
- def fromlistlclear(b, a, l): # MODIFIES LIST!
- b.wait()
- l.clear()
-
- def iter_next(b, a, it): # MODIFIES ITERATOR!
- b.wait()
- list(it)
-
- def iter_reduce(b, a, it):
- b.wait()
- c = it.__reduce__()
- assert not c[1] or 0xdd not in c[1][0]
-
- def check(funcs, a=None, *args):
- if a is None:
- a = array.array('i', [1])
-
- barrier = threading.Barrier(len(funcs))
- threads = []
-
- for func in funcs:
- thread = threading.Thread(target=func, args=(barrier, a, *args))
-
- threads.append(thread)
-
- with threading_helper.start_threads(threads):
- pass
-
- check([pop1] * 10)
- check([pop1] + [subscr0] * 10)
- check([append1] * 10)
- check([insert1] * 10)
- check([pop1] + [index1] * 10)
- check([pop1] + [contains1] * 10)
- check([insert1] + [repeat2] * 10)
- check([pop1] + [repr1] * 10)
- check([inplace_repeat2] * 10)
- check([byteswap] * 10)
- check([insert1] + [clear] * 10)
- check([pop1] + [count1] * 10)
- check([remove1] * 10)
- check([clear] + [copy] * 10, array.array('B', b'0' * 0x400000))
- check([pop1] + [reduce_ex2] * 10)
- check([clear] + [reduce_ex3] * 10, array.array('B', b'0' * 0x400000))
- check([pop1] + [tobytes] * 10)
- check([pop1] + [tolist] * 10)
- check([clear, tounicode] * 10, array.array('w', 'a'*10000))
- check([clear, tofile] * 10, array.array('w', 'a'*10000))
- check([clear] + [extend] * 10)
- check([clear] + [inplace_concat] * 10)
- check([clear] + [concat] * 10, array.array('w', 'a'*10000))
- check([fromunicode] * 10, array.array('w', 'a'))
- check([frombytes] * 10)
- check([fromlist] * 10)
- check([clear] + [richcmplhs] * 10, array.array('i', [1]*10000))
- check([clear] + [richcmprhs] * 10, array.array('i', [1]*10000))
- check([clear, ass0] * 10, array.array('i', [1]*10000)) # to test array_ass_item must disable Py_mp_ass_subscript
- check([clear] + [new] * 10, array.array('w', 'a'*10000))
- check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
- check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
- check([clear] + [irepeat] * 10, array.array('B', b'0' * 0x40000))
- check([clear] + [iter_reduce] * 10, a := array.array('B', b'0' * 0x400), iter(a))
-
- # make sure we handle non-self objects correctly
- check([clear] + [newi] * 10, [2] * random.randint(0, 100))
- check([fromlistlclear] + [fromlistl] * 10, array.array('i', [1]), [2] * random.randint(0, 100))
- check([clear2] + [concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
- check([clear2] + [inplace_concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
- check([clear2] + [extend2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
- check([clear2] + [ass_subscr2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
- check([clear2] + [frombytes2] * 10, array.array('w', 'a'*10000), array.array('B', b'a'*10000))
-
- # iterator stuff
- check([clear] + [iter_next] * 10, a := array.array('i', [1] * 10), iter(a))
-
-
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2025-01-17-13-53-32.gh-issue-128942.DxzaIg.rst b/Misc/NEWS.d/next/Library/2025-01-17-13-53-32.gh-issue-128942.DxzaIg.rst
deleted file mode 100644
index 2b5eb581f1ffa1..00000000000000
--- a/Misc/NEWS.d/next/Library/2025-01-17-13-53-32.gh-issue-128942.DxzaIg.rst
+++ /dev/null
@@ -1 +0,0 @@
-Make the :mod:`array` module safe under :term:`free threading`.
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 0775b26e1d68ed..5b86ec98393e48 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -13,7 +13,6 @@
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_moduleobject.h" // _PyModule_GetState()
-#include "pycore_pyatomic_ft_wrappers.h"
#include <stddef.h> // offsetof()
#include <stdbool.h>
@@ -69,19 +68,6 @@ typedef struct {
PyObject *str_iter;
} array_state;
-static inline Py_ssize_t Pyarrayobject_GET_SIZE(PyObject *op) {
- arrayobject *ao = (arrayobject *)op;
-#ifdef Py_GIL_DISABLED
- return _Py_atomic_load_ssize_relaxed(&(_PyVarObject_CAST(ao)->ob_size));
-#else
- return Py_SIZE(ao);
-#endif
-}
-#define Pyarrayobject_GET_SIZE(op) Pyarrayobject_GET_SIZE(_PyObject_CAST(op))
-
-/* Forward declaration. */
-static PyObject *array_array_frombytes(PyObject *self, PyObject *bytes);
-
static array_state *
get_array_state(PyObject *module)
{
@@ -147,7 +133,6 @@ enum machine_format_code {
static int
array_resize(arrayobject *self, Py_ssize_t newsize)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
char *items;
size_t _new_size;
@@ -173,7 +158,7 @@ array_resize(arrayobject *self, Py_ssize_t newsize)
PyMem_Free(self->ob_item);
self->ob_item = NULL;
Py_SET_SIZE(self, 0);
- FT_ATOMIC_STORE_SSIZE_RELAXED(self->allocated, 0);
+ self->allocated = 0;
return 0;
}
@@ -203,7 +188,7 @@ array_resize(arrayobject *self, Py_ssize_t newsize)
}
self->ob_item = items;
Py_SET_SIZE(self, newsize);
- FT_ATOMIC_STORE_SSIZE_RELAXED(self->allocated, _new_size);
+ self->allocated = _new_size;
return 0;
}
@@ -687,7 +672,6 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr *des
static PyObject *
getarrayitem(PyObject *op, Py_ssize_t i)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
#ifndef NDEBUG
array_state *state = find_array_state_by_type(Py_TYPE(op));
assert(array_Check(op, state));
@@ -701,7 +685,6 @@ getarrayitem(PyObject *op, Py_ssize_t i)
static int
ins1(arrayobject *self, Py_ssize_t where, PyObject *v)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
char *items;
Py_ssize_t n = Py_SIZE(self);
if (v == NULL) {
@@ -745,11 +728,6 @@ array_dealloc(PyObject *op)
PyObject_GC_UnTrack(op);
arrayobject *self = arrayobject_CAST(op);
- if (self->ob_exports > 0) {
- PyErr_SetString(PyExc_SystemError,
- "deallocated array object has exported buffers");
- PyErr_Print();
- }
if (self->weakreflist != NULL) {
PyObject_ClearWeakRefs(op);
}
@@ -761,10 +739,8 @@ array_dealloc(PyObject *op)
}
static PyObject *
-array_richcompare_lock_held(PyObject *v, PyObject *w, int op)
+array_richcompare(PyObject *v, PyObject *w, int op)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(v);
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(w);
array_state *state = find_array_state_by_type(Py_TYPE(v));
arrayobject *va, *wa;
PyObject *vi = NULL;
@@ -872,27 +848,15 @@ array_richcompare_lock_held(PyObject *v, PyObject *w, int op)
return res;
}
-static PyObject *
-array_richcompare(PyObject *v, PyObject *w, int op)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION2(v, w);
- ret = array_richcompare_lock_held(v, w, op);
- Py_END_CRITICAL_SECTION2();
- return ret;
-}
-
static Py_ssize_t
array_length(PyObject *op)
{
- arrayobject *self = arrayobject_CAST(op);
- return Pyarrayobject_GET_SIZE(self);
+ return Py_SIZE(op);
}
static PyObject *
-array_item_lock_held(PyObject *op, Py_ssize_t i)
+array_item(PyObject *op, Py_ssize_t i)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
if (i < 0 || i >= Py_SIZE(op)) {
PyErr_SetString(PyExc_IndexError, "array index out of range");
return NULL;
@@ -900,20 +864,9 @@ array_item_lock_held(PyObject *op, Py_ssize_t i)
return getarrayitem(op, i);
}
-static PyObject *
-array_item(PyObject *op, Py_ssize_t i)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_item_lock_held(op, i);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static PyObject *
array_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(a);
array_state *state = find_array_state_by_type(Py_TYPE(a));
arrayobject *np;
@@ -938,7 +891,6 @@ array_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
}
/*[clinic input]
-@critical_section
array.array.clear
Remove all items from the array.
@@ -946,7 +898,7 @@ Remove all items from the array.
static PyObject *
array_array_clear_impl(arrayobject *self)
-/*[clinic end generated code: output=5efe0417062210a9 input=1c9dfcc80f5b6731]*/
+/*[clinic end generated code: output=5efe0417062210a9 input=5dffa30e94e717a4]*/
{
if (array_resize(self, 0) == -1) {
return NULL;
@@ -955,7 +907,6 @@ array_array_clear_impl(arrayobject *self)
}
/*[clinic input]
-@critical_section
array.array.__copy__
Return a copy of the array.
@@ -963,13 +914,12 @@ Return a copy of the array.
static PyObject *
array_array___copy___impl(arrayobject *self)
-/*[clinic end generated code: output=dec7c3f925d9619e input=7622f8f9489472d5]*/
+/*[clinic end generated code: output=dec7c3f925d9619e input=ad1ee5b086965f09]*/
{
return array_slice(self, 0, Py_SIZE(self));
}
/*[clinic input]
-@critical_section
array.array.__deepcopy__
unused: object
@@ -979,17 +929,15 @@ Return a copy of the array.
[clinic start generated code]*/
static PyObject *
-array_array___deepcopy___impl(arrayobject *self, PyObject *unused)
-/*[clinic end generated code: output=703b4c412feaaf31 input=1a29f718f5b8a1dc]*/
+array_array___deepcopy__(arrayobject *self, PyObject *unused)
+/*[clinic end generated code: output=1ec748d8e14a9faa input=2405ecb4933748c4]*/
{
return array_array___copy___impl(self);
}
static PyObject *
-array_concat_lock_held(PyObject *op, PyObject *bb)
+array_concat(PyObject *op, PyObject *bb)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(bb);
arrayobject *a = arrayobject_CAST(op);
array_state *state = find_array_state_by_type(Py_TYPE(a));
Py_ssize_t size;
@@ -1025,19 +973,8 @@ array_concat_lock_held(PyObject *op, PyObject *bb)
}
static PyObject *
-array_concat(PyObject *op, PyObject *bb)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION2(op, bb);
- ret = array_concat_lock_held(op, bb);
- Py_END_CRITICAL_SECTION2();
- return ret;
-}
-
-static PyObject *
-array_repeat_lock_held(PyObject *op, Py_ssize_t n)
+array_repeat(PyObject *op, Py_ssize_t n)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
arrayobject *a = arrayobject_CAST(op);
array_state *state = find_array_state_by_type(Py_TYPE(a));
@@ -1061,20 +998,9 @@ array_repeat_lock_held(PyObject *op, Py_ssize_t n)
return (PyObject *)np;
}
-static PyObject *
-array_repeat(PyObject *op, Py_ssize_t n)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_repeat_lock_held(op, n);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static int
array_del_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(a);
char *item;
Py_ssize_t d; /* Change in size */
if (ilow < 0)
@@ -1108,9 +1034,8 @@ array_del_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
}
static int
-setarrayitem(PyObject *op, Py_ssize_t i, PyObject *v)
+array_ass_item(PyObject *op, Py_ssize_t i, PyObject *v)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
arrayobject *a = arrayobject_CAST(op);
if (i < 0 || i >= Py_SIZE(a)) {
PyErr_SetString(PyExc_IndexError,
@@ -1123,20 +1048,18 @@ setarrayitem(PyObject *op, Py_ssize_t i, PyObject *v)
}
static int
-array_ass_item(PyObject *op, Py_ssize_t i, PyObject *v)
+setarrayitem(PyObject *a, Py_ssize_t i, PyObject *v)
{
- int ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = setarrayitem(op, i, v);
- Py_END_CRITICAL_SECTION();
- return ret;
+#ifndef NDEBUG
+ array_state *state = find_array_state_by_type(Py_TYPE(a));
+ assert(array_Check(a, state));
+#endif
+ return array_ass_item(a, i, v);
}
static int
array_iter_extend(arrayobject *self, PyObject *bb)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(bb);
PyObject *it, *v;
it = PyObject_GetIter(bb);
@@ -1158,10 +1081,8 @@ array_iter_extend(arrayobject *self, PyObject *bb)
}
static int
-array_do_extend_lock_held(array_state *state, arrayobject *self, PyObject *bb)
+array_do_extend(array_state *state, arrayobject *self, PyObject *bb)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(bb);
Py_ssize_t size, oldsize, bbsize;
if (!array_Check(bb, state))
@@ -1192,16 +1113,6 @@ array_do_extend_lock_held(array_state *state, arrayobject *self, PyObject *bb)
#undef b
}
-static int
-array_do_extend(array_state *state, arrayobject *self, PyObject *bb)
-{
- int ret;
- Py_BEGIN_CRITICAL_SECTION2(self, bb);
- ret = array_do_extend_lock_held(state, self, bb);
- Py_END_CRITICAL_SECTION2();
- return ret;
-}
-
static PyObject *
array_inplace_concat(PyObject *op, PyObject *bb)
{
@@ -1220,9 +1131,8 @@ array_inplace_concat(PyObject *op, PyObject *bb)
}
static PyObject *
-array_inplace_repeat_lock_held(PyObject *op, Py_ssize_t n)
+array_inplace_repeat(PyObject *op, Py_ssize_t n)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
arrayobject *self = arrayobject_CAST(op);
const Py_ssize_t array_size = Py_SIZE(self);
@@ -1245,19 +1155,16 @@ array_inplace_repeat_lock_held(PyObject *op, Py_ssize_t n)
return Py_NewRef(self);
}
+
static PyObject *
-array_inplace_repeat(PyObject *op, Py_ssize_t n)
+ins(arrayobject *self, Py_ssize_t where, PyObject *v)
{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_inplace_repeat_lock_held(op, n);
- Py_END_CRITICAL_SECTION();
- return ret;
+ if (ins1(self, where, v) != 0)
+ return NULL;
+ Py_RETURN_NONE;
}
-
/*[clinic input]
-@critical_section
array.array.count
v: object
@@ -1267,8 +1174,8 @@ Return number of occurrences of v in the array.
[clinic start generated code]*/
static PyObject *
-array_array_count_impl(arrayobject *self, PyObject *v)
-/*[clinic end generated code: output=93ead26a2affb739 input=c12c0042c1d0e27e]*/
+array_array_count(arrayobject *self, PyObject *v)
+/*[clinic end generated code: output=3dd3624bf7135a3a input=d9bce9d65e39d1f5]*/
{
Py_ssize_t count = 0;
Py_ssize_t i;
@@ -1292,7 +1199,6 @@ array_array_count_impl(arrayobject *self, PyObject *v)
/*[clinic input]
-@critical_section
array.array.index
v: object
@@ -1308,7 +1214,7 @@ Raise ValueError if the value is not present.
static PyObject *
array_array_index_impl(arrayobject *self, PyObject *v, Py_ssize_t start,
Py_ssize_t stop)
-/*[clinic end generated code: output=c45e777880c99f52 input=fa32ac8ec22175d6]*/
+/*[clinic end generated code: output=c45e777880c99f52 input=089dff7baa7e5a7e]*/
{
if (start < 0) {
start += Py_SIZE(self);
@@ -1341,34 +1247,22 @@ array_array_index_impl(arrayobject *self, PyObject *v, Py_ssize_t start,
}
static int
-array_contains_lock_held(PyObject *op, PyObject *v)
+array_contains(PyObject *self, PyObject *v)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
Py_ssize_t i;
int cmp;
- for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(op); i++) {
- PyObject *opi = getarrayitem(op, i);
- if (opi == NULL)
+ for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(self); i++) {
+ PyObject *selfi = getarrayitem(self, i);
+ if (selfi == NULL)
return -1;
- cmp = PyObject_RichCompareBool(opi, v, Py_EQ);
- Py_DECREF(opi);
+ cmp = PyObject_RichCompareBool(selfi, v, Py_EQ);
+ Py_DECREF(selfi);
}
return cmp;
}
-static int
-array_contains(PyObject *op, PyObject *v)
-{
- int ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_contains_lock_held(op, v);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
/*[clinic input]
-@critical_section
array.array.remove
v: object
@@ -1378,8 +1272,8 @@ Remove the first occurrence of v in the array.
[clinic start generated code]*/
static PyObject *
-array_array_remove_impl(arrayobject *self, PyObject *v)
-/*[clinic end generated code: output=f2a24e288ecb2a35 input=78bef3fd40e62f7a]*/
+array_array_remove(arrayobject *self, PyObject *v)
+/*[clinic end generated code: output=bef06be9fdf9dceb input=0b1e5aed25590027]*/
{
Py_ssize_t i;
@@ -1405,7 +1299,6 @@ array_array_remove_impl(arrayobject *self, PyObject *v)
}
/*[clinic input]
-@critical_section
array.array.pop
i: Py_ssize_t = -1
@@ -1418,7 +1311,7 @@ i defaults to -1.
static PyObject *
array_array_pop_impl(arrayobject *self, Py_ssize_t i)
-/*[clinic end generated code: output=bc1f0c54fe5308e4 input=c69a7f1f8c570e2f]*/
+/*[clinic end generated code: output=bc1f0c54fe5308e4 input=8e5feb4c1a11cd44]*/
{
PyObject *v;
@@ -1465,7 +1358,6 @@ array_array_extend_impl(arrayobject *self, PyTypeObject *cls, PyObject *bb)
}
/*[clinic input]
-@critical_section
array.array.insert
i: Py_ssize_t
@@ -1477,15 +1369,12 @@ Insert a new item v into the array before position i.
static PyObject *
array_array_insert_impl(arrayobject *self, Py_ssize_t i, PyObject *v)
-/*[clinic end generated code: output=5a3648e278348564 input=3c922bbd81462978]*/
+/*[clinic end generated code: output=5a3648e278348564 input=5577d1b4383e9313]*/
{
- if (ins1(self, i, v) != 0)
- return NULL;
- Py_RETURN_NONE;
+ return ins(self, i, v);
}
/*[clinic input]
-@critical_section
array.array.buffer_info
Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array's contents.
@@ -1496,7 +1385,7 @@ the buffer length in bytes.
static PyObject *
array_array_buffer_info_impl(arrayobject *self)
-/*[clinic end generated code: output=9b2a4ec3ae7e98e7 input=9d0dc1ff0e6542e8]*/
+/*[clinic end generated code: output=9b2a4ec3ae7e98e7 input=a58bae5c6e1ac6a6]*/
{
PyObject *retval = NULL, *v;
@@ -1522,7 +1411,6 @@ array_array_buffer_info_impl(arrayobject *self)
}
/*[clinic input]
-@critical_section
array.array.append
v: object
@@ -1532,16 +1420,13 @@ Append new value v to the end of the array.
[clinic start generated code]*/
static PyObject *
-array_array_append_impl(arrayobject *self, PyObject *v)
-/*[clinic end generated code: output=2f1e8cbad70c2a8b input=9cdd897c66a40c3f]*/
+array_array_append(arrayobject *self, PyObject *v)
+/*[clinic end generated code: output=745a0669bf8db0e2 input=0b98d9d78e78f0fa]*/
{
- if (ins1(self, Py_SIZE(self), v) != 0)
- return NULL;
- Py_RETURN_NONE;
+ return ins(self, Py_SIZE(self), v);
}
/*[clinic input]
-@critical_section
array.array.byteswap
Byteswap all items of the array.
@@ -1552,7 +1437,7 @@ raised.
static PyObject *
array_array_byteswap_impl(arrayobject *self)
-/*[clinic end generated code: output=5f8236cbdf0d90b5 input=e691b6eff94d8b2e]*/
+/*[clinic end generated code: output=5f8236cbdf0d90b5 input=6a85591b950a0186]*/
{
char *p;
Py_ssize_t i;
@@ -1602,7 +1487,6 @@ array_array_byteswap_impl(arrayobject *self)
}
/*[clinic input]
-@critical_section
array.array.reverse
Reverse the order of the items in the array.
@@ -1610,7 +1494,7 @@ Reverse the order of the items in the array.
static PyObject *
array_array_reverse_impl(arrayobject *self)
-/*[clinic end generated code: output=c04868b36f6f4089 input=e3947e98aed068ed]*/
+/*[clinic end generated code: output=c04868b36f6f4089 input=cd904f01b27d966a]*/
{
Py_ssize_t itemsize = self->ob_descr->itemsize;
char *p, *q;
@@ -1700,7 +1584,6 @@ array_array_fromfile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f,
}
/*[clinic input]
-@critical_section
array.array.tofile
cls: defining_class
@@ -1712,7 +1595,7 @@ Write all items (as machine values) to the file object f.
static PyObject *
array_array_tofile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f)
-/*[clinic end generated code: output=4560c628d9c18bc2 input=a26bc66df57864dd]*/
+/*[clinic end generated code: output=4560c628d9c18bc2 input=5a24da7a7b407b52]*/
{
Py_ssize_t nbytes = Py_SIZE(self) * self->ob_descr->itemsize;
/* Write 64K blocks at a time */
@@ -1745,12 +1628,11 @@ array_array_tofile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f)
Py_DECREF(res); /* drop write result */
}
-done:
+ done:
Py_RETURN_NONE;
}
/*[clinic input]
-@critical_section self list
array.array.fromlist
list: object
@@ -1760,8 +1642,8 @@ Append items to array from list.
[clinic start generated code]*/
static PyObject *
-array_array_fromlist_impl(arrayobject *self, PyObject *list)
-/*[clinic end generated code: output=6c23733a68dd68df input=c7c056aaf85d997a]*/
+array_array_fromlist(arrayobject *self, PyObject *list)
+/*[clinic end generated code: output=26411c2d228a3e3f input=be2605a96c49680f]*/
{
Py_ssize_t n;
@@ -1794,7 +1676,6 @@ array_array_fromlist_impl(arrayobject *self, PyObject *list)
}
/*[clinic input]
-@critical_section
array.array.tolist
Convert array to an ordinary list with the same items.
@@ -1802,7 +1683,7 @@ Convert array to an ordinary list with the same items.
static PyObject *
array_array_tolist_impl(arrayobject *self)
-/*[clinic end generated code: output=00b60cc9eab8ef89 input=4543fdbac475c52c]*/
+/*[clinic end generated code: output=00b60cc9eab8ef89 input=a8d7784a94f86b53]*/
{
PyObject *list = PyList_New(Py_SIZE(self));
Py_ssize_t i;
@@ -1822,29 +1703,19 @@ array_array_tolist_impl(arrayobject *self)
return NULL;
}
-
-/*[clinic input]
-@critical_section
-array.array.frombytes
-
- buffer: Py_buffer
- /
-
-Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method.
-[clinic start generated code]*/
-
static PyObject *
-array_array_frombytes_impl(arrayobject *self, Py_buffer *buffer)
-/*[clinic end generated code: output=d9842c8f7510a516 input=2245f9ea58579960]*/
+frombytes(arrayobject *self, Py_buffer *buffer)
{
int itemsize = self->ob_descr->itemsize;
Py_ssize_t n;
if (buffer->itemsize != 1) {
+ PyBuffer_Release(buffer);
PyErr_SetString(PyExc_TypeError, "a bytes-like object is required");
return NULL;
}
n = buffer->len;
if (n % itemsize != 0) {
+ PyBuffer_Release(buffer);
PyErr_SetString(PyExc_ValueError,
"bytes length not a multiple of item size");
return NULL;
@@ -1854,19 +1725,37 @@ array_array_frombytes_impl(arrayobject *self, Py_buffer *buffer)
Py_ssize_t old_size = Py_SIZE(self);
if ((n > PY_SSIZE_T_MAX - old_size) ||
((old_size + n) > PY_SSIZE_T_MAX / itemsize)) {
+ PyBuffer_Release(buffer);
return PyErr_NoMemory();
}
if (array_resize(self, old_size + n) == -1) {
+ PyBuffer_Release(buffer);
return NULL;
}
memcpy(self->ob_item + old_size * itemsize,
buffer->buf, n * itemsize);
}
+ PyBuffer_Release(buffer);
Py_RETURN_NONE;
}
/*[clinic input]
-@critical_section
+array.array.frombytes
+
+ buffer: Py_buffer
+ /
+
+Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method.
+[clinic start generated code]*/
+
+static PyObject *
+array_array_frombytes_impl(arrayobject *self, Py_buffer *buffer)
+/*[clinic end generated code: output=d9842c8f7510a516 input=378db226dfac949e]*/
+{
+ return frombytes(self, buffer);
+}
+
+/*[clinic input]
array.array.tobytes
Convert the array to an array of machine values and return the bytes representation.
@@ -1874,7 +1763,7 @@ Convert the array to an array of machine values and return the bytes representat
static PyObject *
array_array_tobytes_impl(arrayobject *self)
-/*[clinic end generated code: output=87318e4edcdc2bb6 input=c4d44d5499d2320f]*/
+/*[clinic end generated code: output=87318e4edcdc2bb6 input=90ee495f96de34f5]*/
{
if (Py_SIZE(self) <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) {
return PyBytes_FromStringAndSize(self->ob_item,
@@ -1885,7 +1774,6 @@ array_array_tobytes_impl(arrayobject *self)
}
/*[clinic input]
-@critical_section
array.array.fromunicode
ustr: unicode
@@ -1900,7 +1788,7 @@ some other type.
static PyObject *
array_array_fromunicode_impl(arrayobject *self, PyObject *ustr)
-/*[clinic end generated code: output=24359f5e001a7f2b input=01e2a776cee82011]*/
+/*[clinic end generated code: output=24359f5e001a7f2b input=025db1fdade7a4ce]*/
{
int typecode = self->ob_descr->typecode;
if (typecode != 'u' && typecode != 'w') {
@@ -1948,7 +1836,6 @@ array_array_fromunicode_impl(arrayobject *self, PyObject *ustr)
}
/*[clinic input]
-@critical_section
array.array.tounicode
Extends this array with data from the unicode string ustr.
@@ -1960,7 +1847,7 @@ unicode string from an array of some other type.
static PyObject *
array_array_tounicode_impl(arrayobject *self)
-/*[clinic end generated code: output=08e442378336e1ef input=6c69dfe81a279b91]*/
+/*[clinic end generated code: output=08e442378336e1ef input=127242eebe70b66d]*/
{
int typecode = self->ob_descr->typecode;
if (typecode != 'u' && typecode != 'w') {
@@ -1989,8 +1876,7 @@ array_array___sizeof___impl(arrayobject *self)
/*[clinic end generated code: output=d8e1c61ebbe3eaed input=805586565bf2b3c6]*/
{
size_t res = _PyObject_SIZE(Py_TYPE(self));
- res += (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(self->allocated)
- * (size_t)self->ob_descr->itemsize;
+ res += (size_t)self->allocated * (size_t)self->ob_descr->itemsize;
return PyLong_FromSize_t(res);
}
@@ -2385,7 +2271,6 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype,
}
/*[clinic input]
-@critical_section
array.array.__reduce_ex__
cls: defining_class
@@ -2398,7 +2283,7 @@ Return state information for pickling.
static PyObject *
array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls,
PyObject *value)
-/*[clinic end generated code: output=4958ee5d79452ad5 input=18c90a4cad7ac527]*/
+/*[clinic end generated code: output=4958ee5d79452ad5 input=19968cf0f91d3eea]*/
{
PyObject *dict;
PyObject *result;
@@ -2526,9 +2411,8 @@ static PyMethodDef array_methods[] = {
};
static PyObject *
-array_repr_lock_held(PyObject *op)
+array_repr(PyObject *op)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
char typecode;
PyObject *s, *v = NULL;
Py_ssize_t len;
@@ -2554,20 +2438,9 @@ array_repr_lock_held(PyObject *op)
return s;
}
-static PyObject *
-array_repr(PyObject *op)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_repr_lock_held(op);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static PyObject*
-array_subscr_lock_held(PyObject *op, PyObject *item)
+array_subscr(PyObject *op, PyObject *item)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
arrayobject *self = arrayobject_CAST(op);
array_state *state = find_array_state_by_type(Py_TYPE(self));
@@ -2629,28 +2502,12 @@ array_subscr_lock_held(PyObject *op, PyObject *item)
}
}
-static PyObject *
-array_subscr(PyObject *op, PyObject *item)
-{
- PyObject *ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_subscr_lock_held(op, item);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static int
-array_ass_subscr_lock_held(PyObject *op, PyObject* item, PyObject* value)
+array_ass_subscr(PyObject *op, PyObject *item, PyObject *value)
{
- array_state* state = find_array_state_by_type(Py_TYPE(op));
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
-#ifdef Py_DEBUG
- if (value != NULL && array_Check(value, state)) {
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(value);
- }
-#endif
- arrayobject *self = arrayobject_CAST(op);
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;
@@ -2701,7 +2558,7 @@ array_ass_subscr_lock_held(PyObject *op, PyObject* item, PyObject* value)
value = array_slice(other, 0, needed);
if (value == NULL)
return -1;
- ret = array_ass_subscr_lock_held(op, item, value);
+ ret = array_ass_subscr(op, item, value);
Py_DECREF(value);
return ret;
}
@@ -2804,38 +2661,19 @@ array_ass_subscr_lock_held(PyObject *op, PyObject* item, PyObject* value)
}
}
-static int
-array_ass_subscr(PyObject *op, PyObject* item, PyObject* value)
-{
- int ret;
- array_state* state = find_array_state_by_type(Py_TYPE(op));
- if (value != NULL && array_Check(value, state)) {
- Py_BEGIN_CRITICAL_SECTION2(op, value);
- ret = array_ass_subscr_lock_held(op, item, value);
- Py_END_CRITICAL_SECTION2();
- }
- else {
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_ass_subscr_lock_held(op, item, value);
- Py_END_CRITICAL_SECTION();
- }
- return ret;
-}
-
static const void *emptybuf = "";
static int
-array_buffer_getbuf_lock_held(PyObject *op, Py_buffer *view, int flags)
+array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags)
{
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
- arrayobject *self = arrayobject_CAST(op);
if (view == NULL) {
PyErr_SetString(PyExc_BufferError,
"array_buffer_getbuf: view==NULL argument is obsolete");
return -1;
}
+ arrayobject *self = arrayobject_CAST(op);
view->buf = (void *)self->ob_item;
view->obj = Py_NewRef(self);
if (view->buf == NULL)
@@ -2867,39 +2705,61 @@ array_buffer_getbuf_lock_held(PyObject *op, Py_buffer *view, int flags)
return 0;
}
-static int
-array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags)
-{
- int ret;
- Py_BEGIN_CRITICAL_SECTION(op);
- ret = array_buffer_getbuf_lock_held(op, view, flags);
- Py_END_CRITICAL_SECTION();
- return ret;
-}
-
static void
array_buffer_relbuf(PyObject *op, Py_buffer *Py_UNUSED(view))
{
- Py_BEGIN_CRITICAL_SECTION(op);
arrayobject *self = arrayobject_CAST(op);
self->ob_exports--;
- assert(self->ob_exports >= 0);
- Py_END_CRITICAL_SECTION();
}
static PyObject *
-array_new_internal_lock_held(PyTypeObject *type, PyObject *initial, int c)
+array_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
-#ifdef Py_DEBUG
- if (initial != NULL) {
- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(initial);
- }
-#endif
array_state *state = find_array_state_by_type(type);
- PyObject *it = NULL;
+ int c;
+ PyObject *initial = NULL, *it = NULL;
const struct arraydescr *descr;
+
+ if ((type == state->ArrayType ||
+ type->tp_init == state->ArrayType->tp_init) &&
+ !_PyArg_NoKeywords("array.array", kwds))
+ return NULL;
+
+ if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial))
+ return NULL;
+
+ if (PySys_Audit("array.__new__", "CO",
+ c, initial ? initial : Py_None) < 0) {
+ return NULL;
+ }
+
+ if (c == 'u') {
+ if (PyErr_WarnEx(PyExc_DeprecationWarning,
+ "The 'u' type code is deprecated and "
+ "will be removed in Python 3.16",
+ 1)) {
+ return NULL;
+ }
+ }
+
bool is_unicode = c == 'u' || c == 'w';
+ if (initial && !is_unicode) {
+ if (PyUnicode_Check(initial)) {
+ PyErr_Format(PyExc_TypeError, "cannot use a str to initialize "
+ "an array with typecode '%c'", c);
+ return NULL;
+ }
+ else if (array_Check(initial, state)) {
+ int ic = ((arrayobject*)initial)->ob_descr->typecode;
+ if (ic == 'u' || ic == 'w') {
+ PyErr_Format(PyExc_TypeError, "cannot use a unicode array to "
+ "initialize an array with typecode '%c'", c);
+ return NULL;
+ }
+ }
+ }
+
if (!(initial == NULL || PyList_Check(initial)
|| PyByteArray_Check(initial)
|| PyBytes_Check(initial)
@@ -3017,69 +2877,6 @@ array_new_internal_lock_held(PyTypeObject *type, PyObject *initial, int c)
return NULL;
}
-static PyObject *
-array_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- array_state *state = find_array_state_by_type(type);
- int c;
- PyObject *initial = NULL;
-
- if ((type == state->ArrayType ||
- type->tp_init == state->ArrayType->tp_init) &&
- !_PyArg_NoKeywords("array.array", kwds)) {
- return NULL;
- }
-
- if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial)) {
- return NULL;
- }
-
- if (PySys_Audit("array.__new__", "CO",
- c, initial ? initial : Py_None) < 0) {
- return NULL;
- }
-
- if (c == 'u') {
- if (PyErr_WarnEx(PyExc_DeprecationWarning,
- "The 'u' type code is deprecated and "
- "will be removed in Python 3.16",
- 1)) {
- return NULL;
- }
- }
-
- bool is_unicode = c == 'u' || c == 'w';
-
- if (initial && !is_unicode) {
- if (PyUnicode_Check(initial)) {
- PyErr_Format(PyExc_TypeError, "cannot use a str to initialize "
- "an array with typecode '%c'", c);
- return NULL;
- }
- else if (array_Check(initial, state)) {
- int ic = ((arrayobject*)initial)->ob_descr->typecode;
- if (ic == 'u' || ic == 'w') {
- PyErr_Format(PyExc_TypeError, "cannot use a unicode array to "
- "initialize an array with typecode '%c'", c);
- return NULL;
- }
- }
- }
-
- PyObject *ret;
-
- if (initial == NULL) {
- ret = array_new_internal_lock_held(type, initial, c);
- }
- else {
- Py_BEGIN_CRITICAL_SECTION(initial);
- ret = array_new_internal_lock_held(type, initial, c);
- Py_END_CRITICAL_SECTION();
- }
-
- return ret;
-}
-
PyDoc_STRVAR(module_doc,
"This module defines an object type which can efficiently represent\n\
@@ -3222,7 +3019,7 @@ array_iter(PyObject *op)
return NULL;
it->ao = (arrayobject*)Py_NewRef(ao);
- it->index = 0; // -1 indicates exhausted
+ it->index = 0;
it->getitem = ao->ob_descr->getitem;
PyObject_GC_Track(it);
return (PyObject *)it;
@@ -3233,37 +3030,23 @@ arrayiter_next(PyObject *op)
{
arrayiterobject *it = arrayiterobject_CAST(op);
assert(it != NULL);
- Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->index);
- if (index < 0) {
- return NULL;
- }
- PyObject *ret;
- arrayobject *ao = it->ao;
#ifndef NDEBUG
array_state *state = find_array_state_by_type(Py_TYPE(it));
assert(PyObject_TypeCheck(it, state->ArrayIterType));
- assert(array_Check(ao, state));
#endif
-
- Py_BEGIN_CRITICAL_SECTION(ao);
- if (index < Py_SIZE(ao)) {
- ret = (*it->getitem)(ao, index);
- }
- else {
- ret = NULL;
- }
- Py_END_CRITICAL_SECTION();
-
- if (ret != NULL) {
- FT_ATOMIC_STORE_SSIZE_RELAXED(it->index, index + 1);
+ arrayobject *ao = it->ao;
+ if (ao == NULL) {
+ return NULL;
}
- else {
- FT_ATOMIC_STORE_SSIZE_RELAXED(it->index, -1);
-#ifndef Py_GIL_DISABLED
- Py_CLEAR(it->ao);
+#ifndef NDEBUG
+ assert(array_Check(ao, state));
#endif
+ if (it->index < Py_SIZE(ao)) {
+ return (*it->getitem)(ao, it->index++);
}
- return ret;
+ it->ao = NULL;
+ Py_DECREF(ao);
+ return NULL;
}
static void
@@ -3299,14 +3082,14 @@ static PyObject *
array_arrayiterator___reduce___impl(arrayiterobject *self, PyTypeObject *cls)
/*[clinic end generated code: output=4b032417a2c8f5e6 input=ac64e65a87ad452e]*/
{
+
array_state *state = get_array_state_by_class(cls);
assert(state != NULL);
PyObject *func = _PyEval_GetBuiltin(state->str_iter);
- Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->index);
- if (index >= 0) {
- return Py_BuildValue("N(O)n", func, self->ao, index);
+ if (self->ao == NULL) {
+ return Py_BuildValue("N(())", func);
}
- return Py_BuildValue("N(())", func);
+ return Py_BuildValue("N(O)n", func, self->ao, self->index);
}
/*[clinic input]
@@ -3323,20 +3106,17 @@ array_arrayiterator___setstate__(arrayiterobject *self, PyObject *state)
/*[clinic end generated code: output=397da9904e443cbe input=f47d5ceda19e787b]*/
{
Py_ssize_t index = PyLong_AsSsize_t(state);
- if (index == -1 && PyErr_Occurred()) {
+ if (index == -1 && PyErr_Occurred())
return NULL;
- }
- if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->index) >= 0) {
- if (index < -1) {
- index = -1;
+ arrayobject *ao = self->ao;
+ if (ao != NULL) {
+ if (index < 0) {
+ index = 0;
}
- else {
- Py_ssize_t size = Pyarrayobject_GET_SIZE(self->ao);
- if (index > size) {
- index = size; /* iterator at end */
- }
+ else if (index > Py_SIZE(ao)) {
+ index = Py_SIZE(ao); /* iterator exhausted */
}
- FT_ATOMIC_STORE_SSIZE_RELAXED(self->index, index);
+ self->index = index;
}
Py_RETURN_NONE;
}
diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h
index 3816bb7709658e..c5b62b16699d06 100644
--- a/Modules/clinic/arraymodule.c.h
+++ b/Modules/clinic/arraymodule.c.h
@@ -6,7 +6,6 @@ preserve
# include "pycore_runtime.h" // _Py_SINGLETON()
#endif
#include "pycore_abstract.h" // _PyNumber_Index()
-#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(array_array_clear__doc__,
@@ -24,13 +23,7 @@ array_array_clear_impl(arrayobject *self);
static PyObject *
array_array_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_clear_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_clear_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___copy____doc__,
@@ -48,13 +41,7 @@ array_array___copy___impl(arrayobject *self);
static PyObject *
array_array___copy__(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array___copy___impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array___copy___impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___deepcopy____doc__,
@@ -66,21 +53,6 @@ PyDoc_STRVAR(array_array___deepcopy____doc__,
#define ARRAY_ARRAY___DEEPCOPY___METHODDEF \
{"__deepcopy__", (PyCFunction)array_array___deepcopy__, METH_O, array_array___deepcopy____doc__},
-static PyObject *
-array_array___deepcopy___impl(arrayobject *self, PyObject *unused);
-
-static PyObject *
-array_array___deepcopy__(arrayobject *self, PyObject *unused)
-{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array___deepcopy___impl((arrayobject *)self, unused);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
-}
-
PyDoc_STRVAR(array_array_count__doc__,
"count($self, v, /)\n"
"--\n"
@@ -90,21 +62,6 @@ PyDoc_STRVAR(array_array_count__doc__,
#define ARRAY_ARRAY_COUNT_METHODDEF \
{"count", (PyCFunction)array_array_count, METH_O, array_array_count__doc__},
-static PyObject *
-array_array_count_impl(arrayobject *self, PyObject *v);
-
-static PyObject *
-array_array_count(arrayobject *self, PyObject *v)
-{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_count_impl((arrayobject *)self, v);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
-}
-
PyDoc_STRVAR(array_array_index__doc__,
"index($self, v, start=0, stop=sys.maxsize, /)\n"
"--\n"
@@ -145,9 +102,7 @@ array_array_index(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_index_impl((arrayobject *)self, v, start, stop);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -162,21 +117,6 @@ PyDoc_STRVAR(array_array_remove__doc__,
#define ARRAY_ARRAY_REMOVE_METHODDEF \
{"remove", (PyCFunction)array_array_remove, METH_O, array_array_remove__doc__},
-static PyObject *
-array_array_remove_impl(arrayobject *self, PyObject *v);
-
-static PyObject *
-array_array_remove(arrayobject *self, PyObject *v)
-{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_remove_impl((arrayobject *)self, v);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
-}
-
PyDoc_STRVAR(array_array_pop__doc__,
"pop($self, i=-1, /)\n"
"--\n"
@@ -216,9 +156,7 @@ array_array_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival;
}
skip_optional:
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_pop_impl((arrayobject *)self, i);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -303,9 +241,7 @@ array_array_insert(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival;
}
v = args[1];
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_insert_impl((arrayobject *)self, i, v);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -329,13 +265,7 @@ array_array_buffer_info_impl(arrayobject *self);
static PyObject *
array_array_buffer_info(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_buffer_info_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_buffer_info_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_append__doc__,
@@ -347,21 +277,6 @@ PyDoc_STRVAR(array_array_append__doc__,
#define ARRAY_ARRAY_APPEND_METHODDEF \
{"append", (PyCFunction)array_array_append, METH_O, array_array_append__doc__},
-static PyObject *
-array_array_append_impl(arrayobject *self, PyObject *v);
-
-static PyObject *
-array_array_append(arrayobject *self, PyObject *v)
-{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_append_impl((arrayobject *)self, v);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
-}
-
PyDoc_STRVAR(array_array_byteswap__doc__,
"byteswap($self, /)\n"
"--\n"
@@ -380,13 +295,7 @@ array_array_byteswap_impl(arrayobject *self);
static PyObject *
array_array_byteswap(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_byteswap_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_byteswap_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_reverse__doc__,
@@ -404,13 +313,7 @@ array_array_reverse_impl(arrayobject *self);
static PyObject *
array_array_reverse(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_reverse_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_reverse_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_fromfile__doc__,
@@ -509,9 +412,7 @@ array_array_tofile(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_
goto exit;
}
f = args[0];
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tofile_impl((arrayobject *)self, cls, f);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -526,21 +427,6 @@ PyDoc_STRVAR(array_array_fromlist__doc__,
#define ARRAY_ARRAY_FROMLIST_METHODDEF \
{"fromlist", (PyCFunction)array_array_fromlist, METH_O, array_array_fromlist__doc__},
-static PyObject *
-array_array_fromlist_impl(arrayobject *self, PyObject *list);
-
-static PyObject *
-array_array_fromlist(arrayobject *self, PyObject *list)
-{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION2(self, list);
- return_value = array_array_fromlist_impl((arrayobject *)self, list);
- Py_END_CRITICAL_SECTION2();
-
- return return_value;
-}
-
PyDoc_STRVAR(array_array_tolist__doc__,
"tolist($self, /)\n"
"--\n"
@@ -556,13 +442,7 @@ array_array_tolist_impl(arrayobject *self);
static PyObject *
array_array_tolist(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_tolist_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_tolist_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_frombytes__doc__,
@@ -586,9 +466,7 @@ array_array_frombytes(PyObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) {
goto exit;
}
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_frombytes_impl((arrayobject *)self, &buffer);
- Py_END_CRITICAL_SECTION();
exit:
/* Cleanup for buffer */
@@ -614,13 +492,7 @@ array_array_tobytes_impl(arrayobject *self);
static PyObject *
array_array_tobytes(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_tobytes_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_tobytes_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_fromunicode__doc__,
@@ -650,9 +522,7 @@ array_array_fromunicode(PyObject *self, PyObject *arg)
goto exit;
}
ustr = arg;
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_fromunicode_impl((arrayobject *)self, ustr);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -677,13 +547,7 @@ array_array_tounicode_impl(arrayobject *self);
static PyObject *
array_array_tounicode(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- PyObject *return_value = NULL;
-
- Py_BEGIN_CRITICAL_SECTION(self);
- return_value = array_array_tounicode_impl((arrayobject *)self);
- Py_END_CRITICAL_SECTION();
-
- return return_value;
+ return array_array_tounicode_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___sizeof____doc__,
@@ -795,9 +659,7 @@ array_array___reduce_ex__(PyObject *self, PyTypeObject *cls, PyObject *const *ar
goto exit;
}
value = args[0];
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___reduce_ex___impl((arrayobject *)self, cls, value);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -833,4 +695,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__,
#define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \
{"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__},
-/*[clinic end generated code: output=c9219e074c62e0c8 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=8120dc5c4fa414b9 input=a9049054013a1b77]*/
1
0

GH-116380: Speed up `glob.[i]glob()` by making fewer system calls. (#116392)
by barneygale Feb. 28, 2025
by barneygale Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/da4899b94a9a9083fed4972b2473546e0d…
commit: da4899b94a9a9083fed4972b2473546e0d997727
branch: main
author: Barney Gale <barney.gale(a)gmail.com>
committer: barneygale <barney.gale(a)gmail.com>
date: 2025-02-28T20:33:51Z
summary:
GH-116380: Speed up `glob.[i]glob()` by making fewer system calls. (#116392)
## Filtered recursive walk
Expanding a recursive `**` segment entails walking the entire directory
tree, and so any subsequent pattern segments (except special segments) can
be evaluated by filtering the expanded paths through a regex. For example,
`glob.glob("foo/**/*.py", recursive=True)` recursively walks `foo/` with
`os.scandir()`, and then filters paths through a regex based on "`**/*.py`,
with no further filesystem access needed.
This fixes an issue where `glob()` could return duplicate results.
## Tracking path existence
We store a flag alongside each path indicating whether the path is
guaranteed to exist. As we process the pattern:
- Certain special pattern segments (`""`, `"."` and `".."`) leave the flag
unchanged
- Literal pattern segments (e.g. `foo/bar`) set the flag to false
- Wildcard pattern segments (e.g. `*/*.py`) set the flag to true (because
children are found via `os.scandir()`)
- Recursive pattern segments (e.g. `**`) leave the flag unchanged for the
root path, and set it to true for descendants discovered via
`os.scandir()`.
If the flag is false at the end, we call `lstat()` on each path to filter
out missing paths.
## Minor speed-ups
- Exclude paths that don't match a non-terminal non-recursive wildcard
pattern _prior_ to calling `is_dir()`.
- Use a stack rather than recursion to implement recursive wildcards.
- This fixes a recursion error when globbing deep trees.
- Pre-compile regular expressions and pre-join literal pattern segments.
- Convert to/from `bytes` (a minor use-case) in `iglob()` rather than
supporting `bytes` throughout. This particularly simplifies the code
needed to handle relative bytes paths with `dir_fd`.
- Avoid calling `os.path.join()`; instead we keep paths in a normalized
form and append trailing slashes when needed.
- Avoid calling `os.path.normcase()`; instead we use case-insensitive regex
matching.
## Implementation notes
Much of this functionality is already present in pathlib's implementation
of globbing. The specific additions we make are:
1. Support for `dir_fd`
2. Support for `include_hidden`
3. Support for generating paths relative to `root_dir`
This unifies the implementations of globbing in the `glob` and `pathlib`
modules.
Co-authored-by: Pieter Eendebak <pieter.eendebak(a)gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz(a)users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2024-03-05-23-08-11.gh-issue-116380.56HU7I.rst
M Doc/library/glob.rst
M Doc/whatsnew/3.14.rst
M Lib/glob.py
M Lib/pathlib/_abc.py
M Lib/pathlib/_local.py
M Lib/test/test_glob.py
diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst
index 684466d354aef8..849316553e408a 100644
--- a/Doc/library/glob.rst
+++ b/Doc/library/glob.rst
@@ -75,10 +75,6 @@ The :mod:`glob` module defines the following functions:
Using the "``**``" pattern in large directory trees may consume
an inordinate amount of time.
- .. note::
- This function may return duplicate path names if *pathname*
- contains multiple "``**``" patterns and *recursive* is true.
-
.. versionchanged:: 3.5
Support for recursive globs using "``**``".
@@ -88,6 +84,11 @@ The :mod:`glob` module defines the following functions:
.. versionchanged:: 3.11
Added the *include_hidden* parameter.
+ .. versionchanged:: 3.14
+ Matching path names are returned only once. In previous versions, this
+ function may return duplicate path names if *pathname* contains multiple
+ "``**``" patterns and *recursive* is true.
+
.. function:: iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, \
include_hidden=False)
@@ -98,10 +99,6 @@ The :mod:`glob` module defines the following functions:
.. audit-event:: glob.glob pathname,recursive glob.iglob
.. audit-event:: glob.glob/2 pathname,recursive,root_dir,dir_fd glob.iglob
- .. note::
- This function may return duplicate path names if *pathname*
- contains multiple "``**``" patterns and *recursive* is true.
-
.. versionchanged:: 3.5
Support for recursive globs using "``**``".
@@ -111,6 +108,11 @@ The :mod:`glob` module defines the following functions:
.. versionchanged:: 3.11
Added the *include_hidden* parameter.
+ .. versionchanged:: 3.14
+ Matching path names are yielded only once. In previous versions, this
+ function may yield duplicate path names if *pathname* contains multiple
+ "``**``" patterns and *recursive* is true.
+
.. function:: escape(pathname)
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index dbd59a9d7be150..3c876a193fad32 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -968,6 +968,14 @@ base64
(Contributed by Bénédikt Tran, Chris Markiewicz, and Adam Turner in :gh:`118761`.)
+glob
+----
+
+* Reduce the number of system calls in :func:`glob.glob` and :func:`~glob.iglob`,
+ thereby improving the speed of globbing operations by 20-80%.
+ (Contributed by Barney Gale in :gh:`116380`.)
+
+
io
---
* :mod:`io` which provides the built-in :func:`open` makes less system calls
diff --git a/Lib/glob.py b/Lib/glob.py
index d1a6dddeeb1610..7ac497d4f0075a 100644
--- a/Lib/glob.py
+++ b/Lib/glob.py
@@ -1,13 +1,10 @@
"""Filename globbing utility."""
-import contextlib
import os
import re
import fnmatch
import functools
-import itertools
import operator
-import stat
import sys
@@ -45,82 +42,35 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
"""
sys.audit("glob.glob", pathname, recursive)
sys.audit("glob.glob/2", pathname, recursive, root_dir, dir_fd)
- if root_dir is not None:
- root_dir = os.fspath(root_dir)
- else:
- root_dir = pathname[:0]
- it = _iglob(pathname, root_dir, dir_fd, recursive, False,
- include_hidden=include_hidden)
- if not pathname or recursive and _isrecursive(pathname[:2]):
- try:
- s = next(it) # skip empty string
- if s:
- it = itertools.chain((s,), it)
- except StopIteration:
- pass
- return it
-
-def _iglob(pathname, root_dir, dir_fd, recursive, dironly,
- include_hidden=False):
- dirname, basename = os.path.split(pathname)
- if not has_magic(pathname):
- assert not dironly
- if basename:
- if _lexists(_join(root_dir, pathname), dir_fd):
- yield pathname
- else:
- # Patterns ending with a slash should match only directories
- if _isdir(_join(root_dir, dirname), dir_fd):
- yield pathname
- return
- if not dirname:
- if recursive and _isrecursive(basename):
- yield from _glob2(root_dir, basename, dir_fd, dironly,
- include_hidden=include_hidden)
- else:
- yield from _glob1(root_dir, basename, dir_fd, dironly,
- include_hidden=include_hidden)
- return
- # `os.path.split()` returns the argument itself as a dirname if it is a
- # drive or UNC path. Prevent an infinite recursion if a drive or UNC path
- # contains magic characters (i.e. r'\\?\C:').
- if dirname != pathname and has_magic(dirname):
- dirs = _iglob(dirname, root_dir, dir_fd, recursive, True,
- include_hidden=include_hidden)
- else:
- dirs = [dirname]
- if has_magic(basename):
- if recursive and _isrecursive(basename):
- glob_in_dir = _glob2
- else:
- glob_in_dir = _glob1
+ pathname = os.fspath(pathname)
+ if isinstance(pathname, bytes):
+ pathname = os.fsdecode(pathname)
+ if root_dir is not None:
+ root_dir = os.fsdecode(root_dir)
+ paths = _iglob(pathname, root_dir, dir_fd, recursive, include_hidden)
+ return map(os.fsencode, paths)
else:
- glob_in_dir = _glob0
- for dirname in dirs:
- for name in glob_in_dir(_join(root_dir, dirname), basename, dir_fd, dironly,
- include_hidden=include_hidden):
- yield os.path.join(dirname, name)
-
-# These 2 helper functions non-recursively glob inside a literal directory.
-# They return a list of basenames. _glob1 accepts a pattern while _glob0
-# takes a literal basename (so it only has to check for its existence).
-
-def _glob1(dirname, pattern, dir_fd, dironly, include_hidden=False):
- names = _listdir(dirname, dir_fd, dironly)
- if not (include_hidden or _ishidden(pattern)):
- names = (x for x in names if not _ishidden(x))
- return fnmatch.filter(names, pattern)
-
-def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False):
- if basename:
- if _lexists(_join(dirname, basename), dir_fd):
- return [basename]
+ return _iglob(pathname, root_dir, dir_fd, recursive, include_hidden)
+
+def _iglob(pathname, root_dir, dir_fd, recursive, include_hidden):
+ if os.path.altsep:
+ pathname = pathname.replace(os.path.altsep, os.path.sep)
+ drive, root, tail = os.path.splitroot(pathname)
+ parts = tail.split(os.path.sep)[::-1] if tail else []
+ globber = _StringGlobber(recursive=recursive, include_hidden=include_hidden)
+ select = globber.selector(parts)
+ if drive:
+ root = drive + root
+ return select(root, dir_fd, root)
+ elif root:
+ return select(root, dir_fd, root, exists=True)
+ elif not root_dir:
+ return select(root, dir_fd, root, empty=True)
else:
- # `os.path.split()` returns an empty basename for paths ending with a
- # directory separator. 'q*x/' should match only directories.
- if _isdir(dirname, dir_fd):
- return [basename]
- return []
+ root = os.path.join(root_dir, '')
+ root_len = len(root)
+ paths = select(root, dir_fd, root, empty=True)
+ return (path[root_len:] for path in paths)
_deprecated_function_message = (
"{name} is deprecated and will be removed in Python {remove}. Use "
@@ -130,102 +80,16 @@ def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False):
def glob0(dirname, pattern):
import warnings
warnings._deprecated("glob.glob0", _deprecated_function_message, remove=(3, 15))
- return _glob0(dirname, pattern, None, False)
+ dirname = os.path.join(dirname, '')
+ select = _StringGlobber().literal_selector(pattern, [])
+ return [path[len(dirname):] for path in select(dirname)]
def glob1(dirname, pattern):
import warnings
warnings._deprecated("glob.glob1", _deprecated_function_message, remove=(3, 15))
- return _glob1(dirname, pattern, None, False)
-
-# This helper function recursively yields relative pathnames inside a literal
-# directory.
-
-def _glob2(dirname, pattern, dir_fd, dironly, include_hidden=False):
- assert _isrecursive(pattern)
- if not dirname or _isdir(dirname, dir_fd):
- yield pattern[:0]
- yield from _rlistdir(dirname, dir_fd, dironly,
- include_hidden=include_hidden)
-
-# If dironly is false, yields all file names inside a directory.
-# If dironly is true, yields only directory names.
-def _iterdir(dirname, dir_fd, dironly):
- try:
- fd = None
- fsencode = None
- if dir_fd is not None:
- if dirname:
- fd = arg = os.open(dirname, _dir_open_flags, dir_fd=dir_fd)
- else:
- arg = dir_fd
- if isinstance(dirname, bytes):
- fsencode = os.fsencode
- elif dirname:
- arg = dirname
- elif isinstance(dirname, bytes):
- arg = bytes(os.curdir, 'ASCII')
- else:
- arg = os.curdir
- try:
- with os.scandir(arg) as it:
- for entry in it:
- try:
- if not dironly or entry.is_dir():
- if fsencode is not None:
- yield fsencode(entry.name)
- else:
- yield entry.name
- except OSError:
- pass
- finally:
- if fd is not None:
- os.close(fd)
- except OSError:
- return
-
-def _listdir(dirname, dir_fd, dironly):
- with contextlib.closing(_iterdir(dirname, dir_fd, dironly)) as it:
- return list(it)
-
-# Recursively yields relative pathnames inside a literal directory.
-def _rlistdir(dirname, dir_fd, dironly, include_hidden=False):
- names = _listdir(dirname, dir_fd, dironly)
- for x in names:
- if include_hidden or not _ishidden(x):
- yield x
- path = _join(dirname, x) if dirname else x
- for y in _rlistdir(path, dir_fd, dironly,
- include_hidden=include_hidden):
- yield _join(x, y)
-
-
-def _lexists(pathname, dir_fd):
- # Same as os.path.lexists(), but with dir_fd
- if dir_fd is None:
- return os.path.lexists(pathname)
- try:
- os.lstat(pathname, dir_fd=dir_fd)
- except (OSError, ValueError):
- return False
- else:
- return True
-
-def _isdir(pathname, dir_fd):
- # Same as os.path.isdir(), but with dir_fd
- if dir_fd is None:
- return os.path.isdir(pathname)
- try:
- st = os.stat(pathname, dir_fd=dir_fd)
- except (OSError, ValueError):
- return False
- else:
- return stat.S_ISDIR(st.st_mode)
-
-def _join(dirname, basename):
- # It is common if dirname or basename is empty
- if not dirname or not basename:
- return dirname or basename
- return os.path.join(dirname, basename)
+ dirname = os.path.join(dirname, '')
+ select = _StringGlobber().wildcard_selector(pattern, [])
+ return [path[len(dirname):] for path in select(dirname)]
magic_check = re.compile('([*?[])')
magic_check_bytes = re.compile(b'([*?[])')
@@ -237,15 +101,6 @@ def has_magic(s):
match = magic_check.search(s)
return match is not None
-def _ishidden(path):
- return path[0] in ('.', b'.'[0])
-
-def _isrecursive(pattern):
- if isinstance(pattern, bytes):
- return pattern == b'**'
- else:
- return pattern == '**'
-
def escape(pathname):
"""Escape all special characters.
"""
@@ -319,12 +174,13 @@ def translate(pat, *, recursive=False, include_hidden=False, seps=None):
return fr'(?s:{res})\Z'
-(a)functools.lru_cache(maxsize=512)
-def _compile_pattern(pat, sep, case_sensitive, recursive=True):
+(a)functools.lru_cache(maxsize=1024)
+def _compile_pattern(pat, sep, case_sensitive, recursive, include_hidden):
"""Compile given glob pattern to a re.Pattern object (observing case
sensitivity)."""
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
- regex = translate(pat, recursive=recursive, include_hidden=True, seps=sep)
+ regex = translate(pat, recursive=recursive,
+ include_hidden=include_hidden, seps=sep)
return re.compile(regex, flags=flags).match
@@ -332,11 +188,13 @@ class _GlobberBase:
"""Abstract class providing shell-style pattern matching and globbing.
"""
- def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False):
+ def __init__(self, sep=os.path.sep, case_sensitive=os.name != 'nt',
+ case_pedantic=False, recursive=False, include_hidden=False):
self.sep = sep
self.case_sensitive = case_sensitive
self.case_pedantic = case_pedantic
self.recursive = recursive
+ self.include_hidden = include_hidden
# Abstract methods
@@ -346,12 +204,38 @@ def lexists(path):
"""
raise NotImplementedError
+ @staticmethod
+ def lstat(path, dir_fd=None):
+ """Implements os.lstat()
+ """
+ raise NotImplementedError
+
+ @staticmethod
+ def open(path, flags, dir_fd=None):
+ """Implements os.open()
+ """
+ raise NotImplementedError
+
@staticmethod
def scandir(path):
"""Like os.scandir(), but generates (entry, name, path) tuples.
"""
raise NotImplementedError
+ @staticmethod
+ def scandir_cwd():
+ raise NotImplementedError
+
+ @staticmethod
+ def scandir_fd(fd, prefix):
+ raise NotImplementedError
+
+ @staticmethod
+ def close(fd):
+ """Implements os.close().
+ """
+ raise NotImplementedError
+
@staticmethod
def concat_path(path, text):
"""Implements path concatenation.
@@ -361,7 +245,8 @@ def concat_path(path, text):
# High-level methods
def compile(self, pat):
- return _compile_pattern(pat, self.sep, self.case_sensitive, self.recursive)
+ return _compile_pattern(pat, self.sep, self.case_sensitive,
+ self.recursive, self.include_hidden)
def selector(self, parts):
"""Returns a function that selects from a given path, walking and
@@ -386,10 +271,14 @@ def special_selector(self, part, parts):
if parts:
part += self.sep
select_next = self.selector(parts)
+ if not part:
+ return select_next
- def select_special(path, exists=False):
+ def select_special(path, dir_fd=None, rel_path=None, exists=False, empty=False):
path = self.concat_path(path, part)
- return select_next(path, exists)
+ if dir_fd is not None:
+ rel_path = self.concat_path(rel_path, part)
+ return select_next(path, dir_fd, rel_path, exists)
return select_special
def literal_selector(self, part, parts):
@@ -406,9 +295,11 @@ def literal_selector(self, part, parts):
select_next = self.selector(parts)
- def select_literal(path, exists=False):
+ def select_literal(path, dir_fd=None, rel_path=None, exists=False, empty=False):
path = self.concat_path(path, part)
- return select_next(path, exists=False)
+ if dir_fd is not None:
+ rel_path = self.concat_path(rel_path, part)
+ return select_next(path, dir_fd, rel_path)
return select_literal
def wildcard_selector(self, part, parts):
@@ -416,14 +307,24 @@ def wildcard_selector(self, part, parts):
filtering by pattern.
"""
- match = None if part == '*' else self.compile(part)
+ match = None if self.include_hidden and part == '*' else self.compile(part)
dir_only = bool(parts)
if dir_only:
select_next = self.selector(parts)
- def select_wildcard(path, exists=False):
+ def select_wildcard(path, dir_fd=None, rel_path=None, exists=False, empty=False):
+ close_fd = False
try:
- entries = self.scandir(path)
+ if dir_fd is None:
+ fd = None
+ entries = self.scandir(path) if path else self.scandir_cwd()
+ elif not rel_path:
+ fd = dir_fd
+ entries = self.scandir_fd(fd, path)
+ else:
+ fd = self.open(rel_path, _dir_open_flags, dir_fd=dir_fd)
+ close_fd = True
+ entries = self.scandir_fd(fd, path)
except OSError:
pass
else:
@@ -436,9 +337,17 @@ def select_wildcard(path, exists=False):
except OSError:
continue
entry_path = self.concat_path(entry_path, self.sep)
- yield from select_next(entry_path, exists=True)
+ if fd is not None:
+ entry_name = entry_name + self.sep
+ yield from select_next(
+ entry_path, fd, entry_name, exists=True)
else:
+ # Optimization: directly yield the path if this is
+ # last pattern part.
yield entry_path
+ finally:
+ if close_fd:
+ self.close(fd)
return select_wildcard
def recursive_selector(self, part, parts):
@@ -460,26 +369,49 @@ def recursive_selector(self, part, parts):
while parts and parts[-1] not in _special_parts:
part += self.sep + parts.pop()
- match = None if part == '**' else self.compile(part)
+ match = None if self.include_hidden and part == '**' else self.compile(part)
dir_only = bool(parts)
select_next = self.selector(parts)
- def select_recursive(path, exists=False):
+ def select_recursive(path, dir_fd=None, rel_path=None, exists=False, empty=False):
match_pos = len(str(path))
if match is None or match(str(path), match_pos):
- yield from select_next(path, exists)
- stack = [path]
- while stack:
- yield from select_recursive_step(stack, match_pos)
+ yield from select_next(path, dir_fd, rel_path, exists, empty)
+ stack = [(path, dir_fd, rel_path)]
+ try:
+ while stack:
+ yield from select_recursive_step(stack, match_pos)
+ finally:
+ # Close any file descriptors still on the stack.
+ while stack:
+ path, dir_fd, _rel_path = stack.pop()
+ if path is None:
+ try:
+ self.close(dir_fd)
+ except OSError:
+ pass
def select_recursive_step(stack, match_pos):
- path = stack.pop()
+ path, dir_fd, rel_path = stack.pop()
try:
- entries = self.scandir(path)
+ if path is None:
+ self.close(dir_fd)
+ return
+ elif dir_fd is None:
+ fd = None
+ entries = self.scandir(path) if path else self.scandir_cwd()
+ elif not rel_path:
+ fd = dir_fd
+ entries = self.scandir_fd(fd, path)
+ else:
+ fd = self.open(rel_path, _dir_open_flags, dir_fd=dir_fd)
+ # Schedule the file descriptor to be closed next step.
+ stack.append((None, fd, None))
+ entries = self.scandir_fd(fd, path)
except OSError:
pass
else:
- for entry, _entry_name, entry_path in entries:
+ for entry, entry_name, entry_path in entries:
is_dir = False
try:
if entry.is_dir(follow_symlinks=follow_symlinks):
@@ -491,25 +423,38 @@ def select_recursive_step(stack, match_pos):
entry_path_str = str(entry_path)
if dir_only:
entry_path = self.concat_path(entry_path, self.sep)
+ if fd is not None:
+ entry_name = entry_name + self.sep
if match is None or match(entry_path_str, match_pos):
if dir_only:
- yield from select_next(entry_path, exists=True)
+ yield from select_next(
+ entry_path, fd, entry_name, exists=True)
else:
# Optimization: directly yield the path if this is
# last pattern part.
yield entry_path
if is_dir:
- stack.append(entry_path)
+ stack.append((entry_path, fd, entry_name))
return select_recursive
- def select_exists(self, path, exists=False):
- """Yields the given path, if it exists.
+ def select_exists(self, path, dir_fd=None, rel_path=None, exists=False, empty=False):
+ """Yields the given path, if it exists. If *dir_fd* is given, we check
+ whether *rel_path* exists relative to the fd.
"""
- if exists:
+ if empty:
+ # Suppress initial path so iglob() doesn't yield the empty string.
+ pass
+ elif exists:
# Optimization: this path is already known to exist, e.g. because
# it was returned from os.scandir(), so we skip calling lstat().
yield path
+ elif dir_fd is not None:
+ try:
+ self.lstat(rel_path, dir_fd=dir_fd)
+ yield path
+ except OSError:
+ pass
elif self.lexists(path):
yield path
@@ -518,6 +463,9 @@ class _StringGlobber(_GlobberBase):
"""Provides shell-style pattern matching and globbing for string paths.
"""
lexists = staticmethod(os.path.lexists)
+ lstat = staticmethod(os.lstat)
+ open = staticmethod(os.open)
+ close = staticmethod(os.close)
concat_path = operator.add
@staticmethod
@@ -528,6 +476,20 @@ def scandir(path):
entries = list(scandir_it)
return ((entry, entry.name, entry.path) for entry in entries)
+ @staticmethod
+ def scandir_cwd():
+ with os.scandir() as scandir_it:
+ entries = list(scandir_it)
+ # Suppress leading dot when scanning current directory.
+ return ((entry, entry.name, entry.name) for entry in entries)
+
+ @staticmethod
+ def scandir_fd(fd, prefix):
+ prefix = os.path.join(prefix, prefix[:0])
+ with os.scandir(fd) as scandir_it:
+ entries = list(scandir_it)
+ return ((entry, entry.name, prefix + entry.name) for entry in entries)
+
class _PathGlobber(_GlobberBase):
"""Provides shell-style pattern matching and globbing for pathlib paths.
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 930701d4789f5c..bd2252ef77dcf6 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -196,7 +196,8 @@ def full_match(self, pattern, *, case_sensitive=None):
pattern = self.with_segments(pattern)
if case_sensitive is None:
case_sensitive = self.parser.normcase('Aa') == 'Aa'
- globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True)
+ globber = _PathGlobber(pattern.parser.sep, case_sensitive,
+ recursive=True, include_hidden=True)
match = globber.compile(str(pattern))
return match(str(self)) is not None
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index 83da6960f00489..492bae0b7f7cd8 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -558,7 +558,8 @@ def full_match(self, pattern, *, case_sensitive=None):
# paths shouldn't match wildcards, so we change it to the empty string.
path = str(self) if self.parts else ''
pattern = str(pattern) if pattern.parts else ''
- globber = _StringGlobber(self.parser.sep, case_sensitive, recursive=True)
+ globber = _StringGlobber(self.parser.sep, case_sensitive,
+ recursive=True, include_hidden=True)
return globber.compile(pattern)(path) is not None
def match(self, path_pattern, *, case_sensitive=None):
@@ -580,7 +581,8 @@ def match(self, path_pattern, *, case_sensitive=None):
return False
if len(path_parts) > len(pattern_parts) and path_pattern.anchor:
return False
- globber = _StringGlobber(self.parser.sep, case_sensitive)
+ globber = _StringGlobber(self.parser.sep, case_sensitive,
+ include_hidden=True)
for path_part, pattern_part in zip(path_parts, pattern_parts):
match = globber.compile(pattern_part)
if match(path_part) is None:
@@ -855,7 +857,8 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=False):
case_pedantic = True
parts = self._parse_pattern(pattern)
recursive = True if recurse_symlinks else _no_recurse_symlinks
- globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive)
+ globber = _StringGlobber(self.parser.sep, case_sensitive, case_pedantic,
+ recursive, include_hidden=True)
select = globber.selector(parts[::-1])
root = str(self)
paths = select(self.parser.join(root, ''))
diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py
index da73769c16e9af..2290e06e60e377 100644
--- a/Lib/test/test_glob.py
+++ b/Lib/test/test_glob.py
@@ -4,14 +4,18 @@
import shutil
import sys
import unittest
+import unittest.mock
import warnings
from test import support
-from test.support import is_wasi, Py_DEBUG
+from test.support import is_wasi, Py_DEBUG, infinite_recursion
from test.support.os_helper import (TESTFN, skip_unless_symlink,
can_symlink, create_empty_file, change_cwd)
+_supports_dir_fd = {os.open, os.stat} <= os.supports_dir_fd and os.scandir in os.supports_fd
+
+
class GlobTests(unittest.TestCase):
dir_fd = None
@@ -49,7 +53,7 @@ def setUp(self):
def open_dirfd(self):
if self.dir_fd is not None:
os.close(self.dir_fd)
- if {os.open, os.stat} <= os.supports_dir_fd and os.scandir in os.supports_fd:
+ if _supports_dir_fd:
self.dir_fd = os.open(self.tempdir, os.O_RDONLY | os.O_DIRECTORY)
else:
self.dir_fd = None
@@ -177,20 +181,18 @@ def test_glob_directory_with_trailing_slash(self):
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.norm('aaa') + os.sep])
+ # Redundant separators are preserved and normalized
self.assertEqual(glob.glob(self.norm('aaa') + sep*2),
- [self.norm('aaa') + sep*2])
+ [self.norm('aaa') + os.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])
+ [self.norm('aaa') + os.sep*2, self.norm('aab') + os.sep*2])
def test_glob_bytes_directory_with_trailing_slash(self):
# Same as test_glob_directory_with_trailing_slash, but with a
@@ -200,16 +202,16 @@ def test_glob_bytes_directory_with_trailing_slash(self):
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)])
+ [os.fsencode(self.norm('aaa') + os.sep)])
self.assertEqual(glob.glob(os.fsencode(self.norm('aaa') + sep*2)),
- [os.fsencode(self.norm('aaa') + sep*2)])
+ [os.fsencode(self.norm('aaa') + os.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)])
+ [os.fsencode(self.norm('aaa') + os.sep*2),
+ os.fsencode(self.norm('aab') + os.sep*2)])
@skip_unless_symlink
def test_glob_symlinks(self):
@@ -326,8 +328,12 @@ def test_recursive_glob(self):
with change_cwd(self.tempdir):
join = os.path.join
eq(glob.glob('**', recursive=True), [join(*i) for i in full])
+ eq(glob.glob(join('**', '**'), recursive=True),
+ [join(*i) for i in full])
eq(glob.glob(join('**', ''), recursive=True),
[join(*i) for i in dirs])
+ eq(glob.glob(join('**', '**', ''), recursive=True),
+ [join(*i) for i in dirs])
eq(glob.glob(join('**', '*'), recursive=True),
[join(*i) for i in full])
eq(glob.glob(join(os.curdir, '**'), recursive=True),
@@ -394,6 +400,33 @@ def test_glob_many_open_files(self):
for it in iters:
self.assertEqual(next(it), p)
+ def test_glob_above_recursion_limit(self):
+ depth = 30
+ base = os.path.join(self.tempdir, 'deep')
+ p = os.path.join(base, *(['d']*depth))
+ os.makedirs(p)
+ pattern = os.path.join(base, '**', 'd')
+ with infinite_recursion(depth - 5):
+ glob.glob(pattern, recursive=True)
+
+ @unittest.skipUnless(_supports_dir_fd, "Needs support for iglob(dir_fd=...)")
+ def test_iglob_iter_close(self):
+ base = os.path.join(self.tempdir, 'deep')
+ p = os.path.join(base, *(['d'] * 10))
+ os.makedirs(p)
+ with (
+ unittest.mock.patch("glob._StringGlobber.open", wraps=os.open) as os_open,
+ unittest.mock.patch("glob._StringGlobber.close", wraps=os.close) as os_close
+ ):
+ self.assertEqual(os_open.call_count, os_close.call_count)
+ iter = glob.iglob('**/*/d', dir_fd=self.dir_fd, recursive=True)
+ self.assertEqual(os_open.call_count, os_close.call_count)
+ self.assertEqual(next(iter), 'deep/d')
+ self.assertEqual(next(iter), 'deep/d/d')
+ self.assertGreater(os_open.call_count, os_close.call_count)
+ iter.close()
+ self.assertEqual(os_open.call_count, os_close.call_count)
+
def test_glob0(self):
with self.assertWarns(DeprecationWarning):
glob.glob0(self.tempdir, 'a')
diff --git a/Misc/NEWS.d/next/Library/2024-03-05-23-08-11.gh-issue-116380.56HU7I.rst b/Misc/NEWS.d/next/Library/2024-03-05-23-08-11.gh-issue-116380.56HU7I.rst
new file mode 100644
index 00000000000000..b7f27ab7191a96
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-03-05-23-08-11.gh-issue-116380.56HU7I.rst
@@ -0,0 +1,2 @@
+Speed up :func:`glob.glob` and :func:`glob.iglob` by making use of
+:func:`glob.translate` and tracking path existence more precisely.
1
0

GH-130608: Remove `dirs_exist_ok` argument from `pathlib.Path.copy()` (#130610)
by barneygale Feb. 28, 2025
by barneygale Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/b5454509612870dd0e09aaba4b79865a5f…
commit: b5454509612870dd0e09aaba4b79865a5faad284
branch: main
author: Barney Gale <barney.gale(a)gmail.com>
committer: barneygale <barney.gale(a)gmail.com>
date: 2025-02-28T19:29:20Z
summary:
GH-130608: Remove `dirs_exist_ok` argument from `pathlib.Path.copy()` (#130610)
This feature isn't sufficiently motivated.
files:
A Misc/NEWS.d/next/Library/2025-02-26-21-21-08.gh-issue-130608.f7ix0Y.rst
M Doc/library/pathlib.rst
M Lib/pathlib/_abc.py
M Lib/pathlib/_local.py
M Lib/pathlib/_os.py
M Lib/test/test_pathlib/test_pathlib_abc.py
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 8977ccfe6e4124..a2955d28d2dd9e 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -1571,8 +1571,7 @@ Creating files and directories
Copying, moving and deleting
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. method:: Path.copy(target, *, follow_symlinks=True, dirs_exist_ok=False, \
- preserve_metadata=False)
+.. method:: Path.copy(target, *, follow_symlinks=True, preserve_metadata=False)
Copy this file or directory tree to the given *target*, and return a new
:class:`!Path` instance pointing to *target*.
@@ -1582,12 +1581,6 @@ Copying, moving and deleting
default), the symlink's target is copied. Otherwise, the symlink is
recreated at the destination.
- If the source is a directory and *dirs_exist_ok* is false (the default), a
- :exc:`FileExistsError` is raised if the target is an existing directory.
- If *dirs_exists_ok* is true, the copying operation will overwrite
- existing files within the destination tree with corresponding files
- from the source tree.
-
If *preserve_metadata* is false (the default), only directory structures
and file data are guaranteed to be copied. Set *preserve_metadata* to true
to ensure that file and directory permissions, flags, last access and
@@ -1604,7 +1597,7 @@ Copying, moving and deleting
.. method:: Path.copy_into(target_dir, *, follow_symlinks=True, \
- dirs_exist_ok=False, preserve_metadata=False)
+ preserve_metadata=False)
Copy this file or directory tree into the given *target_dir*, which should
be an existing directory. Other arguments are handled identically to
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 6b46aea9aea4b9..930701d4789f5c 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -340,19 +340,18 @@ def readlink(self):
"""
raise NotImplementedError
- def copy(self, target, follow_symlinks=True, dirs_exist_ok=False,
- preserve_metadata=False):
+ def copy(self, target, follow_symlinks=True, preserve_metadata=False):
"""
Recursively copy this file or directory tree to the given destination.
"""
if not hasattr(target, 'with_segments'):
target = self.with_segments(target)
ensure_distinct_paths(self, target)
- copy_file(self, target, follow_symlinks, dirs_exist_ok, preserve_metadata)
+ copy_file(self, target, follow_symlinks, preserve_metadata)
return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, *, follow_symlinks=True,
- dirs_exist_ok=False, preserve_metadata=False):
+ preserve_metadata=False):
"""
Copy this file or directory tree into the given existing directory.
"""
@@ -364,7 +363,6 @@ def copy_into(self, target_dir, *, follow_symlinks=True,
else:
target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks,
- dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata)
diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py
index ba81c3de1ccb8b..83da6960f00489 100644
--- a/Lib/pathlib/_local.py
+++ b/Lib/pathlib/_local.py
@@ -1093,19 +1093,18 @@ def replace(self, target):
target = self.with_segments(target)
return target
- def copy(self, target, follow_symlinks=True, dirs_exist_ok=False,
- preserve_metadata=False):
+ def copy(self, target, follow_symlinks=True, preserve_metadata=False):
"""
Recursively copy this file or directory tree to the given destination.
"""
if not hasattr(target, 'with_segments'):
target = self.with_segments(target)
ensure_distinct_paths(self, target)
- copy_file(self, target, follow_symlinks, dirs_exist_ok, preserve_metadata)
+ copy_file(self, target, follow_symlinks, preserve_metadata)
return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, *, follow_symlinks=True,
- dirs_exist_ok=False, preserve_metadata=False):
+ preserve_metadata=False):
"""
Copy this file or directory tree into the given existing directory.
"""
@@ -1117,7 +1116,6 @@ def copy_into(self, target_dir, *, follow_symlinks=True,
else:
target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks,
- dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata)
def move(self, target):
diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py
index a741f201b6d925..c8cb4be548d045 100644
--- a/Lib/pathlib/_os.py
+++ b/Lib/pathlib/_os.py
@@ -242,8 +242,7 @@ def ensure_different_files(source, target):
raise err
-def copy_file(source, target, follow_symlinks=True, dirs_exist_ok=False,
- preserve_metadata=False):
+def copy_file(source, target, follow_symlinks=True, preserve_metadata=False):
"""
Recursively copy the given source ReadablePath to the given target WritablePath.
"""
@@ -254,10 +253,10 @@ def copy_file(source, target, follow_symlinks=True, dirs_exist_ok=False,
target._write_info(info, follow_symlinks=False)
elif info.is_dir():
children = source.iterdir()
- target.mkdir(exist_ok=dirs_exist_ok)
+ target.mkdir()
for src in children:
dst = target.joinpath(src.name)
- copy_file(src, dst, follow_symlinks, dirs_exist_ok, preserve_metadata)
+ copy_file(src, dst, follow_symlinks, preserve_metadata)
if preserve_metadata:
target._write_info(info)
else:
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py
index a6e3e0709833a3..b64924d06d420b 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -1495,23 +1495,6 @@ def test_copy_dir_to_existing_directory(self):
target.joinpath('dirD').mkdir()
self.assertRaises(FileExistsError, source.copy, target)
- def test_copy_dir_to_existing_directory_dirs_exist_ok(self):
- base = self.cls(self.base)
- source = base / 'dirC'
- target = base / 'copyC'
- target.mkdir()
- target.joinpath('dirD').mkdir()
- result = source.copy(target, dirs_exist_ok=True)
- self.assertEqual(result, target)
- self.assertTrue(result.info.is_dir())
- self.assertTrue(result.joinpath('dirD').info.is_dir())
- self.assertTrue(result.joinpath('dirD', 'fileD').info.is_file())
- self.assertEqual(result.joinpath('dirD', 'fileD').read_text(),
- "this is file D\n")
- self.assertTrue(result.joinpath('fileC').info.is_file())
- self.assertTrue(result.joinpath('fileC').read_text(),
- "this is file C\n")
-
def test_copy_dir_to_itself(self):
base = self.cls(self.base)
source = base / 'dirC'
diff --git a/Misc/NEWS.d/next/Library/2025-02-26-21-21-08.gh-issue-130608.f7ix0Y.rst b/Misc/NEWS.d/next/Library/2025-02-26-21-21-08.gh-issue-130608.f7ix0Y.rst
new file mode 100644
index 00000000000000..240c14fbca2580
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-02-26-21-21-08.gh-issue-130608.f7ix0Y.rst
@@ -0,0 +1,2 @@
+Remove *dirs_exist_ok* argument from :meth:`pathlib.Path.copy` and
+:meth:`~pathlib.Path.copy_into`. These methods are new in Python 3.14.
1
0

gh-130660: Restore sys.ps1 and sys.ps2 after code.interact (#130661)
by gaogaotiantian Feb. 28, 2025
by gaogaotiantian Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/fdcbc29f26448f47201ec40fcf3b640563…
commit: fdcbc29f26448f47201ec40fcf3b6405631c85d3
branch: main
author: Tian Gao <gaogaotiantian(a)hotmail.com>
committer: gaogaotiantian <gaogaotiantian(a)hotmail.com>
date: 2025-02-28T13:15:55-05:00
summary:
gh-130660: Restore sys.ps1 and sys.ps2 after code.interact (#130661)
files:
A Misc/NEWS.d/next/Library/2025-02-28-01-10-14.gh-issue-130660.VIThEz.rst
M Lib/code.py
M Lib/test/test_code_module.py
diff --git a/Lib/code.py b/Lib/code.py
index 1cc2ed8b1dbf28..41331dfd071f11 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -219,12 +219,17 @@ def interact(self, banner=None, exitmsg=None):
"""
try:
sys.ps1
+ delete_ps1_after = False
except AttributeError:
sys.ps1 = ">>> "
+ delete_ps1_after = True
try:
- sys.ps2
+ _ps2 = sys.ps2
+ delete_ps2_after = False
except AttributeError:
sys.ps2 = "... "
+ delete_ps2_after = True
+
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
if banner is None:
self.write("Python %s on %s\n%s\n(%s)\n" %
@@ -287,6 +292,12 @@ def interact(self, banner=None, exitmsg=None):
if _quit is not None:
builtins.quit = _quit
+ if delete_ps1_after:
+ del sys.ps1
+
+ if delete_ps2_after:
+ del sys.ps2
+
if exitmsg is None:
self.write('now exiting %s...\n' % self.__class__.__name__)
elif exitmsg != '':
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index faa0b38f8373e3..57fb130070b34e 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -39,19 +39,47 @@ def setUp(self):
self.mock_sys()
def test_ps1(self):
- self.infunc.side_effect = EOFError('Finished')
+ self.infunc.side_effect = [
+ "import code",
+ "code.sys.ps1",
+ EOFError('Finished')
+ ]
self.console.interact()
- self.assertEqual(self.sysmod.ps1, '>>> ')
+ output = ''.join(''.join(call[1]) for call in self.stdout.method_calls)
+ self.assertIn('>>> ', output)
+ self.assertNotHasAttr(self.sysmod, 'ps1')
+
+ self.infunc.side_effect = [
+ "import code",
+ "code.sys.ps1",
+ EOFError('Finished')
+ ]
self.sysmod.ps1 = 'custom1> '
self.console.interact()
+ output = ''.join(''.join(call[1]) for call in self.stdout.method_calls)
+ self.assertIn('custom1> ', output)
self.assertEqual(self.sysmod.ps1, 'custom1> ')
def test_ps2(self):
- self.infunc.side_effect = EOFError('Finished')
+ self.infunc.side_effect = [
+ "import code",
+ "code.sys.ps2",
+ EOFError('Finished')
+ ]
self.console.interact()
- self.assertEqual(self.sysmod.ps2, '... ')
+ output = ''.join(''.join(call[1]) for call in self.stdout.method_calls)
+ self.assertIn('... ', output)
+ self.assertNotHasAttr(self.sysmod, 'ps2')
+
+ self.infunc.side_effect = [
+ "import code",
+ "code.sys.ps2",
+ EOFError('Finished')
+ ]
self.sysmod.ps2 = 'custom2> '
self.console.interact()
+ output = ''.join(''.join(call[1]) for call in self.stdout.method_calls)
+ self.assertIn('custom2> ', output)
self.assertEqual(self.sysmod.ps2, 'custom2> ')
def test_console_stderr(self):
diff --git a/Misc/NEWS.d/next/Library/2025-02-28-01-10-14.gh-issue-130660.VIThEz.rst b/Misc/NEWS.d/next/Library/2025-02-28-01-10-14.gh-issue-130660.VIThEz.rst
new file mode 100644
index 00000000000000..92984e7e2d53c8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-02-28-01-10-14.gh-issue-130660.VIThEz.rst
@@ -0,0 +1 @@
+``sys.ps1`` and ``sys.ps2`` are now restored after :func:`code.interact` call.
1
0

Feb. 28, 2025
https://github.com/python/cpython/commit/54965f3fb25b381995a73b09d928c344bd…
commit: 54965f3fb25b381995a73b09d928c344bd2b86bd
branch: main
author: Mark Shannon <mark(a)hotpy.org>
committer: markshannon <mark(a)hotpy.org>
date: 2025-02-28T18:00:38Z
summary:
GH-130296: Avoid stack transients in four instructions. (GH-130310)
* Combine _GUARD_GLOBALS_VERSION_PUSH_KEYS and _LOAD_GLOBAL_MODULE_FROM_KEYS into _LOAD_GLOBAL_MODULE
* Combine _GUARD_BUILTINS_VERSION_PUSH_KEYS and _LOAD_GLOBAL_BUILTINS_FROM_KEYS into _LOAD_GLOBAL_BUILTINS
* Combine _CHECK_ATTR_MODULE_PUSH_KEYS and _LOAD_ATTR_MODULE_FROM_KEYS into _LOAD_ATTR_MODULE
* Remove stack transient in LOAD_ATTR_WITH_HINT
files:
M Include/internal/pycore_opcode_metadata.h
M Include/internal/pycore_uop_ids.h
M Include/internal/pycore_uop_metadata.h
M Lib/test/test_generated_cases.py
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
M Python/optimizer.c
M Python/optimizer_analysis.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Tools/cases_generator/analyzer.py
M Tools/cases_generator/generators_common.py
M Tools/cases_generator/opcode_metadata_generator.py
M Tools/cases_generator/tier2_generator.py
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 195ec832f73c62..b6d85490eef1f3 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1546,7 +1546,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) {
return 0;
}
case LOAD_ATTR_MODULE: {
- *effect = Py_MAX(1, (oparg & 1));
+ *effect = Py_MAX(0, (oparg & 1));
return 0;
}
case LOAD_ATTR_NONDESCRIPTOR_NO_DICT: {
@@ -1566,7 +1566,7 @@ int _PyOpcode_max_stack_effect(int opcode, int oparg, int *effect) {
return 0;
}
case LOAD_ATTR_WITH_HINT: {
- *effect = Py_MAX(1, (oparg & 1));
+ *effect = Py_MAX(0, (oparg & 1));
return 0;
}
case LOAD_BUILD_CLASS: {
@@ -1991,7 +1991,7 @@ enum InstructionFormat {
#define OPCODE_HAS_ERROR_NO_POP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ERROR_NO_POP_FLAG))
#define OPCODE_HAS_NO_SAVE_IP(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_NO_SAVE_IP_FLAG))
-#define OPARG_FULL 0
+#define OPARG_SIMPLE 0
#define OPARG_CACHE_1 1
#define OPARG_CACHE_2 2
#define OPARG_CACHE_4 4
@@ -1999,6 +1999,9 @@ enum InstructionFormat {
#define OPARG_BOTTOM 6
#define OPARG_SAVE_RETURN_OFFSET 7
#define OPARG_REPLACED 9
+#define OPERAND1_1 10
+#define OPERAND1_2 11
+#define OPERAND1_4 12
struct opcode_metadata {
uint8_t valid_entry;
@@ -2145,7 +2148,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
[LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
[LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
[LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
- [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
+ [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
[LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[LOAD_COMMON_CONSTANT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
[LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG },
@@ -2254,184 +2257,184 @@ extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256];
#ifdef NEED_OPCODE_METADATA
const struct opcode_macro_expansion
_PyOpcode_macro_expansion[256] = {
- [BINARY_OP] = { .nuops = 1, .uops = { { _BINARY_OP, 0, 0 } } },
- [BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } },
- [BINARY_OP_ADD_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_ADD_INT, 0, 0 } } },
- [BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } },
+ [BINARY_OP] = { .nuops = 1, .uops = { { _BINARY_OP, OPARG_SIMPLE, 4 } } },
+ [BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_FLOAT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_ADD_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_ADD_UNICODE, OPARG_SIMPLE, 5 } } },
[BINARY_OP_EXTEND] = { .nuops = 2, .uops = { { _GUARD_BINARY_OP_EXTEND, 4, 1 }, { _BINARY_OP_EXTEND, 4, 1 } } },
- [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 } } },
- [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 } } },
- [BUILD_SLICE] = { .nuops = 1, .uops = { { _BUILD_SLICE, 0, 0 } } },
- [BUILD_STRING] = { .nuops = 1, .uops = { { _BUILD_STRING, 0, 0 } } },
- [BUILD_TUPLE] = { .nuops = 1, .uops = { { _BUILD_TUPLE, 0, 0 } } },
- [CALL_ALLOC_AND_ENTER_INIT] = { .nuops = 4, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_AND_ALLOCATE_OBJECT, 2, 1 }, { _CREATE_INIT_FRAME, 0, 0 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, 0, 0 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_BOUND_METHOD_GENERAL] = { .nuops = 6, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_METHOD_VERSION, 2, 1 }, { _EXPAND_METHOD, 0, 0 }, { _PY_FRAME_GENERAL, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_BUILTIN_CLASS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_CLASS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_BUILTIN_FAST] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST_WITH_KEYWORDS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_BUILTIN_O] = { .nuops = 2, .uops = { { _CALL_BUILTIN_O, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_INTRINSIC_1] = { .nuops = 1, .uops = { { _CALL_INTRINSIC_1, 0, 0 } } },
- [CALL_INTRINSIC_2] = { .nuops = 1, .uops = { { _CALL_INTRINSIC_2, 0, 0 } } },
- [CALL_ISINSTANCE] = { .nuops = 1, .uops = { { _CALL_ISINSTANCE, 0, 0 } } },
- [CALL_KW_BOUND_METHOD] = { .nuops = 6, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_METHOD_VERSION_KW, 2, 1 }, { _EXPAND_METHOD_KW, 0, 0 }, { _PY_FRAME_KW, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_KW_NON_PY] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE_KW, 0, 0 }, { _CALL_KW_NON_PY, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_KW_PY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _PY_FRAME_KW, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_LEN] = { .nuops = 1, .uops = { { _CALL_LEN, 0, 0 } } },
- [CALL_LIST_APPEND] = { .nuops = 1, .uops = { { _CALL_LIST_APPEND, 0, 0 } } },
- [CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_NOARGS, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_O, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_NON_PY_GENERAL] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE, 0, 0 }, { _CALL_NON_PY_GENERAL, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_PY_EXACT_ARGS] = { .nuops = 7, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, 0, 0 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_PY_GENERAL] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _PY_FRAME_GENERAL, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 3 }, { _PUSH_FRAME, 0, 0 } } },
- [CALL_STR_1] = { .nuops = 2, .uops = { { _CALL_STR_1, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_TUPLE_1] = { .nuops = 2, .uops = { { _CALL_TUPLE_1, 0, 0 }, { _CHECK_PERIODIC, 0, 0 } } },
- [CALL_TYPE_1] = { .nuops = 1, .uops = { { _CALL_TYPE_1, 0, 0 } } },
- [CHECK_EG_MATCH] = { .nuops = 1, .uops = { { _CHECK_EG_MATCH, 0, 0 } } },
- [CHECK_EXC_MATCH] = { .nuops = 1, .uops = { { _CHECK_EXC_MATCH, 0, 0 } } },
- [COMPARE_OP] = { .nuops = 1, .uops = { { _COMPARE_OP, 0, 0 } } },
- [COMPARE_OP_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _COMPARE_OP_FLOAT, 0, 0 } } },
- [COMPARE_OP_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _COMPARE_OP_INT, 0, 0 } } },
- [COMPARE_OP_STR] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _COMPARE_OP_STR, 0, 0 } } },
- [CONTAINS_OP] = { .nuops = 1, .uops = { { _CONTAINS_OP, 0, 0 } } },
- [CONTAINS_OP_DICT] = { .nuops = 1, .uops = { { _CONTAINS_OP_DICT, 0, 0 } } },
- [CONTAINS_OP_SET] = { .nuops = 1, .uops = { { _CONTAINS_OP_SET, 0, 0 } } },
- [CONVERT_VALUE] = { .nuops = 1, .uops = { { _CONVERT_VALUE, 0, 0 } } },
- [COPY] = { .nuops = 1, .uops = { { _COPY, 0, 0 } } },
- [COPY_FREE_VARS] = { .nuops = 1, .uops = { { _COPY_FREE_VARS, 0, 0 } } },
- [DELETE_ATTR] = { .nuops = 1, .uops = { { _DELETE_ATTR, 0, 0 } } },
- [DELETE_DEREF] = { .nuops = 1, .uops = { { _DELETE_DEREF, 0, 0 } } },
- [DELETE_FAST] = { .nuops = 1, .uops = { { _DELETE_FAST, 0, 0 } } },
- [DELETE_GLOBAL] = { .nuops = 1, .uops = { { _DELETE_GLOBAL, 0, 0 } } },
- [DELETE_NAME] = { .nuops = 1, .uops = { { _DELETE_NAME, 0, 0 } } },
- [DELETE_SUBSCR] = { .nuops = 1, .uops = { { _DELETE_SUBSCR, 0, 0 } } },
- [DICT_MERGE] = { .nuops = 1, .uops = { { _DICT_MERGE, 0, 0 } } },
- [DICT_UPDATE] = { .nuops = 1, .uops = { { _DICT_UPDATE, 0, 0 } } },
- [END_FOR] = { .nuops = 1, .uops = { { _END_FOR, 0, 0 } } },
- [END_SEND] = { .nuops = 1, .uops = { { _END_SEND, 0, 0 } } },
- [EXIT_INIT_CHECK] = { .nuops = 1, .uops = { { _EXIT_INIT_CHECK, 0, 0 } } },
- [FORMAT_SIMPLE] = { .nuops = 1, .uops = { { _FORMAT_SIMPLE, 0, 0 } } },
- [FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { _FORMAT_WITH_SPEC, 0, 0 } } },
- [FOR_ITER] = { .nuops = 1, .uops = { { _FOR_ITER, 9, 0 } } },
- [FOR_ITER_GEN] = { .nuops = 3, .uops = { { _CHECK_PEP_523, 0, 0 }, { _FOR_ITER_GEN_FRAME, 0, 0 }, { _PUSH_FRAME, 0, 0 } } },
- [FOR_ITER_LIST] = { .nuops = 3, .uops = { { _ITER_CHECK_LIST, 0, 0 }, { _ITER_JUMP_LIST, 9, 1 }, { _ITER_NEXT_LIST, 0, 0 } } },
- [FOR_ITER_RANGE] = { .nuops = 3, .uops = { { _ITER_CHECK_RANGE, 0, 0 }, { _ITER_JUMP_RANGE, 9, 1 }, { _ITER_NEXT_RANGE, 0, 0 } } },
- [FOR_ITER_TUPLE] = { .nuops = 3, .uops = { { _ITER_CHECK_TUPLE, 0, 0 }, { _ITER_JUMP_TUPLE, 9, 1 }, { _ITER_NEXT_TUPLE, 0, 0 } } },
- [GET_AITER] = { .nuops = 1, .uops = { { _GET_AITER, 0, 0 } } },
- [GET_ANEXT] = { .nuops = 1, .uops = { { _GET_ANEXT, 0, 0 } } },
- [GET_AWAITABLE] = { .nuops = 1, .uops = { { _GET_AWAITABLE, 0, 0 } } },
- [GET_ITER] = { .nuops = 1, .uops = { { _GET_ITER, 0, 0 } } },
- [GET_LEN] = { .nuops = 1, .uops = { { _GET_LEN, 0, 0 } } },
- [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { _GET_YIELD_FROM_ITER, 0, 0 } } },
- [IMPORT_FROM] = { .nuops = 1, .uops = { { _IMPORT_FROM, 0, 0 } } },
- [IMPORT_NAME] = { .nuops = 1, .uops = { { _IMPORT_NAME, 0, 0 } } },
- [IS_OP] = { .nuops = 1, .uops = { { _IS_OP, 0, 0 } } },
- [LIST_APPEND] = { .nuops = 1, .uops = { { _LIST_APPEND, 0, 0 } } },
- [LIST_EXTEND] = { .nuops = 1, .uops = { { _LIST_EXTEND, 0, 0 } } },
- [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, 0, 0 } } },
- [LOAD_ATTR_CLASS] = { .nuops = 3, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { .nuops = 4, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _GUARD_TYPE_VERSION, 2, 3 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
+ [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, OPARG_SIMPLE, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_MULTIPLY_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_MULTIPLY_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBSCR_DICT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_DICT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBSCR_GETITEM] = { .nuops = 4, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_CHECK_FUNC, OPARG_SIMPLE, 5 }, { _BINARY_OP_SUBSCR_INIT_CALL, OPARG_SIMPLE, 5 }, { _PUSH_FRAME, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_LIST_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBSCR_STR_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_STR_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBSCR_TUPLE_INT] = { .nuops = 1, .uops = { { _BINARY_OP_SUBSCR_TUPLE_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, OPARG_SIMPLE, 5 } } },
+ [BINARY_OP_SUBTRACT_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_INT, OPARG_SIMPLE, 5 } } },
+ [BINARY_SLICE] = { .nuops = 1, .uops = { { _BINARY_SLICE, OPARG_SIMPLE, 0 } } },
+ [BUILD_LIST] = { .nuops = 1, .uops = { { _BUILD_LIST, OPARG_SIMPLE, 0 } } },
+ [BUILD_MAP] = { .nuops = 1, .uops = { { _BUILD_MAP, OPARG_SIMPLE, 0 } } },
+ [BUILD_SET] = { .nuops = 1, .uops = { { _BUILD_SET, OPARG_SIMPLE, 0 } } },
+ [BUILD_SLICE] = { .nuops = 1, .uops = { { _BUILD_SLICE, OPARG_SIMPLE, 0 } } },
+ [BUILD_STRING] = { .nuops = 1, .uops = { { _BUILD_STRING, OPARG_SIMPLE, 0 } } },
+ [BUILD_TUPLE] = { .nuops = 1, .uops = { { _BUILD_TUPLE, OPARG_SIMPLE, 0 } } },
+ [CALL_ALLOC_AND_ENTER_INIT] = { .nuops = 4, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_AND_ALLOCATE_OBJECT, 2, 1 }, { _CREATE_INIT_FRAME, OPARG_SIMPLE, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, OPARG_SIMPLE, 1 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_BOUND_METHOD_GENERAL] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_METHOD_VERSION, 2, 1 }, { _EXPAND_METHOD, OPARG_SIMPLE, 3 }, { _PY_FRAME_GENERAL, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_BUILTIN_CLASS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_CLASS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_BUILTIN_FAST] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_BUILTIN_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_BUILTIN_O] = { .nuops = 2, .uops = { { _CALL_BUILTIN_O, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_INTRINSIC_1] = { .nuops = 1, .uops = { { _CALL_INTRINSIC_1, OPARG_SIMPLE, 0 } } },
+ [CALL_INTRINSIC_2] = { .nuops = 1, .uops = { { _CALL_INTRINSIC_2, OPARG_SIMPLE, 0 } } },
+ [CALL_ISINSTANCE] = { .nuops = 1, .uops = { { _CALL_ISINSTANCE, OPARG_SIMPLE, 3 } } },
+ [CALL_KW_BOUND_METHOD] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_METHOD_VERSION_KW, 2, 1 }, { _EXPAND_METHOD_KW, OPARG_SIMPLE, 3 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_KW_NON_PY] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE_KW, OPARG_SIMPLE, 3 }, { _CALL_KW_NON_PY, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_KW_PY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_LEN] = { .nuops = 1, .uops = { { _CALL_LEN, OPARG_SIMPLE, 3 } } },
+ [CALL_LIST_APPEND] = { .nuops = 1, .uops = { { _CALL_LIST_APPEND, OPARG_SIMPLE, 3 } } },
+ [CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_O, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_NON_PY_GENERAL] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE, OPARG_SIMPLE, 3 }, { _CALL_NON_PY_GENERAL, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_PY_EXACT_ARGS] = { .nuops = 7, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_PY_GENERAL] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _PY_FRAME_GENERAL, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
+ [CALL_STR_1] = { .nuops = 2, .uops = { { _CALL_STR_1, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_TUPLE_1] = { .nuops = 2, .uops = { { _CALL_TUPLE_1, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC, OPARG_SIMPLE, 3 } } },
+ [CALL_TYPE_1] = { .nuops = 1, .uops = { { _CALL_TYPE_1, OPARG_SIMPLE, 3 } } },
+ [CHECK_EG_MATCH] = { .nuops = 1, .uops = { { _CHECK_EG_MATCH, OPARG_SIMPLE, 0 } } },
+ [CHECK_EXC_MATCH] = { .nuops = 1, .uops = { { _CHECK_EXC_MATCH, OPARG_SIMPLE, 0 } } },
+ [COMPARE_OP] = { .nuops = 1, .uops = { { _COMPARE_OP, OPARG_SIMPLE, 0 } } },
+ [COMPARE_OP_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, OPARG_SIMPLE, 0 }, { _COMPARE_OP_FLOAT, OPARG_SIMPLE, 1 } } },
+ [COMPARE_OP_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, OPARG_SIMPLE, 0 }, { _COMPARE_OP_INT, OPARG_SIMPLE, 1 } } },
+ [COMPARE_OP_STR] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, OPARG_SIMPLE, 0 }, { _COMPARE_OP_STR, OPARG_SIMPLE, 1 } } },
+ [CONTAINS_OP] = { .nuops = 1, .uops = { { _CONTAINS_OP, OPARG_SIMPLE, 0 } } },
+ [CONTAINS_OP_DICT] = { .nuops = 1, .uops = { { _CONTAINS_OP_DICT, OPARG_SIMPLE, 1 } } },
+ [CONTAINS_OP_SET] = { .nuops = 1, .uops = { { _CONTAINS_OP_SET, OPARG_SIMPLE, 1 } } },
+ [CONVERT_VALUE] = { .nuops = 1, .uops = { { _CONVERT_VALUE, OPARG_SIMPLE, 0 } } },
+ [COPY] = { .nuops = 1, .uops = { { _COPY, OPARG_SIMPLE, 0 } } },
+ [COPY_FREE_VARS] = { .nuops = 1, .uops = { { _COPY_FREE_VARS, OPARG_SIMPLE, 0 } } },
+ [DELETE_ATTR] = { .nuops = 1, .uops = { { _DELETE_ATTR, OPARG_SIMPLE, 0 } } },
+ [DELETE_DEREF] = { .nuops = 1, .uops = { { _DELETE_DEREF, OPARG_SIMPLE, 0 } } },
+ [DELETE_FAST] = { .nuops = 1, .uops = { { _DELETE_FAST, OPARG_SIMPLE, 0 } } },
+ [DELETE_GLOBAL] = { .nuops = 1, .uops = { { _DELETE_GLOBAL, OPARG_SIMPLE, 0 } } },
+ [DELETE_NAME] = { .nuops = 1, .uops = { { _DELETE_NAME, OPARG_SIMPLE, 0 } } },
+ [DELETE_SUBSCR] = { .nuops = 1, .uops = { { _DELETE_SUBSCR, OPARG_SIMPLE, 0 } } },
+ [DICT_MERGE] = { .nuops = 1, .uops = { { _DICT_MERGE, OPARG_SIMPLE, 0 } } },
+ [DICT_UPDATE] = { .nuops = 1, .uops = { { _DICT_UPDATE, OPARG_SIMPLE, 0 } } },
+ [END_FOR] = { .nuops = 1, .uops = { { _END_FOR, OPARG_SIMPLE, 0 } } },
+ [END_SEND] = { .nuops = 1, .uops = { { _END_SEND, OPARG_SIMPLE, 0 } } },
+ [EXIT_INIT_CHECK] = { .nuops = 1, .uops = { { _EXIT_INIT_CHECK, OPARG_SIMPLE, 0 } } },
+ [FORMAT_SIMPLE] = { .nuops = 1, .uops = { { _FORMAT_SIMPLE, OPARG_SIMPLE, 0 } } },
+ [FORMAT_WITH_SPEC] = { .nuops = 1, .uops = { { _FORMAT_WITH_SPEC, OPARG_SIMPLE, 0 } } },
+ [FOR_ITER] = { .nuops = 1, .uops = { { _FOR_ITER, OPARG_REPLACED, 0 } } },
+ [FOR_ITER_GEN] = { .nuops = 3, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _FOR_ITER_GEN_FRAME, OPARG_SIMPLE, 1 }, { _PUSH_FRAME, OPARG_SIMPLE, 1 } } },
+ [FOR_ITER_LIST] = { .nuops = 3, .uops = { { _ITER_CHECK_LIST, OPARG_SIMPLE, 1 }, { _ITER_JUMP_LIST, OPARG_REPLACED, 1 }, { _ITER_NEXT_LIST, OPARG_SIMPLE, 1 } } },
+ [FOR_ITER_RANGE] = { .nuops = 3, .uops = { { _ITER_CHECK_RANGE, OPARG_SIMPLE, 1 }, { _ITER_JUMP_RANGE, OPARG_REPLACED, 1 }, { _ITER_NEXT_RANGE, OPARG_SIMPLE, 1 } } },
+ [FOR_ITER_TUPLE] = { .nuops = 3, .uops = { { _ITER_CHECK_TUPLE, OPARG_SIMPLE, 1 }, { _ITER_JUMP_TUPLE, OPARG_REPLACED, 1 }, { _ITER_NEXT_TUPLE, OPARG_SIMPLE, 1 } } },
+ [GET_AITER] = { .nuops = 1, .uops = { { _GET_AITER, OPARG_SIMPLE, 0 } } },
+ [GET_ANEXT] = { .nuops = 1, .uops = { { _GET_ANEXT, OPARG_SIMPLE, 0 } } },
+ [GET_AWAITABLE] = { .nuops = 1, .uops = { { _GET_AWAITABLE, OPARG_SIMPLE, 0 } } },
+ [GET_ITER] = { .nuops = 1, .uops = { { _GET_ITER, OPARG_SIMPLE, 0 } } },
+ [GET_LEN] = { .nuops = 1, .uops = { { _GET_LEN, OPARG_SIMPLE, 0 } } },
+ [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { _GET_YIELD_FROM_ITER, OPARG_SIMPLE, 0 } } },
+ [IMPORT_FROM] = { .nuops = 1, .uops = { { _IMPORT_FROM, OPARG_SIMPLE, 0 } } },
+ [IMPORT_NAME] = { .nuops = 1, .uops = { { _IMPORT_NAME, OPARG_SIMPLE, 0 } } },
+ [IS_OP] = { .nuops = 1, .uops = { { _IS_OP, OPARG_SIMPLE, 0 } } },
+ [LIST_APPEND] = { .nuops = 1, .uops = { { _LIST_APPEND, OPARG_SIMPLE, 0 } } },
+ [LIST_EXTEND] = { .nuops = 1, .uops = { { _LIST_EXTEND, OPARG_SIMPLE, 0 } } },
+ [LOAD_ATTR] = { .nuops = 1, .uops = { { _LOAD_ATTR, OPARG_SIMPLE, 8 } } },
+ [LOAD_ATTR_CLASS] = { .nuops = 3, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
+ [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = { .nuops = 4, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _GUARD_TYPE_VERSION, 2, 3 }, { _LOAD_ATTR_CLASS, 4, 5 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
+ [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, OPARG_SIMPLE, 3 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
[LOAD_ATTR_METHOD_LAZY_DICT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_METHOD_LAZY_DICT, 1, 3 }, { _LOAD_ATTR_METHOD_LAZY_DICT, 4, 5 } } },
[LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } },
- [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } },
- [LOAD_ATTR_MODULE] = { .nuops = 3, .uops = { { _CHECK_ATTR_MODULE_PUSH_KEYS, 2, 1 }, { _LOAD_ATTR_MODULE_FROM_KEYS, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
+ [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } },
+ [LOAD_ATTR_MODULE] = { .nuops = 3, .uops = { { _LOAD_ATTR_MODULE, 2, 1 }, { _LOAD_ATTR_MODULE, OPERAND1_1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
[LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_NONDESCRIPTOR_NO_DICT, 4, 5 } } },
- [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } },
- [LOAD_ATTR_PROPERTY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, 0, 0 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_PROPERTY_FRAME, 4, 5 }, { _SAVE_RETURN_OFFSET, 7, 9 }, { _PUSH_FRAME, 0, 0 } } },
- [LOAD_ATTR_SLOT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_ATTR_WITH_HINT] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { _LOAD_BUILD_CLASS, 0, 0 } } },
- [LOAD_COMMON_CONSTANT] = { .nuops = 1, .uops = { { _LOAD_COMMON_CONSTANT, 0, 0 } } },
- [LOAD_CONST_IMMORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_IMMORTAL, 0, 0 } } },
- [LOAD_CONST_MORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_MORTAL, 0, 0 } } },
- [LOAD_DEREF] = { .nuops = 1, .uops = { { _LOAD_DEREF, 0, 0 } } },
- [LOAD_FAST] = { .nuops = 1, .uops = { { _LOAD_FAST, 0, 0 } } },
- [LOAD_FAST_AND_CLEAR] = { .nuops = 1, .uops = { { _LOAD_FAST_AND_CLEAR, 0, 0 } } },
- [LOAD_FAST_CHECK] = { .nuops = 1, .uops = { { _LOAD_FAST_CHECK, 0, 0 } } },
- [LOAD_FAST_LOAD_FAST] = { .nuops = 2, .uops = { { _LOAD_FAST, 5, 0 }, { _LOAD_FAST, 6, 0 } } },
- [LOAD_FROM_DICT_OR_DEREF] = { .nuops = 1, .uops = { { _LOAD_FROM_DICT_OR_DEREF, 0, 0 } } },
- [LOAD_GLOBAL] = { .nuops = 2, .uops = { { _LOAD_GLOBAL, 0, 0 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_GLOBAL_BUILTIN] = { .nuops = 4, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _GUARD_BUILTINS_VERSION_PUSH_KEYS, 1, 2 }, { _LOAD_GLOBAL_BUILTINS_FROM_KEYS, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_GLOBAL_MODULE] = { .nuops = 3, .uops = { { _GUARD_GLOBALS_VERSION_PUSH_KEYS, 1, 1 }, { _LOAD_GLOBAL_MODULE_FROM_KEYS, 1, 3 }, { _PUSH_NULL_CONDITIONAL, 0, 0 } } },
- [LOAD_LOCALS] = { .nuops = 1, .uops = { { _LOAD_LOCALS, 0, 0 } } },
- [LOAD_NAME] = { .nuops = 1, .uops = { { _LOAD_NAME, 0, 0 } } },
- [LOAD_SMALL_INT] = { .nuops = 1, .uops = { { _LOAD_SMALL_INT, 0, 0 } } },
- [LOAD_SPECIAL] = { .nuops = 1, .uops = { { _LOAD_SPECIAL, 0, 0 } } },
- [LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_ATTR, 0, 0 } } },
- [LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_METHOD, 0, 0 } } },
- [MAKE_CELL] = { .nuops = 1, .uops = { { _MAKE_CELL, 0, 0 } } },
- [MAKE_FUNCTION] = { .nuops = 1, .uops = { { _MAKE_FUNCTION, 0, 0 } } },
- [MAP_ADD] = { .nuops = 1, .uops = { { _MAP_ADD, 0, 0 } } },
- [MATCH_CLASS] = { .nuops = 1, .uops = { { _MATCH_CLASS, 0, 0 } } },
- [MATCH_KEYS] = { .nuops = 1, .uops = { { _MATCH_KEYS, 0, 0 } } },
- [MATCH_MAPPING] = { .nuops = 1, .uops = { { _MATCH_MAPPING, 0, 0 } } },
- [MATCH_SEQUENCE] = { .nuops = 1, .uops = { { _MATCH_SEQUENCE, 0, 0 } } },
- [NOP] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } },
- [NOT_TAKEN] = { .nuops = 1, .uops = { { _NOP, 0, 0 } } },
- [POP_EXCEPT] = { .nuops = 1, .uops = { { _POP_EXCEPT, 0, 0 } } },
- [POP_ITER] = { .nuops = 1, .uops = { { _POP_TOP, 0, 0 } } },
- [POP_JUMP_IF_FALSE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_FALSE, 9, 1 } } },
- [POP_JUMP_IF_NONE] = { .nuops = 2, .uops = { { _IS_NONE, 0, 0 }, { _POP_JUMP_IF_TRUE, 9, 1 } } },
- [POP_JUMP_IF_NOT_NONE] = { .nuops = 2, .uops = { { _IS_NONE, 0, 0 }, { _POP_JUMP_IF_FALSE, 9, 1 } } },
- [POP_JUMP_IF_TRUE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_TRUE, 9, 1 } } },
- [POP_TOP] = { .nuops = 1, .uops = { { _POP_TOP, 0, 0 } } },
- [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { _PUSH_EXC_INFO, 0, 0 } } },
- [PUSH_NULL] = { .nuops = 1, .uops = { { _PUSH_NULL, 0, 0 } } },
- [RESUME_CHECK] = { .nuops = 1, .uops = { { _RESUME_CHECK, 0, 0 } } },
- [RETURN_GENERATOR] = { .nuops = 1, .uops = { { _RETURN_GENERATOR, 0, 0 } } },
- [RETURN_VALUE] = { .nuops = 1, .uops = { { _RETURN_VALUE, 0, 0 } } },
- [SEND_GEN] = { .nuops = 3, .uops = { { _CHECK_PEP_523, 0, 0 }, { _SEND_GEN_FRAME, 0, 0 }, { _PUSH_FRAME, 0, 0 } } },
- [SETUP_ANNOTATIONS] = { .nuops = 1, .uops = { { _SETUP_ANNOTATIONS, 0, 0 } } },
- [SET_ADD] = { .nuops = 1, .uops = { { _SET_ADD, 0, 0 } } },
- [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { _SET_FUNCTION_ATTRIBUTE, 0, 0 } } },
- [SET_UPDATE] = { .nuops = 1, .uops = { { _SET_UPDATE, 0, 0 } } },
- [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, 0, 0 } } },
- [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION_AND_LOCK, 2, 1 }, { _GUARD_DORV_NO_DICT, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } },
+ [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, OPARG_SIMPLE, 3 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, 4, 5 } } },
+ [LOAD_ATTR_PROPERTY] = { .nuops = 5, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_PROPERTY_FRAME, 4, 5 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 9 }, { _PUSH_FRAME, OPARG_SIMPLE, 9 } } },
+ [LOAD_ATTR_SLOT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
+ [LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_WITH_HINT, 1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 9 } } },
+ [LOAD_BUILD_CLASS] = { .nuops = 1, .uops = { { _LOAD_BUILD_CLASS, OPARG_SIMPLE, 0 } } },
+ [LOAD_COMMON_CONSTANT] = { .nuops = 1, .uops = { { _LOAD_COMMON_CONSTANT, OPARG_SIMPLE, 0 } } },
+ [LOAD_CONST_IMMORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_IMMORTAL, OPARG_SIMPLE, 0 } } },
+ [LOAD_CONST_MORTAL] = { .nuops = 1, .uops = { { _LOAD_CONST_MORTAL, OPARG_SIMPLE, 0 } } },
+ [LOAD_DEREF] = { .nuops = 1, .uops = { { _LOAD_DEREF, OPARG_SIMPLE, 0 } } },
+ [LOAD_FAST] = { .nuops = 1, .uops = { { _LOAD_FAST, OPARG_SIMPLE, 0 } } },
+ [LOAD_FAST_AND_CLEAR] = { .nuops = 1, .uops = { { _LOAD_FAST_AND_CLEAR, OPARG_SIMPLE, 0 } } },
+ [LOAD_FAST_CHECK] = { .nuops = 1, .uops = { { _LOAD_FAST_CHECK, OPARG_SIMPLE, 0 } } },
+ [LOAD_FAST_LOAD_FAST] = { .nuops = 2, .uops = { { _LOAD_FAST, OPARG_TOP, 0 }, { _LOAD_FAST, OPARG_BOTTOM, 0 } } },
+ [LOAD_FROM_DICT_OR_DEREF] = { .nuops = 1, .uops = { { _LOAD_FROM_DICT_OR_DEREF, OPARG_SIMPLE, 0 } } },
+ [LOAD_GLOBAL] = { .nuops = 2, .uops = { { _LOAD_GLOBAL, OPARG_SIMPLE, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 3 } } },
+ [LOAD_GLOBAL_BUILTIN] = { .nuops = 4, .uops = { { _GUARD_GLOBALS_VERSION, 1, 1 }, { _LOAD_GLOBAL_BUILTINS, 1, 2 }, { _LOAD_GLOBAL_BUILTINS, OPERAND1_1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 4 } } },
+ [LOAD_GLOBAL_MODULE] = { .nuops = 4, .uops = { { _NOP, OPARG_SIMPLE, 1 }, { _LOAD_GLOBAL_MODULE, 1, 1 }, { _LOAD_GLOBAL_MODULE, OPERAND1_1, 3 }, { _PUSH_NULL_CONDITIONAL, OPARG_SIMPLE, 4 } } },
+ [LOAD_LOCALS] = { .nuops = 1, .uops = { { _LOAD_LOCALS, OPARG_SIMPLE, 0 } } },
+ [LOAD_NAME] = { .nuops = 1, .uops = { { _LOAD_NAME, OPARG_SIMPLE, 0 } } },
+ [LOAD_SMALL_INT] = { .nuops = 1, .uops = { { _LOAD_SMALL_INT, OPARG_SIMPLE, 0 } } },
+ [LOAD_SPECIAL] = { .nuops = 1, .uops = { { _LOAD_SPECIAL, OPARG_SIMPLE, 0 } } },
+ [LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_ATTR, OPARG_SIMPLE, 1 } } },
+ [LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_METHOD, OPARG_SIMPLE, 1 } } },
+ [MAKE_CELL] = { .nuops = 1, .uops = { { _MAKE_CELL, OPARG_SIMPLE, 0 } } },
+ [MAKE_FUNCTION] = { .nuops = 1, .uops = { { _MAKE_FUNCTION, OPARG_SIMPLE, 0 } } },
+ [MAP_ADD] = { .nuops = 1, .uops = { { _MAP_ADD, OPARG_SIMPLE, 0 } } },
+ [MATCH_CLASS] = { .nuops = 1, .uops = { { _MATCH_CLASS, OPARG_SIMPLE, 0 } } },
+ [MATCH_KEYS] = { .nuops = 1, .uops = { { _MATCH_KEYS, OPARG_SIMPLE, 0 } } },
+ [MATCH_MAPPING] = { .nuops = 1, .uops = { { _MATCH_MAPPING, OPARG_SIMPLE, 0 } } },
+ [MATCH_SEQUENCE] = { .nuops = 1, .uops = { { _MATCH_SEQUENCE, OPARG_SIMPLE, 0 } } },
+ [NOP] = { .nuops = 1, .uops = { { _NOP, OPARG_SIMPLE, 0 } } },
+ [NOT_TAKEN] = { .nuops = 1, .uops = { { _NOP, OPARG_SIMPLE, 0 } } },
+ [POP_EXCEPT] = { .nuops = 1, .uops = { { _POP_EXCEPT, OPARG_SIMPLE, 0 } } },
+ [POP_ITER] = { .nuops = 1, .uops = { { _POP_TOP, OPARG_SIMPLE, 0 } } },
+ [POP_JUMP_IF_FALSE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_FALSE, OPARG_REPLACED, 1 } } },
+ [POP_JUMP_IF_NONE] = { .nuops = 2, .uops = { { _IS_NONE, OPARG_SIMPLE, 1 }, { _POP_JUMP_IF_TRUE, OPARG_REPLACED, 1 } } },
+ [POP_JUMP_IF_NOT_NONE] = { .nuops = 2, .uops = { { _IS_NONE, OPARG_SIMPLE, 1 }, { _POP_JUMP_IF_FALSE, OPARG_REPLACED, 1 } } },
+ [POP_JUMP_IF_TRUE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_TRUE, OPARG_REPLACED, 1 } } },
+ [POP_TOP] = { .nuops = 1, .uops = { { _POP_TOP, OPARG_SIMPLE, 0 } } },
+ [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { _PUSH_EXC_INFO, OPARG_SIMPLE, 0 } } },
+ [PUSH_NULL] = { .nuops = 1, .uops = { { _PUSH_NULL, OPARG_SIMPLE, 0 } } },
+ [RESUME_CHECK] = { .nuops = 1, .uops = { { _RESUME_CHECK, OPARG_SIMPLE, 0 } } },
+ [RETURN_GENERATOR] = { .nuops = 1, .uops = { { _RETURN_GENERATOR, OPARG_SIMPLE, 0 } } },
+ [RETURN_VALUE] = { .nuops = 1, .uops = { { _RETURN_VALUE, OPARG_SIMPLE, 0 } } },
+ [SEND_GEN] = { .nuops = 3, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _SEND_GEN_FRAME, OPARG_SIMPLE, 1 }, { _PUSH_FRAME, OPARG_SIMPLE, 1 } } },
+ [SETUP_ANNOTATIONS] = { .nuops = 1, .uops = { { _SETUP_ANNOTATIONS, OPARG_SIMPLE, 0 } } },
+ [SET_ADD] = { .nuops = 1, .uops = { { _SET_ADD, OPARG_SIMPLE, 0 } } },
+ [SET_FUNCTION_ATTRIBUTE] = { .nuops = 1, .uops = { { _SET_FUNCTION_ATTRIBUTE, OPARG_SIMPLE, 0 } } },
+ [SET_UPDATE] = { .nuops = 1, .uops = { { _SET_UPDATE, OPARG_SIMPLE, 0 } } },
+ [STORE_ATTR] = { .nuops = 1, .uops = { { _STORE_ATTR, OPARG_SIMPLE, 3 } } },
+ [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION_AND_LOCK, 2, 1 }, { _GUARD_DORV_NO_DICT, OPARG_SIMPLE, 3 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } },
[STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } },
[STORE_ATTR_WITH_HINT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_WITH_HINT, 1, 3 } } },
- [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, 0, 0 } } },
- [STORE_FAST] = { .nuops = 1, .uops = { { _STORE_FAST, 0, 0 } } },
- [STORE_FAST_LOAD_FAST] = { .nuops = 2, .uops = { { _STORE_FAST, 5, 0 }, { _LOAD_FAST, 6, 0 } } },
- [STORE_FAST_STORE_FAST] = { .nuops = 2, .uops = { { _STORE_FAST, 5, 0 }, { _STORE_FAST, 6, 0 } } },
- [STORE_GLOBAL] = { .nuops = 1, .uops = { { _STORE_GLOBAL, 0, 0 } } },
- [STORE_NAME] = { .nuops = 1, .uops = { { _STORE_NAME, 0, 0 } } },
- [STORE_SLICE] = { .nuops = 1, .uops = { { _STORE_SLICE, 0, 0 } } },
- [STORE_SUBSCR] = { .nuops = 1, .uops = { { _STORE_SUBSCR, 0, 0 } } },
- [STORE_SUBSCR_DICT] = { .nuops = 1, .uops = { { _STORE_SUBSCR_DICT, 0, 0 } } },
- [STORE_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _STORE_SUBSCR_LIST_INT, 0, 0 } } },
- [SWAP] = { .nuops = 1, .uops = { { _SWAP, 0, 0 } } },
- [TO_BOOL] = { .nuops = 1, .uops = { { _TO_BOOL, 0, 0 } } },
- [TO_BOOL_ALWAYS_TRUE] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _REPLACE_WITH_TRUE, 0, 0 } } },
- [TO_BOOL_BOOL] = { .nuops = 1, .uops = { { _TO_BOOL_BOOL, 0, 0 } } },
- [TO_BOOL_INT] = { .nuops = 1, .uops = { { _TO_BOOL_INT, 0, 0 } } },
- [TO_BOOL_LIST] = { .nuops = 1, .uops = { { _TO_BOOL_LIST, 0, 0 } } },
- [TO_BOOL_NONE] = { .nuops = 1, .uops = { { _TO_BOOL_NONE, 0, 0 } } },
- [TO_BOOL_STR] = { .nuops = 1, .uops = { { _TO_BOOL_STR, 0, 0 } } },
- [UNARY_INVERT] = { .nuops = 1, .uops = { { _UNARY_INVERT, 0, 0 } } },
- [UNARY_NEGATIVE] = { .nuops = 1, .uops = { { _UNARY_NEGATIVE, 0, 0 } } },
- [UNARY_NOT] = { .nuops = 1, .uops = { { _UNARY_NOT, 0, 0 } } },
- [UNPACK_EX] = { .nuops = 1, .uops = { { _UNPACK_EX, 0, 0 } } },
- [UNPACK_SEQUENCE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE, 0, 0 } } },
- [UNPACK_SEQUENCE_LIST] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_LIST, 0, 0 } } },
- [UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TUPLE, 0, 0 } } },
- [UNPACK_SEQUENCE_TWO_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TWO_TUPLE, 0, 0 } } },
- [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { _WITH_EXCEPT_START, 0, 0 } } },
- [YIELD_VALUE] = { .nuops = 1, .uops = { { _YIELD_VALUE, 0, 0 } } },
+ [STORE_DEREF] = { .nuops = 1, .uops = { { _STORE_DEREF, OPARG_SIMPLE, 0 } } },
+ [STORE_FAST] = { .nuops = 1, .uops = { { _STORE_FAST, OPARG_SIMPLE, 0 } } },
+ [STORE_FAST_LOAD_FAST] = { .nuops = 2, .uops = { { _STORE_FAST, OPARG_TOP, 0 }, { _LOAD_FAST, OPARG_BOTTOM, 0 } } },
+ [STORE_FAST_STORE_FAST] = { .nuops = 2, .uops = { { _STORE_FAST, OPARG_TOP, 0 }, { _STORE_FAST, OPARG_BOTTOM, 0 } } },
+ [STORE_GLOBAL] = { .nuops = 1, .uops = { { _STORE_GLOBAL, OPARG_SIMPLE, 0 } } },
+ [STORE_NAME] = { .nuops = 1, .uops = { { _STORE_NAME, OPARG_SIMPLE, 0 } } },
+ [STORE_SLICE] = { .nuops = 1, .uops = { { _STORE_SLICE, OPARG_SIMPLE, 0 } } },
+ [STORE_SUBSCR] = { .nuops = 1, .uops = { { _STORE_SUBSCR, OPARG_SIMPLE, 0 } } },
+ [STORE_SUBSCR_DICT] = { .nuops = 1, .uops = { { _STORE_SUBSCR_DICT, OPARG_SIMPLE, 1 } } },
+ [STORE_SUBSCR_LIST_INT] = { .nuops = 1, .uops = { { _STORE_SUBSCR_LIST_INT, OPARG_SIMPLE, 1 } } },
+ [SWAP] = { .nuops = 1, .uops = { { _SWAP, OPARG_SIMPLE, 0 } } },
+ [TO_BOOL] = { .nuops = 1, .uops = { { _TO_BOOL, OPARG_SIMPLE, 2 } } },
+ [TO_BOOL_ALWAYS_TRUE] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _REPLACE_WITH_TRUE, OPARG_SIMPLE, 3 } } },
+ [TO_BOOL_BOOL] = { .nuops = 1, .uops = { { _TO_BOOL_BOOL, OPARG_SIMPLE, 3 } } },
+ [TO_BOOL_INT] = { .nuops = 1, .uops = { { _TO_BOOL_INT, OPARG_SIMPLE, 3 } } },
+ [TO_BOOL_LIST] = { .nuops = 1, .uops = { { _TO_BOOL_LIST, OPARG_SIMPLE, 3 } } },
+ [TO_BOOL_NONE] = { .nuops = 1, .uops = { { _TO_BOOL_NONE, OPARG_SIMPLE, 3 } } },
+ [TO_BOOL_STR] = { .nuops = 1, .uops = { { _TO_BOOL_STR, OPARG_SIMPLE, 3 } } },
+ [UNARY_INVERT] = { .nuops = 1, .uops = { { _UNARY_INVERT, OPARG_SIMPLE, 0 } } },
+ [UNARY_NEGATIVE] = { .nuops = 1, .uops = { { _UNARY_NEGATIVE, OPARG_SIMPLE, 0 } } },
+ [UNARY_NOT] = { .nuops = 1, .uops = { { _UNARY_NOT, OPARG_SIMPLE, 0 } } },
+ [UNPACK_EX] = { .nuops = 1, .uops = { { _UNPACK_EX, OPARG_SIMPLE, 0 } } },
+ [UNPACK_SEQUENCE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE, OPARG_SIMPLE, 0 } } },
+ [UNPACK_SEQUENCE_LIST] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_LIST, OPARG_SIMPLE, 1 } } },
+ [UNPACK_SEQUENCE_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TUPLE, OPARG_SIMPLE, 1 } } },
+ [UNPACK_SEQUENCE_TWO_TUPLE] = { .nuops = 1, .uops = { { _UNPACK_SEQUENCE_TWO_TUPLE, OPARG_SIMPLE, 1 } } },
+ [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { _WITH_EXCEPT_START, OPARG_SIMPLE, 0 } } },
+ [YIELD_VALUE] = { .nuops = 1, .uops = { { _YIELD_VALUE, OPARG_SIMPLE, 0 } } },
};
#endif // NEED_OPCODE_METADATA
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index 4e04dd69542648..5143b10def5396 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -55,101 +55,97 @@ extern "C" {
#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_CALL_BOUND_METHOD_EXACT_ARGS 330
#define _CHECK_EG_MATCH CHECK_EG_MATCH
#define _CHECK_EXC_MATCH CHECK_EXC_MATCH
-#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 _CHECK_FUNCTION 331
+#define _CHECK_FUNCTION_EXACT_ARGS 332
+#define _CHECK_FUNCTION_VERSION 333
+#define _CHECK_FUNCTION_VERSION_INLINE 334
+#define _CHECK_FUNCTION_VERSION_KW 335
+#define _CHECK_IS_NOT_PY_CALLABLE 336
+#define _CHECK_IS_NOT_PY_CALLABLE_KW 337
+#define _CHECK_MANAGED_OBJECT_HAS_VALUES 338
+#define _CHECK_METHOD_VERSION 339
+#define _CHECK_METHOD_VERSION_KW 340
+#define _CHECK_PEP_523 341
+#define _CHECK_PERIODIC 342
+#define _CHECK_PERIODIC_IF_NOT_YIELD_FROM 343
+#define _CHECK_STACK_SPACE 344
+#define _CHECK_STACK_SPACE_OPERAND 345
+#define _CHECK_VALIDITY 346
+#define _CHECK_VALIDITY_AND_SET_IP 347
+#define _COMPARE_OP 348
+#define _COMPARE_OP_FLOAT 349
+#define _COMPARE_OP_INT 350
+#define _COMPARE_OP_STR 351
+#define _CONTAINS_OP 352
#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 355
+#define _CREATE_INIT_FRAME 353
#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 356
+#define _DEOPT 354
#define _DICT_MERGE DICT_MERGE
#define _DICT_UPDATE DICT_UPDATE
-#define _DO_CALL 357
-#define _DO_CALL_FUNCTION_EX 358
-#define _DO_CALL_KW 359
+#define _DO_CALL 355
+#define _DO_CALL_FUNCTION_EX 356
+#define _DO_CALL_KW 357
#define _END_FOR END_FOR
#define _END_SEND END_SEND
-#define _ERROR_POP_N 360
+#define _ERROR_POP_N 358
#define _EXIT_INIT_CHECK EXIT_INIT_CHECK
-#define _EXPAND_METHOD 361
-#define _EXPAND_METHOD_KW 362
-#define _FATAL_ERROR 363
+#define _EXPAND_METHOD 359
+#define _EXPAND_METHOD_KW 360
+#define _FATAL_ERROR 361
#define _FORMAT_SIMPLE FORMAT_SIMPLE
#define _FORMAT_WITH_SPEC FORMAT_WITH_SPEC
-#define _FOR_ITER 364
-#define _FOR_ITER_GEN_FRAME 365
-#define _FOR_ITER_TIER_TWO 366
+#define _FOR_ITER 362
+#define _FOR_ITER_GEN_FRAME 363
+#define _FOR_ITER_TIER_TWO 364
#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 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 _GUARD_BINARY_OP_EXTEND 365
+#define _GUARD_BOTH_FLOAT 366
+#define _GUARD_BOTH_INT 367
+#define _GUARD_BOTH_UNICODE 368
+#define _GUARD_DORV_NO_DICT 369
+#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 370
+#define _GUARD_GLOBALS_VERSION 371
+#define _GUARD_IS_FALSE_POP 372
+#define _GUARD_IS_NONE_POP 373
+#define _GUARD_IS_NOT_NONE_POP 374
+#define _GUARD_IS_TRUE_POP 375
+#define _GUARD_KEYS_VERSION 376
+#define _GUARD_NOS_FLOAT 377
+#define _GUARD_NOS_INT 378
+#define _GUARD_NOT_EXHAUSTED_LIST 379
+#define _GUARD_NOT_EXHAUSTED_RANGE 380
+#define _GUARD_NOT_EXHAUSTED_TUPLE 381
+#define _GUARD_TOS_FLOAT 382
+#define _GUARD_TOS_INT 383
+#define _GUARD_TYPE_VERSION 384
+#define _GUARD_TYPE_VERSION_AND_LOCK 385
#define _IMPORT_FROM IMPORT_FROM
#define _IMPORT_NAME IMPORT_NAME
-#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 _INIT_CALL_BOUND_METHOD_EXACT_ARGS 386
+#define _INIT_CALL_PY_EXACT_ARGS 387
+#define _INIT_CALL_PY_EXACT_ARGS_0 388
+#define _INIT_CALL_PY_EXACT_ARGS_1 389
+#define _INIT_CALL_PY_EXACT_ARGS_2 390
+#define _INIT_CALL_PY_EXACT_ARGS_3 391
+#define _INIT_CALL_PY_EXACT_ARGS_4 392
#define _INSTRUMENTED_FOR_ITER INSTRUMENTED_FOR_ITER
#define _INSTRUMENTED_INSTRUCTION INSTRUMENTED_INSTRUCTION
#define _INSTRUMENTED_JUMP_FORWARD INSTRUMENTED_JUMP_FORWARD
@@ -159,137 +155,135 @@ 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 397
+#define _IS_NONE 393
#define _IS_OP IS_OP
-#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 _ITER_CHECK_LIST 394
+#define _ITER_CHECK_RANGE 395
+#define _ITER_CHECK_TUPLE 396
+#define _ITER_JUMP_LIST 397
+#define _ITER_JUMP_RANGE 398
+#define _ITER_JUMP_TUPLE 399
+#define _ITER_NEXT_LIST 400
+#define _ITER_NEXT_RANGE 401
+#define _ITER_NEXT_TUPLE 402
+#define _JUMP_TO_TOP 403
#define _LIST_APPEND LIST_APPEND
#define _LIST_EXTEND LIST_EXTEND
-#define _LOAD_ATTR 408
-#define _LOAD_ATTR_CLASS 409
+#define _LOAD_ATTR 404
+#define _LOAD_ATTR_CLASS 405
#define _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
-#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_ATTR_INSTANCE_VALUE 406
+#define _LOAD_ATTR_METHOD_LAZY_DICT 407
+#define _LOAD_ATTR_METHOD_NO_DICT 408
+#define _LOAD_ATTR_METHOD_WITH_VALUES 409
+#define _LOAD_ATTR_MODULE 410
+#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 411
+#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 412
+#define _LOAD_ATTR_PROPERTY_FRAME 413
+#define _LOAD_ATTR_SLOT 414
+#define _LOAD_ATTR_WITH_HINT 415
#define _LOAD_BUILD_CLASS LOAD_BUILD_CLASS
-#define _LOAD_BYTECODE 421
+#define _LOAD_BYTECODE 416
#define _LOAD_COMMON_CONSTANT LOAD_COMMON_CONSTANT
#define _LOAD_CONST LOAD_CONST
#define _LOAD_CONST_IMMORTAL LOAD_CONST_IMMORTAL
-#define _LOAD_CONST_INLINE 422
-#define _LOAD_CONST_INLINE_BORROW 423
+#define _LOAD_CONST_INLINE 417
+#define _LOAD_CONST_INLINE_BORROW 418
#define _LOAD_CONST_MORTAL LOAD_CONST_MORTAL
#define _LOAD_DEREF LOAD_DEREF
-#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 419
+#define _LOAD_FAST_0 420
+#define _LOAD_FAST_1 421
+#define _LOAD_FAST_2 422
+#define _LOAD_FAST_3 423
+#define _LOAD_FAST_4 424
+#define _LOAD_FAST_5 425
+#define _LOAD_FAST_6 426
+#define _LOAD_FAST_7 427
#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 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_GLOBAL 428
+#define _LOAD_GLOBAL_BUILTINS 429
+#define _LOAD_GLOBAL_MODULE 430
#define _LOAD_LOCALS LOAD_LOCALS
#define _LOAD_NAME LOAD_NAME
-#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_SMALL_INT 431
+#define _LOAD_SMALL_INT_0 432
+#define _LOAD_SMALL_INT_1 433
+#define _LOAD_SMALL_INT_2 434
+#define _LOAD_SMALL_INT_3 435
#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 443
+#define _MAKE_CALLARGS_A_TUPLE 436
#define _MAKE_CELL MAKE_CELL
#define _MAKE_FUNCTION MAKE_FUNCTION
-#define _MAKE_WARM 444
+#define _MAKE_WARM 437
#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 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 _MAYBE_EXPAND_METHOD 438
+#define _MAYBE_EXPAND_METHOD_KW 439
+#define _MONITOR_CALL 440
+#define _MONITOR_CALL_KW 441
+#define _MONITOR_JUMP_BACKWARD 442
+#define _MONITOR_RESUME 443
#define _NOP NOP
#define _POP_EXCEPT POP_EXCEPT
-#define _POP_JUMP_IF_FALSE 451
-#define _POP_JUMP_IF_TRUE 452
+#define _POP_JUMP_IF_FALSE 444
+#define _POP_JUMP_IF_TRUE 445
#define _POP_TOP POP_TOP
-#define _POP_TOP_LOAD_CONST_INLINE_BORROW 453
+#define _POP_TOP_LOAD_CONST_INLINE 446
+#define _POP_TOP_LOAD_CONST_INLINE_BORROW 447
#define _PUSH_EXC_INFO PUSH_EXC_INFO
-#define _PUSH_FRAME 454
+#define _PUSH_FRAME 448
#define _PUSH_NULL PUSH_NULL
-#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 _PUSH_NULL_CONDITIONAL 449
+#define _PY_FRAME_GENERAL 450
+#define _PY_FRAME_KW 451
+#define _QUICKEN_RESUME 452
+#define _REPLACE_WITH_TRUE 453
#define _RESUME_CHECK RESUME_CHECK
#define _RETURN_GENERATOR RETURN_GENERATOR
#define _RETURN_VALUE RETURN_VALUE
-#define _SAVE_RETURN_OFFSET 460
-#define _SEND 461
-#define _SEND_GEN_FRAME 462
+#define _SAVE_RETURN_OFFSET 454
+#define _SEND 455
+#define _SEND_GEN_FRAME 456
#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 463
-#define _STORE_ATTR 464
-#define _STORE_ATTR_INSTANCE_VALUE 465
-#define _STORE_ATTR_SLOT 466
-#define _STORE_ATTR_WITH_HINT 467
+#define _START_EXECUTOR 457
+#define _STORE_ATTR 458
+#define _STORE_ATTR_INSTANCE_VALUE 459
+#define _STORE_ATTR_SLOT 460
+#define _STORE_ATTR_WITH_HINT 461
#define _STORE_DEREF STORE_DEREF
-#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 462
+#define _STORE_FAST_0 463
+#define _STORE_FAST_1 464
+#define _STORE_FAST_2 465
+#define _STORE_FAST_3 466
+#define _STORE_FAST_4 467
+#define _STORE_FAST_5 468
+#define _STORE_FAST_6 469
+#define _STORE_FAST_7 470
#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 477
-#define _STORE_SUBSCR 478
+#define _STORE_SLICE 471
+#define _STORE_SUBSCR 472
#define _STORE_SUBSCR_DICT STORE_SUBSCR_DICT
#define _STORE_SUBSCR_LIST_INT STORE_SUBSCR_LIST_INT
#define _SWAP SWAP
-#define _TIER2_RESUME_CHECK 479
-#define _TO_BOOL 480
+#define _TIER2_RESUME_CHECK 473
+#define _TO_BOOL 474
#define _TO_BOOL_BOOL TO_BOOL_BOOL
#define _TO_BOOL_INT TO_BOOL_INT
#define _TO_BOOL_LIST TO_BOOL_LIST
@@ -299,13 +293,13 @@ extern "C" {
#define _UNARY_NEGATIVE UNARY_NEGATIVE
#define _UNARY_NOT UNARY_NOT
#define _UNPACK_EX UNPACK_EX
-#define _UNPACK_SEQUENCE 481
+#define _UNPACK_SEQUENCE 475
#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 481
+#define MAX_UOP_ID 475
#ifdef __cplusplus
}
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 1a1fd328195b46..0013540c496938 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -125,10 +125,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_LOAD_GLOBAL] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_PUSH_NULL_CONDITIONAL] = HAS_ARG_FLAG,
[_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG,
- [_GUARD_GLOBALS_VERSION_PUSH_KEYS] = HAS_DEOPT_FLAG,
- [_GUARD_BUILTINS_VERSION_PUSH_KEYS] = HAS_DEOPT_FLAG,
- [_LOAD_GLOBAL_MODULE_FROM_KEYS] = HAS_DEOPT_FLAG,
- [_LOAD_GLOBAL_BUILTINS_FROM_KEYS] = HAS_DEOPT_FLAG,
+ [_LOAD_GLOBAL_MODULE] = HAS_DEOPT_FLAG,
+ [_LOAD_GLOBAL_BUILTINS] = HAS_DEOPT_FLAG,
[_DELETE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
[_MAKE_CELL] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
[_DELETE_DEREF] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
@@ -154,10 +152,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_GUARD_TYPE_VERSION_AND_LOCK] = HAS_EXIT_FLAG,
[_CHECK_MANAGED_OBJECT_HAS_VALUES] = 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,
- [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG,
+ [_LOAD_ATTR_MODULE] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
+ [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
[_LOAD_ATTR_SLOT] = HAS_DEOPT_FLAG,
[_CHECK_ATTR_CLASS] = HAS_EXIT_FLAG,
[_LOAD_ATTR_CLASS] = 0,
@@ -274,12 +270,10 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_EXIT_TRACE] = HAS_ESCAPES_FLAG,
[_CHECK_VALIDITY] = HAS_DEOPT_FLAG,
[_LOAD_CONST_INLINE] = HAS_PURE_FLAG,
+ [_POP_TOP_LOAD_CONST_INLINE] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
[_LOAD_CONST_INLINE_BORROW] = HAS_PURE_FLAG,
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
[_CHECK_FUNCTION] = HAS_DEOPT_FLAG,
- [_LOAD_GLOBAL_MODULE] = HAS_DEOPT_FLAG,
- [_LOAD_GLOBAL_BUILTINS] = HAS_DEOPT_FLAG,
- [_LOAD_ATTR_MODULE] = HAS_DEOPT_FLAG,
[_START_EXECUTOR] = HAS_ESCAPES_FLAG,
[_MAKE_WARM] = 0,
[_FATAL_ERROR] = 0,
@@ -341,8 +335,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_CHECK_AND_ALLOCATE_OBJECT] = "_CHECK_AND_ALLOCATE_OBJECT",
[_CHECK_ATTR_CLASS] = "_CHECK_ATTR_CLASS",
[_CHECK_ATTR_METHOD_LAZY_DICT] = "_CHECK_ATTR_METHOD_LAZY_DICT",
- [_CHECK_ATTR_MODULE_PUSH_KEYS] = "_CHECK_ATTR_MODULE_PUSH_KEYS",
- [_CHECK_ATTR_WITH_HINT] = "_CHECK_ATTR_WITH_HINT",
[_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = "_CHECK_CALL_BOUND_METHOD_EXACT_ARGS",
[_CHECK_EG_MATCH] = "_CHECK_EG_MATCH",
[_CHECK_EXC_MATCH] = "_CHECK_EXC_MATCH",
@@ -405,11 +397,9 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_GUARD_BOTH_FLOAT] = "_GUARD_BOTH_FLOAT",
[_GUARD_BOTH_INT] = "_GUARD_BOTH_INT",
[_GUARD_BOTH_UNICODE] = "_GUARD_BOTH_UNICODE",
- [_GUARD_BUILTINS_VERSION_PUSH_KEYS] = "_GUARD_BUILTINS_VERSION_PUSH_KEYS",
[_GUARD_DORV_NO_DICT] = "_GUARD_DORV_NO_DICT",
[_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = "_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT",
[_GUARD_GLOBALS_VERSION] = "_GUARD_GLOBALS_VERSION",
- [_GUARD_GLOBALS_VERSION_PUSH_KEYS] = "_GUARD_GLOBALS_VERSION_PUSH_KEYS",
[_GUARD_IS_FALSE_POP] = "_GUARD_IS_FALSE_POP",
[_GUARD_IS_NONE_POP] = "_GUARD_IS_NONE_POP",
[_GUARD_IS_NOT_NONE_POP] = "_GUARD_IS_NOT_NONE_POP",
@@ -451,7 +441,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_LOAD_ATTR_METHOD_NO_DICT] = "_LOAD_ATTR_METHOD_NO_DICT",
[_LOAD_ATTR_METHOD_WITH_VALUES] = "_LOAD_ATTR_METHOD_WITH_VALUES",
[_LOAD_ATTR_MODULE] = "_LOAD_ATTR_MODULE",
- [_LOAD_ATTR_MODULE_FROM_KEYS] = "_LOAD_ATTR_MODULE_FROM_KEYS",
[_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = "_LOAD_ATTR_NONDESCRIPTOR_NO_DICT",
[_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = "_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES",
[_LOAD_ATTR_PROPERTY_FRAME] = "_LOAD_ATTR_PROPERTY_FRAME",
@@ -479,9 +468,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_LOAD_FROM_DICT_OR_DEREF] = "_LOAD_FROM_DICT_OR_DEREF",
[_LOAD_GLOBAL] = "_LOAD_GLOBAL",
[_LOAD_GLOBAL_BUILTINS] = "_LOAD_GLOBAL_BUILTINS",
- [_LOAD_GLOBAL_BUILTINS_FROM_KEYS] = "_LOAD_GLOBAL_BUILTINS_FROM_KEYS",
[_LOAD_GLOBAL_MODULE] = "_LOAD_GLOBAL_MODULE",
- [_LOAD_GLOBAL_MODULE_FROM_KEYS] = "_LOAD_GLOBAL_MODULE_FROM_KEYS",
[_LOAD_LOCALS] = "_LOAD_LOCALS",
[_LOAD_NAME] = "_LOAD_NAME",
[_LOAD_SMALL_INT] = "_LOAD_SMALL_INT",
@@ -506,6 +493,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
[_NOP] = "_NOP",
[_POP_EXCEPT] = "_POP_EXCEPT",
[_POP_TOP] = "_POP_TOP",
+ [_POP_TOP_LOAD_CONST_INLINE] = "_POP_TOP_LOAD_CONST_INLINE",
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = "_POP_TOP_LOAD_CONST_INLINE_BORROW",
[_PUSH_EXC_INFO] = "_PUSH_EXC_INFO",
[_PUSH_FRAME] = "_PUSH_FRAME",
@@ -781,14 +769,10 @@ int _PyUop_num_popped(int opcode, int oparg)
return 0;
case _GUARD_GLOBALS_VERSION:
return 0;
- case _GUARD_GLOBALS_VERSION_PUSH_KEYS:
+ case _LOAD_GLOBAL_MODULE:
return 0;
- case _GUARD_BUILTINS_VERSION_PUSH_KEYS:
+ case _LOAD_GLOBAL_BUILTINS:
return 0;
- case _LOAD_GLOBAL_MODULE_FROM_KEYS:
- return 1;
- case _LOAD_GLOBAL_BUILTINS_FROM_KEYS:
- return 1;
case _DELETE_FAST:
return 0;
case _MAKE_CELL:
@@ -839,14 +823,10 @@ int _PyUop_num_popped(int opcode, int oparg)
return 0;
case _LOAD_ATTR_INSTANCE_VALUE:
return 1;
- case _CHECK_ATTR_MODULE_PUSH_KEYS:
- return 0;
- case _LOAD_ATTR_MODULE_FROM_KEYS:
- return 2;
- case _CHECK_ATTR_WITH_HINT:
- return 0;
+ case _LOAD_ATTR_MODULE:
+ return 1;
case _LOAD_ATTR_WITH_HINT:
- return 2;
+ return 1;
case _LOAD_ATTR_SLOT:
return 1;
case _CHECK_ATTR_CLASS:
@@ -1079,18 +1059,14 @@ int _PyUop_num_popped(int opcode, int oparg)
return 0;
case _LOAD_CONST_INLINE:
return 0;
+ case _POP_TOP_LOAD_CONST_INLINE:
+ return 1;
case _LOAD_CONST_INLINE_BORROW:
return 0;
case _POP_TOP_LOAD_CONST_INLINE_BORROW:
return 1;
case _CHECK_FUNCTION:
return 0;
- case _LOAD_GLOBAL_MODULE:
- return 0;
- case _LOAD_GLOBAL_BUILTINS:
- return 0;
- case _LOAD_ATTR_MODULE:
- return 1;
case _START_EXECUTOR:
return 0;
case _MAKE_WARM:
diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py
index ff6b566faa4a23..32d14548159cce 100644
--- a/Lib/test/test_generated_cases.py
+++ b/Lib/test/test_generated_cases.py
@@ -1931,55 +1931,6 @@ def test_pystackref_frompyobject_new_next_to_cmacro(self):
"""
self.run_cases_test(input, output)
- def test_pop_input(self):
- input = """
- inst(OP, (a, b --)) {
- POP_INPUT(b);
- HAM(a);
- INPUTS_DEAD();
- }
- """
- output = """
- TARGET(OP) {
- #if Py_TAIL_CALL_INTERP
- int opcode = OP;
- (void)(opcode);
- #endif
- frame->instr_ptr = next_instr;
- next_instr += 1;
- INSTRUCTION_STATS(OP);
- _PyStackRef a;
- _PyStackRef b;
- b = stack_pointer[-1];
- a = stack_pointer[-2];
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
- HAM(a);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
- DISPATCH();
- }
- """
- self.run_cases_test(input, output)
-
- def test_pop_input_with_empty_stack(self):
- input = """
- inst(OP, (--)) {
- POP_INPUT(foo);
- }
- """
- with self.assertRaises(SyntaxError):
- self.run_cases_test(input, "")
-
- def test_pop_input_with_non_tos(self):
- input = """
- inst(OP, (a, b --)) {
- POP_INPUT(a);
- }
- """
- with self.assertRaises(SyntaxError):
- self.run_cases_test(input, "")
-
def test_no_escaping_calls_in_branching_macros(self):
input = """
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 1b9b651fd01539..24aa7bbb87c193 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1689,70 +1689,55 @@ dummy_func(
assert(DK_IS_UNICODE(keys));
}
- op(_GUARD_GLOBALS_VERSION_PUSH_KEYS, (version / 1 -- globals_keys: PyDictKeysObject *))
+ op(_LOAD_GLOBAL_MODULE, (version/1, unused/1, index/1 -- res))
{
PyDictObject *dict = (PyDictObject *)GLOBALS();
DEOPT_IF(!PyDict_CheckExact(dict));
PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version);
- globals_keys = keys;
- assert(DK_IS_UNICODE(globals_keys));
- }
-
- op(_GUARD_BUILTINS_VERSION_PUSH_KEYS, (version / 1 -- builtins_keys: PyDictKeysObject *))
- {
- PyDictObject *dict = (PyDictObject *)BUILTINS();
- DEOPT_IF(!PyDict_CheckExact(dict));
- PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
- DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version);
- builtins_keys = keys;
- assert(DK_IS_UNICODE(builtins_keys));
- }
-
- op(_LOAD_GLOBAL_MODULE_FROM_KEYS, (index/1, globals_keys: PyDictKeysObject* -- res)) {
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(globals_keys);
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
+ assert(index < DK_SIZE(keys));
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
- DEAD(globals_keys);
- SYNC_SP();
DEOPT_IF(res_o == NULL);
#if Py_GIL_DISABLED
int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res);
DEOPT_IF(!increfed);
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
}
- op(_LOAD_GLOBAL_BUILTINS_FROM_KEYS, (index/1, builtins_keys: PyDictKeysObject* -- res)) {
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(builtins_keys);
+ op(_LOAD_GLOBAL_BUILTINS, (version/1, index/1 -- res))
+ {
+ PyDictObject *dict = (PyDictObject *)BUILTINS();
+ DEOPT_IF(!PyDict_CheckExact(dict));
+ PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
+ DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version);
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
- DEAD(builtins_keys);
- SYNC_SP();
DEOPT_IF(res_o == NULL);
#if Py_GIL_DISABLED
int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res);
DEOPT_IF(!increfed);
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
}
macro(LOAD_GLOBAL_MODULE) =
unused/1 + // Skip over the counter
- _GUARD_GLOBALS_VERSION_PUSH_KEYS +
- unused/1 + // Skip over the builtins version
- _LOAD_GLOBAL_MODULE_FROM_KEYS +
+ NOP + // For guard insertion in the JIT optimizer
+ _LOAD_GLOBAL_MODULE +
_PUSH_NULL_CONDITIONAL;
macro(LOAD_GLOBAL_BUILTIN) =
unused/1 + // Skip over the counter
_GUARD_GLOBALS_VERSION +
- _GUARD_BUILTINS_VERSION_PUSH_KEYS +
- _LOAD_GLOBAL_BUILTINS_FROM_KEYS +
+ _LOAD_GLOBAL_BUILTINS +
_PUSH_NULL_CONDITIONAL;
inst(DELETE_FAST, (--)) {
@@ -2247,23 +2232,17 @@ dummy_func(
unused/5 +
_PUSH_NULL_CONDITIONAL;
- op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys: PyDictKeysObject *)) {
+ op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr)) {
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro);
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);
- mod_keys = keys;
- }
-
- op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys: PyDictKeysObject * -- attr)) {
- assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ assert(keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index;
PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
- // Clear mod_keys from stack in case we need to deopt
- POP_INPUT(mod_keys);
DEOPT_IF(attr_o == NULL);
#ifdef Py_GIL_DISABLED
int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
@@ -2271,8 +2250,7 @@ dummy_func(
DEOPT_IF(true);
}
#else
- Py_INCREF(attr_o);
- attr = PyStackRef_FromPyObjectSteal(attr_o);
+ attr = PyStackRef_FromPyObjectNew(attr_o);
#endif
STAT_INC(LOAD_ATTR, hit);
PyStackRef_CLOSE(owner);
@@ -2280,62 +2258,49 @@ dummy_func(
macro(LOAD_ATTR_MODULE) =
unused/1 +
- _CHECK_ATTR_MODULE_PUSH_KEYS +
- _LOAD_ATTR_MODULE_FROM_KEYS +
+ _LOAD_ATTR_MODULE +
unused/5 +
_PUSH_NULL_CONDITIONAL;
- op(_CHECK_ATTR_WITH_HINT, (owner -- owner, dict: PyDictObject *)) {
+ op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr)) {
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
-
assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o);
- EXIT_IF(dict_o == NULL);
- assert(PyDict_CheckExact((PyObject *)dict_o));
- dict = dict_o;
- }
-
- op(_LOAD_ATTR_WITH_HINT, (hint/1, owner, dict: PyDictObject * -- attr)) {
+ PyDictObject *dict = _PyObject_GetManagedDict(owner_o);
+ DEOPT_IF(dict == NULL);
+ assert(PyDict_CheckExact((PyObject *)dict));
PyObject *attr_o;
if (!LOCK_OBJECT(dict)) {
- POP_INPUT(dict);
DEOPT_IF(true);
}
if (hint >= (size_t)dict->ma_keys->dk_nentries) {
UNLOCK_OBJECT(dict);
- POP_INPUT(dict);
DEOPT_IF(true);
}
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) {
UNLOCK_OBJECT(dict);
- POP_INPUT(dict);
DEOPT_IF(true);
}
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
if (ep->me_key != name) {
UNLOCK_OBJECT(dict);
- POP_INPUT(dict);
DEOPT_IF(true);
}
attr_o = ep->me_value;
if (attr_o == NULL) {
UNLOCK_OBJECT(dict);
- POP_INPUT(dict);
DEOPT_IF(true);
}
STAT_INC(LOAD_ATTR, hit);
attr = PyStackRef_FromPyObjectNew(attr_o);
UNLOCK_OBJECT(dict);
- DEAD(dict);
- DECREF_INPUTS();
+ PyStackRef_CLOSE(owner);
}
macro(LOAD_ATTR_WITH_HINT) =
unused/1 +
_GUARD_TYPE_VERSION +
- _CHECK_ATTR_WITH_HINT +
_LOAD_ATTR_WITH_HINT +
unused/5 +
_PUSH_NULL_CONDITIONAL;
@@ -5045,6 +5010,11 @@ dummy_func(
value = PyStackRef_FromPyObjectNew(ptr);
}
+ tier2 pure op (_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
+ PyStackRef_CLOSE(pop);
+ value = PyStackRef_FromPyObjectNew(ptr);
+ }
+
tier2 pure op(_LOAD_CONST_INLINE_BORROW, (ptr/4 -- value)) {
value = PyStackRef_FromPyObjectImmortal(ptr);
}
@@ -5060,38 +5030,6 @@ dummy_func(
DEOPT_IF(func->func_version != func_version);
}
- tier2 op(_LOAD_GLOBAL_MODULE, (index/1 -- res)) {
- PyDictObject *dict = (PyDictObject *)GLOBALS();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- PyObject *res_o = entries[index].me_value;
- DEOPT_IF(res_o == NULL);
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
- }
-
- tier2 op(_LOAD_GLOBAL_BUILTINS, (index/1 -- res)) {
- PyDictObject *dict = (PyDictObject *)BUILTINS();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- PyObject *res_o = entries[index].me_value;
- DEOPT_IF(res_o == NULL);
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
- }
-
- tier2 op(_LOAD_ATTR_MODULE, (index/1, owner -- attr)) {
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
- PyObject *attr_o = ep->me_value;
- DEOPT_IF(attr_o == NULL);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(attr_o);
- attr = PyStackRef_FromPyObjectSteal(attr_o);
- DECREF_INPUTS();
- }
-
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 6ae88a817244b1..e164f11620de41 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2267,9 +2267,10 @@
break;
}
- case _GUARD_GLOBALS_VERSION_PUSH_KEYS: {
- PyDictKeysObject *globals_keys;
+ case _LOAD_GLOBAL_MODULE: {
+ _PyStackRef res;
uint16_t version = (uint16_t)CURRENT_OPERAND0();
+ uint16_t index = (uint16_t)CURRENT_OPERAND1();
PyDictObject *dict = (PyDictObject *)GLOBALS();
if (!PyDict_CheckExact(dict)) {
UOP_STAT_INC(uopcode, miss);
@@ -2280,44 +2281,10 @@
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- globals_keys = keys;
- assert(DK_IS_UNICODE(globals_keys));
- stack_pointer[0].bits = (uintptr_t)globals_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _GUARD_BUILTINS_VERSION_PUSH_KEYS: {
- PyDictKeysObject *builtins_keys;
- uint16_t version = (uint16_t)CURRENT_OPERAND0();
- PyDictObject *dict = (PyDictObject *)BUILTINS();
- if (!PyDict_CheckExact(dict)) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
- if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- builtins_keys = keys;
- assert(DK_IS_UNICODE(builtins_keys));
- stack_pointer[0].bits = (uintptr_t)builtins_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_GLOBAL_MODULE_FROM_KEYS: {
- PyDictKeysObject *globals_keys;
- _PyStackRef res;
- globals_keys = (PyDictKeysObject *)stack_pointer[-1].bits;
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(globals_keys);
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
+ assert(index < DK_SIZE(keys));
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (res_o == NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -2329,8 +2296,7 @@
JUMP_TO_JUMP_TARGET();
}
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
stack_pointer[0] = res;
@@ -2339,15 +2305,23 @@
break;
}
- case _LOAD_GLOBAL_BUILTINS_FROM_KEYS: {
- PyDictKeysObject *builtins_keys;
+ case _LOAD_GLOBAL_BUILTINS: {
_PyStackRef res;
- builtins_keys = (PyDictKeysObject *)stack_pointer[-1].bits;
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(builtins_keys);
+ uint16_t version = (uint16_t)CURRENT_OPERAND0();
+ uint16_t index = (uint16_t)CURRENT_OPERAND1();
+ PyDictObject *dict = (PyDictObject *)BUILTINS();
+ if (!PyDict_CheckExact(dict)) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys);
+ if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) {
+ UOP_STAT_INC(uopcode, miss);
+ JUMP_TO_JUMP_TARGET();
+ }
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (res_o == NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -2359,8 +2333,7 @@
JUMP_TO_JUMP_TARGET();
}
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
stack_pointer[0] = res;
@@ -3143,11 +3116,12 @@
break;
}
- case _CHECK_ATTR_MODULE_PUSH_KEYS: {
+ case _LOAD_ATTR_MODULE: {
_PyStackRef owner;
- PyDictKeysObject *mod_keys;
+ _PyStackRef attr;
owner = stack_pointer[-1];
uint32_t dict_version = (uint32_t)CURRENT_OPERAND0();
+ uint16_t index = (uint16_t)CURRENT_OPERAND1();
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) {
UOP_STAT_INC(uopcode, miss);
@@ -3160,27 +3134,10 @@
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- mod_keys = keys;
- stack_pointer[0].bits = (uintptr_t)mod_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_ATTR_MODULE_FROM_KEYS: {
- PyDictKeysObject *mod_keys;
- _PyStackRef owner;
- _PyStackRef attr;
- mod_keys = (PyDictKeysObject *)stack_pointer[-1].bits;
- owner = stack_pointer[-2];
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ assert(keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index;
PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
- // Clear mod_keys from stack in case we need to deopt
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (attr_o == NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3194,8 +3151,7 @@
}
}
#else
- Py_INCREF(attr_o);
- attr = PyStackRef_FromPyObjectSteal(attr_o);
+ attr = PyStackRef_FromPyObjectNew(attr_o);
#endif
STAT_INC(LOAD_ATTR, hit);
stack_pointer[-1] = attr;
@@ -3205,37 +3161,22 @@
break;
}
- case _CHECK_ATTR_WITH_HINT: {
+ case _LOAD_ATTR_WITH_HINT: {
_PyStackRef owner;
- PyDictObject *dict;
+ _PyStackRef attr;
+ oparg = CURRENT_OPARG();
owner = stack_pointer[-1];
+ uint16_t hint = (uint16_t)CURRENT_OPERAND0();
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o);
- if (dict_o == NULL) {
+ PyDictObject *dict = _PyObject_GetManagedDict(owner_o);
+ if (dict == NULL) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
}
- assert(PyDict_CheckExact((PyObject *)dict_o));
- dict = dict_o;
- stack_pointer[0].bits = (uintptr_t)dict;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_ATTR_WITH_HINT: {
- PyDictObject *dict;
- _PyStackRef owner;
- _PyStackRef attr;
- oparg = CURRENT_OPARG();
- dict = (PyDictObject *)stack_pointer[-1].bits;
- owner = stack_pointer[-2];
- uint16_t hint = (uint16_t)CURRENT_OPERAND0();
+ assert(PyDict_CheckExact((PyObject *)dict));
PyObject *attr_o;
if (!LOCK_OBJECT(dict)) {
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3243,8 +3184,6 @@
}
if (hint >= (size_t)dict->ma_keys->dk_nentries) {
UNLOCK_OBJECT(dict);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3253,8 +3192,6 @@
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1);
if (dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) {
UNLOCK_OBJECT(dict);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3263,8 +3200,6 @@
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
if (ep->me_key != name) {
UNLOCK_OBJECT(dict);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3273,8 +3208,6 @@
attr_o = ep->me_value;
if (attr_o == NULL) {
UNLOCK_OBJECT(dict);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
if (true) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
@@ -3283,15 +3216,10 @@
STAT_INC(LOAD_ATTR, hit);
attr = PyStackRef_FromPyObjectNew(attr_o);
UNLOCK_OBJECT(dict);
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
+ stack_pointer[-1] = attr;
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyStackRef tmp = owner;
- owner = attr;
- stack_pointer[-1] = owner;
- PyStackRef_CLOSE(tmp);
+ PyStackRef_CLOSE(owner);
stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer[-1] = attr;
break;
}
@@ -3395,7 +3323,7 @@
break;
}
- /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it has unused cache entries */
+ /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it has too many cache entries */
case _GUARD_DORV_NO_DICT: {
_PyStackRef owner;
@@ -6833,6 +6761,23 @@
break;
}
+ case _POP_TOP_LOAD_CONST_INLINE: {
+ _PyStackRef pop;
+ _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_FromPyObjectNew(ptr);
+ stack_pointer[0] = value;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
+ break;
+ }
+
case _LOAD_CONST_INLINE_BORROW: {
_PyStackRef value;
PyObject *ptr = (PyObject *)CURRENT_OPERAND0();
@@ -6871,70 +6816,6 @@
break;
}
- case _LOAD_GLOBAL_MODULE: {
- _PyStackRef res;
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyDictObject *dict = (PyDictObject *)GLOBALS();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- PyObject *res_o = entries[index].me_value;
- if (res_o == NULL) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
- stack_pointer[0] = res;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_GLOBAL_BUILTINS: {
- _PyStackRef res;
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyDictObject *dict = (PyDictObject *)BUILTINS();
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- PyObject *res_o = entries[index].me_value;
- if (res_o == NULL) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
- stack_pointer[0] = res;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_ATTR_MODULE: {
- _PyStackRef owner;
- _PyStackRef attr;
- owner = stack_pointer[-1];
- uint16_t index = (uint16_t)CURRENT_OPERAND0();
- PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict;
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
- PyObject *attr_o = ep->me_value;
- if (attr_o == NULL) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(attr_o);
- attr = PyStackRef_FromPyObjectSteal(attr_o);
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _PyStackRef tmp = owner;
- owner = attr;
- stack_pointer[-1] = owner;
- PyStackRef_CLOSE(tmp);
- stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer[-1] = attr;
- break;
- }
-
case _START_EXECUTOR: {
PyObject *executor = (PyObject *)CURRENT_OPERAND0();
_PyFrame_SetStackPointer(frame, stack_pointer);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 564e5be92ae789..8c3c0e3910b8d1 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -8173,14 +8173,14 @@
INSTRUCTION_STATS(LOAD_ATTR_MODULE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
_PyStackRef owner;
- PyDictKeysObject *mod_keys;
_PyStackRef attr;
_PyStackRef null = PyStackRef_NULL;
/* Skip 1 cache entry */
- // _CHECK_ATTR_MODULE_PUSH_KEYS
+ // _LOAD_ATTR_MODULE
{
owner = stack_pointer[-1];
uint32_t dict_version = read_u32(&this_instr[2].cache);
+ uint16_t index = read_u16(&this_instr[4].cache);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) {
UPDATE_MISS_STATS(LOAD_ATTR);
@@ -8195,16 +8195,10 @@
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
JUMP_TO_PREDICTED(LOAD_ATTR);
}
- mod_keys = keys;
- }
- // _LOAD_ATTR_MODULE_FROM_KEYS
- {
- uint16_t index = read_u16(&this_instr[4].cache);
- assert(mod_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(mod_keys->dk_nentries));
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mod_keys) + index;
+ assert(keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries));
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index;
PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value);
- // Clear mod_keys from stack in case we need to deopt
if (attr_o == NULL) {
UPDATE_MISS_STATS(LOAD_ATTR);
assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
@@ -8220,8 +8214,7 @@
}
}
#else
- Py_INCREF(attr_o);
- attr = PyStackRef_FromPyObjectSteal(attr_o);
+ attr = PyStackRef_FromPyObjectNew(attr_o);
#endif
STAT_INC(LOAD_ATTR, hit);
stack_pointer[-1] = attr;
@@ -8530,7 +8523,6 @@
INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT);
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
_PyStackRef owner;
- PyDictObject *dict;
_PyStackRef attr;
_PyStackRef null = PyStackRef_NULL;
/* Skip 1 cache entry */
@@ -8546,22 +8538,18 @@
JUMP_TO_PREDICTED(LOAD_ATTR);
}
}
- // _CHECK_ATTR_WITH_HINT
+ // _LOAD_ATTR_WITH_HINT
{
+ uint16_t hint = read_u16(&this_instr[4].cache);
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictObject *dict_o = _PyObject_GetManagedDict(owner_o);
- if (dict_o == NULL) {
+ PyDictObject *dict = _PyObject_GetManagedDict(owner_o);
+ if (dict == 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;
- }
- // _LOAD_ATTR_WITH_HINT
- {
- uint16_t hint = read_u16(&this_instr[4].cache);
+ assert(PyDict_CheckExact((PyObject *)dict));
PyObject *attr_o;
if (!LOCK_OBJECT(dict)) {
if (true) {
@@ -8608,13 +8596,10 @@
STAT_INC(LOAD_ATTR, hit);
attr = PyStackRef_FromPyObjectNew(attr_o);
UNLOCK_OBJECT(dict);
+ stack_pointer[-1] = attr;
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyStackRef tmp = owner;
- owner = attr;
- stack_pointer[-1] = owner;
- PyStackRef_CLOSE(tmp);
+ PyStackRef_CLOSE(owner);
stack_pointer = _PyFrame_GetStackPointer(frame);
- stack_pointer[-1] = attr;
}
/* Skip 5 cache entries */
// _PUSH_NULL_CONDITIONAL
@@ -9052,7 +9037,6 @@
next_instr += 5;
INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN);
static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size");
- PyDictKeysObject *builtins_keys;
_PyStackRef res;
_PyStackRef null = PyStackRef_NULL;
/* Skip 1 cache entry */
@@ -9073,9 +9057,10 @@
}
assert(DK_IS_UNICODE(keys));
}
- // _GUARD_BUILTINS_VERSION_PUSH_KEYS
+ // _LOAD_GLOBAL_BUILTINS
{
uint16_t version = read_u16(&this_instr[3].cache);
+ uint16_t index = read_u16(&this_instr[4].cache);
PyDictObject *dict = (PyDictObject *)BUILTINS();
if (!PyDict_CheckExact(dict)) {
UPDATE_MISS_STATS(LOAD_GLOBAL);
@@ -9088,13 +9073,8 @@
assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL));
JUMP_TO_PREDICTED(LOAD_GLOBAL);
}
- builtins_keys = keys;
- assert(DK_IS_UNICODE(builtins_keys));
- }
- // _LOAD_GLOBAL_BUILTINS_FROM_KEYS
- {
- uint16_t index = read_u16(&this_instr[4].cache);
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(builtins_keys);
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
if (res_o == NULL) {
UPDATE_MISS_STATS(LOAD_GLOBAL);
@@ -9109,8 +9089,7 @@
JUMP_TO_PREDICTED(LOAD_GLOBAL);
}
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
}
@@ -9136,13 +9115,16 @@
next_instr += 5;
INSTRUCTION_STATS(LOAD_GLOBAL_MODULE);
static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size");
- PyDictKeysObject *globals_keys;
_PyStackRef res;
_PyStackRef null = PyStackRef_NULL;
/* Skip 1 cache entry */
- // _GUARD_GLOBALS_VERSION_PUSH_KEYS
+ // _NOP
+ {
+ }
+ // _LOAD_GLOBAL_MODULE
{
uint16_t version = read_u16(&this_instr[2].cache);
+ uint16_t index = read_u16(&this_instr[4].cache);
PyDictObject *dict = (PyDictObject *)GLOBALS();
if (!PyDict_CheckExact(dict)) {
UPDATE_MISS_STATS(LOAD_GLOBAL);
@@ -9155,14 +9137,9 @@
assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL));
JUMP_TO_PREDICTED(LOAD_GLOBAL);
}
- globals_keys = keys;
- assert(DK_IS_UNICODE(globals_keys));
- }
- /* Skip 1 cache entry */
- // _LOAD_GLOBAL_MODULE_FROM_KEYS
- {
- uint16_t index = read_u16(&this_instr[4].cache);
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(globals_keys);
+ assert(DK_IS_UNICODE(keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
+ assert(index < DK_SIZE(keys));
PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value);
if (res_o == NULL) {
UPDATE_MISS_STATS(LOAD_GLOBAL);
@@ -9177,8 +9154,7 @@
JUMP_TO_PREDICTED(LOAD_GLOBAL);
}
#else
- Py_INCREF(res_o);
- res = PyStackRef_FromPyObjectSteal(res_o);
+ res = PyStackRef_FromPyObjectNew(res_o);
#endif
STAT_INC(LOAD_GLOBAL, hit);
}
diff --git a/Python/optimizer.c b/Python/optimizer.c
index d86d58d82e391b..e05523451da859 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -230,16 +230,18 @@ _PyUOpPrint(const _PyUOpInstruction *uop)
}
switch(uop->format) {
case UOP_FORMAT_TARGET:
- printf(" (%d, target=%d, operand=%#" PRIx64,
+ printf(" (%d, target=%d, operand0=%#" PRIx64 ", operand1=%#" PRIx64,
uop->oparg,
uop->target,
- (uint64_t)uop->operand0);
+ (uint64_t)uop->operand0,
+ (uint64_t)uop->operand1);
break;
case UOP_FORMAT_JUMP:
- printf(" (%d, jump_target=%d, operand=%#" PRIx64,
+ printf(" (%d, jump_target=%d, operand0=%#" PRIx64 ", operand1=%#" PRIx64,
uop->oparg,
uop->jump_target,
- (uint64_t)uop->operand0);
+ (uint64_t)uop->operand0,
+ (uint64_t)uop->operand1);
break;
default:
printf(" (%d, Unknown format)", uop->oparg);
@@ -682,7 +684,7 @@ translate_bytecode_to_trace(
// Add one to account for the actual opcode/oparg pair:
int offset = expansion->uops[i].offset + 1;
switch (expansion->uops[i].size) {
- case OPARG_FULL:
+ case OPARG_SIMPLE:
assert(opcode != JUMP_BACKWARD_NO_INTERRUPT && opcode != JUMP_BACKWARD);
break;
case OPARG_CACHE_1:
@@ -716,6 +718,21 @@ translate_bytecode_to_trace(
}
#endif
break;
+ case OPERAND1_1:
+ assert(trace[trace_length-1].opcode == uop);
+ operand = read_u16(&instr[offset].cache);
+ trace[trace_length-1].operand1 = operand;
+ continue;
+ case OPERAND1_2:
+ assert(trace[trace_length-1].opcode == uop);
+ operand = read_u32(&instr[offset].cache);
+ trace[trace_length-1].operand1 = operand;
+ continue;
+ case OPERAND1_4:
+ assert(trace[trace_length-1].opcode == uop);
+ operand = read_u64(&instr[offset].cache);
+ trace[trace_length-1].operand1 = operand;
+ continue;
default:
fprintf(stderr,
"opcode=%d, oparg=%d; nuops=%d, i=%d; size=%d, offset=%d\n",
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index 6c0aadb87e6741..29a05088e62c45 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -93,26 +93,27 @@ type_watcher_callback(PyTypeObject* type)
}
static PyObject *
-convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj)
+convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop)
{
- assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE_FROM_KEYS);
+ assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE);
assert(PyDict_CheckExact(obj));
PyDictObject *dict = (PyDictObject *)obj;
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- assert(inst->operand0 <= UINT16_MAX);
- if ((int)inst->operand0 >= dict->ma_keys->dk_nentries) {
+ int64_t index = inst->operand1;
+ assert(index <= UINT16_MAX);
+ if ((int)index >= dict->ma_keys->dk_nentries) {
return NULL;
}
- PyObject *res = entries[inst->operand0].me_value;
+ PyObject *res = entries[index].me_value;
if (res == NULL) {
return NULL;
}
if (_Py_IsImmortal(res)) {
- inst->opcode = _LOAD_CONST_INLINE_BORROW;
+ inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE_BORROW;
}
else {
- inst->opcode = _LOAD_CONST_INLINE;
+ inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE : _LOAD_CONST_INLINE;
}
if (inst->oparg & 1) {
assert(inst[1].opcode == _PUSH_NULL_CONDITIONAL);
@@ -198,21 +199,18 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
_PyUOpInstruction *inst = &buffer[pc];
int opcode = inst->opcode;
switch(opcode) {
- case _GUARD_BUILTINS_VERSION_PUSH_KEYS:
- if (incorrect_keys(inst, builtins)) {
+ case _GUARD_GLOBALS_VERSION:
+ if (incorrect_keys(inst, globals)) {
OPT_STAT_INC(remove_globals_incorrect_keys);
return 0;
}
- if (interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
- continue;
- }
- if (!check_next_uop(buffer, buffer_size, pc,
- _LOAD_GLOBAL_BUILTINS_FROM_KEYS)) {
+ if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
continue;
}
- if ((builtins_watched & 1) == 0) {
- PyDict_Watch(BUILTINS_WATCHER_ID, builtins);
- builtins_watched |= 1;
+ if ((globals_watched & 1) == 0) {
+ PyDict_Watch(GLOBALS_WATCHER_ID, globals);
+ _Py_BloomFilter_Add(dependencies, globals);
+ globals_watched |= 1;
}
if (function_checked & 1) {
buffer[pc].opcode = NOP;
@@ -222,24 +220,29 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
buffer[pc].operand0 = function_version;
function_checked |= 1;
}
- // We're no longer pushing the builtins keys; rewrite the
- // instruction that consumed the keys to load them from the
- // frame.
- buffer[pc + 1].opcode = _LOAD_GLOBAL_BUILTINS;
break;
- case _GUARD_GLOBALS_VERSION:
- case _GUARD_GLOBALS_VERSION_PUSH_KEYS:
- if (incorrect_keys(inst, globals)) {
+ case _LOAD_GLOBAL_BUILTINS:
+ if (incorrect_keys(inst, builtins)) {
OPT_STAT_INC(remove_globals_incorrect_keys);
return 0;
}
- uint64_t watched_mutations = get_mutations(globals);
- if (watched_mutations >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
+ if (interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
continue;
}
- if (opcode == _GUARD_GLOBALS_VERSION_PUSH_KEYS &&
- !check_next_uop(buffer, buffer_size, pc,
- _LOAD_GLOBAL_MODULE_FROM_KEYS)) {
+ if ((builtins_watched & 1) == 0) {
+ PyDict_Watch(BUILTINS_WATCHER_ID, builtins);
+ builtins_watched |= 1;
+ }
+ if (function_checked & globals_watched & 1) {
+ convert_global_to_const(inst, builtins, false);
+ }
+ break;
+ case _LOAD_GLOBAL_MODULE:
+ if (incorrect_keys(inst, globals)) {
+ OPT_STAT_INC(remove_globals_incorrect_keys);
+ return 0;
+ }
+ if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
continue;
}
if ((globals_watched & 1) == 0) {
@@ -247,29 +250,13 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
_Py_BloomFilter_Add(dependencies, globals);
globals_watched |= 1;
}
- if (function_checked & 1) {
- buffer[pc].opcode = NOP;
- }
- else {
- buffer[pc].opcode = _CHECK_FUNCTION;
- buffer[pc].operand0 = function_version;
+ if ((function_checked & 1) == 0 && buffer[pc-1].opcode == _NOP) {
+ buffer[pc-1].opcode = _CHECK_FUNCTION;
+ buffer[pc-1].operand0 = function_version;
function_checked |= 1;
}
- if (opcode == _GUARD_GLOBALS_VERSION_PUSH_KEYS) {
- // We're no longer pushing the globals keys; rewrite the
- // instruction that consumed the keys to load them from the
- // frame.
- buffer[pc + 1].opcode = _LOAD_GLOBAL_MODULE;
- }
- break;
- case _LOAD_GLOBAL_BUILTINS:
- if (function_checked & globals_watched & builtins_watched & 1) {
- convert_global_to_const(inst, builtins);
- }
- break;
- case _LOAD_GLOBAL_MODULE:
- if (function_checked & globals_watched & 1) {
- convert_global_to_const(inst, globals);
+ if (function_checked & 1) {
+ convert_global_to_const(inst, globals, false);
}
break;
case _PUSH_FRAME:
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 41eb59c931aaa7..f3625a1492c47c 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -492,6 +492,14 @@ dummy_func(void) {
value = sym_new_const(ctx, ptr);
}
+ op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
+ value = sym_new_const(ctx, ptr);
+ }
+
+ op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) {
+ value = sym_new_const(ctx, ptr);
+ }
+
op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
assert(oparg > 0);
top = bottom;
@@ -509,22 +517,27 @@ dummy_func(void) {
(void)offset;
}
- op(_CHECK_ATTR_MODULE_PUSH_KEYS, (dict_version/2, owner -- owner, mod_keys)) {
+ op(_LOAD_ATTR_MODULE, (dict_version/2, owner, index/1 -- attr)) {
(void)dict_version;
- mod_keys = sym_new_not_null(ctx);
+ (void)index;
+ attr = NULL;
if (sym_is_const(owner)) {
- PyObject *cnst = sym_get_const(owner);
- if (PyModule_CheckExact(cnst)) {
- PyModuleObject *mod = (PyModuleObject *)cnst;
+ PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
+ if (PyModule_CheckExact(mod)) {
PyObject *dict = mod->md_dict;
uint64_t watched_mutations = get_mutations(dict);
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
_Py_BloomFilter_Add(dependencies, dict);
- this_instr->opcode = _NOP;
+ PyObject *res = convert_global_to_const(this_instr, dict, true);
+ attr = sym_new_const(ctx, res);
}
}
}
+ if (attr == NULL) {
+ /* No conversion made. We don't know what `attr` is. */
+ attr = sym_new_not_null(ctx);
+ }
}
op (_PUSH_NULL_CONDITIONAL, ( -- null if (oparg & 1))) {
@@ -541,35 +554,7 @@ dummy_func(void) {
}
}
- op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys -- attr)) {
- (void)index;
- attr = NULL;
- if (this_instr[-1].opcode == _NOP) {
- // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched.
- assert(sym_is_const(owner));
- PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
- assert(PyModule_CheckExact(mod));
- PyObject *dict = mod->md_dict;
- PyObject *res = convert_global_to_const(this_instr, dict);
- if (res != NULL) {
- this_instr[-1].opcode = _POP_TOP;
- attr = sym_new_const(ctx, res);
- }
- else {
- this_instr->opcode = _LOAD_ATTR_MODULE;
- }
- }
- if (attr == NULL) {
- /* No conversion made. We don't know what `attr` is. */
- attr = sym_new_not_null(ctx);
- }
- }
-
- op(_CHECK_ATTR_WITH_HINT, (owner -- owner, dict)) {
- dict = sym_new_not_null(ctx);
- }
-
- op(_LOAD_ATTR_WITH_HINT, (hint/1, owner, dict -- attr)) {
+ op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr)) {
attr = sym_new_not_null(ctx);
(void)hint;
}
@@ -888,16 +873,6 @@ dummy_func(void) {
ctx->done = true;
}
- op(_GUARD_GLOBALS_VERSION_PUSH_KEYS, (version/1 -- globals_keys)) {
- globals_keys = sym_new_unknown(ctx);
- (void)version;
- }
-
- op(_GUARD_BUILTINS_VERSION_PUSH_KEYS, (version/1 -- builtins_keys)) {
- builtins_keys = sym_new_unknown(ctx);
- (void)version;
- }
-
op(_REPLACE_WITH_TRUE, (value -- res)) {
res = sym_new_const(ctx, Py_True);
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 51d0fa63e64bc5..0372870b94ec0a 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -932,39 +932,21 @@
break;
}
- case _GUARD_GLOBALS_VERSION_PUSH_KEYS: {
- JitOptSymbol *globals_keys;
- uint16_t version = (uint16_t)this_instr->operand0;
- globals_keys = sym_new_unknown(ctx);
- (void)version;
- stack_pointer[0] = globals_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _GUARD_BUILTINS_VERSION_PUSH_KEYS: {
- JitOptSymbol *builtins_keys;
- uint16_t version = (uint16_t)this_instr->operand0;
- builtins_keys = sym_new_unknown(ctx);
- (void)version;
- stack_pointer[0] = builtins_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_GLOBAL_MODULE_FROM_KEYS: {
+ case _LOAD_GLOBAL_MODULE: {
JitOptSymbol *res;
res = sym_new_not_null(ctx);
- stack_pointer[-1] = res;
+ stack_pointer[0] = res;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
break;
}
- case _LOAD_GLOBAL_BUILTINS_FROM_KEYS: {
+ case _LOAD_GLOBAL_BUILTINS: {
JitOptSymbol *res;
res = sym_new_not_null(ctx);
- stack_pointer[-1] = res;
+ stack_pointer[0] = res;
+ stack_pointer += 1;
+ assert(WITHIN_STACK_BOUNDS());
break;
}
@@ -1168,80 +1150,34 @@
break;
}
- case _CHECK_ATTR_MODULE_PUSH_KEYS: {
+ case _LOAD_ATTR_MODULE: {
JitOptSymbol *owner;
- JitOptSymbol *mod_keys;
+ JitOptSymbol *attr;
owner = stack_pointer[-1];
uint32_t dict_version = (uint32_t)this_instr->operand0;
+ uint16_t index = (uint16_t)this_instr->operand0;
(void)dict_version;
- mod_keys = sym_new_not_null(ctx);
+ (void)index;
+ attr = NULL;
if (sym_is_const(owner)) {
- PyObject *cnst = sym_get_const(owner);
- if (PyModule_CheckExact(cnst)) {
- PyModuleObject *mod = (PyModuleObject *)cnst;
+ PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
+ if (PyModule_CheckExact(mod)) {
PyObject *dict = mod->md_dict;
- stack_pointer[0] = mod_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
+ stack_pointer[-1] = attr;
uint64_t watched_mutations = get_mutations(dict);
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
_Py_BloomFilter_Add(dependencies, dict);
- this_instr->opcode = _NOP;
+ PyObject *res = convert_global_to_const(this_instr, dict, true);
+ attr = sym_new_const(ctx, res);
}
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
}
}
- stack_pointer[0] = mod_keys;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_ATTR_MODULE_FROM_KEYS: {
- JitOptSymbol *owner;
- JitOptSymbol *attr;
- owner = stack_pointer[-2];
- uint16_t index = (uint16_t)this_instr->operand0;
- (void)index;
- attr = NULL;
- if (this_instr[-1].opcode == _NOP) {
- // Preceding _CHECK_ATTR_MODULE_PUSH_KEYS was removed: mod is const and dict is watched.
- assert(sym_is_const(owner));
- PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
- assert(PyModule_CheckExact(mod));
- PyObject *dict = mod->md_dict;
- stack_pointer[-2] = attr;
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
- PyObject *res = convert_global_to_const(this_instr, dict);
- if (res != NULL) {
- this_instr[-1].opcode = _POP_TOP;
- attr = sym_new_const(ctx, res);
- }
- else {
- this_instr->opcode = _LOAD_ATTR_MODULE;
- }
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- }
if (attr == NULL) {
/* No conversion made. We don't know what `attr` is. */
attr = sym_new_not_null(ctx);
}
- stack_pointer[-2] = attr;
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _CHECK_ATTR_WITH_HINT: {
- JitOptSymbol *dict;
- dict = sym_new_not_null(ctx);
- stack_pointer[0] = dict;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
+ stack_pointer[-1] = attr;
break;
}
@@ -1250,9 +1186,7 @@
uint16_t hint = (uint16_t)this_instr->operand0;
attr = sym_new_not_null(ctx);
(void)hint;
- stack_pointer[-2] = attr;
- stack_pointer += -1;
- assert(WITHIN_STACK_BOUNDS());
+ stack_pointer[-1] = attr;
break;
}
@@ -2420,6 +2354,14 @@
break;
}
+ case _POP_TOP_LOAD_CONST_INLINE: {
+ JitOptSymbol *value;
+ PyObject *ptr = (PyObject *)this_instr->operand0;
+ value = sym_new_const(ctx, ptr);
+ stack_pointer[-1] = value;
+ break;
+ }
+
case _LOAD_CONST_INLINE_BORROW: {
JitOptSymbol *value;
PyObject *ptr = (PyObject *)this_instr->operand0;
@@ -2432,7 +2374,8 @@
case _POP_TOP_LOAD_CONST_INLINE_BORROW: {
JitOptSymbol *value;
- value = sym_new_not_null(ctx);
+ PyObject *ptr = (PyObject *)this_instr->operand0;
+ value = sym_new_const(ctx, ptr);
stack_pointer[-1] = value;
break;
}
@@ -2441,31 +2384,6 @@
break;
}
- case _LOAD_GLOBAL_MODULE: {
- JitOptSymbol *res;
- res = sym_new_not_null(ctx);
- stack_pointer[0] = res;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_GLOBAL_BUILTINS: {
- JitOptSymbol *res;
- res = sym_new_not_null(ctx);
- stack_pointer[0] = res;
- stack_pointer += 1;
- assert(WITHIN_STACK_BOUNDS());
- break;
- }
-
- case _LOAD_ATTR_MODULE: {
- JitOptSymbol *attr;
- attr = sym_new_not_null(ctx);
- stack_pointer[-1] = attr;
- break;
- }
-
case _START_EXECUTOR: {
break;
}
diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py
index 10db9acbb362c8..162a0fdb2cc459 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -215,7 +215,7 @@ def why_not_viable(self) -> str | None:
if self.properties.needs_this:
return "uses the 'this_instr' variable"
if len([c for c in self.caches if c.name != "unused"]) > 2:
- return "has unused cache entries"
+ return "has too many cache entries"
if self.properties.error_with_pop and self.properties.error_without_pop:
return "has both popping and not-popping errors"
return None
@@ -416,11 +416,14 @@ def analyze_caches(inputs: list[parser.InputEffect]) -> list[CacheEntry]:
caches: list[parser.CacheEffect] = [
i for i in inputs if isinstance(i, parser.CacheEffect)
]
- for cache in caches:
- if cache.name == "unused":
- raise analysis_error(
- "Unused cache entry in op. Move to enclosing macro.", cache.tokens[0]
- )
+ if caches:
+ # Middle entries are allowed to be unused. Check first and last caches.
+ for index in (0, -1):
+ cache = caches[index]
+ if cache.name == "unused":
+ position = "First" if index == 0 else "Last"
+ msg = f"{position} cache entry in op is unused. Move to enclosing macro."
+ raise analysis_error(msg, cache.tokens[0])
return [CacheEntry(i.name, int(i.size)) for i in caches]
diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py
index 6b2ef51b29f4eb..9511974efb423e 100644
--- a/Tools/cases_generator/generators_common.py
+++ b/Tools/cases_generator/generators_common.py
@@ -126,7 +126,6 @@ def __init__(self, out: CWriter, labels: dict[str, Label]):
"PyStackRef_AsPyObjectSteal": self.stackref_steal,
"DISPATCH": self.dispatch,
"INSTRUCTION_SIZE": self.instruction_size,
- "POP_INPUT": self.pop_input,
"stack_pointer": self.stack_pointer,
}
self.out = out
@@ -421,29 +420,6 @@ def save_stack(
self.emit_save(storage)
return True
- def pop_input(
- self,
- tkn: Token,
- tkn_iter: TokenIterator,
- uop: CodeSection,
- storage: Storage,
- inst: Instruction | None,
- ) -> bool:
- next(tkn_iter)
- name_tkn = next(tkn_iter)
- name = name_tkn.text
- next(tkn_iter)
- next(tkn_iter)
- if not storage.inputs:
- raise analysis_error("stack is empty", tkn)
- tos = storage.inputs[-1]
- if tos.name != name:
- raise analysis_error(f"'{name} is not top of stack", name_tkn)
- tos.defined = False
- storage.clear_dead_inputs()
- storage.flush(self.out)
- return True
-
def emit_reload(self, storage: Storage) -> None:
storage.reload(self.out)
self._print_storage(storage)
diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py
index 453db6905d6842..bfee3c8aa301bb 100644
--- a/Tools/cases_generator/opcode_metadata_generator.py
+++ b/Tools/cases_generator/opcode_metadata_generator.py
@@ -26,7 +26,7 @@
# Constants used instead of size for macro expansions.
# Note: 1, 2, 4 must match actual cache entry sizes.
OPARG_KINDS = {
- "OPARG_FULL": 0,
+ "OPARG_SIMPLE": 0,
"OPARG_CACHE_1": 1,
"OPARG_CACHE_2": 2,
"OPARG_CACHE_4": 4,
@@ -35,6 +35,9 @@
"OPARG_SAVE_RETURN_OFFSET": 7,
# Skip 8 as the other powers of 2 are sizes
"OPARG_REPLACED": 9,
+ "OPERAND1_1": 10,
+ "OPERAND1_2": 11,
+ "OPERAND1_4": 12,
}
FLAGS = [
@@ -313,10 +316,10 @@ def generate_metadata_table(analysis: Analysis, out: CWriter) -> None:
def generate_expansion_table(analysis: Analysis, out: CWriter) -> None:
- expansions_table: dict[str, list[tuple[str, int, int]]] = {}
+ expansions_table: dict[str, list[tuple[str, str, int]]] = {}
for inst in sorted(analysis.instructions.values(), key=lambda t: t.name):
offset: int = 0 # Cache effect offset
- expansions: list[tuple[str, int, int]] = [] # [(name, size, offset), ...]
+ expansions: list[tuple[str, str, int]] = [] # [(name, size, offset), ...]
if inst.is_super():
pieces = inst.name.split("_")
assert len(pieces) == 4, f"{inst.name} doesn't look like a super-instr"
@@ -332,22 +335,32 @@ def generate_expansion_table(analysis: Analysis, out: CWriter) -> None:
assert (
len(instr2.parts) == 1
), f"{name2} is not a good superinstruction part"
- expansions.append((instr1.parts[0].name, OPARG_KINDS["OPARG_TOP"], 0))
- expansions.append((instr2.parts[0].name, OPARG_KINDS["OPARG_BOTTOM"], 0))
+ expansions.append((instr1.parts[0].name, "OPARG_TOP", 0))
+ expansions.append((instr2.parts[0].name, "OPARG_BOTTOM", 0))
elif not is_viable_expansion(inst):
continue
else:
for part in inst.parts:
size = part.size
- if part.name == "_SAVE_RETURN_OFFSET":
- size = OPARG_KINDS["OPARG_SAVE_RETURN_OFFSET"]
if isinstance(part, Uop):
# Skip specializations
if "specializing" in part.annotations:
continue
+ # Add the primary expansion.
+ fmt = "OPARG_SIMPLE"
+ if part.name == "_SAVE_RETURN_OFFSET":
+ fmt = "OPARG_SAVE_RETURN_OFFSET"
+ elif part.caches:
+ fmt = str(part.caches[0].size)
if "replaced" in part.annotations:
- size = OPARG_KINDS["OPARG_REPLACED"]
- expansions.append((part.name, size, offset if size else 0))
+ fmt = "OPARG_REPLACED"
+ expansions.append((part.name, fmt, offset))
+ if len(part.caches) > 1:
+ # Add expansion for the second operand
+ internal_offset = 0
+ for cache in part.caches[:-1]:
+ internal_offset += cache.size
+ expansions.append((part.name, f"OPERAND1_{part.caches[-1].size}", offset+internal_offset))
offset += part.size
expansions_table[inst.name] = expansions
max_uops = max(len(ex) for ex in expansions_table.values())
diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py
index 5e23360cdc0aaf..fc7b29af627baa 100644
--- a/Tools/cases_generator/tier2_generator.py
+++ b/Tools/cases_generator/tier2_generator.py
@@ -153,7 +153,8 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack:
code_list, storage = Storage.for_uop(stack, uop)
for code in code_list:
emitter.emit(code)
- for idx, cache in enumerate(uop.caches):
+ idx = 0
+ for cache in uop.caches:
if cache.name != "unused":
if cache.size == 4:
type = cast = "PyObject *"
@@ -161,6 +162,7 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack:
type = f"uint{cache.size*16}_t "
cast = f"uint{cache.size*16}_t"
emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n")
+ idx += 1
storage = emitter.emit_tokens(uop, storage, None)
except StackError as ex:
raise analysis_error(ex.args[0], uop.body[0]) from None
1
0

gh-129666: Revert "gh-129666: Add C11/C++11 to docs and `-pedantic-errors` to GCC/clang test_c[pp]ext tests (GH-130686)" (GH-130688)
by encukou Feb. 28, 2025
by encukou Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/ab11c097052757b79060c75dd4835c2431…
commit: ab11c097052757b79060c75dd4835c2431e752b7
branch: main
author: Petr Viktorin <encukou(a)gmail.com>
committer: encukou <encukou(a)gmail.com>
date: 2025-02-28T16:05:36Z
summary:
gh-129666: Revert "gh-129666: Add C11/C++11 to docs and `-pedantic-errors` to GCC/clang test_c[pp]ext tests (GH-130686)" (GH-130688)
This reverts commit 003e6d2b9776c07147a9c628eb028fd2ac3f0008.
files:
M Doc/c-api/intro.rst
M Lib/test/test_cext/__init__.py
M Lib/test/test_cext/extension.c
M Lib/test/test_cext/setup.py
M Lib/test/test_cppext/__init__.py
M Lib/test/test_cppext/extension.cpp
M Lib/test/test_cppext/setup.py
diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst
index 76d7d5793428f8..8ef463e3f88ca8 100644
--- a/Doc/c-api/intro.rst
+++ b/Doc/c-api/intro.rst
@@ -30,16 +30,6 @@ familiar with writing an extension before attempting to embed Python in a real
application.
-Language version compatibility
-==============================
-
-Python's C API is compatible with C11 and C++11 versions of C and C++.
-
-This is a lower limit: the C API does not require features from later
-C/C++ versions.
-You do *not* need to enable your compiler's "c11 mode".
-
-
Coding standards
================
diff --git a/Lib/test/test_cext/__init__.py b/Lib/test/test_cext/__init__.py
index 402a2d04fa875e..54859f9ff7622d 100644
--- a/Lib/test/test_cext/__init__.py
+++ b/Lib/test/test_cext/__init__.py
@@ -38,9 +38,6 @@ def test_build_c11(self):
@unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99")
def test_build_c99(self):
- # In public docs, we say C API is compatible with C11. However,
- # in practice we do maintain C99 compatibility in public headers.
- # Please ask the C API WG before adding a new C11-only feature.
self.check_build('_test_c99_cext', std='c99')
@support.requires_gil_enabled('incompatible with Free Threading')
diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c
index 64629c5a6da8cd..b76abe1d74c628 100644
--- a/Lib/test/test_cext/extension.c
+++ b/Lib/test/test_cext/extension.c
@@ -58,24 +58,11 @@ _testcext_exec(
return 0;
}
-// Converting from function pointer to void* has undefined behavior, but
-// works on all known platforms, and CPython's module and type slots currently
-// need it.
-// (GCC doesn't have a narrower category for this than -Wpedantic.)
-_Py_COMP_DIAG_PUSH
-#if defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpedantic"
-#elif defined(__clang__)
-#pragma clang diagnostic ignored "-Wpedantic"
-#endif
-
static PyModuleDef_Slot _testcext_slots[] = {
{Py_mod_exec, (void*)_testcext_exec},
{0, NULL}
};
-_Py_COMP_DIAG_POP
-
PyDoc_STRVAR(_testcext_doc, "C test extension.");
diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py
index 1275282983f7ff..e97749b45ea6f3 100644
--- a/Lib/test/test_cext/setup.py
+++ b/Lib/test/test_cext/setup.py
@@ -21,9 +21,6 @@
# gh-120593: Check the 'const' qualifier
'-Wcast-qual',
-
- # Ask for strict(er) compliance with the standard
- '-pedantic-errors',
]
if not support.Py_GIL_DISABLED:
CFLAGS.append(
diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py
index 7d56cb8b68620a..d5195227308fec 100644
--- a/Lib/test/test_cppext/__init__.py
+++ b/Lib/test/test_cppext/__init__.py
@@ -29,9 +29,6 @@ def test_build(self):
self.check_build('_testcppext')
def test_build_cpp03(self):
- # In public docs, we say C API is compatible with C++11. However,
- # in practice we do maintain C++03 compatibility in public headers.
- # Please ask the C API WG before adding a new C++11-only feature.
self.check_build('_testcpp03ext', std='c++03')
@unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11")
diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp
index 5b3571b295bec3..500d5918145c00 100644
--- a/Lib/test/test_cppext/extension.cpp
+++ b/Lib/test/test_cppext/extension.cpp
@@ -161,24 +161,11 @@ class VirtualPyObject : public PyObject {
int VirtualPyObject::instance_count = 0;
-// Converting from function pointer to void* has undefined behavior, but
-// works on all known platforms, and CPython's module and type slots currently
-// need it.
-// (GCC doesn't have a narrower category for this than -Wpedantic.)
-_Py_COMP_DIAG_PUSH
-#if defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpedantic"
-#elif defined(__clang__)
-#pragma clang diagnostic ignored "-Wpedantic"
-#endif
-
PyType_Slot VirtualPyObject_Slots[] = {
{Py_tp_free, (void*)VirtualPyObject::dealloc},
{0, _Py_NULL},
};
-_Py_COMP_DIAG_POP
-
PyType_Spec VirtualPyObject_Spec = {
/* .name */ STR(MODULE_NAME) ".VirtualPyObject",
/* .basicsize */ sizeof(VirtualPyObject),
@@ -254,20 +241,11 @@ _testcppext_exec(PyObject *module)
return 0;
}
-// Need to ignore "-Wpedantic" warnings; see VirtualPyObject_Slots above
-_Py_COMP_DIAG_PUSH
-#if defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wpedantic"
-#elif defined(__clang__)
-#pragma clang diagnostic ignored "-Wpedantic"
-#endif
-
static PyModuleDef_Slot _testcppext_slots[] = {
{Py_mod_exec, reinterpret_cast<void*>(_testcppext_exec)},
{0, _Py_NULL}
};
-_Py_COMP_DIAG_POP
PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");
diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py
index aa09c7bc8cb170..019ff18446a2eb 100644
--- a/Lib/test/test_cppext/setup.py
+++ b/Lib/test/test_cppext/setup.py
@@ -18,14 +18,6 @@
# a C++ extension using the Python C API does not emit C++ compiler
# warnings
'-Werror',
-
- # Ask for strict(er) compliance with the standard.
- '-pedantic-errors',
-
- # But allow C++11 features for -std=C++03. We use:
- # - `long long` (-Wno-c++11-long-long)
- # - comma at end of `enum` lists (no narrower GCC option exists)
- '-Wno-c++11-extensions',
]
else:
# MSVC compiler flags
1
0

gh-129666: Add C11/C++11 to docs and `-pedantic-errors` to GCC/clang test_c[pp]ext tests (GH-130686)
by encukou Feb. 28, 2025
by encukou Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/003e6d2b9776c07147a9c628eb028fd2ac…
commit: 003e6d2b9776c07147a9c628eb028fd2ac3f0008
branch: main
author: Petr Viktorin <encukou(a)gmail.com>
committer: encukou <encukou(a)gmail.com>
date: 2025-02-28T16:03:02+01:00
summary:
gh-129666: Add C11/C++11 to docs and `-pedantic-errors` to GCC/clang test_c[pp]ext tests (GH-130686)
files:
M Doc/c-api/intro.rst
M Lib/test/test_cext/__init__.py
M Lib/test/test_cext/extension.c
M Lib/test/test_cext/setup.py
M Lib/test/test_cppext/__init__.py
M Lib/test/test_cppext/extension.cpp
M Lib/test/test_cppext/setup.py
diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst
index 8ef463e3f88ca8..76d7d5793428f8 100644
--- a/Doc/c-api/intro.rst
+++ b/Doc/c-api/intro.rst
@@ -30,6 +30,16 @@ familiar with writing an extension before attempting to embed Python in a real
application.
+Language version compatibility
+==============================
+
+Python's C API is compatible with C11 and C++11 versions of C and C++.
+
+This is a lower limit: the C API does not require features from later
+C/C++ versions.
+You do *not* need to enable your compiler's "c11 mode".
+
+
Coding standards
================
diff --git a/Lib/test/test_cext/__init__.py b/Lib/test/test_cext/__init__.py
index 54859f9ff7622d..402a2d04fa875e 100644
--- a/Lib/test/test_cext/__init__.py
+++ b/Lib/test/test_cext/__init__.py
@@ -38,6 +38,9 @@ def test_build_c11(self):
@unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99")
def test_build_c99(self):
+ # In public docs, we say C API is compatible with C11. However,
+ # in practice we do maintain C99 compatibility in public headers.
+ # Please ask the C API WG before adding a new C11-only feature.
self.check_build('_test_c99_cext', std='c99')
@support.requires_gil_enabled('incompatible with Free Threading')
diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c
index b76abe1d74c628..64629c5a6da8cd 100644
--- a/Lib/test/test_cext/extension.c
+++ b/Lib/test/test_cext/extension.c
@@ -58,11 +58,24 @@ _testcext_exec(
return 0;
}
+// Converting from function pointer to void* has undefined behavior, but
+// works on all known platforms, and CPython's module and type slots currently
+// need it.
+// (GCC doesn't have a narrower category for this than -Wpedantic.)
+_Py_COMP_DIAG_PUSH
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wpedantic"
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wpedantic"
+#endif
+
static PyModuleDef_Slot _testcext_slots[] = {
{Py_mod_exec, (void*)_testcext_exec},
{0, NULL}
};
+_Py_COMP_DIAG_POP
+
PyDoc_STRVAR(_testcext_doc, "C test extension.");
diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py
index e97749b45ea6f3..1275282983f7ff 100644
--- a/Lib/test/test_cext/setup.py
+++ b/Lib/test/test_cext/setup.py
@@ -21,6 +21,9 @@
# gh-120593: Check the 'const' qualifier
'-Wcast-qual',
+
+ # Ask for strict(er) compliance with the standard
+ '-pedantic-errors',
]
if not support.Py_GIL_DISABLED:
CFLAGS.append(
diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py
index d5195227308fec..7d56cb8b68620a 100644
--- a/Lib/test/test_cppext/__init__.py
+++ b/Lib/test/test_cppext/__init__.py
@@ -29,6 +29,9 @@ def test_build(self):
self.check_build('_testcppext')
def test_build_cpp03(self):
+ # In public docs, we say C API is compatible with C++11. However,
+ # in practice we do maintain C++03 compatibility in public headers.
+ # Please ask the C API WG before adding a new C++11-only feature.
self.check_build('_testcpp03ext', std='c++03')
@unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11")
diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp
index 500d5918145c00..5b3571b295bec3 100644
--- a/Lib/test/test_cppext/extension.cpp
+++ b/Lib/test/test_cppext/extension.cpp
@@ -161,11 +161,24 @@ class VirtualPyObject : public PyObject {
int VirtualPyObject::instance_count = 0;
+// Converting from function pointer to void* has undefined behavior, but
+// works on all known platforms, and CPython's module and type slots currently
+// need it.
+// (GCC doesn't have a narrower category for this than -Wpedantic.)
+_Py_COMP_DIAG_PUSH
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wpedantic"
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wpedantic"
+#endif
+
PyType_Slot VirtualPyObject_Slots[] = {
{Py_tp_free, (void*)VirtualPyObject::dealloc},
{0, _Py_NULL},
};
+_Py_COMP_DIAG_POP
+
PyType_Spec VirtualPyObject_Spec = {
/* .name */ STR(MODULE_NAME) ".VirtualPyObject",
/* .basicsize */ sizeof(VirtualPyObject),
@@ -241,11 +254,20 @@ _testcppext_exec(PyObject *module)
return 0;
}
+// Need to ignore "-Wpedantic" warnings; see VirtualPyObject_Slots above
+_Py_COMP_DIAG_PUSH
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wpedantic"
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wpedantic"
+#endif
+
static PyModuleDef_Slot _testcppext_slots[] = {
{Py_mod_exec, reinterpret_cast<void*>(_testcppext_exec)},
{0, _Py_NULL}
};
+_Py_COMP_DIAG_POP
PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");
diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py
index 019ff18446a2eb..aa09c7bc8cb170 100644
--- a/Lib/test/test_cppext/setup.py
+++ b/Lib/test/test_cppext/setup.py
@@ -18,6 +18,14 @@
# a C++ extension using the Python C API does not emit C++ compiler
# warnings
'-Werror',
+
+ # Ask for strict(er) compliance with the standard.
+ '-pedantic-errors',
+
+ # But allow C++11 features for -std=C++03. We use:
+ # - `long long` (-Wno-c++11-long-long)
+ # - comma at end of `enum` lists (no narrower GCC option exists)
+ '-Wno-c++11-extensions',
]
else:
# MSVC compiler flags
1
0

[3.13] gh-124878: Add temporary TSAN suppression for free_threadstate (gh-130602) (gh-130687)
by colesbury Feb. 28, 2025
by colesbury Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/137595f2a184b3f7aba739c5dcfb2fb5c8…
commit: 137595f2a184b3f7aba739c5dcfb2fb5c8cba8a0
branch: 3.13
author: Sam Gross <colesbury(a)gmail.com>
committer: colesbury <colesbury(a)gmail.com>
date: 2025-02-28T14:53:35Z
summary:
[3.13] gh-124878: Add temporary TSAN suppression for free_threadstate (gh-130602) (gh-130687)
The race condition with `free_threadstate` and daemon threads exists in
both the free threading and default builds. We were missing a
suppression in the default build.
(cherry picked from commit cc17307faaa248535c65f6a7668e06dc8ef04575)
files:
M Tools/tsan/suppressions.txt
M Tools/tsan/suppressions_free_threading.txt
diff --git a/Tools/tsan/suppressions.txt b/Tools/tsan/suppressions.txt
index 22ba9d6ba2ab4d..de89a41f420235 100644
--- a/Tools/tsan/suppressions.txt
+++ b/Tools/tsan/suppressions.txt
@@ -3,5 +3,8 @@
race:get_allocator_unlocked
race:set_allocator_unlocked
+# gh-124878: race condition when interpreter finalized while daemon thread runs
+race:free_threadstate
+
# https://gist.github.com/mpage/daaf32b39180c1989572957b943eb665
thread:pthread_create
diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt
index 5ba0a81ac71c3d..da30d7020d1202 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -17,7 +17,8 @@ race:set_allocator_unlocked
# https://gist.github.com/swtaarrs/8e0e365e1d9cecece3269a2fb2f2b8b8
race:sock_recv_impl
-# https://gist.github.com/swtaarrs/08dfe7883b4c975c31ecb39388987a67
+
+# gh-124878: race condition when interpreter finalized while daemon thread runs
race:free_threadstate
1
0

gh-124878: Add temporary TSAN suppression for free_threadstate (gh-130602)
by colesbury Feb. 28, 2025
by colesbury Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/cc17307faaa248535c65f6a7668e06dc8e…
commit: cc17307faaa248535c65f6a7668e06dc8ef04575
branch: main
author: Sam Gross <colesbury(a)gmail.com>
committer: colesbury <colesbury(a)gmail.com>
date: 2025-02-28T09:27:51-05:00
summary:
gh-124878: Add temporary TSAN suppression for free_threadstate (gh-130602)
The race condition with `free_threadstate` and daemon threads exists in
both the free threading and default builds. We were missing a
suppression in the default build.
files:
M Tools/tsan/suppressions.txt
M Tools/tsan/suppressions_free_threading.txt
diff --git a/Tools/tsan/suppressions.txt b/Tools/tsan/suppressions.txt
index 6bda5ecd570889..c70b0ddca059c2 100644
--- a/Tools/tsan/suppressions.txt
+++ b/Tools/tsan/suppressions.txt
@@ -1,5 +1,8 @@
# This file contains suppressions for the default (with GIL) build.
# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
+# gh-124878: race condition when interpreter finalized while daemon thread runs
+race:free_threadstate
+
# https://gist.github.com/mpage/daaf32b39180c1989572957b943eb665
thread:pthread_create
diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt
index 3354b5756811c9..c32c43db19cb96 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -10,7 +10,7 @@
# These entries are for warnings that trigger in a library function, as called
# by a CPython function.
-# https://gist.github.com/swtaarrs/08dfe7883b4c975c31ecb39388987a67
+# gh-124878: race condition when interpreter finalized while daemon thread runs
race:free_threadstate
# These warnings trigger directly in a CPython function.
1
0

gh-130605: Use relaxed atomics to set the GIL switch interval (gh-130654)
by colesbury Feb. 28, 2025
by colesbury Feb. 28, 2025
Feb. 28, 2025
https://github.com/python/cpython/commit/038e4d606bdc3e38f74514ae3ddfdc7a48…
commit: 038e4d606bdc3e38f74514ae3ddfdc7a48b7b19e
branch: main
author: Sam Gross <colesbury(a)gmail.com>
committer: colesbury <colesbury(a)gmail.com>
date: 2025-02-28T09:27:18-05:00
summary:
gh-130605: Use relaxed atomics to set the GIL switch interval (gh-130654)
The interval may be concurrently read by a thread attempting to acquire
the GIL.
files:
M Python/ceval_gil.c
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index 416eec01052224..7a3cd8d8044739 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -325,7 +325,10 @@ take_gil(PyThreadState *tstate)
while (_Py_atomic_load_int_relaxed(&gil->locked)) {
unsigned long saved_switchnum = gil->switch_number;
- unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
+ unsigned long interval = _Py_atomic_load_ulong_relaxed(&gil->interval);
+ if (interval < 1) {
+ interval = 1;
+ }
int timed_out = 0;
COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
@@ -420,7 +423,7 @@ void _PyEval_SetSwitchInterval(unsigned long microseconds)
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _gil_runtime_state *gil = interp->ceval.gil;
assert(gil != NULL);
- gil->interval = microseconds;
+ _Py_atomic_store_ulong_relaxed(&gil->interval, microseconds);
}
unsigned long _PyEval_GetSwitchInterval(void)
@@ -428,7 +431,7 @@ unsigned long _PyEval_GetSwitchInterval(void)
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _gil_runtime_state *gil = interp->ceval.gil;
assert(gil != NULL);
- return gil->interval;
+ return _Py_atomic_load_ulong_relaxed(&gil->interval);
}
1
0