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

Allow irinterp to refine nothrow effect #48066

Merged
merged 1 commit into from
Jan 6, 2023
Merged

Allow irinterp to refine nothrow effect #48066

merged 1 commit into from
Jan 6, 2023

Conversation

Keno
Copy link
Member

@Keno Keno commented Dec 31, 2022

This addresses a remaining todo in the irinterp code to allow it to compute whether its particular evaluation refined nothrow. As a result, we can re-enable it for a larger class of ir (we had previously disabled it to avoid regressing cases where regular constprop was able to prove a nothrow refinement, but irinterp was not).

@Keno Keno requested a review from aviatesk December 31, 2022 21:37
@@ -225,16 +225,21 @@ function reprocess_instruction!(interp::AbstractInterpreter,
(; rt, effects) = abstract_eval_statement_expr(interp, inst, nothing, ir, irsv.mi)
# All other effects already guaranteed effect free by construction
if is_nothrow(effects)
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

IR_FLAG_EFFECT_FREE seems a bit risky here since we may prove :effect_free-ness in a presence of effect-ful statement, e.g.

julia> mutable struct SafeRef{T}
           x::T
       end

julia> Base.getindex(x::SafeRef) = x.x

julia> Base.setindex!(x::SafeRef, y) = x.x = y

julia> Base.infer_effects((String,String,)) do x, y
           r = SafeRef(x)
           r[] = y
           r[]
       end
(+c,+e,+n,+t,+s,+m)

So I guess we need to check is_removable_if_unused here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think you're probably right, though IR_FLAG_EFFECT_FREE was there before, so this might be an existing bug. Ideally we should have a testcase for this.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I attempted to create a test case to trigger an error because of this issue, but it seems very difficult to invent one. Something like the example below would be close (it actually crashes due to a different bug in our SROA though).

Base.@assume_effects :consistent :nothrow @inline function foo(a::Int, b)
    r = Ref(a)
    try
        setfield!(r, :x, b)
        nothing
    catch err
        return isa(b, String)
    end
end

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

xref: #48067

Copy link
Sponsor Member

@aviatesk aviatesk left a comment

Choose a reason for hiding this comment

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

I'm fine with move this forward leaving the correctness issue for the future.

@Keno
Copy link
Member Author

Keno commented Jan 3, 2023

I looked into the broadcast test failure. The issue there turned out to be due to the --check-bounds=yes flag used during testing. The irinterp uses the IR cached in the sysimage, which has a mismatched check-bounds flag. If the sysimage is built with --check-bounds=yes, also the tests pass. That's perhaps a bit surprising, but what happens is that there are two bounds checks that throw different errors and the second of them captures an allocated ref, so if they're both there, only the first one counts, so the ref doesn't get allocated.

I don't think that's really a bug with this PR, I'd like to just allow this case in the test. Maybe in the future we can separate out the caches between check-bounds=yes and no.

@Keno
Copy link
Member Author

Keno commented Jan 3, 2023

@nanosoldier runtests()

Keno added a commit that referenced this pull request Jan 4, 2023
I was investigating why LLVM was unable to remove the boundscheck
that was causing the test failure in #48066
and it turned out that the check was removable by instcombine, but
only if cvp ran first. However, we ran the passes in the opposite
order and then loop-rotated the condition away before instcombine
ran again. I think it makes sense to run cvp before instcombine to
make sure that instcombine can make use of the overflow flags inferred
by cvp. For the particular case in #48066, we also need
https://reviews.llvm.org/D140933 (and we may need it in general, since
we like to emit our conditionals as negations), but overall
this seems like a better place for cvp in our pass pipeline.
@Keno
Copy link
Member Author

Keno commented Jan 4, 2023

#48110 + https://reviews.llvm.org/D140933 also makes the test work regardless by allowing LLVM to eliminate the boundscheck. However, I intend to decouple the two changes by allowing the failure here.

@aviatesk
Copy link
Sponsor Member

aviatesk commented Jan 4, 2023

However, I intend to decouple the two changes by allowing the failure here.

Fine by me.

@nanosoldier
Copy link
Collaborator

Your package evaluation job has completed - possible new issues were detected. A full report can be found here.

@Keno
Copy link
Member Author

Keno commented Jan 5, 2023

A bunch of packages are complaining about lack of :splatnew support in irinterp, which should be easy enough to fix, and then looks like there's inference precision regressions in ChainRulesCore, ForwardDiff, FuncPipelines, JSOSolvers, PropertyDicts, SolverTools, Transits that need to be looked into.

@Keno
Copy link
Member Author

Keno commented Jan 5, 2023

ForwardDiff is the same failure as the broadcast test, so should be fixed by #48110.

Keno added a commit that referenced this pull request Jan 5, 2023
To catch a case that occurs in FuncPipelines.jl and was
causing precision issues in #48066.
Keno added a commit that referenced this pull request Jan 5, 2023
To catch a case that occurs in FuncPipelines.jl and was
causing precision issues in #48066.
Keno added a commit that referenced this pull request Jan 5, 2023
To catch a case that occurs in FuncPipelines.jl and was
causing precision issues in #48066.
This addresses a remaining todo in the irinterp code to allow it
to compute whether its particular evaluation refined `nothrow`.
As a result, we can re-enable it for a larger class of ir (we had
previously disabled it to avoid regressing cases where regular
constprop was able to prove a `nothrow` refinement, but irinterp
was not).
@Keno
Copy link
Member Author

Keno commented Jan 5, 2023

Ok, I've gone through all of them and they should be fixed between #48144 and #48110 and the changes I made here. A general trend was that we should probably generally make methods that we give effect overrides @noinline, since otherwise irinterp does not have access to those overrides. Of course, we may also want to revisit using that information more during inlining to preserve it.

@aviatesk
Copy link
Sponsor Member

aviatesk commented Jan 6, 2023

A general trend was that we should probably generally make methods that we give effect overrides @noinline, since otherwise irinterp does not have access to those overrides.

(Just to clarify) The problem here is that a :foldable method is no longer foldable during irinterp if it has been inlined. Especially, it needs to be :invoke call to be eligible for concrete_eval_invoke:

if is_foldable(effects) && is_all_const_arg(argtypes, #=start=#1)

It might be a good idea to use the @noinline annotation on these methods because otherwise, we would need to perform irinterp on pre-inlining IR, which goes against the purpose of irinterp as a fast abstract interpretation on optimized IR.

@Keno
Copy link
Member Author

Keno commented Jan 6, 2023

(Just to clarify) The problem here is that a :foldable method is no longer foldable during irinterp if it has been inlined.

That's correct

@Keno Keno merged commit fd41b59 into master Jan 6, 2023
@Keno Keno deleted the kf/irinterpnothrow branch January 6, 2023 22:49
Keno added a commit that referenced this pull request Jan 6, 2023
To catch a case that occurs in FuncPipelines.jl and was
causing precision issues in #48066.
Keno added a commit that referenced this pull request Jan 6, 2023
Rolls up individual review comments from #48066, #48144, #48151.
Keno added a commit that referenced this pull request Jan 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants