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