Skip to content

Commit 795ef49

Browse files
Basic trace recording
1 parent a269e69 commit 795ef49

File tree

11 files changed

+13209
-44
lines changed

11 files changed

+13209
-44
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,9 @@ struct _is {
943943
struct types_state types;
944944
struct callable_cache callable_cache;
945945
PyObject *common_consts[NUM_COMMON_CONSTANTS];
946+
int jit_tracer_code_curr_size;
947+
_Py_CODEUNIT *jit_tracer_code_buffer;
948+
_Py_CODEUNIT *jit_tracer_initial_instr;
946949
bool jit;
947950
bool compiling;
948951
struct _PyUOpInstruction *jit_uop_buffer;

Include/internal/pycore_uop.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,15 @@ typedef struct _PyUOpInstruction{
3535
#endif
3636
} _PyUOpInstruction;
3737

38-
// This is the length of the trace we project initially.
38+
// This is the length of the trace we translate initially.
3939
#define UOP_MAX_TRACE_LENGTH 1200
4040
#define UOP_BUFFER_SIZE (UOP_MAX_TRACE_LENGTH * sizeof(_PyUOpInstruction))
4141

42+
// This is the length of the trace we record.
43+
// This includes the inline caches.
44+
#define TRACE_MAX_TRACE_LENGTH 1000
45+
#define TRACER_BUFFER_SIZE ((int)(TRACE_MAX_TRACE_LENGTH * sizeof(_Py_CODEUNIT)))
46+
4247
#ifdef __cplusplus
4348
}
4449
#endif

Python/bytecodes.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2960,19 +2960,26 @@ dummy_func(
29602960
oparg >>= 8;
29612961
start--;
29622962
}
2963-
_PyExecutorObject *executor;
2964-
int optimized = _PyOptimizer_Optimize(frame, start, &executor, 0);
2965-
if (optimized <= 0) {
2966-
this_instr[1].counter = restart_backoff_counter(counter);
2967-
ERROR_IF(optimized < 0);
2963+
if (tstate->interp->jit_tracer_code_buffer == NULL) {
2964+
tstate->interp->jit_tracer_code_buffer = (_Py_CODEUNIT *)_PyObject_VirtualAlloc(TRACER_BUFFER_SIZE);
2965+
tstate->interp->jit_tracer_code_curr_size = 0;
2966+
if (tstate->interp->jit_tracer_code_buffer == NULL) {
2967+
// Don't error, just go to next instruction.
2968+
DISPATCH();
2969+
}
29682970
}
2969-
else {
2970-
this_instr[1].counter = initial_jump_backoff_counter();
2971-
assert(tstate->current_executor == NULL);
2972-
assert(executor != tstate->interp->cold_executor);
2973-
tstate->jit_exit = NULL;
2974-
TIER1_TO_TIER2(executor);
2971+
if (this_instr == tstate->interp->jit_tracer_initial_instr) {
2972+
// Looped back to initial instr. End tracing.
2973+
LEAVE_TRACING();
2974+
DISPATCH();
2975+
}
2976+
ENTER_TRACING();
2977+
// Nothing in the buffer, begin tracing!
2978+
if (tstate->interp->jit_tracer_code_curr_size == 0) {
2979+
tstate->interp->jit_tracer_initial_instr = this_instr;
29752980
}
2981+
// Not tracing dispatch, normal dispatch because we don't record the current instruction.
2982+
DISPATCH();
29762983
}
29772984
else {
29782985
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);

Python/ceval.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,21 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch)
973973
}
974974
}
975975

976+
// 1 for trace full, 0 for successful write.
977+
static inline int
978+
add_to_code_trace(PyThreadState *tstate, _Py_CODEUNIT *this_instr)
979+
{
980+
assert(tstate->interp->jit_tracer_code_curr_size < TRACE_MAX_TRACE_LENGTH);
981+
int curr_size = tstate->interp->jit_tracer_code_curr_size;
982+
int nsize = _PyOpcode_Caches[this_instr->op.code] + 1;
983+
if (curr_size + nsize > TRACE_MAX_TRACE_LENGTH) {
984+
return 1;
985+
}
986+
for (int i = 0; i < nsize; i++) {
987+
tstate->interp->jit_tracer_code_buffer[curr_size + i] = *(this_instr + i);
988+
}
989+
return 0;
990+
}
976991

