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

post-opt analysis bug: wrongly refine :consistent of Base._getindex(::Base.OneTo{Int}, ::Int) #53508

Closed
aviatesk opened this issue Feb 28, 2024 · 5 comments · Fixed by #53518
Closed
Labels
compiler:optimizer Optimization passes (mostly in base/compiler/ssair/)

Comments

@aviatesk
Copy link
Sponsor Member

From https://discourse.julialang.org/t/trying-to-understand-inferred-effects/110854.
Base._getindex(::Base.OneTo{Int}, ::Int) is expected to be !:consistent since it does have @boundscheck, yet post-optimization analysis erroneously refines it as :consistent. Specifically, the post-optimization analysis of Base._getindex(::Base.OneTo{Int}, ::Int) does the refinement with analyzing all_retpaths_consistent=true (

consistent = sv.all_retpaths_consistent ? ALWAYS_TRUE : effects.consistent,
). However, as evident in the Cthulhu view, this method contains a GotoIfNot whose condition is Expr(:boundscheck), that analysis is wrong.

_getindex(v::UnitRange{T}, i::Integer) where T<:Union{Bool, Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} @ Base range.jl:931
     ── %0 = invoke _getindex(::UnitRange{Int64},::Int64)::Int64 (+c,+e,!n,+t,+s,+m,+u)                                                                 
932 1 ── %1  = Base.getfield(v, :start)::Int64                                                                                                       │╻   getproperty
    │    %2  = Base.sub_int(i, 1)::Int64                                                                                                             │╻   -%3  = Base.add_int(%1, %2)::Int64                                                                                                           │╻   +
933%4  = $(Expr(:boundscheck))::Bool                                                                                                           │   
    └───       goto #10 if not %4                                                                                                                    │   
    2 ── %6  = Base.slt_int(0, i)::Bool                                                                                                              │╻╷  _in_unit_range
    └───       goto #6 if not %6                                                                                                                     ││  
    3 ── %8  = Base.getfield(v, :stop)::Int64                                                                                                        ││╻   getproperty
    │    %9  = Base.sle_int(%3, %8)::Bool                                                                                                            ││╻   <=
    └───       goto #5 if not %9                                                                                                                     ││  
    4 ── %11 = Base.getfield(v, :start)::Int64                                                                                                       ││╻   getproperty
    │    %12 = Base.sle_int(%11, %3)::Bool                                                                                                           │││╻   <=
    └───       goto #7                                                                                                                               │   
    5 ──       goto #7                                                                                                                               │   
    6 ──       goto #7                                                                                                                               │   
    7 ┄─ %16 = φ (#4 => %12, #5 => false, #6 => false)::Bool                                                                                         │   
    └───       goto #9 if not %16                                                                                                                    │   
    8 ──       goto #10                                                                                                                              │   
    9 ──       invoke Base.throw_boundserror(v::UnitRange{Int64}, i::Int64)::Union{}                                                                 │   
    └───       unreachable                                                                                                                           │   
    10return %3                                                                                                                             │   
Select a call to descend into or  to ascend. [q]uit. [b]ookmark.
Toggles: [w]arn, [h]ide type-stable statements, [t]ype annotations, [s]yntax highlight for Source/LLVM/Native, [j]ump to source always, [o]ptimize, [d]ebuginfo, [r]emarks, [e]ffects, e[x]ception types, [i]nlining costs.
Show: [S]ource code, [A]ST, [T]yped code, [L]LVM IR, [N]ative code
Actions: [E]dit source code, [R]evise and redisplay
Advanced: dump [P]arams cache.
 • %19 = invoke throw_boundserror(::UnitRange{Int64},::Int64)::Union{} (+c,+e,!n,+t,+s,+m,+u)
   

The underlying issue may reside in the lazyagdomtree logic, necessitating a deeper understanding of the code's purpose.

(; cfg, domtree) = get!(sv.lazyagdomtree)
for succ in iterated_dominance_frontier(cfg, BlockLiveness(sv.ir.cfg.blocks[bb].succs, nothing), domtree)
if succ == length(cfg.blocks)
# Phi node in the virtual exit -> We have a conditional
# return. TODO: Check if all the retvals are egal.
sv.all_retpaths_consistent = false
else
visit_bb_phis!(sv.ir, succ) do phiidx::Int
push!(sv.inconsistent, phiidx)
end
end
end

/cc @Keno

@aviatesk aviatesk added the compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) label Feb 28, 2024
@Keno
Copy link
Member

Keno commented Feb 28, 2024

Why is the analysis wrong? The return value does not depend on the :boundscheck condition.

@aviatesk
Copy link
Sponsor Member Author

aviatesk commented Feb 28, 2024

Doesn't :consistent currently includes the termination consistency? If some inconsistency can lead to throwing error, I believe it's not all_retpaths_consistent.

So I guess this is a bug of conditional_successors_may_throw not hitting properly actually?

@Keno
Copy link
Member

Keno commented Feb 28, 2024

Ah yes, fair point. I did write that in the spec, but then I forgot about it ;).

@Keno
Copy link
Member

Keno commented Feb 28, 2024

(Though I have been thinking that maybe the two notions of value consistency and termination consistency ought to be separated, but we should fix this at least.

@aviatesk
Copy link
Sponsor Member Author

I'm in favor of the separation.

aviatesk added a commit that referenced this issue Feb 29, 2024
Previously `conditional_successors_may_throw` performed post-domination
analysis not on the initially specified `bb` (which was given as the
argument), but on those BBs being analyzed that were popped from the
work-queue at the time. As a result, there were cases where not all
conditional successors were visited.

fixes #53508
aviatesk added a commit that referenced this issue Mar 1, 2024
Previously `conditional_successors_may_throw` performed post-domination
analysis not on the initially specified `bb` (which was given as the
argument), but on those BBs being analyzed that were popped from the
work-queue at the time. As a result, there were cases where not all
conditional successors were visited.

fixes #53508
aviatesk added a commit that referenced this issue Mar 1, 2024
Previously `conditional_successors_may_throw` performed post-domination
analysis not on the initially specified `bb` (which was given as the
argument), but on those BBs being analyzed that were popped from the
work-queue at the time. As a result, there were cases where not all
conditional successors were visited.

fixes #53508
tecosaur pushed a commit to tecosaur/julia that referenced this issue Mar 4, 2024
…ang#53518)

Previously `conditional_successors_may_throw` performed post-domination
analysis not on the initially specified `bb` (which was given as the
argument), but on those BBs being analyzed that were popped from the
work-queue at the time. As a result, there were cases where not all
conditional successors were visited.

fixes JuliaLang#53508
mkitti pushed a commit to mkitti/julia that referenced this issue Apr 13, 2024
…ang#53518)

Previously `conditional_successors_may_throw` performed post-domination
analysis not on the initially specified `bb` (which was given as the
argument), but on those BBs being analyzed that were popped from the
work-queue at the time. As a result, there were cases where not all
conditional successors were visited.

fixes JuliaLang#53508
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:optimizer Optimization passes (mostly in base/compiler/ssair/)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants