Skip to content

Commit fe91ac3

Browse files
Restore REPLACE_OPCODE_IF_EVALUATES_PURE optimization
1 parent d00d39f commit fe91ac3

File tree

9 files changed

+1535
-946
lines changed

9 files changed

+1535
-946
lines changed

Include/internal/pycore_uop_ids.h

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

Include/internal/pycore_uop_metadata.h

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

Lib/test/test_capi/test_opt.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,9 +1433,8 @@ def testfunc(n):
14331433
self.assertEqual(res, 3)
14341434
self.assertIsNotNone(ex)
14351435
uops = get_opnames(ex)
1436-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
1437-
# self.assertNotIn("_BINARY_OP_ADD_INT", uops)
1438-
# self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1436+
self.assertNotIn("_BINARY_OP_ADD_INT", uops)
1437+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
14391438
self.assertNotIn("_GUARD_NOS_INT", uops)
14401439
self.assertNotIn("_GUARD_TOS_INT", uops)
14411440

@@ -1653,8 +1652,7 @@ def testfunc(n):
16531652
self.assertNotIn("_COMPARE_OP", uops)
16541653
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
16551654

1656-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1657-
def test_compare_op_int_pop_two_load_const_inline_borrow(self):
1655+
def test_compare_op_int_insert_two_load_const_inline_borrow(self):
16581656
def testfunc(n):
16591657
x = 0
16601658
for _ in range(n):
@@ -1669,10 +1667,9 @@ def testfunc(n):
16691667
self.assertIsNotNone(ex)
16701668
uops = get_opnames(ex)
16711669
self.assertNotIn("_COMPARE_OP_INT", uops)
1672-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1670+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
16731671

1674-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1675-
def test_compare_op_str_pop_two_load_const_inline_borrow(self):
1672+
def test_compare_op_str_insert_two_load_const_inline_borrow(self):
16761673
def testfunc(n):
16771674
x = 0
16781675
for _ in range(n):
@@ -1687,10 +1684,9 @@ def testfunc(n):
16871684
self.assertIsNotNone(ex)
16881685
uops = get_opnames(ex)
16891686
self.assertNotIn("_COMPARE_OP_STR", uops)
1690-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1687+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
16911688

1692-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1693-
def test_compare_op_float_pop_two_load_const_inline_borrow(self):
1689+
def test_compare_op_float_insert_two_load_const_inline_borrow(self):
16941690
def testfunc(n):
16951691
x = 0
16961692
for _ in range(n):
@@ -1705,7 +1701,7 @@ def testfunc(n):
17051701
self.assertIsNotNone(ex)
17061702
uops = get_opnames(ex)
17071703
self.assertNotIn("_COMPARE_OP_FLOAT", uops)
1708-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1704+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
17091705

17101706
def test_contains_op_pop_two_load_const_inline_borrow(self):
17111707
def testfunc(n):
@@ -2126,9 +2122,8 @@ def testfunc(n):
21262122
uops = get_opnames(ex)
21272123
self.assertIn("_CALL_TUPLE_1", uops)
21282124
self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops)
2129-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2130-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2131-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2125+
self.assertNotIn("_COMPARE_OP_INT", uops)
2126+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
21322127

21332128
def test_call_len(self):
21342129
def testfunc(n):
@@ -2193,9 +2188,8 @@ class C:
21932188
# length allows us to optimize more code, such as conditionals
21942189
# in this case
21952190
self.assertIn("_CALL_LEN", uops)
2196-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2197-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2198-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2191+
self.assertNotIn("_COMPARE_OP_INT", uops)
2192+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
21992193

22002194
def test_call_builtin_o(self):
22012195
def testfunc(n):
@@ -2290,9 +2284,8 @@ def testfunc(n):
22902284
self.assertIsNotNone(ex)
22912285
uops = get_opnames(ex)
22922286
self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops)
2293-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2294-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2295-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2287+
self.assertNotIn("_COMPARE_OP_INT", uops)
2288+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
22962289

22972290
def test_call_isinstance_guards_removed(self):
22982291
def testfunc(n):

Lib/test/test_generated_cases.py

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,7 +2238,7 @@ def test_pure_uop_body_copied_in(self):
22382238
"""
22392239
input2 = """
22402240
op(OP, (foo -- res)) {
2241-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2241+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
22422242
res = sym_new_known(ctx, foo);
22432243
}
22442244
"""
@@ -2278,7 +2278,7 @@ def test_pure_uop_body_copied_in_deopt(self):
22782278
"""
22792279
input2 = """
22802280
op(OP, (foo -- res)) {
2281-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2281+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
22822282
res = foo;
22832283
}
22842284
"""
@@ -2322,7 +2322,7 @@ def test_pure_uop_body_copied_in_error_if(self):
23222322
"""
23232323
input2 = """
23242324
op(OP, (foo -- res)) {
2325-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2325+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
23262326
res = foo;
23272327
}
23282328
"""
@@ -2368,7 +2368,7 @@ def test_replace_opcode_uop_body_copied_in_complex(self):
23682368
"""
23692369
input2 = """
23702370
op(OP, (foo -- res)) {
2371-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2371+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
23722372
res = sym_new_known(ctx, foo);
23732373
}
23742374
"""
@@ -2415,7 +2415,7 @@ def test_replace_opcode_escaping_uop_body_copied_in_complex(self):
24152415
"""
24162416
input2 = """
24172417
op(OP, (foo -- res)) {
2418-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2418+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
24192419
res = sym_new_known(ctx, foo);
24202420
}
24212421
"""
@@ -2449,6 +2449,118 @@ def test_replace_opcode_escaping_uop_body_copied_in_complex(self):
24492449
"""
24502450
self.run_cases_test(input, input2, output)
24512451

