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

Fix #32579 - Issue in typeconstraint accumulation #32605

Merged
merged 1 commit into from
Jul 17, 2019
Merged

Fix #32579 - Issue in typeconstraint accumulation #32605

merged 1 commit into from
Jul 17, 2019

Commits on Jul 17, 2019

  1. Fix #32579 - Issue in typeconstraint accumulation

    The bug here is a bit subtle, but perhaps best illustrated with
    the included test case:
    ```
    function f32579(x::Int64, b::Bool)
        if b
            x = nothing
        end
        if isa(x, Int64)
            y = x
        else
            y = x
        end
        if isa(y, Nothing)
            z = y
        else
            z = y
        end
        return z === nothing
    end
    ```
    The code just after SSA conversion looks like:
    ```
    2  1 ─       goto #3 if not _3
    3  2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
    5  3 ┄ %3  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
       │   %4  = (%3 isa Main.Int64)::Bool
       └──       goto #5 if not %4
    6  4 ─ %6  = π (%3, Int64)
       └──       goto #6
    8  5 ─ %8  = π (%3, Nothing)
    10 6 ┄ %9  = φ (#4 => %6, #5 => %8)::Union{Nothing, Int64}
       │   %10 = (%9 isa Main.Nothing)::Bool
       └──       goto #8 if not %10
    11 7 ─ %12 = π (%9, Nothing)
       └──       goto #9
    13 8 ─ %14 = π (%9, Int64)
    15 9 ┄ %15 = φ (#7 => %12, #8 => %14)::Union{Nothing, Int64}
       │   %16 = (%15 === Main.nothing)::Bool
       └──       return %16
    ```
    Now, we have special code in SROA (despite it not really being an
    SROA transform) that looks at `===` and replaces
    it by a nest of phis of booleans. The reasoning for this transform
    is that it eliminates a use of a value where we only care about the
    type and not the content, thus making it more likely that the value
    will subsequently be eligible for SROA. In addition, while it goes
    along resolving which values feed into any particular phi, it
    accumulates and type conditions it encounters along the way.
    
    Thus in the example above, something like the following happens:
    - We look at %14, which πs to %9 with an Int64 constraint, so we only
      consider the #4 predecessor for %9 (due to the constraint), until
      we get to %3, where we again only consider the #1 predecessor,
      where we find the argument (of type Int64) and conclude the result
      is always false
    - Now we pop the next item of the stack from our original phi, look
      at %12, which πs to %9 with a Nothing constraint.
    
    At this point we used to terminate the search because we already looked
    at %9. However, crucially, we looked at %9 only with an Int64 constraint,
    so we missed the fact that `nothing` was in fact a possible value for this
    phi. The result was a missing entry in the generated phi node:
    ```
    1 ─       goto #3 if not b
    2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
    3 ┄ %3  = φ (#1 => false)::Bool
    │   %4  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
    │   %5  = (%4 isa Main.Int64)::Bool
    └──       goto #5 if not %5
    4 ─ %7  = π (%4, Int64)
    └──       goto #6
    5 ─ %9  = π (%4, Nothing)
    6 ┄ %10 = φ (#4 => %3, #5 => %3)::Bool
    │   %11 = φ (#4 => %7, #5 => %9)::Union{Nothing, Int64}
    │   %12 = (%11 isa Main.Nothing)::Bool
    └──       goto #8 if not %12
    7 ─       goto #9
    8 ─       nothing::Nothing
    9 ┄ %16 = φ (#7 => %10, #8 => %10)::Bool
    └──       return %16
    ```
    (note the missing #2 predecessor in phi node %3), which would result
    in an undefined value at runtime, though in this case LLVM would
    have taken advantage of that to just return 0:
    ```
    define i8 @julia_f32579_16051(i64, i8) {
    top:
    ;  @ REPL[1]:15 within `f32579'
      ret i8 0
    }
    ```
    Compare this now to the optimized IR with this patch:
    ```
    1 ─       goto #3 if not b
    2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
    3 ┄ %3  = φ (#2 => true, #1 => false)::Bool
    │   %4  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
    │   %5  = (%4 isa Main.Int64)::Bool
    └──       goto #5 if not %5
    4 ─ %7  = π (%4, Int64)
    └──       goto #6
    5 ─ %9  = π (%4, Nothing)
    6 ┄ %10 = φ (#4 => %3, #5 => %3)::Bool
    │   %11 = φ (#4 => %7, #5 => %9)::Union{Nothing, Int64}
    │   %12 = (%11 isa Main.Nothing)::Bool
    └──       goto #8 if not %12
    7 ─       goto #9
    8 ─       nothing::Nothing
    9 ┄ %16 = φ (#7 => %10, #8 => %10)::Bool
    └──       return %16
    ```
    The %3 phi node has its missing entry and the generated LLVM code
    correctly returns `b`:
    ```
    define i8 @julia_f32579_16112(i64, i8) {
    top:
      %2 = and i8 %1, 1
    ;  @ REPL[1]:15 within `f32579'
      ret i8 %2
    }
    ```
    Keno committed Jul 17, 2019
    Configuration menu
    Copy the full SHA
    7934641 View commit details
    Browse the repository at this point in the history