From 62f0b7a8257628792ddc1a1867c9306de7a04a28 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:21:51 +0100 Subject: [PATCH 1/5] Make opcode targets table dynamic --- Python/ceval.c | 2 ++ Python/opcode_targets.h | 2 +- Tools/cases_generator/target_generator.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 578c5d2a8b1420..e9e6e065b8b881 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1040,6 +1040,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int return NULL; } + void **opcode_targets = opcode_targets_table; + /* Local "register" variables. * These are cached values from the frame and code object. */ _Py_CODEUNIT *next_instr; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 1d6dcddab4b12d..95ef1725e5cdef 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -1,5 +1,5 @@ #if !Py_TAIL_CALL_INTERP -static void *opcode_targets[256] = { +static void *opcode_targets_table[256] = { &&TARGET_CACHE, &&TARGET_BINARY_SLICE, &&TARGET_BUILD_TEMPLATE, diff --git a/Tools/cases_generator/target_generator.py b/Tools/cases_generator/target_generator.py index ca151ff640a30c..c393a72c210243 100644 --- a/Tools/cases_generator/target_generator.py +++ b/Tools/cases_generator/target_generator.py @@ -27,7 +27,7 @@ def write_opcode_targets(analysis: Analysis, out: CWriter) -> None: if op < 256: targets[op] = f"&&TARGET_{name},\n" out.emit("#if !Py_TAIL_CALL_INTERP\n") - out.emit("static void *opcode_targets[256] = {\n") + out.emit("static void *opcode_targets_table[256] = {\n") for target in targets: out.emit(target) out.emit("};\n") From 178b03e89cebf1b9d567ef31199eed0030eb9648 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:36:41 +0100 Subject: [PATCH 2/5] fix for tail call --- Python/ceval.c | 11 +++++------ Python/ceval_macros.h | 14 +++++++------- Python/opcode_targets.h | 4 ++-- Tools/cases_generator/target_generator.py | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index e9e6e065b8b881..41d41ff3944360 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1031,6 +1031,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL); + void **opcode_targets = opcode_targets_table; #endif _PyEntryFrame entry; @@ -1040,8 +1041,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int return NULL; } - void **opcode_targets = opcode_targets_table; - /* Local "register" variables. * These are cached values from the frame and code object. */ _Py_CODEUNIT *next_instr; @@ -1103,9 +1102,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int stack_pointer = _PyFrame_GetStackPointer(frame); #if Py_TAIL_CALL_INTERP # if Py_STATS - return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0, lastopcode); + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, 0, lastopcode); # else - return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0); + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, 0); # endif #else goto error; @@ -1114,9 +1113,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #if Py_TAIL_CALL_INTERP # if Py_STATS - return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0, lastopcode); + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_table, 0, lastopcode); # else - return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0); + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_table, 0); # endif #else goto start_frame; diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 64ca7716fdbdee..66233a442cfc6d 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -71,11 +71,11 @@ #endif #ifdef Py_STATS -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, int oparg, int lastopcode -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, oparg, lastopcode +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *INSTRUCTION_TABLE, int oparg, int lastopcode +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, INSTRUCTION_TABLE, oparg, lastopcode #else -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, int oparg -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, oparg +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *INSTRUCTION_TABLE, int oparg +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, INSTRUCTION_TABLE, oparg #endif #if Py_TAIL_CALL_INTERP @@ -87,7 +87,7 @@ # define TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_##op(TAIL_CALL_PARAMS) # define DISPATCH_GOTO() \ do { \ - Py_MUSTTAIL return (INSTRUCTION_TABLE[opcode])(TAIL_CALL_ARGS); \ + Py_MUSTTAIL return (((py_tail_call_funcptr *)INSTRUCTION_TABLE)[opcode])(TAIL_CALL_ARGS); \ } while (0) # define JUMP_TO_LABEL(name) \ do { \ @@ -96,12 +96,12 @@ # ifdef Py_STATS # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg, lastopcode); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, INSTRUCTION_TABLE, oparg, lastopcode); \ } while (0) # else # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, INSTRUCTION_TABLE, oparg); \ } while (0) # endif # define LABEL(name) TARGET(name) diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 95ef1725e5cdef..605935b60569ce 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -258,7 +258,7 @@ static void *opcode_targets_table[256] = { &&TARGET_ENTER_EXECUTOR, }; #else /* Py_TAIL_CALL_INTERP */ -static py_tail_call_funcptr INSTRUCTION_TABLE[256]; +static py_tail_call_funcptr instruction_funcptr_table[256]; Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); @@ -503,7 +503,7 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) JUMP_TO_LABEL(error); } -static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { +static py_tail_call_funcptr instruction_funcptr_table[256] = { [BINARY_OP] = _TAIL_CALL_BINARY_OP, [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_BINARY_OP_ADD_FLOAT, [BINARY_OP_ADD_INT] = _TAIL_CALL_BINARY_OP_ADD_INT, diff --git a/Tools/cases_generator/target_generator.py b/Tools/cases_generator/target_generator.py index c393a72c210243..17f8e0099d9a9f 100644 --- a/Tools/cases_generator/target_generator.py +++ b/Tools/cases_generator/target_generator.py @@ -38,7 +38,7 @@ def function_proto(name: str) -> str: def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: - out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256];\n") + out.emit("static py_tail_call_funcptr instruction_funcptr_table[256];\n") out.emit("\n") # Emit function prototypes for labels. @@ -60,7 +60,7 @@ def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: out.emit("\n") # Emit the dispatch table. - out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256] = {\n") + out.emit("static py_tail_call_funcptr instruction_funcptr_table[256] = {\n") for name in sorted(analysis.instructions.keys()): out.emit(f"[{name}] = _TAIL_CALL_{name},\n") named_values = analysis.opmap.values() From 406ccfa0839439012a1a6be5a7fd46e510f9ea50 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:39:58 +0100 Subject: [PATCH 3/5] Change name to not shout --- Python/ceval_macros.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 66233a442cfc6d..b9efc59194a97f 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -71,11 +71,11 @@ #endif #ifdef Py_STATS -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *INSTRUCTION_TABLE, int oparg, int lastopcode -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, INSTRUCTION_TABLE, oparg, lastopcode +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg, int lastopcode +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg, lastopcode #else -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *INSTRUCTION_TABLE, int oparg -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, INSTRUCTION_TABLE, oparg +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg #endif #if Py_TAIL_CALL_INTERP @@ -87,7 +87,7 @@ # define TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_##op(TAIL_CALL_PARAMS) # define DISPATCH_GOTO() \ do { \ - Py_MUSTTAIL return (((py_tail_call_funcptr *)INSTRUCTION_TABLE)[opcode])(TAIL_CALL_ARGS); \ + Py_MUSTTAIL return (((py_tail_call_funcptr *)instruction_funcptr_table)[opcode])(TAIL_CALL_ARGS); \ } while (0) # define JUMP_TO_LABEL(name) \ do { \ @@ -96,12 +96,12 @@ # ifdef Py_STATS # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, INSTRUCTION_TABLE, oparg, lastopcode); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg, lastopcode); \ } while (0) # else # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, INSTRUCTION_TABLE, oparg); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg); \ } while (0) # endif # define LABEL(name) TARGET(name) From a2fd3159a9e74ed5abb5b8f598479937495b7c4c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:50:43 +0100 Subject: [PATCH 4/5] fix --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index 41d41ff3944360..5fedd8ccac8174 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1022,6 +1022,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #if USE_COMPUTED_GOTOS && !Py_TAIL_CALL_INTERP /* Import the static jump table */ #include "opcode_targets.h" + void **opcode_targets = opcode_targets_table; #endif #ifdef Py_STATS @@ -1031,7 +1032,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL); - void **opcode_targets = opcode_targets_table; #endif _PyEntryFrame entry; From 00caa65c227c25545b52ef4d5fac0e3d8af2604f Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:18:43 +0100 Subject: [PATCH 5/5] Update _parser.py --- Tools/c-analyzer/cpython/_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py index 95a474a49229a1..8afeceec875b3f 100644 --- a/Tools/c-analyzer/cpython/_parser.py +++ b/Tools/c-analyzer/cpython/_parser.py @@ -83,6 +83,7 @@ def clean_lines(text): Python/generated_cases.c.h Python/executor_cases.c.h Python/optimizer_cases.c.h +Python/opcode_targets.c.h # XXX: Throws errors if PY_VERSION_HEX is not mocked out Modules/clinic/_testclinic_depr.c.h