Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimizer: Update SROA def-uses after DCE #57201

Merged
merged 3 commits into from
Feb 3, 2025

Conversation

serenity4
Copy link
Contributor

Fixes #57141.

Given the function

julia> function _f()
           ref = Ref{Any}()
           ref[] = 3
           @assert isdefined(ref, :x)
           inner = Returns(ref)
           x = inner()
           (x, ref[])
       end
_f (generic function with 1 method)

julia> f() = first(_f())
f (generic function with 1 method)

Here is before:

julia> @code_typed f()
CodeInfo(
1%1 = %new(Base.RefValue{Any})::Base.RefValue{Any}
└──      goto #3
2 ─      unreachable
3return %1
) => Base.RefValue{Any}

Here is after this PR:

julia> @code_typed f()
CodeInfo(
1%1 = %new(Base.RefValue{Any})::Base.RefValue{Any}
│          builtin Base.setfield!(%1, :x, 3)::Int64%3 =   builtin Main.isdefined(%1, :x)::Bool
└──      goto #3 if not %3
2 ─      goto #4
3%6 =    invoke Base.AssertionError("isdefined(ref, :x)"::String)::AssertionError
│          builtin Base.throw(%6)::Union{}
└──      unreachable
4return %1
) => Base.RefValue{Any}

The elimination of setfield! was due to a use still being recorded for ref[] in the def-use data while DCE eliminated this getindex call (by virtue of not using the second tuple element in the result).

@oscardssmith oscardssmith added bugfix This change fixes an existing bug compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) labels Jan 30, 2025
@serenity4 serenity4 changed the title Inference: Update SROA def-uses after DCE Optimizer: Update SROA def-uses after DCE Jan 30, 2025
@serenity4
Copy link
Contributor Author

I believe the test failure to be unrelated, I could not reproduce locally

Compiler/test/irpasses.jl Outdated Show resolved Hide resolved
Compiler/test/irpasses.jl Outdated Show resolved Hide resolved
@oscardssmith oscardssmith merged commit fea26dd into JuliaLang:master Feb 3, 2025
6 of 7 checks passed
@serenity4 serenity4 deleted the sroa-mutables-defuse-fix branch February 3, 2025 15:01
@@ -1511,6 +1511,10 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing)
used_ssas[x.id] -= 1
end
ir = complete(compact)
# remove any use that has been optimized away by the DCE
for (intermediaries, defuse) in values(defuses)
filter!(x -> ir[SSAValue(x.idx)][:stmt] !== nothing, defuse.uses)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add x::SSAUse annotation here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, defuse is already typed as SSADefUse in the defuses container and .uses is also typed to be SSAUse:

struct SSADefUse
    uses::Vector{SSAUse}
    defs::Vector{Int}
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that’s true in general cases, but since Compiler code needs to be inferred even before inference is fully set up during bootstrap, there is a best practice of declaring method signatures as concretely as possible.

More broadly, even in cases where inference doesn’t work perfectly, having concrete method signatures allows that the method can still be compiled, making this a generally useful technique. In this specific case, inference is very likely to succeed, but given that other parts of the code follow this convention, it might make sense to do so here as well.

That said, I’m not entirely sure if this best practice is still necessary in the current implementation of Compiler.jl. What do you think on this @vtjnash?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is mainly about readability here. A lot of untyped arguments makes it really hard to understand what is supposed to happen in the code. If only one thing can happen, then it is usually better to assert that it happened, both so that if something different happens the compiler gets an error and so the reader can see what the author expected to happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix This change fixes an existing bug compiler:optimizer Optimization passes (mostly in base/compiler/ssair/)
Projects
None yet
4 participants