Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
3008fc7
Boilerplate for new uop optimizer+executor
gvanrossum Jun 17, 2023
512b764
Make uop optimizer execute a LOAD_FAST
gvanrossum Jun 17, 2023
52742c8
Generalize uop optimizer infrastructure
gvanrossum Jun 18, 2023
4de0468
Give uop_execute an instruction decoding loop
gvanrossum Jun 19, 2023
5ded690
Make the translator use a switch
gvanrossum Jun 19, 2023
e41acc0
Support LOAD_FAST_LOAD_FAST
gvanrossum Jun 19, 2023
76ccf2d
Support LOAD_CONST
gvanrossum Jun 19, 2023
3001dde
Fix edge case for LOAD_FAST_LOAD_FAST
gvanrossum Jun 19, 2023
a7d2cba
Generate SET_IP after each instr
gvanrossum Jun 19, 2023
e00703c
Turn stats into properties (tp_members)
gvanrossum Jun 19, 2023
3d41ffe
Comment out fprintfs
gvanrossum Jun 19, 2023
7db1d12
Enable UOp optimizer by default in main interpreter
gvanrossum Jun 19, 2023
17bcc61
The world isn't ready for 'const int X = 1;'
gvanrossum Jun 19, 2023
e07bc4c
Avoid MSC warning about implicit cast
gvanrossum Jun 19, 2023
02b0624
Add missing cast in pylifecycle.c
gvanrossum Jun 19, 2023
b5feec7
Use write_provenance_header() more
gvanrossum Jun 20, 2023
775df6e
Generate (some) cases for the executor
gvanrossum Jun 20, 2023
e2855ca
Recompile flowgraph.c when opcodse_metadata.h changes
gvanrossum Jun 20, 2023
0ad18cf
Add uop status to opcode metadata
gvanrossum Jun 20, 2023
f5c8316
Use macros from ceval_macros.h
gvanrossum Jun 20, 2023
13c6bd2
Interim -- opcode without oparg using cache entry
gvanrossum Jun 20, 2023
1b1584c
ERROR_IF is no longer a dirty word (but errors crash)
gvanrossum Jun 20, 2023
c2c1868
Regenerate generated_cases.c.h
gvanrossum Jun 20, 2023
340570f
Hacky way to avoid flagging macros as uops
gvanrossum Jun 20, 2023
96fa5ef
Support DEOPT_IF
gvanrossum Jun 21, 2023
f2d71f5
Support ERROR_IF (had to forbid unbound_local_error)
gvanrossum Jun 21, 2023
be5dcb2
Attempt to fix check-c-globals
gvanrossum Jun 21, 2023
6ba3557
Add exceptions for static UOp*_Type to c-analyzer
gvanrossum Jun 21, 2023
792f45f
Add c-analyzer exceptions for real
gvanrossum Jun 21, 2023
71ff09c
More sophisticated uop tracing
gvanrossum Jun 21, 2023
6d532c7
gh-105481: refactor instr flag related code into a new InstructionFla…
iritkatriel Jun 21, 2023
bf7abc2
Restore IS_UOP flag generation
gvanrossum Jun 21, 2023
121fbad
Trace uop optimizations if PYTHON_UOP_DEBUG is set
gvanrossum Jun 22, 2023
4dc0019
Merge remote-tracking branch 'origin/main' into optim-exec
gvanrossum Jun 22, 2023
a5880da
Move check for oparg-or-one-short-cache into is_viable_uop()
gvanrossum Jun 22, 2023
59c84a3
Support SET_FUNCTION_CLOSURE
gvanrossum Jun 22, 2023
8fd4a71
Move uop optimizer to ceval.c so it can use static functions defined …
gvanrossum Jun 22, 2023
f0ec658
Support tstate
gvanrossum Jun 22, 2023
65a3c80
Rename 'adjust_cache[s]' to 'tier'
gvanrossum Jun 22, 2023
decfe4a
Reformat Analyzer.__init__ header
gvanrossum Jun 22, 2023
f9db482
Merge remote-tracking branch 'origin/main' into optim-exec
gvanrossum Jun 22, 2023
94441b6
Auto-generate uop values
gvanrossum Jun 23, 2023
8a6bc14
Switch to using macro expansion
gvanrossum Jun 23, 2023
88fbe66
Remove IS_UOP flag
gvanrossum Jun 23, 2023
7f9349e
Allow pointer-sized ints for Tier-2 oparg
gvanrossum Jun 23, 2023
97aab6e
Rename FORBIDDEN_INSTRUCTIONS
gvanrossum Jun 23, 2023
67f7798
Remove separate UOpOptimizerObject type
gvanrossum Jun 23, 2023
2848c3d
Try to use pystats
gvanrossum Jun 23, 2023
5b018c2
Rationalize errors
gvanrossum Jun 24, 2023
a228b5d
Fix typo in pystats output
gvanrossum Jun 24, 2023
2695bb7
Move uop_optimizer back to optimizer.c
gvanrossum Jun 24, 2023
91dd86c
Fix 4/6 failing tests
gvanrossum Jun 25, 2023
7d1750e
Receiving end of macro expansion
gvanrossum Jun 25, 2023
c1bcd0f
Avoid warnings; subtler debug output levels
gvanrossum Jun 25, 2023
9df20bb
Add eval breaker check to _PyUopExecute; fixes test_threading
gvanrossum Jun 25, 2023
b0f6d18
Merge remote-tracking branch 'origin/main' into optim-exec
gvanrossum Jun 25, 2023
acfd143
Add opcode name to ADD_TO_TRACE debug output
gvanrossum Jun 26, 2023
e3471da
Fix test_memoryview by excluding BEFORE_WITH
gvanrossum Jun 26, 2023
44a1ffc
Fix Windows test_mmap -- by removing BUILD_SLICE/STORE_SLICE
gvanrossum Jun 26, 2023
2254f83
Fix bug in ENTER_EXECUTOR - on error go to resume_with_error
gvanrossum Jun 26, 2023
2f6603e
Silence warnings on Windows
gvanrossum Jun 26, 2023
00f8f2c
Replace oparg with operand in more debug output
gvanrossum Jun 26, 2023
4bd7bb6
Fix test_memoryview again by excluding BEFORE_WITH
gvanrossum Jun 26, 2023
9646b4e
Add -Xuops flag and debug tweaks
gvanrossum Jun 26, 2023
9c04e40
Revert "Fix 4/6 failing tests"
gvanrossum Jun 26, 2023
39bc8fe
Use PRIu64 instead of %lld/%llu formats
gvanrossum Jun 26, 2023
6606c5d
Remove superfluous INCREF -- hope it fixes the refleaks
gvanrossum Jun 26, 2023
c14e104
📜🤖 Added by blurb_it.
blurb-it[bot] Jun 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move uop_optimizer back to optimizer.c
Keep the executor in ceval.c; it depends on static functions there.
For the shared definitions I created a new file, pycore_uops.h.
Renamed the definitions to have a _Py prefix.
  • Loading branch information
