|
3 | 3 | import sys
|
4 | 4 | import textwrap
|
5 | 5 | import unittest
|
| 6 | +import gc |
6 | 7 |
|
7 | 8 | import _testinternalcapi
|
8 | 9 |
|
@@ -556,6 +557,214 @@ def testfunc(n):
|
556 | 557 | # too much already.
|
557 | 558 | self.assertEqual(count, 1)
|
558 | 559 |
|
| 560 | +class TestUopsOptimization(unittest.TestCase): |
| 561 | + |
<
10000
/td> | 562 | + def test_int_type_propagation(self): |
| 563 | + def testfunc(loops): |
| 564 | + num = 0 |
| 565 | + while num < loops: |
| 566 | + x = num + num |
| 567 | + a = x + 1 |
| 568 | + num += 1 |
| 569 | + return a |
| 570 | + |
| 571 | + opt = _testinternalcapi.get_uop_optimizer() |
| 572 | + res = None |
| 573 | + with temporary_optimizer(opt): |
| 574 | + res = testfunc(32) |
| 575 | + |
| 576 | + ex = get_first_executor(testfunc) |
| 577 | + self.assertIsNotNone(ex) |
| 578 | + self.assertEqual(res, 63) |
| 579 | + binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] |
| 580 | + guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] |
| 581 | + self.assertGreaterEqual(len(binop_count), 3) |
| 582 | + self.assertLessEqual(len(guard_both_int_count), 1) |
| 583 | + |
| 584 | + def test_int_type_propagation_through_frame(self): |
| 585 | + def double(x): |
| 586 | + return x + x |
| 587 | + def testfunc(loops): |
| 588 | + num = 0 |
| 589 | + while num < loops: |
| 590 | + x = num + num |
| 591 | + a = double(x) |
| 592 | + num += 1 |
| 593 | + return a |
| 594 | + |
| 595 | + opt = _testinternalcapi.get_uop_optimizer() |
| 596 | + res = None |
| 597 | + with temporary_optimizer(opt): |
| 598 | + res = testfunc(32) |
| 599 | + |
| 600 | + ex = get_first_executor(testfunc) |
| 601 | + self.assertIsNotNone(ex) |
| 602 | + self.assertEqual(res, 124) |
| 603 | + binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] |
| 604 | + guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] |
| 605 | + self.assertGreaterEqual(len(binop_count), 3) |
| 606 | + self.assertLessEqual(len(guard_both_int_count), 1) |
| 607 | + |
| 608 | + def test_int_type_propagation_from_frame(self): |
| 609 | + def double(x): |
| 610 | + return x + x |
| 611 | + def testfunc(loops): |
| 612 | + num = 0 |
| 613 | + while num < loops: |
| 614 | + a = double(num) |
| 615 | + x = a + a |
| 616 | + num += 1 |
| 617 | + return x |
| 618 | + |
| 619 | + opt = _testinternalcapi.get_uop_optimizer() |
| 620 | + res = None |
| 621 | + with temporary_optimizer(opt): |
| 622 | + res = testfunc(32) |
| 623 | + |
| 624 | + ex = get_first_executor(testfunc) |
| 625 | + self.assertIsNotNone(ex) |
| 626 | + self.assertEqual(res, 124) |
| 627 | + binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] |
| 628 | + guard_both_int_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] |
| 629 | + self.assertGreaterEqual(len(binop_count), 3) |
| 630 | + self.assertLessEqual(len(guard_both_int_count), 1) |
| 631 | + |
| 632 | + def test_int_impure_region(self): |
| 633 | + def testfunc(loops): |
| 634 | + num = 0 |
| 635 | + while num < loops: |
| 636 | + x = num + num |
| 637 | + y = 1 |
| 638 | + x // 2 |
| 639 | + a = x + y |
| 640 | + num += 1 |
| 641 | + return a |
| 642 | + |
| 643 | + opt = _testinternalcapi.get_uop_optimizer() |
| 644 | + res = None |
| 645 | + with temporary_optimizer(opt): |
| 646 | + res = testfunc(64) |
| 647 | + |
| 648 | + ex = get_first_executor(testfunc) |
| 649 | + self.assertIsNotNone(ex) |
| 650 | + binop_count = [opname for opname, _, _ in ex if opname == "_BINARY_OP_ADD_INT"] |
| 651 | + self.assertGreaterEqual(len(binop_count), 3) |
| 652 | + |
| 653 | + def test_call_py_exact_args(self): |
| 654 | + def testfunc(n): |
| 655 | + def dummy(x): |
| 656 | + return x+1 |
| 657 | + for i in range(n): |
| 658 | + dummy(i) |
| 659 | + |
| 660 | + opt = _testinternalcapi.get_uop_optimizer() |
| 661 | + with temporary_optimizer(opt): |
| 662 | + testfunc(20) |
| 663 | + |
| 664 | + ex = get_first_executor(testfunc) |
| 665 | + self.assertIsNotNone(ex) |
| 666 | + uops = {opname for opname, _, _ in ex} |
| 667 | + self.assertIn("_PUSH_FRAME", uops) |
| 668 | + self.assertIn("_BINARY_OP_ADD_INT", uops) |
| 669 | + self.assertNotIn("_CHECK_PEP_523", uops) |
| 670 | + |
| 671 | + def test_int_type_propagate_through_range(self): |
| 672 | + def testfunc(n): |
| 673 | + |
| 674 | + for i in range(n): |
| 675 | + x = i + i |
| 676 | + return x |
| 677 | + |
| 678 | + opt = _testinternalcapi.get_uop_optimizer() |
| 679 | + with temporary_optimizer(opt): |
| 680 | + res = testfunc(20) |
| 681 | + |
| 682 | + ex = get_first_executor(testfunc) |
| 683 | + self.assertEqual(res, 19 * 2) |
| 684 | + self.assertIsNotNone(ex) |
| 685 | + uops = {opname for opname, _, _ in ex} |
| 686 | + self.assertNotIn("_GUARD_BOTH_INT", uops) |
| 687 | + |
| 688 | + def test_int_value_numbering(self): |
| 689 | + def testfunc(n): |
| 690 | + |
| 691 | + y = 1 |
| 692 | + for i in range(n): |
| 693 | + x = y |
| 694 | + z = x |
| 695 | + a = z |
| 696 | + b = a |
| 697 | + res = x + z + a + b |
| 698 | + return res |
| 699 | + |
| 700 | + opt = _testinternalcapi.get_uop_optimizer() |
| 701 | + with temporary_optimizer(opt): |
| 702 | + res = testfunc(20) |
| 703 | + |
| 704 | + ex = get_first_executor(testfunc) |
| 705 | + self.assertEqual(res, 4) |
| 706 | + self.assertIsNotNone(ex) |
| 707 | + uops = {opname for opname, _, _ in ex} |
| 708 | + self.assertIn("_GUARD_BOTH_INT", uops) |
| 709 | + guard_count = [opname for opname, _, _ in ex if opname == "_GUARD_BOTH_INT"] |
| 710 | + self.assertEqual(len(guard_count), 1) |
| 711 | + |
| 712 | + def test_comprehension(self): |
| 713 | + def testfunc(n): |
| 714 | + for _ in range(n): |
| 715 | + return [i for i in range(n)] |
| 716 | + |
| 717 | + opt = _testinternalcapi.get_uop_optimizer() |
| 718 | + with temporary_optimizer(opt): |
| 719 | + testfunc(20) |
| 720 | + |
| 721 | + ex = get_first_executor(testfunc) |
| 722 | + self.assertIsNotNone(ex) |
| 723 | + uops = {opname for opname, _, _ in ex} |
| 724 | + self.assertNotIn("_BINARY_OP_ADD_INT", uops) |
| 725 | + |
| 726 | + def test_call_py_exact_args_disappearing(self): |
| 727 | + def dummy(x): |
| 728 | + return x+1 |
| 729 | + |
| 730 | + def testfunc(n): |
| 731 | + for i in range(n): |
| 732 | + dummy(i) |
| 733 | + |
| 734 | + opt = _testinternalcapi.get_uop_optimizer() |
| 735 | + # Trigger specialization |
| 736 | + testfunc(8) |
| 737 | + with temporary_optimizer(opt): |
| 738 | + del dummy |
| 739 | + gc.collect() |
| 740 | + |
| 741 | + def dummy(x): |
| 742 | + return x + 2 |
| 743 | + testfunc(10) |
| 744 | + |
| 745 | + ex = get_first_executor(testfunc) |
| 746 | + # Honestly as long as it doesn't crash it's fine. |
| 747 | + # Whether we get an executor or not is non-deterministic, |
| 748 | + # because it's decided by when the function is freed. |
| 749 | + # This test is a little implementation specific. |
| 750 | + |
| 751 | + def test_promote_globals_to_constants(self): |
| 752 | + def testfunc(n): |
| 753 | + for i in range(n): |
| 754 | + x = range(i) |
| 755 | + return x |
| 756 | + |
| 757 | + opt = _testinternalcapi.get_uop_optimizer() |
| 758 | + with temporary_optimizer(opt): |
| 759 | + testfunc(20) |
| 760 | + |
| 761 | + ex = get_first_executor(testfunc) |
| 762 | + self.assertIsNotNone(ex) |
| 763 | + uops = {opname for opname, _, _ in ex} |
| 764 | + self.assertNotIn("_LOAD_GLOBAL_BUILTIN", uops) |
| 765 | + self.assertIn("_LOAD_CONST_INLINE_BORROW_WITH_NULL", uops) |
| 766 | + |
| 767 | + |
559 | 768 |
|
560 | 769 | if __name__ == "__main__":
|
561 | 770 | unittest.main()
|
0 commit comments