Skip to content

Commit e86a841

Browse files
committed
Remove remove_globals. Work in progress
1 parent 2501d9b commit e86a841

5 files changed

Lines changed: 283 additions & 43 deletions

File tree

Include/internal/pycore_optimizer.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ typedef enum _JitSymType {
172172
JIT_SYM_KNOWN_CLASS_TAG = 6,
173173
JIT_SYM_KNOWN_VALUE_TAG = 7,
174174
JIT_SYM_TUPLE_TAG = 8,
175+
JIT_SYM_FUNCTION_TAG = 9,
175176
} JitSymType;
176177

177178
typedef struct _jit_opt_known_class {
@@ -198,24 +199,34 @@ typedef struct _jit_opt_tuple {
198199
uint16_t items[MAX_SYMBOLIC_TUPLE_SIZE];
199200
} JitOptTuple;
200201

202+
typedef struct _jit_opt_function {
203+
uint8_t tag;
204+
uint16_t arg_count;
205+
uint32_t version;
206+
PyCodeObject *code;
207+
} JitOptFunction;
208+
201209
typedef union _jit_opt_symbol {
202210
uint8_t tag;
203211
JitOptKnownClass cls;
204212
JitOptKnownValue value;
205213
JitOptKnownVersion version;
206214
JitOptTuple tuple;
215+
JitOptFunction function;
207216
} JitOptSymbol;
208217

209218

210-
211219
struct _Py_UOpsAbstractFrame {
212220
// Max stacklen
213221
int stack_len;
214222
int locals_len;
215-
223+
PyFunctionObject *function;
216224
JitOptSymbol **stack_pointer;
217225
JitOptSymbol **stack;
218226
JitOptSymbol **locals;
227+
bool function_checked;
228+
bool builtins_watched;
229+
bool globals_watched;
219230
};
220231

221232
typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
@@ -234,6 +245,8 @@ typedef struct _JitOptContext {
234245
_Py_UOpsAbstractFrame *frame;
235246
_Py_UOpsAbstractFrame frames[MAX_ABSTRACT_FRAME_DEPTH];
236247
int curr_frame_depth;
248+
PyInterpreterState *interp;
249+
_PyBloomFilter *dependencies;
237250

238251
// Arena for the symbolic types.
239252
ty_arena t_arena;
@@ -260,6 +273,8 @@ extern void _Py_uop_sym_set_null(JitOptContext *ctx, JitOptSymbol *sym);
260273
extern void _Py_uop_sym_set_non_null(JitOptContext *ctx, JitOptSymbol *sym);
261274
extern void _Py_uop_sym_set_type(JitOptContext *ctx, JitOptSymbol *sym, PyTypeObject *typ);
262275
extern bool _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptSymbol *sym, unsigned int version);
276+
extern void _Py_uop_sym_set_function_version(JitOptContext *ctx, JitOptSymbol *sym, uint32_t version);
277+
extern uint32_t _Py_uop_sym_get_function_version(JitOptSymbol *sym);
263278
extern void _Py_uop_sym_set_const(JitOptContext *ctx, JitOptSymbol *sym, PyObject *const_val);
264279
extern bool _Py_uop_sym_is_bottom(JitOptSymbol *sym);
265280
extern int _Py_uop_sym_truthiness(JitOptSymbol *sym);

Python/optimizer_analysis.c

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#ifdef _Py_TIER2
1+
// #ifdef _Py_TIER2
22

33
/*
44
* This file contains the support code for CPython's uops optimizer.
@@ -124,36 +124,122 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop)
124124
}
125125

126126
static int
127-
incorrect_keys(_PyUOpInstruction *inst, PyObject *obj)
127+
incorrect_keys(uint32_t version, PyObject *obj)
128128
{
129129
if (!PyDict_CheckExact(obj)) {
130130
return 1;
131131
}
132132
PyDictObject *dict = (PyDictObject *)obj;
133-
if (dict->ma_keys->dk_version != inst->operand0) {
133+
if (dict->ma_keys->dk_version != version) {
134134
return 1;
135135
}
136136
return 0;
137137
}
138138

139-
static int
140-
check_next_uop(_PyUOpInstruction *buffer, int size, int pc, uint16_t expected)
139+
int
140+
optimize_guard_globals_version(
141+
_PyUOpInstruction *instr,
142+
uint32_t version,
143+
JitOptContext *ctx
144+
)
141145
{
142-
if (pc + 1 >= size) {
143-
DPRINTF(1, "Cannot rewrite %s at pc %d: buffer too small\n",
144-
_PyOpcode_uop_name[buffer[pc].opcode], pc);
145-
return 0;
146+
if (ctx->frame->function == NULL) {
147+
return -1;
146148
}
147-
uint16_t next_opcode = buffer[pc + 1].opcode;
148-
if (next_opcode != expected) {
149-
DPRINTF(1,
150-
"Cannot rewrite %s at pc %d: unexpected next opcode %s, "
151-
"expected %s\n",
152-
_PyOpcode_uop_name[buffer[pc].opcode], pc,
153-
_PyOpcode_uop_name[next_opcode], _PyOpcode_uop_name[expected]);
154-
return 0;
149+
PyDictObject *globals = (PyDictObject *)ctx->frame->function->func_globals;
150+
if (!PyDict_CheckExact(globals)) {
151+
ctx->done = true;
152+
return -1;
153+
}
154+
if (globals->ma_keys->dk_version != version) {
155+
OPT_STAT_INC(remove_globals_incorrect_keys);
156+
ctx->done = true;
157+
return -1;
158+
}
159+
uint64_t watched_mutations = get_mutations(globals);
160+
if (watched_mutations >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
161+
return -1;
162+
}
163+
if (!ctx->frame->globals_watched) {
164+
PyDict_Watch(GLOBALS_WATCHER_ID, (PyObject *)globals);
165+
ctx->frame->globals_watched = true;
166+
}
167+
instr->opcode = _NOP;
168+
instr->oparg = 0;
169+
return 0;
170+
}
171+
172+
JitOptSymbol *
173+
optimize_load_global_module(
174+
_PyUOpInstruction *instr,
175+
uint32_t version,
176+
JitOptContext *ctx
177+
)
178+
{
179+
if (ctx->frame->function == NULL) {
180+
goto no_opt;
155181
}
156-
return 1;
182+
PyObject *globals = ctx->frame->function->func_globals;
183+
if (incorrect_keys(version, globals)) {
184+
OPT_STAT_INC(remove_globals_incorrect_keys);
185+
ctx->done = true;
186+
goto no_opt;
187+
}
188+
if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
189+
goto no_opt;
190+
}
191+
if (!ctx->frame->globals_watched) {
192+
PyDict_Watch(GLOBALS_WATCHER_ID, globals);
193+
_Py_BloomFilter_Add(ctx->dependencies, globals);
194+
ctx->frame->globals_watched = true;
195+
}
196+
if (!ctx->frame->function_checked && instr[-1].opcode == _NOP) {
197+
instr[-1].opcode = _CHECK_FUNCTION;
198+
instr[-1].operand0 = func_version;
199+
ctx->frame->function_checked = true;
200+
}
201+
if (ctx->frame->function_checked) {
202+
PyObject *cnst = convert_global_to_const(instr, globals, false);
203+
if (cnst != NULL) {
204+
return _Py_uop_sym_new_const(ctx, cnst);
205+
}
206+
}
207+
no_opt:
208+
return _Py_uop_sym_new_not_null(ctx);
209+
}
210+
211+
212+
JitOptSymbol *
213+
optimize_load_global_builtins(
214+
_PyUOpInstruction *instr,
215+
uint32_t version,
216+
JitOptContext *ctx
217+
)
218+
{
219+
if (ctx->frame->function == NULL) {
220+
goto no_opt;
221+
}
222+
PyObject *builtins = ctx->frame->function->func_builtins;
223+
if (incorrect_keys(version, builtins)) {
224+
OPT_STAT_INC(remove_globals_incorrect_keys);
225+
ctx->done = true;
226+
goto no_opt;
227+
}
228+
if (ctx->interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
229+
goto no_opt;
230+
}
231+
if (!ctx->frame->builtins_watched) {
232+
PyDict_Watch(BUILTINS_WATCHER_ID, builtins);
233+
ctx->frame->builtins_watched = true;
234+
}
235+
if (ctx->frame->function_checked) {
236+
PyObject *cnst = convert_global_to_const(instr, builtins, false);
237+
if (cnst != NULL) {
238+
return _Py_uop_sym_new_const(ctx, cnst);
239+
}
240+
}
241+
no_opt:
242+
return _Py_uop_sym_new_not_null(ctx);
157243
}
158244

159245
/* Returns 1 if successfully optimized
@@ -200,7 +286,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
200286
int opcode = inst->opcode;
201287
switch(opcode) {
202288
case _GUARD_GLOBALS_VERSION:
203-
if (incorrect_keys(inst, globals)) {
289+
if (incorrect_keys(inst->operand0, globals)) {
204290
OPT_STAT_INC(remove_globals_incorrect_keys);
205291
return 0;
206292
}
@@ -222,7 +308,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
222308
}
223309
break;
224310
case _LOAD_GLOBAL_BUILTINS:
225-
if (incorrect_keys(inst, builtins)) {
311+
if (incorrect_keys(inst->operand0, builtins)) {
226312
OPT_STAT_INC(remove_globals_incorrect_keys);
227313
return 0;
228314
}
@@ -238,7 +324,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
238324
}
239325
break;
240326
case _LOAD_GLOBAL_MODULE:
241-
if (incorrect_keys(inst, globals)) {
327+
if (incorrect_keys(inst->operand0, globals)) {
242328
OPT_STAT_INC(remove_globals_incorrect_keys);
243329
return 0;
244330
}
@@ -350,6 +436,8 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
350436
#define sym_get_type _Py_uop_sym_get_type
351437
#define sym_matches_type _Py_uop_sym_matches_type
352438
#define sym_matches_type_version _Py_uop_sym_matches_type_version
439+
#define sym_get_function_version _Py_uop_sym_get_function_version
440+
#define sym_set_function_version _Py_uop_sym_set_function_version
353441
#define sym_set_null(SYM) _Py_uop_sym_set_null(ctx, SYM)
354442
#define sym_set_non_null(SYM) _Py_uop_sym_set_non_null(ctx, SYM)
355443
#define sym_set_type(SYM, TYPE) _Py_uop_sym_set_type(ctx, SYM, TYPE)
@@ -447,14 +535,16 @@ get_code_with_logging(_PyUOpInstruction *op)
447535
/* 1 for success, 0 for not ready, cannot error at the moment. */
448536
static int
449537
optimize_uops(
450-
PyCodeObject *co,
538+
_PyInterpreterFrame *real_frame,
451539
_PyUOpInstruction *trace,
452540
int trace_len,
453541
int curr_stacklen,
454542
_PyBloomFilter *dependencies
455543
)
456544
{
457-
545+
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(real_frame->f_funcobj);
546+
PyCodeObject *co = _PyFrame_GetCode(real_frame);
547+
assert(PyFunction_Check(func));
458548
JitOptContext context;
459549
JitOptContext *ctx = &context;
460550
uint32_t opcode = UINT16_MAX;
@@ -468,11 +558,14 @@ optimize_uops(
468558
if (frame == NULL) {
469559
return -1;
470560
}
561+
frame->function = (PyFunctionObject *)func;
471562
ctx->curr_frame_depth++;
472563
ctx->frame = frame;
473564
ctx->done = false;
474565
ctx->out_of_space = false;
475566
ctx->contradiction = false;
567+
ctx->dependencies = dependencies;
568+
ctx->interp = _PyInterpreterState_GET();
476569

477570
_PyUOpInstruction *this_instr = NULL;
478571
for (int i = 0; !ctx->done; i++) {
@@ -649,7 +742,7 @@ _Py_uop_analyze_and_optimize(
649742
}
650743

651744
length = optimize_uops(
652-
_PyFrame_GetCode(frame), buffer,
745+
frame, buffer,
653746
length, curr_stacklen, dependencies);
654747

655748
if (length <= 0) {

Python/optimizer_bytecodes.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
2020
#define sym_new_null _Py_uop_sym_new_null
2121
#define sym_matches_type _Py_uop_sym_matches_type
2222
#define sym_matches_type_version _Py_uop_sym_matches_type_version
23+
#define sym_get_function_version _Py_uop_sym_get_function_version
24+
#define sym_set_function_version _Py_uop_sym_set_function_version
2325
#define sym_get_type _Py_uop_sym_get_type
2426
#define sym_has_type _Py_uop_sym_has_type
2527
#define sym_set_null(SYM) _Py_uop_sym_set_null(ctx, SYM)
@@ -596,12 +598,18 @@ dummy_func(void) {
596598
}
597599

598600
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
599-
if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) {
600-
assert(PyFunction_Check(sym_get_const(callable)));
601-
REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
602-
this_instr->operand1 = (uintptr_t)sym_get_const(callable);
601+
assert(func_version != 0);
602+
if (sym_get_function_version(callable) == func_version) {
603+
REPLACE_OP(this_instr, _NOP, 0, 0);
604+
}
605+
else {
606+
if (sym_is_const(callable) && sym_matches_type(callable, &PyFunction_Type)) {
607+
assert(PyFunction_Check(sym_get_const(callable)));
608+
REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
609+
this_instr->operand1 = (uintptr_t)sym_get_const(callable);
610+
}
611+
sym_set_function_version(ctx, callable, func_version);
603612
}
604-
sym_set_type(callable, &PyFunction_Type);
605613
}
606614

607615
op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
@@ -762,13 +770,21 @@ dummy_func(void) {
762770
ctx->frame = new_frame;
763771
ctx->curr_frame_depth++;
764772
stack_pointer = new_frame->stack_pointer;
765-
co = get_code(this_instr);
766-
if (co == NULL) {
773+
uint64_t operand = this_instr->operand0;
774+
if (operand == 0) {
767775
// should be about to _EXIT_TRACE anyway
768776
ctx->done = true;
769777
break;
770778
}
771-
779+
if (operand & 1) {
780+
co = (PyCodeObject *)(operand & ~1);
781+
}
782+
else {
783+
PyFunctionObject *func = (PyFunctionObject *)operand;
784+
assert(PyFunction_Check(func));
785+
co = (PyCodeObject *)func->func_code;
786+
new_frame->function = func;
787+
}
772788
/* Stack space handling */
773789
int framesize = co->co_framesize;
774790
assert(framesize > 0);
@@ -889,6 +905,20 @@ dummy_func(void) {
889905
}
890906
}
891907

908+
op(_GUARD_GLOBALS_VERSION, (version/1 --)) {
909+
optimize_guard_globals_version(this_instr, version, ctx);
910+
}
911+
912+
op(_LOAD_GLOBAL_MODULE, (version/1, unused/1, index/1 -- res)) {
913+
(void)index;
914+
res = optimize_load_global_module(this_instr, version, ctx);
915+
}
916+
917+
op(_LOAD_GLOBAL_BUILTINS, (version/1, index/1 -- res)) {
918+
(void)index;
919+
res = optimize_load_global_builtins(this_instr, version, ctx);
920+
}
921+
892922

893923
// END BYTECODES //
894924

0 commit comments

Comments
 (0)