diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index c5532547339f47..f53fd302bca3a0 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1697,155 +1697,6 @@ def test_multiple_labels(self): } """ -class TestGeneratedTailCallErorHandlers(unittest.TestCase): - def setUp(self) -> None: - super().setUp() - self.maxDiff = None - - self.temp_dir = tempfile.gettempdir() - self.temp_input_filename = os.path.join(self.temp_dir, "input.txt") - self.temp_output_filename = os.path.join(self.temp_dir, "output.txt") - self.temp_metadata_filename = os.path.join(self.temp_dir, "metadata.txt") - self.temp_pymetadata_filename = os.path.join(self.temp_dir, "pymetadata.txt") - self.temp_executor_filename = os.path.join(self.temp_dir, "executor.txt") - - def tearDown(self) -> None: - for filename in [ - self.temp_input_filename, - self.temp_output_filename, - self.temp_metadata_filename, - self.temp_pymetadata_filename, - self.temp_executor_filename, - ]: - try: - os.remove(filename) - except: - pass - super().tearDown() - - def run_cases_test(self, input: str, expected: str): - with open(self.temp_input_filename, "w+") as temp_input: - temp_input.write(textwrap.dedent(input)) - temp_input.flush() - - with handle_stderr(): - tier1_tail_call_generator.generate_label_handlers_from_files( - self.temp_input_filename, self.temp_output_filename - ) - - with open(self.temp_output_filename) as temp_output: - lines = temp_output.readlines() - while lines and lines[0].startswith(("// ", "#", " #", "\n")): - lines.pop(0) - while lines and lines[-1].startswith(("#", "\n")): - lines.pop(-1) - actual = "".join(lines) - - self.assertEqual(actual.strip(), textwrap.dedent(expected).strip()) - - def test_correctly_finds_pyeval_framedefault(self): - input = """ - PyObject* _Py_HOT_FUNCTION - _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) - { - - /* END_BASE_INTERPRETER */ - } - """ - output = """ - """ - self.run_cases_test(input, output) - - def test_simple_label(self): - input = """ - PyObject* _Py_HOT_FUNCTION - _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) - { - - TAIL_CALL_TARGET(error): - DO_THING(); - /* END_BASE_INTERPRETER */ - } - """ - output = """ - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); - - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) - { - DO_THING(); - /* END_BASE_INTERPRETER */ - } - """ - self.run_cases_test(input, output) - - def test_fallthrough_label(self): - input = """ - PyObject* _Py_HOT_FUNCTION - _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) - { - - TAIL_CALL_TARGET(error): - DO_THING(); - TAIL_CALL_TARGET(fallthrough): - DO_THING2(); - /* END_BASE_INTERPRETER */ - } - """ - output = """ - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS); - - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) - { - DO_THING(); - TAIL_CALL(fallthrough); - } - - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS) - { - DO_THING2(); - /* END_BASE_INTERPRETER */ - } - """ - self.run_cases_test(input, output) - - def test_transform_gotos(self): - input = """ - PyObject* _Py_HOT_FUNCTION - _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) - { - - TAIL_CALL_TARGET(error): - if (thing) { - goto fallthrough; - } - DO_THING(); - TAIL_CALL_TARGET(fallthrough): - DO_THING2(); - /* END_BASE_INTERPRETER */ - } - """ - output = """ - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS); - - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) - { - if (thing) { - TAIL_CALL(fallthrough); - } - DO_THING(); - TAIL_CALL(fallthrough); - } - - Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS) - { - DO_THING2(); - /* END_BASE_INTERPRETER */ - } - """ - self.run_cases_test(input, output) - class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f7c768f6ca5157..807d20f27f1abd 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5287,7 +5287,14 @@ dummy_func( lltrace_resume_frame(frame); } #endif + // This is a little complicated... + // If we are in a tail call handler, we want to tail call (DISPATCH). + // If we're not then we need the shim frame. +#if defined(Py_TAIL_CALL_INTERP) && !defined(IN_TAIL_CALL_INTERP) + return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0); +#else DISPATCH(); +#endif } label(exit_unwind) { @@ -5299,7 +5306,7 @@ dummy_func( frame = tstate->current_frame = dying->previous; _PyEval_FrameClearAndPop(tstate, dying); frame->return_offset = 0; - if (frame == &entry_frame) { + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { /* Restore previous frame and exit */ tstate->current_frame = frame->previous; tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; diff --git a/Python/ceval.c b/Python/ceval.c index 33c24ccd3c8916..86d69ffc39d37a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -808,7 +808,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef Py_STATS int lastopcode = 0; #endif -#ifndef Py_TAIL_CALL_INTERP uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ @@ -835,7 +834,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Push frame */ entry_frame.previous = tstate->current_frame; - entry_frame.is_entry_frame = 1; frame->previous = &entry_frame; tstate->current_frame = frame; @@ -910,18 +908,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif #ifdef Py_TAIL_CALL_INTERP -#ifdef LLTRACE - return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0, lltrace); -#else return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0); -#endif #else DISPATCH(); #endif #include "generated_cases.c.h" - #ifdef _Py_TIER2 // Tier 2 is also here! diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 35852015103691..83718f64025884 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -313,7 +313,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { UPDATE_MISS_STATS((INSTNAME)); \ assert(_PyOpcode_Deopt[opcode] == (INSTNAME)); \ Py_MUSTTAIL \ - return (INSTRUCTION_TABLE[op])(frame, stack_pointer, tstate, next_instr - 1 - size, opcode, oparg); \ + return (INSTRUCTION_TABLE[INSTNAME])(frame, stack_pointer, tstate, next_instr - 1 - SIZE, opcode, oparg); \ } #else # define DEOPT_IF(COND, INSTNAME, SIZE) \ diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 4f4a858ce2c804..d443c615e33520 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8,6 +8,8 @@ #endif #define TIER_ONE 1 +#ifndef Py_TAIL_CALL_INTERP + #if !USE_COMPUTED_GOTOS dispatch_opcode: switch (opcode) @@ -8547,6 +8549,7 @@ /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); +#endif /* Py_TAIL_CALL_INTERP */ /* BEGIN LABELS */ pop_4_error: @@ -8644,7 +8647,14 @@ lltrace_resume_frame(frame); } #endif + // This is a little complicated... + // If we are in a tail call handler, we want to tail call (DISPATCH). + // If we're not then we need the shim frame. + #if defined(Py_TAIL_CALL_INTERP) && !defined(IN_TAIL_CALL_INTERP) + return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0); + #else DISPATCH(); + #endif } exit_unwind: @@ -8657,7 +8667,7 @@ frame = tstate->current_frame = dying->previous; _PyEval_FrameClearAndPop(tstate, dying); frame->return_offset = 0; - if (frame == &entry_frame) { + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { /* Restore previous frame and exit */ tstate->current_frame = frame->previous; tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; diff --git a/Python/generated_tail_call_handlers.c.h b/Python/generated_tail_call_handlers.c.h index 6469b8507617e2..3336ea34522ccf 100644 --- a/Python/generated_tail_call_handlers.c.h +++ b/Python/generated_tail_call_handlers.c.h @@ -10,6 +10,7 @@ #define IN_TAIL_CALL_INTERP 1 static inline PyObject *_TAIL_CALL_shim(TAIL_CALL_PARAMS); static py_tail_call_funcptr INSTRUCTION_TABLE[256]; + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_4_error(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_3_error(TAIL_CALL_PARAMS); Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); @@ -45,116 +46,101 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS) Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) { - /* Double-check exception status. */ -#ifdef NDEBUG - if (!_PyErr_Occurred(tstate)) { - _PyErr_SetString(tstate, PyExc_SystemError, + /* Double-check exception status. */ + #ifdef NDEBUG + if (!_PyErr_Occurred(tstate)) { + _PyErr_SetString(tstate, PyExc_SystemError, "error return without exception set"); - } -#else - assert(_PyErr_Occurred(tstate)); -#endif + } + #else + assert(_PyErr_Occurred(tstate)); + #endif - /* Log traceback info. */ - assert(!frame->is_entry_frame); - if (!_PyFrame_IsIncomplete(frame)) { - PyFrameObject *f = _PyFrame_GetFrameObject(frame); - if (f != NULL) { - PyTraceBack_Here(f); - } + /* Log traceback info. */ + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + if (!_PyFrame_IsIncomplete(frame)) { + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + if (f != NULL) { + PyTraceBack_Here(f); } - _PyEval_MonitorRaise(tstate, frame, next_instr-1); + } + _PyEval_MonitorRaise(tstate, frame, next_instr-1); TAIL_CALL(exception_unwind); } Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS) { - { - /* We can't use frame->instr_ptr here, as RERAISE may have set it */ - int offset = INSTR_OFFSET()-1; - int level, handler, lasti; - if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) { - // No handlers, so exit. - assert(_PyErr_Occurred(tstate)); - - /* Pop remaining stack entries. */ - _PyStackRef *stackbase = _PyFrame_Stackbase(frame); - while (stack_pointer > stackbase) { - PyStackRef_XCLOSE(POP()); - } - assert(STACK_LEVEL() == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); - monitor_unwind(tstate, frame, next_instr-1); - TAIL_CALL(exit_unwind); - } - - assert(STACK_LEVEL() >= level); - _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; - while (stack_pointer > new_top) { - PyStackRef_XCLOSE(POP()); - } - if (lasti) { - int frame_lasti = _PyInterpreterFrame_LASTI(frame); - PyObject *lasti = PyLong_FromLong(frame_lasti); - if (lasti == NULL) { - TAIL_CALL(exception_unwind); - } - PUSH(PyStackRef_FromPyObjectSteal(lasti)); - } - - /* Make the raw exception data - available to the handler, - so a program can emulate the - Python main loop. */ - PyObject *exc = _PyErr_GetRaisedException(tstate); - PUSH(PyStackRef_FromPyObjectSteal(exc)); - next_instr = _PyFrame_GetBytecode(frame) + handler; - - if (monitor_handled(tstate, frame, next_instr, exc) < 0) { - TAIL_CALL(exception_unwind); - } - /* Resume normal execution */ -#ifdef LLTRACE - if (frame->lltrace >= 5) { - lltrace_resume_frame(frame); - } -#endif - -#ifdef Py_TAIL_CALL_INTERP -# ifdef IN_TAIL_CALL_INTERP - DISPATCH(); -# else -# ifdef LLTRACE - return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0, lltrace); -# else - return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0); -# endif -# endif -#else - DISPATCH(); -#endif + /* We can't use frame->instr_ptr here, as RERAISE may have set it */ + int offset = INSTR_OFFSET()-1; + int level, handler, lasti; + if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) { + // No handlers, so exit. + assert(_PyErr_Occurred(tstate)); + /* Pop remaining stack entries. */ + _PyStackRef *stackbase = _PyFrame_Stackbase(frame); + while (stack_pointer > stackbase) { + PyStackRef_XCLOSE(POP()); } - - TAIL_CALL(exit_unwind); + assert(STACK_LEVEL() == 0); + _PyFrame_SetStackPointer(frame, stack_pointer); + monitor_unwind(tstate, frame, next_instr-1); + TAIL_CALL(exit_unwind); + } + assert(STACK_LEVEL() >= level); + _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; + while (stack_pointer > new_top) { + PyStackRef_XCLOSE(POP()); + } + if (lasti) { + int frame_lasti = _PyInterpreterFrame_LASTI(frame); + PyObject *lasti = PyLong_FromLong(frame_lasti); + if (lasti == NULL) { + TAIL_CALL(exception_unwind); + } + PUSH(PyStackRef_FromPyObjectSteal(lasti)); + } + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. */ + PyObject *exc = _PyErr_GetRaisedException(tstate); + PUSH(PyStackRef_FromPyObjectSteal(exc)); + next_instr = _PyFrame_GetBytecode(frame) + handler; + if (monitor_handled(tstate, frame, next_instr, exc) < 0) { + TAIL_CALL(exception_unwind); + } + /* Resume normal execution */ + #ifdef LLTRACE + if (frame->lltrace >= 5) { + lltrace_resume_frame(frame); + } + #endif + // This is a little complicated... + // If we are in a tail call handler, we want to tail call (DISPATCH). + // If we're not then we need the shim frame. + #if defined(Py_TAIL_CALL_INTERP) && !defined(IN_TAIL_CALL_INTERP) + return _TAIL_CALL_shim(frame, stack_pointer, tstate, next_instr, 0, 0); + #else + DISPATCH(); + #endif } Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); - assert(!frame->is_entry_frame); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; _PyEval_FrameClearAndPop(tstate, dying); frame->return_offset = 0; - if (frame->is_entry_frame) { + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { /* Restore previous frame and exit */ tstate->current_frame = frame->previous; tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; return NULL; } - TAIL_CALL(resume_with_error); } @@ -163,8 +149,6 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_resume_with_error(TAIL_CALL_PARA next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); TAIL_CALL(error); - -/* END_BASE_INTERPRETER */ } diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index d76d66be5f467a..c3c386458c1fce 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -651,6 +651,19 @@ def emit_tokens( raise analysis_error(ex.args[0], rbrace) from None return storage + def emit_tokens_simple( + self, + tokens: list[Token] + ) -> Storage: + tkn_iter = TokenIterator(tokens) + self.out.start_line() + for tkn in tkn_iter: + if tkn.text in self._replacers: + self._replacers[tkn.text](tkn, tkn_iter, None, None, None) + continue + self.out.emit(tkn) + + def emit(self, txt: str | Token) -> None: self.out.emit(txt) diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index d5fbaca75e5cc6..27262d0ebd5f72 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -176,6 +176,8 @@ def generate_tier1( #endif #define TIER_ONE 1 +#ifndef Py_TAIL_CALL_INTERP + #if !USE_COMPUTED_GOTOS dispatch_opcode: switch (opcode) @@ -207,6 +209,7 @@ def generate_tier1( /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); +#endif /* Py_TAIL_CALL_INTERP */ {LABEL_START_MARKER} """) generate_tier1_labels(analysis, outfile, lines) diff --git a/Tools/cases_generator/tier1_tail_call_generator.py b/Tools/cases_generator/tier1_tail_call_generator.py index f87c70212048ea..f752bd263ff465 100644 --- a/Tools/cases_generator/tier1_tail_call_generator.py +++ b/Tools/cases_generator/tier1_tail_call_generator.py @@ -34,12 +34,8 @@ DEFAULT_INPUT = ROOT / "Python/bytecodes.c" DEFAULT_OUTPUT = ROOT / "Python/generated_tail_call_handlers.c.h" -DEFAULT_CEVAL_INPUT = ROOT / "Python/ceval.c" - FOOTER = "#undef TIER_ONE\n#undef IN_TAIL_CALL_INTERP\n" -TARGET_LABEL = "TAIL_CALL_TARGET" - class TailCallEmitter(Emitter): def __init__(self, out: CWriter, analysis: Analysis): @@ -71,41 +67,77 @@ def go_to_instruction( self.emit(f"Py_MUSTTAIL return (INSTRUCTION_TABLE[{name.text}])(frame, stack_pointer, tstate, next_instr - 1 - {size}, opcode, oparg);\n") return True -def generate_label_handlers(infile: TextIO, outfile: TextIO) -> list[str]: - out = CWriter(outfile, 0, False) - str_in = infile.read() - # https://stackoverflow.com/questions/8303488/regex-to-match-any-character-including-new-lines - eval_framedefault = re.findall(r"_PyEval_EvalFrameDefault\(.*\)\n({[\s\S]*\/\* END_BASE_INTERPRETER \*\/)", str_in)[0] - function_protos = re.findall(rf"{TARGET_LABEL}\((\w+)\):", eval_framedefault) - for proto in function_protos: - out.emit(f"{function_proto(proto)};\n") - out.emit("\n") - lines = iter(eval_framedefault[eval_framedefault.find(TARGET_LABEL):].split("\n")) - next(lines) - for i in range(len(function_protos)): - curr_proto = function_protos[i] - fallthrough_proto = function_protos[i + 1] if i + 1 < len(function_protos) else None - out.emit(function_proto(curr_proto)) - out.emit("\n") - out.emit("{\n") - for line in lines: - if TARGET_LABEL in line: +class TailCallLabelsEmitter(Emitter): + def __init__(self, out: CWriter): + super().__init__(out) + self._replacers = { + 'goto': self.goto, + } + + def go_to_instruction( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: Uop, + storage: Storage, + inst: Instruction | None, + ) -> bool: + next(tkn_iter) + name = next(tkn_iter) + next(tkn_iter) + next(tkn_iter) + assert name.kind == "IDENTIFIER" + self.emit("\n") + inst = self.analysis.instructions[name.text] + fam = None + # Search for the family (if any) + for family_name, family in self.analysis.families.items(): + if inst.name == family_name: + fam = family break - if label := re.findall(r"goto (\w+);", line): - out.emit(f"TAIL_CALL({label[0]});\n") - else: - out.emit_text(line) - out.emit("\n") - if fallthrough_proto: - out.emit(f"TAIL_CALL({fallthrough_proto});\n") - out.emit("}\n") - out.emit("\n") - return function_protos + size = fam.size if fam is not None else 0 + self.emit(f"Py_MUSTTAIL return (INSTRUCTION_TABLE[{name.text}])(frame, stack_pointer, tstate, next_instr - 1 - {size}, opcode, oparg);\n") + return True + + def goto( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: Uop, + storage: Storage, + inst: Instruction | None, + ) -> bool: + # Only labels need to replace their gotos with tail calls. + # We can't do this in normal code due to GCC's escape analysis + # complaining. + name = next(tkn_iter) + next(tkn_iter) + assert name.kind == "IDENTIFIER" + self.out.emit("\n") + self.emit(f"TAIL_CALL({name.text});\n") + def function_proto(name: str) -> str: return f"Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_{name}(TAIL_CALL_PARAMS)" +def generate_label_handlers( + analysis: Analysis, outfile: TextIO, lines: bool +) -> None: + out = CWriter(outfile, 0, lines) + emitter = TailCallLabelsEmitter(out) + emitter.emit("\n") + for name in analysis.labels: + emitter.emit(f"{function_proto(name)};\n") + emitter.emit("\n") + for name, label in analysis.labels.items(): + emitter.emit(f"{function_proto(name)}\n") + emitter.emit_tokens_simple(label.body) + + emitter.emit("\n") + emitter.emit("\n") + + def generate_tier1( filenames: list[str], analysis: Analysis, outfile: TextIO, lines: bool ) -> None: @@ -123,8 +155,7 @@ def generate_tier1( out.emit("static inline PyObject *_TAIL_CALL_shim(TAIL_CALL_PARAMS);\n") out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256];\n"); - with open(DEFAULT_CEVAL_INPUT, "r") as infile: - err_labels = generate_label_handlers(infile, outfile) + generate_label_handlers(analysis, outfile, lines) emitter = TailCallEmitter(out, analysis) out.emit("\n") @@ -146,7 +177,7 @@ def generate_tier1( # at the branch also produces the same. # Furthermore, this is required to make GCC 15's escape analysis happy # as written above. - for err_label in err_labels: + for err_label in analysis.labels.keys(): out.emit(f"{err_label}:\n") out.emit(f"TAIL_CALL({err_label});\n") out.start_line()