Skip to content

Commit 2125cd5

Browse files
Add a constant pool to all executors
1 parent 57f4d09 commit 2125cd5

File tree

7 files changed

+152
-68
lines changed

7 files changed

+152
-68
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ typedef struct _PyExitData {
5252
typedef struct _PyExecutorObject {
5353
PyObject_VAR_HEAD
5454
const _PyUOpInstruction *trace;
55+
PyObject *constant_pool;
5556
_PyVMData vm_data; /* Used by the VM, but opaque to the optimizer */
5657
uint32_t exit_count;
5758
uint32_t code_size;
@@ -98,7 +99,7 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);
9899

99100
int _Py_uop_analyze_and_optimize(_PyInterpreterFrame *frame,
100101
_PyUOpInstruction *trace, int trace_len, int curr_stackentries,
101-
_PyBloomFilter *dependencies);
102+
_PyBloomFilter *dependencies, PyObject **constant_pool_ptr);
102103

103104
extern PyTypeObject _PyUOpExecutor_Type;
104105

@@ -278,6 +279,7 @@ typedef struct _JitOptContext {
278279
bool contradiction;
279280
// Has the builtins dict been watched?
280281
bool builtins_watched;
282+
PyObject *constant_pool;
281283
// The current "executing" frame.
282284
_Py_UOpsAbstractFrame *frame;
283285
_Py_UOpsAbstractFrame frames[MAX_ABSTRACT_FRAME_DEPTH];
@@ -324,8 +326,9 @@ extern bool _Py_uop_sym_is_compact_int(JitOptRef sym);
324326
extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
325327
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
326328

327-
extern void _Py_uop_abstractcontext_init(JitOptContext *ctx);
329+
extern int _Py_uop_abstractcontext_init(JitOptContext *ctx);
328330
extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);
331+
extern int _Py_uop_promote_to_constant_pool(JitOptContext *ctx, PyObject *obj);
329332

330333
extern _Py_UOpsAbstractFrame *_Py_uop_frame_new(
331334
JitOptContext *ctx,

Lib/test/test_capi/test_opt.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,8 +2594,7 @@ def testfunc(n):
25942594
self.assertIsNotNone(ex)
25952595
uops = get_opnames(ex)
25962596

2597-
# For now... until we constant propagate it away.
2598-
self.assertIn("_BINARY_OP", uops)
2597+
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
25992598

26002599
def test_jitted_code_sees_changed_globals(self):
26012600
"Issue 136154: Check that jitted code spots the change in the globals"

Python/optimizer.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
100100
}
101101

102102
static _PyExecutorObject *
103-
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies);
103+
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, PyObject *constant_pool);
104104

