]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-115999: Specialize `LOAD_SUPER_ATTR` in free-threaded builds (gh-127128)
authorNeil Schemenauer <nas-github@arctrix.com>
Tue, 3 Dec 2024 17:32:26 +0000 (09:32 -0800)
committerGitHub <noreply@github.com>
Tue, 3 Dec 2024 17:32:26 +0000 (09:32 -0800)
Use existing helpers to atomically modify the bytecode.  Add unit tests
to ensure specializing is happening as expected.  Add test_specialize.py
that can be used with ThreadSanitizer to detect data races.
Fix thread safety issue with cell_set_contents().

Lib/test/test_opcache.py
Objects/cellobject.c
Python/bytecodes.c
Python/ceval.c
Python/generated_cases.c.h
Python/specialize.c

index b989b21cd9b3a97b72e0993a764371b0c9fbca76..527114445ac13b0885bbd1732322323dde39b850 100644 (file)
@@ -1249,6 +1249,45 @@ class TestSpecializer(TestBase):
         self.assert_specialized(binary_op_add_unicode, "BINARY_OP_ADD_UNICODE")
         self.assert_no_opcode(binary_op_add_unicode, "BINARY_OP")
 
+    @cpython_only
+    @requires_specialization_ft
+    def test_load_super_attr(self):
+        """Ensure that LOAD_SUPER_ATTR is specialized as expected."""
+
+        class A:
+            def __init__(self):
+                meth = super().__init__
+                super().__init__()
+
+        for _ in range(100):
+            A()
+
+        self.assert_specialized(A.__init__, "LOAD_SUPER_ATTR_ATTR")
+        self.assert_specialized(A.__init__, "LOAD_SUPER_ATTR_METHOD")
+        self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR")
+
+        # Temporarily replace super() with something else.
+        real_super = super
+
+        def fake_super():
+            def init(self):
+                pass
+
+            return init
+
+        # Force unspecialize
+        globals()['super'] = fake_super
+        try:
+            # Should be unspecialized after enough calls.
+            for _ in range(100):
+                A()
+        finally:
+            globals()['super'] = real_super
+
+        # Ensure the specialized instructions are not present
+        self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR_ATTR")
+        self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR_METHOD")
+
     @cpython_only
     @requires_specialization_ft
     def test_contain_op(self):
index 590c8a808576990eee62462654524f2525109adb..4ab9083af5e300ac147071584019c952fdce3da9 100644 (file)
@@ -145,8 +145,9 @@ cell_get_contents(PyObject *self, void *closure)
 static int
 cell_set_contents(PyObject *self, PyObject *obj, void *Py_UNUSED(ignored))
 {
-    PyCellObject *op = _PyCell_CAST(self);
-    Py_XSETREF(op->ob_ref, Py_XNewRef(obj));
+    PyCellObject *cell = _PyCell_CAST(self);
+    Py_XINCREF(obj);
+    PyCell_SetTakeRef((PyCellObject *)cell, obj);
     return 0;
 }
 
index e96674c3502ef1882b6d6b7442475f8adbea9f4f..dd28aae6a3cb6152f8beef1540958f105475266d 100644 (file)
@@ -1946,7 +1946,7 @@ dummy_func(
         };
 
         specializing op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super_st, class_st, unused -- global_super_st, class_st, unused)) {
-            #if ENABLE_SPECIALIZATION
+            #if ENABLE_SPECIALIZATION_FT
             int load_method = oparg & 1;
             if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
                 next_instr = this_instr;
@@ -1955,7 +1955,7 @@ dummy_func(
             }
             OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR);
             ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-            #endif  /* ENABLE_SPECIALIZATION */
+            #endif  /* ENABLE_SPECIALIZATION_FT */
         }
 
         tier1 op(_LOAD_SUPER_ATTR, (global_super_st, class_st, self_st -- attr, null if (oparg & 1))) {
index f9514a6bf25c1b39d7d5be55c53f51830f3e8731..6795a160506231f5a049086d404aec3bac099187 100644 (file)
@@ -28,7 +28,6 @@
 #include "pycore_setobject.h"     // _PySet_Update()
 #include "pycore_sliceobject.h"   // _PyBuildSlice_ConsumeRefs
 #include "pycore_tuple.h"         // _PyTuple_ITEMS()
-#include "pycore_typeobject.h"    // _PySuper_Lookup()
 #include "pycore_uop_ids.h"       // Uops
 #include "pycore_pyerrors.h"
 
index e1f951558de7daacad991e2321f0eb3e281aa79e..c31601f6d82b77a81bd628c42ffac85d99c34f51 100644 (file)
                 global_super_st = stack_pointer[-3];
                 uint16_t counter = read_u16(&this_instr[1].cache);
                 (void)counter;
-                #if ENABLE_SPECIALIZATION
+                #if ENABLE_SPECIALIZATION_FT
                 int load_method = oparg & 1;
                 if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
                     next_instr = this_instr;
                 }
                 OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR);
                 ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-                #endif  /* ENABLE_SPECIALIZATION */
+                #endif  /* ENABLE_SPECIALIZATION_FT */
             }
             // _LOAD_SUPER_ATTR
             {
index ba13b02a29b133ac23b64a175d4d322a8567687f..0fe4e7904de9f8286b83eed9e7e9cc985e8f96a7 100644 (file)
@@ -794,9 +794,8 @@ _Py_Specialize_LoadSuperAttr(_PyStackRef global_super_st, _PyStackRef cls_st, _P
     PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st);
     PyObject *cls = PyStackRef_AsPyObjectBorrow(cls_st);
 
-    assert(ENABLE_SPECIALIZATION);
+    assert(ENABLE_SPECIALIZATION_FT);
     assert(_PyOpcode_Caches[LOAD_SUPER_ATTR] == INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR);
-    _PySuperAttrCache *cache = (_PySuperAttrCache *)(instr + 1);
     if (global_super != (PyObject *)&PySuper_Type) {
         SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_SHADOWED);
         goto fail;
@@ -805,19 +804,11 @@ _Py_Specialize_LoadSuperAttr(_PyStackRef global_super_st, _PyStackRef cls_st, _P
         SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_BAD_CLASS);
         goto fail;
     }
-    instr->op.code = load_method ? LOAD_SUPER_ATTR_METHOD : LOAD_SUPER_ATTR_ATTR;
-    goto success;
-
-fail:
-    STAT_INC(LOAD_SUPER_ATTR, failure);
-    assert(!PyErr_Occurred());
-    instr->op.code = LOAD_SUPER_ATTR;
-    cache->counter = adaptive_counter_backoff(cache->counter);
+    uint8_t load_code = load_method ? LOAD_SUPER_ATTR_METHOD : LOAD_SUPER_ATTR_ATTR;
+    specialize(instr, load_code);
     return;
-success:
-    STAT_INC(LOAD_SUPER_ATTR, success);
-    assert(!PyErr_Occurred());
-    cache->counter = adaptive_counter_cooldown();
+fail:
+    unspecialize(instr);
 }
 
 typedef enum {