Skip to content

Commit 9525911

Browse files
gh-142764: Restore REPLACE_OPCODE_IF_EVALUATES_PURE optimization for some ops (GH-143335)
1 parent e0fb278 commit 9525911

File tree

9 files changed

+1555
-995
lines changed

9 files changed

+1555
-995
lines changed

Include/internal/pycore_uop_ids.h

Lines changed: 938 additions & 934 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: 19 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
@@ -1438,9 +1438,8 @@ def testfunc(n):
14381438
self.assertEqual(res, 3)
14391439
self.assertIsNotNone(ex)
14401440
uops = get_opnames(ex)
1441-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
1442-
# self.assertNotIn("_BINARY_OP_ADD_INT", uops)
1443-
# self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1441+
self.assertNotIn("_BINARY_OP_ADD_INT", uops)
1442+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
14441443
self.assertNotIn("_GUARD_NOS_INT", uops)
14451444
self.assertNotIn("_GUARD_TOS_INT", uops)
14461445

@@ -1658,8 +1657,7 @@ def testfunc(n):
16581657
self.assertNotIn("_COMPARE_OP", uops)
16591658
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
16601659

1661-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1662-
def test_compare_op_int_pop_two_load_const_inline_borrow(self):
1660+
def test_compare_op_int_insert_two_load_const_inline_borrow(self):
16631661
def testfunc(n):
16641662
x = 0
16651663
for _ in range(n):
@@ -1674,10 +1672,9 @@ def testfunc(n):
16741672
self.assertIsNotNone(ex)
16751673
uops = get_opnames(ex)
16761674
self.assertNotIn("_COMPARE_OP_INT", uops)
1677-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1675+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
16781676

1679-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1680-
def test_compare_op_str_pop_two_load_const_inline_borrow(self):
1677+
def test_compare_op_str_insert_two_load_const_inline_borrow(self):
16811678
def testfunc(n):
16821679
x = 0
16831680
for _ in range(n):
@@ -1692,10 +1689,9 @@ def testfunc(n):
16921689
self.assertIsNotNone(ex)
16931690
uops = get_opnames(ex)
16941691
self.assertNotIn("_COMPARE_OP_STR", uops)
1695-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1692+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
16961693

1697-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1698-
def test_compare_op_float_pop_two_load_const_inline_borrow(self):
1694+
def test_compare_op_float_insert_two_load_const_inline_borrow(self):
16991695
def testfunc(n):
17001696
x = 0
17011697
for _ in range(n):
@@ -1710,7 +1706,7 @@ def testfunc(n):
17101706
self.assertIsNotNone(ex)
17111707
uops = get_opnames(ex)
17121708
self.assertNotIn("_COMPARE_OP_FLOAT", uops)
1713-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1709+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
17141710

17151711
def test_contains_op_pop_two_load_const_inline_borrow(self):
17161712
def testfunc(n):
@@ -2171,9 +2167,8 @@ def testfunc(n):
21712167
uops = get_opnames(ex)
21722168
self.assertIn("_CALL_TUPLE_1", uops)
21732169
self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops)
2174-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2175-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2176-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2170+
self.assertNotIn("_COMPARE_OP_INT", uops)
2171+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
21772172

21782173
def test_call_len(self):
21792174
def testfunc(n):
@@ -2238,9 +2233,8 @@ class C:
22382233
# length allows us to optimize more code, such as conditionals
22392234
# in this case
22402235
self.assertIn("_CALL_LEN", uops)
2241-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2242-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2243-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2236+
self.assertNotIn("_COMPARE_OP_INT", uops)
2237+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
22442238

22452239
def test_call_builtin_o(self):
22462240
def testfunc(n):
@@ -2333,9 +2327,8 @@ def testfunc(n):
23332327
self.assertIsNotNone(ex)
23342328
uops = get_opnames(ex)
23352329
self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops)
2336-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2337-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2338-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2330+
self.assertNotIn("_COMPARE_OP_INT", uops)
2331+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
23392332

