99
1010from __future__ import annotations
1111
12+ from typing import Final
13+
1214from mypyc .ir .func_ir import FuncIR
1315from mypyc .ir .ops import (
1416 Box ,
@@ -54,6 +56,18 @@ def get_load_global_name(op: CallC) -> str | None:
5456 return None
5557
5658
59+ # These primitives perform an implicit IncRef for the return value. Only some of the most common ones
60+ # are included.
61+ primitives_that_inc_ref : Final = {
62+ "list_get_item_unsafe" ,
63+ "CPyList_GetItemShort" ,
64+ "CPyDict_GetWithNone" ,
65+ "CPyList_GetItem" ,
66+ "CPyDict_GetItem" ,
67+ "CPyList_PopLast" ,
68+ }
69+
70+
5771class LogTraceEventTransform (IRTransform ):
5872 def __init__ (self , builder : LowLevelIRBuilder , fullname : str ) -> None :
5973 super ().__init__ (builder )
@@ -64,7 +78,10 @@ def visit_call(self, op: Call) -> Value:
6478 return self .log (op , "call" , op .fn .fullname )
6579
6680 def visit_primitive_op (self , op : PrimitiveOp ) -> Value :
67- return self .log (op , "primitive_op" , op .desc .name )
81+ value = self .log (op , "primitive_op" , op .desc .name )
82+ if op .desc .name in primitives_that_inc_ref :
83+ self .log_inc_ref (value )
84+ return value
6885
6986 def visit_call_c (self , op : CallC ) -> Value :
7087 if global_name := get_load_global_name (op ):
@@ -79,34 +96,47 @@ def visit_call_c(self, op: CallC) -> Value:
7996 elif func_name == "PyObject_VectorcallMethod" and isinstance (op .args [0 ], LoadLiteral ):
8097 return self .log (op , "python_call_method" , str (op .args [0 ].value ))
8198
82- return self .log (op , "call_c" , func_name )
99+ value = self .log (op , "call_c" , func_name )
100+ if func_name in primitives_that_inc_ref :
101+ self .log_inc_ref (value )
102+ return value
83103
84- def visit_get_attr (self , op : GetAttr ) -> Value | None :
85- return self .log (op , "get_attr" , f"{ op .class_type .name } .{ op .attr } " )
104+ def visit_get_attr (self , op : GetAttr ) -> Value :
105+ value = self .log (op , "get_attr" , f"{ op .class_type .name } .{ op .attr } " )
106+ if not op .is_borrowed and op .type .is_refcounted :
107+ self .log_inc_ref (op )
108+ return value
86109
87- def visit_set_attr (self , op : SetAttr ) -> Value | None :
110+ def visit_set_attr (self , op : SetAttr ) -> Value :
88111 name = "set_attr" if not op .is_init else "set_attr_init"
89112 return self .log (op , name , f"{ op .class_type .name } .{ op .attr } " )
90113
91- def visit_box (self , op : Box ) -> Value | None :
114+ def visit_box (self , op : Box ) -> Value :
92115 if op .src .type is none_rprimitive :
93116 # Boxing 'None' is a very quick operation, so we don't log it.
94117 return self .add (op )
95118 else :
96119 return self .log (op , "box" , str (op .src .type ))
97120
98- def visit_unbox (self , op : Unbox ) -> Value | None :
121+ def visit_unbox (self , op : Unbox ) -> Value :
99122 return self .log (op , "unbox" , str (op .type ))
100123
101- def visit_inc_ref (self , op : IncRef ) -> Value | None :
124+ def visit_inc_ref (self , op : IncRef ) -> Value :
102125 return self .log (op , "inc_ref" , str (op .src .type ))
103126
104- def visit_dec_ref (self , op : DecRef ) -> Value | None :
127+ def visit_dec_ref (self , op : DecRef ) -> Value :
105128 return self .log (op , "dec_ref" , str (op .src .type ))
106129
130+ def log_inc_ref (self , value : Value ) -> None :
131+ self .log_event ("inc_ref" , str (value .type ), value .line )
132+
107133 def log (self , op : Op , name : str , details : str ) -> Value :
108- if op .line >= 0 :
109- line_str = str (op .line )
134+ self .log_event (name , details , op .line )
135+ return self .add (op )
136+
137+ def log_event (self , name : str , details : str , line : int ) -> None :
138+ if line >= 0 :
139+ line_str = str (line )
110140 else :
111141 line_str = ""
112142 self .builder .primitive_op (
@@ -117,6 +147,5 @@ def log(self, op: Op, name: str, details: str) -> Value:
117147 CString (name .encode ("utf-8" )),
118148 CString (details .encode ("utf-8" )),
119149 ],
120- op . line ,
150+ line ,
121151 )
122- return self .add (op )
0 commit comments