Skip to content

Commit 049a879

Browse files
authored
[mypyc] Don't free target of LoadMem too early (python#9299)
Add optional reference to the object from which we are reading from to `LoadMem` so that the object won't be freed before we read memory. Fixes mypyc/mypyc#756.
1 parent ce2a216 commit 049a879

File tree

9 files changed

+62
-27
lines changed

9 files changed

+62
-27
lines changed

mypyc/ir/ops.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,25 +1348,42 @@ def accept(self, visitor: 'OpVisitor[T]') -> T:
13481348

13491349

13501350
class LoadMem(RegisterOp):
1351-
"""Reading a memory location
1352-
1353-
type ret = *(type*)src
1351+
"""Read a memory location.
1352+
1353+
type ret = *(type *)src
1354+
1355+
Attributes:
1356+
type: Type of the read value
1357+
src: Pointer to memory to read
1358+
base: If not None, the object from which we are reading memory.
1359+
It's used to avoid the target object from being freed via
1360+
reference counting. If the target is not in reference counted
1361+
memory, or we know that the target won't be freed, it can be
1362+
None.
13541363
"""
13551364
error_kind = ERR_NEVER
13561365

1357-
def __init__(self, type: RType, src: Value, line: int = -1) -> None:
1366+
def __init__(self, type: RType, src: Value, base: Optional[Value], line: int = -1) -> None:
13581367
super().__init__(line)
13591368
self.type = type
13601369
# TODO: for now we enforce that the src memory address should be Py_ssize_t
13611370
# later we should also support same width unsigned int
13621371
assert is_pointer_rprimitive(src.type)
13631372
self.src = src
1373+
self.base = base
13641374

13651375
def sources(self) -> List[Value]:
1366-
return [self.src]
1376+
if self.base:
1377+
return [self.src, self.base]
1378+
else:
1379+
return [self.src]
13671380

13681381
def to_str(self, env: Environment) -> str:
1369-
return env.format("%r = load_mem %r :: %r*", self, self.src, self.type)
1382+
if self.base:
1383+
base = env.format(', %r', self.base)
1384+
else:
1385+
base = ''
1386+
return env.format("%r = load_mem %r%s :: %r*", self, self.src, base, self.type)
13701387

13711388
def accept(self, visitor: 'OpVisitor[T]') -> T:
13721389
return visitor.visit_load_mem(self)

mypyc/irbuild/ll_builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ def builtin_len(self, val: Value, line: int) -> Value:
826826
typ = val.type
827827
if is_list_rprimitive(typ) or is_tuple_rprimitive(typ):
828828
elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size'))
829-
size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address))
829+
size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val))
830830
offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
831831
return self.binary_int_op(short_int_rprimitive, size_value, offset,
832832
BinaryIntOp.LEFT_SHIFT, line)
@@ -837,7 +837,7 @@ def builtin_len(self, val: Value, line: int) -> Value:
837837
BinaryIntOp.LEFT_SHIFT, line)
838838
elif is_set_rprimitive(typ):
839839
elem_address = self.add(GetElementPtr(val, PySetObject, 'used'))
840-
size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address))
840+
size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val))
841841
offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive))
842842
return self.binary_int_op(short_int_rprimitive, size_value, offset,
843843
BinaryIntOp.LEFT_SHIFT, line)

mypyc/test-data/irbuild-basic.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,7 @@ def lst(x):
13681368
r3 :: bool
13691369
L0:
13701370
r0 = get_element_ptr x ob_size :: PyVarObject
1371-
r1 = load_mem r0 :: native_int*
1371+
r1 = load_mem r0, x :: native_int*
13721372
r2 = r1 << 1
13731373
r3 = r2 != 0
13741374
if r3 goto L1 else goto L2 :: bool
@@ -1980,7 +1980,7 @@ L0:
19801980
r5 = 0
19811981
L1:
19821982
r6 = get_element_ptr r4 ob_size :: PyVarObject
1983-
r7 = load_mem r6 :: native_int*
1983+
r7 = load_mem r6, r4 :: native_int*
19841984
r8 = r7 << 1
19851985
r9 = r5 < r8 :: signed
19861986
if r9 goto L2 else goto L14 :: bool
@@ -2065,7 +2065,7 @@ L0:
20652065
r5 = 0
20662066
L1:
20672067
r6 = get_element_ptr r4 ob_size :: PyVarObject
2068-
r7 = load_mem r6 :: native_int*
2068+
r7 = load_mem r6, r4 :: native_int*
20692069
r8 = r7 << 1
20702070
r9 = r5 < r8 :: signed
20712071
if r9 goto L2 else goto L14 :: bool
@@ -2152,7 +2152,7 @@ L0:
21522152
r0 = 0
21532153
L1:
21542154
r1 = get_element_ptr l ob_size :: PyVarObject
2155-
r2 = load_mem r1 :: native_int*
2155+
r2 = load_mem r1, l :: native_int*
21562156
r3 = r2 << 1
21572157
r4 = r0 < r3 :: signed
21582158
if r4 goto L2 else goto L4 :: bool
@@ -2174,7 +2174,7 @@ L4:
21742174
r12 = 0
21752175
L5:
21762176
r13 = get_element_ptr l ob_size :: PyVarObject
2177-
r14 = load_mem r13 :: native_int*
2177+
r14 = load_mem r13, l :: native_int*
21782178
r15 = r14 << 1
21792179
r16 = r12 < r15 :: signed
21802180
if r16 goto L6 else goto L8 :: bool

mypyc/test-data/irbuild-lists.test

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def f(a):
117117
r2 :: short_int
118118
L0:
119119
r0 = get_element_ptr a ob_size :: PyVarObject
120-
r1 = load_mem r0 :: native_int*
120+
r1 = load_mem r0, a :: native_int*
121121
r2 = r1 << 1
122122
return r2
123123

@@ -156,7 +156,7 @@ def increment(l):
156156
r9 :: short_int
157157
L0:
158158
r0 = get_element_ptr l ob_size :: PyVarObject
159-
r1 = load_mem r0 :: native_int*
159+
r1 = load_mem r0, l :: native_int*
160160
r2 = r1 << 1
161161
r3 = 0
162162
i = r3
@@ -196,4 +196,3 @@ L0:
196196
r5 = box(short_int, 6)
197197
r6 = PyList_Append(r2, r5)
198198
return r2
199-

mypyc/test-data/irbuild-set.test

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ L0:
6969
r5 = box(short_int, 6)
7070
r6 = PySet_Add(r0, r5)
7171
r7 = get_element_ptr r0 used :: PySetObject
72-
r8 = load_mem r7 :: native_int*
72+
r8 = load_mem r7, r0 :: native_int*
7373
r9 = r8 << 1
7474
return r9
7575

@@ -223,4 +223,3 @@ L0:
223223
r7 = box(short_int, 6)
224224
r8 = PySet_Add(r0, r7)
225225
return r0
226-

mypyc/test-data/irbuild-statements.test

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ L0:
323323
r0 = 0
324324
L1:
325325
r1 = get_element_ptr ls ob_size :: PyVarObject
326-
r2 = load_mem r1 :: native_int*
326+
r2 = load_mem r1, ls :: native_int*
327327
r3 = r2 << 1
328328
r4 = r0 < r3 :: signed
329329
if r4 goto L2 else goto L4 :: bool
@@ -848,7 +848,7 @@ L0:
848848
r1 = 0
849849
L1:
850850
r2 = get_element_ptr a ob_size :: PyVarObject
851-
r3 = load_mem r2 :: native_int*
851+
r3 = load_mem r2, a :: native_int*
852852
r4 = r3 << 1
853853
r5 = r1 < r4 :: signed
854854
if r5 goto L2 else goto L4 :: bool
@@ -926,7 +926,7 @@ L0:
926926
r1 = PyObject_GetIter(b)
927927
L1:
928928
r2 = get_element_ptr a ob_size :: PyVarObject
929-
r3 = load_mem r2 :: native_int*
929+
r3 = load_mem r2, a :: native_int*
930930
r4 = r3 << 1
931931
r5 = r0 < r4 :: signed
932932
if r5 goto L2 else goto L7 :: bool
@@ -977,7 +977,7 @@ L1:
977977
if is_error(r3) goto L6 else goto L2
978978
L2:
979979
r4 = get_element_ptr b ob_size :: PyVarObject
980-
r5 = load_mem r4 :: native_int*
980+
r5 = load_mem r4, b :: native_int*
981981
r6 = r5 << 1
982982
r7 = r1 < r6 :: signed
983983
if r7 goto L3 else goto L6 :: bool
@@ -1002,4 +1002,3 @@ L6:
10021002
r14 = CPy_NoErrOccured()
10031003
L7:
10041004
return 1
1005-

mypyc/test-data/irbuild-tuple.test

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def f(x):
6767
r2 :: short_int
6868
L0:
6969
r0 = get_element_ptr x ob_size :: PyVarObject
70-
r1 = load_mem r0 :: native_int*
70+
r1 = load_mem r0, x :: native_int*
7171
r2 = r1 << 1
7272
return r2
7373

@@ -134,7 +134,7 @@ L0:
134134
r0 = 0
135135
L1:
136136
r1 = get_element_ptr xs ob_size :: PyVarObject
137-
r2 = load_mem r1 :: native_int*
137+
r2 = load_mem r1, xs :: native_int*
138138
r3 = r2 << 1
139139
r4 = r0 < r3 :: signed
140140
if r4 goto L2 else goto L4 :: bool
@@ -176,4 +176,3 @@ L2:
176176
r2 = CPySequenceTuple_GetItem(nt, 2)
177177
r3 = unbox(int, r2)
178178
return r3
179-

mypyc/test-data/refcount.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,3 +831,23 @@ L2:
831831
L3:
832832
return 1
833833

834+
[case testGetElementPtrLifeTime]
835+
from typing import List
836+
837+
def f() -> int:
838+
x: List[str] = []
839+
return len(x)
840+
[out]
841+
def f():
842+
r0, x :: list
843+
r1 :: ptr
844+
r2 :: native_int
845+
r3 :: short_int
846+
L0:
847+
r0 = []
848+
x = r0
849+
r1 = get_element_ptr x ob_size :: PyVarObject
850+
r2 = load_mem r1, x :: native_int*
851+
dec_ref x
852+
r3 = r2 << 1
853+
return r3

mypyc/test/test_emitfunc.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,10 @@ def test_binary_int_op(self) -> None:
262262
"""cpy_r_r04 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""")
263263

264264
def test_load_mem(self) -> None:
265-
self.assert_emit(LoadMem(bool_rprimitive, self.ptr),
265+
self.assert_emit(LoadMem(bool_rprimitive, self.ptr, None),
266266
"""cpy_r_r0 = *(char *)cpy_r_ptr;""")
267+
self.assert_emit(LoadMem(bool_rprimitive, self.ptr, self.s1),
268+
"""cpy_r_r00 = *(char *)cpy_r_ptr;""")
267269

268270
def test_get_element_ptr(self) -> None:
269271
r = RStruct("Foo", ["b", "i32", "i64"], [bool_rprimitive,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy