Skip to content

Commit 65e1f94

Browse files
Optimize _GUARD_CODE_VERSION+IP via fun version symbols
1 parent e0f7c10 commit 65e1f94

File tree

7 files changed

+277
-41
lines changed

7 files changed

+277
-41
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ extern PyCodeObject *_Py_uop_sym_get_probable_func_code(JitOptRef sym);
312312
extern PyObject *_Py_uop_sym_get_probable_value(JitOptRef sym);
313313
extern PyTypeObject *_Py_uop_sym_get_probable_type(JitOptRef sym);
314314
extern JitOptRef *_Py_uop_sym_set_stack_depth(JitOptContext *ctx, int stack_depth, JitOptRef *current_sp);
315+
extern uint32_t _Py_uop_sym_get_func_version(JitOptRef ref);
316+
bool _Py_uop_sym_set_func_version(JitOptContext *ctx, JitOptRef ref, uint32_t version);
315317

316318
extern void _Py_uop_abstractcontext_init(JitOptContext *ctx, _PyBloomFilter *dependencies);
317319
extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);

Include/internal/pycore_optimizer_types.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ typedef enum _JitSymType {
3636
JIT_SYM_NON_NULL_TAG = 3,
3737
JIT_SYM_BOTTOM_TAG = 4,
3838
JIT_SYM_TYPE_VERSION_TAG = 5,
39-
JIT_SYM_KNOWN_CLASS_TAG = 6,
40-
JIT_SYM_KNOWN_VALUE_TAG = 7,
41-
JIT_SYM_TUPLE_TAG = 8,
42-
JIT_SYM_TRUTHINESS_TAG = 9,
43-
JIT_SYM_COMPACT_INT = 10,
44-
JIT_SYM_PREDICATE_TAG = 11,
45-
JIT_SYM_RECORDED_VALUE_TAG = 12,
46-
JIT_SYM_RECORDED_TYPE_TAG = 13,
47-
JIT_SYM_RECORDED_GEN_FUNC_TAG = 14,
39+
JIT_SYM_FUNC_VERSION_TAG = 6,
40+
JIT_SYM_KNOWN_CLASS_TAG = 7,
41+
JIT_SYM_KNOWN_VALUE_TAG = 8,
42+
JIT_SYM_TUPLE_TAG = 9,
43+
JIT_SYM_TRUTHINESS_TAG = 10,
44+
JIT_SYM_COMPACT_INT = 11,
45+
JIT_SYM_PREDICATE_TAG = 12,
46+
JIT_SYM_RECORDED_VALUE_TAG = 13,
47+
JIT_SYM_RECORDED_TYPE_TAG = 14,
48+
JIT_SYM_RECORDED_GEN_FUNC_TAG = 15,
4849
} JitSymType;
4950

