99
1010from  __future__ import  annotations 
1111
12+ from  typing  import  Final 
13+ 
1214from  mypyc .ir .func_ir  import  FuncIR 
13- from  mypyc .ir .ops  import  Call , CallC , CString , LoadLiteral , LoadStatic , Op , PrimitiveOp , Value 
15+ from  mypyc .ir .ops  import  (
16+     Box ,
17+     Call ,
18+     CallC ,
19+     Cast ,
20+     CString ,
21+     DecRef ,
22+     GetAttr ,
23+     IncRef ,
24+     LoadLiteral ,
25+     LoadStatic ,
26+     Op ,
27+     PrimitiveOp ,
28+     SetAttr ,
29+     Unbox ,
30+     Value ,
31+ )
32+ from  mypyc .ir .rtypes  import  none_rprimitive 
1433from  mypyc .irbuild .ll_builder  import  LowLevelIRBuilder 
1534from  mypyc .options  import  CompilerOptions 
1635from  mypyc .primitives .misc_ops  import  log_trace_event 
@@ -38,6 +57,18 @@ def get_load_global_name(op: CallC) -> str | None:
3857    return  None 
3958
4059
60+ # These primitives perform an implicit IncRef for the return value. Only some of the most common ones 
61+ # are included, and mostly ops that could be switched to use borrowing in some contexts. 
62+ primitives_that_inc_ref : Final  =  {
63+     "list_get_item_unsafe" ,
64+     "CPyList_GetItemShort" ,
65+     "CPyDict_GetWithNone" ,
66+     "CPyList_GetItem" ,
67+     "CPyDict_GetItem" ,
68+     "CPyList_PopLast" ,
69+ }
70+ 
71+ 
4172class  LogTraceEventTransform (IRTransform ):
4273    def  __init__ (self , builder : LowLevelIRBuilder , fullname : str ) ->  None :
4374        super ().__init__ (builder )
@@ -48,7 +79,10 @@ def visit_call(self, op: Call) -> Value:
4879        return  self .log (op , "call" , op .fn .fullname )
4980
5081    def  visit_primitive_op (self , op : PrimitiveOp ) ->  Value :
51-         return  self .log (op , "primitive_op" , op .desc .name )
82+         value  =  self .log (op , "primitive_op" , op .desc .name )
83+         if  op .desc .name  in  primitives_that_inc_ref :
84+             self .log_inc_ref (value )
85+         return  value 
5286
5387    def  visit_call_c (self , op : CallC ) ->  Value :
5488        if  global_name  :=  get_load_global_name (op ):
@@ -63,11 +97,53 @@ def visit_call_c(self, op: CallC) -> Value:
6397        elif  func_name  ==  "PyObject_VectorcallMethod"  and  isinstance (op .args [0 ], LoadLiteral ):
6498            return  self .log (op , "python_call_method" , str (op .args [0 ].value ))
6599
66-         return  self .log (op , "call_c" , func_name )
100+         value  =  self .log (op , "call_c" , func_name )
101+         if  func_name  in  primitives_that_inc_ref :
102+             self .log_inc_ref (value )
103+         return  value 
104+ 
105+     def  visit_get_attr (self , op : GetAttr ) ->  Value :
106+         value  =  self .log (op , "get_attr" , f"{ op .class_type .name }  .{ op .attr }  " )
107+         if  not  op .is_borrowed  and  op .type .is_refcounted :
108+             self .log_inc_ref (op )
109+         return  value 
110+ 
111+     def  visit_set_attr (self , op : SetAttr ) ->  Value :
112+         name  =  "set_attr"  if  not  op .is_init  else  "set_attr_init" 
113+         return  self .log (op , name , f"{ op .class_type .name }  .{ op .attr }  " )
114+ 
115+     def  visit_box (self , op : Box ) ->  Value :
116+         if  op .src .type  is  none_rprimitive :
117+             # Boxing 'None' is a very quick operation, so we don't log it. 
118+             return  self .add (op )
119+         else :
120+             return  self .log (op , "box" , str (op .src .type ))
121+ 
122+     def  visit_unbox (self , op : Unbox ) ->  Value :
123+         return  self .log (op , "unbox" , str (op .type ))
124+ 
125+     def  visit_cast (self , op : Cast ) ->  Value  |  None :
126+         value  =  self .log (op , "cast" , str (op .type ))
127+         if  not  op .is_borrowed :
128+             self .log_inc_ref (value )
129+         return  value 
130+ 
131+     def  visit_inc_ref (self , op : IncRef ) ->  Value :
132+         return  self .log (op , "inc_ref" , str (op .src .type ))
133+ 
134+     def  visit_dec_ref (self , op : DecRef ) ->  Value :
135+         return  self .log (op , "dec_ref" , str (op .src .type ))
136+ 
137+     def  log_inc_ref (self , value : Value ) ->  None :
138+         self .log_event ("inc_ref" , str (value .type ), value .line )
67139
68140    def  log (self , op : Op , name : str , details : str ) ->  Value :
69-         if  op .line  >=  0 :
70-             line_str  =  str (op .line )
141+         self .log_event (name , details , op .line )
142+         return  self .add (op )
143+ 
144+     def  log_event (self , name : str , details : str , line : int ) ->  None :
145+         if  line  >=  0 :
146+             line_str  =  str (line )
71147        else :
72148            line_str  =  "" 
73149        self .builder .primitive_op (
@@ -78,6 +154,5 @@ def log(self, op: Op, name: str, details: str) -> Value:
78154                CString (name .encode ("utf-8" )),
79155                CString (details .encode ("utf-8" )),
80156            ],
81-             op . line ,
157+             line ,
82158        )
83-         return  self .add (op )
0 commit comments