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 to ceval.c so it can use static functions defined …
…there
  • Loading branch information
gvanrossum committed Jun 22, 2023
commit 8fd4a710f92f78ee32f48e4b1215b590f14271e5
277 changes: 277 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2763,3 +2763,280 @@ void Py_LeaveRecursiveCall(void)
{
_Py_LeaveRecursiveCall();
}

///////////////////// 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


typedef struct {
_PyOptimizerObject base;
int traces_executed;
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
int instrs_executed;
} UOpOptimizerObject;

#define MAX_TRACE_LENGTH 16

// UOp opcodes are outside the range of bytecodes or pseudo ops
#define EXIT_TRACE 512
#define SET_IP 513
// TODO: Generate these in Tools/cases_generator
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
#define _BINARY_OP_MULTIPLY_FLOAT 514
#define _BINARY_OP_ADD_FLOAT 515
#define _BINARY_OP_SUBTRACT_FLOAT 516
#define _BINARY_OP_MULTIPLY_INT 517
#define _BINARY_OP_ADD_INT 518
#define _BINARY_OP_SUBTRACT_INT 519
#define _BINARY_OP_ADD_UNICODE 520
#define _GUARD_BOTH_INT 521
#define _GUARD_BOTH_FLOAT 522
#define _GUARD_BOTH_UNICODE 523

typedef struct {

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.

These should also go in a header file

int opcode;
int oparg;
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
} uop_instruction;

typedef struct {
_PyExecutorObject executor; // Base
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
UOpOptimizerObject *optimizer;
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
uop_instruction trace[MAX_TRACE_LENGTH]; // TODO: variable length
} UOpExecutorObject;

static void
uop_dealloc(UOpExecutorObject *self) {
Py_DECREF(self->optimizer);
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,
};

static _PyInterpreterFrame *
uop_execute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer)
{
#ifdef LLTRACE
int lltrace = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__));
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
char *uop_debug = Py_GETENV("PYTHON_UOP_DEBUG");
if (uop_debug != NULL && *uop_debug >= '2') {
lltrace = 2;
}
#endif
UOpExecutorObject *self = (UOpExecutorObject *)executor;
self->optimizer->traces_executed++;
_Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive - 1;
int pc = 0;
int opcode;
int oparg;
for (;;) {
opcode = self->trace[pc].opcode;
oparg = self->trace[pc].oparg;
#ifdef LLTRACE
if (lltrace) {
// TODO: Print line numbers; uop names.
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
const char *opname = opcode < 256 ? _PyOpcode_OpName[opcode] : "";
int stack_level = (int)(stack_pointer - _PyFrame_Stackbase(frame));
fprintf(stderr, "uop %s %d, oparg %d, stack_level %d\n",
opname, opcode, oparg, stack_level);
}
#endif
pc++;
self->optimizer->instrs_executed++;
switch (opcode) {

#undef ENABLE_SPECIALIZATION
#define ENABLE_SPECIALIZATION 0
#include "executor_cases.c.h"

case SET_IP:
{
frame->prev_instr = ip_offset + oparg;
break;
}

case EXIT_TRACE:
{
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return frame;
}

default:
{
fprintf(stderr, "Unknown uop %d, oparg %d\n", opcode, oparg);
Py_FatalError("Unknown uop");
abort(); // Unreachable
for (;;) {}
// Really unreachable
}

}
}

pop_4_error:
STACK_SHRINK(1);
pop_3_error:
STACK_SHRINK(1);
pop_2_error:
STACK_SHRINK(1);
pop_1_error:
STACK_SHRINK(1);
error:
// On ERROR_IF we return NULL as the frame.
// The caller recovers the frame from cframe.current_frame.
#ifdef LLTRACE
if (lltrace) {
fprintf(stderr, "error: [Opcode %d, oparg %d]\n", opcode, oparg);
}
#endif
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return NULL;

PREDICTED(UNPACK_SEQUENCE)
PREDICTED(COMPARE_OP)
PREDICTED(LOAD_SUPER_ATTR)
PREDICTED(STORE_SUBSCR)
PREDICTED(BINARY_SUBSCR)
PREDICTED(BINARY_OP)
// On DEOPT_IF we just repeat the last instruction.
// This presumes nothing was popped from the stack (nor pushed).
#ifdef LLTRACE
if (lltrace) {
fprintf(stderr, "DEOPT: [Opcode %d, oparg %d]\n", opcode, oparg);
}
#endif
_PyFrame_SetStackPointer(frame, stack_pointer);
Py_DECREF(self);
return frame;
}

static int

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.

This should also go in its own file, and a different file from the uop interpreter.

translate_bytecode_to_trace(
PyCodeObject *code,
_Py_CODEUNIT *instr,
uop_instruction *trace,
int max_length)
{
#define ADD_TO_TRACE(OPCODE, OPARG) \
/* fprintf(stderr, "ADD_TO_TRACE(%d, %d)\n", (OPCODE), (OPARG)); */ \
trace[trace_length].opcode = (OPCODE); \
trace[trace_length].oparg = (OPARG); \
trace_length++;

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:
{
if (OPCODE_IS_UOP(opcode)) {
ADD_TO_TRACE(opcode, 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);
// fprintf(stderr, "Created a new trace of length %d\n", trace_length);
}
return trace_length;

#undef ADD_TO_TRACE
}

static int
uop_optimize(

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.

Yet another file, please.

_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;
}
UOpExecutorObject *executor = (UOpExecutorObject *)_PyObject_New(&UOpExecutor_Type);
if (executor == NULL) {
return -1;
}
#ifdef Py_DEBUG
char *uop_debug = Py_GETENV("PYTHON_UOP_DEBUG");
if (uop_debug != NULL && *uop_debug >= '1') {
fprintf(stderr, "Optimizing %s (%s:%d) at offset %ld -- %d uops\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
executor->executor.execute = uop_execute;
Py_INCREF(self);
executor->optimizer = (UOpOptimizerObject *)self;
memcpy(executor->trace, trace, trace_length * sizeof(uop_instruction));
*exec_ptr = (_PyExecutorObject *)executor;
return 1;
}

static PyMemberDef uop_members[] = {
{"traces_executed", Py_T_INT, offsetof(UOpOptimizerObject, traces_executed), 0,
"Total number of traces executed since inception"},
{"instrs_executed", Py_T_INT, offsetof(UOpOptimizerObject, instrs_executed), 0,
"Total number of instructions executed since inception"},
{NULL}
};

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

PyObject *
PyUnstable_Optimizer_NewUOpOptimizer(void)
{
if (PyType_Ready(&UOpOptimizer_Type) < 0) {
return NULL;
}
UOpOptimizerObject *opt = (UOpOptimizerObject *)_PyObject_New(&UOpOptimizer_Type);
if (opt == NULL) {
return NULL;
}
opt->base.optimize = uop_optimize;
opt->base.resume_threshold = UINT16_MAX;
opt->base.backedge_threshold = 0;
opt->traces_executed = 0;
opt->instrs_executed = 0;
return (PyObject *)opt;
}
Loading