gvanrossum committed Jun 24, 2023
commit 2695bb7b74cec06ddef6ef07349654839b2688ee
31 changes: 31 additions & 0 deletions Include/internal/pycore_uops.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef Py_INTERNAL_UOPS_H
#define Py_INTERNAL_UOPS_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif

#define _Py_UOP_MAX_TRACE_LENGTH 16

typedef struct {
int opcode;
uintptr_t oparg;
} _PyUOpInstruction;

typedef struct {
_PyExecutorObject base;
_PyUOpInstruction trace[_Py_UOP_MAX_TRACE_LENGTH]; // TODO: variable length
} _PyUOpExecutorObject;

_PyInterpreterFrame *_PyUopExecute(
_PyExecutorObject *executor,
_PyInterpreterFrame *frame,
PyObject **stack_pointer);

#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_UOPS_H */
187 changes: 5 additions & 182 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "pycore_sysmodule.h" // _PySys_Audit()
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "pycore_typeobject.h" // _PySuper_Lookup()
#include "pycore_uops.h" // _PyUOpExecutorObject
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS

#include "pycore_dict.h"
Expand Down Expand Up @@ -2766,39 +2767,13 @@ void Py_LeaveRecursiveCall(void)

///////////////////// Experimental UOp Interpreter /////////////////////

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In its own file, please.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that there are many instructions that use static functions defined in ceval.c. The rest can go in another file, sure.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can be renamed, as needed.
I'm fairly sure the limited set of instructions we are starting with don't use any static functions.

