Skip to content

Commit 83fd02c

Browse files
committed
add tests
1 parent 0fee38e commit 83fd02c

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,101 @@ def fn(a):
15621562

15631563
fn(A())
15641564

1565+
def test_init_resolves_callable(self):
1566+
"""
1567+
_CHECK_AND_ALLOCATE_OBJECT should resolve __init__ to a constant,
1568+
enabling the optimizer to trace into the init frame and eliminate
1569+
redundant function version and arg count checks.
1570+
"""
1571+
class MyPoint:
1572+
def __init__(self, x, y):
1573+
self.x = x
1574+
self.y = y
1575+
1576+
def testfunc(n):
1577+
total = 0.0
1578+
for _ in range(n):
1579+
p = MyPoint(1.0, 2.0)
1580+
total += p.x
1581+
return total
1582+
1583+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
1584+
self.assertAlmostEqual(res, TIER2_THRESHOLD * 1.0)
1585+
self.assertIsNotNone(ex)
1586+
uops = get_opnames(ex)
1587+
# The __init__ call should be traced through via _PUSH_FRAME
1588+
self.assertIn("_PUSH_FRAME", uops)
1589+
# __init__ resolution eliminates function version and arg checks
1590+
self.assertNotIn("_CHECK_FUNCTION_VERSION", uops)
1591+
self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops)
1592+
1593+
def test_guard_type_version_locked_propagates(self):
1594+
"""
1595+
_GUARD_TYPE_VERSION_LOCKED should set the type version on the
1596+
symbol so repeated accesses to the same type can benefit.
1597+
"""
1598+
class Item:
1599+
def __init__(self, val):
1600+
self.val = val
1601+
1602+
def get(self):
1603+
return self.val
1604+
1605+
def get2(self):
1606+
return self.val + 1
1607+
1608+
def testfunc(n):
1609+
item = Item(42)
1610+
total = 0
1611+
for _ in range(n):
1612+
# Two method calls on the same object — the second
1613+
# should benefit from type info set by the first.
1614+
total += item.get() + item.get2()
1615+
return total
1616+
1617+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
1618+
self.assertEqual(res, TIER2_THRESHOLD * (42 + 43))
1619+
self.assertIsNotNone(ex)
1620+
uops = get_opnames(ex)
1621+
# Both methods should be traced through
1622+
self.assertEqual(uops.count("_PUSH_FRAME"), 2)
1623+
# Type version propagation: one guard covers both method lookups
1624+
self.assertEqual(uops.count("_GUARD_TYPE_VERSION"), 1)
1625+
# Function checks eliminated (type info resolves the callable)
1626+
self.assertNotIn("_CHECK_FUNCTION_VERSION", uops)
1627+
self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops)
1628+
1629+
def test_method_chain_guard_elimination(self):
1630+
"""
1631+
Calling two methods on the same object should share the outer
1632+
type guard — only one _GUARD_TYPE_VERSION for the two lookups.
1633+
"""
1634+
class Calc:
1635+
def __init__(self, val):
1636+
self.val = val
1637+
1638+
def add(self, x):
1639+
self.val += x
1640+
return self
1641+
1642+
def testfunc(n):
1643+
c = Calc(0)
1644+
for _ in range(n):
1645+
c.add(1).add(2)
1646+
return c.val
1647+
1648+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
1649+
self.assertEqual(res, TIER2_THRESHOLD * 3)
1650+
self.assertIsNotNone(ex)
1651+
uops = get_opnames(ex)
1652+
# Both add() calls should be inlined
1653+
push_count = uops.count("_PUSH_FRAME")
1654+
self.assertEqual(push_count, 2)
1655+
# Only one outer type version guard for the two method lookups
1656+
# on the same object c (the second lookup reuses type info)
1657+
guard_version_count = uops.count("_GUARD_TYPE_VERSION")
1658+
self.assertEqual(guard_version_count, 1)
1659+
15651660
def test_func_guards_removed_or_reduced(self):
15661661
def testfunc(n):
15671662
for i in range(n):

0 commit comments

Comments
 (0)