Skip to content

Commit cedd7af

Browse files
Rewrite the tracing JIT to use a common opcode handler
1 parent 3385420 commit cedd7af

20 files changed

+712
-15668
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,18 +759,23 @@ typedef _Py_CODEUNIT *(*_PyJitEntryFuncPtr)(struct _PyExecutorObject *exec, _PyI
759759

760760
typedef struct _PyJitTracerState {
761761
bool dependencies_still_valid;
762+
bool do_not_specialize;
763+
bool dynamic_jump_taken;
762764
int code_max_size;
763765
int code_curr_size;
764766
int initial_stack_depth;
765767
int initial_chain_depth;
768+
int prev_instr_oparg;
769+
int prev_instr_stacklevel;
766770
_PyUOpInstruction *code_buffer;
767771
_Py_CODEUNIT *insert_exec_instr;
768772
_Py_CODEUNIT *close_loop_instr;
769-
_Py_CODEUNIT *last_specialized_instr;
770773
PyCodeObject *initial_code; // Strong
771774
PyFunctionObject *initial_func; // Strong
772-
struct _PyExitData *previous_exit;
773-
_PyInterpreterFrame *current_frame;
775+
_Py_CODEUNIT *prev_instr;
776+
PyCodeObject *prev_instr_code; // Strong
777+
struct _PyExitData *prev_exit;
778+
_PyInterpreterFrame *prev_instr_frame;
774779
_PyBloomFilter dependencies;
775780
} _PyJitTracerState;
776781

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ Known values:
286286
Python 3.15a1 3653 (Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST)
287287
Python 3.15a1 3654 (Fix missing exception handlers in logical expression)
288288
Python 3.15a1 3655 (Fix miscompilation of some module-level annotations)
289+
Python 3.15a2 3656 (Add RECORD_PREVIOUS_INST for a tracing JIT)
289290
290291
291292
Python 3.16 will start with 3700
@@ -299,7 +300,7 @@ PC/launcher.c must also be updated.
299300
300301
*/
301302

302-
#define PYC_MAGIC_NUMBER 3655
303+
#define PYC_MAGIC_NUMBER 3656
303304
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
304305
(little-endian) and then appending b'\r\n'. */
305306
#define PYC_MAGIC_NUMBER_TOKEN \

Include/internal/pycore_opcode_metadata.h

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_optimizer.h

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -358,22 +358,12 @@ PyAPI_FUNC(int) _PyDumpExecutors(FILE *out);
358358
extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp);
359359
#endif
360360

361-
int
362-
_PyJit_translate_single_bytecode_to_trace(
363-
PyThreadState *tstate,
364-
_PyInterpreterFrame *frame,
365-
_Py_CODEUNIT *this_instr,
366-
_Py_CODEUNIT *next_instr,
367-
PyCodeObject *code,
368-
PyFunctionObject *func,
369-
int old_stack_level,
370-
int opcode,
371-
int oparg,
372-
int jump_taken);
361+
int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr);
373362

374363
void
375-
_PyJit_InitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *insert_exec_instr,
376-
_Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit);
364+
_PyJit_InitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame,
365+
_Py_CODEUNIT *curr_instr, _Py_CODEUNIT *insert_exec_instr,
366+
_Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit, int oparg);
377367

378368
void _PyJit_FinalizeTracing(PyThreadState *tstate);
379369

Include/opcode_ids.h

Lines changed: 17 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/_opcode_metadata.py

Lines changed: 17 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2255,7 +2255,7 @@ def frame_3_jit() -> None:
22552255
# 1 extra iteration for tracing.
22562256
for i in range(_testinternalcapi.TIER2_THRESHOLD + 2):
22572257
# Careful, doing this in the reverse order breaks tracing:
2258-
expected = {enabled} and i >= _testinternalcapi.TIER2_THRESHOLD
2258+
expected = {enabled} and i >= _testinternalcapi.TIER2_THRESHOLD + 1
22592259
assert sys._jit.is_active() is expected
22602260
frame_2_jit(expected)
22612261
assert sys._jit.is_active() is expected

Makefile.pre.in

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,7 +2141,7 @@ Objects/mimalloc/page.o: $(srcdir)/Objects/mimalloc/page-queue.c
21412141
regen-cases: \
21422142
regen-opcode-ids regen-opcode-targets regen-uop-ids regen-opcode-metadata-py \
21432143
regen-generated-cases regen-executor-cases regen-optimizer-cases \
2144-
regen-opcode-metadata regen-uop-metadata regen-tracer-cases
2144+
regen-opcode-metadata regen-uop-metadata
21452145

21462146
.PHONY: regen-opcode-ids
21472147
regen-opcode-ids:
@@ -2187,13 +2187,6 @@ regen-optimizer-cases:
21872187
$(srcdir)/Python/bytecodes.c
21882188
$(UPDATE_FILE) $(srcdir)/Python/optimizer_cases.c.h $(srcdir)/Python/optimizer_cases.c.h.new
21892189

2190-
.PHONY: regen-tracer-cases
2191-
regen-tracer-cases:
2192-
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/tracer_generator.py \
2193-
-o $(srcdir)/Python/generated_tracer_cases.c.h.new $(srcdir)/Python/bytecodes.c
2194-
$(UPDATE_FILE) $(srcdir)/Python/generated_tracer_cases.c.h $(srcdir)/Python/generated_tracer_cases.c.h.new
2195-
2196-
21972190
.PHONY: regen-opcode-metadata
21982191
regen-opcode-metadata:
21992192
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/opcode_metadata_generator.py \

