gh-128563: A new tail-calling interpreter (GH-128718)

Co-authored-by: Garrett Gu <garrettgu777@gmail.com>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
Ken Jin 2025-02-06 23:21:57 +08:00 committed by GitHub
parent 555dc50c81
commit cb640b659e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 3883 additions and 623 deletions

View file

@ -70,12 +70,41 @@
#define INSTRUCTION_STATS(op) ((void)0)
#endif
#if USE_COMPUTED_GOTOS
#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
#ifdef Py_TAIL_CALL_INTERP
// Note: [[clang::musttail]] works for GCC 15, but not __attribute__((musttail)) at the moment.
# define Py_MUSTTAIL [[clang::musttail]]
# define Py_PRESERVE_NONE_CC __attribute__((preserve_none))
Py_PRESERVE_NONE_CC typedef PyObject* (*py_tail_call_funcptr)(TAIL_CALL_PARAMS);
# 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); \
} while (0)
# define JUMP_TO_LABEL(name) \
do { \
Py_MUSTTAIL return (_TAIL_CALL_##name)(TAIL_CALL_ARGS); \
} while (0)
# define JUMP_TO_PREDICTED(name) \
do { \
Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg); \
} while (0)
# define LABEL(name) TARGET(name)
#elif USE_COMPUTED_GOTOS
# define TARGET(op) TARGET_##op:
# define DISPATCH_GOTO() goto *opcode_targets[opcode]
# define JUMP_TO_LABEL(name) goto name;
# define JUMP_TO_PREDICTED(name) goto PREDICTED_##name;
# define LABEL(name) name:
#else
# define TARGET(op) case op: TARGET_##op:
# define DISPATCH_GOTO() goto dispatch_opcode
# define JUMP_TO_LABEL(name) goto name;
# define JUMP_TO_PREDICTED(name) goto PREDICTED_##name;
# define LABEL(name) name:
#endif
/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */
@ -92,7 +121,7 @@ do { \
int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); \
frame->lltrace = lltrace; \
if (lltrace < 0) { \
goto exit_unwind; \
JUMP_TO_LABEL(exit_unwind); \
} \
} while (0)
#else
@ -129,11 +158,11 @@ do { \
assert((NEW_FRAME)->previous == frame); \
frame = tstate->current_frame = (NEW_FRAME); \
CALL_STAT_INC(inlined_py_calls); \
goto start_frame; \
JUMP_TO_LABEL(start_frame); \
} while (0)
// Use this instead of 'goto error' so Tier 2 can go to a different label
#define GOTO_ERROR(LABEL) goto LABEL
#define GOTO_ERROR(LABEL) JUMP_TO_LABEL(LABEL)
/* Tuple access macros */
@ -236,14 +265,6 @@ GETITEM(PyObject *v, Py_ssize_t i) {
#define UPDATE_MISS_STATS(INSTNAME) ((void)0)
#endif
#define DEOPT_IF(COND, INSTNAME) \
if ((COND)) { \
/* This is only a single jump on release builds! */ \
UPDATE_MISS_STATS((INSTNAME)); \
assert(_PyOpcode_Deopt[opcode] == (INSTNAME)); \
goto PREDICTED_##INSTNAME; \
}
// Try to lock an object in the free threading build, if it's not already
// locked. Use with a DEOPT_IF() to deopt if the object is already locked.
@ -328,7 +349,7 @@ do { \
stack_pointer = _PyFrame_GetStackPointer(frame); \
if (next_instr == NULL) { \
next_instr = (dest)+1; \
goto error; \
JUMP_TO_LABEL(error); \
} \
} \
} while (0);