977992
/* _PyEval_EvalFrameDefault is too large to optimize for speed with PGO on MSVC.
978993
*/
@@ -1102,9 +1117,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11021117
stack_pointer = _PyFrame_GetStackPointer(frame);
11031118
#if _Py_TAIL_CALL_INTERP
11041119
# if Py_STATS
1105-
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, 0, lastopcode);
1120+
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0, lastopcode);
11061121
# else
1107-
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, 0);
1122+
return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0);
11081123
# endif
11091124
#else
11101125
goto error;
@@ -1113,9 +1128,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11131128

11141129
#if _Py_TAIL_CALL_INTERP
11151130
# if Py_STATS
1116-
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_table, 0, lastopcode);
1131+
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0, lastopcode);
11171132
# else
1118-
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_table, 0);
1133+
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0);
11191134
# endif
11201135
#else
11211136
goto start_frame;

Python/ceval_macros.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,23 @@
7878
# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg
7979
#endif
8080

81+
# define TRACING_DISPATCH_GOTO() do { \
82+
if (add_to_code_trace(tstate, this_instr)) { \
83+
LEAVE_TRACING(); \
84+
} \
85+
DISPATCH_GOTO(); \
86+
} while (0);
87+
8188
#if _Py_TAIL_CALL_INTERP
8289
// Note: [[clang::musttail]] works for GCC 15, but not __attribute__((musttail)) at the moment.
8390
# define Py_MUSTTAIL [[clang::musttail]]
8491
# define Py_PRESERVE_NONE_CC __attribute__((preserve_none))
8592
Py_PRESERVE_NONE_CC typedef PyObject* (*py_tail_call_funcptr)(TAIL_CALL_PARAMS);
8693

8794
# define TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_##op(TAIL_CALL_PARAMS)
95+
# define TRACING_TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_TRACING_##op(TAIL_CALL_PARAMS)
96+
# define LEAVE_TRACING() instruction_funcptr_table = instruction_funcptr_handler_table;
97+
# define ENTER_TRACING() instruction_funcptr_table = instruction_funcptr_tracing_table
8898
# define DISPATCH_GOTO() \
8999
do { \
90100
Py_MUSTTAIL return (((py_tail_call_funcptr *)instruction_funcptr_table)[opcode])(TAIL_CALL_ARGS); \
@@ -107,7 +117,10 @@
107117
# define LABEL(name) TARGET(name)
108118
#elif USE_COMPUTED_GOTOS
109119
# define TARGET(op) TARGET_##op:
120+
# define TRACING_TARGET(op) TARGET_TRACING_##op:
110121
# define DISPATCH_GOTO() goto *opcode_targets[opcode]
122+
# define LEAVE_TRACING() opcode_targets = opcode_targets_table;
123+
# define ENTER_TRACING() opcode_targets = opcode_tracing_targets_table;
111124
# define JUMP_TO_LABEL(name) goto name;
112125
# define JUMP_TO_PREDICTED(name) goto PREDICTED_##name;
113126
# define LABEL(name) name:
@@ -172,6 +185,14 @@ do { \
172185
JUMP_TO_LABEL(start_frame); \
173186
} while (0)
174187

188+
#define TRACING_DISPATCH() \
189+
{ \
190+
assert(frame->stackpointer == NULL); \
191+
NEXTOPARG(); \
192+
PRE_DISPATCH_GOTO(); \
193+
TRACING_DISPATCH_GOTO(); \
194+
}
195+
175196
/* Tuple access macros */
176197

177198
#ifndef Py_DEBUG

0 commit comments

Comments
 (0)