Skip to content

Commit 7fe6e67

Browse files
Fidget-Spinnerreidenong
authored andcommitted
pythongh-142764: Restore REPLACE_OPCODE_IF_EVALUATES_PURE optimization for some ops (pythonGH-143335)
1 parent d0fb0b4 commit 7fe6e67

9 files changed

Lines changed: 1164 additions & 603 deletions

File tree

Include/internal/pycore_uop_ids.h

Lines changed: 547 additions & 542 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

@@ -1663,8 +1662,7 @@ def testfunc(n):
16631662
self.assertNotIn("_COMPARE_OP", uops)
16641663
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
16651664

1666-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1667-
def test_compare_op_int_pop_two_load_const_inline_borrow(self):
1665+
def test_compare_op_int_insert_two_load_const_inline_borrow(self):
16681666
def testfunc(n):
16691667
x = 0
16701668
for _ in range(n):
@@ -1679,10 +1677,9 @@ def testfunc(n):
16791677
self.assertIsNotNone(ex)
16801678
uops = get_opnames(ex)
16811679
self.assertNotIn("_COMPARE_OP_INT", uops)
1682-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1680+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
16831681

1684-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1685-
def test_compare_op_str_pop_two_load_const_inline_borrow(self):
1682+
def test_compare_op_str_insert_two_load_const_inline_borrow(self):
16861683
def testfunc(n):
16871684
x = 0
16881685
for _ in range(n):
@@ -1697,10 +1694,9 @@ def testfunc(n):
16971694
self.assertIsNotNone(ex)
16981695
uops = get_opnames(ex)
16991696
self.assertNotIn("_COMPARE_OP_STR", uops)
1700-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1697+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
17011698

1702-
@unittest.skip("TODO (gh-142764): Re-enable after we get back automatic constant propagation.")
1703-
def test_compare_op_float_pop_two_load_const_inline_borrow(self):
1699+
def test_compare_op_float_insert_two_load_const_inline_borrow(self):
17041700
def testfunc(n):
17051701
x = 0
17061702
for _ in range(n):
@@ -1715,7 +1711,7 @@ def testfunc(n):
17151711
self.assertIsNotNone(ex)
17161712
uops = get_opnames(ex)
17171713
self.assertNotIn("_COMPARE_OP_FLOAT", uops)
1718-
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1714+
self.assertIn("_INSERT_2_LOAD_CONST_INLINE_BORROW", uops)
17191715

17201716
def test_contains_op_pop_two_load_const_inline_borrow(self):
17211717
def testfunc(n):
@@ -2176,9 +2172,8 @@ def testfunc(n):
21762172
uops = get_opnames(ex)
21772173
self.assertIn("_CALL_TUPLE_1", uops)
21782174
self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops)
2179-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2180-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2181-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2175+
self.assertNotIn("_COMPARE_OP_INT", uops)
2176+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
21822177

21832178
def test_call_len(self):
21842179
def testfunc(n):
@@ -2243,9 +2238,8 @@ class C:
22432238
# length allows us to optimize more code, such as conditionals
22442239
# in this case
22452240
self.assertIn("_CALL_LEN", uops)
2246-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2247-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2248-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2241+
self.assertNotIn("_COMPARE_OP_INT", uops)
2242+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
22492243

22502244
def test_call_builtin_o(self):
22512245
def testfunc(n):
@@ -2338,9 +2332,8 @@ def testfunc(n):
23382332
self.assertIsNotNone(ex)
23392333
uops = get_opnames(ex)
23402334
self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops)
2341-
# TODO (gh-142764): Re-enable after we get back automatic constant propagation.
2342-
# self.assertNotIn("_COMPARE_OP_INT", uops)
2343-
# self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
2335+
self.assertNotIn("_COMPARE_OP_INT", uops)
2336+
self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
23442337

23452338
def test_call_isinstance_guards_removed(self):
23462339
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
@@ -5392,6 +5392,13 @@ dummy_func(
53925392
INPUTS_DEAD();
53935393
}
53945394

5395+
tier2 op(_INSERT_2_LOAD_CONST_INLINE_BORROW, (ptr/4, left, right -- res, l, r)) {
5396+
res = PyStackRef_FromPyObjectBorrow(ptr);
5397+
l = left;
5398+
r = right;
5399+
INPUTS_DEAD();
5400+
}
5401+
53955402
tier2 op(_SHUFFLE_2_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, arg -- res, a)) {
53965403
res = PyStackRef_FromPyObjectBorrow(ptr);
53975404
a = arg;

0 commit comments

Comments
 (0)