Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork3k
Reference Counting Transform
Python usesreference counting to free (garbage collect) objects when they are no longer needed. When writing C extensions manually, maintaining reference counts is one of the most error-prone tasks. Mypyc abstracts this away by automatically inserting the necessary operations to increment/decrement reference counts. These are implemented through theIncRef
andDecRef
ops, respectively.
When generating IR inmypyc.irbuild
, no reference count manipulation operations are explicitly added. After the initial IR has been generated, the transform implemented inmypyc.transform.refcount
adds these operations.
These are the primary things the transform does (but there are many special cases beyond these):
- If a new reference to a value is needed, for example when assigning a value to another register, add an IncRef op.
- If the lifetime of a value ends, add a DecRef op.
Consider this code:
deff(x:str)->str:y=xg(y)
This is the initial IR (without reference count manipulation):
def f(x): x, y, r0 :: strL0: y = x r0 = g(y) return 1
The reference counting transform will produce this output (exception handling is omitted for brevity):
def f(x): x, y, r0 :: strL0: inc_ref x y = x r0 = g(y) dec_ref y dec_ref r0 return 1
Note that the generated IR is not optimal -- strictly speaking, we don't need to haveinc_ref x
anddec_ref y
. This illustrates that adding reference count manipulations optimally is non-trivial. We discuss some optimizations mypyc performs below.
You can find more examples in the test cases, inmypyc/test-data/refcount.test
.
A key principle of reference counting is that if you create another reference to an object, you need to increment the reference count. However, if we only hold the reference briefly and we can be certain that nobody can decrease the reference count during that period to free the object, it's possible toborrow a reference, i.e. take a reference without incrementing/decrementing the reference count.
Mypyc borrows values in some cases. It knows about certain operations that won't free (arbitrary) objects, and avoids redundant reference count manipulations across these operations. A typical example is the expressionx.y.z
, ify
andz
are native attributes. We can borrowx.y
, since reading the attributez
can't causex.y
to be freed. Thus we can generate this IR:
def f(x): x :: __main__.C r0 :: __main__.D r1 :: intL0: r0 = borrow x.y r1 = r0.z return r1
Note that the first attribute access looks like this:
r0 = borrow x.y
Theborrow
prefix means that this reads the attributex.y
without increasing the reference count. The second accessr0.z
increases the reference count. This is necessary since we will return the value, and we can't predict what the caller will do with the reference, so no borrowing is possible.