Skip to content

Commit bd97afa

Browse files
fixup
1 parent 24df49f commit bd97afa

File tree

4 files changed

+173
-26
lines changed

4 files changed

+173
-26
lines changed

Python/ceval.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,13 +866,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
866866
#endif
867867

868868
#ifdef Py_TAIL_CALL_INTERP
869-
return _TAIL_CALL_start_frame(frame, stack_pointer, tstate, next_instr, 0, 0);
869+
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0, 0);
870+
# include "generated_tail_call_labels.c.h"
870871
#else
871872
goto start_frame;
873+
# include "generated_cases.c.h"
872874
#endif
873875

874-
#include "generated_cases.c.h"
875-
876876
#ifdef _Py_TIER2
877877

878878
// Tier 2 is also here!
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// This file is generated by Tools/cases_generator/tier1_tail_call_generator.py
2+
// from:
3+
// Python/bytecodes.c
4+
// Do not edit!
5+
6+
#ifndef Py_TAIL_CALL_INTERP
7+
#error "This file is for tail-calling interpreter only."
8+
#endif
9+
#define TIER_ONE 1
10+
11+
error:
12+
{
13+
/* Double-check exception status. */
14+
#ifdef NDEBUG
15+
if (!_PyErr_Occurred(tstate)) {
16+
_PyErr_SetString(tstate, PyExc_SystemError,
17+
"error return without exception set");
18+
}
19+
#else
20+
assert(_PyErr_Occurred(tstate));
21+
#endif
22+
23+
/* Log traceback info. */
24+
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
25+
if (!_PyFrame_IsIncomplete(frame)) {
26+
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
27+
if (f != NULL) {
28+
PyTraceBack_Here(f);
29+
}
30+
}
31+
_PyEval_MonitorRaise(tstate, frame, next_instr-1);
32+
goto exception_unwind;
33+
}
34+
35+
exception_unwind:
36+
{
37+
/* We can't use frame->instr_ptr here, as RERAISE may have set it */
38+
int offset = INSTR_OFFSET()-1;
39+
int level, handler, lasti;
40+
if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) {
41+
// No handlers, so exit.
42+
assert(_PyErr_Occurred(tstate));
43+
/* Pop remaining stack entries. */
44+
_PyStackRef *stackbase = _PyFrame_Stackbase(frame);
45+
while (stack_pointer > stackbase) {
46+
PyStackRef_XCLOSE(POP());
47+
}
48+
assert(STACK_LEVEL() == 0);
49+
_PyFrame_SetStackPointer(frame, stack_pointer);
50+
monitor_unwind(tstate, frame, next_instr-1);
51+
goto exit_unwind;
52+
}
53+
assert(STACK_LEVEL() >= level);
54+
_PyStackRef *new_top = _PyFrame_Stackbase(frame) + level;
55+
while (stack_pointer > new_top) {
56+
PyStackRef_XCLOSE(POP());
57+
}
58+
if (lasti) {
59+
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
60+
PyObject *lasti = PyLong_FromLong(frame_lasti);
61+
if (lasti == NULL) {
62+
goto exception_unwind;
63+
}
64+
PUSH(PyStackRef_FromPyObjectSteal(lasti));
65+
}
66+
/* Make the raw exception data
67+
available to the handler,
68+
so a program can emulate the
69+
Python main loop. */
70+
PyObject *exc = _PyErr_GetRaisedException(tstate);
71+
PUSH(PyStackRef_FromPyObjectSteal(exc));
72+
next_instr = _PyFrame_GetBytecode(frame) + handler;
73+
if (monitor_handled(tstate, frame, next_instr, exc) < 0) {
74+
goto exception_unwind;
75+
}
76+
/* Resume normal execution */
77+
#ifdef LLTRACE
78+
if (frame->lltrace >= 5) {
79+
lltrace_resume_frame(frame);
80+
}
81+
#endif
82+
_TAIL_CALL_entry(frame, stack_pointer, tstate, next_instr, 0, 0);
83+
;
84+
}
85+
86+
exit_unwind:
87+
{
88+
assert(_PyErr_Occurred(tstate));
89+
_Py_LeaveRecursiveCallPy(tstate);
90+
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
91+
// GH-99729: We need to unlink the frame *before* clearing it:
92+
_PyInterpreterFrame *dying = frame;
93+
frame = tstate->current_frame = dying->previous;
94+
_PyEval_FrameClearAndPop(tstate, dying);
95+
frame->return_offset = 0;
96+
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
97+
/* Restore previous frame and exit */
98+
tstate->current_frame = frame->previous;
99+
tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS;
100+
return NULL;
101+
}
102+
next_instr = frame->instr_ptr;
103+
stack_pointer = _PyFrame_GetStackPointer(frame);
104+
goto error;
105+
}
106+
107+
#undef TIER_ONE

Tools/cases_generator/tier1_generator.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Flush,
1616
analysis_error,
1717
StackItem,
18+
Label,
1819
)
1920
from generators_common import (
2021
DEFAULT_INPUT,
@@ -221,21 +222,21 @@ def generate_tier1(
221222
#endif /* Py_TAIL_CALL_INTERP */
222223
{LABEL_START_MARKER}
223224
""")
224-
generate_tier1_labels(analysis, outfile, lines)
225+
out = CWriter(outfile, 2, lines)
226+
emitter = Emitter(out)
227+
generate_tier1_labels(analysis.labels, emitter)
225228
outfile.write(f"{LABEL_END_MARKER}\n")
226229
outfile.write(FOOTER)
227230

228231
def generate_tier1_labels(
229-
analysis: Analysis, outfile: TextIO, lines: bool
232+
labels: dict[str, Label], emitter: Emitter
230233
) -> None:
231-
out = CWriter(outfile, 2, lines)
232-
out.emit("\n")
233-
for name, label in analysis.labels.items():
234-
out.emit(f"{name}:\n")
235-
for tkn in label.body:
236-
out.emit(tkn)
237-
out.emit("\n")
238-
out.emit("\n")
234+
emitter.emit("\n")
235+
for name, label in labels.items():
236+
emitter.emit(f"{name}:\n")
237+
emitter.emit_label(label)
238+
emitter.emit("\n")
239+
emitter.emit("\n")
239240

240241
def generate_tier1_cases(
241242
analysis: Analysis, outfile: TextIO, lines: bool

Tools/cases_generator/tier1_tail_call_generator.py

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
)
2727

2828
from tier1_generator import (
29-
write_single_inst
29+
write_single_inst,
30+
generate_tier1_labels,
3031
)
3132

3233
from lexer import Token
@@ -35,9 +36,22 @@
3536

3637
DEFAULT_INPUT = ROOT / "Python/bytecodes.c"
3738
DEFAULT_OUTPUT = ROOT / "Python/generated_tail_call_handlers.c.h"
39+
DEFAULT_LABELS_OUTPUT = ROOT / "Python/generated_tail_call_labels.c.h"
3840

41+
PRELUDE = """
42+
#ifndef Py_TAIL_CALL_INTERP
43+
#error "This file is for tail-calling interpreter only."
44+
#endif
45+
#define TIER_ONE 1
46+
"""
3947
FOOTER = "#undef TIER_ONE\n"
4048

49+
NEEDED_LABELS = {
50+
"error",
51+
"exception_unwind",
52+
"exit_unwind",
53+
}
54+
4155
class TailCallEmitter(Emitter):
4256

4357
def __init__(self, out: CWriter, analysis: Analysis):
@@ -110,11 +124,32 @@ def goto(
110124
name = next(tkn_iter)
111125
next(tkn_iter)
112126
assert name.kind == "IDENTIFIER"
113-
self.out.emit("\n")
127+
self.out.start_line()
114128
self.emit(f"TAIL_CALL({name.text});\n")
115129
return True
116130

117131

132+
class TailCallCevalLabelsEmitter(Emitter):
133+
def __init__(self, out: CWriter):
134+
super().__init__(out)
135+
self._replacers = {
136+
'DISPATCH': self.dispatch,
137+
}
138+
139+
def dispatch(
140+
self,
141+
tkn: Token,
142+
tkn_iter: TokenIterator,
143+
uop: Uop | Label,
144+
storage: Storage,
145+
inst: Instruction | None,
146+
) -> bool:
147+
# Replace DISPATCH with _TAIL_CALL_entry(...)
148+
next(tkn_iter)
149+
next(tkn_iter)
150+
self.emit("_TAIL_CALL_entry(frame, stack_pointer, tstate, next_instr, 0, 0);\n")
151+
return True
152+
118153

119154
def function_proto(name: str) -> str:
120155
return f"Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_{name}(TAIL_CALL_PARAMS)"
@@ -152,17 +187,17 @@ def uses_this(inst: Instruction) -> bool:
152187
return False
153188

154189
def generate_tier1(
155-
filenames: list[str], analysis: Analysis, outfile: TextIO, lines: bool
190+
filenames: list[str], analysis: Analysis, outfile: TextIO, labels_outfile: TextIO, lines: bool
156191
) -> None:
192+
# Write labels required for main ceval
193+
write_header(__file__, filenames, labels_outfile)
194+
labels_outfile.write(PRELUDE)
195+
out = CWriter(labels_outfile, 2, lines)
196+
emitter = TailCallCevalLabelsEmitter(out)
197+
generate_tier1_labels({label: analysis.labels[label] for label in NEEDED_LABELS}, emitter)
198+
labels_outfile.write(FOOTER)
157199
write_header(__file__, filenames, outfile)
158-
outfile.write(
159-
"""
160-
#ifndef Py_TAIL_CALL_INTERP
161-
#error "This file is for tail-calling interpreter only."
162-
#endif
163-
#define TIER_ONE 1
164-
"""
165-
)
200+
outfile.write(PRELUDE)
166201
out = CWriter(outfile, 0, lines)
167202
out.emit("static inline PyObject *_TAIL_CALL_entry(TAIL_CALL_PARAMS);\n")
168203
out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256];\n");
@@ -232,6 +267,10 @@ def generate_tier1(
232267
"-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT
233268
)
234269

270+
arg_parser.add_argument(
271+
"-lo", "--labels-output", type=str, help="Generated labels", default=DEFAULT_LABELS_OUTPUT
272+
)
273+
235274
arg_parser.add_argument(
236275
"-l", "--emit-line-directives", help="Emit #line directives", action="store_true"
237276
)
@@ -246,5 +285,5 @@ def generate_tier1(
246285
if len(args.input) == 0:
247286
args.input.append(DEFAULT_INPUT)
248287
data = analyze_files(args.input)
249-
with open(args.output, "w") as outfile:
250-
generate_tier1(args.input, data, outfile, args.emit_line_directives)
288+
with open(args.output, "w") as outfile, open(args.labels_output, 'w') as labels_outfile:
289+
generate_tier1(args.input, data, outfile, labels_outfile, args.emit_line_directives)

0 commit comments

Comments
 (0)