105105
static int
106106
uop_optimize(_PyInterpreterFrame *frame, _Py_CODEUNIT *instr,
@@ -388,6 +388,7 @@ static int
388388
executor_traverse(PyObject *o, visitproc visit, void *arg)
389389
{
390390
_PyExecutorObject *executor = _PyExecutorObject_CAST(o);
391+
Py_VISIT(executor->constant_pool);
391392
for (uint32_t i = 0; i < executor->exit_count; i++) {
392393
Py_VISIT(executor->exits[i].executor);
393394
}
@@ -1115,13 +1116,15 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length)
11151116
/* Executor side exits */
11161117

11171118
static _PyExecutorObject *
1118-
allocate_executor(int exit_count, int length)
1119+
allocate_executor(int exit_count, int length, PyObject *constant_pool)
11191120
{
11201121
int size = exit_count*sizeof(_PyExitData) + length*sizeof(_PyUOpInstruction);
11211122
_PyExecutorObject *res = PyObject_GC_NewVar(_PyExecutorObject, &_PyUOpExecutor_Type, size);
11221123
if (res == NULL) {
11231124
return NULL;
11241125
}
1126+
// Transfer ownership
1127+
res->constant_pool = constant_pool;
11251128
res->trace = (_PyUOpInstruction *)(res->exits + exit_count);
11261129
res->code_size = length;
11271130
res->exit_count = exit_count;
@@ -1196,10 +1199,10 @@ sanity_check(_PyExecutorObject *executor)
11961199
* and not a NOP.
11971200
*/
11981201
static _PyExecutorObject *
1199-
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies)
1202+
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, PyObject *constant_pool)
12001203
{
12011204
int exit_count = count_exits(buffer, length);
1202-
_PyExecutorObject *executor = allocate_executor(exit_count, length);
1205+
_PyExecutorObject *executor = allocate_executor(exit_count, length, constant_pool);
12031206
if (executor == NULL) {
12041207
return NULL;
12051208
}
@@ -1293,6 +1296,7 @@ uop_optimize(
12931296
_PyBloomFilter dependencies;
12941297
_Py_BloomFilter_Init(&dependencies);
12951298
PyInterpreterState *interp = _PyInterpreterState_GET();
1299+
PyObject *constant_pool = NULL;
12961300
if (interp->jit_uop_buffer == NULL) {
12971301
interp->jit_uop_buffer = (_PyUOpInstruction *)_PyObject_VirtualAlloc(UOP_BUFFER_SIZE);
12981302
if (interp->jit_uop_buffer == NULL) {
@@ -1316,7 +1320,7 @@ uop_optimize(
13161320
if (!is_noopt) {
13171321
length = _Py_uop_analyze_and_optimize(frame, buffer,
13181322
length,
1319-
curr_stackentries, &dependencies);
1323+
curr_stackentries, &dependencies, &constant_pool);
13201324
if (length <= 0) {
13211325
return length;
13221326
}
@@ -1339,7 +1343,7 @@ uop_optimize(
13391343
OPT_HIST(effective_trace_length(buffer, length), optimized_trace_length_hist);
13401344
length = prepare_for_execution(buffer, length);
13411345
assert(length <= UOP_MAX_TRACE_LENGTH);
1342-
_PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies);
1346+
_PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies, constant_pool);
13431347
if (executor == NULL) {
13441348
return -1;
13451349
}
@@ -1512,7 +1516,7 @@ _PyExecutor_GetColdExecutor(void)
15121516
if (interp->cold_executor != NULL) {
15131517
return interp->cold_executor;
15141518
}
1515-
_PyExecutorObject *cold = allocate_executor(0, 1);
1519+
_PyExecutorObject *cold = allocate_executor(0, 1, NULL);
15161520
if (cold == NULL) {
15171521
Py_FatalError("Cannot allocate core JIT code");
15181522
}
@@ -1575,6 +1579,8 @@ executor_clear(PyObject *op)
15751579
unlink_executor(executor);
15761580
executor->vm_data.valid = 0;
15771581

1582+
Py_CLEAR(executor->constant_pool);
1583+
15781584
/* It is possible for an executor to form a reference
15791585
* cycle with itself, so decref'ing a side exit could
15801586
* free the executor unless we hold a strong reference to it

Python/optimizer_analysis.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ incorrect_keys(PyObject *obj, uint32_t version)
153153
(INST)->oparg = ARG; \
154154
(INST)->operand0 = OPERAND;
155155

156+
#define PROMOTE_TO_CONSTANT_POOL _Py_uop_promote_to_constant_pool
157+
156158
/* Shortened forms for convenience, used in optimizer_bytecodes.c */
157159
#define sym_is_not_null _Py_uop_sym_is_not_null
158160
#define sym_is_const _Py_uop_sym_is_const
@@ -290,7 +292,8 @@ optimize_uops(
290292
_PyUOpInstruction *trace,
291293
int trace_len,
292294
int curr_stacklen,
293-
_PyBloomFilter *dependencies
295+
_PyBloomFilter *dependencies,
296+
PyObject **constant_pool_ptr
294297
)
295298
{
296299
assert(!PyErr_Occurred());
@@ -310,9 +313,13 @@ optimize_uops(
310313
interp->type_watchers[TYPE_WATCHER_ID] = type_watcher_callback;
311314
}
312315

313-
_Py_uop_abstractcontext_init(ctx);
316+
if (_Py_uop_abstractcontext_init(ctx)) {
317+
return 0;
318+
}
319+
314320
_Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, (PyCodeObject *)func->func_code, curr_stacklen, NULL, 0);
315321
if (frame == NULL) {
322+
_Py_uop_abstractcontext_fini(ctx);
316323
return 0;
317324
}
318325
frame->func = func;
@@ -367,6 +374,7 @@ optimize_uops(
367374

368375
/* Either reached the end or cannot optimize further, but there
369376
* would be no benefit in retrying later */
377+
*constant_pool_ptr = Py_NewRef(ctx->constant_pool);
370378
_Py_uop_abstractcontext_fini(ctx);
371379
if (first_valid_check_stack != NULL) {
372380
assert(first_valid_check_stack->opcode == _CHECK_STACK_SPACE);
@@ -522,14 +530,15 @@ _Py_uop_analyze_and_optimize(
522530
_PyUOpInstruction *buffer,
523531
int length,
524532
int curr_stacklen,
525-
_PyBloomFilter *dependencies
533+
_PyBloomFilter *dependencies,
534+
PyObject **constant_pool_ptr
526535
)
527536
{
528537
OPT_STAT_INC(optimizer_attempts);
529538

530539
length = optimize_uops(
531540
_PyFrame_GetFunction(frame), buffer,
532-
length, curr_stacklen, dependencies);
541+
length, curr_stacklen, dependencies, constant_pool_ptr);
533542

534543
if (length == 0) {
535544
return length;

0 commit comments

Comments
 (0)