@@ -1521,6 +1521,29 @@ class Foo:
15211521 Foo .attr = 0
15221522 self .assertFalse (ex .is_valid ())
15231523
1524+ def test_guard_type_version_locked_removed (self ):
1525+ """
1526+ Verify that redundant _GUARD_TYPE_VERSION_LOCKED guards are
1527+ eliminated for sequential STORE_ATTR_INSTANCE_VALUE in __init__.
1528+ """
1529+
1530+ class Foo :
1531+ def __init__ (self ):
1532+ self .a = 1
1533+ self .b = 2
1534+ self .c = 3
1535+
1536+ def thing (n ):
1537+ for _ in range (n ):
1538+ Foo ()
1539+
1540+ res , ex = self ._run_with_optimizer (thing , TIER2_THRESHOLD )
1541+ self .assertIsNotNone (ex )
1542+ opnames = list (iter_opnames (ex ))
1543+ guard_locked_count = opnames .count ("_GUARD_TYPE_VERSION_LOCKED" )
1544+ # Only the first store needs the guard; the rest should be NOPed.
1545+ self .assertEqual (guard_locked_count , 1 )
1546+
15241547 def test_type_version_doesnt_segfault (self ):
15251548 """
15261549 Tests that setting a type version doesn't cause a segfault when later looking at the stack.
@@ -1542,6 +1565,98 @@ def fn(a):
15421565
15431566 fn (A ())
15441567
1568+ def test_init_resolves_callable (self ):
1569+ """
1570+ _CHECK_AND_ALLOCATE_OBJECT should resolve __init__ to a constant,
1571+ enabling the optimizer to propagate type information through the frame
1572+ and eliminate redundant function version and arg count checks.
1573+ """
1574+ class MyPoint :
1575+ def __init__ (self , x , y ):
1576+ # If __init__ callable is propagated through, then
1577+ # These will get promoted from globals to constants.
1578+ self .x = range (1 )
1579+ self .y = range (1 )
1580+
1581+ def testfunc (n ):
1582+ for _ in range (n ):
1583+ p = MyPoint (1.0 , 2.0 )
1584+
1585+ _ , ex = self ._run_with_optimizer (testfunc , TIER2_THRESHOLD )
1586+ self .assertIsNotNone (ex )
1587+ uops = get_opnames (ex )
1588+ # The __init__ call should be traced through via _PUSH_FRAME
1589+ self .assertIn ("_PUSH_FRAME" , uops )
1590+ # __init__ resolution allows promotion of range to constant
1591+ self .assertNotIn ("_LOAD_GLOBAL_BUILTINS" , 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+
15451660 def test_func_guards_removed_or_reduced (self ):
15461661 def testfunc (n ):
15471662 for i in range (n ):
0 commit comments