From 279e61d5d3490f3c18fe8dc35379a027fe3902c5 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 6 Aug 2024 17:38:48 -0700 Subject: [PATCH 01/11] Promote immortal locals to constants --- Python/optimizer_bytecodes.c | 27 +++++++++++++++++++++++++++ Python/optimizer_cases.c.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 9a1b9da52f4bb5..e4066513af944f 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -81,6 +81,12 @@ dummy_func(void) { op(_LOAD_FAST, (-- value)) { value = GETLOCAL(oparg); + if (sym_is_const(value)) { + PyObject *val = sym_get_const(value); + if (_Py_IsImmortal(val)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + } + } } op(_LOAD_FAST_AND_CLEAR, (-- value)) { @@ -323,10 +329,31 @@ dummy_func(void) { } res = sym_new_const(ctx, temp); Py_DECREF(temp); + // TODO gh-115506: + // replace opcode with constant propagated one and update tests! + } + else { + res = sym_new_type(ctx, &PyUnicode_Type); + } + } + + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { + _Py_UopsSymbol *res; + if (sym_is_const(left) && sym_is_const(right) && + sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { + PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); + if (temp == NULL) { + goto error; + } + res = sym_new_const(ctx, temp); + Py_DECREF(temp); + // TODO gh-115506: + // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyUnicode_Type); } + GETLOCAL(this_instr->operand) = res; } op(_BINARY_SUBSCR_INIT_CALL, (container, sub -- new_frame: _Py_UOpsAbstractFrame *)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 4d172e3c762704..efb554a46c5555 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -39,6 +39,12 @@ case _LOAD_FAST: { _Py_UopsSymbol *value; value = GETLOCAL(oparg); + if (sym_is_const(value)) { + PyObject *val = sym_get_const(value); + if (_Py_IsImmortal(val)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + } + } stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); @@ -473,6 +479,8 @@ } res = sym_new_const(ctx, temp); Py_DECREF(temp); + // TODO gh-115506: + // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -484,6 +492,26 @@ } case _BINARY_OP_INPLACE_ADD_UNICODE: { + _Py_UopsSymbol *right; + _Py_UopsSymbol *left; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + _Py_UopsSymbol *res; + if (sym_is_const(left) && sym_is_const(right) && + sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { + PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); + if (temp == NULL) { + goto error; + } + res = sym_new_const(ctx, temp); + Py_DECREF(temp); + // TODO gh-115506: + // replace opcode with constant propagated one and update tests! + } + else { + res = sym_new_type(ctx, &PyUnicode_Type); + } + GETLOCAL(this_instr->operand) = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); break; From 89d6d45f763a8b9f9dfe138515bbd11120e671d1 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 6 Aug 2024 17:39:13 -0700 Subject: [PATCH 02/11] Promote constant math and locals to constant loads --- Include/internal/pycore_opcode_metadata.h | 16 +-- Include/internal/pycore_optimizer.h | 3 +- Lib/test/test_capi/test_opt.py | 3 +- Python/bytecodes.c | 16 +-- Python/generated_cases.c.h | 27 +++++ Python/optimizer.c | 13 ++- Python/optimizer_analysis.c | 14 ++- Python/optimizer_bytecodes.c | 129 +++++++++++++++++++--- Python/optimizer_cases.c.h | 128 ++++++++++++++++++--- 9 files changed, 292 insertions(+), 57 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 3344ede5e92c07..862796b6bb67f6 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1257,14 +1257,14 @@ extern const struct opcode_macro_expansion _PyOpcode_macro_expansion[256]; const struct opcode_macro_expansion _PyOpcode_macro_expansion[256] = { [BINARY_OP] = { .nuops = 1, .uops = { { _BINARY_OP, 0, 0 } } }, - [BINARY_OP_ADD_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } }, - [BINARY_OP_ADD_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_ADD_INT, 0, 0 } } }, - [BINARY_OP_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 2, .uops = { { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, 0, 0 } } }, - [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, 0, 0 } } }, - [BINARY_OP_MULTIPLY_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_MULTIPLY_INT, 0, 0 } } }, - [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 2, .uops = { { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } }, - [BINARY_OP_SUBTRACT_INT] = { .nuops = 2, .uops = { { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_SUBTRACT_INT, 0, 0 } } }, + [BINARY_OP_ADD_FLOAT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_ADD_FLOAT, 0, 0 } } }, + [BINARY_OP_ADD_INT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_ADD_INT, 0, 0 } } }, + [BINARY_OP_ADD_UNICODE] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_ADD_UNICODE, 0, 0 } } }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { .nuops = 4, .uops = { { _NOP, 0, 0 }, { _NOP, 0, 0 }, { _GUARD_BOTH_UNICODE, 0, 0 }, { _BINARY_OP_INPLACE_ADD_UNICODE, 0, 0 } } }, + [BINARY_OP_MULTIPLY_FLOAT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_MULTIPLY_FLOAT, 0, 0 } } }, + [BINARY_OP_MULTIPLY_INT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_MULTIPLY_INT, 0, 0 } } }, + [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_FLOAT, 0, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, 0, 0 } } }, + [BINARY_OP_SUBTRACT_INT] = { .nuops = 3, .uops = { { _NOP, 0, 0 }, { _GUARD_BOTH_INT, 0, 0 }, { _BINARY_OP_SUBTRACT_INT, 0, 0 } } }, [BINARY_SLICE] = { .nuops = 1, .uops = { { _BINARY_SLICE, 0, 0 } } }, [BINARY_SUBSCR] = { .nuops = 1, .uops = { { _BINARY_SUBSCR, 0, 0 } } }, [BINARY_SUBSCR_DICT] = { .nuops = 1, .uops = { { _BINARY_SUBSCR_DICT, 0, 0 } } }, diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index f92c0a0cddf906..993ea704510db2 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -76,6 +76,7 @@ typedef struct _PyExecutorObject { size_t jit_size; void *jit_code; void *jit_side_entry; + PyObject *refs; _PyExitData exits[1]; } _PyExecutorObject; @@ -144,7 +145,7 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp); int _Py_uop_analyze_and_optimize(struct _PyInterpreterFrame *frame, _PyUOpInstruction *trace, int trace_len, int curr_stackentries, - _PyBloomFilter *dependencies); + _PyBloomFilter *dependencies, PyObject *refs); extern PyTypeObject _PyCounterExecutor_Type; extern PyTypeObject _PyCounterOptimizer_Type; diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index f1ab72180d714d..8df6355f28e17c 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -991,7 +991,8 @@ def testfunc(n): self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_GUARD_BOTH_INT", uops) - self.assertIn("_BINARY_OP_ADD_INT", uops) + self.assertNotIn("_BINARY_OP_ADD_INT", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) # Try again, but between the runs, set the global to a float. # This should result in no executor the second time. ns = {} diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8535306d9c7a03..7d2bfaa5e28751 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -495,11 +495,11 @@ dummy_func( } macro(BINARY_OP_MULTIPLY_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_MULTIPLY_INT; + NOP + _GUARD_BOTH_INT + unused/1 + _BINARY_OP_MULTIPLY_INT; macro(BINARY_OP_ADD_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_ADD_INT; + NOP + _GUARD_BOTH_INT + unused/1 + _BINARY_OP_ADD_INT; macro(BINARY_OP_SUBTRACT_INT) = - _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; + NOP + _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -558,11 +558,11 @@ dummy_func( } macro(BINARY_OP_MULTIPLY_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_MULTIPLY_FLOAT; + NOP + _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_MULTIPLY_FLOAT; macro(BINARY_OP_ADD_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_ADD_FLOAT; + NOP + _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_ADD_FLOAT; macro(BINARY_OP_SUBTRACT_FLOAT) = - _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; + NOP + _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; op(_GUARD_BOTH_UNICODE, (left, right -- left, right)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -585,7 +585,7 @@ dummy_func( } macro(BINARY_OP_ADD_UNICODE) = - _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_ADD_UNICODE; + NOP + _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_ADD_UNICODE; // This is a subtle one. It's a super-instruction for // BINARY_OP_ADD_UNICODE followed by STORE_FAST @@ -634,7 +634,7 @@ dummy_func( } macro(BINARY_OP_INPLACE_ADD_UNICODE) = - _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_INPLACE_ADD_UNICODE; + NOP + NOP + _GUARD_BOTH_UNICODE + unused/1 + _BINARY_OP_INPLACE_ADD_UNICODE; family(BINARY_SUBSCR, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = { BINARY_SUBSCR_DICT, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 1201fe82efb919..5475530f617da0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -62,6 +62,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_FLOAT right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -98,6 +101,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_INT right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -133,6 +139,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_UNICODE right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -167,6 +176,12 @@ static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size"); _PyStackRef left; _PyStackRef right; + // _NOP + { + } + // _NOP + { + } // _GUARD_BOTH_UNICODE right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -229,6 +244,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_FLOAT right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -265,6 +283,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_INT right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -300,6 +321,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_FLOAT right = stack_pointer[-1]; left = stack_pointer[-2]; @@ -336,6 +360,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + // _NOP + { + } // _GUARD_BOTH_INT right = stack_pointer[-1]; left = stack_pointer[-2]; diff --git a/Python/optimizer.c b/Python/optimizer.c index 978649faa04d45..d46eb98d940169 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -257,6 +257,7 @@ uop_dealloc(_PyExecutorObject *self) { _PyObject_GC_UNTRACK(self); assert(self->vm_data.code == NULL); unlink_executor(self); + Py_CLEAR(self->refs); #ifdef _Py_JIT _PyJIT_Free(self); #endif @@ -360,6 +361,7 @@ static int executor_traverse(PyObject *o, visitproc visit, void *arg) { _PyExecutorObject *executor = (_PyExecutorObject *)o; + Py_VISIT(executor->refs); for (uint32_t i = 0; i < executor->exit_count; i++) { Py_VISIT(executor->exits[i].executor); } @@ -1066,6 +1068,7 @@ allocate_executor(int exit_count, int length) res->trace = (_PyUOpInstruction *)(res->exits + exit_count); res->code_size = length; res->exit_count = exit_count; + res->refs = NULL; return res; } @@ -1248,11 +1251,16 @@ uop_optimize( assert(length < UOP_MAX_TRACE_LENGTH); OPT_STAT_INC(traces_created); char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); + PyObject *refs = PyList_New(0); + if (refs == NULL) { + return -1; + } if (env_var == NULL || *env_var == '\0' || *env_var > '0') { length = _Py_uop_analyze_and_optimize(frame, buffer, length, - curr_stackentries, &dependencies); + curr_stackentries, &dependencies, refs); if (length <= 0) { + Py_DECREF(refs); return length; } } @@ -1279,8 +1287,10 @@ uop_optimize( assert(length <= UOP_MAX_TRACE_LENGTH); _PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies); if (executor == NULL) { + Py_DECREF(refs); return -1; } + executor->refs = refs; assert(length <= UOP_MAX_TRACE_LENGTH); *exec_ptr = executor; return 1; @@ -1584,6 +1594,7 @@ executor_clear(_PyExecutorObject *executor) * free the executor unless we hold a strong reference to it */ Py_INCREF(executor); + Py_CLEAR(executor->refs); for (uint32_t i = 0; i < executor->exit_count; i++) { executor->exits[i].temperature = initial_unreachable_backoff_counter(); Py_CLEAR(executor->exits[i].executor); diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index b202b58a8b7214..2889b129bd73c1 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -301,9 +301,9 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define GETLOCAL(idx) ((ctx->frame->locals[idx])) #define REPLACE_OP(INST, OP, ARG, OPERAND) \ - INST->opcode = OP; \ - INST->oparg = ARG; \ - INST->operand = OPERAND; + (INST)->opcode = OP; \ + (INST)->oparg = ARG; \ + (INST)->operand = OPERAND; /* Shortened forms for convenience, used in optimizer_bytecodes.c */ #define sym_is_not_null _Py_uop_sym_is_not_null @@ -392,7 +392,8 @@ optimize_uops( _PyUOpInstruction *trace, int trace_len, int curr_stacklen, - _PyBloomFilter *dependencies + _PyBloomFilter *dependencies, + PyObject *refs ) { @@ -580,7 +581,8 @@ _Py_uop_analyze_and_optimize( _PyUOpInstruction *buffer, int length, int curr_stacklen, - _PyBloomFilter *dependencies + _PyBloomFilter *dependencies, + PyObject *refs ) { OPT_STAT_INC(optimizer_attempts); @@ -592,7 +594,7 @@ _Py_uop_analyze_and_optimize( length = optimize_uops( _PyFrame_GetCode(frame), buffer, - length, curr_stacklen, dependencies); + length, curr_stacklen, dependencies, refs); if (length <= 0) { return length; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e4066513af944f..6da2710e7d13c0 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -63,6 +63,7 @@ dummy_func(void) { _Py_UOpsContext *ctx; _PyUOpInstruction *this_instr; _PyBloomFilter *dependencies; + PyObject *refs; int modified; int curr_space; int max_space; @@ -86,6 +87,12 @@ dummy_func(void) { if (_Py_IsImmortal(val)) { REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); } + else { + if (PyList_Append(refs, val)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)val); + } } } @@ -202,10 +209,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -223,10 +241,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -244,10 +273,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -266,10 +306,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -288,10 +339,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -310,10 +372,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -327,10 +400,21 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -345,10 +429,23 @@ dummy_func(void) { if (temp == NULL) { goto error; } + assert(this_instr[-3].opcode == _NOP); + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! + REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); } else { res = sym_new_type(ctx, &PyUnicode_Type); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index efb554a46c5555..293dca1692bdd9 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -44,6 +44,12 @@ if (_Py_IsImmortal(val)) { REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); } + else { + if (PyList_Append(refs, val)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)val); + } } stack_pointer[0] = value; stack_pointer += 1; @@ -253,10 +259,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -283,10 +300,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -313,10 +341,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and add tests! } else { res = sym_new_type(ctx, &PyLong_Type); @@ -375,10 +414,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -406,10 +456,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -437,10 +498,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -477,10 +549,21 @@ if (temp == NULL) { goto error; } + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -503,10 +586,23 @@ if (temp == NULL) { goto error; } + assert(this_instr[-3].opcode == _NOP); + assert(this_instr[-2].opcode == _NOP); + assert(this_instr[-1].opcode == _NOP); + REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); + REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); + if (_Py_IsImmortal(temp)) { + REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + } + else { + if (PyList_Append(refs, temp)) { + goto error; + } + REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + } res = sym_new_const(ctx, temp); Py_DECREF(temp); - // TODO gh-115506: - // replace opcode with constant propagated one and update tests! + REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); } else { res = sym_new_type(ctx, &PyUnicode_Type); From 8977c393ea3500e316e4f0bee692852103763f85 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Tue, 6 Aug 2024 17:45:35 -0700 Subject: [PATCH 03/11] Promote constant copies to constant loads --- Python/optimizer_bytecodes.c | 12 ++++++++++++ Python/optimizer_cases.c.h | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 6da2710e7d13c0..5779311b913f6e 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -568,6 +568,18 @@ dummy_func(void) { } op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { + if (sym_is_const(bottom)) { + PyObject *value = sym_get_const(bottom); + if (_Py_IsImmortal(value)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)value); + } + else { + if (PyList_Append(refs, value)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)value); + } + } assert(oparg > 0); top = bottom; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 293dca1692bdd9..137a2b7d2de705 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -2279,6 +2279,18 @@ _Py_UopsSymbol *bottom; _Py_UopsSymbol *top; bottom = stack_pointer[-1 - (oparg-1)]; + if (sym_is_const(bottom)) { + PyObject *value = sym_get_const(bottom); + if (_Py_IsImmortal(value)) { + REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)value); + } + else { + if (PyList_Append(refs, value)) { + goto error; + } + REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)value); + } + } assert(oparg > 0); top = bottom; stack_pointer[0] = top; From 60e1e10e9ceef9c0acacbcb13e3f08a34a9397d4 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Aug 2024 10:57:08 -0700 Subject: [PATCH 04/11] Deduplicate constants, and make some attribute loads constant --- Python/optimizer.c | 18 ++++- Python/optimizer_analysis.c | 20 +++++- Python/optimizer_bytecodes.c | 128 ++++++++++++----------------------- Python/optimizer_cases.c.h | 128 ++++++++++++----------------------- 4 files changed, 123 insertions(+), 171 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index d46eb98d940169..3cedabe92275b8 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1251,7 +1251,7 @@ uop_optimize( assert(length < UOP_MAX_TRACE_LENGTH); OPT_STAT_INC(traces_created); char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); - PyObject *refs = PyList_New(0); + PyObject *refs = PyDict_New(); if (refs == NULL) { return -1; } @@ -1290,7 +1290,21 @@ uop_optimize( Py_DECREF(refs); return -1; } - executor->refs = refs; + PyObject *refs_tuple = PyTuple_New(PyDict_GET_SIZE(refs)); + if (refs_tuple == NULL) { + Py_DECREF(executor); + return -1; + } + PyObject *k, *v; + Py_ssize_t i = 0, p = 0; + Py_BEGIN_CRITICAL_SECTION(refs); + while (PyDict_Next(refs, &p, &k, &v)) { + PyTuple_SET_ITEM(refs_tuple, i++, Py_NewRef(v)); + } + Py_END_CRITICAL_SECTION(); + assert(i == Py_SIZE(refs_tuple)); + Py_DECREF(refs); + executor->refs = refs_tuple; assert(length <= UOP_MAX_TRACE_LENGTH); *exec_ptr = executor; return 1; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 2889b129bd73c1..c32b06e1d97c2b 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -289,7 +289,25 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, return 0; } - +static int +merge_const(PyObject *refs, PyObject **o_p) +{ + PyObject *o = *o_p; + PyObject *k = _PyCode_ConstantKey(o); + // A key of tuple[int, object] is used for "other" constants, which may + // include arbitrary objects. We don't want to try to hash them or check + // their equality, so just make the key a tuple[int] (their address): + if (PyTuple_CheckExact(k) && PyLong_CheckExact(PyTuple_GET_ITEM(k, 0))) { + Py_SETREF(k, PyTuple_Pack(1, PyTuple_GET_ITEM(k, 0))); + if (k == NULL) { + return -1; + } + } + int res = PyDict_SetDefaultRef(refs, k, o, o_p); + Py_DECREF(k); + Py_DECREF(o); + return res; +} #define STACK_LEVEL() ((int)(stack_pointer - ctx->frame->stack)) #define STACK_SIZE() ((int)(ctx->frame->stack_len)) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 5779311b913f6e..760e35d9367151 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -84,15 +84,11 @@ dummy_func(void) { value = GETLOCAL(oparg); if (sym_is_const(value)) { PyObject *val = sym_get_const(value); - if (_Py_IsImmortal(val)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); - } - else { - if (PyList_Append(refs, val)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)val); + if (merge_const(refs, &val) < 0) { + goto error; } + int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); } } @@ -213,15 +209,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -245,15 +237,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -277,15 +265,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -310,15 +294,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -343,15 +323,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -376,15 +352,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -404,15 +376,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -434,15 +402,11 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(&this_instr[-1], opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); @@ -570,15 +534,11 @@ dummy_func(void) { op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { if (sym_is_const(bottom)) { PyObject *value = sym_get_const(bottom); - if (_Py_IsImmortal(value)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)value); - } - else { - if (PyList_Append(refs, value)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)value); + if (merge_const(refs, &value) < 0) { + goto error; } + int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); } assert(oparg > 0); top = bottom; @@ -657,7 +617,7 @@ dummy_func(void) { } op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr, null if (oparg & 1))) { - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); null = sym_new_null(ctx); (void)descr; (void)owner; @@ -665,19 +625,19 @@ dummy_func(void) { op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; } op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; } op(_LOAD_ATTR_METHOD_LAZY_DICT, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 137a2b7d2de705..72cebb5cf1c4c1 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -41,15 +41,11 @@ value = GETLOCAL(oparg); if (sym_is_const(value)) { PyObject *val = sym_get_const(value); - if (_Py_IsImmortal(val)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); - } - else { - if (PyList_Append(refs, val)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)val); + if (merge_const(refs, &val) < 0) { + goto error; } + int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); } stack_pointer[0] = value; stack_pointer += 1; @@ -263,15 +259,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -304,15 +296,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -345,15 +333,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -418,15 +402,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -460,15 +440,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -502,15 +478,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -553,15 +525,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); } @@ -591,15 +559,11 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (_Py_IsImmortal(temp)) { - REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); - } - else { - if (PyList_Append(refs, temp)) { - goto error; - } - REPLACE_OP(&this_instr[-1], _LOAD_CONST_INLINE, 0, (uintptr_t)temp); + if (merge_const(refs, &temp) < 0) { + goto error; } + int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(&this_instr[-1], opcode, 0, (uintptr_t)temp); res = sym_new_const(ctx, temp); Py_DECREF(temp); REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); @@ -1303,7 +1267,7 @@ _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); null = sym_new_null(ctx); (void)descr; (void)owner; @@ -1699,7 +1663,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; @@ -1715,7 +1679,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; @@ -1749,7 +1713,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_not_null(ctx); + attr = sym_new_const(ctx, descr); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; @@ -2281,15 +2245,11 @@ bottom = stack_pointer[-1 - (oparg-1)]; if (sym_is_const(bottom)) { PyObject *value = sym_get_const(bottom); - if (_Py_IsImmortal(value)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)value); - } - else { - if (PyList_Append(refs, value)) { - goto error; - } - REPLACE_OP(this_instr, _LOAD_CONST_INLINE, 0, (uintptr_t)value); + if (merge_const(refs, &value) < 0) { + goto error; } + int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; + REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); } assert(oparg > 0); top = bottom; From 0dfd1fea5536f3fbec114f66eff440d87801e66e Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Aug 2024 11:42:24 -0700 Subject: [PATCH 05/11] Constant merging isn't needed for anything already created --- Python/optimizer_bytecodes.c | 6 ------ Python/optimizer_cases.c.h | 6 ------ 2 files changed, 12 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 760e35d9367151..cfe45c37986ed6 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -84,9 +84,6 @@ dummy_func(void) { value = GETLOCAL(oparg); if (sym_is_const(value)) { PyObject *val = sym_get_const(value); - if (merge_const(refs, &val) < 0) { - goto error; - } int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); } @@ -534,9 +531,6 @@ dummy_func(void) { op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { if (sym_is_const(bottom)) { PyObject *value = sym_get_const(bottom); - if (merge_const(refs, &value) < 0) { - goto error; - } int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 72cebb5cf1c4c1..fafc126a6eb23f 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -41,9 +41,6 @@ value = GETLOCAL(oparg); if (sym_is_const(value)) { PyObject *val = sym_get_const(value); - if (merge_const(refs, &val) < 0) { - goto error; - } int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); } @@ -2245,9 +2242,6 @@ bottom = stack_pointer[-1 - (oparg-1)]; if (sym_is_const(bottom)) { PyObject *value = sym_get_const(bottom); - if (merge_const(refs, &value) < 0) { - goto error; - } int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); } From 0d2e040ba5ff97d0883d199fa2a6e22058ec8c1c Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Aug 2024 12:49:24 -0700 Subject: [PATCH 06/11] Improve handling of _POP_TOP_LOAD_CONST_INLINE_BORROW and _REPLACE_WITH_TRUE --- Python/optimizer_analysis.c | 10 ++++++++-- Python/optimizer_bytecodes.c | 6 ++++++ Python/optimizer_cases.c.h | 6 +++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index c32b06e1d97c2b..27760d1253d080 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -543,6 +543,7 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) last_set_ip = pc; break; case _POP_TOP: + case _POP_TOP_LOAD_CONST_INLINE_BORROW: { _PyUOpInstruction *last = &buffer[pc-1]; while (last->opcode == _NOP) { @@ -554,9 +555,14 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) last->opcode == _COPY ) { last->opcode = _NOP; - buffer[pc].opcode = _NOP; + if (buffer[pc].opcode == _POP_TOP_LOAD_CONST_INLINE_BORROW) { + buffer[pc].opcode = _LOAD_CONST_INLINE_BORROW; + } + else { + buffer[pc].opcode = _NOP; + } } - if (last->opcode == _REPLACE_WITH_TRUE) { + if (last->opcode == _POP_TOP_LOAD_CONST_INLINE_BORROW) { last->opcode = _NOP; } break; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index cfe45c37986ed6..df05e958d6a2e7 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -922,6 +922,12 @@ dummy_func(void) { self_or_null = sym_new_unknown(ctx); } + op(_REPLACE_WITH_TRUE, (value -- res)) { + (void)value; + REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + res = sym_new_const(ctx, Py_True); + } + op(_JUMP_TO_TOP, (--)) { ctx->done = true; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index fafc126a6eb23f..464ae83cec2425 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -192,8 +192,12 @@ } case _REPLACE_WITH_TRUE: { + _Py_UopsSymbol *value; _Py_UopsSymbol *res; - res = sym_new_not_null(ctx); + value = stack_pointer[-1]; + (void)value; + REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + res = sym_new_const(ctx, Py_True); stack_pointer[-1] = res; break; } From 8e6e40646375ef90570f9d0131292b7aab38747b Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 21 Aug 2024 20:15:13 -0700 Subject: [PATCH 07/11] Remove original or unused constants --- Python/optimizer.c | 97 ++++++++++++++++++++++++++++-------- Python/optimizer_analysis.c | 24 ++------- Python/optimizer_bytecodes.c | 18 +++---- Python/optimizer_cases.c.h | 16 +++--- 4 files changed, 97 insertions(+), 58 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 3cedabe92275b8..2aa84ac0d8e302 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1133,6 +1133,47 @@ sanity_check(_PyExecutorObject *executor) #undef CHECK #endif +static PyObject * +safe_constant_key(PyObject *o) +{ + PyObject *k = _PyCode_ConstantKey(o); + // A key of tuple[int, object] is used for "other" constants, which may + // include arbitrary objects. We don't want to try to hash them or check + // their equality, so just make the key a tuple[int] (their address): + if (k && PyTuple_CheckExact(k) && PyLong_CheckExact(PyTuple_GET_ITEM(k, 0))) { + Py_SETREF(k, PyTuple_Pack(1, PyTuple_GET_ITEM(k, 0))); + } + return k; +} + +static bool +safe_contains(PyObject *refs, PyObject *o) +{ + assert(PyList_CheckExact(refs)); + for (int i = 0; i < PyList_GET_SIZE(refs); i++) { + if (Py_Is(o, PyList_GET_ITEM(refs, i))) { + return true; + } + } + return false; +} + +static int +merge_const(PyObject *refs, PyObject **o_p) +{ + assert(PyDict_CheckExact(refs)); + assert(!_Py_IsImmortal(*o_p)); + PyObject *o = *o_p; + PyObject *k = safe_constant_key(o); + if (k == NULL) { + return -1; + } + int res = PyDict_SetDefaultRef(refs, k, o, o_p); + Py_DECREF(k); + Py_DECREF(o); + return res; +} + /* Makes an executor from a buffer of uops. * Account for the buffer having gaps and NOPs by computing a "used" * bit vector and only copying the used uops. Here "used" means reachable @@ -1251,16 +1292,16 @@ uop_optimize( assert(length < UOP_MAX_TRACE_LENGTH); OPT_STAT_INC(traces_created); char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); - PyObject *refs = PyDict_New(); - if (refs == NULL) { + PyObject *new_refs = PyList_New(0); + if (new_refs == NULL) { return -1; } if (env_var == NULL || *env_var == '\0' || *env_var > '0') { length = _Py_uop_analyze_and_optimize(frame, buffer, length, - curr_stackentries, &dependencies, refs); + curr_stackentries, &dependencies, new_refs); if (length <= 0) { - Py_DECREF(refs); + Py_DECREF(new_refs); return length; } } @@ -1282,29 +1323,45 @@ uop_optimize( assert(_PyOpcode_uop_name[buffer[pc].opcode]); assert(strncmp(_PyOpcode_uop_name[buffer[pc].opcode], _PyOpcode_uop_name[opcode], strlen(_PyOpcode_uop_name[opcode])) == 0); } + PyObject *used_refs = PyDict_New(); + if (used_refs == NULL) { + Py_DECREF(new_refs); + return -1; + } + for (int i = 0; i < length; i++) { + if (buffer[i].opcode == _LOAD_CONST_INLINE) { + PyObject **o_p = (PyObject **)&buffer[i].operand; + if (!safe_contains(new_refs, *o_p)) { + continue; + } + Py_INCREF(*o_p); + int err = merge_const(used_refs, o_p); + Py_DECREF(*o_p); + if (err < 0) { + Py_DECREF(used_refs); + Py_DECREF(new_refs); + return -1; + } + } + } + Py_DECREF(new_refs); + Py_SETREF(used_refs, PyDict_Values(used_refs)); + if (used_refs == NULL) { + return -1; + } + Py_SETREF(used_refs, PyList_AsTuple(used_refs)); + if (used_refs == NULL) { + return -1; + } OPT_HIST(effective_trace_length(buffer, length), optimized_trace_length_hist); length = prepare_for_execution(buffer, length); assert(length <= UOP_MAX_TRACE_LENGTH); _PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies); if (executor == NULL) { - Py_DECREF(refs); + Py_DECREF(used_refs); return -1; } - PyObject *refs_tuple = PyTuple_New(PyDict_GET_SIZE(refs)); - if (refs_tuple == NULL) { - Py_DECREF(executor); - return -1; - } - PyObject *k, *v; - Py_ssize_t i = 0, p = 0; - Py_BEGIN_CRITICAL_SECTION(refs); - while (PyDict_Next(refs, &p, &k, &v)) { - PyTuple_SET_ITEM(refs_tuple, i++, Py_NewRef(v)); - } - Py_END_CRITICAL_SECTION(); - assert(i == Py_SIZE(refs_tuple)); - Py_DECREF(refs); - executor->refs = refs_tuple; + executor->refs = used_refs; assert(length <= UOP_MAX_TRACE_LENGTH); *exec_ptr = executor; return 1; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 27760d1253d080..7e3270d6e17089 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -289,25 +289,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, return 0; } -static int -merge_const(PyObject *refs, PyObject **o_p) -{ - PyObject *o = *o_p; - PyObject *k = _PyCode_ConstantKey(o); - // A key of tuple[int, object] is used for "other" constants, which may - // include arbitrary objects. We don't want to try to hash them or check - // their equality, so just make the key a tuple[int] (their address): - if (PyTuple_CheckExact(k) && PyLong_CheckExact(PyTuple_GET_ITEM(k, 0))) { - Py_SETREF(k, PyTuple_Pack(1, PyTuple_GET_ITEM(k, 0))); - if (k == NULL) { - return -1; - } - } - int res = PyDict_SetDefaultRef(refs, k, o, o_p); - Py_DECREF(k); - Py_DECREF(o); - return res; -} + #define STACK_LEVEL() ((int)(stack_pointer - ctx->frame->stack)) #define STACK_SIZE() ((int)(ctx->frame->stack_len)) @@ -606,7 +588,7 @@ _Py_uop_analyze_and_optimize( int length, int curr_stacklen, _PyBloomFilter *dependencies, - PyObject *refs + PyObject *new_refs ) { OPT_STAT_INC(optimizer_attempts); @@ -618,7 +600,7 @@ _Py_uop_analyze_and_optimize( length = optimize_uops( _PyFrame_GetCode(frame), buffer, - length, curr_stacklen, dependencies, refs); + length, curr_stacklen, dependencies, new_refs); if (length <= 0) { return length; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index df05e958d6a2e7..713aa67866a0db 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -63,7 +63,7 @@ dummy_func(void) { _Py_UOpsContext *ctx; _PyUOpInstruction *this_instr; _PyBloomFilter *dependencies; - PyObject *refs; + PyObject *new_refs; int modified; int curr_space; int max_space; @@ -206,7 +206,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -234,7 +234,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -262,7 +262,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -291,7 +291,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -320,7 +320,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -349,7 +349,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -373,7 +373,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -399,7 +399,7 @@ dummy_func(void) { assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 464ae83cec2425..5a3c558235eed5 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -260,7 +260,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -297,7 +297,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -334,7 +334,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -403,7 +403,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -441,7 +441,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -479,7 +479,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -526,7 +526,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -560,7 +560,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (merge_const(refs, &temp) < 0) { + if (PyList_Append(refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; From cc185ce9606e8bd48ad3b86161f5fe7f9755d3f2 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 28 Aug 2024 15:16:32 -0700 Subject: [PATCH 08/11] fixup --- Python/optimizer_analysis.c | 2 +- Python/optimizer_cases.c.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 7e3270d6e17089..5f3db45cf747e9 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -393,7 +393,7 @@ optimize_uops( int trace_len, int curr_stacklen, _PyBloomFilter *dependencies, - PyObject *refs + PyObject *new_refs ) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 5a3c558235eed5..516612d8d32c28 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -260,7 +260,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -297,7 +297,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -334,7 +334,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -403,7 +403,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -441,7 +441,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -479,7 +479,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -526,7 +526,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; @@ -560,7 +560,7 @@ assert(this_instr[-1].opcode == _NOP); REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (PyList_Append(refs, temp)) { + if (PyList_Append(new_refs, temp)) { goto error; } int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; From adfacc1cb380cb722a0e50cd447d2d3ba651da30 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 28 Aug 2024 15:48:43 -0700 Subject: [PATCH 09/11] Don't resurrect dead caches --- Python/optimizer_bytecodes.c | 8 ++++---- Python/optimizer_cases.c.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 713aa67866a0db..80371dd0106d90 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -611,7 +611,7 @@ dummy_func(void) { } op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr, null if (oparg & 1))) { - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)descr; (void)owner; @@ -619,19 +619,19 @@ dummy_func(void) { op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; } op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; } op(_LOAD_ATTR_METHOD_LAZY_DICT, (descr/4, owner -- attr, self if (1))) { (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 516612d8d32c28..cfc27255f43c6b 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1268,7 +1268,7 @@ _Py_UopsSymbol *null = NULL; owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); null = sym_new_null(ctx); (void)descr; (void)owner; @@ -1664,7 +1664,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; @@ -1680,7 +1680,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; @@ -1714,7 +1714,7 @@ owner = stack_pointer[-1]; PyObject *descr = (PyObject *)this_instr->operand; (void)descr; - attr = sym_new_const(ctx, descr); + attr = sym_new_not_null(ctx); self = owner; stack_pointer[-1] = attr; stack_pointer[0] = self; From d93c5a3c92356bae2656f8247df7242ae562f5b2 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 28 Aug 2024 15:53:00 -0700 Subject: [PATCH 10/11] Don't bother deduplicating (and factor out some repeated code) --- Include/internal/pycore_optimizer.h | 2 +- Python/optimizer.c | 87 +++++---------------- Python/optimizer_analysis.c | 18 ++++- Python/optimizer_bytecodes.c | 117 +++++++++------------------- Python/optimizer_cases.c.h | 117 +++++++++------------------- 5 files changed, 108 insertions(+), 233 deletions(-) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 993ea704510db2..ce391da7aa4724 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -145,7 +145,7 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp); int _Py_uop_analyze_and_optimize(struct _PyInterpreterFrame *frame, _PyUOpInstruction *trace, int trace_len, int curr_stackentries, - _PyBloomFilter *dependencies, PyObject *refs); + _PyBloomFilter *dependencies, PyObject *new_refs); extern PyTypeObject _PyCounterExecutor_Type; extern PyTypeObject _PyCounterOptimizer_Type; diff --git a/Python/optimizer.c b/Python/optimizer.c index 2aa84ac0d8e302..4b405b0b818c08 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1133,47 +1133,6 @@ sanity_check(_PyExecutorObject *executor) #undef CHECK #endif -static PyObject * -safe_constant_key(PyObject *o) -{ - PyObject *k = _PyCode_ConstantKey(o); - // A key of tuple[int, object] is used for "other" constants, which may - // include arbitrary objects. We don't want to try to hash them or check - // their equality, so just make the key a tuple[int] (their address): - if (k && PyTuple_CheckExact(k) && PyLong_CheckExact(PyTuple_GET_ITEM(k, 0))) { - Py_SETREF(k, PyTuple_Pack(1, PyTuple_GET_ITEM(k, 0))); - } - return k; -} - -static bool -safe_contains(PyObject *refs, PyObject *o) -{ - assert(PyList_CheckExact(refs)); - for (int i = 0; i < PyList_GET_SIZE(refs); i++) { - if (Py_Is(o, PyList_GET_ITEM(refs, i))) { - return true; - } - } - return false; -} - -static int -merge_const(PyObject *refs, PyObject **o_p) -{ - assert(PyDict_CheckExact(refs)); - assert(!_Py_IsImmortal(*o_p)); - PyObject *o = *o_p; - PyObject *k = safe_constant_key(o); - if (k == NULL) { - return -1; - } - int res = PyDict_SetDefaultRef(refs, k, o, o_p); - Py_DECREF(k); - Py_DECREF(o); - return res; -} - /* Makes an executor from a buffer of uops. * Account for the buffer having gaps and NOPs by computing a "used" * bit vector and only copying the used uops. Here "used" means reachable @@ -1291,11 +1250,13 @@ uop_optimize( } assert(length < UOP_MAX_TRACE_LENGTH); OPT_STAT_INC(traces_created); - char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); + // These are any references that were created during optimization, and need + // to be kept alive until we build the executor's refs tuple: PyObject *new_refs = PyList_New(0); if (new_refs == NULL) { return -1; } + char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); if (env_var == NULL || *env_var == '\0' || *env_var > '0') { length = _Py_uop_analyze_and_optimize(frame, buffer, length, @@ -1323,45 +1284,39 @@ uop_optimize( assert(_PyOpcode_uop_name[buffer[pc].opcode]); assert(strncmp(_PyOpcode_uop_name[buffer[pc].opcode], _PyOpcode_uop_name[opcode], strlen(_PyOpcode_uop_name[opcode])) == 0); } - PyObject *used_refs = PyDict_New(); - if (used_refs == NULL) { + // We *might* want to de-duplicate these. In addition to making sure we do + // so in a way that preserves "equal" constants with different types (see + // _PyCode_ConstantKey), we *also* need to be careful to compare unknown + // objects by identity, since we don't want to invoke arbitary code in a + // __hash__/__eq__ implementation. It might be more trouble than it's worth: + int refs_needed = 0; + for (int i = 0; i < length; i++) { + if (buffer[i].opcode == _LOAD_CONST_INLINE) { + refs_needed++; + } + } + PyObject *refs = PyTuple_New(refs_needed); + if (refs == NULL) { Py_DECREF(new_refs); return -1; } + int j = 0; for (int i = 0; i < length; i++) { if (buffer[i].opcode == _LOAD_CONST_INLINE) { - PyObject **o_p = (PyObject **)&buffer[i].operand; - if (!safe_contains(new_refs, *o_p)) { - continue; - } - Py_INCREF(*o_p); - int err = merge_const(used_refs, o_p); - Py_DECREF(*o_p); - if (err < 0) { - Py_DECREF(used_refs); - Py_DECREF(new_refs); - return -1; - } + PyTuple_SET_ITEM(refs, j++, Py_NewRef(buffer[i].operand)); } } Py_DECREF(new_refs); - Py_SETREF(used_refs, PyDict_Values(used_refs)); - if (used_refs == NULL) { - return -1; - } - Py_SETREF(used_refs, PyList_AsTuple(used_refs)); - if (used_refs == NULL) { - return -1; - } + assert(j == refs_needed); OPT_HIST(effective_trace_length(buffer, length), optimized_trace_length_hist); length = prepare_for_execution(buffer, length); assert(length <= UOP_MAX_TRACE_LENGTH); _PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies); if (executor == NULL) { - Py_DECREF(used_refs); + Py_DECREF(refs); return -1; } - executor->refs = used_refs; + executor->refs = refs; assert(length <= UOP_MAX_TRACE_LENGTH); *exec_ptr = executor; return 1; diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 5f3db45cf747e9..0177ec1b56586d 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -300,10 +300,20 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define GETLOCAL(idx) ((ctx->frame->locals[idx])) -#define REPLACE_OP(INST, OP, ARG, OPERAND) \ - (INST)->opcode = OP; \ - (INST)->oparg = ARG; \ - (INST)->operand = OPERAND; +#define REPLACE_OP(INST, OP, ARG, OPERAND) \ + do { \ + (INST)->opcode = (OP); \ + (INST)->oparg = (ARG); \ + (INST)->operand = (OPERAND); \ + } while (0) + +#define REPLACE_OP_WITH_LOAD_CONST(INST, CONST) \ + do { \ + PyObject *o = (CONST); \ + int opcode = _Py_IsImmortal(o) ? _LOAD_CONST_INLINE_BORROW \ + : _LOAD_CONST_INLINE; \ + REPLACE_OP((INST), opcode, 0, (uintptr_t)o); \ + } while (0) /* Shortened forms for convenience, used in optimizer_bytecodes.c */ #define sym_is_not_null _Py_uop_sym_is_not_null diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 80371dd0106d90..66efaf165dd53e 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -83,9 +83,7 @@ dummy_func(void) { op(_LOAD_FAST, (-- value)) { value = GETLOCAL(oparg); if (sym_is_const(value)) { - PyObject *val = sym_get_const(value); - int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); + REPLACE_OP_WITH_LOAD_CONST(this_instr, sym_get_const(value)); } } @@ -199,20 +197,15 @@ dummy_func(void) { assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -227,20 +220,15 @@ dummy_func(void) { assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -255,20 +243,15 @@ dummy_func(void) { assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -284,20 +267,15 @@ dummy_func(void) { PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) + PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -313,20 +291,15 @@ dummy_func(void) { PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) - PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -342,20 +315,15 @@ dummy_func(void) { PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) * PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -366,20 +334,15 @@ dummy_func(void) { if (sym_is_const(left) && sym_is_const(right) && sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -391,22 +354,17 @@ dummy_func(void) { if (sym_is_const(left) && sym_is_const(right) && sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-3].opcode == _NOP); assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(&this_instr[-1], opcode, 0, (uintptr_t)temp); - res = sym_new_const(ctx, temp); - Py_DECREF(temp); + REPLACE_OP(this_instr - 3, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr - 1, temp); REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); + res = sym_new_const(ctx, temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -505,8 +463,7 @@ dummy_func(void) { op(_LOAD_CONST, (-- value)) { PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg); - int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); + REPLACE_OP_WITH_LOAD_CONST(this_instr, val); value = sym_new_const(ctx, val); } @@ -530,9 +487,7 @@ dummy_func(void) { op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { if (sym_is_const(bottom)) { - PyObject *value = sym_get_const(bottom); - int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); + REPLACE_OP_WITH_LOAD_CONST(this_instr, sym_get_const(bottom)); } assert(oparg > 0); top = bottom; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index cfc27255f43c6b..78306c7d0aaf06 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -40,9 +40,7 @@ _Py_UopsSymbol *value; value = GETLOCAL(oparg); if (sym_is_const(value)) { - PyObject *val = sym_get_const(value); - int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); + REPLACE_OP_WITH_LOAD_CONST(this_instr, sym_get_const(value)); } stack_pointer[0] = value; stack_pointer += 1; @@ -64,8 +62,7 @@ case _LOAD_CONST: { _Py_UopsSymbol *value; PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg); - int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val); + REPLACE_OP_WITH_LOAD_CONST(this_instr, val); value = sym_new_const(ctx, val); stack_pointer[0] = value; stack_pointer += 1; @@ -253,20 +250,15 @@ assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -290,20 +282,15 @@ assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -327,20 +314,15 @@ assert(PyLong_CheckExact(sym_get_const(right))); PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(left), (PyLongObject *)sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyLong_Type); @@ -396,20 +378,15 @@ PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) * PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -434,20 +411,15 @@ PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) + PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -472,20 +444,15 @@ PyObject *temp = PyFloat_FromDouble( PyFloat_AS_DOUBLE(sym_get_const(left)) - PyFloat_AS_DOUBLE(sym_get_const(right))); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyFloat_Type); @@ -519,20 +486,15 @@ if (sym_is_const(left) && sym_is_const(right) && sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-1], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)temp); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 1, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr, temp); res = sym_new_const(ctx, temp); - Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -552,22 +514,17 @@ if (sym_is_const(left) && sym_is_const(right) && sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, &PyUnicode_Type)) { PyObject *temp = PyUnicode_Concat(sym_get_const(left), sym_get_const(right)); - if (temp == NULL) { + if (temp == NULL || _PyList_AppendTakeRef((PyListObject *)new_refs, temp)) { goto error; } assert(this_instr[-3].opcode == _NOP); assert(this_instr[-2].opcode == _NOP); assert(this_instr[-1].opcode == _NOP); - REPLACE_OP(&this_instr[-3], _POP_TOP, 0, 0); - REPLACE_OP(&this_instr[-2], _POP_TOP, 0, 0); - if (PyList_Append(new_refs, temp)) { - goto error; - } - int opcode = _Py_IsImmortal(temp) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(&this_instr[-1], opcode, 0, (uintptr_t)temp); - res = sym_new_const(ctx, temp); - Py_DECREF(temp); + REPLACE_OP(this_instr - 3, _POP_TOP, 0, 0); + REPLACE_OP(this_instr - 2, _POP_TOP, 0, 0); + REPLACE_OP_WITH_LOAD_CONST(this_instr - 1, temp); REPLACE_OP(this_instr, _STORE_FAST, this_instr->operand, 0); + res = sym_new_const(ctx, temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); @@ -2245,9 +2202,7 @@ _Py_UopsSymbol *top; bottom = stack_pointer[-1 - (oparg-1)]; if (sym_is_const(bottom)) { - PyObject *value = sym_get_const(bottom); - int opcode = _Py_IsImmortal(value) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)value); + REPLACE_OP_WITH_LOAD_CONST(this_instr, sym_get_const(bottom)); } assert(oparg > 0); top = bottom; From cc0d30fb043c67d40879799385054e15e5383caf Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 30 Sep 2024 15:25:03 -0700 Subject: [PATCH 11/11] blurb add --- .../2024-09-30-15-23-16.gh-issue-115506.NpDku1.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-15-23-16.gh-issue-115506.NpDku1.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-15-23-16.gh-issue-115506.NpDku1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-15-23-16.gh-issue-115506.NpDku1.rst new file mode 100644 index 00000000000000..daf4edae4aee67 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-30-15-23-16.gh-issue-115506.NpDku1.rst @@ -0,0 +1 @@ +Improve constant propagation and folding in JIT-compiled code.