5051
typedef struct _jit_opt_known_class {
@@ -58,6 +59,11 @@ typedef struct _jit_opt_known_version {
5859
uint32_t version;
5960
} JitOptKnownVersion;
6061

62+
typedef struct _jit_opt_known_func_version {
63+
uint8_t tag;
64+
uint32_t func_version;
65+
} JitOptKnownFuncVersion;
66+
6167
typedef struct _jit_opt_known_value {
6268
uint8_t tag;
6369
PyObject *value;
@@ -118,6 +124,7 @@ typedef union _jit_opt_symbol {
118124
JitOptKnownClass cls;
119125
JitOptKnownValue value;
120126
JitOptKnownVersion version;
127+
JitOptKnownFuncVersion func_version;
121128
JitOptTuple tuple;
122129
JitOptTruthiness truthiness;
123130
JitOptCompactInt compact;
@@ -140,6 +147,7 @@ typedef struct _Py_UOpsAbstractFrame {
140147
int stack_len;
141148
int locals_len;
142149
bool caller; // We have made a call from this frame during the trace
150+
JitOptRef callable;
143151
PyFunctionObject *func;
144152
PyCodeObject *code;
145153

Lib/test/test_capi/test_opt.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ def dummy(x):
630630
self.assertIn("_PUSH_FRAME", uops)
631631
self.assertIn("_BINARY_OP_ADD_INT", uops)
632632
self.assertNotIn("_CHECK_PEP_523", uops)
633+
self.assertNotIn("_GUARD_CODE_VERSION__PUSH_FRAME", uops)
634+
self.assertNotIn("_GUARD_IP__PUSH_FRAME", uops)
633635

634636
def test_int_type_propagate_through_range(self):
635637
def testfunc(n):
@@ -1540,8 +1542,9 @@ def testfunc(n):
15401542
self.assertIsNotNone(ex)
15411543
uops = get_opnames(ex)
15421544
self.assertIn("_PUSH_FRAME", uops)
1543-
# Strength reduced version
1544-
self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops)
1545+
# Both should be not present, as this is a call
1546+
# to a simple function with a known function version.
1547+
self.assertNotIn("_CHECK_FUNCTION_VERSION_INLINE", uops)
15451548
self.assertNotIn("_CHECK_FUNCTION_VERSION", uops)
15461549
# Removed guard
15471550
self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops)

Python/optimizer_analysis.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr,
291291
#define sym_get_probable_func_code _Py_uop_sym_get_probable_func_code
292292
#define sym_get_probable_value _Py_uop_sym_get_probable_value
293293
#define sym_set_stack_depth(DEPTH, SP) _Py_uop_sym_set_stack_depth(ctx, DEPTH, SP)
294+
#define sym_get_func_version _Py_uop_sym_get_func_version
295+
#define sym_set_func_version _Py_uop_sym_set_func_version
294296

295297
/* Comparison oparg masks */
296298
#define COMPARE_LT_MASK 2

Python/optimizer_bytecodes.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -857,12 +857,12 @@ dummy_func(void) {
857857
}
858858

859859
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
860-
if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyFunction_Type)) {
861-
assert(PyFunction_Check(sym_get_const(ctx, callable)));
862-
ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
863-
uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)sym_get_const(ctx, callable);
860+
if (sym_get_func_version(callable) == func_version) {
861+
REPLACE_OP(this_instr, _NOP, 0, 0);
862+
}
863+
else {
864+
sym_set_func_version(ctx, callable, func_version);
864865
}
865-
sym_set_type(callable, &PyFunction_Type);
866866
}
867867

868868
op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] -- callable, null, unused[oparg])) {
@@ -1767,12 +1767,46 @@ dummy_func(void) {
17671767
sym_set_recorded_gen_func(nos, func);
17681768
}
17691769

1770+
op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) {
1771+
PyCodeObject *co = get_current_code_object(ctx);
1772+
if (co->co_version == version) {
1773+
_Py_BloomFilter_Add(dependencies, co);
1774+
if (sym_get_func_version(ctx->frame->callable) != 0) {
1775+
REPLACE_OP(this_instr, _NOP, 0, 0);
1776+
}
1777+
}
1778+
else {
1779+
ctx->done = true;
1780+
}
1781+
}
1782+
1783+
op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) {
1784+
if (ctx->frame->caller) {
1785+
REPLACE_OP(this_instr, _NOP, 0, 0);
1786+
}
1787+
}
1788+
1789+
op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) {
1790+
if (ctx->frame->caller) {
1791+
REPLACE_OP(this_instr, _NOP, 0, 0);
1792+
}
1793+
}
1794+
1795+
op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) {
1796+
if (ctx->frame->caller) {
1797+
REPLACE_OP(this_instr, _NOP, 0, 0);
1798+
}
1799+
}
1800+
17701801
op(_GUARD_IP__PUSH_FRAME, (ip/4 --)) {
17711802
(void)ip;
17721803
stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer);
1773-
// TO DO
1774-
// Normal function calls to known functions
1775-
// do not need an IP guard.
1804+
if (sym_get_func_version(ctx->frame->callable) != 0 &&
1805+
// We can remove this guard for simple function call targets.
1806+
(((PyCodeObject *)ctx->frame->func->func_code)->co_flags &
1807+
(CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) {
1808+
REPLACE_OP(this_instr, _NOP, 0, 0);
1809+
}
17761810
}
17771811

17781812
op(_GUARD_IP_YIELD_VALUE, (ip/4 --)) {

Python/optimizer_cases.c.h

Lines changed: 34 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)