@gvanrossum gvanrossum Jun 26, 2023 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly sure the limited set of instructions we are starting with don't use any static functions.

Alas, they do:

  • format_awaitable_error
  • format_exc_check_arg (4 times)
  • format_exc_unbound (3 times)
  • format_kwargs_error


#define MAX_TRACE_LENGTH 16

typedef struct {
int opcode;
uintptr_t oparg;
} uop_instruction;

typedef struct {
_PyExecutorObject base;
uop_instruction trace[MAX_TRACE_LENGTH]; // TODO: variable length
} UOpExecutorObject;

static void
uop_dealloc(UOpExecutorObject *self) {
PyObject_Free(self);
}

static PyTypeObject UOpExecutor_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "uop_executor",
.tp_basicsize = sizeof(UOpExecutorObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.tp_dealloc = (destructor)uop_dealloc,
};

// UPDATE_MISS_STATS (called by DEOPT_IF) uses next_instr
// TODO: Make it do something useful
#undef UPDATE_MISS_STATS
#define UPDATE_MISS_STATS(INSTNAME) ((void)0)

static _PyInterpreterFrame *
uop_execute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer)
_PyInterpreterFrame *
_PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer)
{
#ifdef LLTRACE
char *uop_debug = Py_GETENV("PYTHON_UOP_DEBUG");
Expand All @@ -2810,15 +2785,15 @@ uop_execute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **
PyCodeObject *code = _PyFrame_GetCode(frame);
_Py_CODEUNIT *instr = frame->prev_instr + 1;
fprintf(stderr,
"Entering uop_execute for %s (%s:%d) at offset %ld\n",
"Entering _PyUopExecute for %s (%s:%d) at offset %ld\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
(long)(instr - (_Py_CODEUNIT *)code->co_code_adaptive));
}
#endif
PyThreadState *tstate = _PyThreadState_GET();
UOpExecutorObject *self = (UOpExecutorObject *)executor;
_PyUOpExecutorObject *self = (_PyUOpExecutorObject *)executor;
OBJECT_STAT_INC(optimization_traces_executed);
_Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive - 1;
int pc = 0;
Expand Down Expand Up @@ -2905,155 +2880,3 @@ PREDICTED(BINARY_OP)
Py_DECREF(self);
return frame;
}

