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

Slightly refactor compile.jl #1

Merged
merged 1 commit into from
Apr 7, 2019

Conversation

jpsamaroo
Copy link

Alternatively, we could refactor runtests.jl to compile each function in it into a native .so and executable, which would make it possible to use Julia's testing infrastructure. Let me know if you'd prefer I do that instead.

@tshort
Copy link
Owner

tshort commented Apr 7, 2019

LGTM. Any ideas you have for better testing would be great.

@tshort tshort merged commit f8fc866 into tshort:standalone-mode Apr 7, 2019
Keno added a commit that referenced this pull request Jan 17, 2020
…#32605)

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 JuliaLang#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 JuliaLang#5 if not %4
6  4 ─ %6  = π (%3, Int64)
   └──       goto JuliaLang#6
8  5 ─ %8  = π (%3, Nothing)
10 6 ┄ %9  = φ (JuliaLang#4 => %6, JuliaLang#5 => %8)::Union{Nothing, Int64}
   │   %10 = (%9 isa Main.Nothing)::Bool
   └──       goto JuliaLang#8 if not %10
11 7 ─ %12 = π (%9, Nothing)
   └──       goto JuliaLang#9
13 8 ─ %14 = π (%9, Int64)
15 9 ┄ %15 = φ (JuliaLang#7 => %12, JuliaLang#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 JuliaLang#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 JuliaLang#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 JuliaLang#5 if not %5
4 ─ %7  = π (%4, Int64)
└──       goto JuliaLang#6
5 ─ %9  = π (%4, Nothing)
6 ┄ %10 = φ (JuliaLang#4 => %3, JuliaLang#5 => %3)::Bool
│   %11 = φ (JuliaLang#4 => %7, JuliaLang#5 => %9)::Union{Nothing, Int64}
│   %12 = (%11 isa Main.Nothing)::Bool
└──       goto JuliaLang#8 if not %12
7 ─       goto JuliaLang#9
8 ─       nothing::Nothing
9 ┄ %16 = φ (JuliaLang#7 => %10, JuliaLang#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 JuliaLang#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 JuliaLang#5 if not %5
4 ─ %7  = π (%4, Int64)
└──       goto JuliaLang#6
5 ─ %9  = π (%4, Nothing)
6 ┄ %10 = φ (JuliaLang#4 => %3, JuliaLang#5 => %3)::Bool
│   %11 = φ (JuliaLang#4 => %7, JuliaLang#5 => %9)::Union{Nothing, Int64}
│   %12 = (%11 isa Main.Nothing)::Bool
└──       goto JuliaLang#8 if not %12
7 ─       goto JuliaLang#9
8 ─       nothing::Nothing
9 ┄ %16 = φ (JuliaLang#7 => %10, JuliaLang#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
}
```
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.

None yet

2 participants