Skip to content

Commit

Permalink
Merge branch 'main' into utf8
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaraditya303 authored Dec 19, 2024
2 parents 5fd952e + 46dc1ba commit 756085e
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/reusable-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
run: |
brew install pkg-config [email protected] xz gdbm tcl-tk@8 make
# Because alternate versions are not symlinked into place by default:
brew link tcl-tk@8
brew link --overwrite tcl-tk@8
- name: Configure CPython
run: |
GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ typedef int (*_py_validate_type)(PyTypeObject *);
// and if the validation is passed, it will set the ``tp_version`` as valid
// tp_version_tag from the ``ty``.
extern int _PyType_Validate(PyTypeObject *ty, _py_validate_type validate, unsigned int *tp_version);
extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject *descriptor, uint32_t tp_version);

#ifdef __cplusplus
}
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ def write(items):
opname = "STORE_SUBSCR_LIST_INT"
self.assert_races_do_not_crash(opname, get_items, read, write)

@requires_specialization
@requires_specialization_ft
def test_unpack_sequence_list(self):
def get_items():
items = []
Expand Down Expand Up @@ -1245,6 +1245,14 @@ def f(o, n):
f(test_obj, 1)
self.assertEqual(test_obj.b, 0)

# gh-127274: BINARY_SUBSCR_GETITEM will only cache __getitem__ methods that
# are deferred. We only defer functions defined at the top-level.
class CGetItem:
def __init__(self, val):
self.val = val
def __getitem__(self, item):
return self.val


class TestSpecializer(TestBase):

Expand Down Expand Up @@ -1520,6 +1528,15 @@ def binary_subscr_str_int():
self.assert_specialized(binary_subscr_str_int, "BINARY_SUBSCR_STR_INT")
self.assert_no_opcode(binary_subscr_str_int, "BINARY_SUBSCR")

def binary_subscr_getitems():
items = [CGetItem(i) for i in range(100)]
for i in range(100):
self.assertEqual(items[i][i], i)

binary_subscr_getitems()
self.assert_specialized(binary_subscr_getitems, "BINARY_SUBSCR_GETITEM")
self.assert_no_opcode(binary_subscr_getitems, "BINARY_SUBSCR")


if __name__ == "__main__":
unittest.main()
25 changes: 25 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5679,6 +5679,31 @@ _PyType_CacheInitForSpecialization(PyHeapTypeObject *type, PyObject *init,
return can_cache;
}

int
_PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject *descriptor, uint32_t tp_version)
{
if (!descriptor || !tp_version) {
return 0;
}
int can_cache;
BEGIN_TYPE_LOCK();
can_cache = ((PyTypeObject*)ht)->tp_version_tag == tp_version;
// This pointer is invalidated by PyType_Modified (see the comment on
// struct _specialization_cache):
PyFunctionObject *func = (PyFunctionObject *)descriptor;
uint32_t version = _PyFunction_GetVersionForCurrentState(func);
can_cache = can_cache && _PyFunction_IsVersionValid(version);
#ifdef Py_GIL_DISABLED
can_cache = can_cache && _PyObject_HasDeferredRefcount(descriptor);
#endif
if (can_cache) {
FT_ATOMIC_STORE_PTR_RELEASE(ht->_spec_cache.getitem, descriptor);
FT_ATOMIC_STORE_UINT32_RELAXED(ht->_spec_cache.getitem_version, version);
}
END_TYPE_LOCK();
return can_cache;
}

static void
set_flags(PyTypeObject *self, unsigned long mask, unsigned long flags)
{
Expand Down
2 changes: 1 addition & 1 deletion Programs/test_frozenmain.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 10 additions & 12 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,26 +865,24 @@ dummy_func(
res = PyStackRef_FromPyObjectSteal(res_o);
}

op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused)) {
op(_BINARY_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) {
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container));
DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE));
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
PyObject *getitem = ht->_spec_cache.getitem;
DEOPT_IF(getitem == NULL);
assert(PyFunction_Check(getitem));
uint32_t cached_version = ht->_spec_cache.getitem_version;
DEOPT_IF(((PyFunctionObject *)getitem)->func_version != cached_version);
PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem);
PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem);
DEOPT_IF(getitem_o == NULL);
assert(PyFunction_Check(getitem_o));
uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version);
DEOPT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version);
PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o);
assert(code->co_argcount == 2);
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize));
getitem = PyStackRef_FromPyObjectNew(getitem_o);
STAT_INC(BINARY_SUBSCR, hit);
}

op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _PyInterpreterFrame* )) {
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container));
PyHeapTypeObject *ht = (PyHeapTypeObject *)tp;
PyObject *getitem = ht->_spec_cache.getitem;
new_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(getitem), 2, frame);
op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _PyInterpreterFrame* )) {
new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame);
new_frame->localsplus[0] = container;
new_frame->localsplus[1] = sub;
INPUTS_DEAD();
Expand Down
32 changes: 18 additions & 14 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 9 additions & 10 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Python/optimizer_bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,10 @@ dummy_func(void) {
GETLOCAL(this_instr->operand0) = res;
}

op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _Py_UOpsAbstractFrame *)) {
op(_BINARY_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) {
(void)container;
(void)sub;
(void)getitem;
new_frame = NULL;
ctx->done = true;
}
Expand Down
16 changes: 12 additions & 4 deletions Python/optimizer_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 756085e

Please sign in to comment.