From 8de72765c7953d81ee1ee157fd6f757a2f37ce50 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 20 Mar 2025 15:50:15 +0000 Subject: [PATCH 01/16] add CONSTANT_BUILTIN_ALL/ANY/TUPLE oparg for LOAD_COMMON_CONST --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_opcode_utils.h | 5 ++++- Include/internal/pycore_uop_metadata.h | 2 +- Lib/opcode.py | 4 +++- Python/bytecodes.c | 15 +++++++++++-- Python/executor_cases.c.h | 27 +++++++++++++++++++++-- Python/generated_cases.c.h | 27 +++++++++++++++++++++-- 7 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 152238d21e5da7..45e499dc980880 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2150,7 +2150,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [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 | 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_COMMON_CONSTANT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_IMMORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_MORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, diff --git a/Include/internal/pycore_opcode_utils.h b/Include/internal/pycore_opcode_utils.h index 0872231d1f2d11..bdf8fff743b65f 100644 --- a/Include/internal/pycore_opcode_utils.h +++ b/Include/internal/pycore_opcode_utils.h @@ -67,7 +67,10 @@ extern "C" { /* Values used as the oparg for LOAD_COMMON_CONSTANT */ #define CONSTANT_ASSERTIONERROR 0 #define CONSTANT_NOTIMPLEMENTEDERROR 1 -#define NUM_COMMON_CONSTANTS 2 +#define CONSTANT_BUILTIN_TUPLE 2 +#define CONSTANT_BUILTIN_ALL 3 +#define CONSTANT_BUILTIN_ANY 4 +#define NUM_COMMON_CONSTANTS 5 /* Values used in the oparg for RESUME */ #define RESUME_AT_FUNC_START 0 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 7f52352913a4ad..cefe450817a63f 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -107,7 +107,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SEND_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_YIELD_VALUE] = HAS_ARG_FLAG, [_POP_EXCEPT] = HAS_ESCAPES_FLAG, - [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG, + [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_BUILD_CLASS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_DELETE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, diff --git a/Lib/opcode.py b/Lib/opcode.py index ea7c27698dd338..4b5b285394174f 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -9,6 +9,7 @@ "HAVE_ARGUMENT", "EXTENDED_ARG", "hasarg", "hasconst", "hasname", "hasjump", "hasjrel", "hasjabs", "hasfree", "haslocal", "hasexc"] +import builtins import _opcode from _opcode import stack_effect @@ -38,7 +39,8 @@ _intrinsic_1_descs = _opcode.get_intrinsic1_descs() _intrinsic_2_descs = _opcode.get_intrinsic2_descs() _special_method_names = _opcode.get_special_method_names() -_common_constants = [AssertionError, NotImplementedError] +_common_constants = [AssertionError, NotImplementedError, + builtins.tuple, builtins.all, builtins.any] _nb_ops = _opcode.get_nb_ops() hascompare = [opmap["COMPARE_OP"]] diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 66546080b1f5fe..d92005f6c1619e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1409,10 +1409,21 @@ dummy_func( if (oparg == CONSTANT_ASSERTIONERROR) { val = PyExc_AssertionError; } - else { - assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR); + else if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { val = PyExc_NotImplementedError; } + else if (oparg == CONSTANT_BUILTIN_TUPLE) { + val = (PyObject*)&PyTuple_Type; + } + else if (oparg == CONSTANT_BUILTIN_ALL) { + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + } + else if (oparg == CONSTANT_BUILTIN_ANY) { + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + } + else { + Py_UNREACHABLE(); + } value = PyStackRef_FromPyObjectImmortal(val); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ff9f33b6db0187..678e4ccf1e340e 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1908,8 +1908,31 @@ val = PyExc_AssertionError; } else { - assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR); - val = PyExc_NotImplementedError; + if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { + val = PyExc_NotImplementedError; + } + else { + if (oparg == CONSTANT_BUILTIN_TUPLE) { + val = (PyObject*)&PyTuple_Type; + } + else { + if (oparg == CONSTANT_BUILTIN_ALL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + if (oparg == CONSTANT_BUILTIN_ANY) { + _PyFrame_SetStackPointer(frame, stack_pointer); + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + Py_UNREACHABLE(); + } + } + } + } } value = PyStackRef_FromPyObjectImmortal(val); stack_pointer[0] = value; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 558b0b48ceaa71..270d6d136f8237 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8754,8 +8754,31 @@ val = PyExc_AssertionError; } else { - assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR); - val = PyExc_NotImplementedError; + if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { + val = PyExc_NotImplementedError; + } + else { + if (oparg == CONSTANT_BUILTIN_TUPLE) { + val = (PyObject*)&PyTuple_Type; + } + else { + if (oparg == CONSTANT_BUILTIN_ALL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + if (oparg == CONSTANT_BUILTIN_ANY) { + _PyFrame_SetStackPointer(frame, stack_pointer); + val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + Py_UNREACHABLE(); + } + } + } + } } value = PyStackRef_FromPyObjectImmortal(val); stack_pointer[0] = value; From df6bccf217e5f94646bf83f349dbcfd2b9d717c5 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Sat, 22 Mar 2025 12:39:54 +0000 Subject: [PATCH 02/16] implement all/any/tuple optimization --- .../pycore_global_objects_fini_generated.h | 2 + Include/internal/pycore_global_strings.h | 2 + Include/internal/pycore_magic_number.h | 2 +- .../internal/pycore_runtime_init_generated.h | 2 + .../internal/pycore_unicodeobject_generated.h | 8 ++ Lib/test/test_builtin.py | 42 +++++++ Python/bytecodes.c | 6 +- Python/codegen.c | 110 +++++++++++++++++- Python/compile.c | 6 + Python/executor_cases.c.h | 6 +- Python/generated_cases.c.h | 6 +- 11 files changed, 179 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 90214a314031d1..70e2bc94ad67ae 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -787,7 +787,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(aggregate_class)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(alias)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(align)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(all)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(allow_code)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(any)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(append)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arg)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argdefs)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 5056128dc97ca0..1ecc4f7c6445da 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -278,7 +278,9 @@ struct _Py_global_strings { STRUCT_FOR_ID(aggregate_class) STRUCT_FOR_ID(alias) STRUCT_FOR_ID(align) + STRUCT_FOR_ID(all) STRUCT_FOR_ID(allow_code) + STRUCT_FOR_ID(any) STRUCT_FOR_ID(append) STRUCT_FOR_ID(arg) STRUCT_FOR_ID(argdefs) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 839a86e5830e78..397acae01e3d5b 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -284,7 +284,7 @@ PC/launcher.c must also be updated. */ -#define PYC_MAGIC_NUMBER 3619 +#define PYC_MAGIC_NUMBER 3620 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes (little-endian) and then appending b'\r\n'. */ #define PYC_MAGIC_NUMBER_TOKEN \ diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 4f928cc050bf8e..375599b4a49c0b 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -785,7 +785,9 @@ extern "C" { INIT_ID(aggregate_class), \ INIT_ID(alias), \ INIT_ID(align), \ + INIT_ID(all), \ INIT_ID(allow_code), \ + INIT_ID(any), \ INIT_ID(append), \ INIT_ID(arg), \ INIT_ID(argdefs), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 5b78d038fc1192..447532e532f404 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -900,10 +900,18 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(all); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(allow_code); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(any); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(append); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 90998ffce6d4e1..057554e9a91bbd 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -225,6 +225,8 @@ def test_all(self): self.assertEqual(all(x > 42 for x in S), True) S = [50, 40, 60] self.assertEqual(all(x > 42 for x in S), False) + S = [50, 40, 60, TestFailingBool()] + self.assertEqual(all(x > 42 for x in S), False) def test_any(self): self.assertEqual(any([None, None, None]), False) @@ -238,9 +240,49 @@ def test_any(self): self.assertEqual(any([1, TestFailingBool()]), True) # Short-circuit S = [40, 60, 30] self.assertEqual(any(x > 42 for x in S), True) + S = [40, 60, 30, TestFailingBool()] + self.assertEqual(any(x > 42 for x in S), True) S = [10, 20, 30] self.assertEqual(any(x > 42 for x in S), False) + def test_all_any_tuple_optimization(self): + def f_all(): + return all(x-2 for x in [1,2,3]) + + def f_any(): + return any(x-1 for x in [1,2,3]) + + def f_tuple(): + return tuple(2*x for x in [1,2,3]) + + funcs = [f_all, f_any, f_tuple] + + for f in funcs: + # check that generator code object is not duplicated + code_objs = [c for c in f.__code__.co_consts if isinstance(c, type(f.__code__))] + self.assertEqual(len(code_objs), 1) + + + # check the overriding the builtins works + bltin_outputs = [f() for f in funcs] + + global all, any, tuple + saved = all, any, tuple + try: + all = lambda x : "all" + any = lambda x : "any" + tuple = lambda x : "tuple" + + return [f() for f in funcs] + finally: + all, any, tuple = saved + + overridden_outputs = run_with_overrides() + + for f, out1, out2 in zip(funcs, bltin_outputs, overridden_outputs): + with self.subTest(func = f.__name__): + self.assertNotEqual(out1, out2) + def test_ascii(self): self.assertEqual(ascii(''), '\'\'') self.assertEqual(ascii(0), '0') diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d92005f6c1619e..f8a01d0882b358 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1416,15 +1416,15 @@ dummy_func( val = (PyObject*)&PyTuple_Type; } else if (oparg == CONSTANT_BUILTIN_ALL) { - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); } else if (oparg == CONSTANT_BUILTIN_ANY) { - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); } else { Py_UNREACHABLE(); } - value = PyStackRef_FromPyObjectImmortal(val); + value = PyStackRef_FromPyObjectNew(val); } inst(LOAD_BUILD_CLASS, ( -- bc)) { diff --git a/Python/codegen.c b/Python/codegen.c index e1f647451f7002..a657f548112a9f 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3723,6 +3723,105 @@ update_start_location_to_match_attr(compiler *c, location loc, return loc; } +static int push_inlined_comprehension_state(compiler *c, location loc, + PySTEntryObject *comp, + _PyCompile_InlinedComprehensionState *state); +static int pop_inlined_comprehension_state(compiler *c, location loc, + _PyCompile_InlinedComprehensionState *state); + +PyObject *_PyCompile_Filename(compiler *c); + +static int +maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) +{ + asdl_expr_seq *args = e->v.Call.args; + asdl_keyword_seq *kwds = e->v.Call.keywords; + expr_ty func = e->v.Call.func; + + if (! (func->kind == Name_kind && + asdl_seq_LEN(args) == 1 && + asdl_seq_LEN(kwds) == 0 && + asdl_seq_GET(args, 0)->kind == GeneratorExp_kind)) + { + return 0; + } + + expr_ty generator_exp = asdl_seq_GET(args, 0); + + if (asdl_seq_LEN(generator_exp->v.GeneratorExp.generators) != 1) { + return 0; + } + + location loc = LOC(func); + + int optimized = 0; + NEW_JUMP_TARGET_LABEL(c, skip_optimization); + + int const_oparg = -1; + PyObject *initial_res; + int continue_jump_opcode = -1; + if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "all")) { + const_oparg = CONSTANT_BUILTIN_ALL; + initial_res = Py_True; + continue_jump_opcode = POP_JUMP_IF_TRUE; + } + else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "any")) { + const_oparg = CONSTANT_BUILTIN_ANY; + initial_res = Py_False; + continue_jump_opcode = POP_JUMP_IF_FALSE; + } + else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "tuple")) { + const_oparg = CONSTANT_BUILTIN_TUPLE; + } + if (const_oparg != -1) { + RETURN_IF_ERROR(codegen_nameop(c, loc, func->v.Name.id, Load)); + ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, const_oparg); + ADDOP_COMPARE(c, loc, Is); + ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, skip_optimization); + + if (const_oparg == CONSTANT_BUILTIN_TUPLE) { + ADDOP_I(c, loc, BUILD_LIST, 0); + } + else { + ADDOP_LOAD_CONST(c, loc, initial_res); + } + VISIT(c, expr, generator_exp); + + NEW_JUMP_TARGET_LABEL(c, loop); + NEW_JUMP_TARGET_LABEL(c, cleanup); + + USE_LABEL(c, loop); + ADDOP_JUMP(c, loc, FOR_ITER, cleanup); + if (const_oparg == CONSTANT_BUILTIN_TUPLE) { + ADDOP_I(c, loc, LIST_APPEND, 2); + ADDOP_JUMP(c, loc, JUMP, loop); + } + else { + ADDOP(c, loc, TO_BOOL); + ADDOP_JUMP(c, loc, continue_jump_opcode, loop); + } + + ADDOP(c, NO_LOCATION, POP_ITER); + if (const_oparg != CONSTANT_BUILTIN_TUPLE) { + ADDOP(c, loc, POP_TOP); + ADDOP_LOAD_CONST(c, loc, initial_res == Py_True ? Py_False : Py_True); + } + ADDOP_JUMP(c, loc, JUMP, end); + + USE_LABEL(c, cleanup); + ADDOP(c, NO_LOCATION, END_FOR); + ADDOP(c, NO_LOCATION, POP_ITER); + if (const_oparg == CONSTANT_BUILTIN_TUPLE) { + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); + } + + optimized = 1; + ADDOP_JUMP(c, loc, JUMP, end); + } + USE_LABEL(c, skip_optimization); + return optimized; +} + // Return 1 if the method call was optimized, 0 if not, and -1 on error. static int maybe_optimize_method_call(compiler *c, expr_ty e) @@ -3829,14 +3928,18 @@ codegen_call(compiler *c, expr_ty e) if (ret == 1) { return SUCCESS; } + NEW_JUMP_TARGET_LABEL(c, skip_normal_call); + RETURN_IF_ERROR(maybe_optimize_function_call(c, e, skip_normal_call)); RETURN_IF_ERROR(check_caller(c, e->v.Call.func)); VISIT(c, expr, e->v.Call.func); location loc = LOC(e->v.Call.func); ADDOP(c, loc, PUSH_NULL); loc = LOC(e); - return codegen_call_helper(c, loc, 0, - e->v.Call.args, - e->v.Call.keywords); + ret = codegen_call_helper(c, loc, 0, + e->v.Call.args, + e->v.Call.keywords); + USE_LABEL(c, skip_normal_call); + return ret; } static int @@ -4547,6 +4650,7 @@ codegen_comprehension(compiler *c, expr_ty e, int type, } ADDOP_I(c, loc, op, 0); + if (is_inlined) { ADDOP_I(c, loc, SWAP, 2); } diff --git a/Python/compile.c b/Python/compile.c index 303d959c9d790c..23751ff159ca9a 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1231,6 +1231,12 @@ _PyCompile_Metadata(compiler *c) return &c->u->u_metadata; } +PyObject * +_PyCompile_Filename(compiler *c) +{ + return c->c_filename; +} + // Merge *obj* with constant cache, without recursion. int _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 678e4ccf1e340e..03129e8ff78ac0 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1918,13 +1918,13 @@ else { if (oparg == CONSTANT_BUILTIN_ALL) { _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); stack_pointer = _PyFrame_GetStackPointer(frame); } else { if (oparg == CONSTANT_BUILTIN_ANY) { _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); stack_pointer = _PyFrame_GetStackPointer(frame); } else { @@ -1934,7 +1934,7 @@ } } } - value = PyStackRef_FromPyObjectImmortal(val); + value = PyStackRef_FromPyObjectNew(val); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 270d6d136f8237..5aa5627436ad10 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8764,13 +8764,13 @@ else { if (oparg == CONSTANT_BUILTIN_ALL) { _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); stack_pointer = _PyFrame_GetStackPointer(frame); } else { if (oparg == CONSTANT_BUILTIN_ANY) { _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); stack_pointer = _PyFrame_GetStackPointer(frame); } else { @@ -8780,7 +8780,7 @@ } } } - value = PyStackRef_FromPyObjectImmortal(val); + value = PyStackRef_FromPyObjectNew(val); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); From 045cdc90083210fed77cf19fd50660f25b86b947 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 20:38:11 +0000 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst new file mode 100644 index 00000000000000..6cbe00cc42b2b6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst @@ -0,0 +1 @@ +Compiler emits optimised code for builtin any/all/tuple calls over a generator expression. From d142f19eece8a5f6b015bb8a8fd69f363f7ae36d Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 25 Mar 2025 20:44:55 +0000 Subject: [PATCH 04/16] add comment --- Include/internal/pycore_magic_number.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Include/internal/pycore_magic_number.h b/Include/internal/pycore_magic_number.h index 397acae01e3d5b..edcc52b337364f 100644 --- a/Include/internal/pycore_magic_number.h +++ b/Include/internal/pycore_magic_number.h @@ -272,6 +272,7 @@ Known values: Python 3.14a6 3617 (Branch monitoring for async for loops) Python 3.14a6 3618 (Add oparg to END_ASYNC_FOR) Python 3.14a6 3619 (Renumber RESUME opcode from 149 to 128) + Python 3.14a6 3620 (Optimize bytecode for all/any/tuple called on a genexp) Python 3.15 will start with 3650 From cb4eddbf2ca336f52d320733b4251c9e9ea6e063 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 25 Mar 2025 20:46:45 +0000 Subject: [PATCH 05/16] fix warning --- Python/codegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/codegen.c b/Python/codegen.c index a657f548112a9f..bc1ad7281786c3 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3758,7 +3758,7 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) NEW_JUMP_TARGET_LABEL(c, skip_optimization); int const_oparg = -1; - PyObject *initial_res; + PyObject *initial_res = NULL; int continue_jump_opcode = -1; if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "all")) { const_oparg = CONSTANT_BUILTIN_ALL; From c3bb23204ccec5f2bdeac40bdd2927ad474c6d2a Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 25 Mar 2025 20:49:51 +0000 Subject: [PATCH 06/16] fixup --- Python/codegen.c | 9 --------- Python/compile.c | 6 ------ 2 files changed, 15 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index bc1ad7281786c3..0935c941f3f610 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3723,14 +3723,6 @@ update_start_location_to_match_attr(compiler *c, location loc, return loc; } -static int push_inlined_comprehension_state(compiler *c, location loc, - PySTEntryObject *comp, - _PyCompile_InlinedComprehensionState *state); -static int pop_inlined_comprehension_state(compiler *c, location loc, - _PyCompile_InlinedComprehensionState *state); - -PyObject *_PyCompile_Filename(compiler *c); - static int maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) { @@ -4650,7 +4642,6 @@ codegen_comprehension(compiler *c, expr_ty e, int type, } ADDOP_I(c, loc, op, 0); - if (is_inlined) { ADDOP_I(c, loc, SWAP, 2); } diff --git a/Python/compile.c b/Python/compile.c index 23751ff159ca9a..303d959c9d790c 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1231,12 +1231,6 @@ _PyCompile_Metadata(compiler *c) return &c->u->u_metadata; } -PyObject * -_PyCompile_Filename(compiler *c) -{ - return c->c_filename; -} - // Merge *obj* with constant cache, without recursion. int _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj) From a217fc4c9c6e305ae69cee51890d7a0a9c0c6017 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 25 Mar 2025 22:14:54 +0000 Subject: [PATCH 07/16] use interp->callable_cache --- Include/internal/pycore_interp_structs.h | 2 ++ Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/test_builtin.py | 22 ++++++++++++++++------ Python/bytecodes.c | 15 +++++++-------- Python/executor_cases.c.h | 21 +++++++++------------ Python/generated_cases.c.h | 21 +++++++++------------ Python/pylifecycle.c | 12 ++++++++++++ 8 files changed, 57 insertions(+), 40 deletions(-) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index ec788de316fcef..3a9604d74d514b 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -655,6 +655,8 @@ struct _Py_unicode_state { struct callable_cache { PyObject *isinstance; PyObject *len; + PyObject *all; + PyObject *any; PyObject *list_append; PyObject *object__getattribute__; }; diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 45e499dc980880..89fbc7ce375620 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2150,7 +2150,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [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 | 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 | HAS_ESCAPES_FLAG }, + [LOAD_COMMON_CONSTANT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_IMMORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_MORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index cefe450817a63f..5de21b026b0d54 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -107,7 +107,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SEND_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_YIELD_VALUE] = HAS_ARG_FLAG, [_POP_EXCEPT] = HAS_ESCAPES_FLAG, - [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, + [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_BUILD_CLASS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_DELETE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 057554e9a91bbd..b1594519484c0f 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -264,7 +264,6 @@ def f_tuple(): # check the overriding the builtins works - bltin_outputs = [f() for f in funcs] global all, any, tuple saved = all, any, tuple @@ -273,15 +272,26 @@ def f_tuple(): any = lambda x : "any" tuple = lambda x : "tuple" - return [f() for f in funcs] + overridden_outputs = [f() for f in funcs] finally: all, any, tuple = saved - overridden_outputs = run_with_overrides() + self.assertEqual(overridden_outputs, ['all', 'any', 'tuple']) + + # Now repeat, overriding the builtins module as well + saved = all, any, tuple + try: + builtins.all = all = lambda x : "all" + builtins.any = any = lambda x : "any" + builtins.tuple = tuple = lambda x : "tuple" + + overridden_outputs = [f() for f in funcs] + finally: + all, any, tuple = saved + builtins.all, builtins.any, builtins.tuple = saved + + self.assertEqual(overridden_outputs, ['all', 'any', 'tuple']) - for f, out1, out2 in zip(funcs, bltin_outputs, overridden_outputs): - with self.subTest(func = f.__name__): - self.assertNotEqual(out1, out2) def test_ascii(self): self.assertEqual(ascii(''), '\'\'') diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f8a01d0882b358..d8895c69f6fc0a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1405,26 +1405,25 @@ dummy_func( inst(LOAD_COMMON_CONSTANT, ( -- value)) { // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - PyObject *val; if (oparg == CONSTANT_ASSERTIONERROR) { - val = PyExc_AssertionError; + value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); } else if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - val = PyExc_NotImplementedError; + value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); } else if (oparg == CONSTANT_BUILTIN_TUPLE) { - val = (PyObject*)&PyTuple_Type; + value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); } else if (oparg == CONSTANT_BUILTIN_ALL) { - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); } else if (oparg == CONSTANT_BUILTIN_ANY) { - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); } else { - Py_UNREACHABLE(); + _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); + ERROR_IF(true, error); } - value = PyStackRef_FromPyObjectNew(val); } inst(LOAD_BUILD_CLASS, ( -- bc)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 03129e8ff78ac0..fb8bbb24e71736 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1903,38 +1903,35 @@ oparg = CURRENT_OPARG(); // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - PyObject *val; if (oparg == CONSTANT_ASSERTIONERROR) { - val = PyExc_AssertionError; + value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); } else { if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - val = PyExc_NotImplementedError; + value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); } else { if (oparg == CONSTANT_BUILTIN_TUPLE) { - val = (PyObject*)&PyTuple_Type; + value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); } else { if (oparg == CONSTANT_BUILTIN_ALL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); - stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); } else { if (oparg == CONSTANT_BUILTIN_ANY) { - _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); - stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); } else { - Py_UNREACHABLE(); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_ERROR(); } } } } } - value = PyStackRef_FromPyObjectNew(val); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5aa5627436ad10..b0f227f8a48e31 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8749,38 +8749,35 @@ _PyStackRef value; // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - PyObject *val; if (oparg == CONSTANT_ASSERTIONERROR) { - val = PyExc_AssertionError; + value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); } else { if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - val = PyExc_NotImplementedError; + value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); } else { if (oparg == CONSTANT_BUILTIN_TUPLE) { - val = (PyObject*)&PyTuple_Type; + value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); } else { if (oparg == CONSTANT_BUILTIN_ALL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(all)); - stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); } else { if (oparg == CONSTANT_BUILTIN_ANY) { - _PyFrame_SetStackPointer(frame, stack_pointer); - val = PyDict_GetItemWithError(BUILTINS(), &_Py_ID(any)); - stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); } else { - Py_UNREACHABLE(); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); } } } } } - value = PyStackRef_FromPyObjectNew(val); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 0513d614c6e1ef..6945a5a1f03e8d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -794,6 +794,18 @@ pycore_init_builtins(PyThreadState *tstate) } interp->callable_cache.len = len; + PyObject *all = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); + if (!all) { + goto error; + } + interp->callable_cache.all = all; + + PyObject *any = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); + if (!any) { + goto error; + } + interp->callable_cache.any = any; + PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append)); if (list_append == NULL) { goto error; From 9f71f163bd6664bfc9126e2d3fa243110ee34355 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 10:14:26 +0000 Subject: [PATCH 08/16] tweaks from review --- Lib/opcode.py | 2 +- .../2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst | 2 +- Python/codegen.c | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Lib/opcode.py b/Lib/opcode.py index 4b5b285394174f..0e9520b6832499 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -39,7 +39,7 @@ _intrinsic_1_descs = _opcode.get_intrinsic1_descs() _intrinsic_2_descs = _opcode.get_intrinsic2_descs() _special_method_names = _opcode.get_special_method_names() -_common_constants = [AssertionError, NotImplementedError, +_common_constants = [builtins.AssertionError, builtins.NotImplementedError, builtins.tuple, builtins.all, builtins.any] _nb_ops = _opcode.get_nb_ops() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst index 6cbe00cc42b2b6..ac99b2afa81838 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-25-20-38-06.gh-issue-131738.eCb0OQ.rst @@ -1 +1 @@ -Compiler emits optimised code for builtin any/all/tuple calls over a generator expression. +Compiler emits optimized code for builtin any/all/tuple calls over a generator expression. diff --git a/Python/codegen.c b/Python/codegen.c index 0935c941f3f610..6d3e32864ff154 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3738,12 +3738,6 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) return 0; } - expr_ty generator_exp = asdl_seq_GET(args, 0); - - if (asdl_seq_LEN(generator_exp->v.GeneratorExp.generators) != 1) { - return 0; - } - location loc = LOC(func); int optimized = 0; @@ -3777,6 +3771,7 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) else { ADDOP_LOAD_CONST(c, loc, initial_res); } + expr_ty generator_exp = asdl_seq_GET(args, 0); VISIT(c, expr, generator_exp); NEW_JUMP_TARGET_LABEL(c, loop); From 10ef70236e14e266d0a813b5c939fbcf3f895000 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 17:47:20 +0000 Subject: [PATCH 09/16] avoid duplicating name lookup --- Python/codegen.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index 6d3e32864ff154..2d114d8ea0fc7a 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3760,10 +3760,11 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) const_oparg = CONSTANT_BUILTIN_TUPLE; } if (const_oparg != -1) { - RETURN_IF_ERROR(codegen_nameop(c, loc, func->v.Name.id, Load)); + ADDOP_I(c, loc, COPY, 1); // the function ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, const_oparg); ADDOP_COMPARE(c, loc, Is); ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, skip_optimization); + ADDOP(c, loc, POP_TOP); if (const_oparg == CONSTANT_BUILTIN_TUPLE) { ADDOP_I(c, loc, BUILD_LIST, 0); @@ -3916,9 +3917,9 @@ codegen_call(compiler *c, expr_ty e) return SUCCESS; } NEW_JUMP_TARGET_LABEL(c, skip_normal_call); - RETURN_IF_ERROR(maybe_optimize_function_call(c, e, skip_normal_call)); RETURN_IF_ERROR(check_caller(c, e->v.Call.func)); VISIT(c, expr, e->v.Call.func); + RETURN_IF_ERROR(maybe_optimize_function_call(c, e, skip_normal_call)); location loc = LOC(e->v.Call.func); ADDOP(c, loc, PUSH_NULL); loc = LOC(e); From 222ef3b24818c98f871d10e7a321da6e9a90e0ea Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 18:03:40 +0000 Subject: [PATCH 10/16] remove pop --- Python/codegen.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Python/codegen.c b/Python/codegen.c index 2d114d8ea0fc7a..26f0f06ff239aa 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -3769,9 +3769,6 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) if (const_oparg == CONSTANT_BUILTIN_TUPLE) { ADDOP_I(c, loc, BUILD_LIST, 0); } - else { - ADDOP_LOAD_CONST(c, loc, initial_res); - } expr_ty generator_exp = asdl_seq_GET(args, 0); VISIT(c, expr, generator_exp); @@ -3791,7 +3788,6 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) ADDOP(c, NO_LOCATION, POP_ITER); if (const_oparg != CONSTANT_BUILTIN_TUPLE) { - ADDOP(c, loc, POP_TOP); ADDOP_LOAD_CONST(c, loc, initial_res == Py_True ? Py_False : Py_True); } ADDOP_JUMP(c, loc, JUMP, end); @@ -3802,6 +3798,9 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) if (const_oparg == CONSTANT_BUILTIN_TUPLE) { ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); } + else { + ADDOP_LOAD_CONST(c, loc, initial_res); + } optimized = 1; ADDOP_JUMP(c, loc, JUMP, end); From ef78fd11a2180bdc57dacd54552e597a4777e447 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 18:04:53 +0000 Subject: [PATCH 11/16] fix build --- Tools/jit/_targets.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index aa2b56abf446b1..73a4210eee03f6 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -522,7 +522,14 @@ def get_target(host: str) -> _COFF | _ELF | _MachO: args = ["-fms-runtime-lib=dll"] target = _COFF(host, args=args) elif re.fullmatch(r"x86_64-.*-linux-gnu", host): - args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"] + args = [ + # Jump tables generate R_X86_64_32S relocations, which assume a + # signed 32-bit address space: + "-fno-jump-tables", + "-fno-pic", + "-mcmodel=medium", + "-mlarge-data-threshold=0", + ] target = _ELF(host, args=args) else: raise ValueError(host) From e4b3ccdd496e78effcdfa6d31857c54839fce948 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 19:32:22 +0000 Subject: [PATCH 12/16] use table for common consts --- Include/internal/pycore_interp_structs.h | 4 +-- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_opcode_utils.h | 2 -- Include/internal/pycore_uop_metadata.h | 2 +- Objects/genobject.c | 2 ++ Python/bytecodes.c | 21 ++------------- Python/executor_cases.c.h | 31 ++--------------------- Python/generated_cases.c.h | 31 ++--------------------- Python/pylifecycle.c | 12 +++++++-- 9 files changed, 22 insertions(+), 85 deletions(-) diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 3a9604d74d514b..88fd629e043490 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -4,6 +4,7 @@ extern "C" { #endif +#include "pycore_opcode_utils.h" // NUM_COMMON_CONSTANTS #include "pycore_structs.h" #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR #include "pycore_llist.h" @@ -655,8 +656,6 @@ struct _Py_unicode_state { struct callable_cache { PyObject *isinstance; PyObject *len; - PyObject *all; - PyObject *any; PyObject *list_append; PyObject *object__getattribute__; }; @@ -927,6 +926,7 @@ struct _is { struct ast_state ast; struct types_state types; struct callable_cache callable_cache; + PyObject *common_consts[NUM_COMMON_CONSTANTS]; bool jit; struct _PyExecutorObject *executor_list_head; size_t trace_run_counter; diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 89fbc7ce375620..152238d21e5da7 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2150,7 +2150,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [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 | 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 | 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 }, [LOAD_CONST_IMMORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [LOAD_CONST_MORTAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, diff --git a/Include/internal/pycore_opcode_utils.h b/Include/internal/pycore_opcode_utils.h index bdf8fff743b65f..b3056e7bb84c69 100644 --- a/Include/internal/pycore_opcode_utils.h +++ b/Include/internal/pycore_opcode_utils.h @@ -8,8 +8,6 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif -#include "opcode_ids.h" - #define MAX_REAL_OPCODE 254 #define IS_WITHIN_OPCODE_RANGE(opcode) \ diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 5de21b026b0d54..7f52352913a4ad 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -107,7 +107,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SEND_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_YIELD_VALUE] = HAS_ARG_FLAG, [_POP_EXCEPT] = HAS_ESCAPES_FLAG, - [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG, [_LOAD_BUILD_CLASS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_DELETE_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, diff --git a/Objects/genobject.c b/Objects/genobject.c index 005f1a13c4b4c2..796fb2e8baa3f0 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -17,6 +17,8 @@ #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "opcode_ids.h" // RESUME, etc + // Forward declarations static PyObject* gen_close(PyObject *, PyObject *); static PyObject* async_gen_asend_new(PyAsyncGenObject *, PyObject *); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d8895c69f6fc0a..3620f02e469c83 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1405,25 +1405,8 @@ dummy_func( inst(LOAD_COMMON_CONSTANT, ( -- value)) { // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - if (oparg == CONSTANT_ASSERTIONERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); - } - else if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); - } - else if (oparg == CONSTANT_BUILTIN_TUPLE) { - value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); - } - else if (oparg == CONSTANT_BUILTIN_ALL) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); - } - else if (oparg == CONSTANT_BUILTIN_ANY) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); - } - else { - _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); - ERROR_IF(true, error); - } + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); } inst(LOAD_BUILD_CLASS, ( -- bc)) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index fb8bbb24e71736..18f39f9aac1fc7 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1903,35 +1903,8 @@ oparg = CURRENT_OPARG(); // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - if (oparg == CONSTANT_ASSERTIONERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); - } - else { - if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); - } - else { - if (oparg == CONSTANT_BUILTIN_TUPLE) { - value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); - } - else { - if (oparg == CONSTANT_BUILTIN_ALL) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); - } - else { - if (oparg == CONSTANT_BUILTIN_ANY) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - } - } - } - } + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b0f227f8a48e31..662d8e1b9b5037 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8749,35 +8749,8 @@ _PyStackRef value; // Keep in sync with _common_constants in opcode.py // If we ever have more than two constants, use a lookup table - if (oparg == CONSTANT_ASSERTIONERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_AssertionError); - } - else { - if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { - value = PyStackRef_FromPyObjectImmortal(PyExc_NotImplementedError); - } - else { - if (oparg == CONSTANT_BUILTIN_TUPLE) { - value = PyStackRef_FromPyObjectImmortal((PyObject*)&PyTuple_Type); - } - else { - if (oparg == CONSTANT_BUILTIN_ALL) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.all); - } - else { - if (oparg == CONSTANT_BUILTIN_ANY) { - value = PyStackRef_FromPyObjectNew(tstate->interp->callable_cache.any); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_ValueError, "unknown common const"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); - } - } - } - } - } + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 6945a5a1f03e8d..550a04bce45665 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -798,13 +798,21 @@ pycore_init_builtins(PyThreadState *tstate) if (!all) { goto error; } - interp->callable_cache.all = all; PyObject *any = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); if (!any) { goto error; } - interp->callable_cache.any = any; + + interp->common_consts[CONSTANT_ASSERTIONERROR] = (PyObject*)&PyTuple_Type; + interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] = (PyObject*)&PyTuple_Type; + interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject*)&PyTuple_Type; + interp->common_consts[CONSTANT_BUILTIN_ALL] = all; + interp->common_consts[CONSTANT_BUILTIN_ANY] = any; + + for (int i=0; i < NUM_COMMON_CONSTANTS; i++) { + assert(interp->common_consts[i] != NULL); + } PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append)); if (list_append == NULL) { From 7ae52aba9d290c21d2269ea6d6500137c545233d Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 26 Mar 2025 21:39:05 +0000 Subject: [PATCH 13/16] typo --- Python/pylifecycle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 550a04bce45665..fc2b0f37f8d46a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -804,8 +804,8 @@ pycore_init_builtins(PyThreadState *tstate) goto error; } - interp->common_consts[CONSTANT_ASSERTIONERROR] = (PyObject*)&PyTuple_Type; - interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] = (PyObject*)&PyTuple_Type; + interp->common_consts[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError; + interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] = PyExc_NotImplementedError; interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject*)&PyTuple_Type; interp->common_consts[CONSTANT_BUILTIN_ALL] = all; interp->common_consts[CONSTANT_BUILTIN_ANY] = any; From 50dd66b61f199e1805895c6222959d10662ba417 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 26 Mar 2025 22:45:06 +0000 Subject: [PATCH 14/16] Update Python/bytecodes.c --- Python/bytecodes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 1a2941a101a6a5..b3b7441c31569a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1402,7 +1402,6 @@ dummy_func( inst(LOAD_COMMON_CONSTANT, ( -- value)) { // Keep in sync with _common_constants in opcode.py - // If we ever have more than two constants, use a lookup table assert(oparg < NUM_COMMON_CONSTANTS); value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); } From d5071b39f6f241cbc62fd24bb517ea81df276519 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:07:42 +0000 Subject: [PATCH 15/16] Update Tools/jit/_targets.py Co-authored-by: Brandt Bucher --- Tools/jit/_targets.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 96088d87bcfbb2..b3b065652e0bb0 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -537,14 +537,7 @@ def get_target(host: str) -> _COFF | _ELF | _MachO: args = ["-fms-runtime-lib=dll"] target = _COFF(host, args=args) elif re.fullmatch(r"x86_64-.*-linux-gnu", host): - args = [ - # Jump tables generate R_X86_64_32S relocations, which assume a - # signed 32-bit address space: - "-fno-jump-tables", - "-fno-pic", - "-mcmodel=medium", - "-mlarge-data-threshold=0", - ] + args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"] target = _ELF(host, args=args) else: raise ValueError(host) From 44b9e8dadba3b5cc3bf643924230ebf411947ccf Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Fri, 28 Mar 2025 10:09:57 +0000 Subject: [PATCH 16/16] regen-cases --- Python/executor_cases.c.h | 1 - Python/generated_cases.c.h | 1 - 2 files changed, 2 deletions(-) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a6dc291bcf22a1..d5cb4ceee17f5f 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1899,7 +1899,6 @@ _PyStackRef value; oparg = CURRENT_OPARG(); // Keep in sync with _common_constants in opcode.py - // If we ever have more than two constants, use a lookup table assert(oparg < NUM_COMMON_CONSTANTS); value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); stack_pointer[0] = value; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index c183a51ff5045b..80b937cc4567d9 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8722,7 +8722,6 @@ INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); _PyStackRef value; // Keep in sync with _common_constants in opcode.py - // If we ever have more than two constants, use a lookup table assert(oparg < NUM_COMMON_CONSTANTS); value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); stack_pointer[0] = value;