9
9
10
10
from __future__ import annotations
11
11
12
+ from typing import Final
13
+
12
14
from 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
14
33
from mypyc .irbuild .ll_builder import LowLevelIRBuilder
15
34
from mypyc .options import CompilerOptions
16
35
from mypyc .primitives .misc_ops import log_trace_event
@@ -38,6 +57,18 @@ def get_load_global_name(op: CallC) -> str | None:
38
57
return None
39
58
40
59
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
+
41
72
class LogTraceEventTransform (IRTransform ):
42
73
def __init__ (self , builder : LowLevelIRBuilder , fullname : str ) -> None :
43
74
super ().__init__ (builder )
@@ -48,7 +79,10 @@ def visit_call(self, op: Call) -> Value:
48
79
return self .log (op , "call" , op .fn .fullname )
49
80
50
81
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
52
86
53
87
def visit_call_c (self , op : CallC ) -> Value :
54
88
if global_name := get_load_global_name (op ):
@@ -63,11 +97,53 @@ def visit_call_c(self, op: CallC) -> Value:
63
97
elif func_name == "PyObject_VectorcallMethod" and isinstance (op .args [0 ], LoadLiteral ):
64
98
return self .log (op , "python_call_method" , str (op .args [0 ].value ))
65
99
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 )
67
139
68
140
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 )
71
147
else :
72
148
line_str = ""
73
149
self .builder .primitive_op (
@@ -78,6 +154,5 @@ def log(self, op: Op, name: str, details: str) -> Value:
78
154
CString (name .encode ("utf-8" )),
79
155
CString (details .encode ("utf-8" )),
80
156
],
81
- op . line ,
157
+ line ,
82
158
)
83
- return self .add (op )
0 commit comments