]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148871: Add CONSTANT_EMPTY_TUPLE to LOAD_COMMON_CONSTANT (GH-149688)
authorPieter Eendebak <pieter.eendebak@gmail.com>
Thu, 21 May 2026 14:54:46 +0000 (16:54 +0200)
committerGitHub <noreply@github.com>
Thu, 21 May 2026 14:54:46 +0000 (15:54 +0100)
Include/internal/pycore_magic_number.h
Include/internal/pycore_opcode_utils.h
Lib/dis.py
Lib/opcode.py
Lib/test/test_peepholer.py
Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst [new file with mode: 0644]
Python/flowgraph.c
Python/pylifecycle.c

index 0f1af8ba33886528c2782a6f7ed373cf6b986fc8..6a8fa124ba38a7c46435384ef071dd743dd3605b 100644 (file)
@@ -298,6 +298,7 @@ Known values:
     Python 3.15a8 3665 (Add FOR_ITER_VIRTUAL and GET_ITER specializations)
     Python 3.15b1 3666 (Add SEND_VIRTUAL and SEND_ASYNC_GEN specializations)
     Python 3.16a0 3700 (Initial version)
+    Python 3.16a0 3701 (Add CONSTANT_EMPTY_TUPLE to LOAD_COMMON_CONSTANT)
 
 
     Python 3.17 will start with 3750
@@ -311,7 +312,7 @@ PC/launcher.c must also be updated.
 
 */
 
-#define PYC_MAGIC_NUMBER 3700
+#define PYC_MAGIC_NUMBER 3701
 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
    (little-endian) and then appending b'\r\n'. */
 #define PYC_MAGIC_NUMBER_TOKEN \
index b20718344b39981449a8d1e766f09586a33821fc..7067b48ec22cb3c5cb33e4b5acf2d16a513edae1 100644 (file)
@@ -81,7 +81,8 @@ extern "C" {
 #define CONSTANT_FALSE 10
 #define CONSTANT_MINUS_ONE 11
 #define CONSTANT_BUILTIN_FROZENSET 12
-#define NUM_COMMON_CONSTANTS 13
+#define CONSTANT_EMPTY_TUPLE 13
+#define NUM_COMMON_CONSTANTS 14
 
 /* Values used in the oparg for RESUME */
 #define RESUME_AT_FUNC_START 0
index d60507ae47345330d0aeedc24c22e57339d73606..cb32a4c0c7d30373bd4a3da9c54f4a6fa717c11f 100644 (file)
@@ -698,8 +698,8 @@ def _get_const_value(op, arg, co_consts):
     if op == LOAD_SMALL_INT:
         return arg
     if op == LOAD_COMMON_CONSTANT:
-        # Opargs 0-6 are callables; 7-11 are literal values.
-        if 7 <= arg <= 11:
+        # Opargs 0-6 and 12 are callables; 7-11 and 13 are literal values.
+        if 7 <= arg <= 11 or arg == 13:
             return _common_constants[arg]
         return UNKNOWN
     argval = UNKNOWN
index bb7824da70e8e5a06e07410c70952e650ce6716d..750d83b2c87af5889c573764788d564fd7fdb13a 100644 (file)
@@ -44,7 +44,7 @@ _common_constants = [builtins.AssertionError, builtins.NotImplementedError,
                      builtins.set,
                      # Append-only — must match CONSTANT_* in
                      # Include/internal/pycore_opcode_utils.h.
-                     None, "", True, False, -1, builtins.frozenset]
+                     None, "", True, False, -1, builtins.frozenset, ()]
 _nb_ops = _opcode.get_nb_ops()
 
 hascompare = [opmap["COMPARE_OP"]]
index 61c4ec9b7d5b4a7e337414b806662e2f5131b10a..28748009f731bc13408db58059621529edc36c68 100644 (file)
@@ -1307,10 +1307,10 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
             ('RETURN_VALUE', None, 0),
         ]
         after = [
-            ('LOAD_CONST', 0, 0),
+            ('LOAD_COMMON_CONSTANT', opcode._common_constants.index(()), 0),
             ('RETURN_VALUE', None, 0),
         ]
-        self.cfg_optimization_test(before, after, consts=[], expected_consts=[()])
+        self.cfg_optimization_test(before, after, consts=[], expected_consts=[])
 
     def test_fold_tuple_of_constants(self):
         before = [
@@ -1365,10 +1365,10 @@ class DirectCfgOptimizerTests(CfgOptimizationTestCase):
             ('RETURN_VALUE', None, 0)
         ]
         after = [
-            ('LOAD_CONST', 0, 0),
+            ('LOAD_COMMON_CONSTANT', opcode._common_constants.index(()), 0),
             ('RETURN_VALUE', None, 0)
         ]
-        self.cfg_optimization_test(before, after, consts=[], expected_consts=[()])
+        self.cfg_optimization_test(before, after, consts=[], expected_consts=[])
 
         # multiple BUILD_LIST 0: ([], 1, [], 2)
         same = [
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst
new file mode 100644 (file)
index 0000000..506e369
--- /dev/null
@@ -0,0 +1,3 @@
+The empty tuple ``()`` is now loaded via :opcode:`LOAD_COMMON_CONSTANT`
+instead of :opcode:`LOAD_CONST`, removing it from per-code-object
+:attr:`~codeobject.co_consts` tuples.
index 224426b7aa44bc2b991192ef9a8df2cad109834a..6e3e5378cfbf155b8925cdc5b00cc77799003da5 100644 (file)
@@ -1468,6 +1468,10 @@ maybe_instr_make_load_common_const(cfg_instr *instr, PyObject *newconst)
              && PyUnicode_GET_LENGTH(newconst) == 0) {
         oparg = CONSTANT_EMPTY_STR;
     }
+    else if (PyTuple_CheckExact(newconst)
+             && PyTuple_GET_SIZE(newconst) == 0) {
+        oparg = CONSTANT_EMPTY_TUPLE;
+    }
     else if (PyLong_CheckExact(newconst)) {
         int overflow;
         long val = PyLong_AsLongAndOverflow(newconst, &overflow);
index bbf2ce4f5eee7dc663c8f815a97ff438ac4547df..a17c3baca3d006c248246303fa4f0d8fa7ae3bd7 100644 (file)
@@ -893,6 +893,8 @@ pycore_init_builtins(PyThreadState *tstate)
     interp->common_consts[CONSTANT_MINUS_ONE] =
         (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1];
     interp->common_consts[CONSTANT_BUILTIN_FROZENSET] = (PyObject *)&PyFrozenSet_Type;
+    interp->common_consts[CONSTANT_EMPTY_TUPLE] =
+        Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_TUPLE);
     for (int i = 0; i < NUM_COMMON_CONSTANTS; i++) {
         assert(interp->common_consts[i] != NULL);
     }