23402333
def test_call_isinstance_guards_removed(self):
23412334
def testfunc(n):

Lib/test/test_generated_cases.py

Lines changed: 172 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,172 @@ 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+
(void)l_stackref;
2545+
(void)r_stackref;
2546+
res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref));
2547+
CHECK_STACK_BOUNDS(1);
2548+
stack_pointer[-2] = res;
2549+
stack_pointer[-1] = l;
2550+
stack_pointer[0] = r;
2551+
stack_pointer += 1;
2552+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2553+
break;
2554+
}
2555+
CHECK_STACK_BOUNDS(1);
2556+
stack_pointer[-2] = res;
2557+
stack_pointer[-1] = l;
2558+
stack_pointer[0] = r;
2559+
stack_pointer += 1;
2560+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2561+
break;
2562+
}
2563+
"""
2564+
self.run_cases_test(input, input2, output)
2565+
2566+
def test_replace_opcode_unaryop_one_output_insert(self):
2567+
input = """
2568+
pure op(OP, (left -- res, l)) {
2569+
res = foo(left);
2570+
l = left;
2571+
}
2572+
"""
2573+
input2 = """
2574+
op(OP, (left -- res, l)) {
2575+
res = sym_new_non_null(ctx, foo);
2576+
l = left;
2577+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, res);
2578+
}
2579+
"""
2580+
output = """
2581+
case OP: {
2582+
JitOptRef left;
2583+
JitOptRef res;
2584+
JitOptRef l;
2585+
left = stack_pointer[-1];
2586+
res = sym_new_non_null(ctx, foo);
2587+
l = left;
2588+
if (
2589+
sym_is_safe_const(ctx, left)
2590+
) {
2591+
JitOptRef left_sym = left;
2592+
_PyStackRef left = sym_get_const_as_stackref(ctx, left_sym);
2593+
_PyStackRef res_stackref;
2594+
_PyStackRef l_stackref;
2595+
/* Start of uop copied from bytecodes for constant evaluation */
2596+
res_stackref = foo(left);
2597+
l_stackref = left;
2598+
/* End of uop copied from bytecodes for constant evaluation */
2599+
(void)l_stackref;
2600+
res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref));
2601+
CHECK_STACK_BOUNDS(1);
2602+
stack_pointer[-1] = res;
2603+
stack_pointer[0] = l;
2604+
stack_pointer += 1;
2605+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2606+
break;
2607+
}
2608+
CHECK_STACK_BOUNDS(1);
2609+
stack_pointer[-1] = res;
2610+
stack_pointer[0] = l;
2611+
stack_pointer += 1;
2612+
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
2613+
break;
2614+
}
2615+
"""
2616+
self.run_cases_test(input, input2, output)
2617+
24522618
def test_replace_opocode_uop_reject_array_effects(self):
24532619
input = """
24542620
pure op(OP, (foo[2] -- res)) {
@@ -2462,7 +2628,7 @@ def test_replace_opocode_uop_reject_array_effects(self):
24622628
"""
24632629
input2 = """
24642630
op(OP, (foo[2] -- res)) {
2465-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
2631+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res);
24662632
res = sym_new_unknown(ctx);
24672633
}
24682634
"""

Python/bytecodes.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5395,6 +5395,13 @@ dummy_func(
53955395
INPUTS_DEAD();
53965396
}
53975397

5398+
tier2 op(_INSERT_2_LOAD_CONST_INLINE_BORROW, (ptr/4, left, right -- res, l, r)) {
5399+
res = PyStackRef_FromPyObjectBorrow(ptr);
5400+
l = left;
5401+
r = right;
5402+
INPUTS_DEAD();
5403+
}
5404+
53985405
tier2 op(_SHUFFLE_2_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, arg -- res, a)) {
53995406
res = PyStackRef_FromPyObjectBorrow(ptr);
54005407
a = arg;

0 commit comments

Comments
 (0)