Skip to content

Commit 0b08438

Browse files
authored
gh-130415: Narrowing to constants in branches involving is comparisons with a constant (GH-143895)
1 parent 6181b69 commit 0b08438

File tree

7 files changed

+241
-20
lines changed

7 files changed

+241
-20
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ extern JitOptRef _Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptRef value,
205205
extern bool _Py_uop_sym_is_compact_int(JitOptRef sym);
206206
extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
207207
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
208+
extern JitOptRef _Py_uop_sym_new_predicate(JitOptContext *ctx, JitOptRef lhs_ref, JitOptRef rhs_ref, JitOptPredicateKind kind);
209+
extern void _Py_uop_sym_apply_predicate_narrowing(JitOptContext *ctx, JitOptRef sym, bool branch_is_true);
208210

209211
extern void _Py_uop_abstractcontext_init(JitOptContext *ctx);
210212
extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);

Include/internal/pycore_optimizer_types.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef enum _JitSymType {
4040
JIT_SYM_TUPLE_TAG = 8,
4141
JIT_SYM_TRUTHINESS_TAG = 9,
4242
JIT_SYM_COMPACT_INT = 10,
43+
JIT_SYM_PREDICATE_TAG = 11,
4344
} JitSymType;
4445

4546
typedef struct _jit_opt_known_class {
@@ -72,6 +73,18 @@ typedef struct {
7273
uint16_t value;
7374
} JitOptTruthiness;
7475

76+
typedef enum {
77+
JIT_PRED_IS,
78+
JIT_PRED_IS_NOT,
79+
} JitOptPredicateKind;
80+
81+
typedef struct {
82+
uint8_t tag;
83+
uint8_t kind;
84+
uint16_t lhs;
85+
uint16_t rhs;
86+
} JitOptPredicate;
87+
7588
typedef struct {
7689
uint8_t tag;
7790
} JitOptCompactInt;
@@ -84,6 +97,7 @@ typedef union _jit_opt_symbol {
8497
JitOptTuple tuple;
8598
JitOptTruthiness truthiness;
8699
JitOptCompactInt compact;
100+
JitOptPredicate predicate;
87101
} JitOptSymbol;
88102

89103
// This mimics the _PyStackRef API

Lib/test/test_capi/test_opt.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3551,6 +3551,46 @@ def test_is_none(n):
35513551
self.assertIn("_POP_TOP_NOP", uops)
35523552
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
35533553

3554+
def test_is_true_narrows_to_constant(self):
3555+
def f(n):
3556+
def return_true():
3557+
return True
3558+
3559+
hits = 0
3560+
v = return_true()
3561+
for i in range(n):
3562+
if v is True:
3563+
hits += v + 1
3564+
return hits
3565+
3566+
res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
3567+
self.assertEqual(res, TIER2_THRESHOLD * 2)
3568+
self.assertIsNotNone(ex)
3569+
uops = get_opnames(ex)
3570+
3571+
# v + 1 should be constant folded
3572+
self.assertNotIn("_BINARY_OP", uops)
3573+
3574+
def test_is_false_narrows_to_constant(self):
3575+
def f(n):
3576+
def return_false():
3577+
return False
3578+
3579+
hits = 0
3580+
v = return_false()
3581+
for i in range(n):
3582+
if v is False:
3583+
hits += v + 1
3584+
return hits
3585+
3586+
res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
3587+
self.assertEqual(res, TIER2_THRESHOLD)
3588+
self.assertIsNotNone(ex)
3589+
uops = get_opnames(ex)
3590+
3591+
# v + 1 should be constant folded
3592+
self.assertNotIn("_BINARY_OP", uops)
3593+
35543594
def test_for_iter_gen_frame(self):
35553595
def f(n):
35563596
for i in range(n):

Python/optimizer_analysis.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr,
247247
#define sym_is_compact_int _Py_uop_sym_is_compact_int
248248
#define sym_new_compact_int _Py_uop_sym_new_compact_int
249249
#define sym_new_truthiness _Py_uop_sym_new_truthiness
250+
#define sym_new_predicate _Py_uop_sym_new_predicate
251+
#define sym_apply_predicate_narrowing _Py_uop_sym_apply_predicate_narrowing
250252

251253
#define JUMP_TO_LABEL(label) goto label;
252254

Python/optimizer_bytecodes.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
3838
#define sym_new_compact_int _Py_uop_sym_new_compact_int
3939
#define sym_is_compact_int _Py_uop_sym_is_compact_int
4040
#define sym_new_truthiness _Py_uop_sym_new_truthiness
41+
#define sym_new_predicate _Py_uop_sym_new_predicate
42+
#define sym_apply_predicate_narrowing _Py_uop_sym_apply_predicate_narrowing
4143

4244
extern int
4345
optimize_to_bool(
@@ -533,7 +535,7 @@ dummy_func(void) {
533535
}
534536

535537
op(_IS_OP, (left, right -- b, l, r)) {
536-
b = sym_new_type(ctx, &PyBool_Type);
538+
b = sym_new_predicate(ctx, left, right, (oparg ? JIT_PRED_IS_NOT : JIT_PRED_IS));
537539
l = left;
538540
r = right;
539541
}
@@ -1173,6 +1175,8 @@ dummy_func(void) {
11731175
}
11741176

11751177
op(_GUARD_IS_TRUE_POP, (flag -- )) {
1178+
sym_apply_predicate_narrowing(ctx, flag, true);
1179+
11761180
if (sym_is_const(ctx, flag)) {
11771181
PyObject *value = sym_get_const(ctx, flag);
11781182
assert(value != NULL);
@@ -1191,6 +1195,8 @@ dummy_func(void) {
11911195
}
11921196

11931197
op(_GUARD_IS_FALSE_POP, (flag -- )) {
1198+
sym_apply_predicate_narrowing(ctx, flag, false);
1199+
11941200
if (sym_is_const(ctx, flag)) {
11951201
PyObject *value = sym_get_const(ctx, flag);
11961202
assert(value != NULL);

Python/optimizer_cases.c.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)