static int
translate_bytecode_to_trace(
PyCodeObject *code,
_Py_CODEUNIT *instr,
uop_instruction *trace,
int max_length)
{
#ifdef LLTRACE
char *uop_debug = Py_GETENV("PYTHON_UOP_DEBUG");
int lltrace = 0;
if (uop_debug != NULL && *uop_debug >= '0') {
lltrace = *uop_debug - '0'; // TODO: Parse an int and all that
}
if (lltrace >= 2) {
fprintf(stderr,
"Optimizing %s (%s:%d) at offset %ld\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
(long)(instr - (_Py_CODEUNIT *)code->co_code_adaptive));
}
#define ADD_TO_TRACE(OPCODE, OPARG) \
if (lltrace >= 2) fprintf(stderr, " ADD_TO_TRACE(%d, %d)\n", (OPCODE), (OPARG)); \
trace[trace_length].opcode = (OPCODE); \
trace[trace_length].oparg = (OPARG); \
trace_length++;
#else
#define ADD_TO_TRACE(OPCODE, OPARG) \
trace[trace_length].opcode = (OPCODE); \
trace[trace_length].oparg = (OPARG); \
trace_length++;
#endif

int trace_length = 0;
// Always reserve space for one uop, plus SET_UP, plus EXIT_TRACE
while (trace_length + 3 <= max_length) {
int opcode = instr->op.code;
int oparg = instr->op.arg;
switch (opcode) {
case LOAD_FAST_LOAD_FAST:
{
// Reserve space for two uops (+ SETUP + EXIT_TRACE)
if (trace_length + 4 > max_length) {
goto done;
}
int oparg1 = oparg >> 4;
int oparg2 = oparg & 15;
ADD_TO_TRACE(LOAD_FAST, oparg1);
ADD_TO_TRACE(LOAD_FAST, oparg2);
break;
}
default:
{
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];
if (expansion->nuops > 0) {
// Reserve space for nuops (+ SETUP + EXIT_TRACE)
if (trace_length + expansion->nuops + 2 > max_length) {
goto done;
}
for (int i = 0; i < expansion->nuops; i++) {
assert(expansion->uops[0].size == 0); // TODO
ADD_TO_TRACE(expansion->uops[i].uop, oparg); // TODO: oparg?
}
break;
}
// fprintf(stderr, "Unsupported opcode %d\n", opcode);
goto done; // Break out of while loop
}
}
instr++;
// Add cache size for opcode
instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
ADD_TO_TRACE(SET_IP, (int)(instr - (_Py_CODEUNIT *)code->co_code_adaptive));
}
done:
if (trace_length > 0) {
ADD_TO_TRACE(EXIT_TRACE, 0);
#ifdef LLTRACE
if (lltrace >= 1) {
fprintf(stderr,
"Created a trace for %s (%s:%d) at offset %ld -- length %d\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
(long)(instr - (_Py_CODEUNIT *)code->co_code_adaptive),
trace_length);
}
#endif
}
else {
#ifdef LLTRACE
if (lltrace >= 2) {
fprintf(stderr,
"No trace for %s (%s:%d) at offset %ld\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
(long)(instr - (_Py_CODEUNIT *)code->co_code_adaptive));
}
#endif
}
return trace_length;

#undef ADD_TO_TRACE
}

static int
uop_optimize(
_PyOptimizerObject *self,
PyCodeObject *code,
_Py_CODEUNIT *instr,
_PyExecutorObject **exec_ptr)
{
uop_instruction trace[MAX_TRACE_LENGTH];
int trace_length = translate_bytecode_to_trace(code, instr, trace, MAX_TRACE_LENGTH);
if (trace_length <= 0) {
// Error or nothing translated
return trace_length;
}
OBJECT_STAT_INC(optimization_traces_created);
UOpExecutorObject *executor = (UOpExecutorObject *)_PyObject_New(&UOpExecutor_Type);
if (executor == NULL) {
return -1;
}
executor->base.execute = uop_execute;
Py_INCREF(self);
memcpy(executor->trace, trace, trace_length * sizeof(uop_instruction));
*exec_ptr = (_PyExecutorObject *)executor;
return 1;
}

static PyTypeObject UOpOptimizer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "UOp Optimizer",
.tp_basicsize = sizeof(_PyOptimizerObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
};

PyObject *
PyUnstable_Optimizer_NewUOpOptimizer(void)
{
_PyOptimizerObject *opt = (_PyOptimizerObject *)_PyObject_New(&UOpOptimizer_Type);
if (opt == NULL) {
return NULL;
}
opt->optimize = uop_optimize;
opt->resume_threshold = UINT16_MAX;
opt->backedge_threshold = 0;
return (PyObject *)opt;
}
Loading