Python/bytecodes.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
#define USE_COMPUTED_GOTOS 0
4747
#include "ceval_macros.h"
48+
#include "ceval_macros.h"
49+
#include "ceval_macros.h"
4850
#include "../Include/internal/pycore_code.h"
4951
#include "../Include/internal/pycore_stackref.h"
5052

@@ -2985,14 +2987,9 @@ dummy_func(
29852987
oparg >>= 8;
29862988
insert_exec_at--;
29872989
}
2988-
_PyJit_InitializeTracing(tstate, frame, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL);
2990+
_PyJit_InitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg);
29892991
ENTER_TRACING();
29902992
}
2991-
int _jump_taken = false;
2992-
PyCodeObject *old_code = _PyFrame_GetCode(frame);
2993-
PyFunctionObject *old_func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
2994-
int _old_stack_level = 0;
2995-
TRACING_DISPATCH();
29962993
}
29972994
else {
29982995
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
@@ -3057,9 +3054,6 @@ dummy_func(
30573054
}
30583055
assert(executor != tstate->interp->cold_executor);
30593056
tstate->jit_exit = NULL;
3060-
#if TRACING_JIT
3061-
RECORD_TRACE_NO_DISPATCH();
3062-
#endif
30633057
TIER1_TO_TIER2(executor);
30643058
#else
30653059
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
@@ -5224,6 +5218,25 @@ dummy_func(
52245218
Py_FatalError("Executing RESERVED instruction.");
52255219
}
52265220

5221+
tier1 no_save_ip inst(RECORD_PREVIOUS_INST, (--)) {
5222+
assert(IS_JIT_TRACING());
5223+
int opcode = next_instr->op.code;
5224+
int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr);
5225+
if (full) {
5226+
LEAVE_TRACING();
5227+
int err = bail_tracing_and_jit(tstate, frame);
5228+
ERROR_IF(err < 0);
5229+
DISPATCH_GOTO_NON_TRACING();
5230+
}
5231+
tstate->interp->jit_state.do_not_specialize = false;
5232+
PyCodeObject *prev_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame));
5233+
Py_SETREF(tstate->interp->jit_state.prev_instr_code, prev_code);
5234+
tstate->interp->jit_state.prev_instr = next_instr;
5235+
tstate->interp->jit_state.prev_instr_frame = frame;
5236+
tstate->interp->jit_state.prev_instr_oparg = oparg;
5237+
tstate->interp->jit_state.prev_instr_stacklevel = STACK_LEVEL();
5238+
DISPATCH_GOTO_NON_TRACING();
5239+
}
52275240
///////// Tier-2 only opcodes /////////
52285241

52295242
op (_GUARD_IS_TRUE_POP, (flag -- )) {
@@ -5456,7 +5469,10 @@ dummy_func(
54565469
_PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit);
54575470
assert(tstate->current_executor == (PyObject *)previous_executor);
54585471
int chain_depth = previous_executor->vm_data.chain_depth + 1;
5459-
_PyJit_InitializeTracing(tstate, frame, target, target, STACK_LEVEL(), chain_depth, exit);
5472+
// Note: it's safe to use target->op.arg here instead of the oparg given by EXTENDED_ARG.
5473+
// The invariant in the optimizer is the deopt target always points back to the first EXTENDED_ARG.
5474+
// So setting it to anything else is wrong.
5475+
_PyJit_InitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg);
54605476
exit->temperature = initial_temperature_backoff_counter();
54615477
GOTO_TIER_ONE(target, 1);
54625478
}

Python/ceval.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -984,17 +984,6 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch)
984984
}
985985
}
986986

987-
#if _Py_TIER2
988-
// 1 for trace full, 0 for successful write.
989-
static inline int
990-
add_to_code_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, PyCodeObject *old_code, PyFunctionObject *old_func, int old_stack_level, _Py_CODEUNIT *this_instr, _Py_CODEUNIT *next_instr, int opcode, int oparg, int jump_taken)
991-
{
992-
assert(frame != NULL);
993-
assert(tstate->interp->jit_state.code_curr_size < UOP_MAX_TRACE_LENGTH);
994-
return !_PyJit_translate_single_bytecode_to_trace(tstate, frame, this_instr, next_instr, old_code, old_func, old_stack_level, opcode, oparg, jump_taken);
995-
}
996-
997-
998987
// 0 for success, -1 for error.
999988
static int
1000989
bail_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
@@ -1007,7 +996,6 @@ bail_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
1007996
_PyJit_FinalizeTracing(tstate);
1008997
return err;
1009998
}
1010-
#endif
1011999

10121000
/* _PyEval_EvalFrameDefault is too large to optimize for speed with PGO on MSVC.
10131001
*/
@@ -1025,7 +1013,6 @@ bail_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
10251013
#if _Py_TAIL_CALL_INTERP
10261014
#include "opcode_targets.h"
10271015
#include "generated_cases.c.h"
1028-
#include "generated_tracer_cases.c.h"
10291016
#endif
10301017

10311018
#if (defined(__GNUC__) && __GNUC__ >= 10 && !defined(__clang__)) && defined(__x86_64__)
@@ -1154,7 +1141,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11541141
#else
11551142
goto start_frame;
11561143
# include "generated_cases.c.h"
1157-
#include "generated_tracer_cases.c.h"
11581144
#endif
11591145

11601146

0 commit comments

Comments
 (0)