If I modify the example given here https://github.com/JuliaLang/julia/pull/46651#issuecomment-1253471390 to
const FINALIZATION_COUNT = Ref(0)
init_finalization_count!() = FINALIZATION_COUNT[] = 0
get_finalization_count() = FINALIZATION_COUNT[]
@noinline add_finalization_count!(x) = FINALIZATION_COUNT[] += x
@noinline Base.@assume_effects :nothrow safeprint(io::IO, x...) = (@nospecialize; print(io, x...))
@test Core.Compiler.is_finalizer_inlineable(Base.infer_effects(add_finalization_count!, (Int,)))
mutable struct DoAllocWithFieldInter
x::Int
end
function register_finalizer!(obj::DoAllocWithFieldInter)
finalizer(obj) do this
add_finalization_count!(this.x)
end
end
function cfg_finalization6(io)
for i = -999:1000
o = DoAllocWithFieldInter(0)
register_finalizer!(o)
safeprint(io, o.x, '\n')
end
end
Letβs look at the ir code to see where the finalizer has been inserted
julia> ir = Base.code_ircode(cfg_finalization6, (IO,); optimize_until="SROA") |> only |> first
2 1 β goto #7 if not true β
2 β %21 = Ο (#1 => -999, #6 => %14)::Int64
β nothing::Tuple{Int64, Int64}
β %3 = %21::Int64 β
4 β %4 = %new(Base.RefValue{Int64}, 0)::Base.RefValue{Int64}
5 β nothing::Nothing ββ» register_finalizer!
6 β %6 = Base.getfield(%4, :x)::Int64β» getproperty
β %23 = Base.getfield(%4, :x)::Int64
β invoke Main.add_finalization_count!(%23::Int64)::Int64
β invoke Main.safeprint(_2::IO, %6::Any, '\n'::Vararg{Any})::Any
13 β %8 = (%3 === 1000)::Bool ββ»β· iterate
βββ goto #4 if not %8 ββ
3 β goto #5 ββ
4 β %11 = Base.add_int(%3, 1)::Int64 βββ» +
β nothing::Tuple{Int64, Int64}β» iterate
βββ goto #5 ββ
5 β %14 = Ο (#4 => %11)::Int64 β
β %22 = Ο (#3 => true, #4 => false)::Bool
β nothing::Union{Nothing, Tuple{Int64, Int64}}
β %16 = %22::Bool β
β %17 = Base.not_int(%16)::Bool β
βββ goto #7 if not %17 β
6 β goto #2 β
7 β return nothing
Itβs been inserted after the last getfield
call but before the safeprint
. Is this intended as I want to instead free a pointer in the finalizer and so cannot have any uses of it after the finalizer, e.g.
mutable struct UniquePointer{T} <: Ref{T}
ptr::Ptr{T}
function UniquePointer(ptr::Ptr{T}) where T
self = new{T}(ptr)
finalizer(free, self)
end
end
Base.@assume_effects :nothrow :notaskstate free(ptr) = Libc.free(ptr)
free(self::UniquePointer) = free(self.ptr)
I see in the SROA pass that the finalizer only seems to track uses for getfield, setfield, isdefined
and ccall
s. Would changing it to also track uses of its fields be desired.