2452+
def test_replace_opcode_binop_one_output(self):
2453+
input = """
2454+
pure op(OP, (left, right -- res)) {
2455+
res = foo(left, right);
2456+
}
2457+
"""
2458+
input2 = """
2459+
op(OP, (left, right -- res)) {
2460+
res = sym_new_non_null(ctx, foo);
2461+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res);
2462+
}
2463+
"""
2464+
output = """
2465+
case OP: {
2466+
JitOptRef right;
2467+
JitOptRef left;
2468+
JitOptRef res;
2469+
right = stack_pointer[-1];
2470+
left = stack_pointer[-2];
2471+
res = sym_new_non_null(ctx, foo);
2472+
if (
2473+
sym_is_safe_const(ctx, left) &&
2474+
sym_is_safe_const(ctx, right)
2475+
) {
2476+
JitOptRef left_sym = left;
2477+
JitOptRef right_sym = right;
2478+
_PyStackRef left = sym_get_const_as_stackref(ctx, left_sym);
2479+
_PyStackRef right = sym_get_const_as_stackref(ctx, right_sym);
2480+
_PyStackRef res_stackref;
2481+
/* Start of uop copied from bytecodes for constant evaluation */
2482+
res_stackref = foo(left, right);
2483+
/* End of uop copied from bytecodes for constant evaluation */
2484+
res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref));
2485+
CHECK_STACK_BOUNDS(-1);
2486+
stack_pointer[-2] = res;
2487+
stack_pointer += -1;
2488+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2489+
break;
2490+
}
2491+
CHECK_STACK_BOUNDS(-1);
2492+
stack_pointer[-2] = res;
2493+
stack_pointer += -1;
2494+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2495+
break;
2496+
}
2497+
"""
2498+
self.run_cases_test(input, input2, output)
2499+
2500+
def test_replace_opcode_binop_one_output_insert(self):
2501+
input = """
2502+
pure op(OP, (left, right -- res, l, r)) {
2503+
res = foo(left, right);
2504+
l = left;
2505+
r = right;
2506+
}
2507+
"""
2508+
input2 = """
2509+
op(OP, (left, right -- res, l, r)) {
2510+
res = sym_new_non_null(ctx, foo);
2511+
l = left;
2512+
r = right;
2513+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res);
2514+
}
2515+
"""
2516+
output = """
2517+
case OP: {
2518+
JitOptRef right;
2519+
JitOptRef left;
2520+
JitOptRef res;
2521+
JitOptRef l;
2522+
JitOptRef r;
2523+
right = stack_pointer[-1];
2524+
left = stack_pointer[-2];
2525+
res = sym_new_non_null(ctx, foo);
2526+
l = left;
2527+
r = right;
2528+
if (
2529+
sym_is_safe_const(ctx, left) &&
2530+
sym_is_safe_const(ctx, right)
2531+
) {
2532+
JitOptRef left_sym = left;
2533+
JitOptRef right_sym = right;
2534+
_PyStackRef left = sym_get_const_as_stackref(ctx, left_sym);
2535+
_PyStackRef right = sym_get_const_as_stackref(ctx, right_sym);
2536+
_PyStackRef res_stackref;
2537+
_PyStackRef l_stackref;
2538+
_PyStackRef r_stackref;
2539+
/* Start of uop copied from bytecodes for constant evaluation */
2540+
res_stackref = foo(left, right);
2541+
l_stackref = left;
2542+
r_stackref = right;
2543+
/* End of uop copied from bytecodes for constant evaluation */
2544+
res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref));
2545+
CHECK_STACK_BOUNDS(1);
2546+
stack_pointer[-2] = res;
2547+
stack_pointer[-1] = l;
2548+
stack_pointer[0] = r;
2549+
stack_pointer += 1;
2550+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2551+
break;
2552+
}
2553+
CHECK_STACK_BOUNDS(1);
2554+
stack_pointer[-2] = res;
2555+
stack_pointer[-1] = l;
2556+
stack_pointer[0] = r;
2557+
stack_pointer += 1;
2558+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2559+
break;
2560+
}
2561+
"""
2562+
self.run_cases_test(input, input2, output)
2563+
24522564
def test_replace_opocode_uop_reject_array_effects(self):
24532565
input = """
24542566
pure op(OP, (foo[2] -- res)) {
@@ -2462,7 +2574,7 @@ def test_replace_opocode_uop_reject_array_effects(self):
24622574
"""
24632575
input2 = """
24642576
op(OP, (foo[2] -- res)) {
2465-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2577+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
24662578
res = sym_new_unknown(ctx);
24672579
}
24682580
"""

Python/bytecodes.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5276,6 +5276,19 @@ dummy_func(
52765276
value = PyStackRef_FromPyObjectBorrow(ptr);
52775277
}
52785278

5279+
tier2 op(_INSERT_1_LOAD_CONST_INLINE_BORROW, (ptr/4, left -- res, l)) {
5280+
res = PyStackRef_FromPyObjectBorrow(ptr);
5281+
l = left;
5282+
INPUTS_DEAD();
5283+
}
5284+
5285+
tier2 op(_INSERT_2_LOAD_CONST_INLINE_BORROW, (ptr/4, left, right -- res, l, r)) {
5286+
res = PyStackRef_FromPyObjectBorrow(ptr);
5287+
l = left;
5288+
r = right;
5289+
INPUTS_DEAD();
5290+
}
5291+
52795292
tier2 op(_SHUFFLE_2_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, arg -- res, a)) {
52805293
res = PyStackRef_FromPyObjectBorrow(ptr);
52815294
a = arg;

0 commit comments

Comments
 (0)